10-排序-03-选择排序

1.简单选择排序

基本思想:

(1)初始状态:整个数组 r 划分成两个部分,即有序区(初始为空)和无序区。
(2)基本操作:从无序区中选择关键字值最小的记录,将
其与无序区的第一个记录交换(实质是添加到有序区尾部)。
从初态(有序区为空)开始,重复步骤(2),直到终态

(无序区为空)。

排序过程

    首先通过 n-1 次关键字比较,从n 个记录中找出关

键字最小的记录,将它与第一个记录交换

    再通过 n-2次比较,从剩余的 n-1 个记录中找出关

键字次小的记录,将它与第二个记录交换

    重复上述操作,共进行n-1趟 排序后,排序结束。


算法:

 void Selectsort()
{ for (i=1;i<n;i++)
  { k=i;
     for (j=i+1;j<=n;j++)
     if (R[j].key<R[k].key)                  // 选择关键字值最小的记录
         k=j;
     if ( k!=i )
        { R[0]=R[i];
           R[i]=R[k];
           R[k]=R[0];
        }                // 交换记录
   }	
}



效率分析:

         简单选择排序比较次数与关键字初始排序无关。

         找第一个最小记录需进行n-1次比较,找第二个最小记录需要比较n-2次,找第i个最小记录需要进行n-i次比较,总的比较次数为:

       (n-1)+(n-2)+……+(n-i)+……2+1=n(n-1)/2=n2/2

         时间复杂度:O(n2)

         辅助空间:O(1)

         简单选择排序是不稳定的排序方法。

2. 堆排序

堆排序法是利用堆树(Heap Tree)来进行排序的方法,堆树是一种特殊的二叉树,其具备以下特征:
(1)是一棵完全二叉树。
(2)每一个根结点的值均大于或等于它的两个子结点的值。
(3)树根的值是堆树中最大的。


基本思想:

(1)把用数组存储的待排序数据,转换成一棵完全二叉树。
(2)将完全二叉树转换成堆树。
(3)有了堆树后,便可以排序。

例:输入数据序列为:80 13 6 88 27 75 42 69
分析其堆排序的过程。

(1)画出完全二叉树

          位置(i)                    1     2   3    4     5    6     7    8

        一维数组中的数据      80   13  6   88   27  75   42  69

         对于任一位置,若父结点的位置为i则它的两个子结点分别

位于 2i 和 2i+1 。 根据数组中的数据可画出如图所示的完全二叉树。



(2)建堆过程

1) 从数组中间开始调整。
2) 找出此父结点的两个子结点的较大者,再与父结点比较,若父结点小,则交换。然后以交换后的子结点作为新的父结点,重复此步骤直到没有子结点。
3) 把步骤 2)中原来的父结点的位置往前推一个位置,作为新的父结点。重复步骤 2),直到树根为止。

(3) 实现堆排序:

    堆排序:将无序序列建成一个堆,得到关键字最大(或最小)

的记录;输出堆顶的最大)值后, 使剩余的 n-1个元素重又

建成一个堆,则可得到n 个元素的次大)值;重复执行,得

到一个有序序列。

    实现堆排序要解决的一个问题:

   即输出堆顶元素后,怎样调整剩余的 n-1个元素,使其按关

键码成为一个新堆。 

 调整方法:

   设有m个元素的堆,输出堆顶元素后,剩下m-1个元素。

将堆底元素送入堆顶,堆被破坏,其原因仅是根结点不满

足堆的性质。将根结点与左、右子女中较大的进行交换。

若与左子女交换,则左子树堆被破坏,且仅左子树的根结

点不满足堆的性质;若与右子女交换,则右子树堆被破坏,

且仅右子树的根结点不满足堆的性质。继续对不满足堆性

质的子树进行上述交换操作,直到叶子结点,堆被建成

称这个自根结点到叶子结点的调整过程为筛选。

   此过程的输出序列为从大到小。

其过程如图所示:






调整方法:

       输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结

点值与左、右子树的根结点值进行比较,并与其中者进行交换;

重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至

叶子的调整过程为筛选。

        此过程的输出序列为从小到大。




算法:

void HeapAdjust (S_TBL *h,int s,int m)
{             //a[s…m]中的记录关键码除a[s]外均满足堆的定义,本函数
           将对第s个结点为根的子树筛选,使其成为大顶堆
  ac=h->a[s];
  for (j=2*s;j<=m;j=j*2)   //沿关键码较大的子女结点向下筛选 
  { if (j<m&&h->a[j].key<h->a[j+1].key)
        j=j+1;                     	          //为关键码较大的元素下标
     if (ac.key<h->a[j].key)  baeak;      // ac应插入在位置s上
      h->a[s]=h->a[j]; s=j;                   // 使s结点满足堆定义
   }
  h->a[s]=ac;                         	    //  插入 
}
void HeapSoat (S_TBL *h)
{ for (i=h->length/2;i>0;i- -)      // 将a[1..length]建成堆 
        HeapAdjust (h,i,h->length);
  for (i=h->length;i>1;i- -)
      {  h->a[1]<-->h->a[i];             // 堆顶与堆低元素交换 
          HeapAdjust (h,1,i-1);   // 将a[1..i-1]重新调整为堆
       }
}




算法评价:

时间复杂度:最好、坏情况下T(n)=O(nlogn)----n较大时比较有效

空间复杂度:S(n)=O(1)

堆排序为不稳定排序。








  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值