排序复杂度及稳定性对比
**图片名词解释: **
n: 数据规模
k:“桶”的个数
In-place: 占用常数内存,不占用额外内存
Out-place: 占用额外内存
**冒泡排序 - **
**算法思想: **
第一次循环在0~len(arrs)-1的范围内自左至右相邻数字两两比较,较大的数放在后面,在完成所有数字比较之后,第一次循环结束,最大的一个数放在最右边,以此类推
**步骤: **
a.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
b.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
c.针对所有的元素重复以上的步骤,除了最后一个。
d.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
**代码实现: **
def bubbling_sort(arrs):
for i in range(0,len(arrs)-1): # 这个循环负责设置冒泡排序进行的次数
for j in range(len(arrs) - i - 1): # j为列表下标
if arrs[j] > arrs[j + 1]:
arrs[j], arrs[j + 1] = arrs[j + 1], arrs[j]
print(arrs)
arr = [9, 1, 3, 2, 4, 6, 5, 8, 7, 0]
bubbling_sort(arr)
def selection_sort(arr):
for i in range(0,len(arr)-1):
index=i
for j in range(i+1,len(arr)):
if arr[index]>arr[j]:
index = j
arr[i], arr[index] = arr[index], arr[i]
print(arr)
arr=[9,1,3,2,4,6,5,8,7,0]
selection_sort(arr)
运行结果:
-插入排序-
算法思想
首先在len(arr)中选取最小值放在第一个位置,接下来在len(arr)-1的范围内选取最小值放在第二个位置上,以此类推
算法步骤
a.将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
b.从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面)
代码实现:
(1):先找到元素要插入的位置,然后将从该位置(包括)到待插入元素位置之间的元素都向后移动一位,最后将元素插入该位置。
def inert_sort(arr):
N=len(arr)
for x in range(1,N):
a,b=x,x
n=arr[x]
while n=0:
a=a-1
if a-1<0:
a=0
while b>a:
arr[b]=arr[b-1]
b=b-1
arr[a]=n
return arr
arr=[9,1,3,2,4,6,5,8,7,0]
inert_sort(arr)
print(arr)
(2):将待插入元素与其前一个元素进行比较,若小于,则将前一个元素后移一位,在将带插入元素与前前一位进行比较,若小于,则将前前一位往后移,如此操作直到遇到小于待插入元素的位置,插入到该位后一位(此位已经在上一步后移了)
def insertsort(arr):
N=len(arr)
for x in range(1,N):
key=arr[x]
a=x
while key
-希尔排序-
算法思想
先选步长n,然后将下标为n的数与数组第一个数比较,若前者大,交换位置,以此类推,而后改变步长,再执行比较操作,最后以步长为1的操作结束
算法步骤
a. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
b.按增量序列个数k,对序列进行k 趟排序;
c.每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
代码实现
def shell_sort(nums):
# 设定步长
step = len(nums)//2
while step > 0:
for i in range(step, len(nums)):
# 类似插入排序, 当前值与指定步长之前的值比较, 符合条件则交换位置
while i >= step and nums[i-step] > nums[i]:
nums[i], nums[i-step] = nums[i-step], nums[i]
i -= step
step = step//2
print(nums)
arr=[9,1,3,2,4,6,5,8,7,0]
shell_sort(arr)
-归并排序-
算法思想
将整个列表的每一项看做长度为1的有序区间,把相邻的两个有序区间合并,得到长度为2的有序区间,然后相邻有序区间进行合并,以此类推
算法步骤
a.把长度为n的输入序列分成两个长度为n/2的子序列;
b.对这两个子序列分别采用归并排序;
c.将两个排序好的子序列合并成一个最终的排序序列。
(1)
def mergeSort(arr):
import math
if(len(arr)<2):
return arr
middle = math.floor(len(arr)/2)
left, right = arr[0:middle], arr[middle:]
return merge(mergeSort(left), mergeSort(right))
def 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
arr=[9,1,3,2,4,6,5,8,7,]
print(mergeSort(arr))
(2)
def merge(a, b):
c = []
h = j = 0
while j < len(a) and h < len(b):
if a[j] < b[h]:
c.append(a[j])
j += 1
else:
c.append(b[h])
h += 1
if j == len(a):
for i in b[h:]:
c.append(i)
else:
for i in a[j:]:
c.append(i)
return c
def merge_sort(lists):
if len(lists) <= 1:
return lists
middle = len(lists)//2
left = merge_sort(lists[:middle])
right = merge_sort(lists[middle:])
return merge(left, right)
arr=[9,1,3,2,4,6,5,8,7,]
arrs=merge_sort(arr)
print(arrs)
-快速排序-
算法思想
随机在数组中选取一个数组,大于此数字的放在右边,较小的放在左边,而后对齐左右分别递归排序
算法步骤
a.从数列中挑出一个元素,称为 “基准”(pivot);
b.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
c.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
def quick_sort(myList,start,end):
#判断low是否小于high,如果为false,直接返回
if start < end:
i,j = start,end
#设置基准数
base = myList[i]
while i < j:
#如果列表后边的数,比基准数大或相等,则前移一位直到有比基准数小的数出现
while (i < j) and (myList[j] >= base):
j = j - 1
#如找到,则把第j个元素赋值给第个元素i,此时表中i,j个元素相等
myList[i] = myList[j]
#同样的方式比较前半区
while (i < j) and (myList[i] <= base):
i = i + 1
myList[j] = myList[i]
#做完第一轮比较之后,列表被分成了两个半区,并且i=j,需要将这个数设置回base
myList[i] = base
#递归前后半区
quick_sort(myList, start, i - 1)
quick_sort(myList, j + 1, end)
return myList
arr=[9,1,3,2,4,6,5,8,7,0]
quick_sort(arr,0,len(arr)-1)
print(arr)
-堆排序-
算法思想
把数组形成类似一个二叉树的堆结构,最大值位于堆顶,然后与堆尾交换,并取出最大值,堆缩小1,以此类推,直到堆大小为1
算法步骤
a.创建一个堆 H[0……n-1];
b.把堆首(最大值)和堆尾互换;
c.把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
d.重复步骤 2,直到堆的尺寸为 1。
代码实现:
import random
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
if right < HeapSize and heap[larger] < heap[right]:
larger = right
if larger != root:#如果做了堆调整则larger的值等于左节点或者右节点的,这个时候做对调值操作
heap[larger],heap[root] = heap[root],heap[larger]
MAX_Heapify(heap, HeapSize, larger)
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):
heap[0],heap[i] = heap[i],heap[0]
MAX_Heapify(heap, i, 0)
return heap
if __name__ == '__main__':
arr = [9, 1, 3, 2, 4, 6, 5, 8, 7, 0]
print(arr)
HeapSort(arr)
print(arr)
-计数排序-
算法思想:
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。
作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
算法步骤
a.找出待排序的数组中最大和最小的元素;
b. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
c.对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
d.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
代码实现:
def counting_sort(arr, maxValue):
bucketLen = maxValue+1
bucket = [0]*bucketLen
sortedIndex =0
arrLen = len(arr)
for i in range(arrLen):
if not bucket[arr[i]]:
bucket[arr[i]]=0
bucket[arr[i]]+=1
for j in range(bucketLen):
while bucket[j]>0:
arr[sortedIndex] = j
sortedIndex+=1
bucket[j]-=1
return arr
arr = [9, 1, 3, 2, 4, 6, 5, 8, 7, 0]
maxvalue=max(arr)
print(counting_sort(arr,maxvalue))
-桶排序-
算法思想
假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排
算法步骤
a.设置一个定量的数组当作空桶;
b.遍历输入数据,并且把数据一个一个放到对应的桶里去;
c.对每个不是空的桶进行排序;
d.从不是空的桶里把排好序的数据拼接起来。
(1):
def bucket_sort(lst):
buckets = [0] * ((max(lst) - min(lst))+1)
for i in range(len(lst)):
buckets[lst[i]-min(lst)] += 1
res=[]
for i in range(len(buckets)):
if buckets[i] != 0:
res += [i+min(lst)]*buckets[i]
return res
arrs=[9,1,3,2,4,6,5,8,7,0]
print(bucket_sort(arrs))
(2):通常桶排序
arr = [3, 5, 6, 1, 2, 10]
def bucketSort(arr):
max_num = max(arr)
bucket = [0] * (max_num + 1)
for i in arr:
bucket[i] = 1
sorted_num = []
for j in range(len(bucket)):
if bucket[j] != 0:
sorted_num.append(j)
return sorted_num
print(bucketSort(arr))
(3):解决重复元素
arr = [3, 5, 3, 5, 1, 10, 6, 1, 2, 10]
def bucketSort2(arr):
max_num = max(arr)
bucket = [0] * (max_num + 1)
for i in arr:
bucket[i] += 1
sorted_num = []
for j in range(len(bucket)):
if bucket[j] != 0:
for y in range(bucket[j]):
sorted_num.append(j)
return sorted_num
print(bucketSort2(arr))
(4):解决数据稀疏问题
arr = [30, 40, 50, 80, 40, 60, 10, 100]
def bucketSort3(arr):
max_num = max(arr)
min_num = min(arr)
diff = max_num - min_num
bucket = [0] * (diff + 1)
for i in arr:
bucket[i - min_num] += 1
sorted_num = []
for j in range(len(bucket)):
if bucket[j] != 0:
for y in range(bucket[j]):
sorted_num.append(j + min_num)
return sorted_num
print(bucketSort3(arr))
-基数排序-
算法思想
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。
算法步骤
a.取得数组中的最大数,并取得位数;
b.arr为原始数组,从最低位开始取每个位组成radix数组;
c.对radix进行计数排序(利用计数排序适用于小范围数的特点)
代码实现:
from random import randint
def radix_sort(arr,d):
for k in range(d) : #0,1,2
s=[[] for i in range(10)]
for i in arr:
#/取值的结果为float类型,//的结果为int类型
s[i//(10**k)%10].append(i)
arr=[j for i in s for j in i]
return arr
a=[randint(1,999) for i in range(10)]
print(a)
num=len(str(max(a)))
a=radix_sort(a,num)
print(a)