第一大类:基本排序算法
一、选择排序(selection sort)
打个比方:妈妈跟小朋友玩扑克牌,牌面数字分别是3、5、8、10、4
- round 1: 妈妈问:“5个数字里哪个最小呀?”,小朋友回答:“3”,“对!”,于是妈妈拿走了3。
- round 2:妈妈再问:“剩下的4个数字里哪个最小呀?”,小朋友再回答:“4”,妈妈再拿走了4。
- round 3:“3个里面哪个最小?”,“5”,拿走5。
- round 4:“2个里面哪个最小?”,“8”,拿走8。
- round 5:最后,妈妈排好了序:3 4 5 8 10 。
>>核心思想:搜索整个列表,找到最小项的位置,如果该位置不是列表的第1个位置,则交换这两个位置的项;然后回到第2个位置,搜索除第1个位置外的剩余列表,找到最小项的位置,如果该位置不是列表的第2个位置,则交换这两个位置;如此重复,直到末项。
>>为什么叫选择排序?因为,每次会选择出一个最小项。
1.1 代码示例
"""
This function has no return and the original list is changed
"""
def selectionSort(lst):
n=len(lst)
if n>1:
for i in range(n-1):
minIndex=i
for j in range(i+1,n):
if lst[j]<lst[minIndex]:
minIndex=j
if minIndex!=i:
lst[i],lst[minIndex]=lst[minIndex],lst[i]
1.2 时间复杂度
(1) 最好情况:
O
(
n
2
)
O(n^2)
O(n2)
(2) 最坏情况:
O
(
n
2
)
O(n^2)
O(n2)
(3) 平均情况:
O
(
n
2
)
O(n^2)
O(n2)
情况 | 情况分析 |
---|---|
最好情况 | 如果列表本身已经排好序,那么需执行 n − 1 n-1 n−1次寻找最小项的操作,每次执行 n − 1 、 n − 2 、 . . . 、 1 n-1、n-2、...、1 n−1、n−2、...、1次比较操作,即: O ( n 2 ) O(n^2) O(n2) |
最坏情况 | 如果列表是倒序的,那么算法需执行 n n n次寻找最小项的操作,且每次需分别执行 n − 1 n-1 n−1、 n − 2 n-2 n−2、… 1 1 1次赋值操作和 1 1 1次位置交换操作,即: O ( n 2 ) O(n^2) O(n2) |
平均情况 | O ( n 2 ) O(n^2) O(n2) |
1.3 算法过程
以
l
s
t
=
[
1
,
3
,
4
,
8
,
9
,
2
,
5
]
为
例
lst=[1,3,4,8,9,2,5]为例
lst=[1,3,4,8,9,2,5]为例:
二、冒泡排序(bubble sort)
妈妈跟小朋友继续用五张牌(3、5、8、10、4)玩游戏:
- round 1:妈妈问:“3和5哪个大呀?”,答:“5大。”, 牌面不动。
- round 2:“5和8哪个大呀?”,答:“8大。”,牌面不动。
- round 3:“8和10哪个大呀?”,答:“10大。”,牌面不动。
- round 4:“10和4哪个大呀?”,答:“10大。”,妈妈交换了4和10的位置,把10放一边:3、5、8、4、10。
- round 5:妈妈再从头问一遍:“3和5?”,答:“5”,牌面不动。(虽然有点傻)
- round 6:“5和8?”,“8”,牌面不动。(傻 2 ^2 2)
- round 7:“8和4?”,“8“,交换8和4的位置,把8放一边:3、5、4、8、10。
- round 8:从头再来一遍:“3和5?”,“5”,牌面不动。
- round 9:“5和4?”,“5”,交换5和4的位置。
- round 10:“5和8?”,“8”,牌面不动,把5放一边:3、4、5、8、10。
- round 11:“3和4?”,“4”,牌面不动,结束。
>>核心思想: repeatedly compare and swap ( if necesarily) two elements. (循环遍历输入列表进行两两比较,依次获得最大/小值)
2.1 代码示例
def bubbleSort(lst):
n=len(lst)
while n>1:
for i in range(n-1):
if lst[i]>lst[i+1]:
lst[i],lst[i+1]=lst[i+1],lst[i]
n-=1
2.2 时间复杂度
(1) 最好情况:
O
(
n
2
)
O(n^2)
O(n2)
(2) 最坏情况:
O
(
n
2
)
O(n^2)
O(n2)
(3) 平均情况:
O
(
n
2
)
O(n^2)
O(n2)
情况 | 情况分析 |
---|---|
无论什么情况 | 算法都需执行 n − 1 n-1 n−1次冒泡操作,每次冒泡分别需执行 n − 1 、 n − 2... 、 1 n-1、n-2...、1 n−1、n−2...、1次比较操作(+交换操作),即: O ( n 2 ) O(n^2) O(n2) |
2.3 算法过程
以
l
s
t
=
[
1
,
3
,
4
,
8
,
2
,
5
,
9
]
lst=[1,3,4,8,2,5,9]
lst=[1,3,4,8,2,5,9]为例:
三、插入排序(insertion sort)
这个算法类似于抓扑克牌:将新抓的1张牌插入手中牌里正确的位置,显然,手里的牌是排好序的,关键在于如何正确判断新牌的正确位置。
我们假设手里有:
1
、
2
、
3
、
8
1、2、3、8
1、2、3、8 四张牌,新的牌为
5
5
5,先判断
5
5
5与
8
8
8的关系(
5
<
8
5<8
5<8),继续判断
5
5
5与
3
3
3的关系(
5
>
3
5>3
5>3),至此可以判断
5
5
5应该插入到
3
3
3后面的位置,
5
5
5一定比
<
3
<3
<3的前序数字大。>>>即,新数字插入第一个比它小的数字后面。
3.1 算法示例
def insertionSort(lst):
n=len(lst)
if n>1:
for i in range(1,n):
j=i-1
while(j>=0):
if lst[i]<lst[j]:
j-=1
else:
break
lst.insert(j+1,lst.pop(i))
3.2 时间复杂度
(1) 最好情况:
O
(
n
)
O(n)
O(n)
(2) 最坏情况:
O
(
n
2
)
O(n^2)
O(n2)
(3) 平均情况:
O
(
n
2
)
O(n^2)
O(n2)
情况 | 情况分析 |
---|---|
最好情况 | 如果列表本身已经排好序,那么只需执行 n − 1 n-1 n−1次比较操作,即: O ( n ) O(n) O(n) |
最坏情况 | 如果列表是倒序的,那么算法需分别执行 1 1 1、 2 2 2、… n − 1 n-1 n−1次比较操作和1次插入操作,即: O ( n 2 ) O(n^2) O(n2) |
平均情况 | 从最好情况到最坏情况,关键在于比较操作次数,比较操作平均次数近似等于 ( 1 + . . . + n ( n − 1 ) / 2 ) / ( n ( n − 1 ) / 2 ) 即 : (1+...+n(n-1)/2)/(n(n-1)/2)即: (1+...+n(n−1)/2)/(n(n−1)/2)即:O(n^2)$ |
第二大类快速排序算法
一、算法策略
快速排序算法是基于分而治之(divide-and-conquer)的思想,大致策略如下:
- 从列表中选取一项作为基准点(可以是任意项、首项、尾项、中间项等各种选取方法);
- 以基准点为基准,划分列表,使得所有小于基准点的项移动到基准点左边,其他项移动到基准点右边,一次划分结束后,基准点的位置就确定了;
- 分而治之,对在基准点分割列表后形成的子列表递归地重复上述两个步骤,直至子列表少于2项,此时结束。
二、算法特点
- 基准点选取方法的不同会使得每次分割后形成的2个子列表长度不同,进而导致算法时间复杂度的差异。例如:若每次分割后的子列表长度相等,即实现了“二分”,那么,经过
l
o
g
n
logn
logn此分割后结束,算法性能为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn);若每次分割形成长度为1和长度为X-1的子列表,则大约需
n
(
n
−
1
)
/
2
n(n-1)/2
n(n−1)/2次后结束,算法性能为
O
(
n
2
)
O(n^2)
O(n2)。
因此,快速排序策略的时间复杂度如下:- 最好情况: O ( n l o g n ) O(nlogn) O(nlogn)
- 最坏情况: O ( n 2 ) O(n^2) O(n2)
四、归并排序(merge sort)
>>核心思想:分治法,sort blocks of 1’s into 2’s, 2’s into 4’s, etc, on each pass merging sorted blocks into sorted larger blocks.(将输入列表分为长度1为的n个子序列,循环进行两两合并排序)
>>Note: This is the theoretical best-possible Big-O for comparison-based sorting! (理论上,归并排序是时间复杂度最优的基于比较策略的排序算法)
-
时间复杂度: O ( N l o g N ) O(NlogN) O(NlogN)。
-
第一步,将 N N N个元素看作 N N N个子序列,将 N N N个子序列两两合并并排序,长度为 2 2 2的 N / 2 N/2 N/2个子序列均得到了排序。
-
第二步,将 N / 2 N/2 N/2个子序列两两合并并排序,长度为 2 2 2^2 22的 N / 2 2 N/2^2 N/22个子序列得到了排序。
-
…
-
第 k k k步,将2个子序列合并排序,即长度为 2 k = N 2^k=N 2k=N的序列得到了排序。
显然, k = l o g 2 N k=log_2N k=log2N,每一步需进行复杂度为 O ( N ) O(N) O(N)的比较和拷贝等操作, k ∗ O ( N ) = O ( N l o g N ) k*O(N)=O(NlogN) k∗O(N)=O(NlogN)。这里, l o g N 表 示 l o g 2 N logN表示log_2N logN表示log2N。
注:https://blog.csdn.net/xiaozhimonica/article/details/89470129 。