常见排序算法

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

   按稳定性的不同分为:

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

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

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.基数排序








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值