选择排序

1、直接选择排序(Straight Select Sorting)

(1)描述

从无序区选出一个最小的数,与第1个数交换;
再从剩下无序数据中找出最小的数,与第2个数交换,总共选择n-1次。

(2)实现

两层循环,外层循环控制当前轮最小值应该存放的数组下标i
内层循环从i到最后选出最小值的下标min
如果imin不是同一个值,则交换。

void straight_select(int data[], int length)
{
    int i, k;
    int min;
    for (i = 0; i < length-1; ++i)
    {
        min = i;
        for (k = i+1; k < length; ++k)
            if (data[k] < data[min])
                min = k;
        if (i != min)
            SWAP_INT(data[i], data[min])
    }
}

代码链接
链表版代码链接

(3) 时间复杂度

因为每次只能选出1个元素,每次选择需要比较无序区的所有元素,所以
时间复杂度固定为 O(n2) 。
当然也可以优化,比如每次选出2个元素,可以是1max1min,也可以是mintop2,但也只是把外层循环由n-1变成了n/2,最终只是无关紧要的项有变化,复杂度仍然是 O(n2) 。

(4)空间复杂度 O(1)
(5)稳定性:不稳定

比如 2, 2, 1,第1轮选择把第1个2交换到了最后。


(2)堆排序(Heapsort)

(1)描述

这里所说的堆并不是内存的堆,而是数据结构的二叉堆。
二叉堆是一棵完全二叉树,分为两种:最大堆和最小堆。
最大堆的父结点的值总是大于或等于它的子节点,
最小堆的父结点的值总是小于或等于它的子节点,
左右子节点之间并无固定的大小关系。

满二叉树的第1层有1个节点,第2层有2个,第3层有4个,
第 h 层有 2h-1 个节点,是一个公比为 2 的等比数列。
根据等比数列求和公式:

如果从堆顶往下只遍历某一个子树就可以选择出最值,就可以把直接选择排序的选择最值部分的时间复杂度从 O(n) 降到了 O(logn) 。

2、实现

1.构建一个二叉堆。
2.交换堆顶(第1个数据)与最后一个数据。
3.利用堆的便利,轻松将前n-1个数据重新构建堆。重复2和3步骤,一共选择n-1次。

void build_heap(int data[], int father, int length)
{
    int child;
    for(child = 2 * father + 1; child < length; child = 2 * father + 1)
    {
        if (child+1 < length && data[child+1] > data[child])
            ++child;
        if (data[child] > data[father])
        {
            SWAP_INT(data[child], data[father])
            father = child;
            continue;
        }
        break;
    }
}

void heap_sort(int data[], int length)
{
    for(int i = length/2 -1; i >=0; --i)
    {
        build_heap(data, i, length);
    }
    for(int i = 1; i < length; ++i)
    {
        SWAP_INT(data[0], data[length-i])
        build_heap(data, 0, length-i);
    }
}

代码链接

用数组实现是很方便的,因为数组可以随机访问,根据父节点,访问子节点的复杂度是O(1)。
如果用链表实现,则需要把每个节点的父节点和左右子节点记录下来,需要自定义结构体,如果用STL中的list这样的链表,父子节点访问对方的复杂度是O(n),这样一来整个排序的时间复杂度上升了,与直接选择排序相比也丧失了优势。
STL的algorithm有提供堆排序的算法,只需要提供一个比较元素大小的函数即可。代码链接

(3) 时间复杂度

代码大致分为2个部分:1是初始构建堆,2是选出最值+构建堆;

1)初始构建堆
设节点总数为 n , 设堆的高度为 h ,
设最大的非叶子节点所在的层为第k层,则k = h - 1

初始构建堆时,
h 层,因为全是叶子节点,不需要构建堆,即构建0次。
h-1 层,构建1次堆。
所以第 i 层需要构建 h-i 次堆。
且第i层最多有 2(i-1) 个节点。

所以第 i 层一共需要构建堆的次数为

i 层一共是

初始构建堆的过程是前 k 层,一共最多需要构建堆多少次,接下来就是化简 Si ,再把 i = k 代入计算即可。

等式两边同时乘以 2 得:

两式相减(幂相同的项相减)得:

除了首尾的两个项,中间是等比数列可以用等比数列求和公式计算:


因为 k = h - 1 ,所以:

根据上面算过的 h 的取值范围:

去掉低阶项,初始构建堆的时间复杂度是 O(n) 。

2)从堆顶往下构建堆的时间复杂度是O(logn),一共选择(n-1)次,时间复杂度是O(nlogn) 。

两项相加,初始构建堆的时间复杂度是低阶项可以忽略不计,所以:
时间复杂度 O(nlogn) 。

(4)空间复杂度 O(1)
(5)稳定性:不稳定

你可能感兴趣的:(选择排序)