选择排序--(简单选择排序,堆排序,以及构建最大堆和最小堆)

选择排序

基本思想:每一趟在后面n-i+1个记录中选取关键字最小的记录作为有序序列中第i个记录。

通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录交换之。

步骤:

可以看出选择排序是一个不稳定算法。

算法代码:

//选择排序(稳定算法)
void SelectSort(int *array, int length)
{
        int i, j, k;
        int temp;
        for (i = 0; i < length - 1; ++i)
        {
               k = i;
               //逐一与后面的数进行比较,如果比它小就保存其下标
               for (j = i + 1; j < length; ++j)
               {
                       if (array[j] < array[k])
                       {
                              k = j;
                       }
               }
               //交换
               if (k != i)
               {
                       temp = array[i];
                       array[i] = array[k];
                       array[k] = temp;
               }
        }
}

堆排序:

基本思想:

    只需要一个记录大小的辅助空间,每个待排序的记录仅占有一个存储空间。

基本概念:

满二叉树:一颗深度为k且有2^k+1-1个节点的二叉树称为满二叉树。

通俗的说就是树的每一层的节点都达到最大值的树。

完全二叉树: 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

什么是堆?什么是最小堆?什么是最大堆?

  堆的定义如下:

(1)某一序列对应的一维数组可以看成是一个完全二叉树。

(2)所有非根结点的值均不大于(或不小于)左右孩子的节点。

(3)每个节点子树都是一颗堆树。

最大堆:堆树数值最大的元素总是位于树的根部

最小堆:堆树数值最小的元素总是位于树的根部

堆排序过程:

(1)建立一个最大堆(或最小堆),输出最小值。

(2)使剩余n-1个元素的序列又重新建成最小堆,输出次小值。

(3)如此反复执行,得到有序序列。

实现堆排序需要解决两个问题:(1)如何由无序序列建成一个堆。(2)如何在输出堆顶元素之后,调整剩余元素称为一个新的堆

解决第一个问题的一种有效方法是将数据表中的元素顺序地填入一个完全二叉树中。

操作:根节点为1,左子节点为2i(i为父节点的序号),右子结点为2i+1;

往堆中插入一个元素的过程:

(1)将此结点插入到完全二叉树最右面的节点,即二维数组的最右边,此完全二叉树多了一个叶子节点。

(2)找到叶子结点的父节点,两者比较,如果比父节点大,则两者交换位置。

(3)重复步骤2

(4)插入完毕

从堆中删除一个元素的过程:

(1)输出堆顶元素之后,以堆中最后一个元素替代

(2)此时,新的根节点与左右孩子比较,如果比根结点大,选出较大的那一个

(3)将根节点与值较大的孩子结点交换位置

(4)如果孩子结点还有孩子则重复(2)(3)步,直至叶子结点。

时间复杂度:

插入操作,堆调整算法中的循环次数就是树的高度,具有n个节点的完全二叉树的高度是log2^(n+1),关键码比较次数和节点对调次数都是1,因此堆的插入算法的时间复杂度是O(log2^n).

建堆操作:建堆执行了n/2次调整算法,因此时间复杂度为O(nlog2^n)

删除操作:关键码比较次数为2,对调为1,时间复杂度也是O(log2^n).

算法代码描述:
 

/大顶堆

void Heapadjust(int *array, int s, int m)

{

        int j;

        //存放非终端结点(父节点)的值

        int rc = array[s];

        for (j = s * 2+1; j <= m; j *= 2)//沿着较大的孩子向下筛选

        {

               if (j < m&&array[j] < array[j + 1]) ++j;//当孩子不是最后一个结点时并且左孩子小于右孩子孩子,则保存右孩子下标

               if (rc > array[j]) break;

               array[s] = array[j]; s = j;//如果父节点比子节点小,则将子节点放入父节点位置,继续往下建堆

        }

        array[s] = rc;

}

//堆排序(不稳定算法)

void  HeapSort(int *array, int length)

{

        int i;

        //构建大顶堆

        //从一个无序序列建堆的过程就是一个反复“筛选”的过程.若将此序列看成是一个完全二叉树,

        //则最后一个非终端结点就是第n/2个元素.

        for (i = length / 2; i >=0; --i)

        {

               Heapadjust(array,i,length);//将i到length调整为大顶堆

        }

        for (int i = length; i >0; --i)

        {

               swap(array,0,i);

               Heapadjust(array,0,i-1);//将0到i-1重新调整成大顶堆

        }

}

 构建小顶堆的过程与大顶堆正好相反,及其容易实现,不做代码描述。

猜你喜欢

转载自blog.csdn.net/suoyudong/article/details/88186564