时间复杂度也可以称为渐进时间复杂度,是算法执行时间随着数据规模增长的趋势。
时间复杂度从低到高O(1),O(logn),O(n),O(nlogn),O(n2),

编程语言数组下标从0开始,因为数组中元素的占内存的大小都是一样的,如果下标从0开始,用a[k]访问数组下标为k的元素,
a[k]_address = base_address + k*type_size
如果从1开始。

a[k]_address = base_address + (k-1)*type_size

也就是说如果从1开始还需执行一次减1的操作,所以方便起见数组大部分都从0开始。
数组是线性结构可以通过下标来实现随机访问。

链表主要有单链表,双向链表,循环链表。实际工作中双向链表比单链表使用广,主要是空间换时间的设计思想。双向链表主要是能解决单链表删除节点的问题。对于删除给定指针的节点,单链表需要O(n),双向链表只需要O(1),因为双向链表存储了前面节点的指针。还有就是在指定节点前后插入节点双向链表都是O(1),

单链表在插入时候,下面两行代码搞定
new_node->next = p->next;
p->next = new_node;
但是如果链表为空,我们就需要做一个判断
if (head == null) {
head = new_node;
}
删除也是这个道理,这时我们引入哨兵节点,这种链表叫带头链表。
栈可以用数组来实现叫顺序栈,也可以用链表来实现叫链式栈。动态扩容的顺序栈的平均时间复杂度是O(1),可用摊还分析法来分析,将k个数据均摊到k次入栈操作,那么每个入栈操作只需要一个数据移动和一个simple-push操作。

对于排序算法的执行效率,需要从三方面衡量
最好,最坏,平均时间复杂度
时间复杂度的系数,常数,低阶
比较次数和交换次数
排序算法的内存消耗,冒泡,插入,选择排序都是原地排序算法,空间复杂度是O(1)
排序算法的稳定性,排序之后相等元素的前后顺序不变,稳定排序的使用场景,如果我们要给电商系统的订单排序,订单有两个属性,下单时间和金额,如果我们需要一个先按照下单金额排序,然后按照下单时间排序。

归并排序不是原地排序需要额外申请空间,

线性排序,时间复杂度O(n):
桶排序 比较适合外部排序,先分桶,然后桶内快排
计数排序可以理解为桶排序的一种特殊情况。50万考生按照分数排序
基数排序 电话号码排序,

二分查找用数组实现,比起散列表,二叉树来剩空间,

redis的有序集合是用跳表实现的,跳表二分查找树的链表实现。跳表的插入通过随机函数来确保索引大小和数据大小的平衡性。跳表相对于红黑树来说好实现,而且查找区间的数据比较高效。

散列表就是数组支持按照下标随机访问的特性,所以散列表就是数组的一种扩展。
散列函数的三个基本要求:
散列函数得到的散列值是一个非负整数
key1=key2,hash(key1)==hash(key2)
key1!=key2,hash(key1)!=hash(key2)
第三点的就是散列冲突问题。解决方法就是开放寻址法(二次探测,双重散列)和链表。
装载因子,散列表的装载因子=填入元素个数/散列表的长度

散列表可以动态扩缩容,装载因子阈值的设置需要考虑时间和空间复杂度。动态扩容的时候可以考虑分批扩容而不是一次性全都导入到新的散列表里面。
jdk8中HashMap使用链表法解决散列冲突,当链表节点个数小于8的时候就是链表,大于8的时候就用红黑树。

散列表和链表一起使用如java的LinkedHashMap和python的有序字典,这里的链表是指双向链表,散列表插入删除查找都很快,如果我们希望按顺序访问的时候效率就很低,为了解决这个问题,散列表和链表经常一起使用

哈希算法的应用最常见的7种,安全加密,唯一标识,数据校验,散列函数,负载均衡,数据分片,分布式存储。

二叉树,完全二叉树是满二叉树的基础上再添加一层而且最后一层添加的节点都是靠左排列。
所有二叉树都可以用链式存储,完全二叉树可以用数组存储,比较省空间。

二叉树,遍历前序遍历,中序遍历,后序遍历,

支持重复数据的二叉查找树,两种方法,一种,每个节点除了存储数据还会通过链表和支持动态扩容的数组等数据结构,把值相同的数据都放存储到同一个节点上。第二种,在插入的时候放到这个节点的右子树,也就是说把这个新插入的数据当成大于这个数据的值来处理。

普通的二叉查找树在极端情况下的时间复杂度是O(n)(退化成了链表),一般是O(logn),

AVL平衡二叉树,查找效率很高,但是为了维持这种平衡,每次插入删除都比较复杂耗时,有频繁的插入和删除的情况使用AVL就不合适了。
红黑树只是做到了近似平衡,所以在维护的成本上比AVL要低。在各种场景下都比较稳定的平衡二叉查找树。实现比较复杂,实现的时候 一般用调表来实现。

堆排序,原地排序,时间复杂度为O(nlogn),1,堆排序的数据访问方式没有快速排序好,快排都是顺序访问,堆排序需要跳着访问(就是用数组来实现的时候),2,堆排序需要先建堆在排序,快排的数据交换次数不会比逆序度多。

堆排序应用,优先级队列,求top k,求中位数。
优先级队列:合并小文件,高性能定时器
中位数:通过维护一个大顶堆和一个小顶堆,来实现,99百分位响应时间也是这个意思

图存储,
邻接矩阵,存储方式简单,但是占用空间大,很多图的运算可以直接使用矩阵间的运算
邻接表,稀疏图,

字符串区配算法,
BF Brute Force,我们在字符串中A查找B,A就是主串,B就是模式串。BF简单不容易出错。处理小规模的比较好用
RK算法,通过对n-m+1的子串分别求哈希,然后与模式串对比就行了,这个哈希算法有技巧,可以用k进制数表示一个子串,然后转化成10进制数,这个十进制数就是子串的哈希值,这种算法,相邻的两个子串的哈希值有关系。我们还可以设计一个允许散列冲突的算法,这时散列函数可以用一个比较高效的(比如a对应1,b对应2然后所有数字相加,最后的和作为哈希值,这种哈希算法的数据范围就相对小很多,还可每个字母对应一个素数,这样哈希冲突就会少很多),对于这种有哈希冲突的,如果哈希值一样了,在比较一下子串和模式串本身就好了。

trie树,利用字符串之间的公共前缀,将重复的前缀合并在一起。

AC自动机 trie树+KMP字符串区配算法的结合

动态规划用数组实现,一个模型,三个特征。多阶段决策最优解模型,最优子结构,无后效性,重复子问题。两种动态规划解题思路,状态转移表,状态转移方程法。

bloom filter 布隆过滤器,基于位图,位图直接用二进制的数组,寻址快,省空间。如果用这个算法如果没有的话,就没有,如果有的话,可能没有。

向量空间,简单的音乐推荐系统,欧几里得距离。

https://time.geekbang.org/column/article/77830