排序算法大集合:插入类(直接插入、折半插入、希尔)、交换类(冒泡、快排)、选择类。反正你想要的我都要。

排序总结

      感觉还是要先总结一下排序算法的时间复杂度,空间复杂度,稳定性以及一些需要知道的小细节。

  • 时间复杂度
          平均情况下,快速排序、希尔排序、归并排序和堆排序的时间复杂度都是O(nlogn),其他都是O(n^2)。一个特殊的是基数排序,时间复杂度是O(d(n+r));
          最坏的情况下,快速排序的时间复杂度是O(n^2)
          当然还有,直接插入,冒泡排序有可能是O(n)
  • 空间复杂度
          快速排序是O(logn),归并排序是O(n),基数排序是O®,其他都是O(1)
  • 稳定性
          快速排序、希尔排序、简单选择排序、堆排序是不稳定的,其他都是稳定的。
  • 一些小细节
          1、经过一次排序能够到达最终位置的是交换类(冒牌、快速)和选择类(简单选择、堆)排序
          2、排序算法关键字比较次数与原始序列无关:简单选择排序和折半插入排序。
          3、排序算法的排序趟数与原始序列有关的是:交换类排序

插入类排序

1、直接插入排序

      直接插入排序,是先把原始序列的第一个元素看作一个有序序列,然后依次将剩下的元素插入到这个有序序列中。下面看一个例子:
      原始序列:45 35 65 95 75 15 25 45
      1)一开始只看45,它自己肯定是有序的
      45       35 65 95 75 15 25 45
      2)插入35。35<45,所以45后移一个位置,35插入到原来45的位置
      35 45      65 95 75 15 25 45
      3)插入65。65>45,所以不需要移动,直接将65插入到45后
      35 45 65       95 75 15 25 45
      4)插入95。95>65
      35 45 65 95      75 15 25 45
      5)插入75。75<95,所以95后移一个位置;继续比较,75>65所以75就直接插入到65后
      35 45 65 75 95      15 25 45
      6)插入15。15<95,所以95后移一个位置;继续比较,15<75所以75后移一个位置;继续比较,15<65、、、、经过比较15应该插入最前面
      15 35 45 65 75 95      25 45
      7)插入25。25<95,所以95后移一个位置;继续比较,25<75所以75后移一个位置;继续比较,25<65、、、、经过比较25应该插入15之后
      15 25 35 45 65 75 95      45
      8)插入45。经过比较45 = 45<65应该插入45之后,排序完成
      15 25 35 45 45 65 75 95

直接插入排序代码
void InsertSort(int arr[],int n){
	int i,j;
	int temp;
	for(i=1;i<n;i++){
		temp = arr[i];
		j = i - 1 ;
		while(j>=0&&temp<arr[j]){
			arr[j+1] = arr[j];
			--j;
		}
		arr[j+1] = temp;
	}
}

2、折半插入排序

      折半插入排序和直接插入排序的思想一样,区别就是在插入的时候,通过折半查找的方法寻找插入位置

3、希尔排序

      希尔排序,就是将待排序序列分成几个子序列,然后将子序列进行直接插入排序。就比如说:
      原始序列:45 35 65 95 75 15 25 45 55 05
      1)以增量5分割序列,得到以下几个子序列:
      序列1:45                            15
      序列2:      35                            25
      序列3:            65                            45
      序列4:                  95                            55
      序列5:                        75                            05
      直接对这5个子序列进行直接插入排序,一趟希尔排序的结果为:15 25 45 55 05 45 35 65 95 75
      2)以增量2分割序列:
      序列1:15                55               35               75
      序列2:      25                05               35               65
      序列3:            45                45               35               95
      又一趟希尔排序,结果为:15 05 45 35 25 45 55 65 85 75
      3)最后以1增量分割,也就是直接进行直接插入排序,结果为:05 15 25 35 45 45 55 65 75 95

      希尔排序增量的选择一般有n/2、n/4、、、、、2、1,即每一次将增量除以2并向下取整

交换类排序

1、冒泡排序

      冒泡排序是通过一系列的“交换”动作完成的。首先第一个关键字和第二个关键字比较,如果第一个大,则两者交换,否则不交换;然后第二个关键字与第三个关键字比较,如果第二个关键字大,则交换两者,否则不交换·······一直按这种方式进行下去,最终最大的那个元素被交换到了最后的位置(这是升序,降序的话则将小的往后换),这样就完成了一趟冒泡排序。经过多趟这样的排序,最终使得整个序列有序。比如说:
      原始序列:45 35 65 95 75 15 25 45
      下面进行第一趟冒泡排序
      1)1号与2号比较,35<45,交换。
      结果:35 45 65 95 75 15 25 45
      2)2号与3号比较,65>45,不交换。
      结果:35 45 55 95 75 15 25 45
      3)3号与4号比较,95>65,不交换。
      结果:35 45 65 95 75 15 25 45
      4)4号与5号比较,75<95,交换。
      结果:35 45 65 75 95 15 25 45
      5)5号与6号比较,15<95,交换。
      结果:35 45 65 75 15 95 25 45
      6)6号与7号比较,25<95,交换。
      结果:35 45 65 75 15 25 95 45
      7)7号与8号比较,45<95,交换。
      结果:35 45 65 75 15 25 45 95
      最终,第一趟冒泡排序结束,最大的95被交换到了最后的位置,到达了它最终的位置,接下来将第一趟排序的结果进行第二趟冒泡排序。经过若干趟排序后,最终序列有序。冒泡排序的结束条件是在一趟排序过程中没有发生关键字交换。

//冒泡排序代码
void BubbleSort(int Arr[] , int n ){
	int i , j , flag;
	int temp;
	for(i = n -1 ;i >= 1;--i){
		flag = 0;
		for(j = 1;j <= i;++j){
			if(Arr[j-1] >Arr[j]){
				temp = Arr[j];
				Arr[j] = Arr[j-1];
				Arr[j-1] = temp;
				flag = 1;
			}
		}
		if(flag == 0)  
			return;
	}
}

2、快速排序

      快速排序通过多次划分操作实现排序。其操作流程可以概括为:每一趟排序选择当前子序列中的一个关键字作为枢轴,将子序列中比枢轴小的移到枢轴前面,大的移到后面;当本躺所有子序列都被枢轴按照上述规则划分完毕后会得到新的一组更短的子序列,它们成为下一趟划分的初始序列集。

//快速排序的算法如下:
void QuickSort(int R[],int low ,int higt){
	int temp;
	int i = low , j = high;
	if(low<high){
		temp = R[low];
		while(i<j){
			while(j>i&&R[j]>=temp) j--;//从右往左扫描,找到一个小于temp的
			if(i<j){				   //关键字放到左边
				R[i] = R[j];
				i++;	
			}
			while(j>i&&R[i]<temp) i++;//从左往右扫描,找到一个大于temp的
			if(i<j){				  //关键字放到右边
				R[j] = R[[i];
				j--;
			}
		}
		R[i] = temp;
		QuickSort(R,low,i-1);
		QuickSort(R,i+1,high);
	}
}

选择类排序

1、简单选择排序

      选择类排序主要的动作是“选择”,简单选择排序采用最简单的选择方式,从头至尾顺序扫描序列,找出最小的一个关键组,和第一个关键字交换,接着从剩下的关键字中继续这种选择和交换,最终使得序列有序。

//简单选择排序算法
void selectSort(int R[],int n){
	int i,j,k,temp;
	for(i=0;i<n;i++){
		k = i;
		for(j = i+1;j<n;j++)
			if(R[k]>R[j])
				k = j;
		temp = R[i];
		R[i] = R[k];
		R[k] = temp;
	}
}

2、堆排序

      堆是一种数据结构,可以把堆看作一棵完全二叉树,这棵树满足:任何一个非叶节点的值都不大于(或不小于)其左右孩子结点的值。若父亲大,孩子小,则是大顶堆,反之是小顶堆
      根据堆的性质,可以知道根节点是最大或最小值,因此可以将一个无序序列转换为一个堆,就可以找到这个序列的最大或最小值,然后将这个最值交换到序列的最前或最后,这样有序序列的关键字增加了一个,无序序列的关键字减少了一个,对新的无序序列继续这种操作,就实现了排序。
      执行过程:
      原始序列:49 38 65 97 76 13 27 49
      (1)建堆
      先将这个序列调整为一个大顶堆。原始序列对应的完全二叉树如下图所示:
在这里插入图片描述
      1)调整97。97>49,所以97和它的孩子满足堆的定义,不需要调整。
      2)调整65。65>13,65>27,所以65和它的孩子满足堆的定义,不需要调整。
      3)调整38。38<97,38<76,所以38和它的孩子不满足堆的定义,需要调整。在这里,38比它的两个孩子的值都小,所以需要和两者中较大的值交换,即和97交换。交换后,38<49,不满足堆的定义,继续调整,将38和49交换,结果如下图所示:
在这里插入图片描述
      4)调整49。49<97,49<65,所以49和它的孩子不满足堆的定义,需要调整。找到较大的孩子97,将49和97交换。交换后49<76不满足堆的定义,继续调整,将49和76交换,结果如下图所示:
在这里插入图片描述
      (2)插入结点
      需要在插入结点后仍保持堆的性质,因此需要将要插入的结点X放在最底层的最右边,插入后满足完全二叉树的特点;然后将X依次向上调整到合适的位置以满足父大子小的性质。
      (3)删除结点
      当删除堆中的一个结点时,可以将最底层最右边的结点的值赋给要删除的结点,然后调整到合适的位置,并将最底层最右边的叶子结点删除。
      (4)排序
      可以看到,现在已经建好了一个大顶堆,对应的序列为:97 76 65 49 49 13 27 38,将38和97交换,第一趟排序完成,97到达最终的位置,将除了97以外的38 76 65 49 49 13 27重新调整为大顶堆,直到树中只剩下一个结点时排序完成。

//堆排序算法代码如下
void sift(int R[],int low,int high)
{
	int i = low,j=2*i; ///R[j]是R[i]的左孩子结点
	int temp = R[i];
	while(j <= high){
		if(j < high && R[j]<R[j+1]){ //若右孩子较大,则将j指向右孩子
			j++;
		}
		if(temp < R[j]){
			R[i] = R[j];  //调整R[j]到双亲结点的位置
			i = j;
			j = 2 * i;
		}else{
			break;
		}
	}
	R[i] = temp; //将调整的结点的值放入最终的位置
}

void heapSort(int R[],int n)
{
	int i;
	int temp;
	for(i = n/2;i>=1;i--){
		sift(R,i,n); //建立初始堆
	}
	for(i = n;i>=2;i--){
		//将根结点中的关键字交换到最后的位置
		temp = R[1];
		R[1] = R[i];
		R[i] = temp;
		sift(R,1,i-1); //将剩余的无序序列进行调整
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值