3 选择排序
#include 
<stdio.h>
#include 
<stdlib.h>

int main(void)
{
    
int i,j,temp;
    
int lowindex,lowkey;
    
int num[20]={51,22,36,413,165,16,71,98,91,100,111,172,13,714,115,6,1,18,159,200};

    
/* 选择排序*/
    
for(i=1;i<20;i++)
    {
        lowindex
=i-1;
        lowkey
=num[i-1];
        
for(j=i;j<20;j++)
        {
            
if(num[j]<lowkey)
            {
                lowindex
=j;
                lowkey
=num[j];
            }
        }
        
        temp
=num[i-1];
        num[i
-1]=lowkey;
        num[lowindex]
=temp;
    }
    
for(i=0;i<20;i++)
        printf(
"%d\n",num[i]);    

fflush(stdin);
//刷新缓冲区
  char q;
  scanf(
"%c",&q);
  
return 0;
1 冒泡排序

#include 
<stdio.h>
#include 
<stdlib.h>
int main(void)
{
    
int i,j,temp;
    
int num[20]={51,22,36,413,165,16,71,98,91,100,111,172,13,714,115,6,1,18,159,200};    
    
for(i=1;i<20;i++)//对数组进行19次处理,每次处理余下数组中最小的数,移到最前面
        for(j=19;j>=i;j--)
        {
            
if(num[j]<num[j-1])
            {
                temp
=num[j-1];
                num[j
-1]=num[j];
                num[j]
=temp;
            }
        }

    
for(i=0;i<20;i++)
        printf(
"%d\n",num[i]); 

fflush(stdin);
//刷新缓冲区
  char q;
  scanf(
"%c",&q);
  
return 0;


2 插入排序
#include 
<stdio.h>
#include 
<stdlib.h>
int main(void)
{
    
int i,j,temp;
    
int num[20]={51,22,36,413,165,16,71,98,91,100,111,172,13,714,115,6,1,18,159,200};
    
for(i=1;i<20;i++)
    {
        j
=i;
        
while(num[j]<num[j-1]&&j>0)
        {
            temp
=num[j-1];
            num[j
-1]=num[j];
            num[j]
=temp;
            j
--;
        }
    }
    
for(i=0;i<20;i++)
        printf(
"%d\n",num[i]);  

fflush(stdin);
//刷新缓冲区
  char q;
  scanf(
"%c",&q);
  
return 0;
4 快速排序


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

void quicksort(int a[],int left,int right);

int main(void)
{
    
int i,j,temp;
    
int lowindex,lowkey;
    
int num[20]={51,22,36,413,165,16,71,98,91,100,111,172,13,714,115,6,1,18,159,200};

    
/* 快速排序*/
    quicksort(num,
0,19);

    
for(i=0;i<20;i++)
        printf(
"%d\n",num[i]);    

fflush(stdin);
//刷新缓冲区
  char q;
  scanf(
"%c",&q);
  
return 0;


//版本1
void quicksort(int a[],int left,int right)
{
    
int upper,low,keypoint;
    low
=left;
    upper
=right;
    
if(low>=upper)
        
return;
    keypoint
=a[low];

    
while(low<upper)
    {
        
while(a[upper]>=keypoint&&low<upper) upper--;
        
if(low<upper)
            a[low
++]=a[upper];
        
while(a[low]<=keypoint&&low<upper) low++;
        
if(low<upper)
            a[upper
--]=a[low];
    }
    a[low]
=keypoint;
    quicksort(a,left,low
-1);
    quicksort(a,low
+1,right);

}

//版本2
void quicksort(int a[],int left,int right)
{
    
int upper,low,keypoint,temp;
    low
=left;
    upper
=right;
    
if(low>=upper)
        
return;
    keypoint
=a[low];

    
while(low<upper)
    {
        
while(a[upper]>=keypoint&&low<upper) upper--;        
        
while(a[low]<=keypoint&&low<upper) low++;
        temp
=a[low];
        a[low]
=a[upper];
        a[upper]
=temp;
    }
    a[left]
=a[low];
    a[low]
=keypoint;
    quicksort(a,left,low
-1);
    quicksort(a,low
+1,right);

}
5 堆排序
#include 
<stdio.h>
#include 
<stdlib.h>

void heapify(int a[],int i,int n)//修正堆中的第i索引号元素(第一个元素的索引从0开始)
{
    
int l,r,temp,largest;//l表示i元素的左结点,r表示i元素的右结点
    l=2*i+1;
    r
=2*i+2;
    
if(l<n&&a[l]>a[i])
        largest
=l;
    
else
        largest
=i;
    
if(r<n&&a[r]>a[largest])
        largest
=r;
    
if(largest!=i)
    {
        temp
=a[i];
        a[i]
=a[largest];
        a[largest]
=temp;
        heapify(a,largest,n);
//递归调整largest结点
    }
}

void bulidheap(int a[],int n)//建堆
{
    
int i;
    i
=(n-2)/2;//因为元素的索引是从0开始的,所以对于n个元素的完全二叉树,(n-2)/2是第一个非叶子节点 
    for(;i>=0;i--)
        heapify(a,i,n);
}
int main(void)
{
    
int i,j,n,temp;
    
int num[20]={51,22,36,413,165,16,71,98,91,100,111,172,13,714,115,6,1,18,159,200};
    n
=20;//n表示待排序数组的个数
    bulidheap(num,n);
    
    
while(n>1)
    {
        temp
=num[0];
        num[
0]=num[n-1];
        num[n
-1]=temp;
        n
--;
        heapify(num,
0,n);
    }  
    
    
for(i=0;i<20;i++)
        printf(
"%d\n",num[i]); 

fflush(stdin);
//刷新缓冲区
  char q;
  scanf(
"%c",&q);
  
return 0;
6 归并排序
#include 
<stdio.h>
#include 
<stdlib.h>

void merge(int a[],int p,int q,int r)//合并a[p,,q]与a[q+1,.,r]为a[p,..,r]
{
    
int i,k;
    
int begin1,end1,begin2,end2;
    
int *temp=(int*)malloc(sizeof(int)*(r-p+1));//分配r-p+1大小的一块临时内存,用来存放合并后的数
    begin1=p;    end1=q;
    begin2
=q+1;    end2=r;
    
    k
=0;
    
while((begin1<=end1)&&(begin2<=end2))
    {
        
if(a[begin1]<=a[begin2])
        {
            temp[k]
=a[begin1];
            begin1
++;
        }
        
else
        {
            temp[k]
=a[begin2];
            begin2
++;
        }
        k
++;
    }
    
while(begin1<=end1)
    {
        temp[k
++]=a[begin1++];
    }
    
while(begin2<=end2)
    {
        temp[k
++]=a[begin2++];
    }
    
for(i=0;i<(r-p+1);i++)
        a[p
+i]=temp[i];
    free(temp);

}
void mergesort(int a[],int p,int r)
{
    
int q;
    
if(p<r)
    {
        q
=(p+r)/2;
        mergesort(a,p,q);
        mergesort(a,q
+1,r);
        merge(a,p,q,r);
    }
}
int main(void)
{
    
int i,j,n,temp;
    
int num[20]={51,22,36,413,165,16,71,98,91,100,111,172,13,714,115,6,1,18,159,200};
    mergesort(num,
0,19);

    
    
for(i=0;i<20;i++)
        printf(
"%d\n",num[i]); 

fflush(stdin);
//刷新缓冲区
  char q;
  scanf(
"%c",&q);
  
return 0;
7 基数排序

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

//计数排序,npRadix为对应的关键字序列,nMax是关键字的范围。npData是具体要
//排的数据,nLen是数据的范围,这里必须注意npIndex和npData对应的下标要一致
//也就是说npIndex[1] 所对应的值为npData[1]
int RadixCountSort(int* npIndex, int nMax, int* npData, int nLen)
{
    
//这里就不用说了,计数的排序。不过这里为了是排序稳定
    
//在标准的方法上做了小修改。

    
int* pnCount  = (int*)malloc(sizeof(int)* nMax);        //保存计数的个数
    for (int i = 0; i < nMax; ++i)
    {
        pnCount[i] 
= 0;
    }
    
for (int i = 0; i < nLen; ++i)    //初始化计数个数
    {
        
++pnCount[npIndex[i]];
    }

    
for (int i = 1; i < 10++i)  //确定不大于该位置的个数。
    {
        pnCount[i] 
+= pnCount[i - 1];
    }

    
int * pnSort  = (int*)malloc(sizeof(int* nLen);    //存放零时的排序结果。

    
//注意:这里i是从nLen-1到0的顺序排序的,是为了使排序稳定。
    for (int i = nLen - 1; i >= 0--i)
    {
        
--pnCount[npIndex[i]];        
        pnSort[pnCount[npIndex[i]]] 
= npData[i];
    }

    
for (int i = 0; i < nLen; ++i)        //把排序结构输入到返回的数据中。
    {
        npData[i] 
= pnSort[i];
    }
    free(pnSort);                        
//记得释放资源。
    free(pnCount);
    
return 1;
}

//基数排序
int RadixSort(int* nPData, int nLen)
{
    
//申请存放基数的空间
    int* nDataRadix    = (int*)malloc(sizeof(int* nLen);

    
int nRadixBase = 1;    //初始化倍数基数为1
    bool nIsOk = false//设置完成排序为false

    
//循环,知道排序完成
    while (!nIsOk)
    {
        nIsOk 
= true;
        nRadixBase 
*= 10;
        
for (int i = 0; i < nLen; ++i)
        {
            nDataRadix[i] 
= nPData[i] % nRadixBase;
            nDataRadix[i] 
/= nRadixBase / 10;
            
if (nDataRadix[i] > 0)
            {
                nIsOk 
= false;
            }
        }
        
if (nIsOk)        //如果所有的基数都为0,认为排序完成,就是已经判断到最高位了。
        {
            
break;
        }
        RadixCountSort(nDataRadix, 
10, nPData, nLen);
    }

    free(nDataRadix);

    
return 1;
}

int main()
{
    
//测试基数排序。
    int nData[10= {123,5264,9513,854,9639,1985,159,3654,8521,8888};
    RadixSort(nData, 
10);
    
for (int i = 0; i < 10++i)
    {
        printf(
"%d ", nData[i]);
    }
    printf(
"\n");

    system(
"pause");
    
return 0;
}
8 桶排序
  
void BucketSon(R)
    { 
//对R[0..n-1]做桶排序,其中0≤R[i].key<1(0≤i<n)
      for(i=0,i<n;i++//分配过程.
        将R[i]插入到桶B[「n(R[i].key)」]中; //可插入表头上
      for(i=0;i<n;i++//排序过程
        当B[i]非空时用插人排序将B[i]中的记录排序;
      
for(i=0,i<n;i++//收集过程
        若B[i]非空,则将B[i]中的记录依次输出到R中;
     }

稳定性的好处:
排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。

(1)冒泡排序
  冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

(2)选择排序
  选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

(3)插入排序
  插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

(4)快速排序
   快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j, 交换a[i]和a[j],重复上面的过程,直到i>j。 交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j]交换的时刻。

(5)归并排序
    归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。
(6)基数排序
   基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以其是稳定的排序算法。

(7)希尔排序(shell)
    希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

(8)堆排序
   我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n/2-1, n/2-2, ...1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。

综上,得出结论: 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。

不同条件下,排序方法的选择

(1)若n较小(如n≤50),可采用直接插入或直接选择排序。
     当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。
(2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;
(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
     快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
     堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。
     若要求排序稳定,则可选用归并排序。但本章介绍的从单个记录起进行两两归并的  排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定的,所以改进后的归并排序仍是稳定的。


 posted on 2009-08-08 10:54  清水湾  阅读(198)  评论(0)    收藏  举报