一、插入排序
插入排序:每次将一个待排序记录按关键字大小插入到前面已排好的序列中。
如图所示:
意味着待排元素前面已经部分有序,所以先和前一个元素相比,1、若待排元素比前面的大则位置不变,2、若比前面元素小,则先用一个哨兵将此待排元素记录下来,前面比哨兵大的数都依次往后挪,直到碰到比哨兵小的数,将此位置的值替换为哨兵。
def InsertSort(a):
for i in range(2,len(a)): ## 数组位置从1开始
if(a[i]<a[i-1]): ## 当前位置元素于前一个相比(前面已有序)
temp = a[i] ## 暂存数组
for j in range(i-1,0,-1):
if(temp < a[j]):
a[j+1]=a[j] ## 依次往后挪(注意!不是交换!插入排序的一大特点就是往后挪!)
k = j ## 用k记录下当前位置
a[k] = temp
print(a[1:])
Tip: 插入排序的一大特点就是往后挪!而不是交换!!!!!!!
a = [0,7,2,24,26,11,5,1,6] ## 前面那个0没有数值含义,只是为了让数组从1开始
InsertSort(a)
输出:
[2, 7, 24, 26, 11, 5, 1, 6]
[2, 7, 11, 24, 26, 5, 1, 6]
[2, 5, 7, 11, 24, 26, 1, 6]
[1, 2, 5, 7, 11, 24, 26, 6]
[1, 2, 5, 6, 7, 11, 24, 26]
符合插入排序特点。
空间复杂度:O(1)
时间复杂度:O(n^2)
稳定性:稳定
二、希尔排序
希尔排序:先追求部分有序,再逼近全局有序
先将表分成若干子表,对各子表进行直接插入排序,缩小增量d,直到d=1
def ShellSort(a):
for d in range(int(len(a)/2),0,-1): ## 从数组长度一半开始划分(小数向下取整)
for i in range(d+1,len(a)): ## 从后一个往前判断
if(a[i]<a[i-d]): ## 若逆序,则子表按插入排序
temp = a[i] ## 记录数组(将小的记录下来)
for j in range(i-d,0,-d): ## 依次往后挪
if(temp<a[j]):
a[j+d] = a[j]
k = j
a[k] = temp
print(a[1:])
a = [0,7,2,24,26,11,5,1,6]
ShellSort(a)
输出:
[7, 2, 1, 6, 11, 5, 24, 26]
[6, 2, 1, 7, 11, 5, 24, 26]
[1, 2, 6, 5, 11, 7, 24, 26]
[1, 2, 5, 6, 7, 11, 24, 26]
符合希尔排序特点。
(该代码使用for i in range(d+1,len(a)),意为i ++ ,是子表来回切换判断,而不是处理完一个子表再处理下一个)
空间复杂度:O(1)
时间复杂度:最坏O(n^2),当n在某个范围时,O(n ^ 1.3),平均:O(n ^ 1.3)
稳定性:不稳定
适用性:仅用于顺序表
三、冒泡排序
原理:
- 从后往前两两比较,若为逆序则交换,最多n-1 趟
- 每一趟都使一个元素到最终位置
- 若某一趟过程未发生交换,则可提前结束
def BubbleSort(a):
for i in range(0,len(a)):
flag = 0 ## 标记本趟是否发生交换
for j in range(2,len(a)-i):
if(a[j]<a[j-1]): ## 若为逆序,交换
temp = a[j] ## 注意当前指针为j,与j-1相比,这样指针j永远指向大的数
a[j] = a[j-1]
a[j-1] = temp
flag = 1 ## 本趟发生交换
if(flag==0): ## 本趟未发生交换,提前结束
break
print(a)
python中交换两个数的位置还有一种方法:
so,交换部分可以改成:
def BubbleSort(a):
for i in range(0,len(a)):
flag = 0
for j in range(2,len(a)-i):
if(a[j]<a[j-1]):
a[j],a[j-1] = a[j-1],a[j]
flag = 1
if(flag==0):
break
print(a)
运行:
a = [0,7,2,24,26,11,5,1,6]
BubbleSort(a)
[0, 2, 7, 24, 11, 5, 1, 6, 26]
[0, 2, 7, 11, 5, 1, 6, 24, 26]
[0, 2, 7, 5, 1, 6, 11, 24, 26]
[0, 2, 5, 1, 6, 7, 11, 24, 26]
[0, 2, 1, 5, 6, 7, 11, 24, 26]
[0, 1, 2, 5, 6, 7, 11, 24, 26]
符合冒泡排序特点。
空间复杂度:O(1)
时间复杂度:O(n^2)
稳定性:稳定
适用:顺序表,链表
四、快速排序
任取一个元素作为枢轴,更小元素 < 枢轴 < 更大元素(划分为两部分)
def QuickSort(a, low, high):
if(low < high): ## 递归跳出条件
s = Partition(a, low, high) ## 一次划分
QuickSort(a, low, s-1) ## 左子表划分
QuickSort(a, s+1, high) ## 右子表划分
def Partition(a, low, high): ## 划分函数
k = a[high] ## 将枢轴暂存
while(low<high):
while(a[low]<k and low<high):
low = low+1
a[high] = a[low]
while(a[high]>k and low<high):
high = high-1
a[low] = a[high]
a[low] = k
print(a[1:])
return low
调用:
a = [0,7,2,24,26,11,5,1,6,12]
QuickSort(a,0,len(a)-1)
[7, 2, 6, 1, 11, 5, 12, 26, 24]
[1, 2, 5, 6, 11, 7, 12, 26, 24]
[1, 2, 5, 6, 11, 7, 12, 26, 24]
[1, 2, 5, 6, 11, 7, 12, 26, 24]
[1, 2, 5, 6, 7, 11, 12, 26, 24]
[1, 2, 5, 6, 7, 11, 12, 24, 26]
满足快速排序特点。
空间复杂度:O(递归层数){最好:O(log2n),最坏:O(n)}
时间复杂度:O(n递归层数){最好:O(nlog2n),最坏:O(n^n)}
稳定性:不稳定
五、选择排序
原理:每一趟在列表中选取关键字最小的元素加入有序子序列,总共进行 n - 1 趟处理。
def SelectSort(a):
for i in range(1,len(a)): ## n-1趟
flag = 0 ## 标记是否有比当前更小的数
k = i ## 记录最小元素位置
for j in range(i+1,len(a)):
if(a[j]<a[k]): ## 比当前最小还小,记录位置
k = j
flag=1
if(flag ==1): ## flag==1,交换
a[i],a[k] = a[k],a[i]
print(a[1:])
运行:
a = [0,7,2,24,26,11,5,1,6]
SelectSort(a)
输出:
[1, 2, 24, 26, 11, 5, 7, 6]
[1, 2, 24, 26, 11, 5, 7, 6]
[1, 2, 5, 26, 11, 24, 7, 6]
[1, 2, 5, 6, 11, 24, 7, 26]
[1, 2, 5, 6, 7, 24, 11, 26]
[1, 2, 5, 6, 7, 11, 24, 26]
[1, 2, 5, 6, 7, 11, 24, 26]
[1, 2, 5, 6, 7, 11, 24, 26]
符合选择排序的特点。
空间复杂度:O(1)
时间复杂度:O(n^2)
稳定性:不稳定
适用性:顺序表、链表