常见算法
Posted Jeremy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常见算法相关的知识,希望对你有一定的参考价值。
算法与数据结构是面试考察的重中之重,也是日后刷题时需要着重训练的部分。
简单的总结一下,大约有这些内容:
算法 - Algorithms
1、排序算法:快速排序、归并排序、计数排序
2、搜索算法:回溯、递归、剪枝技巧
3、图论:最短路、最小生成树、网络流建模
4、动态规划:背包问题、最长子序列、计数问题
5、基础技巧:分治、倍增、二分、贪心
数据结构 - Data Structures
1、数组与链表:单/双向链表、跳舞链
2、栈与对列
3、树与图:最近公共祖先、并查集
4、哈希表
5、堆:大/小根堆、可并堆
6、字符串:字典树、后缀树
递归与迭代的区别
递归(recursion):递归常被用来描述以自相似方法重复事物的过程,在数学和计算机科学中,指的是在函数定义中使用函数自身的方法。(A调用A)
迭代(iteration):重复反馈过程的活动,每一次迭代的结果会作为下一次迭代的初始值。(A重复调用B)
递归是一个树结构,从字面可以其理解为重复“递推”和“回归”的过程,当“递推”到达底部时就会开始“回归”,其过程相当于树的深度优先遍历。
迭代是一个环结构,从初始状态开始,每次迭代都遍历这个环,并更新状态,多次迭代直到到达结束状态。
# 理论上递归和迭代时间复杂度方面是一样的,但实际应用中(函数调用和函数调用堆栈的开销)递归比迭代效率要低。
链接:https://www.jianshu.com/p/32bcc45efd32
来源:简书
算法的时间复杂度和空间复杂度
-
时间复杂度和空间复杂度是用来评价算法效率高低的2个标准。
-
时间复杂度:就是说执行算法需要消耗的时间长短,越快越好。比如你在电脑上打开计算器,如果一个普通的运算要消耗1分钟时间,那谁还会用它呢,还不如自己口算呢。
-
空间复杂度:就是说执行当前算法需要消耗的存储空间大小,也是越少越好。本来计算机的存储资源就是有限的,如果你的算法总是需要耗费很大的存储空间,这样也会给机器带来很大的负担。
时间复杂度的计算
表示方法
我们一般用“大O符号表示法”来表示时间复杂度:T(n) = O(f(n)) n是影响复杂度变化的因子,f(n)是复杂度具体的算法。
常见的时间复杂度量级
-
常数阶O(1)
-
线性阶O(n)
-
对数阶O(logN)
-
线性对数阶O(nlogN)
-
平方阶O(n²)
-
立方阶O(n³)
-
K次方阶O(n^k)
-
指数阶(2^n)
常见的时间复杂度(按效率排序) O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)
接下来再看一下不同的复杂度所对应的算法类型。
常数阶O(1)
int a = 1;
int b = 2;
int c = 3;
线性阶O(n)
for(i = 1; i <= n; i++) {
j = i;
j++;
}
对数阶O(logN)
int i = 1;
while(i < n) {
i = i * 2;
}
线性对数阶O(nlogN)
for(m = 1; m < n; m++) {
i = 1;
while(i < n) {
i = i * 2;
}
}
平方阶O(n²)
for(x = 1; i <= n; x++){
for(i = 1; i <= n; i++) {
j = i;
j++;
}
}
空间复杂度计算
空间复杂度 O(1)
如果算法执行所需要的临时空间不随着某个变量n的大小而变化,即此算法空间复杂度为一个常量,可表示为 O(1)。
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
代码中的 i、j、m 所分配的空间都不随着处理数据量变化,因此它的空间复杂度 S(n) = O(1)。
空间复杂度 O(n)
int[] m = new int[n]
for(i = 1; i <= n; ++i) {
j = i;
j++;
}
这段代码中,第一行new了一个数组出来,这个数据占用的大小为n,后面虽然有循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,即 S(n) = O(n)。
十大经典排序算法
# 冒泡排序
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
import sys
sys.setrecursionlimit(1000000)
## 冒泡排序 (******)
### 时间复杂度:O(n^2)
def Bubble_sort(li):
for i in range(len(li)-1):
for j in range(len(li)-1-i):
if li[j] > li[j+1]:
li[j], li[j+1] = li[j+1], li[j
return li
## 选择排序
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
#### 时间复杂度:O(n^2)
def select_sort(li):
for i in range(len(li)):
minLoc = i ###i = 0
for j in range(i+1, len(li)):
if li[j] < li[minLoc]:
li[j], li[minLoc] = li[minLoc], li[j]
return li
##### 插入排序(打扑克牌)
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
#### 时间复杂度: O(n^2)
def insert_sort(li):
for i in range(1, len(li)):
tmp = li[i]
j = i - 1
while j >=0 and li[j] > tmp:
li[j+1] = li[j]
j = j - 1
li[j+1] = tmp
## 快速排序
1、从数列中挑出一个元素,称为 "基准"(pivot);
2、重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
def partition(li, left, right):
tmp = li[left]
while left < right:
while left < right and li[right] >= tmp:
right = right - 1
li[left] = li[right]
while left < right and li[