目录
1、排序算法一览表
算法 | 平均时间复杂度 | 最优时间复杂度 | 最坏时间复杂度 | 辅助空间 | 稳定性 |
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
希尔排序 | O(nlog2n)-O(n^2) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 稳定 |
快速排序 | O(nlog2n) | O(nlog2n) | O(n^2) | O(log2n)-O(n) | 不稳定 |
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 不稳定 |
2、collections、heapq模块
2.1、collections模块
常用的函数
deque() | 类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop),双端队列 |
Counter() | 字典的子类,提供了可哈希对象的计数功能 |
OrderedDict() | 字典的子类,保存了他们被添加的顺序 |
deque用法:
(1)maxlen参数:默认为None,deque的长度为任意长度;设置maxlen=n,则长度为n,当deque满后,曾经的元素会从左边pop出去;
from collections import deque
dq = deque(maxlen=10)
for i in range(13):
dq.append(i)
print(dq) # deque([3, 4, 5, 6, 7, 8, 9, 10, 11, 12], maxlen=10)
(2)append():末尾追加完整元素 / appendleft():队首追加完整元素
from collections import deque
dq = deque([1,2,3])
# append()
dq.append([3,4])
print(dq) # deque([1, 2, 3, [3, 4]])
# appendleft()
dq.appendleft((5,4))
print(dq) # deque([(5, 4), 1, 2, 3, [3, 4]])
(3)clear():清空deque所有的元素;
(4)count(x):统计x元素有多少个;
(5)extend():队尾追加元素 / extendleft():队首追加元素
from collections import deque
dq = deque([1,2,3])
# extend()
dq.extend([3,4])
print(dq) # deque([1, 2, 3, 3, 4])
# extendleft()
dq.extendleft((5,4))
print(dq) # deque([4, 5, 1, 2, 3, 3, 4])
(6)index(x, start, stop):在start-stop这个范围内,查询x的第一个出现的位置;
(7)insert(i, x):在位置i,插入x;但是如果被限制了maxlen,deque满后,插入会报错;
from collections import deque
dq = deque([1,2,3], maxlen=3)
dq.insert(1, 10)
print(dq)
"""
执行结果
Traceback (most recent call last):
File "d:/python_算法/双端队列.py", line 5, in <module>
dq.insert(1, 10)
IndexError: deque already at its maximum size
"""
(8)pop():弹出队尾的第一个元素 / popleft():弹出队首的第一个元素;如果没有元素的话,就都会引发一个IndexError
(9)remove(value):删除deque中的第一个value的元素,如果没有的话就引发ValueError;
(10)reverse():逆序;
(11)rotate(n=1):向右循环移动 n 步。 如果 n 是负数,就向左循环。
from collections import deque
dq = deque([1,2,3,4,5])
dq.rotate(2)
print(dq) # deque([4, 5, 1, 2, 3])
dq.rotate(-3)
print(dq) # deque([2, 3, 4, 5, 1])
如果deque不是空的,向右循环移动一步就等价于 d.appendleft(d.pop())
, 向左循环一步就等价于 d.append(d.popleft())
。
Counter用法:
(1)基本用法:将内部字符串/标志好的单词,进行统计,返回类似dict的对象
from collections import Counter
test_str = "hellowordhellohadoop"
test_dic = {"red":2, "blue":3, "blake":1}
test1 = Counter(red=3, blue=2, hadoop=1) # Counter({'red': 3, 'blue': 2, 'hadoop': 1})
test2 = Counter(test_str) # Counter({'o': 5, 'l': 4, 'h': 3, 'e': 2, 'd': 2, 'w': 1, 'r': 1, 'a': 1, 'p': 1})
test3 = Counter(test_dic) # Counter({'blue': 3, 'red': 2, 'blake': 1})
(2)elements():返回一个迭代器,其中每个元素将重复出现计数值所指定次。 元素会按首次出现的顺序返回。 如果一个元素的计数值小于1,elements()会自动忽略。
from collections import Counter
test1 = Counter(red=3, blue=2, hadoop=1, spark=0, hive=-2)
print(sorted(test1.elements())) # ['blue', 'blue', 'hadoop', 'red', 'red', 'red']
(3)most_common(n):返回最多的n个元素;返回一个列表,其中包含 n 个最常见的元素及出现次数,按常见程度由高到低排序。 如果 n 被省略或为None,将返回计数器中的 所有 元素。计数值相等的元素按首次出现的顺序排序。
from collections import Counter
test1 = Counter(red=3, blue=2, hadoop=1, spark=10, hive=2)
print(test1.most_common(3)) # [('spark', 10), ('red', 3), ('blue', 2)]
(4)subtract():求两个Counter的差;
from collections import Counter
test1 = Counter(red=3, blue=2, hadoop=1, spark=10, hive=-2)
test2 = Counter(red=4, blue=1, hadoop=1, spark=-2)
test1.subtract(test2) # Counter({'spark': 12, 'blue': 1, 'hadoop': 0, 'red': -1, 'hive': -2})
print(test1)
(5)update():求两个Counter的和
from collections import Counter
test1 = Counter(red=3, blue=2, hadoop=1, spark=10, hive=-2)
test2 = Counter(red=4, blue=1, hadoop=1, spark=-2)
test1.update(test2) # Counter({'spark': 8, 'red': 7, 'blue': 3, 'hadoop': 2, 'hive': -2})
print(test1)
OrderedDict用法:
(1)基础用法:
from collections import OrderedDict
test_str = "ababsq"
a = OrderedDict.fromkeys(test_str) # OrderedDict([('a', None), ('b', None), ('s', None), ('q', None)])
print(a)
(2)popitem(last=True):有序字典的popitem()方法移除并返回一个 (key, value) 键值对。 如果 last 值为真,后进先出的顺序返回键值对,否则就按先进先出的顺序返回键值对。
from collections import OrderedDict
test_str = "ababsq"
# 设置last为默认,true
a = OrderedDict.fromkeys(test_str) # OrderedDict([('a', None), ('b', None), ('s', None), ('q', None)])
a.popitem(last=True)
print(a) # OrderedDict([('a', None), ('b', None), ('s', None)])
# 设置last为false
b = OrderedDict.fromkeys(test_str) # OrderedDict([('a', None), ('b', None), ('s', None), ('q', None)])
b.popitem(last=False)
print(b) # OrderedDict([('b', None), ('s', None), ('q', None)])
(3)move_to_end(key, last=True):将现有 key 移动到有序字典的任一端。 如果 last 为真值(默认)则将元素移至末尾;如果 last 为假值则将元素移至开头。如果 key 不存在则会触发KeyError。
from collections import OrderedDict
test_str = "ababsq"
# 设置last为默认,true
a = OrderedDict.fromkeys(test_str) # OrderedDict([('a', None), ('b', None), ('s', None), ('q', None)])
a.move_to_end('b', last=False)
print(a) # OrderedDict([('b', None), ('a', None), ('s', None), ('q', None)])
a.move_to_end('a', last=True)
print(a) # OrderedDict([('b', None), ('s', None), ('q', None), ('a', None)])
2.2、heapq模块:内置库模块,生成最小堆
基本用法:
(1)heapq.heapify(x):将list x 转换成堆,原地,线性时间内;
import heapq
ls = [6,1,4,5,3,2]
heapq.heapify(ls)
print(ls) # [1, 3, 2, 5, 6, 4]
(2)heapq.heappush(heap, item):将 item 的值加入 heap 中,保持堆的不变性 ;也可以用该方法生成堆
import heapq
ls = []
# 加入数据
for i in range(10,1,-1):
heapq.heappush(ls, i)
print(ls) # [2, 3, 5, 4, 8, 9, 6, 10, 7]
(3)heapq.heappop(heap):弹出并返回 heap 的最小的元素,保持堆的不变性。如果堆为空,抛出IndexError。使用heap[0],可以访问最小值,而不弹出;
import heapq
ls = []
# 加入数据
for i in range(10,1,-1):
heapq.heappush(ls, i)
print(ls) # [2, 3, 5, 4, 8, 9, 6, 10, 7]
print(heapq.heappop(ls)) # 2
print(ls) # [3, 4, 5, 7, 8, 9, 6, 10]
(4)heapq.heappushpop(heap, item) :将 item 放入堆中,然后弹出并返回 heap 的最小元素。该组合操作比先调用heappush()再调用heappop()更优;
(5)heapq.heapreplace(heap, item):弹出并返回 heap 中最小的一项,同时推入新的 item。 堆的大小不变。 如果堆为空则引发IndexError;这个步骤比heappop()再调用heappush()更高效;
(6)heapq.nlargest(n, iterable, key=None):从 iterable 所定义的数据集中返回前 n 个最大元素组成的列表。
(7)heapq.nsmallest(n, iterable, key=None):从 iterable 所定义的数据集中返回前 n 个最小元素组成的列表。
3、冒泡排序
说明:
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数,也就是有序部分;
- 针对所有的元素重复以上的步骤,除了有序部分;
- 重复步骤1~3,直到排序完成。
import random
ls = list(range(1000))
random.shuffle(ls)
nums = len(ls)
for i in range(nums):
for j in range(0, nums-i-1):
if ls[j] > ls[j+1]: ls[j], ls[j+1] = ls[j+1], ls[j]
print(ls)
4、选择排序
说明:
- 选择排序分为有序区(最开始是空的)和无序区;
- 每次寻找无序区中的最小值放入有序区中;
- 直到无序区为空。
import random
ls = list(range(1000))
random.shuffle(ls)
nums = len(ls)
# 方法一:只要比对应位的值大,就发生交换
# for i in range(nums):
# for j in range(i+1, nums):
# if ls[i] > ls[j]:ls[i], ls[j] = ls[j], ls[i]
# 方法二:记录最小值的下标
for i in range(nums):
min_index = i
for j in range(i+1, nums):
if ls[min_index] > ls[j]:min_index = j
ls[i], ls[min_index] = ls[min_index], ls[i]
print(ls)
5、插入排序
说明:
- 分为有序区和无序区,默认第一位为有序的;
- 无序区的第一位元素和前面有序的元素进行比较,插入对应位置;
- 直到无序区为空。
import random
ls = list(range(1000))
random.shuffle(ls)
nums = len(ls)
for i in range(1, nums):
flag = ls[i]
flag_index = i
# 判断:如果当前数比前一个数小的话,则前一个数向后移动一位
while flag_index > 0 and flag < ls[flag_index-1]:
ls[flag_index] = ls[flag_index-1]
flag_index = flag_index - 1
ls[flag_index] = flag
print(ls)
6、希尔排序
说明:
- 希尔排序就是每次将元素进行基本有序的排序;
- 每次对间隔为n的元素进行比较,然后排序,n=len(ls)//2,下一次为n//2,以此类推;
- 当间个为1的时候,即可将ls排序完成。
import random
ls = list(range(1000))
random.shuffle(ls)
nums = len(ls)
# 进行排序
def insert_sort_gap(ls, gap):
for i in range(gap, len(ls)):
tmp = ls[i]
j = i - gap
while j >= 0 and ls[j] > tmp:
ls[j+gap] = ls[j]
j -= gap
ls[j+gap] = tmp
def shell_sort(ls):
d = len(ls) // 2
while d >= 1:
insert_sort_gap(ls, d)
d //= 2
shell_sort(ls)
print(ls)
7、归并排序
说明:
- 将一个ls,进行不断的分区,直到每个区只有一个元素,一个元素都可认为是有序的;
- 再将只有一个元素的区进行向上归并,将小的元素先放入新的ls,再将新的ls覆盖曾经的ls对应的位置;
- 重复以上步骤,最后归并结束,ls有序。
import random
# 归并
def merge(ls, low, mid, high):
i = low
j = mid+1
ltmp = []
# 两边都有数
while i <= mid and j <= high:
if ls[i] < ls[j]:
ltmp.append(ls[i])
i += 1
else:
ltmp.append(ls[j])
j += 1
# 循环结束,有一部分没数
while i <= mid:
ltmp.append(ls[i])
i += 1
while j <= high:
ltmp.append(ls[j])
j += 1
ls[low:high+1] = ltmp
# 进行分区
def merge_sort(ls, low, high):
if low < high:
mid = (low + high) // 2
merge_sort(ls, low, mid)
merge_sort(ls, mid+1, high)
merge(ls, low, mid, high)
ls = list(range(1000))
random.shuffle(ls)
merge_sort(ls, 0, len(ls)-1)
print(ls)
8、快速排序
说明:
- 定义双端指针,left,right,一般情况下left第一位为基准值;
- left和right相等的时候,则代表第一次遍历结束,left=right的位置是有序的,且left往前的部分都是小于right往后的部分;
- 对left=right这个位置进行切割,成两部分,重复上面的操作。
import random
ls = list(range(1000))
random.shuffle(ls)
def partition(ls, left, right):
tmp = ls[left]
while left < right:
while left < right and ls[right] >= tmp:
right -= 1
ls[left] = ls[right]
while left < right and ls[left] <= tmp:
left += 1
ls[right] = ls[left]
ls[left] = tmp
return left
def quick_sort(ls, left, right):
if left < right:
mid = partition(ls, left, right)
quick_sort(ls, left, mid-1)
quick_sort(ls, mid+1, right)
quick_sort(ls, 0, len(ls)-1)
print(ls)
9、堆排序
说明:
- 堆分为最大堆和最小堆,堆顶为最大值、最小值;
- 每次pop出最大值、最小值,存入新的ls即为有序;
- 以下方法使用heapq模块,生成最小堆。
import heapq, random
ls = list(range(1000))
random.shuffle(ls)
ls1 = []
# 生成最小堆
heapq.heapify(ls)
for i in range(len(ls)):
# 每次pop出最小值
ls1.append(heapq.heappop(ls))
print(ls1)