Chinaunix首页 | 论坛 | 博客
  • 博客访问: 156989
  • 博文数量: 36
  • 博客积分: 802
  • 博客等级: 准尉
  • 技术积分: 717
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-02 22:47
文章分类
文章存档

2012年(36)

分类: C/C++

2012-09-12 20:17:23

三:选择类排序

1:简单选择排序

【算法思想】第一趟简单选择排序时,从第一个记录开始,通过n-1次关键字的比较,从n个记录中选出关键字最小的记录,并且和第一个记录进行交换。第二趟简单排序,从第二个记录开始,通过n-2次关键字的比较,从n-1个记录中选择关键字最小的记录进行交换

如此反复,经过N-1趟简单排序后,将最小记录直接排到最后。

【算法描述】 

A{48   62  35  77  55  14  35  98}//寻找最小的值与48交换

B: 14 {   62   35  77   55  48   35  98}//寻找到了14,然后除去14,继续寻找

C14   35 {  62  77   55   48   35   98 }//寻找到了最开始的35,除去35继续

D14   35   35{  77   55   48   62   98}//寻找到第二个35,继续寻找

E14    35   35  48 {  55   77   62   98}//寻找到了48,继续

F14    35   35  48   55   {77   62   98}//寻找到了55,继续

G14    35   35  48   55   62   {72   98}//寻找到了62,继续

H14    35   35  48   55   62   72   98//完成

【算法代码】

void selectsort(int *r,int len)

{

int i,j,k;

int n=len;

for(i=1;i<=n-1;i++)

{

k=i;

for(j=i+1;j<=n;j++)

{

if(r[j]

k=j;

}

if(k!=i)

{

r[0]=r[i];

r[i]=r[k];

r[k]=r[0];

}

Output(r,len);

}

}

【算法分析】

最好情况下,即待排序记录初始状态就已经是正序列了,则不需要移动,最换情况下。即待排序记录初始状态是按照逆序列的,则需要移动记录的次数最多为3(n-1)

时间复杂读为On^2

2:堆排序

【算法思想】把待排序的记录存放在数组r[1...n]中,将r看成是一颗完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]看作是根,任意节点r[i]的做孩子是r[2*i],右孩子是r[2*i+1];双亲是r[i/2]

堆定义:使得所有节点满足:r[i]>=r[2*i]&&r[i]>=r[2*i+1],满足这个条件的完全二叉树叫做大堆根。

(1)重建堆之---调整堆(堆排序中最重要的算法)

 

(a)48的左右子树均为堆,准备筛48                             (b)将48移除,98准备上移

(c)98上移动后,77准备上移                                                  (d)77上移后,62准备上移

(e)62上移后,48准备移入空记录                                          (f)48移入空记录后,得到筛选的堆

【算法思想】首先将根节点移出,作为待调整记录。此时根节点相当于空节点,从空节点的左右孩子中选出一个关键字较大的记录,如果该记录的关键字大雨这个待调整的记录节点,则将该记录上移至空节点中。此时,那个关键字较大的子节点相当于空节点,从空节点的左右孩子中选出较大的记录,如果该记录的关键字仍然大于待调整记录的关键字。则该记录上移动到空节点。

【筛选的算法】


 

点击(此处)折叠或打开

  1. //调整堆的算法
  2. void sift(int *r,int k,int m)
  3. {
  4.     int i,j,t;
  5.     int flag;
  6.     r[0]=r[k];
  7.     i=k;
  8.     j=2*i;
  9.     flag=0;
  10.     while(j<m&&!flag)
  11.     {
  12.         if(j<m&&r[j]<r[j+1])//先比较出两个孩子中最大的一个
  13.             j++;
  14.         if(r[0]>=r[j])
  15.             flag=1;//如果根大于这个最大的孩子,则结束
  16.         else
  17.         {
  18.             r[i]=r[j];//否则,将这个节点设置为那个最大的孩子
  19.             i=j;//然后再往下一次比较
  20.             j=2*i;
  21.         }
  22.     }
  23.     r[i]=r[0];//将节点i的值设为r[0]
  24. }

(2)堆排序之---建立初始堆

【算法思想】反复利用“调整堆算法”,自底向上逐层把所有子树调整为堆。

{48,62,35,77,55,14,35,98},要求筛选一个堆,再此,n=8,所以先从第4个节点77开始筛选。依次筛选4,3,2,1

 

(a)初始序列对应的完全二叉树,首先准备筛选“77”    (b)77筛选完后,准备筛选35

 

(c)35筛选完比,准备筛选62                               (d)62筛选完后,准备筛选48

 

(e)48筛选完,得到一个大堆根                             

【建立初堆的算法】

点击(此处)折叠或打开

  1. void create_heap(int *r,int len)
  2. {
  3.     int n=len;
  4.     int i;
  5.     for(i=n/2;i>=1;--i)
  6.         sift(r,i,n);
  7. }


(3)堆排序之---堆排序

1)将待排序记录按照堆的定义建立初堆,并输出堆顶元素

2)调整剩余的记录序列,利用筛选法将前n-i个元素重新筛选建成一个新堆,再输出

3)重复执行第二步,进行n-1次筛选,新筛选的堆会越来越小,这个过程称之为堆排序

【过程】

(a)初始大根堆                                                (b)堆顶元素于堆尾元素交换

(c)将前7个元素(除了98)重新调整为堆                       (d)堆顶元素于堆尾交换(77与35交换)

 

(e)将前6个元素重新调整为堆                                   (f)堆顶元素于堆尾元素交换

(g)将前5个元素重新调整为堆                                  (h)堆顶元素与堆尾元素交换

(i)将前4个元素重新调整为堆                                 (j)堆顶元素于堆尾元素交换

(k)将前3个元素重新调整为堆                               (l)堆顶元素于堆尾元素交换

(m)将前2个元素重新调整为堆,堆顶于堆尾交换后,原序列有序

建立堆的时间复杂度为O(log2n),堆排序的时间复杂度为O(nlog2n)

【堆排序算法代码】

点击(此处)折叠或打开

  1. void heap_sort(int r[],int len)
  2. {
  3.     int i,n,b;
  4.     n=len;
  5.     create_heap(r,len);
  6.     Output(r,len);
  7.     for(i=n;i>=2;--i)
  8.     {
  9.         b=r[1];
  10.         r[1]=r[i];
  11.         r[i]=b;
  12.         sift(r,1,i-1);
  13.     }
  14.     Output(r,len);

  15. }

【堆排序与简单排序的例子】


 

点击(此处)折叠或打开

  1. #include<stdio.h>
  2. #include<stdlib.h>

  3. void Output(int *r,int len)
  4. {
  5.     int i;
  6.     for(i=1;i<=len;i++)
  7.         printf("%d ",r[i]);
  8.     printf("\n");
  9. }

  10. //简单选择排序
  11. void selectsort(int *r,int len)
  12. {
  13.     int i,j,k;
  14.     int n=len;
  15.     for(i=1;i<=n-1;i++)
  16.     {
  17.         k=i;
  18.         for(j=i+1;j<=n;j++)
  19.         {
  20.             if(r[j]<r[k])
  21.                 k=j;
  22.         }
  23.         if(k!=i)
  24.         {
  25.             r[0]=r[i];
  26.             r[i]=r[k];
  27.             r[k]=r[0];
  28.         }
  29.         Output(r,len);
  30.     }
  31. }
  32. //调整堆的算法
  33. void sift(int *r,int k,int m)
  34. {
  35.     int i,j,t;
  36.     int flag;
  37.     r[0]=r[k];
  38.     i=k;
  39.     j=2*i;
  40.     flag=0;
  41.     while(j<m&&!flag)
  42.     {
  43.         if(j<m&&r[j]<r[j+1])//先比较出两个孩子中最大的一个
  44.             j++;
  45.         if(r[0]>=r[j])
  46.             flag=1;//如果根大于这个最大的孩子,则结束
  47.         else
  48.         {
  49.             r[i]=r[j];//否则,将这个节点设置为那个最大的孩子
  50.             i=j;//然后再往下一次比较
  51.             j=2*i;
  52.         }
  53.     }
  54.     r[i]=r[0];//将节点i的值设为r[0]
  55. }
  56. //建立初堆
  57. void create_heap(int *r,int len)
  58. {
  59.     int n=len;
  60.     int i;
  61.     for(i=n/2;i>=1;--i)
  62.         sift(r,i,n);
  63. }
  64. //堆排序算法

  65. void heap_sort(int r[],int len)
  66. {
  67.     int i,n,b;
  68.     n=len;
  69.     create_heap(r,len);
  70.     Output(r,len);
  71.     for(i=n;i>=2;--i)
  72.     {
  73.         b=r[1];
  74.         r[1]=r[i];
  75.         r[i]=b;
  76.         sift(r,1,i-1);
  77.     }
  78.     Output(r,len);

  79. }

  80. int main()
  81. {
  82.     int r[8];
  83.     int len=8;
  84.     int t,i;
  85.     printf("请输入您想要排序的数字:\n");
  86.     for(i=1;i<=len;i++)
  87.         scanf("%d",&r[i]);
  88.     printf("请输入您想要选择的方式:");
  89.     scanf("%d",&t);
  90.     switch(t)
  91.     {
  92.         case 0:printf("简单排序开始:");
  93.              selectsort(r,len);
  94.              break;
  95.         case 1:printf("堆排序开始:");
  96.              heap_sort(r,len);
  97.              break;
  98.     }
  99. }



 

阅读(1830) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~