1. 原理
排序过程原理总结
https://zsaisai.github.io/sort_summary/
2.代码
class Solution(object):
'''
1.冒泡排序:bubbleSort()
2.选择排序:selectionSort()
3.插入排序:insertionsort()
4.希尔排序:shellSort()
5.归并排序:mergeSort()
6.快速排序:quickSort()
7.堆排序: heap_sort()
8.计数排序:countingSort()
9.桶排序: bucketSort()
10.基数排序:radixSort()
'''
def bubbleSort(self,nums):
'''
1.比较相邻的元素。如果第一个比第二个大,就交换它们两个;
2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
3.针对所有的元素重复以上的步骤,除了最后一个;
4.重复步骤1~3,直到排序完成。
'''
for i in range(len(nums)):
for j in range(len(nums)-1-i):
if nums[j] > nums[j+1]:
nums[j], nums[j+1] = nums[j+1], nums[j]
#print(nums)
return nums
def selectionSort(self,nums):
'''
n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
初始状态:无序区为R[1..n],有序区为空;
第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。
该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,
使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
n-1趟结束,数组有序化了。
'''
for i in range(len(nums)):
min_num = nums[i]
min_index = i
for j in range(i,len(nums)):
if nums[j]<min_num:
min_num = nums[j]
min_index = j
nums[i], nums[min_index] = nums[min_index], nums[i]
#print(nums)
return nums
def insertionsort(self,nums):
'''
从第一个元素开始,该元素可以认为已经被排序;
取出下一个元素,在已经排序的元素序列中从后向前扫描;
如果该元素(已排序)大于新元素,将该元素移到下一位置;
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
将新元素插入到该位置后;
重复步骤2~5。
'''
for i in range(1,len(nums)):
temp = nums[i]
while(i -1 >= 0 and temp<nums[i-1]):
nums[i] = nums[i-1]
i -= 1
nums[i] = temp
#print(nums)
return nums
def shellSort(self,nums):
'''
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
按增量序列个数k,对序列进行k 趟排序;
每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。
仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
'''
gap = int(len(nums)/2)
while(gap>0):
for i in range(gap,len(nums)):
j = i
current = nums[i]
while(j - gap >= 0 and current < nums[j - gap]):
nums[j] = nums[j -gap]
j -= gap
nums[j] = current
gap = int(gap/2)
return nums
def mergeSort(self,nums):
'''
把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。
'''
if(len(nums)==1):return nums
mid = int(len(nums)/2)
left = nums[:mid]
right = nums[mid:]
return self.merge(self.mergeSort(left),self.mergeSort(right))
def merge(self,left,right):
res = []
while(len(left)>0 and len(right)>0):
if left[0] < right[0]:
res.append(left.pop(0))
else:
res.append(right.pop(0))
while(len(left)>0):
res.append(left.pop(0))
while(len(right)>0):
res.append(right.pop(0))
return res
def quickSort(self,nums):
start, end = 0, len(nums)-1
def quick(nums,start,end):
if start >= end:
return# 递归的退出条件
mid = nums[start]# 设定起始的基准元素
low = start# low为序列左边在开始位置的由左向右移动的游标
high = end# high为序列右边末尾位置的由右向左移动的游标
while low < high:
# 如果low与high未重合,high(右边)指向的元素大于等于基准元素,则high向左移动
while low < high and nums[high] >= mid:
high -= 1
nums[low] = nums[high]
while low < high and nums[low] <= mid:
low += 1
nums[high] = nums[low]
nums[low] = mid
quick(nums,start,low-1)
quick(nums,low+1,end)
quick(nums,start,end)
return (nums)
def big_endian(self, arr, start, end):
root = start#取出当前元素作为根
child = root * 2 + 1 # 求出其对应的左孩子
while child <= end:
# 孩子比最后一个节点还大,也就意味着最后一个叶子节点了,就得跳出去一次循环,已经调整完毕
if child + 1 <= end and arr[child] < arr[child + 1]:
# 为了始终让其跟子元素的较大值比较,如果右边存在且比左边大就左换右,左边大的话就默认
child += 1
if arr[root] < arr[child]:
# 父节点小于子节点直接交换位置,同时坐标也得换,这样下次循环while可以准确判断:是否为最底层,
# 是不是调整完毕
arr[root], arr[child] = arr[child], arr[root]
root = child
child = root * 2 + 1
else:
#当前元素比左右都大 无需调整 直接跳出
break
def heap_sort(self,arr): # 大根堆排序
# 计算最后一个非叶子节点
first = len(arr) // 2 - 1
# 1.开始构建大顶堆
for start in range(first, -1, -1):
# 从第一个非叶子结点从下至上,从右至左调整结构
self.big_endian(arr, start, len(arr) - 1)
# 2.调整堆结构+交换堆顶元素与末尾元素
for end in range(len(arr) - 1, 0, -1):
arr[0], arr[end] = arr[end], arr[0] # 顶部尾部互换位置
self.big_endian(arr, 0, end - 1) # 重新调整子节点的顺序,从顶开始调整
return arr
def countingSort(self,nums):
'''
创建一个bucket,容量为数组最大数+1个(0-max)
统计数组中每个值为出现的次数,存入bucket
反向填充数组,从0开始到max,按出现的次数依次存储到数组中
'''
bucket = [0]*(max(nums)+1)
c = 0
for i in range(len(nums)):
bucket[nums[i]] += 1
for j in range(len(bucket)):
while(bucket[j]>0):
nums[c] = j
c += 1
bucket[j] -= 1
return nums
def bucketSort(self,nums):
'''
桶排序将数据区间划分为若干个k个相同大小的子区间,称为桶。将n个数字分别送到各个桶中,
如果输入数据是均匀分布在各个桶中,桶排序的时间代价是O(n),所以桶排序的速度很快。
在桶排序完成后,只需对每个桶做遍历,即可输出排序的结果。即使输入数据不服从均匀分布,
只要所有桶的大小的平方和与总的元素呈线性关系,桶排序也仍然可以在线性时间内完成。
'''
import math
if len(nums) <= 0:
return nums
maxNum = max(nums)
minNum = min(nums)
bucketLength = len(nums) - 1
bucketSize = ((maxNum - minNum) / bucketLength) # 根据桶的数量找到每个桶的取值范围
buckets = [[] for i in range(bucketLength)]
for i in range(len(nums)): # 将各个数分配到各个桶
# num_bucktes_local界定范围.只要大于第n个桶,就是在第n+1个桶里.所以是向上取整.
num_bucktes_local = math.ceil((nums[i] - minNum) / bucketSize) - 1
if num_bucktes_local <= 0: # 小于0的桶,默认在0.
num_bucktes_local = 0
buckets[num_bucktes_local].append(nums[i])
# ---可删除---
# print('桶的取值范围是:', bucketSize)
# print('每个桶:', buckets)
# ---可删除---
for i in range(bucketLength): # 桶内排序,可以使用各种排序方法
buckets[i].sort()
res = []
for i in range(len(buckets)): # 分别将各个桶内的数提出来,压入结果
res.extend(buckets[i])
return res
def radixSort(self,nums):
pos_list = []
neg_list = []
# 将待排序列分为正负两组
for num in nums:
if num < 0:
neg_list.append(num)
if num >= 0:
pos_list.append(num)
def _radixSort(num_list):
num_digit = 0 # 分桶的位数,0代表个位,1为十位...
e = min(num_list)if num_list[0] < 0 else max(num_list)# 得到最小/大的数
while num_digit < len(str(e)):# 分桶位数(从0开始)小于最小/大数的位数
num_values = [[] for _ in range(10)]# 初始化桶
# 把待排数按位数分桶
for num in num_list:
num_values[int(num / (10 ** num_digit)) % 10].append(num)
num_list.clear()
# 从桶中取出已排的数
for num_value in num_values:
for num in num_value:
num_list.append(num)
#更新位数,进行下一次分桶
num_digit += 1
return num_list
# 按正负,分别排序
if len(neg_list) != 0:neg_list = _radixSort(neg_list)
if len(pos_list) != 0:pos_list = _radixSort(pos_list)
# 组合返回
return neg_list + pos_list
if __name__ == '__main__':
nums = [1,-12,14,-16,213,49]
obj = Solution()
# print(obj.bubbleSort(nums))
# print(obj.selectionSort(nums))
# print(obj.insertionsort(nums))
# print(obj.shellSort(nums))
# print(obj.mergeSort(nums))
# print(obj.quickSort(nums))
# print(obj.heap_sort(nums))
# print(obj.countingSort(nums))
# print(obj.bucketSort(nums))
print(obj.radixSort(nums))