排序算法小结(C++实现)

前言:在这里总结一下各种排序方式以增强理解和之后复习方便,附带一些优化方式

目录

非线性时间

  1.比较

    1冒泡排序

    2快速排序

  2.插入

    1插入排序

    2希尔排序

  3.选择

    1选择排序

    2堆排序

  4.归并

    1二路归并

    2多路归并

线性O(n)

  1.计数排序

  2.堆排序

  3.基数排序

 

=====================================================================================================

=====================================================================================================

正文

1.简单冒泡排序

  两层循环遍历数组:

    内层循环进行交换:如果pre大于next ,swap(pre, next)   =>  实现最大元素交换到队尾

    外层循环进行遍历:每次都把剩余数组中最大元素交换到队尾,最后完成排序

    最好最坏都是O(n^2)

    平均时间复杂度O(n^2)

void BubbleSort(int *a, int n) //3. 冒泡
{
    for(int i=0 ; i<n ; i++)
    {
        for(int j=0 ; j<size-i-1 ; j++) 
        {
            if(a[j]>a[j+1])
            {
                swap(a[j], a[j+1]);
            }
        }
    }
}

 1.5优化冒泡排序

  优化一:标记是否交换过(数组是否已经有序)

    对于像1,2,3,5,4这种基本有序的数列,只用交换一次,后来的循环都是多余的,所以可以加入falg标记是这一趟否交换过,没有的话直接退出

 

void BubbleSort(int *a, int n) //3. 冒泡
{
    for(int i=0 ; i<n ; i++)
    {
        int flag=0;  //flag
        for(int j=0 ; j<n-i-1; j++)
        {
            if(a[j]>a[j+1])
            {
                swap(a[j], a[j+1]);
                flag = 1;
            }
        }
        if(!flag) //优化一:如果没改动过(排好序了),直接返回
            return;
    }
}

在优化一的基础上,我们考虑这样的情况:前面大部分无序,后面有序(如1,2,5,7,4,3,6,8,9,10),优化一并不起作用

  优化二:记录最后一次交换的位置,如果后面没有交换,说明后面是有序的,跳过就好

void BubbleSort(int *a, int n) //3. 冒泡
{
    int k=n-1;
    for(int i=0 ; i<n ; i++)
    {
        int pos=0;
        int flag=0;
        for(int j=0 ; j<k; j++)
        {
            if(a[j]>a[j+1])
            {
                swap(a[j], a[j+1]);
                flag = 1;
                pos=j;//记录
            }
        }
        if(!flag)
            return;
        k=pos;//优化二:跳到最后一次交换的位置
    }
}

  优化三:每次选择最大的与队尾交换,其实就是选择排序,下面写

 

=====================================================================================================

 

2.快速排序

分治法(divide and conquer)、递归(recursion)的一种应用,总的思想:设置一个基准p(pivot),把数组分为两部分:大于p,小于p。

对于每一部分再找p,再分,一直循环直到每个小队列只剩一个元素,排序完成

方法:下标left ,right:当前部分的边界 

   下表 r, l:从两边往中间遍历的两个“指针”(后面被定在“出问题”(需要换位置)的我位置)

     基准p=a[left] ,  l=left, r=right

    从右边开始,第一个出问题的数赋给a[left](第一个出问题的位置留一个坑),  l往右移动到第一个出问题的数赋给刚才右边的坑(l位置出现一个坑)

    一直循环,到最后坑的位置就是中间的位置,把p赋给坑,完成这次大循环

void QuickSort(int *a,int left,int right) //4.快速
{
    if(left>right)
        return;
    int flag=a[left];
    int l=left;
    int r=right;

    while(l<r)
    {
        while(l<r)  //从最右找比flag小的
        {
            if(a[r]>=flag)
            {
                r--; //这个数据没问题,标志左移
            }
            else//要把它放到左边
            {
                a[l]=a[r];
                l++;
                break;
                //找到了一个,退出当前循环
            }
        }
        while(l<r) //从最左找比flag大的
        {
            if(flag>=a[l])//数据没问题
            {
                l++;
            }
            else
            {
                a[r]=a[l];
                r--;
                break;
            }
        }
    }
    a[l]=flag;
    QuickSort(a, left, l-1);
    QuickSort(a,r+1,right);
}

2.5优化快速排序

  快排的优化大部分就是对基准选择的优化,在上面的代码中,我们使用的是最左边的元素a[left]作为p,这种选择对于基本排好的情况(快排的最差情况)很慢,

基本山和最垃圾的冒泡排序差不多O(n^2),甚至更差,这肯定不行啊:

  优化一:三数取中法

    解决数据基本有序的(就是找到数组中最小下标,最大下标,中间下标的数字,进行比较,把中间大的数组放在最左边)

    取a[mid],a[left], a[right] 三者之中间大小的数作为基准p

int m=left+(right-left)/2;//找到中间的数字的下标
    if(arr[left]>arr[right])//最左大于最右的时候,交换左右
    {
        swap(arr,left,right);
    }
    if(arr[m]>arr[right])//如果中间的>right ,交换
    {
        swap(arr,m,right);
    }
    if(arr[m]>arr[left])//如果中间的>left,交换
    {
        swap(arr,m,right);
    }
        //这样就把三个数中中间大小的数换到了left位置上,之后的程序和前面一样
    


while() { ... }

  优化二:随机选择法

    序列部分有序的时候,固定位置选取不好,随机选

/*随机选择p的位置,区间在low和high之间*/  
int SelectPivotRandom(int arr[],int low,int high)  
{  
  //产生p的位置  
   srand((unsigned)time(NULL));  
  int pivotPos = rand()%(high - low) + low;   
  //把p位置的元素和low位置元素互换,此时可以和普通的快排一样调用划分函数  
  swap(arr[pivotPos],arr[low]);  
   return arr[low];  
}  

  优化三:序列够小是使用插入排序

    在数列比较短的时候,使用快排“大材小用”,5-20个数据以下的时候插入排序就比快排更快了,我们取10

#define  max_len  10 //数据量小于10的时候用插入
void quick(int *arr,int left,int right)
{
    int length=right-left;
    if(length>max_len )
    {
        int pivot=partition(arr,left,right);
        quick(arr,left,pivot-1);
        quick(arr,pivot+1,right);
    }
    else
    {
        //用插入排序
    }
}

 

=====================================================================================================

 

posted @ 2019-08-14 15:20  隔壁老张YU  阅读(156)  评论(0编辑  收藏  举报