排序总结

来源:互联网 发布:有没学英语的软件 编辑:程序博客网 时间:2024/06/10 03:43

一、直接插入排序(插入排序)。
     1、思想:如下图所示,每次选择一个元素K插入到之前已排好序的部分A[1…i]中,插入过程中K依次由后向前与A[1…i]中的元素进行比较。若发现发现A[x]>=K,则将K插入到A[x]的后面,插入前需要移动元素。
 
     3、算法时间复杂度。 
        最好的情况下:正序有序(从小到大),这样只需要比较n次,不需要移动。因此时间复杂度为O(n) 
        最坏的情况下:逆序有序,这样每一个元素就需要比较n次,共有n个元素,因此实际复杂度为O(n?2) 
        平均情况下:O(n?2)
     4、稳定性。 
     理解性记忆比死记硬背要好。因此,我们来分析下。稳定性,就是有两个相同的元素,排序先后的相对位置是否变化,主要用在排序时有多个排序规则的情况下。在插入排序中,K1是已排序部分中的元素,当K2和K1比较时,直接插到K1的后面(没有必要插到K1的前面,这样做还需要移动!!),因此,插入排序是稳定的。
     5、代码

void insertsort(int a[],int n)//按递增进行插入排序 {int i,j,temp;for(i=1;i<n;i++){temp=a[i];j=i-1;//从左向右在有序区查找啊a[i]的位置 while(j>=0&&temp<a[i]){a[j+1]=a[j];//大于a[i]的后移 j--;}a[j-1]=temp;//在j+1处插入a[i] }}



二、直接选择排序(选择排序)
      1、思想:首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。具体做法是:选择最小的元素与未排序部分的首部交换,使得序列的前面为有序。 
       2、时间复杂度。
      最好情况下:交换0次,但是每次都要找到最小的元素,因此大约必须遍历N*N次,因此为O(N*N)。减少了交换次数!
      最坏情况下,平均情况下:O(N*N)
      3、稳定性
      由于每次都是选取未排序序列A中的最小元素x与A中的第一个元素交换,因此跨距离了,很可能破坏了元素间的相对位置,因此选择排序是不稳定的!
      4、代码

void selectsort(int a[],int n){int i,j,k,temp;for(i=0;i<n-1;i++)//第i趟排序 {k=i;//从左向右在有序区查找啊a[i]的位置 for(j=i+1;j<n;j++)//在[i,n-1]中选出最小值 {if(a[j]<a[k])k=j;//记下最小值的位置 }if(k!=i)//交换a[i]和a[k] {temp=a[i];a[i]=a[k];a[k]=temp;}  }}



三、冒泡排序
 1、思想:(从小到大排序)存在10个不同大小的气泡,由底至上地把较大的气泡逐步地向上升,这样经过遍历一次后,最大的气泡就会被上升到顶(下标为0),然后再从底至上地这样升,循环直至十个气泡大小有序。
 在冒泡排序中,最重要的思想是两两比较,将两者较大的升上去。
 2、代码

  void popSort(int a[]) {    int i,j,temp;    for(i=0;i<N-1;i++)//遍历N-1次    {     for(j=0;j<N-i-1;j++)//最大的气泡上升到顶     {      if(a[j]>a[j+1])      {       temp=a[j];       a[j]=a[j+1];       a[j+1]=temp;      }     }  }  }



四、快速排序
1、思想

快速排序采用的思想是分治思想。

快速排序是找出一个元素(理论上可以随便找一个)作为基准(pivot),然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。
2、代码

  #include<iostream>  #include<stdio.h>  #include<string.h>  int a[10010];  using namespace std;  void Swap(int *a,int *b){      int t=*a;      *a=*b;      *b=t;  }  int partition(int a[],int l,int h){      int v = a[l];      int i=l;      int j=h+1;      while(true){          while(a[++i]<v)if(i==h)break;          while(v<a[--j])if(j==l)break;          if(i>=j)break;          Swap(&a[i],&a[j]);      }      Swap(&a[l],&a[j]);      return j;  }  void quick_sort(int a[],int l,int h){      if(h<=l)return ;      int j=partition(a,l,h);      quick_sort(a,l,j-1);//左边      quick_sort(a,j+1,h);//右边  }  int main(int argc, char *argv[])  {      //freopen("1157.in","r",stdin);      int N;      int i;      while(scanf("%d",&N)!=EOF)      {          i=0;          for(i=0;i<N;++i)              scanf("%d",&a[i]);          quick_sort(a, 0, N-1);          printf("%d\n",a[N/2]);      }      return 0;  }


题链接:http://acm.hdu.edu.cn/showproblem.php?pid=1157

 


五、合并排序
合并排序(MERGE SORT)是又一类不同的排序方法,合并的含义就是将两个或两个以上的有序数据序列合并成一个新的有序数据序列,因此它又叫归并算法。它的基本思想就是假设数组A有N个元素,那么可以看成数组A是又N个有序的子序列组成,每个子序列的长度为1,然后再两两合并,得到了一个 N/2 个长度为2或1的有序子序列,再两两合并,如此重复,值得得到一个长度为N的有序数据序列为止,这种排序方法称为2—路合并排序。
 //将有序数组a[]和b[]合并到c[]中
 

void MemeryArray(int a[], int n, int b[], int m, int c[]){  int i, j, k;  i = j = k = 0;  while (i < n && j < m)  {   if (a[i] < b[j])    c[k++] = a[i++];   else    c[k++] = b[j++];   }  while (i < n)   c[k++] = a[i++];  while (j < m)   c[k++] = b[j++];}


  例如数组A有7个数据,分别是: 49 38 65 97 76 13 27,那么采用归并排序算法的操作过程如图7所示:

  初始值 [49] [38] [65] [97] [76] [13] [27]

  看成由长度为1的7个子序列组成

  第一次合并之后 [38 49] [65 97] [13 76] [27]

  看成由长度为1或2的4个子序列组成

  第二次合并之后 [38 49 65 97] [13 27 76]

  看成由长度为4或3的2个子序列组成

  第三次合并之后 [13 27 38 49 65 76 97]
    代码

//将有二个有序数列a[first...mid]和a[mid...last]合并。void mergearray(int a[], int first, int mid, int last, int temp[]){int i = first, j = mid + 1;int m = mid,   n = last;int k = 0;while (i <= m && j <= n){if (a[i] <= a[j])temp[k++] = a[i++];elsetemp[k++] = a[j++];}while (i <= m)temp[k++] = a[i++];while (j <= n)temp[k++] = a[j++];for (i = 0; i < k; i++)a[first + i] = temp[i];}void mergesort(int a[], int first, int last, int temp[]){if (first < last){int mid = (first + last) / 2;mergesort(a, first, mid, temp);    //左边有序mergesort(a, mid + 1, last, temp); //右边有序mergearray(a, first, mid, last, temp); //再将二个有序数列合并}}bool MergeSort(int a[], int n){int *p = new int[n];if (p == NULL)return false;mergesort(a, 0, n - 1, p);delete[] p;return true;}


六、堆排序过程
堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
既然是堆排序,自然需要先建立一个堆,而建堆的核心内容是调整堆,使二叉树满足堆的定义(每个节点的值都不大于其父节点的值)。调堆的过程应该从最后一个非叶子节点开始,假设有数组A = {1, 3, 4, 5, 7, 2, 6, 8, 0}。那么调堆的过程如下图,数组下标从0开始,A[3] = 5开始。分别与左孩子和右孩子比较大小,如果A[3]最大,则不用调整,否则和孩子中的值最大的一个交换位置,在图1中是A[7] > A[3] > A[8],所以A[3]与A[7]对换,从图1.1转到图1.2。
 
所以建堆的过程就是
   1: for ( i = headLen/2; i >= 0; i++)
   2: 
   3:        do AdjustHeap(A, heapLen, i)
调堆:如果初始数组是非降序排序,那么就不需要调堆,直接就满足堆的定义,此为最好情况,运行时间为Θ(1);如果初始数组是如图1.5,只有A[0] = 1不满足堆的定义,经过与子节点的比较调整到图1.6,但是图1.6仍然不满足堆的定义,所以要递归调整,一直到满足堆的定义或者到堆底为止。如果递归调堆到堆底才结束,那么是最坏情况,运行时间为O(h) (h为需要调整的节点的高度,堆底高度为0,堆顶高度为floor(logn) )。
建堆完成之后,堆如图1.7是个大根堆。将A[0] = 8 与 A[heapLen-1]交换,然后heapLen减一,如图2.1,然后AdjustHeap(A, heapLen-1, 0),如图2.2。如此交换堆的第一个元
素和堆的最后一个元素,然后堆的大小heapLen减一,对堆的大小为heapLen的堆进行调堆,如此循环,直到heapLen == 1时停止,最后得出结果如图3。60: void HeapSort(int A[], int aLen)
  61: {
  62:     int hLen = aLen;
  63:     int temp;
  64: 
  65:     BuildHeap(A, hLen);      //建堆
  66: 
  67:     while (hLen > 1)
  68:     {
  69:         temp = A[hLen-1];    //交换堆的第一个元素和堆的最后一个元素
  70:         A[hLen-1] = A[0];
  71:         A[0] = temp;
  72:         hLen--;        //堆的大小减一
  73:         AdjustHeap(A, hLen, 0);  //调堆
  74:     }
  75: }


 
   1: /*
   2:     输入:数组A,堆的长度hLen,以及需要调整的节点i
   3:     功能:调堆
   4: */
   5: 
   6: void AdjustHeap(int A[], int hLen, int i)
   7: {
   8:     int left = LeftChild(i);  //节点i的左孩子
   9:     int right = RightChild(i); //节点i的右孩子节点
  10:     int largest = i;
  11:     int temp;
  12: 
  13:     while(left < hLen || right < hLen)
  14:     {
  15:         if (left < hLen && A[largest] < A[left])
  16:         {
  17:             largest = left;
  18:         }
  19:        
  20:         if (right < hLen && A[largest] < A[right])
  21:         {
  22:             largest = right;
  23:         }
  24: 
  25:         if (i != largest)   //如果最大值不是父节点
  26:         {
  27:              temp = A[largest]; //交换父节点和和拥有最大值的子节点交换
  28:              A[largest] = A[i];
  29:              A[i] = temp;
  30: 
  31:             i = largest;         //新的父节点,以备迭代调堆
  32:             left = LeftChild(i);  //新的子节点
  33:             right = RightChild(i);
  34:         }
  35:         else
  36:         {
  37:             break;
  38:         }
  39:     }
  40: }
  41: 
  42: /*
  43:     输入:数组A,堆的大小hLen
  44:     功能:建堆
  45: */
  46: void BuildHeap(int A[], int hLen)
  47: {
  48:     int i;
  49:     int begin = hLen/2 - 1;  //最后一个非叶子节点
  50:     for (i = begin; i >= 0; i--)
  51:     {
  52:         AdjustHeap(A, hLen, i); 
  53:     }
  54: }
  55: 
  56: /*
  57:     输入:数组A,待排序数组的大小aLen
  58:     功能:堆排序
  59: */
  60: void HeapSort(int A[], int aLen)
  61: {
  62:     int hLen = aLen;
  63:     int temp;
  64: 
  65:     BuildHeap(A, hLen);      //建堆
  66: 
  67:     while (hLen > 1)
  68:     {
  69:         temp = A[hLen-1];    //交换堆的第一个元素和堆的最后一个元素
  70:         A[hLen-1] = A[0];
  71:         A[0] = temp;
  72:         hLen--;        //堆的大小减一
  73:         AdjustHeap(A, hLen, 0);  //调堆
  74:     }
  75: }

0 0