最近主要在看算法与数据结构相关的知识点,现在把一些需要看或者已经看了的算法总结一下,列一个大纲,慢慢消化。
一、排序
对于每个排序算法需要思考的问题如下:
1、每个算法的思想是什么?
2、每个算法的稳定性怎样?时间复杂度是多少?
3、在什么情况下,算法出现最好情况 or 最坏情况?
4、每种算法的具体实现又是怎样的?
常用的排序算法的复杂度与稳定性汇总如下表,以供参考。
关于每个排序算法的详细总结见“参考资料”:《常见排序算法小结》。
二、队列和栈
栈和队列也是非常常见的数据结构,它们本身的特点就非常适合用来解决一些实际的问题。
栈对于学习计算机的人来说,是再熟悉不过的东西了,很多东西都需要用栈来存储,像是操作系统就会给每个线程创建一个栈用来存储函数调用时各个函数的参数,返回地址及临时变量等,函数本身也有一个函数栈用来存储函数的局部变量等。
栈的特点就是后进先出,需要O(N)的时间才能找到栈中的最值。队列和栈刚好相反,是先进先出,表面上和栈是一对矛盾体,但实际上,都可以利用对方来实现自己。
以下总结了“队列和栈”经常遇到的相关问题:
1、定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数,时间复杂度为O(1)。
2、用两个栈结构实现队列,支持队列的基本操作(push,pop)。反过来用两个队列实现栈。相关总结可以参考博客剑指offer——栈与队列之间的相互实现。
3、栈的反转:实现一个栈的逆序,但是只能用递归函数和这个栈本身的pop操作来实现,而不能自己申请另外的数据结构,空间复杂度为O(1)。
4、给栈排序。具体实现见参考资料:《栈和队列算法总结》。
5、输入两个整数序列,第一个序列表示压栈顺序,判断第二个序列是否是弹出顺序。
三、链表
数据结构和算法,是我们程序设计最重要的两大元素,可以说,我们的编程,都是在选择和设计合适的数据结构来存放数据,然后再用合适的算法来处理这些数据。在面试中,最经常被提及的就是链表,因为它简单,但又因为需要对指针进行操作,凡是涉及到指针的,都需要我们具有良好的编程基础才能确保代码没有任何错误。链表是一种动态的数据结构,因为在创建链表时,我们不需要知道链表的长度,当插入一个结点时,只需要为该结点分配内存,然后调整指针的指向来确保新结点被连接到链表中。所以,它不像数组,内存是一次性分配完毕的,而是每添加一个结点分配一次内存。正是因为这点,所以它没有闲置的内存,比起数组,空间效率更高。
关于链表的一些问题总结如下:
1、环形链表插值:有一个整数val,如何在节点值有序的环形链表中插入一个节点值为val的节点,并且保证这个环形单链表依然有序。
2、访问单个节点的删除:实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。
3、链表分化:对于一个链表,我们需要用一个特定阈值完成对它的分化,使得小于等于这个值的结点移到前面,大于该值的结点在后面,同时保证两类结点内部的位置关系不变。
4、两个链表公共值打印:现有两个升序链表,且链表中均无重复元素。请设计一个高效的算法,打印两个链表的公共值部分。
5、链表K逆序:有一个单链表,请设计一个算法,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。
6、固定值清除:现在有一个单链表。链表中每个节点保存一个整数,再给定一个值val,把所有等于val的节点删掉。
7、判断链表是否为回文结构。
8、复杂链表复制。
9、如何判断一个单链表是否有环?有环的话返回进入环的第一个节点的值,无环的话返回-1。
10、无环单链表相交判断:现在有两个无环单链表,若两个链表的长度分别为m和n,请设计一个时间复杂度为O(n + m),额外空间复杂度为O(1)的算法,判断这两个链表是否相交。
11、有环单链表相交判断。
12、单链表相交判断。
在实际编程中,树是经常遇到的数据结构,树的逻辑非常简单:除了根结点外,其他每个结点都只有一个父结点,除了叶结点外,其他所有结点都有一个或多个子结点。父结点和子结点间用指针链接。树有很多种形式,最常见的是二叉树,每个结点最多只有两个子结点。二叉树中最重要的操作就是遍历,通常有中序遍历,前序遍历和后序遍历,简单一点讲,这三种遍历的区别就是根结点的遍历顺序问题,像是中序遍历就是左,根,右,而前序遍历是根,左,右,后序遍历则是左,右,根。复杂一点的遍历就是宽度优先遍历:先访问树的第一层结点,再访问树的第二层结点...一直到最下面一层结点。在同一层结点中,从左到右的顺序依次访问。
关于二叉树相关的问题总结如下:
1、以非递归的形式打印二叉树(先序,中序,后序)。
2、按层打印二叉树。
3、输入一棵二叉树的根结点,求该树的深度。
4、完全二叉树的判断。
5、平衡二叉树的判断。
6、输入某二叉树的前序遍历和中序遍历的结果,重建该二叉树。
7、输入n个整数,找出其中最小的k个数。
8、输入两棵二叉树A和B,判断B是不是A的子结构。
9、输入一个二叉树,输出它的镜像。
10、输入一个整数数组,判断该数组是否是某二叉搜索树的后序遍历的结果。
11、输入一棵二叉树和某个整数值,打印出二叉树中结点值的和为输入整数的所有路径,所谓的路径,是指从根结点开始到叶结点,形成一条路径。
12、输入一棵二叉树,将该树转化成一个排序的双向链表,要求不能创建任何新的结点,只能调整树中结点指针的指向。
字符串在编程中经常用到,常见的字符串相关题目和算法总结如下:
1、空格替换练习:请编写一个方法,将字符串中的空格全部替换为“%20”。
2、对于一个字符串,请设计一个算法,判断其是否为一个合法的括号串。
例如:"(()())", 返回 true; "()(()()",返回 false。
3、字符串移位练习题:例如:输入:"ABCDE",5,3,返回:"DEABC"。
4、把一个字符串的大写字母放到字符串后面,各个字符的相对位置不变,且不能申请额外空间。
5、句子的逆序练习题。例如:"dog loves pig",返回:"pig loves dog"。
6、词语变形词:两个字符串出现的种类和次数都一样。
7、旋转词:如果对于一个字符串A,将A的前面任意一部分挪到后边去形成的字符串称为A的旋转词。比如A="12345",A的旋转词有"12345","23451","34512","45123"和"51234"。对于两个字符串A和B,请判断A和B是否互为旋转词。给定两个字符串A和B及他们的长度lena,lenb,请返回一个bool值,代表他们是否互为旋转词。
8、最长无重复字串,例如 :"aabcb",返回:3。
9、KMP算法:两个串之间的匹配,核心思想是pre[i]表示串B的最长的前缀与以i为结尾的后缀相同,每次匹配失败时,从i跳到pre[i]就可以了。
10、Manacher算法:作用是处理出以每个位置为中心的最长回文串长度,核心思想是记录一个最长的延伸到的地方,利用对称的思想处理出一个当前位置的起始值,之后暴力匹配,比较神奇,时间复杂度很好证明。
11、Trie树:作用是存储许多个串,核心思想是除根节点每个点表示一个字母,从根节点到每个点的路径表示一个字符串,单词节点打上标记。
12、AC自动机:作用是多个串的匹配,KMP+Trie树,核心思想是fail[i]表示长度最长的某个前缀是i节点所代表的字符串的一个后缀,通过fail[i]处理出ch[x][i]表示x节点加入一个i字符后转移到的节点。注意可以结合dp来考察。
13、后缀数组:作用是对单个字符串后缀排序,求两个后缀的LCP,核心思想是sa数组和height数组的求法,详见论文,不再过多赘述。
14、后缀自动机:作用是对单串建立自动机,能够给接受该串的每个子串,核心思想是每个节点代表多个字符串,其中每个节点的right集合相同,记录一个fa[i]表示最小的right集合使i的right集合是它的子集,具体还是要看论文,应用更加广泛。
六、二分搜索
1、局部最小值位置,定义局部最小的概念。arr长度为1时,arr[0]是局部最小。arr的长度为N(N>1)时,如果arr[0]
2、元素最左出现:对于一个有序数组arr,再给定一个整数num,请在arr中找到num这个数出现的最左边的位置。
3、循环有序数组最小值
4、有一个有序数组arr,其中不含有重复元素,请找到满足arr[i]==i条件的最左的位置。
5、给定一棵完全二叉树的根节点root,返回这棵树的节点个数。
参考资料:
常见排序算法小结
面试常备---栈和队列总结篇
栈和队列算法总结
面试常备题---链表总结篇
链表各种操作总结
面试常备题---二叉树总结篇
二叉树算法总结
字符串专题小结
编程面试的10大算法概念汇总