初级班第二课

随机快排

快排:quick sort。

  • 分治算法;
  • 把一个序列分成较大的和较小的2个子序列,然后递归地排列两个子序列。

步骤:

  • 挑选基准值:从数列中任意挑一个元素,成为“基准”(pivot),和最后一个元素进行交换;
  • 分割:重新排列数列,所有比基准值小的元素摆放在基准的前面,所有比基准值大的元素摆在基准后面(与基准相等的数可以放到任何一边)。在这个分割结束之后,对基准值的排序就已经完成;
  • 递归排序子数列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。

注意:

  • 递归到最底部的判断条件是数列的大小是0或1,此时该数列显然已经有序。选取基准值有数种具体的方法,选取方法对排序时间性能有决定性影响(随机快排vs经典快排)。
  • 随机快排时间复杂度的期望是O(NlogN)。

Python实现:

import random

def quickSort(numbers):
	if len(numbers) <= 1:
		return numbers

	left, right, mid = [], [], []
	pivot = random.choice(numbers)

	for number in numbers:
		if number == pivot:
			mid.append(number)
		elif number < pivot:
			left.append(number)
		else:
			right.append(number)

	return quickSort(left) + mid + quickSort(right)
def quick_sort(L):
    return q_sort(L, 0, len(L) - 1)

def q_sort(L, left, right):
    if left < right:
        pivot = Partition(L, left, right)

        q_sort(L, left, pivot - 1)
        q_sort(L, pivot + 1, right)
    return L

def Partition(L, left, right):
    pivotkey = L[left]

    while left < right:
        while left < right and L[right] >= pivotkey:
            right -= 1
        L[left] = L[right]
        while left < right and L[left] <= pivotkey:
            left += 1
        L[right] = L[left]

    L[left] = pivotkey
    return left

L = [5, 9, 1, 11, 6, 7, 2, 4]

print quick_sort(L)

sourc:第二段代码

经典快排

类似于随机快排,但是在选取基准值上有所不同。

步骤:

  • 挑选基准值:选取数组的最后一个数x作为基准值;
  • 分割:把整个数组划分成小于等于x和大于x两部分,将x和大于x部分数组的第一个元素交换位置,此时整个数组划分成了|小于等于x|x|大于x|三个部分,也就是这一次排序将x值排好了位置;
  • 递归排序子数列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。

注意:

  • 经典快排的问题是它的时间复杂度与数据状况有关。每次选择x值都是数组的最后一个数,如果数组是[5, 4, 3, 2, 1],时间复杂度就会变成O(N2)。
  • 随机快排和经典快排的区别在于随机快排增加了随机性,使比较的数x具有随机性。
  • 相对于随机快排的实现,经典快排只需将pivot设定为数组的最后一个值即可。

堆排序

堆排序图解Youtube
下面内容的英文链接
堆排序是通过把数组“假想成”完全二叉树的方式来进行排序。

概念

二叉树:binary tree
二叉树是指每个父节点最多有两个子节点的树。
在这里插入图片描述
满二叉树: full tree
满二叉树是一种特殊的二叉树。满二叉树的每个父节点要么有两个父节点,要么没有父节点。
在这里插入图片描述
完全二叉树:complete tree
每层结点都完全填满,在最后一层上如果不是满的,则只缺少右边的若干结点。
在这里插入图片描述
堆结构:heap data structure
堆是一种特殊的基于树的数据结构。如果满足以下条件,则说二叉树是堆:

  • 它是一个完整的二叉树
  • 大根堆(max heap):树中的所有父节点都大于子节点。
  • 小根堆(min heap):树中的所有父节点都小于子节点。
    在这里插入图片描述

由数组构建完全二叉树

  • 选择列表的第一个元素作为根节点。
  • 将第二个元素作为根节点的左子节点,将第三个元素作为右子节点。
  • 将下两个元素作为第二级左节点的子节点。同样,将下两个元素作为第二级右节点的子节点。
  • 不断重复,直到到达最后一个元素。

在这里插入图片描述

数组索引和完全二叉树节点的关系

完整的二叉树具有一个有趣的属性,我们可以用来查找任何节点的子节点和父节点。

如果数组中任何元素的索引为 i ,则索引2i + 1中的元素将成为元素 i 的左子节点,而索引2i + 2中的元素将成为元素i的右子节点。同样,索引 i 处任何元素的父节点都由 (i-1) / 2下限给出。
E.g. 索引为 4 4 4元素的父节点为 ( 4 − 1 ) / 2 = 1.5 (4-1)/2=1.5 (41)/2=1.5 我们使用floor操作得到父节点的索引为1。

将树转化成堆

从完全二叉树开始,我们可以通过在堆的所有非叶节点上运行heapify的函数,将其修改为大根堆。简单来说就是将父节点(i)和其子节点(2i+1 & 2i+2)进行比较,将较大的节点交换到父节点的位置。如果父节点本身就比子节点大,那么不需要进行任何操作。
在这里插入图片描述
当树有多层的时候,使用递归的思想。

要想把任何树转化成最大堆,我们可以从下至上开始对每个子树使用heapify操作直至所有的非叶节点都被heapified过。

对于完全二叉树,非叶子节点的第一个索引由 (n / 2)- 1 给出。在它之后的所有其他节点都是叶节点,因此不需要进行heapify操作。我们从索引为 (n / 2)- 1 的节点开始heapify,然后不断heapify索引-1(对应完全二叉树向左向上)的节点,直到到达跟节点。
在这里插入图片描述
在这里插入图片描述

堆排序的步骤:

  1. 由于树满足max-heap的属性,因此最大的项存储在根节点上。
  2. 将根部元素和尾部元素互换
  3. 将堆的大小减小1,然后再次从 (n / 2)- 1 进行heapify操作 ,以便在根处拥有的元素值最大。
  4. 重复该过程,直到对列表中的所有项目进行排序。

时间复杂度:O(nlogn) 不受数据状况的影响。
在这里插入图片描述
*完整的图示可以看我上面的英文链接。

Python实现

def heapify(arr, n, i):
	# Find the largest among root and children
	largest = i
	l = 2 * i + 1   # index of left node of i
	r = 2 * i - 1   # index of right node of i
	
	# l < n & r < n 确保不会越界(如果i是叶节点2i+1就会越界) 
	if l < n and arr[i] < arr[l]: 
		largest = l
	
	if r < n and arr[largest] < arr[r]:
		largest = r

	# if root is the largest, swap with largest and continue heapifyinf
	if largest != i:
		arr[i], arr[largest] = arr[largest], arr[i]
		heapify(arr, n, largest)

def heapSort(arr):
	n = len(arr)

	# Build heap
	# 从最后一个节点开始,对整个完全二叉树进行heapify操作
	# 注意我们在上面限制了l<n&r<n,所以不会对叶节点进行heapify操作
	# 这里首先将原始数组变成了大根堆
	for i in range(n, 0, -1):
		heapify(arr, n, i)

	# 这里在上面大根堆的基础上开始-1循环
	for i in range(n-1, 0, -1):
		# swap
		arr[i], arr[0] = arr[0], arr[i]

		# 互换之后,堆中就少了一个元素,所以堆的个数变了,变为i
		# 此时由于堆只变了跟节点,所以只需要对根节点进行heapify
		# 然后根节点又回变成值最大的点
		heapify(arr, i, 0)

if __name__ == '__main__':
    arr = [1,3,5,7,3,2,8,0,-1,-2]
    output = heapSort(arr)
    print(output)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值