排序算法总结

来源:互联网 发布:wamp怎么运行php 编辑:程序博客网 时间:2024/06/06 12:41

只讨论内部排序算法:

1.比较排序,时间复杂度O(nlogn)~O(n^2)有:冒泡排序。选择排序,插入排序,归并排序,堆排序,快速排序
2.非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序
排序算法的性能:

排序算法的稳定性:
可以简单定义为:如果Ai =  Aj,排序前Ai在Aj之前,排序后Ai还在Aj之前,则称为这种排序算法是稳定的。也就是保证排序前后两个相等的数的相对顺序不变。排序算法的好处是:排序算法如果是稳定的,那么从一个键上排序。然后再从另一个键上排序,前一个键排序的结果可以为后一个键排序所用

冒泡排序:

冒泡排序是一种很简单的排序的算法,它重复走访过要排序的元素,依次比较相邻两个元素,如果它们的顺序错误就把它们调换过来,直到没有元素再需要交换,排序完成。
原理如下:
1.比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。
2.对每一对相邻元素同样的工作,从开始第一队到结尾的最后一对。这捕做完,最后的元素会是最大的数。
3.针对所有的元素重复以上的步骤,除了最后一个。
4.持续每次对越来越小的元素重复上面的步骤,直到没有任何一对数字需要比较。
代码:
void  sort(int sort[]){for(int j=0;j<sort.length()-1;j++){//每次最大的元素排到数组最后   for(int i=0;i<sort.length()-1-j;i++){//依次比较相邻的两个元素     if(A[i])>A[i+1]{//交换       swaf(A,i,i+1);     }   } }}
改进:设置一标志性变量pos,用于记录每趟排序最后一次进行交换的位置,由于pos位置之后的记录均已交换到位,故在进行下一趟排序只要扫描到pos位置即可。

void sort2(int sort2[]){   int i = sort2.length()-1;//初始时,最后位置保持不变   while(i>0){     int pos = 0;//每趟开始,无记录交换    for(int j = 0;j<i;j++){      if(sort2[j] > sort2[j+1]){        pos = j;//记录交换位置        swaf(sort,j,j+1);     }  i =pos;//为下一趟排序做准备   }  }}
再改进:鸡尾酒排序
此算法和冒泡排序的不同处在于从低到高然后从高到低,而冒泡排序则仅从低到高去比较序列里的每个元素,可以得到比冒泡排序稍微好一点的效能。
void sort3(int A[], int n){   int left = 0;//初始化边界   int right = n-1;   while(left < right){       for(int i = left;i < right; i++){//前半轮,将最大元素放到最后         if(A[i] > A[i+1]){           swap(A,i,i+1);   }  }  right--;  for(int i =right;i>left;i--){    if(A[i-1]>A[i]){      swap(A,i-1.i);    } }left++}}

选择排序:

选择排序是一种简单直观的排序算法,它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放在序列的起始位置作为排序;然后再从剩余排序元素中继续寻找最小(大)元素,放到已排序的末尾,以此类推,直到所有元素均排序完毕。
选择排序和冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放大合适的位置;选择排序每遍历一次都记住当前最小(大)元素的位置,最后仅需交换操作即可将其放到合适的位置。
void SelectSort(int A[],int n){   for(int i=0;i<n-1;i++){//i为已排序列的末尾     int min =i;     for(int j = i+1;j< n;j++){//末排序序列        if(A[j]<A[min]){  //找出末排序序列中最小值           min = j;     }    if(min != i){      Swap(A,min,i)//放到已排序序列的末尾    }   } }}


插入排序:

插入排序是一种简单直观的排序算法,工作原理类似于我们抓扑克牌
具体原理如下:
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后往前扫描
3.如果该元素大于新元素,将该元素移到下一个位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
6.重复步骤2-5
void insertsort(int A[],int n){   for(int i =1;i<n;i++){     int s = A[i];     int j= i-1;     while(j>=0 && A[j]>s){        A[j+1] = A[j];        j--;  }      A[J+1] = s;  } }
改进后插入排序:二分插入排序,因为左边的数字总是排序好的,所以可以用二分排序来优化
void insertdichot(int A[],int n){   for(int i = 1;i<n;i++){     int get = A[i]; //右边的数     int left = 0;     int right = i -1;//左右边边界初始化     while(left <= right){//采用二分定位新数        int mid = (left + right)/2        if(A[mid] > get){           right = mid -1;        else           left = mid + 1;}for(int j =i -1;j>=left;j--){//将欲插入新数位置右边的数整体向右移动一个单位  A[j+1] = A[j];}A[left] = get;         }}}

归并排序:

归并排序的实现分为递归实现与非递归实现,递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题。然后用小问题的答案来解决整个大问题。非递归实现的归并排序首先是进行两两归并,然后四四归并,然后八八归并,一直下去直到并到整个数组。
归并操作步骤如下:
1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一个位置
4.重复步骤3直到某一指针到达序列尾
5.将另一序列剩下的所有元素直接复制到合并序列尾
void Merge(int A[],int left,int mid,int right){//合并两个已排好序的数组A[left...mid]和A[mid+1...right]int len = right - left +1;int *temp  = new int[len];//辅助空间int index = 0;int i = left;//前一数组的起始元素int j=mid+1;//后一数组的起始元素while(i<mid && j<=right){  temp[index] = A[i] <=A[j] ? A[i++] :A[j++];}while(i <= mid){  temp[index++] = A[i++];}while(j <= right){ temp[index++] = A[j++];}for(int k = 0;k<len;K++){   A[left++] =temp[k];}}void MergeSortRecursion(int A[],int left,int right)//递归实现的归并排序{  if(left == right)    return;  int mid = (left + right)/2;  MergeSortRecursion(A, left, mid);    MergeSortRecursion(A, mid + 1, right);    Merge(A, left, mid, right);}void MergeSortIteration(int A[], int len)    // 非递归(迭代)实现的归并排序(自底向上){    int left, mid, right;// 子数组索引,前一个为A[left...mid],后一个子数组为A[mid+1...right]    for (int i = 1; i < len; i *= 2)        // 子数组的大小i初始为1,每轮翻倍    {        left = 0;        while (left + i < len)              // 后一个子数组存在(需要归并)        {            mid = left + i - 1;            right = mid + i < len ? mid + i : len - 1;// 后一个子数组大小可能不够            Merge(A, left, mid, right);            left = right + 1;               // 前一个子数组索引向后移动        }    }}

快速排序算法:

快速排序是对冒泡排序的一种改进,通过一趟排序将要排序的数据分割成独立的两个部分,其中一部分的所有数据都比另一部分的所有数据,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个暑假变成有序序列
一趟快速排序的算法是:
1.设置两个变量i,j,排序开始的时候:i=0;j=N-1;
2.以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3.从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]交换;
4.从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5.重复第3,4步直到i=j;(3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j,i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i,j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)
int Partition(int A[],int left,int right){   // 划分函数
   int pivot = A[right]; //每次都选择最后一个元素作为基准
   int tail = left -1;//tail为小于基准的子数组最后一个元素的索引
   for(int i = left;i<right;i++){//遍历基准以外的其他元素
      if(A[i]< = pivot){
        swap(A,++tail,i);
}
}
swap(A,tail+1,right);//最后把基准放到前一个子数组的后边,剩下的子数组既是大于基准的子数组
return tail +1;

void QuickSort(int A[]),int left,int right{
   if(left >=right){
     return;
   int position = Partition(A,left,right);//基准的索引
   QuickSort(A,left,position-1);
   QuickSort(A.position+1,right);
}

}

}

希尔排序:

希尔排序是插入排序的一种,是直接插入排序算法的一种。
举个例子:


void shell(int a[])  int d=a.length;            while(true)            {                d=d/2;                for(int x=0;x<d;x++)                {                    for(int i=x+d;i<a.length;i=i+d)                    {                        int temp=a[i];                        int j;                        for(j=i-d;j>=0&&a[j]>temp;j=j-d)                        {                            a[j+d]=a[j];                        }                        a[j+d]=temp;                    }                }                if(d==1)                {                    break;                }            }}

排序算法很基础也很重要。也要注意每种算法所适合的场景。


原创粉丝点击