排序算法-整理归档

来源:互联网 发布:管家婆软件哪里买 编辑:程序博客网 时间:2024/06/16 09:05

我觉的冒泡什么的就不说了,只是会提一下。

稳定性的定义:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。

简单理解就是,对于相同的元素,不能因为排序,而改变他们的相对位置,不然就肯定做了无用功了(相对而言)。


1、插入排序-直接插入排序

是一个稳定的算法,不会改变相同元素的相对位置。复杂度 O(n)

2、插入排序-希尔排序

是一个修改的冒泡排序,首先将所有元素分成d组,然后组内进行插入排序,然后将d进行减小,分的组会越来越多,最后d=1时就基本有序了。不稳定

void ShellInsertSort(int a[], int n, int dk)  {      for(int i= dk; i<n; ++i){          if(a[i] < a[i-dk]){          //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入              int j = i-dk;                 int x = a[i];           //复制为哨兵,即存储待排序元素              a[i] = a[i-dk];         //首先后移一个元素              while(x < a[j]){     //查找在有序表的插入位置                  a[j+dk] = a[j];                  j -= dk;             //元素后移              }              a[j+dk] = x;            //插入到正确位置          }          print(a, n,i );      }        }  
void shellSort(int a[], int n){        int dk = n/2;      while( dk >= 1  ){          ShellInsertSort(a, n, dk);          dk = dk/2;      }  } 
3、选择排序-直接选择排序

是将一个剩下的所有元素中选择一个最小的与第一个交换,重复这个过程,我一直觉得这个和冒泡排序很像,存在不相邻元素的互换。不稳定

4、选择排序-堆排序,是对直接选择排序的改进。

堆排序是一个树形结构,堆对应一个完全二叉树。

(1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2),小顶堆,完全二叉树。堆排序是一个不稳定的算算法

由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
堆排序是就地排序,辅助空间为O(1)

/** * 已知H[s…m]除了H[s] 外均满足堆的定义 * 调整H[s],使其成为大顶堆.即将对第s个结点为根的子树筛选, * * @param H是待调整的堆数组 * @param s是待调整的数组元素的位置 * @param length是数组的长度 * */ void HeapAdjust(int H[],int s, int length) { int tmp = H[s]; int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置) while (child < length) { if(child+1 <length && H[child]<H[child+1]) { // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点) ++child ; } if(H[s]<H[child]) { // 如果较大的子结点大于父结点 H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点 s = child; // 重新设置s ,即待调整的下一个结点的位置 child = 2*s+1; } else { // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出 break; } H[s] = tmp; // 当前待调整的结点放到比其大的孩子结点位置上 } print(H,length); } /** * 初始堆进行调整 * 将H[0..length-1]建成堆 * 调整完之后第一个元素是序列的最小的元素 */ void BuildingHeap(int H[], int length) { //最后一个有孩子的节点的位置 i= (length -1) / 2 for (int i = (length -1) / 2 ; i >= 0; --i) HeapAdjust(H,i,length); } /** * 堆排序算法 */ void HeapSort(int H[],int length) { //初始堆 BuildingHeap(H, length); //从最后一个元素开始对序列进行调整 for (int i = length - 1; i > 0; --i) { //交换堆顶元素H[0]和堆中最后一个元素 int temp = H[i]; H[i] = H[0]; H[0] = temp; //每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整 HeapAdjust(H,0,i); } } 


5、冒泡排序

是最简单的,就是一个一个往上移动,最坏时间n^2,最好n,平均是O(n^2),是一个稳定算法

6、快排

快排是不稳定的,从他的原理就可以看出来,需要进行交换。

递归的

void swap(int *a, int *b)  {      int tmp = *a;      *a = *b;      *b = tmp;  }    int partition(int a[], int low, int high)  {      int privotKey = a[low];                             //基准元素      while(low < high){                                   //从表的两端交替地向中间扫描          while(low < high  && a[high] >= privotKey) --high;  //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端          swap(&a[low], &a[high]);          while(low < high  && a[low] <= privotKey ) ++low;          swap(&a[low], &a[high]);      }      print(a,10);      return low;  }      void quickSort(int a[], int low, int high){      if(low < high){          int privotLoc = partition(a,  low,  high);  //将表一分为二          quickSort(a,  low,  privotLoc -1);          //递归对低子表递归排序          quickSort(a,   privotLoc + 1, high);        //递归对高子表递归排序      }  }  

另外一个面试经常考的算法出来了,归并排序

7、归并排序

归并排序是稳定的排序算法,主要是因为归并排序不存在将相同元素变换相对位置的操作。

//将r[i…m]和r[m +1 …n]归并到辅助数组rf[i…n]  void Merge(ElemType *r,ElemType *rf, int i, int m, int n)  {      int j,k;      for(j=m+1,k=i; i<=m && j <=n ; ++k){          if(r[j] < r[i]) rf[k] = r[j++];          else rf[k] = r[i++];      }      while(i <= m)  rf[k++] = r[i++];      while(j <= n)  rf[k++] = r[j++];  }  

merge是公有的,至于是递归去实现还是利用堆栈,是另外一个函数所做的事情。

void MSort(ElemType *r, ElemType *rf,int s, int t)  {       ElemType *rf2;      if(s==t) r[s] = rf[s];      else      {           int m=(s+t)/2;          /*平分*p 表*/          MSort(r, rf2, s, m);        /*递归地将p[s…m]归并为有序的p2[s…m]*/          MSort(r, rf2, m+1, t);      /*递归地将p[m+1…t]归并为有序的p2[m+1…t]*/          Merge(rf2, rf, s, m+1,t);   /*将p2[s…m]和p2[m+1…t]归并到p1[s…t]*/      }  }  
这一个归并排序是,单路的,主要原理是从0序列开始,先归并1个元素,然后归并2个元素,4,8。。。
void MergeSort(ElemType *r, ElemType *rf, int lenght)  {       int len = 1;      ElemType *q = r ;      ElemType *tmp ;      while(len < lenght) {          int s = len;          len = 2 * s ;          int i = 0;          while(i+ len <lenght){              Merge(q, rf,  i, i+ s-1, i+ len-1 ); //对等长的两个子表合并              i = i+ len;          }          if(i + s < lenght){              Merge(q, rf,  i, i+ s -1, lenght -1); //对不等长的两个子表合并          }          tmp = q; q = rf; rf = tmp; //交换q,rf,以保证下一趟归并时,仍从q 归并到rf      }  }    

记住,快排和归并,都是递归的实现最简单。
8、桶排序/基数排序

桶排序是稳定的

把数据分组,放在一个个的桶中,然后对每个桶里面的在进行排序。
例如要对大小为[1..1000]范围内的n个整数A[1..n]排序

首先,可以把桶设为大小为10的范围,具体而言,设集合B[1]存储[1..10]的整数,集合B[2]存储 (10..20]的整数,……集合B[i]存储( (i-1)*10, i*10]的整数,i = 1,2,..100。总共有 100个桶。

然后,对A[1..n]从头到尾扫描一遍,把每个A[i]放入对应的桶B[j]中。 再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择,乃至快排,一般来说任 何排序法都可以。

缺点也很明显

1、空间复杂度

2、要求,数据必须要在一定范围内

基数排序,可以想一下扑克牌,先根据花色进行排序,然后根据数字,是稳定的

基数排序过程无须比较关键字,而是通过“分配”和“收集”过程来实现排序。它们的时间复杂度可达到线性阶:O(n)。

总的比较

分为两种,一种是高位优先MSD,一种是低位优先LSD。

排序类别时间空间稳定插入   O(n^2)1希尔O(n^2)
1×冒泡O(n^2)
1
选择O(n^2)
1×
快速O(nlogn)
O(nlogn)
×
堆排序O(nlogn)
1×
归并排序O(nlogn)
O(n)



0 0
原创粉丝点击