数据结构学习笔记(16)---选择类排序

选择排序思想就是第一次选出关键字最小的的记录作为第一个,然后就是除了第一个元素外再去选取一个最小的元素放在第二个,依次循环知道全部记录有序为止。但是这样效率会有点低,为了改进算法的效率而又提出了树形选择排序和堆排序,下面进行一一介绍:

(1)简单选择排序:选择排序的基本思想:每一趟在n-i+1(i=1,2,3,…,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录,此排序算法比较简单,实现起来也没有那么复杂,就不在详细赘述了,只看一个例子就可以了。例如:

代码如下:

void SelectSort(int a[], int length)
{
    for (size_t i = 1; i < length; i++)
    {
        size_t k = i;
        for (size_t j = k+1; j < length; j++)
        {
            if (a[k] > a[j])
            {
                k = j;
            }
        }
        if (k != i)
        {
            swap(a[k], a[i]);
        }
    }
}
性能分析: 时间复杂度 : T(n) = O(n^2) ,空间复杂度 S(n) = O(1)

(2)树形排序

基本思想:
a. 树形排序必须首先构造个满的的二叉树,不够的时候全部补空,对于一个含有n个元素的数组,首先两两结合,作为此完全二叉树的最后一个层
b. 选取最小值做为两个节点的父节点,其父节点作为倒数第二层
c. 把倒数第二层两两结合,选取最小值作为该满二叉树的倒数第三层
d. 依次类推直该二叉树的根节点,此根节点就是最小的值。将在数组中的此数置空,对数组重新进行上述操作就可以得到次大值。最终得出排序结果

例如:数组 10 8 12 21 14 15 53
由于只有七个元素不能构成满的二叉树,所以需要添加一个空的节点构成满的二叉树,并得出最小值8

8的位置置空,重新构造二叉树,获得次小值10,依此类推

代码如下:
//初始化数组,a[]为原数组,b[]为存储二叉树的数组
void InitArr(int b[],int a[], int k,int length)
{
    for (size_t i = 1; i <= pow(2,k-1)-1; i++)
    {
        b[i] = 11111;
    }
    for (size_t i = 0; i < length; i++)
    {
        b[ i + (size_t)pow(2,k-1)] = a[i];
    }
}
//k为树的深度,len为原数组长度
void TreeSort(int b[],int a[], int k,int len)
{
    int length = pow(2, k) - 1;
    for (size_t j = 1; j < k; j++)
    {
        for (size_t i = 2; i <= length; i++)
        {
            if (b[i] < b[i / 2])
            {
                b[i / 2] = b[i];
            }
        }
    }
    cout << b[1]<<" ";//输出最大值
    for (size_t i = 0; i < len; i++)
    {
        if (a[i] == b[1])
        {
            a[i] = 11111;//置空
            break;
        }   
    }
    InitArr(b, a, k, len);//每次选取一个人后要初始化数组重新
}

(3)堆排序,分为两步第一步为初建堆,第二步为调整堆,由于初建堆需要用到调整堆,所以先来学习如何调堆
调整堆,算法思想:
  a.首先将于堆相应的完全二叉树根结点中的记录移出,该记录称为待调整记录。此时根结点相当于空结点,从空结点的左右孩子中选出一个关键字较大的记录,如果该记录的关键字大于待调整记录的关键字,则将该记录上移至空结点中。
   b.此时,原来那个关键字较大的子结点相当于空结点,从空结点的左右孩子中选出一个关键字较大的记录,如果该记录的关键字仍大于待调整记录的关键字,则将该记录上移至空结点中。
   c.重复上述移动过程,直到空结点左右孩子的关键字均小于待调整记录的关键字。此时,将待调整记录放入空结点即可。

例如:原树为下面的一棵树
这里写图片描述
则调整为
这里写图片描述

代码实现:
void sift(int a[], int k, int m)//调整下标为k的结点
{
    int t = a[k];//“根”结点的左孩子
    int i = k;
    int j = 2 * k;
    bool finesh = false;
    while (j<=m && !finesh)
    {
        if (j<m && a[j]< a[j+1] ) /*在左右子树中选出最大的*/
        {
            j++;
        }
        if (t > a[j])
        {
            finesh = true;/*筛选结束*/
        }
        else  /*继续筛选*/
        {
            a[i] = a[j];
            i = j;
            j = i * 2;
        }
    }
    a[i] = t;
}
初建堆,算法思想:从标号最大的有孩子的点i开始筛选,(i=n整除2),然后i减 1,直到i为1为止。

这里写图片描述

代码实现:
void CreateHeap(int r[], int length)
{
    int n = length;
    for (size_t i = n / 2; i >= 1; i--)
    {
        sift(r, i, n);
    }
}
结合上两步 堆排序算法实现算法思想:
a.  将待排序记录按照堆的定义建初堆;
b.  将堆顶元素与堆中最后一位元素交换。调整剩余的记录序列,将前n-i ,个元素重新建成为一个新堆;
c.  重复步骤b,进行n-1次。
代码实现:
void CreateHeap(int r[], int length)
{
    int n = length;
    for (size_t i = n / 2; i >= 1; i--)
    {
        sift(r, i, n);
    }
}
void HeapSort(int r[], int n)
{
    CreateHeap(r, n);//初建堆
    for (size_t i = n; i >= 2; i--)
    {
        swap(r[1], r[i]);
        sift(r, 1, i - 1);
    }
}
性能分析: 时间复杂度 : T(n) = O(nlog2(n))


小结:
排序算法时间复杂度最好情况最坏情况空间复杂度稳定性
简单选择排序O(n^2)O(n^2)O(n^2)O(1)稳定
树形选择排序O(nlog2(n))O(nlog2(n))O(log2(n)O(n)不稳定
堆排序O(nlog2(n))O(nlog2(n))O(nlog2(n))O(1)不稳定
  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值