剑指offer系列-简单总结


思维导图来自 数据结构与算法看这一篇就够了
在这里插入图片描述

1. 数据结构

1.1 链表

链表相关问题常常伴随着指针的使用。
面试题18 删除链表的节点

双指针

面试题24 反转链表

间隔k步的双指针

面试题22 链表中倒数第k个节点

倍速双指针

面试题23 链表中环的入口节点

消除链表长度差

面试题52 两个链表的第一个公共节点

建造伪节点

例如新建1个值为无穷小的节点
node = Node(float(’-inf’))

1.2 数组

pass

1.3 字符串

滑动窗口法,什么时候扩大窗口,什么时候缩小窗口,什么时候移动窗口。不是只适用于字符串。
朴素匹配法
KMP法

1.4 数字

求商 //
求余 %
右移1位 >> 1,相当于除以2
左移1位 1 <<,相当于乘以2
a & 1 判断a的奇偶,结果为1为奇,为0为偶数
a ^ a 异或自己的结果为0
a ^ 0 异或零的结果为本身
转换为字符串,通过字符串解决问题

1.5 栈(LIFO)

充分考虑站的后进先出特点

1.6 队列(FIFO)

充分考虑队列的先进先出特点。

1.7 哈希表

哈希表是一个用空间换时间的典型,因为哈希表的读取只需要O(1),所以在很多时候都是可以用哈希表来降低时间复杂度的。

需要注意的是,在python3.6中字典是有序的(按照插入顺序),具体的请查看这篇博客 为什么Python 3.6以后字典有序并且效率更高?

1.8 树

树相关的算法一般用递归、队列或栈。
树的问题使用递归时最好理解的,能使用递归的话,优先使用递归。

深度优先遍历(DFS)

深度优先遍历常常与栈相关。

广度优先遍历(BFS,或者叫层序遍历)

广度优先遍历常常与队列相关。

1.9 图

Dijkstra’s 算法

1.10 排序

详情见 图解十大经典排序算法

在这里插入图片描述

冒泡排序

最简单的一种排序算法。先从数组中找到最大值(或最小值)并放到数组最左端(或最右端),然后在剩下的数字中找到次大值(或次小值),以此类推,直到数组有序排列。算法的时间复杂度为O(n^2)。与选择排序不同的是,在每次遍历中,冒泡排序常常要交换多次,也就是一步步往上冒。

选择排序

从数组中找到最大值或者最小值并放到数组的最左端或最右端,每遍历1次才进行1次交换。

插入排序

插入排序的基本思想就是将无序序列插入到有序序列中。通常将数组的第一个元素看做有序数组,然后将后边的元素不断地插入到前面这个有序数组中。

快速排序

每次找一个基准值,左边的元素都比基准值小,右边的值都比基准值大。

一趟快速排序的具体做法为:设置两个指针low和high分别指向待排序列的开始和结尾,记录下基准值baseval(待排序列的第一个记录),然后先从high所指的位置向前搜索直到找到一个小于baseval的记录并互相交换,接着从low所指向的位置向后搜索直到找到一个大于baseval的记录并互相交换,重复这两个步骤直到low=high为止。

希尔排序

希尔排序(Shell’s Sort)也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

归并排序

归并字面上的意思是合并,归并算法的核心思想是分治法,就是将一个数组一刀切两半,递归切,直到切成单个元素,然后重新组装合并,单个元素合并成小数组,两个小数组合并成大数组,直到最终合并完成,排序完毕。

桶排序

桶排序是将待排序集合中处于同一个值域的元素存入同一个桶中,也就是根据元素值特性将集合拆分为多个区域,则拆分后形成的多个桶,从值域上看是处于有序状态的。对每个桶中元素进行排序,则所有桶中元素构成的集合是已排序的。

堆排序

堆排序可以说是一种利用了堆的概念来排序的选择排序。分为两种方法:

大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列。

小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列。

2. 常用算法

双指针法、对撞双指针法

对撞双指针就是,某些情况下左指针右移,某些情况下右指针左移,当两指针碰撞时退出循环。

三指针法

面试题49. 丑数

摩尔投票法

面试题39 数组中出现次数超过一半的数字

二分法

常常要求序列有序,当然也有变种。

递归、记忆化递归

常用于二叉树的相关问题,由于递归可能会存在重复计算问题,所以有了记忆化递归(将之前计算的结果保存起来,供直接使用,避免重复计算)

穷举法

这个没啥好说的,就是暴力法。

动态规划(dynamic programming,简称dp)

动态规划最关键的就是找到状态转移方程,也可以说成是递推公式,根据前面的某些状态推导出当前的状态。

贪心法

贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
解题的一般步骤是:
1.建立数学模型来描述问题;
2.把求解的问题分成若干个子问题;
3.对每一子问题求解,得到子问题的局部最优解;
4.把子问题的局部最优解合成原来问题的一个解。

分治法

分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。

回溯法

在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。

若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。

而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

分支限界法

回溯算法是深度优先,那么分支限界法就是广度优先的一个经典的例子。回溯法一般来说是遍历整个解空间,获取问题的所有解,而分支限界法则是获取一个解(一般来说要获取最优解)。

总结

我觉得,遇到一个算法题,一定要能对这个题目进行分类,就像高中时候做数学题一样,拿到这个题之后,要了解它的考点是什么,我应该用什么方法来解决,掌握了这一点就是把握住了大方向,就算最后还是做不出来也不会差的太远。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章