您的位置:首页 > 其它

排序算法: 三大中级排序算法,原理解析及用法

2018-11-24 00:19 92 查看

三大中级算法

  • 难度 ★★
  • 算法复杂度O(nlogn)
  • 一般情况下排序时间: 快速排序< 归并排序 < 堆排序
  • 快速排序
    : 缺点极端情况下效率低
  • 堆排序
    : 缺点在快的排序算法中相对慢
  • 归并排序
    : 缺点要有额外内存空间

快速排序 ★★

quick Sort

算法复杂度: O(nlogn) <n乘logn>

思路

每趟取第一个元素,让列表被该元素分为两部分,左边的比他小,右边比他大, 重复用每次得来的第一个元素递归

问题1:

递归py有最大深度问题999次!(虽然可以设置)

问题2:

最坏的情况,如果列表本身是一个倒序列表,那么效率相对较低

(解决方案:随机快排,一开始就随机取一个数放在最左边开始排)

def quick_sort(li, left, right):
"""
快速排序
"""
if left < right:  # 至少两个元素
mid = partition(li, left, right)
quick_sort(li, left, mid-1)
quick_sort(li, mid+1, right)

def partition(li, left, right):
"""
将下标为0的元素为参照物,左边的放比他小的,右边放比他大的
"""
tmp = li[left]  # 第一个位置
while left < right:  # 一直循环
while left < right and li[right] >= tmp:  # 右边开始找比tmp 小的数放到左边的空位
right -= 1  # 往左走一步
li[left] = li[right]  # 把右边的值写到左边空位
# 有可能所有数都比自己大
while left < right and li[left] <= tmp:
left += 1
li[right] = li[left]  # 把左边的值写到右边的空位上
li[left] = tmp  # 把tmp归位
return left  # 或者返回right都行

li_test = [3, 2, 7, 1, 6, 9, 8, 4]
quick_sort(li_test, 0, len(li_test)-1)
print(li_test)

堆排序 ★★

算法复杂度: O(nlogn) <n乘logn>

前提

二叉树的知识储备!!

效率:

快排的时间复杂度优于堆排

def sift(li, low, high):
"""
调整堆
:param li: 列表
:param low: 堆的根节点
:param high: 堆的最后一个元素位置
:return:
"""
i = low  # i最开始指向的父
j = 2 * i + 1  # 堆顶左孩子
tmp = li[low]  # 把堆顶存起来
while j <= high:  # 只要j位置有数
if j + 1 <= high and li[j + 1] > li[j]:  # 如果右还在比较大且右孩子有
j = j + 1  # j指向右孩子
if li[j] > tmp:
li[i] = li[j]
i = j  # 往下一步看
j = 2 * i + 1
else:  # tmp更大,把tmp放到i位置上
li[i] = tmp  # 把tmp放到某一级领导位置上
break
else:
li[i] = tmp  # 把tmp放到叶子节点上

def heap_sort(li):
"""
堆排序
"""
n = len(li)
for i in range((n-2)//2, -1, -1):
# i表示建堆时调整的部分的根下标
sift(li, i, n-1)
# 建堆完成了
for i in range(n-1, -1, -1):
# i向当前堆最后一个元素
li[0], li[i] = li[i], li[0]
sift(li, 0, i-1)  # i-1是新的high

li_test = [3, 2, 7, 1, 6, 9, 8, 4]
heap_sort(li_test)
print(li_test)

堆排序py 模块

import heapq  # q : queue 优先列队
import random

li = list(range(100))

random.shuffle(li)  # 打乱

print(li)

heapq.heapify(li)  # 建堆

n = len(li)
for i in range(n):
print(heapq.heappop(li), end=',')

堆排序 topk问题 【常用】

问题:比如热搜网,有n个数,取前k打的数(排序好的)

思路

  1. 取列表前k个元素建立小根堆,堆顶是目前第k大的数
  2. 依次向后面遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素,反之换为该元素,并进行一次调整
  3. 遍历素有元素后,倒序弹出堆顶
import random

def sift(li, low, high):
"""
调整为小根堆
:param li: 列表
:param low: 堆的根节点
:param high: 堆的最后一个元素位置
:return:
"""
i = low  # i最开始指向的父
j = 2 * i + 1  # 堆顶左孩子
tmp = li[low]  # 把堆顶存起来
while j <= high:  # 只要j位置有数
if j + 1 <= high and li[j + 1] < li[j]:  # 如果右还在比较大且右孩子有
j = j + 1  # j指向右孩子
if li[j] < tmp:
li[i] = li[j]
i = j  # 往下一步看
j = 2 * i + 1
else:  # tmp更大,把tmp放到i位置上
li[i] = tmp  # 把tmp放到某一级领导位置上
break
else:
li[i] = tmp  # 把tmp放到叶子节点上

def top_key(li, k):
heap = li[0:k]
for i in range((k-2)//2, -1, -1):
sift(heap, i, k-1)
# 1.建堆
for i in range(k, len(li)-1):
if li[i] > heap[0]:
heap[0] = li[i]
sift(heap, 0, k-1)
# 2.遍历
for i in range(k-1, -1, -1):
heap[0], heap[i] = heap[i], heap[0]
sift(heap, 0, i-1)
# 3.出数
return heap

li = list(range(1000))
random.shuffle(li)

print(top_key(li, 10))  # 测试 前10数

归并排序 ★★

算法复杂度: O(nlogn) <n乘logn>

空间复杂度: O(n)

思路

假设两个列表已有序,那么将他们合并在一起,将列表越分越小,直到分为一个元素

def merge(li, low, mid, high):
"""
:param li: 列表
:param low: 左列表第一个元素
:param mid: 左列表最后一个元素,那么右列表第一个就是mid+1
:param high: 右列表最后一个元素
:return:
"""
i = low  # 第一段第一个元素
j = mid + 1  # 第二段的第一个元素
tmp_list = []
while i <= mid and j <= high:  # 必须左右有数
if li[i] < li[j]:
tmp_list.append(li[i])
# 移动后箭头必须移动一位,因为已经把小的值提出到临时list中
i += 1
else:
tmp_list.append(li[j])
j += 1
# while 执行完,说明有一段执行完了,剩下的接到tmp中即可
# 分别判断一下那个还有数
while i <= mid:
tmp_list.append(li[i])
i += 1
while j <= high:
tmp_list.append(li[j])
j += 1
li[low:high+1] = tmp_list  # 写回去

def merge_sort(li, low, high):
if low < high:  # 至少有2个元素,递归
mid = (low + high) // 2  # 整除2
merge_sort(li, low, mid)
merge_sort(li, mid+1, high)
merge(li, low, mid, high)

li_test = [3, 2, 7, 1, 6, 9, 8, 4]
merge_sort(li_test, 0, len(li_test)-1)
print(li_test)
阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: