常见排序算法

来源:互联网 发布:ubuntu root 登录 编辑:程序博客网 时间:2024/06/07 17:20

    复习了几种常见排序算法,整理起来以加强印象。

   按稳定性的不同分为:

   稳定排序:插入排序,冒泡排序,归并排序,基数排序。

   不稳定排序:选择排序,快速排序,希尔排序,堆排序。

1.冒泡排序

思想:每一轮排序都把最大或最小的元素往后调,如果有n个元素,则一共会进行n-1轮排序,每一轮排序会比较n-1-i次。

时间复杂度:O(N*N)

相等元素的前后顺序在排序过程中不会改变,是稳定排序

代码:

void bubblesort(int a[],int n){    int i,j;for(i=0;i<n-1;i++){for(j=0;j<n-1-i;j++){int temp;if(a[j]>a[j+1]){temp=a[j];a[j]=a[j+1];a[j+1]=temp;}}}}


2.选择排序

思想:第一轮排序在所有元素中选出最大或最小的元素放在第一位,第二轮排序在剩下的元素中选出最大或最小的元素放在第二位,以此类推。

时间复杂度:O(N*N)

相等元素的前后顺序在排序过程中会改变,是不稳定排序

代码:

void select_sort(int a[],int n){    int i,j;    int minindex;//这里是按升序排列    int temp;    for(i=0;i<n-1;i++)    {        minindex=i;        for(j=i+1;j<n;j++)        {            if(a[minindex]>a[j])            {                minindex=j;            }        }        temp=a[i];        a[i]=a[minindex];        a[minindex]=temp;    }}


3.插入排序

思想:假设第一位是排好序的,从第二位元素开始,第i轮排序把第i个元素插入到前i-1位已排好序的元素中(即从第i-1位元素开始往前比较, 直到找到自己的位置)。

时间复杂度:O(N*N)

相等元素的前后顺序在排序过程中不会改变,是稳定排序

代码:

void insert_sort(int a[],int n){    int key;    int i,j;    for(i=1;i<n;i++)    {        key = a[i]; //默认前i-1个元素是排好序的,现在要将a[i]插入前i-1个元素中,使前i个元素是排好序的        if(key<a[i-1])//如果第i个元素比第i-1个元素大,就肯定比i-1个元素之前的元素都大        {            for(j=i-1;j>=0&&a[j]>key;--j)            {                a[j+1]=a[j];//把大于key的元素都后移一位            }            a[j+1] = key;        }    }}


4.快速排序

思想:将第一个元素设置成枢纽元t,挖出来(挖坑法),此时第一个元素为空,设置i,j两个标记,i初始指向第一个元素,j初始指向最后一个元素。

当i<j时,反复以下循环过程:

1.将j向右移动,直到遇到一个小于t的数,将此数挖出并填到上一个坑(即挖第一个枢纽元t所产生的坑,i往右移一步),此时j指向的元素为空。

2.将i往左移,直到遇到一个大于t的数,将此数挖出并填到上一个坑(即上次挖坑后j指向的空位置,j往左移一步),此时i指向的元素为空。

当i等于j时,跳出循环,并把t填到i指向的坑中,这时,元素t的左边全都小于等于t,右边全都大于等于t。继续分别将左边和右边元素进行快排(分治思想)。

时间复杂度:最理想是O(N*logN),最差是O(N*N)

相等元素的前后顺序在排序过程中会改变(容易发生在枢纽元最后归位的时候),是不稳定排序

代码:

void quicksort(int a[],int r,int l){int i=r;int j=l;int temp = a[r];if(i<j){while(i<j){            while(a[j]>=temp&&i<j)j--;            if(i<j)a[i++]=a[j];            while(temp>=a[i]&&i<j)i++;            if(i<j)a[j--]=a[i];}a[i]=temp;quicksort(a,r,i-1);quicksort(a,i+1,l);}}
5.归并排序

思想:采用分治思想,首先递归将数组进行二分操作,分到不可再分的程度(即每一个分组都包含一个元素),然而将相邻的分组进行有序合并(递归回来),最后得到的数组即是排好序的。

时间复杂度:最理想、平均和最差都是是O(N*logN)

相等元素的前后顺序在排序过程中不会改变,是稳定排序

代码:

合并分组:

void merge(int a[],int first,int mid,int last,int temp[]){    int i=first,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 v=0;v<k;v++){        a[first+v] = temp[v];    }}
进行归并排序:

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); //右边有序        merge(a, first, mid, last, temp); //再将二个有序数列合并    }}

6.希尔排序

思想:属于插入排序的一种,又称缩小增量排序。对于一个长度为n的待排序数列,取一个小于n的整数gap(增量)将整个数列分为若干子数列,数列中所有距离为gap的数分在同一个子数列中。然后对每一个子数列进行插入排序,一趟排序过后,该子数列变成了有序的。逐步减少gap的值,直到gap=1时,即所有数字被放到一个子数列中进行插入排序,最后得到的数列是有序的。

时间复杂度:希尔排序的时间复杂度与选取的增量有关,若增量为1,希尔排序则退化成直接插入排序,时间复杂度为O(N*N),若采用Hibbard序列2^k-1,时间复杂度为O(N^1.5)。

相等元素的前后顺序在排序过程中会改变,是稳定排序

代码:

void shellsort(int a[], int n) //n为元素个数{    for(int gap=n/2;gap>0;gap/=2){for (int j = gap; j < n; 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;}}    }}

7.堆排序

思想:构造数组对应的最小堆或最大堆(从非叶子结点开始进行下滤操作),删除堆的第0个数据,用最后一个结点填充,再进行下滤操作。最后输出数组即是排好序的。小根堆为降序,大根堆为升序。

时间复杂度:由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN)。二次操作时间相加还是O(N * logN)。故堆排序的时间复杂度平均和最坏都为O(N*logN)。

相等元素的前后顺序在排序过程中会改变,是稳定排序

代码:

下滤操作:

void minHeapFixdown(int a[],int i,int n){    int temp = a[i];    int j = 2*i+1;    while(j<n){        if(j+1<n&&a[j+1]<a[j])j++;        if(a[j]>=temp)break;        a[i]=a[j];        i=j;        j=2*i+1;    }    a[i] = temp;}
构造小根堆:

void makeMinHeap(int a[],int n){    for(int i=n/2-1;i>=0;i--){        minHeapFixdown(a,i,n);    }}
实例:

int main(){    int a[10]={456,23,12,3,1,0,5,7,-1,324};    int i;    int n=10;    makeMinHeap(a,10);    for(i=n-1;i>=1;i--){        swapnum(a[0],a[i]);        minHeapFixdown(a,0,i);    }    for(int i=0;i<n;i++){        cout<<a[i]<<" "<<endl;    }    return 0;}
其中swapnum函数:

void swapnum(int &a,int &b){    a = a^b;    b = a^b;    a = a^b;}


8.基数排序








0 0