排序算法汇总

1.冒泡排序
冒泡排序,相邻的数据进行两两比较,小数放在前面,大数放在后面,这样一趟下来,最小的数就被排在了第一位,第二趟也是如此,如此类推,直到所有的数据排序完成。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我们不会无聊地把他们俩交换;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。【冒泡排序可谓是最经典的排序算法了,它是基于比较的排序算法,时间复杂度为O(n^2),其优点是实现简单,n较小时性能较好。

#include<iostream>
using namespace std;
void bubble_sort(int*a,int length)
{
    int temp=0;
    int flag=0;
    for(int i=0;i<length-1;i++)//进行n-1趟,不是n趟
    {
         flag=0;
         for(int j=length-1;j>=i;j--)//j>=i是前面i个已经排好序了,每一趟都把最小的冒上去
        {
            if(a[j-1]>a[j])
            {
                temp=a[j];
                a[j]=a[j-1];
                a[j-1]=temp;
                flag=1;
            }
        }
        if(flag==0)
            return;//若一趟下来一个都没有交换,证明已经排好序了,提前结束
    }


}

int main()
{
    int a[5]={2,1,3,6,5};
    bubble_sort(a,5);
    for(int i=0;i<5;i++)
    cout<<a[i]<<"  ";
}

2.插入排序
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。

#include<iostream>
using namespace std;
void insert_sort(int*a,int length)
{
    int temp,i,j;
    for( i=1;i<length;i++)
        {
            temp=a[i];
            for(j=i-1;j>=0&&temp<a[j];j--)
            {
                a[j+1]=a[j];//前面的比我大,那前面的就要往后挪一位
            }
            a[j+1]=temp;//找到我的位置了,放进去
        }

}

int main()
{
    int a[5]={2,1,3,6,5};
    insert_sort(a,5);
    for(int i=0;i<5;i++)
    cout<<a[i]<<"  ";
}

3.选择排序
选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。O(n^2),不稳定。

#include<iostream>
using namespace std;
void select_sort(int*a,int length)
{
    int min,temp;

     for(int i=0;i<length;i++){//找到最小的元素,将其放到a【i】中
        min=i;
        for(int j=i;j<length;j++){

             if(a[j]<a[min])
                min=j;
        }

           temp=a[min];
           a[min]=a[i];
           a[i]=temp;


}
}

int main()
{
    int a[5]={2,1,3,6,5};
    select_sort(a,5);
    for(int i=0;i<5;i++)
    cout<<a[i]<<"  ";
}

4.快速排序
快速排序是目前在实践中非常高效的一种排序算法,它不是稳定的排序算法,平均时间复杂度为O(nlogn),最差情况下复杂度为O(n^2)。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

#include<iostream>
using namespace std;
int quicksort(int v[],int left,int right)
{
    if(left<right)
    {
        int key=v[left];//选取第一个元素作为划分基准
        int low =left;
        int high=right;
        while(low<high)//终止条件为头尾两个指针相遇
        {
            while(low<high&&v[high]>key)
            {
                high--;
            }
            v[low]=v[high];//从右往左扫,若比key值小,则交换,交换完后,又从左往右扫

            while(low<high&&v[low]<key)
            {
                low++;
            }
            v[high]=v[low];//从左往右扫,若比key值大,则交换,交换完后,又从右往左扫
        }
        v[low]=key;//不要忘了划分的基准值
        quicksort(v,left,low-1);
        quicksort(v,low+1,right);
    }
}
int main()
{
     int a[10]={2,4,100,24,12,29,7,23,45,67};
     quicksort(a,0,9);
     for(int i=0;i<10;i++)
     {
         cout<<a[i]<<'\t';
     }
}

5.堆排序
讲解:https://jingyan.baidu.com/article/5225f26b057d5de6fa0908f3.html

#include <iostream>
using namespace std;
void siftdown(int a[],int i,int len){
   int temp=a[i];//a[i]是要下移的元素
   int largest;
   for(largest=2*i+1;largest<len;largest=largest*2+1){//默认最大的为左子节点,同时要迭代完整棵子树
       if(a[largest]<a[largest+1]&&largest+1<len){//右子节点是最大的情况
          largest++;
    }
       if(a[largest]>temp){//将两个子节点中最大的换给父节点
          a[i]=a[largest];
          i=largest;//必须记录最大值的下标,因为下面还可能继续迭代
        }

 }
   a[i]=temp;
}
void makeheap(int a[],int len){//建堆是倒着来的,从最后一个节点开始siftdown,
    //由于叶节点可以直接跳过,因此从最后一个非叶节点开始,也就是i=len/2-1
   for(int i=len/2-1;i>=0;i--)
      siftdown(a,i,len);
}
int main()
{
    int a[10]={2,45,20,3,9,6,5,109,200,10};
    makeheap(a,10);
    for(int j=9;j>=1;j--){
        int temp=a[0];
        a[0]=a[j];
        a[j]=temp;
        siftdown(a,0,j);//建完堆之后,堆顶a[0]为最大值,把它和最后一个元素a[j]交换,
        //这样的话,就需要对a[0]进行siftdown,同时由于最后一个元素已经到了争取的位置,因此不再牵涉它
    }
  for(int i=0;i<10;i++){
      cout<<a[i]<<" ";
  }
}

5.基数排序

#include<iostream>
#include<vector>
using namespace std;
int getmax(int a[],int size){//获取最大有多少位
     int maxnum=a[0];
     int max=-1;//个位称为“0位”
     for(int i=0;i<size;i++){//先求最大的数进而求得最大位数
        if(a[i]>maxnum)
            maxnum=a[i];
     }
     while(maxnum){
        maxnum=maxnum/10;
        max++;
     }
     return max;
}
void radixsort(int a[],int size,int max){
     vector<int> vec[10];//这是一个数组,数组元素的类型是int向量
     int counts[10];//counts[i]的值表示这个位置为i的数的个数,比如个位为2的数字的个数
     int radix=1;
     for(int i=0;i<max;i++){//每一位都要做一次
        for(int p=0;p<10;p++)
            counts[p]=0;
        for(int p=0;p<10;p++)
            vec[p].clear();//每次遍历要清空
        for(int j=0;j<size;j++){//例如个位为2的数则添加到vec[2]中
            int k=a[j]/radix%10;//获得该位的数字
            vec[k].push_back(a[j]);
            counts[k]++;
        }
         int k=0;
        for(int i=0;i<10;i++){
            for(int j=0;j<counts[i];j++){
                a[k]=vec[i][j];
                k++;
            }
        }
     radix=radix*10;
     }
}
int main(){
    int a[5]={123,45,3476,91,32};
    int max=getmax(a,5);
    radixsort(a,5,max);
    for(int i=0;i<5;i++){
        cout<<a[i]<<" ";
    }
}

6.希尔排序
将无序数组分割为若干个子序列,子序列不是逐段分割的,而是相隔特定的增量的子序列,对各个子序列进行插入排序;然后再选择一个更小的增量,再将数组分割为多个子序列进行排序……最后选择增量为1,即使用直接插入排序,使最终数组成为有序。

#include <iostream>
using namespace std;
void shell_sort(int*a,int length)
{
    int i,j,gap;
    for(gap=length/2;gap>0;gap/=2)
    {
        for(i=0;i<gap;i++)
            for(j=i+gap;j<length;j+=gap)
        {
            if(a[j]<a[j-gap])
            {
                int temp=a[j];
                int k=j-gap;
                while(k>=0&&a[k]>temp)
                {
                    a[k+gap]=a[k];
                    k-=gap;
                }
                a[k+gap]=temp;

            }
        }
    }
}
int main()
{
    int a[5]={2,1,3,6,5};
    shell_sort(a,5);
    for(int i=0;i<5;i++)
    cout<<a[i]<<"  ";
}

7.归并排序
归并排序具体工作原理如下(假设序列共有n个元素):

将序列每相邻两个数字进行归并操作(merge),形成floor(n/2)个序列,排序后每个序列包含两个元素
将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
重复步骤2,直到所有元素排序完毕

归并排序是稳定的排序算法,其时间复杂度为O(nlogn),如果是使用链表的实现的话,空间复杂度可以达到O(1),但如果是使用数组来存储数据的话,在归并的过程中,需要临时空间来存储归并好的数据,所以空间复杂度为O(n)

#include <iostream>
using namespace std;
//对一个数组的两部分进行合并排序
void Merge(int a[],int begin,int last,int mid,int temp[])//注意这个参数设计
{
    int i=begin;

    int j=mid+1;
    int k=0;

    while(i<=mid&&j<=last)
    {
        if(a[i]<=a[j])
            temp[k++]=a[i++];
        else
            temp[k++]=a[j++];
    }

    while(i<=mid)
        temp[k++]=a[i++];

    while(j<=last)
        temp[k++]=a[j++];
    //将排好序的写回去
    for(int i=0;i<k;i++)
        a[begin+i]=temp[i];
}
//递归的分割数组
void Divide(int a[],int begin,int last,int temp[])
{
    if(last>begin)
    {
        int mid=(begin+last)/2;
        Divide(a,begin,mid,temp);
        Divide(a,mid+1,last,temp);
        Merge(a,begin,last,mid,temp);
    }

}
int main()
{
    int a[6]={1,3,5,2,4,6};


    int*temp=new int[6];
    Divide(a,0,5,temp);
    for(int i=0;i<6;i++)
        cout<<a[i]<<"\t";
    cout<<endl;
    return 0;
}

总结
这里写图片描述
这里写图片描述

时间复杂度:快速排序最好是O(n*logn),堆排序和归并排序是O(n*logn),其余都是O(n^2)
稳定性:不稳定的有,快速排序,选择排序,堆排序,希尔排序。其余稳定

如何选择:当数据规模较小时,应该选择直接插入排序或冒泡排序。任何排序算法在数据量小时基本体现不出差距。快速排序对于已经基本有序的数据会浪费大量不必要的步骤。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值