冒泡排序
在第一遍扫描
的时候,
比较第一个位置
和第二个位置
,因为54>26
,所以进行交换;
比较第二个位置
和第三个位置
,因为54<93
,所以不发生交换;
比较第三个位置
和第四个位置
,因为93>17
,所以进行交换;
以此类推,在第一遍扫描结束的时候,可以把93放在最后
。
写法一
def bubbleSort(alist):
for passnum in range(len(alist) - 1, 0, -1):
for i in range(passnum):
if alist[i] > alist[i + 1]:
alist[i], alist[i + 1] = alist[i + 1], alist[i]
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
bubbleSort(alist)
print(alist)
由于alist
的长度是9
,所以passnum
的取值范围是{8,7,6,5,4,3,2,1}
第一遍排序的时候,passnum=8
,i
的取值范围是0~7
,而if
语句中判断的是alist[i]>alist[i+1]
, 所以是在a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]
之间依次两两进行比较,最后会把最大的数排在a[8]
的位置;
第二遍排序的时候,passnum=7
,i
的取值范围是0~6
,而if
语句中判断的是alist[i]>alist[i+1]
, 所以是在a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]
之间依次两两进行比较,最后会把最大的数排在a[7]
的位置;
在遍历passnum的过程中,每次都会把余下列表中最大的数排在后面,最终实现排序操作。
写法二
对于上面遍历passnum的for循环,也可以写成while循环的形式
# 等同于上面的冒泡排序
def bubbleSort(alist):
passnum = len(alist) - 1
while passnum > 0:
for i in range(passnum):
if alist[i] > alist[i + 1]:
alist[i], alist[i + 1] = alist[i + 1], alist[i]
passnum -= 1
alist = [54,26,93,17,77,31,44,55,20]
bubbleSort(alist)
print(alist)
短冒泡排序
由于冒泡排序总是需要对元素之间两两进行比较,并决定是否交换,那么当列表本来就是有序时,仍然这样比较一遍,是很浪费时间资源的,于是存在一种短冒泡排序的方法。增加一个标记,如果在一遍排序中,并没有发生交换,说明列表已经有序,后面的操作就不需要了。
# 与上面的冒泡排序不同的是,增加了一个exchanges,来标记是否进行交换
def shortBubbleSort(alist):
exchanges = True
passnum = len(alist) - 1
while passnum > 0 and exchanges:
exchanges = False
for i in range(passnum):
if alist[i] > alist[i + 1]:
exchanges = True
alist[i], alist[i + 1] = alist[i + 1], alist[i]
passnum = passnum - 1
可以看到,和冒泡排序的写法二
差不多,区别在于增加了exchanges
这个标记项,以及while
循环的判断语句。
- 首先标记
exchanges=True
。 - 进入
while
循环,再设置exchanges=False
。 - 如果列表
已经有序
,那么未发生元素交换,exchanges
依然是False
,不满足while
的循环条件,所以退出循环。 - 如果列表
不是有序的
,那么exchanges=True
,可以进入下一轮的while
循环
参考:
Short Bubble Sort Algorithm ( youtube )
https://runestone.academy/runestone/static/pythonds/SortSearch/TheBubbleSort.html
选择排序
选择排序在冒泡排序的基础上进行了改进,每一遍扫描只需要进行一次交换。选择排序在每一次扫描的时候,都找出最大值,并将其放在合适的位置上。
第一遍扫描(alist[:]
),93
最大,和最后的20
进行交换;
第二遍扫描(alist[:-1]
),77
最大,和倒数第二个位置的55
交换;
第三遍扫描(alist[:-2]
),55
最大,和倒数第三个位置的44
交换;
以此类推
写法一
def selectionSort(alist):
for fillslot in range(len(alist)-1,0,-1):
positionOfMax = 0
# 找到最大值
for location in range(1, fillslot + 1):
if alist[location] > alist[positionOfMax]:
positionOfMax = location
# 将最大值与最后一个值进行交换
alist[fillslot], alist[positionOfMax] = alist[positionOfMax], alist[fillslot]
alist = [54,26,93,17,77,31,44,55,20]
selectionSort(alist)
print(alist)
写法二
同样,也可以改成while循环。
def selectionSort(alist):
fillslot = len(alist) - 1
while fillslot > 0:
positionOfMax = 0
# 找到最大值
for location in range(1, fillslot+1):
if alist[location] > alist[positionOfMax]:
positionOfMax = location
# 将最大值与最后一个值进行交换
alist[fillslot], alist[positionOfMax] = alist[positionOfMax], alist[fillslot]
fillslot -= 1
alist = [54,26,93,17,77,31,44,55,20]
selectionSort(alist)
print(alist)
参考:
https://runestone.academy/runestone/static/pythonds/SortSearch/TheSelectionSort.html
插入排序
从第二个元素开始,如果比前一个元素大,就保持不动;如果比前一个元素小,就插入到它前面合适的位置。
以下面这个图为例,
在第五遍扫描的时候,要决定31的位置,在代码中先用currentvalue保存31,
因为93比31大,所以把93往后移,
又因为77比31大,所以77也往后移,
54也比31大,所以54往后移。
25比31小,所以31插入到25后面
def insertionSort(alist):
for index in range(1, len(alist)):
currentvalue = alist[index] # 保存当前位置上的值
position = index # 记录位置
while position > 0 and alist[position - 1] > currentvalue:
# 如果前一个值比当前值大,就把前一个值移到当前位置上
alist[position] = alist[position - 1]
position = position - 1
alist[position] = currentvalue
归并排序
代码来自:
python归并排序–递归实现
def mergesort(seq):
"""归并排序"""
if len(seq) <= 1:
return seq
mid = len(seq) // 2 # 将列表分成更小的两个列表
# 分别对左右两个列表进行处理,分别返回两个排序好的列表
left = mergesort(seq[:mid])
right = mergesort(seq[mid:])
# 对排序好的两个列表合并,产生一个新的排序好的列表
return merge(left, right)
def merge(left, right):
"""合并两个已排序好的列表,产生一个新的已排序好的列表"""
result = [] # 新的已排序好的列表
i = 0 # 左半部分的下标
j = 0 # 右半部分的下标
# 对两个列表中的元素 两两对比。
# 将最小的元素,放到result中,并对当前列表下标加1
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
# 下面两条语句中,只有一条会执行
result += left[i:] # 若第一个表未检测完,添加到result后面
result += right[j:] # 若第二个表未检测完,添加到result后面
return result
seq = [5,3,0,6,1,4]
print('排序前:',seq)
result = mergesort(seq)
print('排序后:',result)
排序前: [5, 3, 0, 6, 1, 4]
排序后: [0, 1, 3, 4, 5, 6]
代码的执行过程:
注意:
在merge
函数中,判断语句的条件只能是<=
if left[i] <= right[j]
这样能保证,归并排序是一个稳定
的排序。也就是当出现两个相同的数时,它们的相对位置不因排序而改变。
快速排序
# 严蔚敏版
def quick_sort(array, l, r):
if l < r:
q = partition(array, l, r)
quick_sort(array, l, q - 1)
quick_sort(array, q + 1, r)
def partition(array, l, r):
# 一趟排序过程
key = array[l]
while l < r:
while l < r and array[r] >= key: # 移动右指针,直到找到比key小的元素
r -= 1
array[l] = array[r]
while l < r and array[l] <= key: # 移动左指针,直到找到比key大的元素
l += 1
array[r] = array[l]
array[l] = key
return l
array = [54,26,93,17,77,31,44,55,20]
quick_sort(array, 0, len(array)-1)
print(array)
下面以array
为例,具体解析代码过程.
这里是一遍扫描
,以54
作为基准值key
,把比它小的都放到左边,比它大的都放到右边。最终得到54
在整个数组中的位置,并把这个位置返回给q
然后以q
位置为分隔线,进行左半部分的递归和右半部分的递归。
接下去是对44
的左半部分31,26
进行扫描,把31
作为基准值key
,会把26
移到31
的左边,然后,54
左半部分就排完序了。54的右半部分也同理。最终会得到一个整体有序的结果。
堆排序
代码来自利用Python实现堆排序
def MAX_Heapify(heap,HeapSize,root):#在堆中做结构调整使得父节点的值大于子节点
left = 2*root + 1
right = left + 1
larger = root
if left < HeapSize and heap[larger] < heap[left]:
larger = left #找到root和left之间的最大值x
if right < HeapSize and heap[larger] < heap[right]:
larger = right # 把right和刚才的最大值x比较
if larger != root: # 如果最大值不是root,就要进行调整
#如果做了堆调整则larger的值等于左节点或者右节点的,这个时候做交换操作
heap[larger],heap[root] = heap[root],heap[larger]
MAX_Heapify(heap, HeapSize, larger) # 重新调整,退出的条件是,root>=left且root>=right
def Build_MAX_Heap(heap):#构造一个堆,将堆中所有数据重新排序
HeapSize = len(heap)#将堆的长度单独拿出来
for i in range((HeapSize -2)//2,-1,-1):#从后往前出数
MAX_Heapify(heap,HeapSize,i) # 重新调整
def HeapSort(heap):
#将根节点取出与最后一位做对调,对前面len-1个节点继续进行对调整过程。
Build_MAX_Heap(heap) # 构建一个大根堆
for i in range(len(heap)-1,-1,-1):
# i的取值范围是7-0,每次0都是最大的,然后分别和7->0等7个元素进行交换
heap[0],heap[i] = heap[i],heap[0]
MAX_Heapify(heap, i, 0) # 重新调整,这里的i是size,依次是7、6、5直到0,表示要重新调整的节点数
return heap
a=[53,17,78,9,45,65,87,32]
HeapSort(a)
print(a)