1. 三种低级排序
冒泡排序
每一趟选出一个最大值,排在最后一个
时间复杂度:o(n2)
def bubble_sort(alist):
n = len(alist)
for i in range(n-1,0,-1):
count = 0
for j in range(i):
if alist[j] > alist[j+1]:
alist[j],alist[j+1] = alist[j+1],alist[j]
count += 1
if 0 == count:
break
return alist
选择排序
每一趟选出一个最小值,放到前面
时间复杂度:o(n2)
def select_sort(alist):
n = len(alist)
for i in range(n-1):
min_index = i
for j in range(i+1,n):
if alist[j] < alist[min_index]:
min_index = j
alist[i],alist[min_index] = alist[min_index], alist[i]
return alist
插入排序
不断地从后面选一个数,然后插入到前面已经有序的序列里
时间复杂度:o(n2)
# 插入排序
def insert_sort(alist):
n = len(alist)
for i in range(1,n):
while (i>0):
if alist[i] < alist[i-1]:
alist[i], alist[i-1] = alist[i-1], alist[i]
i -= 1
else:
break
return alist
希尔排序
是一种分组插入排序算法
时间复杂度:o(nlogn) ~ o(n2)
# 希尔排序
def shell_sort(alist):
n = len(alist)
gap = n // 2
while gap > 0:
for i in range(gap,n):
j = i
while j > 0:
if alist[j] > alist[j-gap]:
alist[j], alist[j-gap] = alist[j-gap], alist[j]
j -= gap
else:
break
gap //= 2
return alist
2. 三种高级排序
快速排序
指定第一个数为mid_value,排序使得mid_value左边的数比mid_value小,右边的数比mid_value大,然后分别对左边和右边进行递归排序。
partition模板
partition,它是 “分而治之” 思想当中 “分” 的那一步。经过 partition 操作以后,每一次都能排定一个元素,并且这个元素左边的数都不大于它,这个元素右边的数都不小于它,并且我们还能知道排定以后的元素的索引。
def partition(nums, left, right):
pivot = nums[left]#初始化一个待比较数据
i,j = left, right
while(i < j):
while(i<j and nums[j]>=pivot): #从后往前查找,直到找到一个比pivot更小的数
j-=1
nums[i] = nums[j] #将更小的数放入左边
while(i<j and nums[i]<=pivot): #从前往后找,直到找到一个比pivot更大的数
i+=1
nums[j] = nums[i] #将更大的数放入右边
#循环结束,i与j相等
nums[i] = pivot #待比较数据放入最终位置
return i #返回待比较数据最终位置
快排示例
# 时间复杂度:o(nlogn)
#快速排序
def quicksort(nums, left, right):
if left < right:
index = partition(nums, left, right)
quicksort(nums, left, index-1)
quicksort(nums, index+1, right)
arr = [1,3,2,2,0]
quicksort(arr, 0, len(arr)-1)
print(arr)
归并排序
拆分到单个元素,然后两个两个往上进行递归合并。设置left 和right两个游标,进行合并。
时间复杂度:o(nlogn)
# 归并排序
def merge_sort(alist):
n = len(alist)
if n <= 1:
return alist
mid = n//2
left = merge_sort(alist[:mid])
right = merge_sort(alist[mid:])
left_point,right_point = 0,0
result = []
while left_point < len(left) and right_point < len(right):
if left[left_point] <= right[right_point]:
result.append(left[left_point])
left_point += 1
else:
result.append(right[right_point])
right_point += 1
result += left[right_point:]
result += right[left_point:]
return result
最变态的堆排序来了😈
堆排序
构造堆:从小堆到大堆,先看最后一个非叶子节点,从下往上
时间复杂度 : o(nlogn)
# 堆排序
# 向下调整函数的实现, 此处建立大根堆,可实现数组升序排列
def sift(alist, low, high):
# 假设只有根节点需要调整,两棵子树都是堆
i = low
j = i *2 +1 #j指向i的左子树
tmp = alist[i]
while j <=high:
if j+1<= high and alist[j] < alist[j+1] #右子树比较大,则指向右子树
j = j+1
if alist[j] > tmp: # 若子树的值比较大,则根节点换成子树,然后向下看一层
alist[i] = alist[j]
i = j
j = i *2 +1
else:
alist[i] = tmp # 子树的值小于根节点,则根节点就放在这一层
break
else:
alist[i] = tmp # j越界跳出循环,则把根节点放在叶子节点
def heap_sort(alist):
# 1、建堆
# 先找到最后一个不是叶子节点的根节点,为(n-2)//2 (若叶子节点为i,则他的父节点为(i-1)//2 )
# 再向上循环根节点,从小到大
n = len(alist)
for i in range((n-2)//2, -1, -1):
sift(alist,i,n-1)
# 2、挨个出数,按升序排列
for i in range(n-1, -1, -1):
alist[0], alist[i] = alist[i], alist[0]
sift(alist, 0, i-1)
if __name__ == '__main__':
li = [54, 26, 93, 17, 77, 31, 44, 55, 20, 13]
heap_sort(li)
print(li)
3.topk问题
leetcode 215.数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
堆排序的思想
可以先建立k个元素的小根堆,则堆顶就是最小值。若k之后的元素大于堆顶,则应该插入该堆,从k开始,一直循环遍历完整个数组,则堆顶就是第k大的数。
# 堆排序的思想
#可借助python中的heapq模块实现堆的功能, 注意建立的是小根堆
class Solution1:
def findKthLargest(self, nums: List[int], k: int) -> int:
import heapq as hq
heap = []
for i in nums:
hq.heappush(heap, i)
if len(heap) > k:
hq.heappop(heap)
return heap[0]
# 自己实现堆
class Solution2:
def findKthLargest(self, nums: List[int], k: int) -> int:
n = len(nums)
heap = nums[:k]
# 建立含k个元素的小根堆
for i in range((k-2)//2, -1, -1):
self.sift(heap, i, k-1)
#若k之后的元素大于根节点,则将该元素与根节点替换,然后做一次调整
for j in range(k,n):
if nums[j] > heap[0]: #找前k大的数
heap[0] = nums[j]
self.sift(heap, 0 , k-1)
# print(heap)
return heap[0] #堆顶就是第k大的数了
# 注意这里要建立小根堆
def sift(self, alist, low, high):
# 假设只有根节点需要调整,两棵子树都是堆
i = low
j = i *2 +1 #j指向i的左子树
tmp = alist[i]
while j <=high:
if j+1<= high and alist[j] > alist[j+1]: #右子树比较小,则指向右子树
j = j+1
if alist[j] < tmp: # 若子树的值比较小,则根节点换成子树,然后向下看一层
alist[i] = alist[j]
i = j
j = i *2 +1
else:
alist[i] = tmp # 子树的值大于根节点,则根节点就放在这一层
break
else:
alist[i] = tmp # j越界跳出循环,则把根节点放在叶子节点
快排思想
快排的思想是,每次把第一个值作为mid_value放到数组的中间,使得mid_value左边的数都比它小,右边的数都比它大。那么,现在可以返回mid_value的下标p,比较k(k = len(nums) - k)和p的大小,若k<p,则要找的这个数在nums[:p-1]里,若k>p, 则要找的这个数在nums[p+1:]里。
# 快排思想
# 时间复杂度相比堆排序慢
class Solution:
def findKthLargest(self, nums, k) -> int:
# 第k个最大的元素,即升序排列后,index为len(nums)-k
k = len(nums) - k
low = 0
high = len(nums) - 1
while low <= high:
p = self.patition(nums, low, high)
if k < p:
high = p-1
elif k > p:
low = p+1
else:
return nums[p]
return -1
def patition(self, alist, low, high):
mid_value = alist[low]
while low <high:
while low < high and alist[high] >= mid_value:
high -= 1
alist[low] = alist[high]
while low < high and alist[low] <= mid_value:
low += 1
alist[high] = alist[low]
alist[low] = mid_value
return low
image.png
(图片来自知乎)
网友评论