排序算法
冒泡排序 O(n**2)
将乱序列表中的最大值逐步找出,将最大值最终移动到最后的位置。
第一个值比第二个大,则将这两个值互换位置,较大的放在后面。
# 推导:只排序一次
def sort(alist):
for i in range(0,len(alist)-1):#循环n-1次,n就是列表元素的个数
if alist[i] > alist[i+1]:
alist[i],alist[i+1] = alist[i+1],alist[i]
print(alist)
alist = [3,8,5,7,6]
sort(alist) # [3, 5, 7, 6, 8]
将上述操作在逐步的作用(n-1次)
def sort(alist):
for j in range(0,len(alist)-1):
for i in range(0,len(alist)-1-j):#循环n-1次,n就是列表元素的个数
if alist[i] > alist[i+1]:
alist[i],alist[i+1] = alist[i+1],alist[i]
return alist
alist = [3,8,5,7,6]
print(sort(alist)) # [3, 5, 6, 7, 8]
选择排序 O(n**2)
直接将列表中的最大值找出,直接放置到列表最后的位置。
# 推导:只排序一次时
def sort(alist):
max_index = 0 # 最大值的下标,一开始假设第0个元素为最大值
for i in range(0,len(alist)-1): # 为了找出最大值的下标
if alist[max_index] < alist[i+1]:
max_index = i+1
alist[max_index],alist[len(alist)-1] = alist[len(alist)-1],alist[max_index] # 将最大值与最后一个值替换位置
return alist
alist = [3,8,5,7,6]
print(sort(alist)) # [3, 6, 5, 7, 8]
将上述操作在逐步的作用(n-1次):
def sort(alist):
for j in range(0,len(alist)-1):
max_index = 0 # 最大值的下标,一开始假设第0个元素为最大值
for i in range(0,len(alist)-1-j): # 为了找出最大值的下标
if alist[max_index] < alist[i+1]:
max_index = i+1
alist[max_index],alist[len(alist)-1-j] = alist[len(alist)-1-j],alist[max_index] # 将最大值与最后一个值替换位置
return alist
alist = [3,8,5,6,7]
print(sort(alist)) # [3, 5, 6, 7, 8]
插入排序 O(n**2)
-
将列表假设分成两部分,第一部分为列表的第一个元素(有序序列),剩下的元素(无序序列),以此将无序序列中的元素逐一插入到有序序列的合适位置
-
变量 i 表示列表下标,还表示有序序列中元素的个数
推导:
# 有序序列只有一个元素,无序序列元素个数为n-1
i = 1
# alist[i-1]:有序序列的最后一个元素
# alist[i]:无序序列的第一个元素
if alist[i] < alist[i-1]:
alist[i],alist[i-1] = alist[i-1],alist[i]
i = 2 # 有序序列有两个元素
# alist[i]:无序序列的第一个元素
# alist[i-1]:有序序列的最后一个元素
while i >= 1:
if alist[i] < alist[i-1]:
alist[i],alist[i-1] = alist[i-1],alist[i]
i -= 1
else:
break
最终完整的代码:自动处理 i (1:len)
def sort(alist):
for i in range(1,len(alist)):
while i >= 1:
if alist[i] < alist[i-1]:
# 无序序列的第一个元素小于有序序列的最后一个元素, 位置替换
alist[i],alist[i-1] = alist[i-1],alist[i]
i -= 1
else:
break
return alist
alist = [49,38,65,97,76,13,27]
print(sort(alist)) # [13, 27, 38, 49, 65, 76, 97]
希尔排序 <O(n**2)
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本,该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量(gap)”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率比直接插入排序有较大提高。
-
插入排序就是增量为1的希尔排序
-
gap:初始值元素个数整除2
- gap分组的组数,以及隔多少个元素分为一组
# 根据插入排序
def sort(alist):
gap = len(alist)//2
# 将下属所有的1换成gap
for i in range(gap,len(alist)):
while i >= gap:
if alist[i] < alist[i-gap]:
alist[i],alist[i-gap] = alist[i-gap],alist[i]
i -= gap
else:
break
return alist
完整:
def sort(alist):
gap = len(alist)//2
while gap >=1:
# 添加一个循环
for i in range(gap,len(alist)):
while i >= gap:
if alist[i] < alist[i-gap]:
alist[i],alist[i-gap]=alist[i-gap],alist[i]
i -= gap
else:
break
gap //= 2
return alist
alist = [49,38,65,97,76,13,27]
print(sort(alist)) # [13, 27, 38, 49, 65, 76, 97]
快速排序 O(nlogn)
- 将列表中第一个元素设定为基准数字,赋值给mid变量,然后将整个列表中比基准小的数值放在基准的左侧,比基准到的数字放在基准右侧。然后将基准数字左右两侧的序列在根据此方法进行排放。
- 定义两个指针,low指向最左侧,high指向最右侧
- 然后对最右侧指针进行向左移动,移动法则是,如果指针指向的数值比基准小,则将指针指向的数字移动到基准数字原始的位置,否则继续移动指针。
- 如果最右侧指针指向的数值移动到基准位置时,开始移动最左侧指针,将其向右移动,如果该指针指向的数值大于基准则将该数值移动到最右侧指针指向的位置,然后停止移动。
- 如果左右侧指针重复则,将基准放入左右指针重复的位置,则基准左侧为比其小的数值,右侧为比其大的数值。
基数:初始值就是列表中的第一个元素
- 操作:将整个列表元素中比基数小的值全部放置到基数的左侧,将比基数大的值全部放置到基数的右侧
#将基数放置到合适的位置:基数左侧为比基数小的数值,基数右侧为比基数大的数值
def sort(alist,start,end):
low = start
high = end
if low > high: # 结束递归的条件
return
mid = alist[low] # 基数
while low < high:
while low < high:
if alist[high] > mid:
high -= 1 # high向左偏移
else:
alist[low] = alist[high]
break
while low < high:
if alist[low] < mid:
low += 1 # low向右偏移
else:
alist[high] = alist[low]
break
if low == high:
alist[low] = mid
# 递归 high=low 都指向基准值
sort(alist,start,high-1) # 处理基数左部分的乱序序列
sort(alist,low+1,end) # 处理基数右部分的乱序序列
return alist
归并排序 O(nlogn)
归并排序采用分而治之的原理:
-
将一个序列从中间位置分成两个序列;
-
在将这两个子序列按照第一步继续二分下去;
-
直到所有子序列的长度都为1,也就是不可以再二分截止。这时候再两两合并成一个有序序列即可。
- 如何合并?
下图中的倒数第三行表示为第一次合并后的数据。其中一组数据为 4 8 , 5 7。该两组数据合并方式为:每一小组数据中指定一个指针,指针指向每小组数据的第一个元素,通过指针的偏移指定数据进行有序排列。排列情况如下:
1. p1指向4,p2指向5,p1和p2指向的元素4和5进行比较,较小的数据归并到一个新的列表中。经过比较p1指向的4会被添加到新的列表中,则p1向后偏移一位,指向了8,p2不变。
2.p1和p2指向的元素8,5继续比较,则p2指向的5较小,添加到新列表中,p2向后偏移一位,指向了7。
3.p1和p2指向的元素8,7继续比较,7添加到新列表中,p2偏移指向NULL,比较结束。
4.最后剩下的指针指向的数据(包含该指针指向数据后面所有的数据)直接添加到新列表中即可。
def merge_sort(alist):
n = len(alist)
#结束递归的条件
if n <= 1:
return alist
#中间索引
mid = n//2
left_li = merge_sort(alist[:mid])
right_li = merge_sort(alist[mid:])
#指向左右表中第一个元素的指针
left_pointer,right_pointer = 0,0
#合并数据对应的列表:该表中存储的为排序后的数据
result = []
while left_pointer < len(left_li) and right_pointer < len(right_li):
#比较最小集合中的元素,将最小元素添加到result列表中
if left_li[left_pointer] < right_li[right_pointer]:
result.append(left_li[left_pointer])
left_pointer += 1
else:
result.append(right_li[right_pointer])
right_pointer += 1
#当左右表的某一个表的指针偏移到末尾的时候,比较大小结束,将另一张表中的数据(有序)添加到result中
result += left_li[left_pointer:]
result += right_li[right_pointer:]
return result
alist = [3,8,5,7,6]
print(merge_sort(alist))