RodneyX

博客园 首页 新随笔 联系 订阅 管理

选择排序的基本思想是,在待排序序列A[p...n-1]中选择一个最值加入到A[0...p-1]中,形成更长的有序序列A[0...p],初始时有序表仅有一个元素,对于表长为n的表,经过n-1次选择即可排序完成,针对选择最值方式的不同,有两种基本的算法,一个是简单选择排序(暴力选择),一个是堆排序(基于数据结构堆)

对于简单选择排序,实现方式非常简单,有如下实现

#include <limits.h>

void simple_Select(int A[], int length)
{
    for (int i = 0; i < length - 1; ++i)
    {
        int min_index = -1;
        int min_val = INT_MAX;
        for (int j = length - 1; j >= i; --j)
        {
            if (A[j] < min_val)
            {
                min_val = A[j];
                min_index = j;
            }
        }
        A[min_index] = A[i];
        A[i] = min_val;
    }
}

定义堆具有如下性质:

  1. 是完全二叉树

  2. 所有分支节点的值大于(小于)其左、右孩子(若存在)的值称之为大(小)顶堆

调整堆的方法是从上向下筛选,也就是说,若分支节点不满足堆的定义,就将这个结点与孩子节点交换,其中若为大顶堆要求这个孩子节点具有最大值,小顶堆则是取具有最小值的孩子结点交换,如此往复直到这个向下筛选的结点满足堆定义

初始化一个堆则从最后一个分支结点开始到完全二叉树根节点,依次调整即可

而堆排序就是先将待排序序列初始化为堆,然后交换第一个和最后一个结点(删除堆顶元素),此时堆规模减一,然后重新调整,对于表长为n的无序表只需执行n-1次交换即可完成排序

于是我们有如下实现

/*完全二叉树下标与结点父子关系的一个简单例子
           0
         /   \
        1      2
       /  \   /  \
      3    4  5   6

 (1 - 1) / 2 = 0  (2 - 1) / 2 = 0
 (3 - 1) / 2 = 1  (4 - 1) / 2 = 1
 (5 - 1) / 2 = 2  (6 - 1) / 2 = 2

 parent = (pos - 1) / 2
 lchild = 2 * i + 1, rchild = lchild + 1
*/

// cannot be called by user
void _adjust_MaxHeap(int A[], int pos, int length)
{
    int adj_val = A[pos];
    int i = pos, k;
    while (i < length)  //事实上只要输入合法,是不会出现越界访问的,i的有效性由while循环内if语句保证,因此这里其实可以不用这么写,直接写个true也行
    {
        int adjusted = 0;
        k = i * 2 + 1;
        if (k + 1 < length && A[k] < A[k + 1])
            ++k;
        if (k < length && adj_val < A[k])
        {
            A[i] = A[k];
            i = k;
            adjusted = 1;
        }  //这里可以不用adjusted变量,只需要else语句块执行break即可,因为没有执行if语句块表明调整完毕,直接退出循环
        if (!adjusted)
        {
            A[i] = adj_val;
            break;
        }
    }
}

// cannot be called by user
void _create_MaxHeap(int A[], int length)  //建堆从最后一个分支节点开始调整
{
    for (int i = 0; i <= (length - 2) / 2; ++i)
        _adjust_MaxHeap(A, (length - 2) / 2 - i, length);
}

void heap_Sort(int A[], int length)
{
    _create_MaxHeap(A, length);
    for (int i = length - 1; i > 0; --i)
    {
        int swap_val = A[i];
        A[i] = A[0];
        A[0] = swap_val;
        _adjust_MaxHeap(A, 0, i);  //注意参数传递,第二个参数是堆数组的首元素下标,第二个参数是堆的规模
    }
}

最后,堆排序的时间复杂度是O(nlogn),这是因为建堆是线性时间,排序时每次筛选最小值是对数时间,会筛选n-1次(假设表长为n

另外,堆的插入是插到堆尾,然后自下往上调整(不是向下筛选!)

posted on 2025-07-30 21:09  RodneyX  阅读(16)  评论(0)    收藏  举报