TOP_K問題
問題簡介
找出一個列表中前k大的數
三種解決方案
-
排序後切片,時間複雜度O(nlogn+k)
-
簡單排序:冒泡排序爲例,時間複雜度O(kn)
-
小根堆,時間複雜度【建堆klogk + (n-k)logk = nlogk】
時間複雜度比較
# -*- encoding: utf-8 -*-
"""
@File : top_k.py
@Time : 2020/1/15 12:38 下午
@Author : zhengjiani
@Email : [email protected]
@Software: PyCharm
"""
# 找前5個最大的,使用小根堆
# 使用內置heapq
import heapq
from leetcode.cal_time import cal_time
"""
1.取列表前k個元素建立一個小根堆,堆頂就是目前第K大的數
2.依次向後遍歷原列表,對於列表中的元素,如果小於堆頂,則忽略該元素;如果大於堆頂,則將堆頂更換爲該元素,並且對堆進行一次調整
3.遍歷列表所有元素後,倒序彈出堆頂
"""
@cal_time
def func_1():
li = [9,5,7,8,2,6,4,1,3]
return heapq.nlargest(5,li)
@cal_time
def func_2():
# 排序後切片
li = [9, 5, 7, 8, 2, 6, 4, 1, 3]
li.sort()
return li[:3:-1]
@cal_time
def func_3():
li = [9, 5, 7, 8, 2, 6, 4, 1, 3]
_bubble_sort(li, 5)
return li[:3:-1]
# 冒泡排序,只冒前5位
def _bubble_sort(li, k):
for i in range(k):
exchange = False
for j in range(len(li) - i - 1):
if li[j] > li[j + 1]:
li[j], li[j + 1] = li[j + 1], li[j]
exchange = True
if not exchange:
break
print(func_1())
print(func_2())
print(func_3())
== 計算時間的裝飾器函數 ==
# -*- encoding: utf-8 -*-
"""
@File : cal_time.py
@Time : 2020/1/3 5:51 下午
@Author : zhengjiani
@Email : [email protected]
@Software: PyCharm
"""
import time
def cal_time(func):
def wrapper(*args, **kwargs):
t1 = time.time()
result = func(*args, **kwargs)
t2 = time.time()
print("%s running time: %s secs." % (func.__name__,t2-t1))
return result
return wrapper
堆
構造堆以及堆排序
# -*- encoding: utf-8 -*-
"""
@File : heap_sort.py
@Time : 2020/1/14 2:22 下午
@Author : zhengjiani
@Email : [email protected]
@Software: PyCharm
"""
# 堆排序
# 大根堆
def sift(li,low,high):
# li表示樹,low表示樹根,high表示樹的最後一個節點的位置
tmp = li[low]
i = low
j = 2 * i + 1 # 初始j指向空位的左孩子
# i指向空位,j指向兩個孩子
while j <= high: # 循環退出的第二種情況:j>high,說明空位i是葉子節點
if j+1 <= high and li[j] < li[j+1]: # 如果右孩子存在並且比左孩子大,指向右孩子
j += 1
if li[j] > tmp:
li[i] = li[j]
i = j
j = 2 * i + 1
else: # 循環退出的第一種情況:j位置的值都比tmp小,說明兩個孩子都比tmp小
break
li[i] = tmp
# 小根堆
def sift_small(li,low,high):
# li表示樹,low表示樹根,high表示樹的最後一個節點的位置
tmp = li[low]
i = low
j = 2 * i + 1 # 初始j指向空位的左孩子
# i指向空位,j指向兩個孩子
while j <= high: # 循環退出的第二種情況:j>high,說明空位i是葉子節點
if j+1 <= high and li[j] > li[j+1]: # 如果右孩子存在並且比左孩子大,指向右孩子
j += 1
if li[j] < tmp:
li[i] = li[j]
i = j
j = 2 * i + 1
else: # 循環退出的第一種情況:j位置的值都比tmp大,說明兩個孩子都比tmp小
break
li[i] = tmp
def heap_sort_small(li):
n = len(li)
# 1.構造堆
for low in range(n//2-1, -1, -1):
sift_small(li, low, n-1)
# 2.挨個出數
for high in range(n-1, -1, -1):
li[0], li[high] = li[high], li[0] # 退休棋子
sift_small(li, 0, high-1)
# 構造堆,要求是完全二叉樹,整個堆最後一個元素的位置當作high
def heap_sort(li):
n = len(li)
# 1.構造堆
for low in range(n//2-1, -1, -1):
sift(li, low, n-1)
# 2.挨個出數
for high in range(n-1, -1, -1):
li[0], li[high] = li[high], li[0] # 退休棋子
sift(li, 0, high-1)