推荐阅读:
参考详细:
https://blog.csdn.net/qq_36759224/article/details/109251836
https://www.runoob.com/w3cnote/ten-sorting-algorithm.html
https://www.cnblogs.com/onepixel/articles/7674659.html
两两比较交换,直到最终。
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
注意循环的次数。
有len(arr)个数字的话,最外面就要进行len(arr)-1次的大循环比对。
每一个大循环结束后,都有一个最大值被发现,并且放到了arr最后面。此时就不用管这个最大值,以相同方法比较剩下的数字。
python
pythondef bubbleSort(arr):
for i in range(len(arr)-1): # 循环len(arr)-1)次
for j in range(len(arr)-i-1): # 循环len(arr)-i-1次
if arr[j] > arr[j+1]: # 如果这个数大于后面的数就交换两者的位置
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
print(bubbleSort([3,2,5,9]))
C
c#include <stdlib.h>
#include <stdio.h>
void bubbleSort(int *arr, int len)
{
int i,j;
int temp;
for(i=0;i< len-1;i++)
{
for(j=0;j< len-1-i;j++)
{
if(arr[j]>arr[j+1])
{
temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
}
int main() {
int a[]={3,5,2,1};
bubbleSort(a,4);
printf("%d %d %d %d",a[0],a[1],a[2],a[3]);
}
当某一轮大循环中如果没有出现元素交换的情况,那么接下来的循环就没有必要进行了。元素已经全部有序了!
python
cdef bubbleSort(arr):
for i in range(len(arr) - 1): # 循环len(arr)-1)次
flag = 0
for j in range(len(arr) - i - 1): # 循环len(arr)-i-1次
if arr[j] > arr[j + 1]: # 如果这个数大于后面的数就交换两者的位置
arr[j], arr[j + 1] = arr[j + 1], arr[j]
flag = 1 # 有交换 变成1
if (flag == 0): # 还是0就说明这一轮没有交换,下一轮也就不用继续了
print("只是循环了这么多大轮就退出了:"+str(i+1))
break
return arr
print(bubbleSort([1, 2, 3, 2, 5, 9]))
在有序区域,本来就是有序的,但还是在比较,没有必要。标记出最后一次交换元素的位置避免做无所谓的比较。
pythondef bubbleSort(arr):
lastExchangeIndex = 0
border = len(arr) - 1 # 有序区域的边界
for i in range(len(arr) - 1): # 循环len(arr)-1)次
flag = 0
for j in range(border): # 循环border次 交换比较出最大值
if arr[j] > arr[j + 1]: # 如果这个数大于后面的数就交换两者的位置
arr[j], arr[j + 1] = arr[j + 1], arr[j]
flag = 1 # 有交换 变成1
lastExchangeIndex = j # 最后一次交换元素的位置
border = lastExchangeIndex # 最后一次交换元素的位置
print("最后一次交换元素的位置:" + str(border))
if (flag == 0): # 还是0就说明这一轮没有交换,下一轮也就不用继续了
print("只是循环了这么多大轮就退出了:" + str(i + 1))
break
return arr
print(bubbleSort([3, 4, 2, 1, 5, 6, 7, 8]))
鸡尾酒排序是为了解决一种数据分布,比如我们要对[2, 3, 4, 5, 6, 7, 1, 8]进行排序,上面冒泡法每一次去找最大值放到这一轮的后面,显得就很呆,明明直接把1放大最前面就直接完事了,直接就是有序的了。
鸡尾酒排序只需要n//2个大循环。
每个大循环中,先从左到右找到最大值放到末尾,然后从右到左找到最小值放到最前面。
新一轮的大循环在剩下的数据中进行。
没有提前结束的代码:
pythondef bubbleSort(arr):
for i in range(len(arr) // 2): # 循环len(arr)-1)次
for j in range(i, len(arr) - i - 1): # 循环len(arr)-i-1次
if arr[j] > arr[j + 1]: # 如果这个数大于后面的数就交换两者的位置
arr[j], arr[j + 1] = arr[j + 1], arr[j]
for j in range(len(arr) - i - 1, i, -1): # 循环
if arr[j - 1] > arr[j]: # 如果这个数大于后面的数就交换两者的位置
arr[j], arr[j - 1] = arr[j - 1], arr[j]
return arr
print(bubbleSort([2, 3, 4, 5, 6, 9, 2, 7, 1, 8]))
带提前结束的逻辑:
pythondef bubbleSort(arr):
for i in range((int)(len(arr) / 2)): # 循环len(arr)-1)次
flag1 = 0
for j in range(i,len(arr) - i - 1): # 循环len(arr)-i-1次
if arr[j] > arr[j + 1]: # 如果这个数大于后面的数就交换两者的位置
arr[j], arr[j + 1] = arr[j + 1], arr[j]
flag1 = 1 # 有交换 变成1
flag2 = 0
for j in range(len(arr) - i - 1, i, -1): # 循环
if arr[j - 1] > arr[j]: # 如果这个数大于后面的数就交换两者的位置
arr[j], arr[j - 1] = arr[j - 1], arr[j]
flag2 = 1 # 有交换 变成1
if (flag1 == 0 and flag2==0): # 都没交换过 提前结束大循环
break
return arr
print(bubbleSort([2, 3, 4, 5, 6, 9, 0, 7, 1, 8]))
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
第1轮:第1个元素和其余的元素比较,记录最小元素的下标。交换。
第2轮:第2个元素和其余的元素比较,记录最小元素的下标。交换。
[2, 3, 4, 5, 6, 9, 0, 7, 1, 8] 原始数据
[0, 3, 4, 5, 6, 9, 2, 7, 1, 8] 第1轮结束后
[0, 1, 4, 5, 6, 9, 2, 7, 3, 8] 第2轮结束后
[0, 1, 2, 5, 6, 9, 4, 7, 3, 8]
[0, 1, 2, 3, 6, 9, 4, 7, 5, 8]
[0, 1, 2, 3, 4, 9, 6, 7, 5, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] OK
……
pythondef selection_sort(arr): # 长度 5: 0 1 2 3 4
for i in range(len(arr) - 1): # 0 1 2 3
min_index = i # 记录最小数的下标 0 1 2 3
for j in range(i + 1, len(arr)): # 1 2 3 4
if arr[j] < arr[min_index]: # 1234 < 0 ?
min_index = j
arr[i], arr[min_index] = arr[min_index], arr[i] # 交换
return arr
print(selection_sort([2, 3, 4, 5, 6, 9, 0, 7, 1, 8]))
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
和整理扑克牌相似,假设前面的都已经有序,选择当前牌去插入到这个有序序列。
假面的都是有序的。把新元素插入进去。
pythondef insertion_sort(arr): # 5: 0 1 2 3 4
for i in range(1, len(arr)): # 1 2 3 4
tmp = arr[i] # 1 2 3 4 从前到后遍历
j = i-1 # 0 1 2 3 从前到后遍历
while j >= 0 and arr[j] > tmp: # j从后向前 每次比较j是否大于取到的元素
arr[j+1] = arr[j]
j -= 1
arr[j+1] = tmp
return arr
print(insertion_sort([2, 3, 4, 5, 6, 9, 0, 7, 1, 8]))
在这个博客https://www.cnblogs.com/chengxiao/p/6104371.html有下面这幅图:
这算法真是感人涕下,算法过程有点曲折。我觉得关注点放在“宏观调控”数据分布上就好。
希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
大循环下:
pythondef shell_sort(arr): # 长度8
d = len(arr) // 2 # d=4
while d >= 1:
for i in range(d, len(arr)): # 4~7 2~7 1~7
tmp = arr[i] # 4~7 2~7 1~7
j = i - d # 0~3 0~5 0~6
while j >= 0 and tmp < arr[j]: # 后面的牌<前面的牌
arr[j + d] = arr[j] # 大牌往后放
j -= d
arr[j + d] = tmp
print("da")
print(j + d, j + 2 * d, arr)
print("----") # 每一轮排序后打印一次
d //= 2 # d=2 d=1 d=0退出
return arr
print(shell_sort([6, 5, 4, 3, 2, 9, 0, 7, 1, 8]))
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
pythondef merge(left,right):
result = []
while left and right:
if left[0] <= right[0]:
result.append(left.pop(0))
else:
result.append(right.pop(0));
while left:
result.append(left.pop(0))
while right:
result.append(right.pop(0))
return result
def mergeSort(arr):
if(len(arr)<2):
return arr
middle = len(arr)//2 # 中间
left, right = arr[0:middle], arr[middle:] # 分离
return merge(mergeSort(left), mergeSort(right)) # 递归
print(mergeSort([ 4,5,1,2,3,2,1,2,7,6]))
加断点,分离后看看分离成啥样子了。
返回合并前看看合并的情况。
[ 4,5,1,2,3,2,1,2,7,6]
[4, 5, 1, 2, 3][2, 1, 2, 7, 6]
[4, 5][1, 2, 3]
[4][5]
[4, 5]合并了
[1][2, 3]
[2][3]
[2, 3]合并了
[1, 2, 3]合并了
[1, 2, 3, 4, 5]合并了
[2, 1][2, 7, 6]
[2][1]
[1, 2]合并了
[2][7, 6]
[7][6]
[6, 7]合并了
[2, 6, 7]合并了
[1, 2, 2, 6, 7]合并了
[1, 1, 2, 2, 2, 3, 4, 5, 6, 7]合并了
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
很重要的一个算法,考研必备。想理解这个算法,必须追溯运行过程。
每一个递归过程其实就是在找一个合适的坑,这个坑基准填下去后,能够让 基准前面的数都是小于等于的 基准后面的都是大于等于的。
在一个递归过程,经过巧妙玩坑填坑。
递归过程:
(1)执行 partition( [ 4,5,1,2,3,2,1,2,7,6] left=0 right=9 )
[ x,5,1,2,3,2,1,2,7,6] 挖掉基准作为一个坑,从右到左找小于基准的往前面填 x代表一个坑
[ 2,5,1,2,3,2,1,x,7,6] 从左到右找大于基准的往后面坑里填
[ 2,x,1,2,3,2,1,5,7,6]
[ 2,1,1,2,3,2,x,5,7,6]
[ 2,1,1,2,3,2,4,5,7,6] 此时left=right=6 填到最后 找到了合适的坑
(2)执行 partition( [ 4,5,1,2,3,2,1,2,7,6] left=0 right=5 )
[ x,1,1,2,3,2,4,5,7,6]
[ 1,1,x,2,3,2,4,5,7,6]
[ 1,1,2,2,3,2,4,5,7,6] left=right=2
(3)执行 partition( [ 4,5,1,2,3,2,1,2,7,6] left=0 right=1 )
[ x,1,2,2,3,2,4,5,7,6]
[ 1,1,2,2,3,2,4,5,7,6] left=right=0
(4)执行 partition( [ 4,5,1,2,3,2,1,2,7,6] left=3 right=5 )
[ 1,1,2,x,3,2,4,5,7,6]
[ 1,1,2,2,3,2,4,5,7,6] left=right=3
(5)执行 partition( [ 4,5,1,2,3,2,1,2,7,6] left=4 right=5 )
[ 1,1,2,2,x,2,4,5,7,6]
[ 1,1,2,2,2,3,4,5,7,6] left=right=3
(6)执行 partition( [ 4,5,1,2,3,2,1,2,7,6] left=7 right=9 )
[ 1,1,2,2,2,3,4,x,7,6]
[ 1,1,2,2,2,3,4,5,7,6] left=right=7
(7)执行 partition( [ 4,5,1,2,3,2,1,2,7,6] left=8 right=9 )
[ 1,1,2,2,2,3,4,5,x,6]
[ 1,1,2,2,2,3,4,5,6,7] left=right=9
pythondef partition(arr, left, right):
# 归位操作,left,right 分别为数组左边和右边的位置索引
tmp = arr[left]
while left < right:
while left < right and arr[right] >= tmp: # 从右边找比 tmp 小的数,如果比 tmp 大,则移动指针
right -= 1 # 将指针左移一个位置
arr[left] = arr[right] # 将右边的值写到左边的空位上
while left < right and arr[left] <= tmp: # 从左边找比 tmp 大的数,如果比 tmp 小,则移动指针
left += 1 # 将指针右移一个位置
arr[right] = arr[left] # 将左边的值写到右边的空位上
arr[left] = tmp # 把 tmp 归位
return left # 返回 left,right 都可以,目的是便于后面的递归操作对左右两部分进行排序
def quick_sort(arr, left, right): # 快速排序
if left < right:
mid = partition(arr, left, right)
quick_sort(arr, left, mid-1) # 对左半部分进行归位操作
quick_sort(arr, mid+1, right) # 对右半部分进行归位操作
return arr
print(quick_sort([ 4,5,1,2,3,2,1,2,7,6],0,9))
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
堆是一种数据结构,也叫优先级队列,有大顶堆、小顶堆。
这里有一个堆的介绍:https://blog.csdn.net/x1131230123/article/details/104493844
python里有一个实现好的了heapq(堆队列、优先级队列)API。
所以堆排序:
用heappush方法将iterable中的元素一个一个压入堆(插入列表末尾,然后上滤),这是一个构建堆的过程。然后利用heappop方法一直取出堆顶元素(下滤到列表末尾,然后删除)。
pythonimport heapq
def heapsort(iterable):
h = []
for value in iterable:
heapq.heappush(h, value)
return [heapq.heappop(h) for i in range(len(h))]
print(heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0]))
构建堆还可以使用别的方法,Floyd 建堆算法(自下而上的下滤),就地运算:
pythonimport heapq
def heapsort(iterable):
heapq.heapify(iterable) #Floyd 建堆算法(自下而上的下滤)
return [heapq.heappop(iterable) for i in range(len(iterable))]
print(heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0]))
或者自行写一个(依据自下而上的下滤):
pythondef buildMaxHeap(arr):
for i in range(len(arr) // 2 - 1, -1, -1): # len(arr)//2-1 是最后一个有孩子的节点。从这个节点开始,自下而上找所有有孩子的父节点。
heapify(arr, i) # 下滤这个i节点
# 下滤这个i节点
def heapify(arr, i):
left = 2 * i + 1 # 左孩子
right = 2 * i + 2 # 右孩子
largest = i # 父亲节点i
if left < arrLen and arr[left] > arr[largest]: # 左孩子index<列表总长度(没有左孩子就会不满足这个条件) and 左孩子数值>其父亲节点数值
largest = left # largest=左孩子index
if right < arrLen and arr[right] > arr[largest]: # 右孩子index<列表总长度(没有右孩子就会不满足这个条件) and 右孩子数值>其父亲节点数值或者左孩子数值
largest = right # largest=右孩子index
if largest != i: # 父亲节点i并不是最大的 左孩子或者右孩子是最大的 index放在largest里面
swap(arr, i, largest) # arr里交换数值 把左孩子或者右孩子中最大的和父亲节点数值交换 此时largest这个index指向了之前左孩子或者右孩子的index 并不是最大的了
heapify(arr, largest) # 为了维持堆的特性 自下而上 下滤小元素,在前一操作中,较大孩子数值和父亲数值交换了,交换后,父亲数值可能与新孩子数值冲突,所以继续下滤操作
def swap(arr, i, j):
arr[i], arr[j] = arr[j], arr[i]
def heapSort(arr):
global arrLen
arrLen = len(arr)
buildMaxHeap(arr) # 堆化 建立堆 大顶堆
for i in range(len(arr) - 1, 0, -1): # len(arr)-1是最末尾元素 1是根节点的左孩子
swap(arr, 0, i) #将根节点数值和最末尾元素交换
arrLen -= 1 #堆长度减去1
heapify(arr, 0) # 下滤 这个新的根节点,但此时堆的size已经减1 也就是说不管列表最后一个元素了,是最大值或者说是有序的一些数值
return arr
# 下滤 是父亲节点和孩子不断比较
# 上滤 是孩子节点和父亲不断比较
print(heapSort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0]))
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
pythondef countingSort(arr, maxValue):
bucketLen = maxValue+1
bucket = [0]*bucketLen # 生成一个bucketLen长度的列表bucket
sortedIndex =0
arrLen = len(arr) # 待排序的列表元素个数
for i in range(arrLen): # 遍历待排序的列表的每一个元素
# if not bucket[arr[i]]: # bucket中对应位置此时是0
# bucket[arr[i]]=0 # 把这个位置给0 这2句多余的 本来初始时刻列表bucket里全是0 代表有0个元素
bucket[arr[i]]+=1 # 把这个元素对应位置加1 表示统计到了待排序的列表又多了一个这个数值
for j in range(bucketLen): # 从0遍历bucketLen
while bucket[j]>0:
arr[sortedIndex] = j # 按顺序装填到 arr
sortedIndex+=1
bucket[j]-=1
return arr
print(countingSort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0],9))
计数排序思想很简单,用空间换时间,比较适合方差不大的一堆数据,但如果待排序的列表是这样:
[1000000,2,1,3]
这初始化bucket空间的时候,申请size为1000001个的列表,可见很多空间被浪费了,这时候就该桶排序上场更好。
同时,计数排序也不能用于浮点数的排序。
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
创建桶,每个桶装一定范围区间内的数据。
对每个桶内的数据分别排序。
最后依次输出每个桶的数据。
什么时候最快
当输入的数据可以均匀的分配到每一个桶中。
什么时候最慢
当输入的数据被分配到了同一个桶中。
cdef count_sort(arr):
if len(arr) < 2: # 如果数组长度小于 2 则直接返回
return arr
# 1 遍历得到最大值 最小值 差值
maxv = max(arr)
minv = min(arr)
d = maxv - minv
# 2 初始化桶
bucketnum = 5 # 想要五个桶
bucketlist = [[] for _ in range(bucketnum)]
print(bucketlist)
# 3 遍历原始队列
for i in range(0, len(arr)):
bucketlist[(int)((arr[i] - minv) * (bucketnum - 1) / d)].append(arr[i])
print(bucketlist)
# 4 每个桶内部排序,可以用一些别的排序算法
for i in range(bucketnum):
bucketlist[i].sort()
print(bucketlist)
# 5 输出每个桶
sortarr=[]
for i in range(bucketnum):
sortarr.extend(bucketlist[i])
print(sortarr)
return sortarr
print(count_sort([1.2, 3, 5, 7, 9.4, 2.2, 4, 6.6, 8, -1.5]))
平均时间复杂度,最好时间复杂度,最差时间复杂度,空间复杂度,稳定否
arr=[13123,343,554,821,6654,644]
第一次循环结束后,arr根据个位数字全部写入了buckets:[[], [821], [], [13123, 343], [554, 6654, 644], [], [], [], [], []]
然后flatten桶后,赋值给arr,此时arr=[821, 13123, 343, 554, 6654, 644]。
第2次循环结束后,
buckets=[[], [], [821, 13123], [], [343, 644], [554, 6654], [], [], [], []]
arr=[821, 13123, 343, 644, 554, 6654]
第3次循环结束后,
buckets=[[], [13123], [], [343], [], [554], [644, 6654], [], [821], []]
arr=[13123, 343, 554, 644, 6654, 821]
第4次循环结束后,
buckets=[[343, 554, 644, 821], [], [], [13123], [], [], [6654], [], [], []]
arr=[343, 554, 644, 821, 13123, 6654]
第5次循环结束后,
buckets=[[343, 554, 644, 821, 6654], [13123], [], [], [], [], [], [], [], []]
arr=[343, 554, 644, 821, 6654, 13123]
pythondef radix_sort(arr):
max_num = max(arr)
it = 0
while 10 ** it <= max_num:
buckets = [[] for _ in range(10)]
for var in arr:
digit = (var // 10 ** it) % 10 # 依次取一位数
buckets[digit].append(var)
# 分桶完成
arr.clear()
for buc in buckets:
arr.extend(buc)
it += 1
return arr
print(radix_sort([13123,343,554,821,6654,644]))
本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!