排序算法
排序算法笔记:
简述算法思路加上代码(python)实现,参考万能互联网。排序实现均为从小到大排列。
冒泡排序
1.冒泡排序思路
冒泡排序类比于冒泡的过程,排序过程:从一端到另一端冒泡,依次比较相邻两个数的大小,若小于。则交换位置。假设共有n个数参与排序,则需要n轮冒泡过程,每轮最后一位元素已排序好。
冒泡排序有两层循环,外层冒泡轮数,内层冒泡过程。复杂度为O(n^2)
2.代码实现:
def bubblesort(list):
for i in range(1,len(list)):
flag = True
# 不断比较相邻元素大小,若小于则交换位置
# 每次循环最后一位已排序好
for j in range(0,len(list)-i):
if list[j] > list[j+1]:
list[j],list[j+1] = list[j+1], list[j]
flag = False
if flag == True:
break
return list
list1 = [23,7,0,12,6,89,33,89]
print(bubblesort(list1))
选择排序
1.选择排序思路
选择排序循环n次,每次从未排序元素中选择最小的元素,与第n位交换位置。
一共两层循环,外层n次选择,内层为每次选择过程。复杂度为O(n^2)。
2.代码实现:
"""
选择排序,遍历n次,每次找出列表中的最小值
将最小值与其交换位置
复杂度为O(n^2)
"""
def selectionSort(list):
# 遍历n次
for i in range(len(list)):
min = i
# 逐个比较未排序的元素
for j in range(i+1,len(list)):
# 记录下最小元素的位置
if list[j] < list[min]:
min=j
# 将最小元素与i交换位置
if i != min:
list[i],list[min] = list[min],list[i]
return list
list1 = [23,7,0,12,6,89,33,89]
print(selectionSort(list1))
插入排序
1.插入排序思路
类似于打扑克,每次拿出一个元素current,将其插入到已排序的数组合适位置。在已排序的数组中从后向前扫描,指针指向current前一位,在扫描过程中,若大于未排序元素,则将扫描过的元素向后移位,指针向前,直到遇到小于等于current的元素,将current插入。直至整个数组排序完成。
一共两层循环,外层采用for循环,为n 个元素排序,内层while,找出进入循环元素的插入位置。复杂度为O(n^2)
"""
插入排序
类似于打扑克摆牌
将未排序元素插入到已排序序列
复杂度O(n^2)
"""
def insertionsort(list):
for i in range(1,len(list)):
preindex = i-1 #指向未排序元素前一个的指针
current = list[i] #未排序元素的值
# 扫描已排序好的序列,将元素插入
while preindex >= 0 and list[preindex] > current:
list[preindex+1] = list[preindex] # 如果大于current,向后移位
preindex = preindex - 1 #指针向前移位
list[preindex+1] = current #插入元素
return list
list1 = [23,7,0,12,6,89,33,89]
print(insertionsort(list1))
希尔排序
1 希尔排序思路:
希尔排序是插入排序的改进,插入排序在基本有序时效率最高。希尔排序可以看做是分组插入排序,使数组基本有序,在进行直接插入排序。
设置一个增量step,类比于插入排序中,每次指针前进1,分组间隔为step。每次循环step减半,直至为1。复杂度为O(nlogn)
也可以说插入排序是step为1 的希尔排序。
2.代码实现:
"""
希尔排序 插入算法的改进
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,
待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
复杂度O(nlogn)
"""
def shellsort(list):
size = len(list)
step = int(size/2) #增量
while step > 0 :
for i in range(step,size): # 分组
j = i
# 插入排序
while j > 0 and list[j-step] > list[j]:
list[j],list[j-step] = list[j-step],list[j]
j = j - step # j 每次减小step 每step个元素一组
step = int(step/2)
return list
list1 = [23,7,0,12,6,89,33,89]
print(shellsort(list1))
快速排序
1 快速排序思路:
快速排序的基本思想是分治,每次每次从序列中挑选一个基准值pivot,比pivot大的值放到右边,小的放在左边。在分别从左右两边子序列挑选基准值,重复上述操作,直至序列数量为1,此时数组有序。快速排序速度很快,复杂度为O(nlogn)
2 代码实现1:
(采用递归的方法)
def quickSort(list):
if len(list) <2 :
return list
else:
pivot = list[0]
less = [i for i in list[1:] if i < pivot] # 比pivot元素小的元素
greater = [i for i in list[1:] if i > pivot] # 比pivot元素大的元素
return quickSort(less) + [pivot] + quickSort(greater) # 递归
list1 = [23,7,0,12,6,89,33,89]
print(quickSort(list1))
代码实现2:
挖坑法(双边扫描):i, j分别为左右两边指针,while循环中,将大于pivot的值交换到右边,小于Pivot的值交换到左边。i, j分别为左右指针,分别从两端扫描,遇到符合条件的值则交换,直至两指针相遇,本次交换完成,将基准值填入到两指针相遇的位置。再对pivot两边进行同样操作。(递归行为)
def quicksort(list,left,right):
if left >= right:
return
i = left # 左边指针
j = right # 右边指针
pivot = list[left] #基准元素
while i < j:
while i < j and list[j] >= pivot:
j = j - 1
list[i] = list[j]
# 从右向左扫描,若大于pivot则指针向左,若有元素小于pivot,停止并提到前面
while i < j and list[i] < pivot:
i = i + 1
list[j] = list[i]
# 从左到右扫描,若小于pivot则指针向右,若有元素大于pivot,停止并放到后面
list[i] = pivot # 两指针相遇位置,为pivot值
# 对基准元素两边继续排序-递归
quicksort(list,left,i-1)
quicksort(list,i+1,right)
list1 = [23,7,0,12,6,89,33,89]
print(list1)
right = len(list1)-1
quicksort(list1,0,right)
print(list1)
归并排序
1 归并排序思路:
归并排序的核心思想是分治法。先是将序列不断分解,直至单个元素,在按大小逐渐合并。复杂度为O(nlogn)
2 代码实现
"""
归并排序,采用分治的思想
复杂度为O(nlogn)
"""
def merge(left,right):
i = 0
j = 0
result = []
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 += right[j:]
return result
def mergesort(list):
if len(list) <= 1 :
return list
mid = int(len(list)/2)
left = mergesort(list[:mid])
right = mergesort(list[mid:])
return merge(left,right)
list1 = [23,7,0,12,6,89,33,89]
print(mergesort(list1))
堆排序
1 堆排序思路:
利用堆实现的一种排序方法。堆可以看做是一个完全二叉树,根节点为序列最大值,子节点的值都小于父节点。堆排序,要做的是首先构造一个大顶堆,根节点为最大值,将根节点与末尾节点交换,此时大顶堆根节点发生变化,需调整堆内元素维护大顶堆,在将新的根节点与未处理的末尾节点交换,以此类推,直到只剩根节点。
复杂度为O(nlogn)
2 代码实现:
"""
堆排序 复杂度O(nlogn)
构造大顶堆,最大值就是根节点,
将其与末尾元素交换
"""
# 维护大顶堆 从上到下
def adjusthesp(list,parnode,lastnode):
newpar = parnode # 父节点
child_node = 2*parnode + 1 # 子节点(左)
while child_node <= lastnode:
if child_node < lastnode and list[child_node] < list[child_node+1]:
child_node += 1 # 若右节点大于左节点,则处理右节点
# 子节点大于父节点,交换
if list[child_node] > list[newpar]:
list[child_node],list[newpar] = list[newpar],list[child_node]
newpar = child_node # 新的父节点,向下处理
child_node = 2*child_node +1
else:
break
# 若小于父节点则不继续处理
# print(list)
def heapsort(list):
length = len(list) #列表长度
last_node = length - 1 # 最后节点的索引
last_node_par = (last_node-1)//2 # 最后一个父节点索引
# 构建大顶堆 从下到上
while last_node_par >=0:
adjusthesp(list,last_node_par,last_node)
last_node_par -= 1
# 交换根节点与最后一个节点,维护大顶堆
while last_node > 0:
list[0],list[last_node] = list[last_node],list[0]
# 交换过的最后一个节点不处理
adjusthesp(list,0,last_node-1)
last_node = last_node - 1
list1 = [23,7,0,12,6,89,33,89,3]
heapsort(list1)
print(list1)
计数排序
1 计数排序思路:
计数排序是一种非比较排序,核心在于输入的数据值转化为键存储在额外开辟的数组空间。排序速度很快,复杂度为O(n+k),k为数据量。某种程度是用空间换时间。
2 代码实现:
"""
计数排序 复杂度O(n+k)
非比较的排序算法,
核心在于输入的数据值转化为键存储在额外开辟的数组空间。
"""
def countsort(input_list):
length = len(input_list)
if length < 2:
return input_list
max_num = max(input_list)
count = [0]*(max_num + 1)
output_list = []
for element in input_list:
count[element] += 1
for i in range(max_num +1):
for j in range(count[i]):
output_list.append(i)
return output_list
list1 = [23,7,0,12,6,89,33,89,3]
output = countsort(list1)
print(output)
桶排序
1 桶排序思路
桶排序可以看做是计数排序的改进,也可以说计数排序是桶大小为1 的桶排序。桶排序先建立桶,每个桶内用于存储某个范围内的元素,将序列中的元素分到每个桶内,在对每个桶内元素进行排序,最后将排序好的桶进行拼接。复杂度为O(n+k)
2 代码实现:
"""
桶排序 复杂度O(n+k)
基于计数排序
将数据放入桶中,对桶中数据进行排序
将排序好的桶进行拼接
"""
def bucketsort(list):
min_num = min(list) # 找出最大值
max_num = max(list) # 找出最小值
bucket_range = (max_num - min_num)//len(list) # 每个桶的长度
bucket_num = (max_num - min_num)//bucket_range # 桶的个数
bucket_list = [[] for i in range(bucket_num+1)] # 建立桶
for i in list: # 将列表中元素放入相应的桶里
bucket_list[(i - min_num)//bucket_range].append(i)
list.clear()
# 循环每个桶,将桶拼接
for bucket in bucket_list:
# 用sorted()对桶中元素进行排序
for ele in sorted(bucket):
list.append(ele) # 拼接桶
list1 = [23,7,0,12,6,89,33,89,3,56,23,1]
bucketsort(list1)
print(list1)
总结
复杂度为O(n^2)的排序:冒泡排序,选择排序,插入排序。
复杂度为O(nlogn)的排序:希尔排序,快速排序,归并排序,堆排序。
复杂度为O(n+k)的排序:计数排序,桶排序。
稳定排序:冒泡排序、插入排序、归并排序、计数排序、桶排序。
不稳定排序:选择排序、希尔排序、快速排序、堆排序。
参考链接:
链接:
https://www.cxyxiaowu.com/725.html
https://www.cxyxiaowu.com/2026.html