选择排序:简单选择排序和堆排序

2021-04-19  本文已影响0人  星光下的胖子

一、原理

简单选择排序

特点:比较次数多,时间复杂度为O(n^2);但移动/交换次数少,时间复杂度为O(n)

堆排序

二、代码实现

题目:随机给定一个数组,按从小到大排序。

逻辑代码:
from time import time
import numpy as np

# 简单选择排序
def selection_sort(arr):
    for i in range(len(arr) - 1):
        min_index = i
        for j in range(i + 1, len(arr)):
            if arr[j] < arr[min_index]:
                min_index = j
        arr[i], arr[min_index] = arr[min_index], arr[i]
    return arr

# 将某一个节点调整成堆
def adjust_heap(arr, n, parent_idx, reverse=False):
    """
    n表示arr中未排序的元素个数,取arr[0:n]进行调整操作。
    默认reverse=Fasle,表示从小到大排序;reverse=True则表示从大到小排序。
    """
    if n <= 1:
        return
    
    flag_idx = parent_idx  # 标记索引。如果是从小到大排序,flag_idx表示最大值的索引;否则表示最小值的索引。
    l_child_idx = 2 * parent_idx + 1  # 左孩子节点
    r_child_idx = 2 * parent_idx + 2  # 右孩子节点
    if l_child_idx >= n:  # 左孩子节点越界(超过数组的索引范围)
        return
    elif l_child_idx == n - 1:  # 左孩子节点为数组最后一个元素,右孩子节点越界
        if reverse:  # 从大到小排序
            if arr[parent_idx] <= arr[l_child_idx]:  # 父节点最小
                return
            else:  # 左孩子节点最小
                flag_idx = l_child_idx
        else:  # 从小到大排序
            if arr[parent_idx] >= arr[l_child_idx]:  # 父节点最大
                return
            else:
                flag_idx = l_child_idx  # 左孩子节点最大
    else:  # 左、右孩子节点都不越界
        if reverse:  # 从大到小排序
            if arr[parent_idx] <= arr[l_child_idx] and arr[parent_idx] <= arr[r_child_idx]:  # 父节点最小
                return
            elif arr[l_child_idx] <= arr[r_child_idx] and arr[l_child_idx] < arr[parent_idx]:  # 左孩子节点最小
                flag_idx = l_child_idx
            else:  # 右孩子节点最小
                flag_idx = r_child_idx
        else:  # 从小到大排列
            if arr[parent_idx] >= arr[l_child_idx] and arr[parent_idx] >= arr[r_child_idx]:  # 父节点最大
                return
            elif arr[l_child_idx] >= arr[r_child_idx] and arr[l_child_idx] > arr[parent_idx]:  # 左孩子节点最大
                flag_idx = l_child_idx
            else:  # 右孩子节点最大
                flag_idx = r_child_idx
    
    # 标记索引位置改变,则互换父子节点,并递归将子节点调整成堆
    if flag_idx != parent_idx:
        arr[parent_idx], arr[flag_idx] = arr[flag_idx], arr[parent_idx]
        adjust_heap(arr, n, flag_idx, reverse)
        
# 将序列构建成堆
def build_heap(arr, reverse=False):
    """
    默认reverse=Fasle,表示从小到大排序;reverse=True则表示从大到小排序。
    """
    n = len(arr)
    start_index = len(arr) // 2 - 1  # 第一个非叶子节点
    # 从start_index开始由后往前依次遍历每个节点,将每个节点调成堆。
    while start_index >= 0:
        adjust_heap(arr, n, start_index, reverse)
        start_index -= 1
        
# 堆排序
def heap_sort(arr, reverse=False):
    """
    默认reverse=Fasle,表示从小到大排序;reverse=True则表示从大到小排序。
    """
    if len(arr) < 2:
        return arr
    
    # 1.将序列构建成堆
    build_heap(arr, reverse)

    # 2.将堆顶与末尾元素互换,并删除末尾节点(已排序好),重新将堆顶节点调整成堆。
    # 3.重复2的过程,直到未排序的元素个数为1。
    n = len(arr)  # 未排序的元素个数
    while True:
        arr[0], arr[n - 1] = arr[n - 1], arr[0]  # 将堆顶与末尾元素互换
        n -= 1
        if n == 1:  # 说明未排序的元素个数为1
            break
        adjust_heap(arr, n, 0, reverse)  # 对堆顶元素重新调整成堆
        
        
if __name__ == "__main__":
    # 简单选择排序
    np.random.seed(10)
    array = list(np.random.randint(0, 1000, size=(2000,)))
    start = time()
    selection_sort(array)
    end = time()
    print(f"selection_sort()函数耗时{end - start}秒")
    print(array[:100])
    
    print("----------")
    # 堆排序
    np.random.seed(10)
    array = list(np.random.randint(0, 1000, size=(2000,)))
    start = time()
    heap_sort(array)
    end = time()
    print(f"heap_sort()函数耗时{end - start}秒")
    print(array[:100])
运行结果如下,\frac{t_{简单选择排序}}{t_{堆排序}} \approx 15(倍)
上一篇 下一篇

猜你喜欢

热点阅读