什么是排序
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
- 插入排序
- 选择排序
- 冒泡排序
- 归并排序
- 快速排序
- 希尔排序
- 堆排序
- 桶排序
排序中的稳定性
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定的;否则称为不稳定的,如果要排序的内容,其原本的初始顺序存在意义,那么我们需要在二次排序的基础上保持原有排序的意义,才需要用到稳定性的算法。
排序中的递归:
简单地说,就是如果在函数中存在着调用函数本身的情况,这种现象就叫递归。
创建递归函数时,通常有三个主要结构需要考虑:边界条件、递归前进阶段和递归返回阶段。
def recurse(n):
if n<1:
return #边界条件
for i in range(1, n+1):
print(i) #递归前进阶段
recurse(n-1)
print('END') #递归后退阶段
recurse(5)
排序算法中的分治
分治指把一个问题分成多个相似的小问题解决的方法。
排序算法中的堆
堆是一种数据结构。它是一种特殊的完全二叉树。堆排序就是根据堆的特性设计的排序算法。
如果这个堆是一个大顶堆(最大的元素在堆顶),那么每个节点上的元素都应该比它的孩子节点上的元素要大,最大的元素在根节点上。
居中并且带尺寸的图片:
如果是小顶堆,那么每个节点上的元素都应该比它的孩子节点小,最小的元素在根节点上。
常见的八种排序的稳定性:
排序算法 | 稳定性 |
---|---|
插入排序 | 稳定 |
选择排序 | 不稳定 |
冒泡排序 | 稳定 |
归并排序 | 稳定 |
快速排序 | 不稳定 |
希尔排序 | 不稳定 |
堆排序 | 不稳定 |
桶排序 | 不稳定 |
八种排序的算法实现源码:
1.插入排序
def insert_sort(li):
for i in range(1, len(li)): # i 表示摸到的牌的下标
tmp = li[i]
j = i - 1 # j指的是手里的牌的下标
while j >= 0 and li[j] > tmp:
li[j + 1] = li[j]
j -= 1
li[j + 1] = tmp
# print(li)
2.选择排序
def select_sort_simple(li):
li_new = []
for i in range(len(li)):
min_val = min(li)
li_new.append(min_val)
li.remove(min_val)
return li_new
# 选择排序:一趟排序记录最小值,放在第一个位置
"""
在一趟排序记录列表无序区最小值放到第二位,以此为推
"""
def select_sort(li):
for i in range(len(li) - 1): # i 是第几趟
min_loc = i
for j in range(i + 1, len(li)):
if li[j] < li[min_loc]:
min_loc = j
li[i], li[min_loc] = li[min_loc], li[i]
print(li)
li = [3, 4, 2, 1, 5, 6, 8, 7, 9]
print(li)
# print(select_sort_simple(li))
select_sort(li)
print(li)
3.冒泡排序
def bubble_sort(li):
for i in range(len(li) - 1): # 第i趟
exchange = False # 加一个标志位使得for循环中的if判断为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
print(li)
if not exchange:
return
4.归并排序
- 排好序的归并
python底层使用的就是归并排序,随着时间的推移算法也可能改变原始底层算法
# 假设两边有序
def merge(li, low, mid, high):
i = low
j = mid + 1
ltmp = []
while i <= mid and j <= high: # 只要左右两边都有数
if li[i] < li[j]:
ltmp.append(li[i])
i += 1
else:
ltmp.append(li[j])
j += 1
# while 执行完,肯定有一部分没数了
while i <= mid:
ltmp.append(li[i])
i += 1
while j <= high:
ltmp.append(li[j])
j += 1
li[low:high + 1] = ltmp
- 使用递归方式排好序
def merge_sort(li,low,high):
if low < high:
mid = (low + high) // 2
merge_sort(li,low,mid)
merge_sort(li,mid + 1,high)
merge(li,low,mid,high)
li = list(range(1000))
import random
random.shuffle(li)
print(li)
merge_sort(li, 0, len(li) - 1)
print(li)
5.快速排序
- 快速排序特点:快
- 快速排序的思路:
1. 取一个元素P(第一个元素),使元素归位;
2.列表被P分成两部分,左边比P小,右边都比P大
3.递归完成
def partition(li, left, right):
tmp = li[left]
while left < right:
while left < right and li[right] >= tmp: # 找从右面tmp小的数
right -= 1 # 往左走一步
li[left] = li[right] # 把右边的值写到左边空位
# print(li, 'right')
while left < right and li[left] <= tmp:
left += 1
li[right] = li[left] # 把左边的值写到右边空位上
# print(li, 'left')
li[left] = tmp # 把tmp归位
return left
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)
6.希尔排序
希尔排序在原先的插入排序的过程中进行升级,更新迭代的过程
def insert_sort(li, gap):
for i in range(gap, len(li)): # i 表示摸到的牌的下标
tmp = li[i]
j = i - gap # j指的是手里的牌的下标
while j >= 0 and li[j] > tmp:
li[j + gap] = li[j]
j -= gap
li[j + gap] = tmp
def shell_sort(li):
d = len(li) // 2
while d >= 1:
insert_sort(li, d)
d //= 2
li = list(range(1000))
import random
random.shuffle(li)
shell_sort(li)
print(li)
7.堆排序
堆一般指的是二叉堆,顾名思义,二叉堆是完全二叉树或者近似完全二叉树。
def sift(li, low, high):
"""
:param li: 列表
:param low: 堆的根节点位置
:param high: 堆的最后一个元素位置
:return:
"""
i = low # 最开始指向根节点
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)
# 建堆完成了
# print(li)
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 = [i for i in range(100)]
import random
random.shuffle(li)
print(li)
heap_sort(li)
print(li)
# nlogn
Python自带树的方法库
import heapq # queue 优先队列
import random
li = list(range(100))
random.shuffle(li)
print(li)
heapq.heapify(li) # 建堆
heapq.heappop(li)
n = len(li)
for i in range(n):
print(heapq.heappop(li), end=",")
8.桶排序
把数据进行分组分桶排序
def bucket_sort(li, n=100, max_num=10000):
buckets = [[] for _ in range(n)] # 创建桶
for var in li:
i = min(var // (max_num // n), n - 1) # i 表示放在几号桶
buckets[i].append(var) # 把var放在对应的桶里面
# 保持桶内的顺序
for j in range(len(buckets[i]) - 1, 0, -1):
if buckets[i][j] < buckets[i][j - 1]:
buckets[i][j], buckets[i][j - 1] = buckets[i][j - 1], buckets[i][j]
else:
break
sorted_li = []
for buc in buckets:
sorted_li.extend(buc)
return sorted_li
import random
li = [random.randint(0, 10000) for i in range(100000)]
print(li)
li = bucket_sort(li)
print(li)
# 【0,2,4,3】
算法的路还很长,引用屈原的一句话:“路漫漫其修远兮吾将上下而求索。”如果有什么不对的地方,大家扶正。