东阳的学习记录,坚持就是胜利!
选择排序的基本思想是:每一趟(如第i趟)在后面n-i+1(i = 1, 2, …, n-1)个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到第n-1趟做完,待排序元素只剩下1个,就不用再选了。堆排序是重点
简单排序算法
根据上面的简单排序思想,可以很直观地得出简单排序算法的思想。
def select_sort(alist):
l = len(alist)
for i in range(l-1):
min_index = i
j = i+1
# 找出最小关键字的下标——min_index
while j < l:
if alist[j] < alist[min_index]:
min_index = j
j += 1
# 交换
if (min_index != i):
alist[i], alist[min_index] = alist[min_index], alist[i]
算法分析
- 时间复杂度:都是O(n**2)
- 空间复杂度:O(1)
- 不稳定
堆排序
堆排序是一种树形选择排序方法,其特点是:在排序过程中,将L[1…n]视为一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大或最小的元素。
构造初始堆
堆的定义如下:n个关键字序列L[1…n]称为堆,当且仅当该序列满足。
- L(i) < L(2i) 且 L(i) < L(2i+1) (小根堆)
- L(i) > L(2i) 且 L(i) < L(2i+1) (大根堆)
对初始序列建堆,是一个反复筛选的过程。n个结点的完全二叉树,最后一个结点是第 n // 2个结点的孩子。从该结点开始筛选,如果根节点的关键字小于其孩子的关键字,则与之交换。对[n //2 ~ 1]执行同样的操作。
def build_max_heap(alist):
"""构建初始堆"""
l = len(alist)
i = l // 2
while i > 0:
i -= 1
adjust_down(alist,i,l)
def adjust_down(alist, k, l):
"""
alist:
k: 当前正在调整的双亲结点的编号
l:
"""
alist[0] = alist[k]
i = 2 * k
while i < l:
if i < l - 1 and alist[i] < alist[i+1]:
i += 1
if alist[0] > alist[i]:
break
else:
alist[k] = alist[i]
k = i
i *= 2
alist[k] = alist[0]
向下调整的时间与树高有关,为O(h),即log(n)。建堆过程中每次向下调整时,大部分结点的高度都较小。因此,在元素个数为n的序列上建堆,其时间复杂度为O(n)。
对构建好的大根堆进行排序
应用堆这种数据结构进行排序的思路也很简单:首先将存放在L[1, n]中的各个元素简称初始堆,由于堆本身的特性,堆定元素就是最大值。输出堆定元素后,通常将堆底元素送入堆顶,此时大根堆的结构被迫环,需要重新执行一次向下调整。
def swap_param(L, i, j):
L[i], L[j] = L[j], L[i]
return L
def heap_sort(alist):
alist.insert(0, 0)
l = len(alist)
build_max_heap(alist)
i = l
while i > 1:
swap_param(alist, 1, i-1)
adjust_down(alist, 1, i-1)
i -= 1
alist.pop(0)
堆的插入和删除
由于堆顶元素或为最大值或为最小值,删除堆顶元素时,先将堆的最后一个元素与堆顶元素交换,再进行向下调整。再对堆进行插入操作时,先将新结点放在堆的末端,再对这个新结点执行向上调整的操作。
def adjust_up(alist, k):
"""向上调整"""
alist[0] = alist[k]
i = k // 2
while i > 0 and alist[i] < alist[0]:
alist[k] = alist[i] # 双亲结点下调
k = i
i = k // 2 # 继续向上比较
alist[k] = alist[0]
算法分析
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)
- 不稳定
- 适用于顺序存储和链式存储(线索二叉树??)