希尔排序堆排序和基数排序

来源:互联网 发布:淘宝会员名有什么用处 编辑:程序博客网 时间:2024/05/16 05:46

希尔排序又称缩小增量排序,它是对直接插入排序方法的一种改进。希尔排序是不稳定的排序,时间复杂度小于O(n^2) 优于直接插入排序,空间复杂度O(1)

希尔排序又称缩小增量排序,它是对直接插入排序方法的一种改进 希尔排序的基本思想:对与含有n个元素的序列,取初始增量dk<n;将待排序序列按下标的初始增量进行分组分成dK个组,在各组内进行直接插入排序;然后缩小增量重复分组和排序的操作,直到增量缩小为1,将n元素放在同一组中进行插入排序为止;


//希尔排序 又称缩小增量排序,它是对直接插入排序方法的改进void ShellSort(int *a,int n){    int dk=n/2;//初始增量dk<n  一般取n/2while(dk>0)//最后一次循环dk=1  增量缩小为1,将n元素放在同一组中进行插入排序为止;{    for(int i=dk;i<n;i++){    if(a[i]<a[i-dk]){     int temp=a[i];//备份带插入元素  挖坑int j;for(j=i-dk;j>=0&&a[j]>temp;j=j-dk)//每组按增量往前搜索合适的插入位置{    a[j+dk]=a[j];//元素按增量后移}a[j+dk]=temp;//填数}}dk=dk/2;//缩小增量}}




1.堆

  堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:

  Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]

  即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。

  堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的

堆排序是利用堆的性质进行的一种选择排序

原理:利用大根堆或小根堆思想,首先建立堆,然后将堆首与堆尾交换,堆尾之后为有序区。

要点:建堆、交换、调整堆

以大顶堆排序为例:

堆排序中的堆结构其实是完全二叉树  堆排序的基本思想:将含有n个元素的序列data[0...n-1]按堆的性质建立初始堆,初始状态整个堆为无序区,

然后将堆顶元素和无序区的最后一个元素交换,交换后将原先无序区中最后一个元素的归到有序区中,形成新的无序区data[0...n-2]和有序区data[n-1]  然后将新的无序区调整为大顶堆,重复之前的交换和调整堆的步骤直到无序区只有一个元素为止


堆排序的三个步骤1建立初始堆,2交换堆顶元素和无序区最后一个元素的值,3调整堆

堆排序中的堆结构其实是完全二叉树对于含有n个元素的数组 data[0],data[1],...,data[n-1]     堆的性质满足   data[i]>=data[2*i+1]&&data[i]>=[2*i+2]   或 data[i]<=data[2*i+1]&&data[i]<=[2*i+2]

建立初始堆的过程:建立初始堆是是从第一个非叶子结点开始调整的。建立初始堆 即把data[0...n-1]中的n个元素调整为大顶堆  建初始堆时,data[n/2]...data[n-1]为叶子结点已经满足堆的性质了所以一开始从第一个非叶子结点data[n/2-1]开始调整 
对于含有n个元素的数组 data[0],data[1],...,data[n-1] 从后往前第一个非叶子结点为data[n/2-1]; 

调整堆的过程:调整堆时将父节点与其子节点中的较大者进行比较下标索引为s的父节点,对于数组来讲,其子结点的第一个结点为2*s+1,第二个结点为2*s+2;

 


#include<iostream>#include<cstdio>#include<cstdlib>using namespace std;//希尔排序 又称缩小增量排序,它是对直接插入排序方法的改进void ShellSort(int *a,int n){    int dk=n/2;//初始增量dk<n  一般取n/2while(dk>0)//最后一次循环dk=1  增量缩小为1,将n元素放在同一组中进行插入排序为止;{    for(int i=dk;i<n;i++){    if(a[i]<a[i-dk]){     int temp=a[i];//备份带插入元素  挖坑int j;for(j=i-dk;j>=0&&a[j]>temp;j=j-dk)//每组按增量往前搜索合适的插入位置{    a[j+dk]=a[j];//元素按增量后移}a[j+dk]=temp;//填数}}dk=dk/2;//缩小增量}}void swap(int &a,int &b){    a=a+b;b=a-b;a=a-b;}//堆排序  在data[s...m]构成的序列中除了 data[s] 外其余元素均满足堆的定义//调整元素data[s] 的位置,使data[s...m] 称为一个大根堆//对于含有n个元素的序列 k1,k2...kn-1,kn,  堆的性质满足  ki>=k2*i&&ki>=k2*i+1  或    ki<=k2*i&&ki>=k2*i+1 //对于含有n个元素的数组 data[0],data[1],...,data[n-1]     堆的性质满足   data[i]>=data[2*i+1]&&data[i]>=[2*i+2]   或 data[i]<=data[2*i+1]&&data[i]<=[2*i+2]//对于含有n个元素的数组 data[0],data[1],...,data[n-1] 从后往前第一个非叶子结点为data[n/2-1];  void HeapAdjust(int *data,int s,int m){int temp=data[s];//保存data[s]值,在找到适当的位置后再插入for(int j=2*s+1;j<=m;j=2*j+1)//标号为j的父结点的左孩子结点为2*j+1,右孩子 结点为2*j+2 初始化时j第一次指向了data[s],左孩子结点{    if(j+1<=m&&(data[j]<data[j+1])){    j++;//让j 是左右孩子结点中的较大者}if(temp>=data[j])//如果待调整的结点已经大于它左右孩子的最大者了则跳出循环,注意此处是 temp>=data[j]{     break;}data[s]=data[j];s=j;//继续向下调整}data[s]=temp;}void HeapSort(int *data,int n){     int i=0; for(i=n/2-1;i>=0;i--)//此循环为建立初始堆 即把data[0...n-1]中的n个元素调整为大顶堆  建初始堆时,data[n/2]...data[n-1]为叶子结点已经满足堆的性质了所以一开始从第一个非叶子结点data[n/2-1]开始调整 {     HeapAdjust(data,i,n-1);//从第一个非叶子结点开始调整 } for(i=n-1;i>0;--i) {   //  swap(data[0],data[i]);//将堆顶元素和无序区的最后一个元素data[i]交换 int temp=data[0]; data[0]=data[i]; data[i]=temp; HeapAdjust(data,0,i-1);//将无序区从新调整为大顶堆 }}int main(){int a[]={1,1,1,434342,3,6,12,43,56,7,9,0,32787,3,4545};int length=sizeof(a)/sizeof(a[0]);HeapSort(a,length);ShellSort(a,length);for(int i=0;i<length;i++){   cout<<a[i]<<"  ";}cout<<endl;  return 0;}



一. 算法描述

基数排序(以整形为例),将整形10进制按每位拆分,然后从低位到高位依次比较各个位。主要分为两个过程:
(1)分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中(比如53,个位为3,则放入3号桶中)
(2)收集,再将放置在0~9号桶中的数据按顺序放到数组中
重复(1)(2)过程,从个位到最高位(比如32位无符号整形最大数4294967296,最高位10位)
以【521 310 72 373 15 546 385 856 187 147】序列为例,具体细节如下图所示









前半部分转自:http://blog.csdn.net/cjf_iceking/article/details/7943609
基数排序是按元素有效位数值进行分配和收集的排序方法 基数排序排序的基本思想以排序十进制数为例:设立10个队列分别编号0到9。首先按最低有有效位(个位数)值,把n个元素分配到10个队列中。然后按队列编号从小到大将各队列中的元素收集起来。接着将收集起来的元素按次低有效位的值分配到10个队列中去。重复上述的分配和收集过程。直到最高有效位 最后一次收集的元素将是一个有序序列

基数排序的程序实现。可以先求出待排序元素的最大值maxValue,然后求出最大值的位数maxBit  一共需要进行maxBit 次的分配和收集操作。
桶可以用队列去描述这样比较简单和直观
下面是参考程序:


#include<iostream>#include<cstdio>#include<cstdlib>#include<queue>using namespace std;/******************************************************** *函数名称:GetNumInPos *参数说明:num 一个整形数据 *          pos 表示要获得的整形的第pos位数据 *说明:    找到num的从低到高的第pos位的数据 *********************************************************/  int GetNumInPos(int num,int pos)  //找到num的从低到高的第pos位的数据 即可以得到装入的桶号{      int temp = 1;      for (int i = 0; i < pos - 1; i++)          temp *= 10;        return (num / temp) % 10;  }  int NumberOfBit(int number)//判读一个十进制整形数是多少位 如23为2位 则返回2{    int count=0;while(number){   count++;   number=number/10;}return count;}void RadixSort(int *a,int length){    if(a==NULL||length<=0){   return ;}    int maxValue=a[0];//for(int i=1;i<length;i++)//找待排序数组中元素的最大值{   if(maxValue<a[i])   {      maxValue=a[i];   }}int maxBit=NumberOfBit(maxValue);queue<int> Queue[10];//用Queue[0]....Queue[9] 九个队列来表示桶int pos=1;//pos范围为1到maxBit 表示从第一位开始 一直到待排序数组中元素的最高位while(maxBit--)//当待排序数组中元素的最大值的位数为maxBit 时,需要进行maxBit次的分配和收集操作{    for(int i=0;i<length;i++){    int index=GetNumInPos(a[i],pos); //根据元素第pos位数值将元素分配到相应的桶号中   即分配操作Queue[index].push(a[i]);}int j=0;//数组下标索引for(int i=0;i<length;i++)//改变此处for(int i=9;i>=0;i--)变为从大大小排列{    while(!Queue[i].empty()){    a[j++]=Queue[i].front();//根据桶号顺序的收集数组元素     收集操作Queue[i].pop();}}pos++;//开始 分配下一位}}int main(){int a[]={1,2,3,43,545,323,3,5,77666,};int length=sizeof(a)/sizeof(a[0]);RadixSort(a,length);for(int i=0;i<length;i++){   cout<<a[i]<<" ";}cout<<endl;   return 0;}



以上文字部分转自:http://www.cnblogs.com/dolphin0520/archive/2011/10/06/2199741.html

图片转自:http://blog.csdn.net/whuslei/article/details/6442755


进行链式基数排序的时间复杂度O(d(n+radix)),其中,一趟分配时间复杂度为O(n)一趟收集时间复杂度为O(radix),共进行d趟分配和收集

0 0