(图片来源网络)
说到基础算法,不得不提排序,结合一些比较好的资料学习了一下排序算法。首先了解每种排序算法的基本原理,可以结合十大经典排序算法(动图演示) - 一像素 - 博客园里面的动图演示来看,大部分都很清晰易懂,然后了解一下每种排序算法的复杂度,一些排序算法是基于其他排序算法优化的,最后再看看哪些算法是可以在链表上使用的。
当然单纯的看,很难理解到精髓,最好自己动手实现一下。我这里根据leetcode上的一道题,来实现了常见的10种经典排序算法,单纯记录一下。
排序的时间空间复杂度对比
排序的数据对象
具体排序算法及代码
使用的题:https://leetcode-cn.com/problems/sort-colors/
这道题其实本身有其他方法,但是我看也可以用排序,就用这道题来验证排序算法了。
冒泡排序
不断对比前后两个元素,如果顺序错误,即交换前后两个元素
class Solution:
#冒泡排序
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n=len(nums)
for i in range(n):
for j in range(n-i-1):
if nums[j]>nums[j+1]:
tmp=nums[j]
nums[j]=nums[j+1]
nums[j+1]=tmp
j+=1
i+=1
return nums
选择排序
分有序区和无序区,从无序区中选出最小的,与无序区中第一个元素交换,无序区第一个元素成为有序区。
class Solution:
#选择排序
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n=len(nums)
for i in range(n-1):
minIndex=i
for j in range(i,n):
if nums[j]<nums[minIndex]:
minIndex=j
tmp=nums[minIndex]
nums[minIndex]=nums[i]
nums[i]=tmp
return nums
插入排序
in-space操作(就地操作)。分有序区和无序区。
取出一个元素,遍历有序区的元素,当小于某个有序区元素时,插入,剩下的元素都往后挪。
class Solution:
#插入排序
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n=len(nums)
for i in range(1,n):
j=i
cur=nums[i]
while j >0 and cur <= nums[j-1]:
nums[j]=nums[j-1]
nums[j-1]=cur
j-=1
i+=1
return nums
希尔排序
插入排序的改进版,将序列分组排序,组数满足{n/2,(n/2)/2…1},中间每组采用插入排序。希尔排序光看前面的动图图解其实不是很好理解,这里又找到一个不错的资料:图解排序算法(二)之希尔排序 - dreamcatcher-cx - 博客园
class Solution:
#希尔排序
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n=len(nums)
gap=n//2
#按增量序列不断重复分组与排序,直到分组gap=1
while gap > 0:
i=gap
#每次一分组完以后开始插入排序
while i < n:
#同一组里的前后元素开始两两对比,进行插入排序
j=i
cur=nums[i]
while j-gap>=0 and cur < nums[j-gap]:
nums[j]=nums[j-gap]
nums[j-gap]=cur
j-=gap
i+=1
gap//=2
return nums
归并排序
分治法,采用递归的方法,参考资料里的动图很好很清晰:先拆分,再排序。拆分后,左右均为有序的,只需要挨个对比排序。
class Solution:
##归并排序
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
nums[:] = self.merge(nums)
def merge(self, nums):
if len(nums)<2:
return nums
mid=len(nums)//2
left=nums[0:mid]
right=nums[mid:]
return self.subsort(self.merge(left),self.merge(right))
def subsort(self,left,right):
result=[]
while len(left)>0 and len(right)>0:
if left[0]<=right[0]:
result.append(left.pop(0))
else:
result.append(right.pop(0))
while len(left)>0:
result.append(left.pop(0))
while len(right)>0:
result.append(right.pop(0))
return result
快速排序
分治法,也用了递归。找到一个基准元素,然后把小于基准元素的放左边,大于的放右边,再基于这个基准元素左右的序列进行同样操作。
class Solution:
#快速排序
def sortColors(self, nums: List[int],left=None,right=None) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n=len(nums)
left = left if left is not None else 0
right = right if right is not None else n-1
if left < right:
indexPartition=self.partition(nums, left, right)
self.sortColors(nums,left,indexPartition-1)
self.sortColors(nums,indexPartition+1,right)
return nums
def partition(self, arr, left, right):
pivot=left
index=pivot+1
i = left
while i <= right:
if arr[i]<arr[pivot]:
arr[i],arr[index]=arr[index],arr[i]
index+=1
i+=1
arr[pivot],arr[index-1]=arr[index-1],arr[pivot]
return index-1
堆排序
首先了解堆是什么:数据结构:堆(Heap)
利用堆的特性,先构建一个最大堆,再利用shiftUp()进行修复排序
class Solution:
#堆排序
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
self.len=len(nums)
self.builtMinHeap(nums)
i=self.len-1
while i > 0:
nums[i],nums[0]=nums[0],nums[i]
self.len-=1
self.heap(nums,0)
i-=1
def builtMinHeap(self, arr):
i=len(arr)//2
while i >= 0:
self.heap(arr,i)
i-=1
return arr
def heap(self, arr,i):
smallest=i
left = 2*i + 1
right = 2*i + 2
if left < self.len and arr[left] > arr[smallest]:
smallest=left
if right < self.len and arr[right] > arr[smallest]:
smallest=right
if i != smallest:
arr[i],arr[smallest]=arr[smallest],arr[i]
self.heap(arr,smallest)
计数排序
计数排序需要先找到序列中的最大值,构建一个存储序列。统计计数,然后根据统计结构形成一个新序列。
当输入的元素是 n 个 0到 k 之间的整数,k不是很大并且序列比较集中时,计数排序是一个较有效的排序。
class Solution:
#计数排序
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
countBucket=[0 for i in range(max(nums)+1)]
#计数
for i in range(len(nums)):
countBucket[nums[i]]+=1
index=0
#重排列
for i in range(len(countBucket)):
while countBucket[i]>0:
nums[index]=i
index+=1
countBucket[i]-=1
return nums
桶排序
找到数组最大最小值,确定每个桶中元素个数(bucket size),算出总桶数。将元素按大小范围插入到桶中。每个桶内部排序(比如用插入排序),最后将桶中元素,挨个连接起来。
class Solution:
#桶排序
def sortColors(self, nums: List[int],bucketSize=2) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
length=len(nums)
arrMax,arrMin=max(nums),min(nums)
#根据最大最小值,确定桶的个数
bucketCount=(arrMax-arrMin)//bucketSize+1
bucket=[[] for i in range(bucketCount)]
#根据取值范围,把数据装入桶中
for i in range(length):
bucketIndex=(nums[i]-arrMin)//bucketSize
bucket[bucketIndex].append(nums[i])
#每个桶内部排序并合并
arrIndex=0
for i in range(bucketCount):
bucket[i]=self.insertSort(bucket[i])
nums[arrIndex:arrIndex+len(bucket[i])]=bucket[i]
arrIndex+=len(bucket[i])
return nums
def insertSort(self,arr):
length=len(arr)
for i in range(1,length):
preIndex=i-1
cur=arr[i]
while preIndex>=0 and arr[preIndex]>cur:
arr[preIndex+1]=arr[preIndex]
preIndex-=1
arr[preIndex+1]=cur
return arr
基数排序
利用位数排序,先排个位,再排十位…依次这么几轮,就可以了
class Solution:
#基数排序
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
length=len(nums)
arrMax=max(nums)
i=1
while i <= arrMax:
bucket=[[] for i in range(10)]
for j in range(length):
bucketIndex=(nums[j]//i)%10
bucket[bucketIndex].append(nums[j])
bucketIndex=0
arrIndex=0
while bucketIndex<10:
if len(bucket[bucketIndex])<1:
bucketIndex+=1
else:
nums[arrIndex]=bucket[bucketIndex].pop()
arrIndex+=1
i*=10
return nums
建议大家也动手实现一遍