常用数据结构
顺序表
数据结构中的名词,在C中用数组实现
链表
C中用带指针的结构体实现
栈
运算受限的线性表,特性是先进后出
队列
特性是先进先出
将尾结点指向头结点,即为循环队列
二叉树
每个节点最多有两个子树的树结构
满二叉树
叶子节点全满
完全二叉树
只允许最后一层有空缺节点且空缺在右边,即叶子节点只能在层次最大的两层上出现
平衡二叉树(AVL)
1.任一节点对应的两棵子树的最大高度差为 1
2.查找、插入和删除在平均和最坏情况下的时间复杂度都是 O(log2n)。增加和删除元素的操作则可能需要借由一次或多次树旋转,以实现树的重新平衡
红黑树
1.可以在 O(log2n)时间内完成查找,插入和删除
2.C++的 STL,包括 Java的数据结构中都大量用到红黑树
3.红黑树相对于 AVL 树的时间复杂度是一样的,但是优势是当插入或者删除节点时,红黑树实际的调整次数更少,旋转次数更少,因此红黑树插入删除的效率高于 AVL 树,大量中间件产品中使用了红黑树
4.牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于 AVL 树
5.每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。红黑树具有以下规则:
1)节点是红色或黑色
2)根是黑色
3)所有叶子都是黑色
4)每个红色节点必须有两个黑色的子节点
5)从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点
6. 这些规则确保了红黑树的关键特性:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长
常用算法
时间复杂度与空间复杂度
1.时间复杂度是一个函数,它定量描述了该算法的运行时间
2.空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度
查找算法
二分查找
每次从中间找,直到找到,O(logn)
#include <stdlib.h>
void *bsearch( const void *key, const void *buf, size_t num, size_t size, int (*compare)(const void *, const void *) );
哈希查找
根据键直接访问相应的数据结构,O(1)
排序算法
不稳定并不是指排序不成功,而是相同大小的数字,在排序后发生了位置交换
插入类:插入排序、希尔排序
1)插入排序:插入排序首先就是将第一个数看成有序的序列,然后把后面的数看成要依次插入的序列,O(n2)
2)希尔排序:设置步长gap,一开始为数组长度的1/2,然后每次按1/2递减,直到为1。每次将相隔gap-1个的所有数进行排序,O(n1.3)
选择类:选择排序、堆排序
1)选择排序:通过一个变量时刻记录最值,一轮比较下来,将最值再和最后的元素交换,这样一轮比较就只需要交换一次,O(n2)
2)堆排序:将数组放入完全二叉树,然后建立大(小)根堆,会得到一个最值,交换堆顶和最值,重新建立,直到排完,O(nlogn)
交换类:冒泡排序、快速排序
1)冒泡排序:通过一轮比较,把最大的放在最后面,然后将剩余的元素再进行一趟比较,再把最大的放在最后面,这样不断进行,最终实现数组有序,O(n2)
2)快速排序:找到数组中一个分割值,把比分割值小的数都放在数组的左边,把分割值大的数放在数组的右边,这样分割值的位置就确定下来了,数组一分为二,继续该操作,O(nlogn)
非递归版本的快排
<stdlib.h>
void qsort( void *buf, size_t num, size_t size, int (*compare)(const void *, const void *) );
对 buf 指向的数据(包含 num 项,每项的大小为 size)进行快速排序。如果函数compare 的第一个参数小于第二个参数,返回负值;如果等于返回零值;如果大于返回正值,函数对 buf 指向的数据按升序排序。qsort 的本质是其内部实现了快排,但是需要我们告诉它任意两个元素如何比较,因为 qsort 可以排序各种类型的数组,因此我们通过 compare函数将比较规则告诉 qsort,qsort 需要一个比较规则,qsort 函数是可以排序任意类型的数组的,当我们排整型数组和排结构体数组,排序的规则自然是不一样的
归并类:归并排序
先将数组分为n/2个,将每个分组进行排序,再将分组的两两进行排序,直到所有元素排序完毕。分治法,O(nlogn),但空间复杂度是O(n)
分配类:计数排序
统计待排序列的最小值与最大值,记录这一范围,以这一范围为大小开辟一个新的数组,然后统计原数组,每存在一个值在新数组上+1计数。统计完成后,新数组每有一个计数,将其下标放回原数组。O(n+m)
贪心
在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是某种意义上的局部最优解,适用于局部最优策略能导致产生全局最优解的场景,但实际运用较少
递归
递去:将递归问题分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决
归来:当你将问题不断缩小规模递去的时候,必须有一个明确的结束递去的临界点(递归出口),一旦达到这个临界点即就从该点原路返回到原点,最终问题得到解决
回溯
把问题的解空间转化成了图或者树的结构表示,然后使用深度优先搜索策略进行遍历,遍历的过程中记录和寻找所有可行解或者最优解。当探索到某一步时,发现原先选择并不优或达不到目标,就退回到上一步,重新选择
动态规划
给定一个问题,我们把它拆成一个个子问题,直到子问题可以直接解决。然后呢,把子问题答案保存起来,以减少重复计算。再根据子问题答案反推,得出原问题解的一种方法