python实现排序(冒泡,选择,插入,希尔,快排,归并,堆排)
写在前面
还是从最基本的开始,实际上4月各种实习面试里最容易问的大概是快排,归并,堆排也有概率问到。周末摸鱼回顾一下!(误)
稳定性
首先说下稳定性:
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的
算法解析实现
冒泡排序
1.快速理解/复杂度
时:时间复杂度O(n^2)
空:空间复杂度O(1)
稳定
冒泡排序对数据操作n-1轮,每轮找出一个最大(小)值。
操作指对相邻两个数比较与交换,每轮会将一个最值交换到数据列首(尾),像冒泡一样。
每轮操作O(n)次,共O(n)轮,时间复杂度O(n^2)。
额外空间开销出在交换数据时那一个过渡空间,空间复杂度O(1)。
2.代码
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
#冒泡
n=len(nums)
for i in range(n):
for j in range(1,n-i):
if nums[j-1]>nums[j]:
nums[j-1],nums[j]=nums[j],nums[j-1]
return nums
##冒泡优化
n=len(nums)
for i in range(n):
flag=True
for j in range(1,n-i):
if nums[j-1]>nums[j]:
nums[j-1],nums[j]=nums[j],nums[j-1]
flag=False
#某次遍历没交换数字说明后面已经排好序了
if flag:
break
return nums
选择排序
时:时间复杂度O(n^2)
空:空间复杂度O(1)
稳定
1.快速理解与复杂度
每次找到未排序中最小/大的数放到排序的数组的末尾
- 运行时间和输入无关
为了找出最小的元素而扫描一遍数组并不能为下一遍扫描提供任何实质性帮助的信息。【一个已经有序的数组或者数组内元素全部相等的数组和一个元素随机排列的数组所用的排序时间一样长】 - 数据移动是最少的
选择排序的交换次数和数组大小关系是线性关系,选择排序无疑是最简单直观的排序。
- 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
- 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的下一个。
以此类推,直到所有元素均排序完毕。
2.代码
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
#选择排序
n=len(nums)
for i in range(n):
min_index=i
for j in range(i+1,n):
if nums[j]<nums[min_index]:
min_index=j
nums[min_index],nums[i]=nums[i],nums[min_index]
return nums
插入排序
1.快速理解/复杂度
时:时间复杂度O(n^2)
空:空间复杂度O(1)
稳定
插入排序的工作原理是,对于每个未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
- 从第一个元素开始,该元素可以认为已经被排序,取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果被扫描的元素(已排序)大于新元素,将该元素后移一位
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置,将新元素插入到该位置后
- 重复上述步骤
2. 代码
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
#插入排序
n=len(nums)
for i in range(1,n):
#key为已排序部分的末尾,需插入元素的前一个
key=i-1
cur=nums[i]
while key>=0 and nums[key]>cur:
nums[key+1]=nums[key]
key-=1
nums[key+1]=cur
return nums
希尔排序
1.快速理解与复杂度
时:时间复杂度O(nlogn)
空:空间复杂度O(1)
不稳定
希尔排序是插入排序的高效实现,对简单插入排序减少移动次数优化而来。实质:分组插入排序
简单插入排序每次插入都要移动大量数据,前后插入时的许多移动都是重复操作,若一步到位移动效率会高很多。
若序列基本有序,简单插入排序不必做很多移动操作,效率很高。
希尔排序将序列按固定间隔划分为多个子序列,在子序列中简单插入排序,先做远距离移动使序列基本有序;逐渐缩小间隔重复操作,最后间隔为1时即简单插入排序。
2.代码
希尔代码这里不是自己写的,参考:link
def shell_sort(ary):
count = len(ary)
gap = round(count / 2)
# 双杠用于整除(向下取整),在python直接用 “/” 得到的永远是浮点数,
# 用round()得到四舍五入值
while gap >= 1:
for i in range(gap, count):
temp = ary[i]
j = i
while j - gap >= 0 and ary[j - gap] > temp: # 到这里与插入排序一样了
ary[j] = ary[j - gap]
j -= gap
ary[j] = temp
gap = round(gap / 2)
return ary
快速排序(!!!)
1.快速理解/复杂度
时间复杂度O(nlogn)
空间复杂度O(logn)
不稳定
快速排序基于选择划分,是简单选择排序的优化。
每次划分将数据选到基准值两边,循环对两边的数据进行划分,类似于二分法。
算法的整体性能取决于划分的平均程度,即基准值的选择,此处衍生出快速排序的许多优化方案,甚至可以划分为多块。
基准值若能把数据分为平均的两块,划分次数O(logn),每次划分遍历比较一遍O(n),时间复杂度O(nlogn)。
额外空间开销出在暂存基准值,O(logn)次划分需要O(logn)个,空间复杂度O(logn)
2.代码
# -*- coding:utf-8 -*-
class Solution:
def findKth(self, a):
# write code here
def quicksort(nums,l,r):
if l>=r: return
i,j=l,r
base=nums[l]
while i<j:
while i<j and nums[j]>=base:
j-=1
nums[i]=nums[j]
while i<j and nums[i]<=base:
i+=1
nums[j]=nums[i]
nums[i]=base
quicksort(nums, l, i-1)
quicksort(nums, i+1, r)
return nums
return quicksort(a, 0, len(a)-1)
归并排序(!!)
1.理解/复杂度
时间复杂度O(nlogn)
空间复杂度O(n)
稳定
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
merge_sort():如果只有1个元素的列表,不需要分两个合并
否则先确立mid=len(arr)//2分两部分数组,
left,right
merge():合并上部分的left,right,主要是依据合并两个数组的方法。
归并排序划分子问题采用二分法,共需O(logn)次划分,当然需要相当次合并;每次合并遍历比较O(n)。时间复杂度O(nlogn)。
额外空间开销出在合并过程中的一个暂存数组,空间复杂度O(n)。
2.代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
# 将给定数组排序
# @param arr int整型一维数组 待排序的数组
# @return int整型一维数组
#
class Solution:
def MySort(self , arr ):
#归并排序
def merge_sort(arr):
if len(arr)<2: return arr
mid=len(arr)//2
left=merge_sort(arr[:mid])
right=merge_sort(arr[mid:])
return merge(left,right)
def merge(left,right):
i,j=0,0
res=[]
while i<len(left) and j<len(right):
if left[i]<right[j]:
res.append(left[i])
i+=1
else:
res.append(right[j])
j+=1
if i<len(left):res+=left[i:]
if j<len(right):res+=right[j:]
return res
return merge_sort(arr)
堆排序(!)
class Sorts:
def build_heap(self,heap):
heapsize = len(heap)
for i in range((heapsize-2)//2,-1,-1):
self.min_heap(heap,heapsize,i)
def min_heap(self,heap,heapsize,root):
min_i = root
left = 2*root+1
right = left+1
if left<heapsize and heap[left]>heap[min_i]:
min_i = left
if right < heapsize and heap[right] > heap[min_i]:
min_i = right
if min_i!=root:
heap[min_i],heap[root] = heap[root],heap[min_i]
self.min_heap(heap,heapsize,min_i)
def heap_sort(self,heap):
self.build_heap(heap)
for i in range(len(heap)-1,-1,-1):
heap[i],heap[0] = heap[0],heap[i]
self.min_heap(heap,i,0)
if __name__ == '__main__':
a = [3,5,6,7,1,2,4]
print(a)
s = Sorts()
s.heap_sort(a)
print(a)