导航:首页 > 源码编译 > 面试算法大全

面试算法大全

发布时间:2024-06-21 01:11:41

A. 面试必会八大排序算法(Python)

一、插入排序

介绍

插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据。

算法适用于少量数据的排序,时间复杂度为O(n^2)。

插入排算法是稳定的排序方法。

步骤

①从第一个元素开始,该元素可以认为已经被排序

②取出下一个元素,在已经排序的元素序列中从后向前扫描

③如果该元素(已排序)大于新元素,将该元素移到下一位置

④重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

⑤将新元素插入到该位置中

⑥重复步骤2

排序演示

算法实现

二、冒泡排序

介绍

冒泡排序(Bubble Sort)是一种简单的排序算法,时间复杂度为O(n^2)。

它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

原理

循环遍历列表,每次循环找出循环最大的元素排在后面;

需要使用嵌套循环实现:外层循环控制总循环次数,内层循环负责每轮的循环比较。

步骤

①比较相邻的元素。如果第一个比第二个大,就交换他们两个。

②对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。

③针对所有的元素重复以上的步骤,除了最后一个。

④持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

算法实现:

三、快速排序

介绍

快速排序(Quicksort)是对冒泡排序的一种改进,借用了分治的思想,由C. A. R. Hoare在1962年提出。

基本思想

快速排序的基本思想是:挖坑填数 + 分治法。

首先选出一个轴值(pivot,也有叫基准的),通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

实现步骤

①从数列中挑出一个元素,称为 “基准”(pivot);

②重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边);

③对所有两个小数列重复第二步,直至各区间只有一个数。

排序演示

算法实现

四、希尔排序

介绍

希尔排序(Shell Sort)是插入排序的一种,也是缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法,时间复杂度为:O(1.3n)。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

·插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率;

·但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位。

基本思想

①希尔排序是把记录按下标的一定量分组,对每组使用直接插入算法排序;

②随着增量逐渐减少,每组包1含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法被终止。

排序演示

算法实现

五、选择排序

介绍

选择排序(Selection sort)是一种简单直观的排序算法,时间复杂度为Ο(n2)。

基本思想

选择排序的基本思想:比较 + 交换。

第一趟,在待排序记录r1 ~ r[n]中选出最小的记录,将它与r1交换;

第二趟,在待排序记录r2 ~ r[n]中选出最小的记录,将它与r2交换;

以此类推,第 i 趟,在待排序记录ri ~ r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕。

排序演示

选择排序的示例动画。红色表示当前最小值,黄色表示已排序序列,蓝色表示当前位置。

算法实现

六、堆排序

介绍

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。

利用数组的特点快速指定索引的元素。

基本思想

堆分为大根堆和小根堆,是完全二叉树。

大根堆的要求是每个节点的值不大于其父节点的值,即A[PARENT[i]] >=A[i]。

在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

排序演示

算法实现

七、归并排序

介绍

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

基本思想

归并排序算法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

算法思想

自上而下递归法(假如序列共有n个元素)

① 将序列每相邻两个数字进行归并操作,形成 floor(n/2)个序列,排序后每个序列包含两个元素;

② 将上述序列再次归并,形成 floor(n/4)个序列,每个序列包含四个元素;

③ 重复步骤②,直到所有元素排序完毕。

自下而上迭代法

① 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

② 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

③ 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

④ 重复步骤③直到某一指针达到序列尾;

⑤ 将另一序列剩下的所有元素直接复制到合并序列尾。

排序演示

算法实现

八、基数排序

介绍

基数排序(Radix Sort)属于“分配式排序”,又称为“桶子法”。

基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m) ,其中 r 为采取的基数,而m为堆数。

在某些时候,基数排序法的效率高于其他的稳定性排序法。

基本思想

将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

基数排序按照优先从高位或低位来排序有两种实现方案:

MSD(Most significant digital) 从最左侧高位开始进行排序。先按k1排序分组, 同一组中记录, 关键码k1相等,再对各组按k2排序分成子组, 之后, 对后面的关键码继续这样的排序分组, 直到按最次位关键码kd对各子组排序后. 再将各组连接起来,便得到一个有序序列。MSD方式适用于位数多的序列。

LSD (Least significant digital)从最右侧低位开始进行排序。先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。LSD方式适用于位数少的序列。

排序效果

算法实现

九、总结

各种排序的稳定性、时间复杂度、空间复杂度的总结:

平方阶O(n²)排序:各类简单排序:直接插入、直接选择和冒泡排序;

从时间复杂度来说:

线性对数阶O(nlog₂n)排序:快速排序、堆排序和归并排序;

O(n1+§))排序,§是介于0和1之间的常数:希尔排序 ;

线性阶O(n)排序:基数排序,此外还有桶、箱排序。

B. 面试最常考的 100 道算法题分类整理

大家好,我是 “负雪明早冲烛” ,一位用 7 年写槐毁了 1000 篇 LeetCode 算法题题解的程序员。欢迎关注。

粉丝常说: LeetCode 算法题太多了,准备面试该刷哪些题目

我之前根据 LeetCode 上面的点赞量分享过: LeetCode 上最经典的 100 道算法题 。

这 100 道题目都属于经典题目了,面试也常考,不过我还是不放心呢,毕竟 经典题 ≠ 面试题 呀!

但如果想知道面试常考的 100 道算法题的话,需要至少整理 1000 篇面经吧?这个工作量可不小啊!

还好,网上有个开源项目,帮我们做了这件事情,这个项目就是 CodeTop

这是网站的界面(地址: https://codetop.cc/home ),展示的就是每个面试题目出现的频度情况,甚至区分了公司和岗位:

这是开源项目的 GitHub 主页,已经 11.5k star ⭐️ 了:

这个项目中的题目来源是牛客网的面经、网友投票等,而且持续更新中,所以还是比较可靠的。

我对这个项目做了陆明歼整理,分类整理出来面试常考的 100 道算法题。

在整理之后,我对结果还是有点 惊讶 的!因为一些常见的数据结构与算法,竟然没有在常考面试中出现过!

比如前缀和、前缀树、并查集、图,这些都没有出现……

最常考面试题还是很基本的链表、二叉树、动态规划等等,是不是符合你的认知呢?

强烈建议大家在面试前把这 100 道题目搞懂!

作为宠粉达人,我提供了 3 种方式查看这 100 道题目:

没有任何套路,直接分享给大家!

在线查看地址: https://www.mubucm.com/doc/7jiBYKCKqet

在线查看地址: https://leetcode-cn.com/problem-list/q3iOID0B/

所有题目的地址如下:

前序遍历

中序遍历

层序遍历

视图

如果你觉得对你有帮助的话,求赞、求分享、求收藏。你的每一点鼓励都是对我的最大帮助!

C. 面试会出哪些经典算法题

如下:

1、排序算法∶快速排序、归并排序、计数排序

2、搜索算法∶回溯、递归、剪枝技巧

3、图论∶最短路、最小生成树、网络流建模

4、动态规划:背包问题、最长子序列、计数问题

5、基础技巧:分治、倍增、二分、贪心

6、数组与链表:单/双向链表、跳舞链

7、栈与队列

8、树与图:最近公共祖先、并查集

9、哈希表

10、堆:大/小根堆、可并堆

11、字符串∶字典树、后缀树

算法简介:

算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。

如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。

算法中的指令描述的是一个计算,当其运行时能从一个初始状态和(可能为空的)初始输入开始,经过一系列有限而清晰定义的状态,最终产生输出并停止于一个终态。一个状态到另一个状态的转移不一定是确定的。随机化算法在内的一些算法,包含了一些随机输入。

形式化算法的概念部分源自尝试解决希尔伯特提出的判定问题,并在其后尝试定义有效计算性或者有效方法中成形。

这些尝试包括库尔特·哥德尔、Jacques Herbrand和斯蒂芬·科尔·克莱尼分别于1930年、1934年和1935年提出的递归函数,阿隆佐·邱奇于1936年提出的λ演算,1936年Emil Leon Post的Formulation 1和艾伦·图灵1937年提出的图灵机。即使在当前,依然常有直觉想法难以定义为形式化算法的情况。

D. 求解公务员面试成绩算法

嗯。。
第一名的笔试成绩折算:119.3分/2×60%=35。8分
你的笔试成绩折算:97.5分/2×60%=29.3分
你们笔试成绩相差:侍高6.5分
按面试老肆尺成绩的比例折算你与第一名之间的分数差:6.5/雹旅40%=16.25分
由此来看,你的面试成绩比第一名多16.25分就能胜他。
祝你成功!·

E. 测试开发面试必知算法

测试开发的技能之一就是需要掌握一些开发的语言,而针对于考察开发语言,业界内比较容易采用的方式就是考察各种算法。在此做一个简单的总结(最近比较喜欢玩Python,所以都是以Python为例子,其它的语言类推。)

冒泡排序

冒泡排序算法的运作如下:(从后往前)
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

实例:对列表 [2, 8, 4, 7, 5, 9, 0]进行冒泡排序

递归

递归过程一般通过函数或子过程来实现。递归方法:在函数或子过程的内部,直接或者间接地调用自己的算法。

实例:要计算1-10的10位数字的乘积,直观的算法是1 2 3 4 5 6 7 8 9,利用递归则思路是循环执行n*n-1,直到n=1时

二叉树遍历算法
从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成。因此,在任一给定结点上,可以按某种次序执行三个操作:
⑴访问结点本身(N),
⑵遍历该结点的左子树(L),
⑶遍历该结点的右子树(R)。
以上三种操作有六种执行次序:
NLR、LNR、LRN、NRL、RNL、RLN。

二叉树的节点表示可以使用

前序遍历:根节点->左子树->右子树
中序遍历:左子树->根节点->右子树
后序遍历:左子树->右子树->根节点

实例:求二叉树深度和宽度
求深度用递归;求宽度用队列,然后把每层的宽度求出来,找出最大的就是二叉树的宽度

字符串倒序输出

思路一:索引的方法

思路二:借组列表进行翻转

后续还有的话会继续添加的。

F. 嵌入式工程师面试中常出现的算法

嵌入式工程师面试中常出现的算法

嵌入式系统是以应用为中心,以计算机技术为基础,并且软硬件可裁剪,适用于应用系统对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。下面我为大家整理了关于嵌入式工程师面试中经常出现的算法文章,希望对你有所帮助。

二分查找的代码.

int bfind(int* a,int len,int val)

{

int m = len/2;

int l = 0;

int r = len;

while(l!=m && r!= m)

{

if(a[m] > val)

{

r = m;

m = (m+l)/2;

}

else if(a[m] < val)

{

l = m;

m = (m+r)/2;

}

else

return m;

}

return -1; //没有找到

}

写出在母串中查找子串出现次数的代码.

int count1(char* str,char* s)

{

char* s1;

char* s2;

int count = 0;

while(*str!='')

{

s1 = str;

s2 = s;

while(*s2 == *s1&&(*s2!='')&&(*s1!='0'))

{

s2++;

s1++;

}

if(*s2 == '')

count++;

str++;

}

return count;

}

查找第一个匹配子串位置,如果返回的是s1长度len1表示没有找到

size_t find(char* s1,char* s2)

{

size_t i=0;

size_t len1 = strlen(s1)

size_t len2 = strlen(s2);

if(len1-len2<0) return len1;

for(;i {

size_t m = i;

for(size_t j=0;j {

if(s1[m]!=s2[j])

break;

m++;

}

if(j==len)

break;

}

return i }

写出快速排序或者某种排序算法代码

快速排序:

int partition(int* a,int l,int r)

{

int i=l-1,j=r,v=a[r];

while(1)

{

while(a[++i] while(a[--j]>v) if(j<=i) break;

if(i>=j)

break;

swap(a[i],a[j]);

}

swap(a[i],a[r]);

return i;

}

void qsort(int* a,int l,int r)

{

if(l>=r) return;

int i = partition(a,l,r);

qsort(a,l,i-1);

qsort(a,i+1,r);

}

冒泡排序:

void buble(int *a,int n)

{

for(int i=0;i {

for(int j=1;j {

if(a[j] {

int temp=a[j];

a[j] = a[j-1];

a[j-1] = temp;

}

}

}

}

插入排序:

void insertsort(int* a,int n)

{

int key;

for(int j=1;j {

key = a[j];

for(int i=j-1;i>=0&&a[i]>key;i--)

{

a[i+1] = a[i];

}

a[i+1] = key;

}

}

出现次数相当频繁

实现strcmp函数

int strcmp11(char* l,char* r)

{

assert(l!=0&&r!=0);

while(*l == *r &&*l != '') l++,r++;

if(*l > *r)

return 1;

else if(*l == *r)

return 0;

return -1;

}

实现字符串翻转

void reserve(char* str)

{

assert(str != NULL);

char * p1 = str;

char * p2 = str-1;

while(*++p2); //一般要求不能使用strlen

p2 -= 1;

while(p1 {

char c = *p1;

*p1++ = *p2;

*p2-- = c;

}

}

将一个单链表逆序

struct list_node

{

list_node(int a,list_node* b):data(a),next(b) //这个为了测试方便

{}

int data;

list_node* next;

};

void reserve(list_node* phead)

{

list_node* p = phead->next;

if(p == NULL || p->next == NULL) return; //只有头节点或一个节点

list_node* p1=p->next;

p->next=NULL;

while(p1!=NULL)

{

p = p1->next;

p1->next = phead->next;

phead->next = p1;

p1 = p;

}

}

测试程序:

list lt;

lt.phead = new list_node(0,0);

lt.phead->next = new list_node(1,0);

lt.phead->next->next = new list_node(2,0);

lt.phead->next->next->next = new list_node(3,0);

lt.reserve();

list_node * p = lt.phead;

while(p)

{

coutnext;

}

循环链表的节点对换和删除。

//双向循环

list_node* earse(list_node* node)

{

// if(node == rear) return node->next; //对于头节点可判断也可不判断。最好加上

list_node* next = node->next;

next->prev = node->prev;

node->prev->next = next;

delete node;

retrun next;

}

//单项循环

list_node* earse(list_node* node)

{

// if(node == rear) return node->next; //对于头节点可判断也可不判断。最好加上

list_node* p = rear;

while(p->next != node) p=p->next;

p->next = node->next;

delete node;

retrun p->next;

}

将一个数字字符串转换为数字."1234" -->1234

int atoii(char* s)

{

assert(s!=NULL);

int num = 0;

int temp;

while(*s>'0' && *s<'9')

{

num *= 10;

num += *s-'0';

s++;

}

return num;

}

出现次数相当频繁

.实现任意长度的整数相加或者相乘功能。

void bigadd(char* num,char* str,int len)

{

for(int i=len;i>0;i--)

{

num[i] += str[i];

int j = i;

while(num[j]>=10)

{

num[j--] -= 10;

num[j] += 1;

}

}

}

.写函数完成内存的拷贝

void* memcpy( void *dst, const void *src, unsigned int len )

{

register char *d;

register char *s;

if (len == 0)

return dst;

if ( dst > src ) //考虑覆盖情况

{

d = (char *)dst + len - 1;

s = (char *)src + len - 1;

while ( len >= 4 ) //循环展开,提高执行效率

{

*d-- = *s--;

*d-- = *s--;

*d-- = *s--;

*d-- = *s--;

len -= 4;

}

while ( len-- )

{

*d-- = *s--;

}

}

else if ( dst < src )

{

d = (char *)dst;

s = (char *)src;

while ( len >= 4 )

{

*d++ = *s++;

*d++ = *s++;

*d++ = *s++;

*d++ = *s++;

len -= 4;

}

while ( len-- )

{

*d++ = *s++;

}

}

return dst;

}

出现次数相当频繁

编写类String的构造函数、析构函数和赋值函数,已知类String的原型为:

class String

{

public:

String(const char *str = NULL); // 普通构造函数

String(const String &other); // 拷贝构造函数

~ String(void); // 析构函数

String & operate =(const String &other); // 赋值函数

private:

char *m_data; // 用于保存字符串

};

解答:

//普通构造函数

String::String(const char *str)

{

if(str==NULL)

{

m_data = new char[1]; // 得分点:对空字符串自动申请存放结束标志''的`空

//加分点:对m_data加NULL 判断

*m_data = '';

}

else

{

int length = strlen(str);

m_data = new char[length+1]; // 若能加 NULL 判断则更好

strcpy(m_data, str);

}

}

// String的析构函数

String::~String(void)

{

delete [] m_data; // 或delete m_data;

}

//拷贝构造函数

String::String(const String &other) // 得分点:输入参数为const型

{

int length = strlen(other.m_data);

m_data = new char[length+1]; //加分点:对m_data加NULL 判断

strcpy(m_data, other.m_data);

}

//赋值函数

String & String::operate =(const String &other) // 得分点:输入参数为const型

{

if(this == &other) //得分点:检查自赋值

return *this;

delete [] m_data; //得分点:释放原有的内存资源

int length = strlen( other.m_data );

m_data = new char[length+1]; //加分点:对m_data加NULL 判断

strcpy( m_data, other.m_data );

return *this; //得分点:返回本对象的引用

}

剖析:

能够准确无误地编写出String类的构造函数、拷贝构造函数、赋值函数和析构函数的面试者至少已经具备了C++基本功的60%以上!

在这个类中包括了指针类成员变量m_data,当类中包括指针类成员变量时,一定要重载其拷贝构造函数、赋值函数和析构函数,这既是对C++程序员的基本要求,也是《EffectiveC++》中特别强调的条款。

实现strcpy

char * strcpy( char *strDest, const char *strSrc )

{

assert( (strDest != NULL) && (strSrc != NULL) );

char *address = strDest;

while( (*strDest++ = * strSrc++) != ‘’ );

return address;

}

编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefgh”

函数头是这样的:

//pStr是指向以''结尾的字符串的指针

//steps是要求移动的n

void LoopMove ( char * pStr, int steps )

{

//请填充...

}

解答:

正确解答1:

void LoopMove ( char *pStr, int steps )

{

int n = strlen( pStr ) - steps;

char tmp[MAX_LEN];

strcpy ( tmp, pStr + n );

strcpy ( tmp + steps, pStr);

*( tmp + strlen ( pStr ) ) = '';

strcpy( pStr, tmp );

}

正确解答2:

void LoopMove ( char *pStr, int steps )

{

int n = strlen( pStr ) - steps;

char tmp[MAX_LEN];

memcpy( tmp, pStr + n, steps );

memcpy(pStr + steps, pStr, n );

memcpy(pStr, tmp, steps );

}

;
阅读全文

与面试算法大全相关的资料

热点内容
python套利交易 浏览:785
java配置环境变量win8 浏览:899
输解压密码时候压缩文件损坏 浏览:399
xampp如何运行php项目 浏览:901
哪个app团购电影票最便宜 浏览:618
国外如何给信件加密 浏览:164
哪个看书app免费书全 浏览:37
用php写一段自我介绍的代码 浏览:660
安卓手机怎么连接欧西亚 浏览:287
thinkpad用来编程怎么样 浏览:680
小米手机的桌面文件夹怎么找 浏览:823
java栈是什么意思 浏览:992
常州数控编程培训 浏览:725
博客编程随想 浏览:715
阿里云服务器怎么弄多个ip 浏览:798
思科配置loopback命令 浏览:169
数控铣床编程图形 浏览:171
编程故事课 浏览:833
达拉斯比洛杉矶源码 浏览:651
房贷提前解压可以吗 浏览:169