常用排序算法

一、冒泡排序

      冒泡排序是最简单的排序算法。假设数组一共有n个元素,元素最大下标为n-1,冒泡排序的具体做法是:第一趟在序列(A[0]~A[n-1])中从前往后进行两个相邻元素的比较,若前者大,则交换,比较 n-1 次;第一趟排序结束,最大元素被交换到A[n-1]中,下一趟排序只需要在子序列(A[0]~A[n-2])中进行;依次类推,冒泡排序最多进行 n-1 趟。基本的冒泡排序可以利用旗标的方式稍微减少一些比较的时间,当寻访完序列后都沒有发生任何的交换动作,表示排序已经完成,而无需再进行之后的比较与交换动作。 

     优点:稳定,比较次数已知;

     缺点:慢,每次只能移动相邻两个数据,移动数据的次数多。

     C语言实现:

#include<stdio.h>
void bubbleSort(int arr[],int n);
void swap(int *a,int *b);
#define N 10
int main(){
	int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
	bubbleSort(arr,N);
	printf("排序后:");
	for(i=0;i<N;i++){
	      printf("%d ",arr[i]);
	}
	printf("\n");
	return 0;
} 
/*冒泡排序*/
void bubbleSort(int arr[],int n){
	int i,j,flag=1;
	for(i=0;i<n-1&&flag;i++){  // 最多进行n-1趟,加flag可以提前结束比较过程 
		flag=0;
		for(j=0;j<n-1-i;j++){  //减去i,就是剩下的要比较的元素个数 
			if(arr[j]>arr[j+1]){
				swap(&arr[j],&arr[j+1]);
				flag=1;
			}
		}
	}
}
/*两数交换*/
void swap(int *a,int *b){
	int temp=*a;
	*a=*b;
	*b=temp;
}

       冒泡排序最好的情况下只需进行一趟排序,(n-1)次比较,此时的时间复杂度为O(n),无需移动元素;最坏的情况下进行 n-1 趟排序,时间复杂度为O(n2);冒泡排序是稳定的排序算法。

二、选择排序

     原理:将初始序列(A[0]~A[n-1])作为待排序序列,第一趟在待排序序列(A[0]~A[n-1])中找到最小值元素,将其与第一个元素A[0]交换,这样子序列(A[0])已经有序,下一趟在排序在待排序子序列(A[1]~A[n-1])中进行。第i趟排序在待排序子序列(A[i-1]~A[n-1])中找到最小值元素,与该子序列中第一个元素A[i-1]交换。经过 n-1 趟排序后使得初始序列有序。

     优点:比较次数与冒泡排序一样,数据移动次数比冒泡排序少;

     缺点:相对之下还是慢。

    C语言实现:

#include<stdio.h>
void selectSort(int arr[],int n);
void swap(int *a,int *b);
#define N 10
int main(){
	int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
	selectSort(arr,N);
	printf("排序后:");
	for(i=0;i<N;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");
	return 0;
} 
/*选择排序*/
void selectSort(int arr[],int n){
	int i,j;
	for(i=0;i<n-1;i++){  //最多进行n-1趟 
	    int m=i;
		for(j=i+1;j<n;j++){  //j要取到最后一个元素,即A[n-1] 
			if(arr[j]<arr[m])  m=j;   //m保存值最小的元素的下标 
		}
		if(i!=m)  swap(&arr[i],&arr[m]);
	}
}
/*两数交换*/
void swap(int *a,int *b){
	int temp=*a;
	*a=*b;
	*b=temp;
}

      选择排序的最好、最坏和平均情况的时间复杂度都为O(n2),而且它还需交换元素(n-1)次和移动元素3(n-1)次;它是不稳定的排序算法。

三、插入排序

  原理:将初始序列中的第一个元素作为一个有序序列,然后将剩下的 n-1 个元素按关键字大小依次插入该有序序列,每插入一个元素后依然保持该序列有序,经过 n-1 趟排序后使初始序列有序。
  优点:稳定,快;
  缺点:比较次数不一定,比较次数越少,插入点后的数据移动越多,特别是当数据总量庞大的时候,但用链表可以解决这个问题。

  C语言实现:

#include<stdio.h>
void insertSort(int arr[],int n);
#define N 10
int main(){
	int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
	insertSort(arr,N);
	printf("排序后:");
	for(i=0;i<N;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");
	return 0;
} 
/*插入排序*/
void insertSort(int arr[],int n){
	int i,j,temp,k;
	for(i=1;i<n;i++){   //从第2个(下标从0开始)元素开始 
		temp=arr[i];
		j=i-1;
		while(temp<arr[j]){    //找插入位置 
			arr[j+1]=arr[j];   //找到后元素往后移 
			j--;
			if(j==-1) break;    
		}
		arr[j+1]=temp;
	}
}

    其他说明:插入排序在最好的情况下时间复杂度为O(n),比较次数为(n-1)次,移动元素次数是2(n-1);最坏的情况下时间复杂度为O(n2);插入排序是稳定的排序算法。

四、快速排序

     原理:快速排序是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。

    优点:极快,数据移动少;

    缺点:不稳定。

    C语言实现:

#include<stdio.h>
void quickSort(int arr[],int left,int right);
#define N 10
int main(){
	int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
	quickSort(arr,0,N-1);
	printf("排序后:");
	for(i=0;i<N;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");
	return 0;
} 
/*快速排序*/
void quickSort(int arr[],int left,int right){
 	if(left < right){
        int key = arr[left];  // 刚开始以第一个数为标志数据
        int low = left;
        int high = right;
        while(low < high){
        	while(low < high && arr[high] > key){
         		high--;  //从后面开始找,找到不大于key值的数为止
            }
            arr[low] = arr[high];  // 将该数放到key值的左边
            while(low < high && arr[low] < key){ 
            	low++;  // 从前面开始找,找到不小于key值的数为止
            }
            arr[high] = arr[low]; // 将该数放到key值的右边
        }
        arr[low] = key;  // 把key值填充到low位置,下次重新找key值
        quickSort(arr,left,low-1); //将key值左边的数递归排序 
        quickSort(arr,low+1,right);   //将key值右边的数递归排序 
  	} 
}

      其它说明:在最好情况下,每次划分所取的基准都是当前无序区的"中值"记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:O(nlogn),尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的平均时间复杂度为O(nlogn)。快速排序是不稳定的排序算法。

五、归并排序

      原理:将n个待排序的记录看作由n个长度为1(即由一个记录构成)的子文件组成。每两个相邻的有序子文件分成一组,共有n/2组,对每一组进行排序,这样就可以得到长度为2的n/2个有序子文件。重复这个过程,即可得到长度为n的一个有序文件。

      优点:快速、稳定

      缺点:对数据的有序性不敏感,如果改造成索引操作,效果就非常好了

      C语言实现:

#include<stdio.h>
void mergeSort(int arr[], int left, int right);
void merge(int arr[], int left, int center, int right);
#define N 10
int main(){
	int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
	mergeSort(arr,0,N-1);
	printf("排序后:");
	for(i=0;i<N;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");
	return 0;
} 

/*归并排序*/
void mergeSort(int arr[], int left, int right){
	if (left >= right) return;
	// 找出中间索引
	int center = (left + right) / 2;
	// 对左边数组进行递归
	mergeSort(arr, left, center);
	// 对右边数组进行递归
	mergeSort(arr, center + 1, right);
	// 合并
	merge(arr, left, center, right);
}
 /**
 * 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序
 * @param arr 数组对象
 * @param left 左数组的第一个元素的索引
 * @param center 左数组的最后一个元素的索引,center+1是右数组第一个元素的索引
 * @param right 右数组最后一个元素的索引
 */
void merge(int arr[], int left, int center, int right){
    // 临时数组
    int tmpArr[N];
    // 右数组第一个元素索引
    int mid = center + 1;
    // third 记录临时数组的索引
    int third = left;
    // 缓存左数组第一个元素的索引
    int tmp = left;
    while (left <= center && mid <= right) {
        // 从两个数组中取出最小的放入临时数组
        if (arr[left] <= arr[mid]) {
            tmpArr[third++] = arr[left++];
        } else {
            tmpArr[third++] = arr[mid++];
        }
    }
    // 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
    while (mid <= right) {
        tmpArr[third++] = arr[mid++];
    }
    while (left <= center) {
        tmpArr[third++] = arr[left++];
    }
    // 将临时数组中的内容拷贝回原数组中
    // (原left-right范围的内容被复制回原数组)
    while (tmp <= right) {
        arr[tmp] = tmpArr[tmp++];
    }	
} 

其它说明:由于归并排序每趟归并都会使得有序子段的长度增长1倍,即是原有序子段的2倍,所以从长度为1的子段开始,需经过log2n次一趟归并才能产生长度为n的有序段。而每一趟归并至多进行n-1次比较。所以其时间复杂度为O(nlog2n)。最好情况、最坏情况、平均情况下,时间复杂度都是O(nlog2n)。归并排序是稳定的排序算法。归并排序需要和待排序文件容量相同的辅助存储空间。

六、希尔排序

     希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是不稳定排序算法。希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
  • 但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位

   原理:已知一组无序数据a[1]、a[2]、……a[n],需将其按升序排列。发现当n不大时,插入排序的效果很好。首先取一增量d(d<n),将a[1]、a[1+d]、a[1+2d]……列为第一组,a[2]、a[2+d]、a[2+2d]……列为第二组……,a[d]、a[2d]、a[3d]……列为最后一组依此类推,在各组内用插入排序,然后取d'<d,重复上述操作,直到d=1。

   优点:快,数据移动少;

   缺点:不稳定,d的取值是多少,应取多少个不同的值,都无法确切知道,只能凭经验来取。

   C语言实现:

#include<stdio.h>
void shellSort(int arr[],int n);
#define N 10
int main(){
	int i,arr[N]={95,27,90,49,80,58,6,9,18,50};
	shellSort(arr,N);
	printf("排序后:");
	for(i=0;i<N;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");
	return 0;
} 

/*希尔排序*/
void shellSort(int arr[],int n){
	int i, j, gap;
    int temp;
    for (gap=n/2;gap>0;gap/=2) {// 计算gap大小
        for (i=gap;i<n;i++) {// 将数据进行分组
            for (j=i-gap;j>=0&&arr[j]>arr[j+gap];j-=gap){//对每组数据进行插入排序
                temp=arr[j];
                arr[j]=arr[j+gap];
                arr[j+gap]=temp;
            }
        }
    }
}


各种排序算法比较:






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值