Files
2021-Postgraduate-408/Data-Structure/Sort
2018-11-19 09:46:21 +08:00
..
2018-11-18 20:42:16 +08:00
2018-11-19 09:46:21 +08:00

排序

插入排序

基本思想:每一趟将一个待排序的记录,按其关键字的大小插入到已排好序的一组记录的适当位置上,直到所有待排序记录全部插入为止。

直接插入排序(straight Insertion sort)

核心:通过构建有序序列,对于未排序序列,在已排序序列中从后向前扫描(对于单向链表则只能从前往后遍历),找到相应位置并插入。

  • 从第一个元素开始,该元素可认为已排序
  • 取下一个元素,对已排序数组从后往前扫描
  • 若从排序数组中取出的元素大于新元素,则移至下一位置
  • 重复步骤3直至找到已排序元素小于或等于新元素的位置
  • 插入新元素至该位置
  • 重复2~5

insertion_sort.gif

代码实现

#include <iostream>
using namespace std;
int main(){
	int a[] = {-1, 6, 5, 2, 8, 4, 1, 3, 7}; //数组从第二位开始,第一位[0]为暂存单元
	int len = sizeof(a) / sizeof(a[0]);
	for(int i = 2; i < len; i++){
		if(a[i] < a[i - 1]){
			a[0] = a[i];
			a[i] = a[i - 1];
			int j = i - 2;
			while(a[0] < a[j]){
				a[j + 1] = a[j];
				j--;
			}
			a[j + 1 ] = a[0];
		}
	}
	for(int i = 1; i < len; i++)
		cout << a[i];
	return 0;
}

算法分析

  • 空间复杂度:需要一个记录的辅助空间[0],空间复杂度为 O(1).
  • 时间复杂度:一共操作了 n-1 趟每趟都分为比较和移动两部分。最好情况只比较不移动O(n);最坏情况,比较 n^2/2 次,移动 n^2/2 次, 时间复杂度为O(n^2).

算法特点

  • 稳定排序。
  • 也适合于链式存储,只需修改相应的指针,无需移动。
  • 适合于初始记录基本有序(正序)的情况当初始记录无序n较大时此算法时间复杂度较高不宜使用。

折半插入排序(Binary Insertion Sort)

直接插入排序每一趟都需要在排好序的部分中从最后一位开始比较。折半插入排序减少了比较的次数,但移动次数没有改变。

代码实现

#include <iostream>
using namespace std;
int main(){
	int a[] = {-1, 6, 5, 2, 8, 4, 1, 3, 7}; //数组从第二位开始, 第一位[0]为暂存单元
	int len = sizeof(a) / sizeof(a[0]);
	for(int i = 2; i < len; i++){
		a[0] = a[i];
		int low = 1, high = i - 1;
		while(low <= high){
			int m = (low + high) / 2;
			if(a[0] < a[m]) high = m - 1;
			else low = m + 1;
		}
		for(int j = i - 1; j >= high + 1; j--)
			a[j + 1] = a[j];
		a[high + 1] = a[0];
	}
	for(int i = 1; i < len; i++)
		cout << a[i];
	return 0;
}

算法分析

  • 时间复杂度:移动次数没有变,所以时间复杂度还是 O(n^2).
  • 空间复杂度O(1).

算法特点

  • 稳定排序。
  • 只能用于顺序结构,不能用于链式结构。
  • 适合初始记录无序、n较大时的情况。

希尔排序(Shell's Sort)

直接插入排序,当待排序的记录个数较少待排序序列的关键字基本有序时,效率较高。希尔排序针对以上两个方面进行了改进。 希尔排序实现上是将待排序序列分成几组分别进行插入排序,最后再合成一组。

基本思想: 先取一个小于n的整数d1作为第一个增量把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序然后取第二个增量d2<d1重复上述的分组和排序直至所取的增量 =1即所有记录放在同一组中进行直接插入排序为止。 一般的初次取序列的一半为增量以后每次减半直到增量为1。

代码实现

#include <iostream>
using namespace std;
int main(){
	int a[] = {6, 5, 2, 8, 4, 1, 3, 7};
	int len = sizeof(a) / sizeof(a[0]);
	int step = len / 2;  //初次增量为len/2
	while(step > 0){
		for(int i = step; i < len; i += step){
			while(i >= step && a[i - step] > a[i]){
				int temp = a[i - step];
				a[i - step] = a[i];
				a[i] = temp;
				i -= step;
			}
		}
		step = step / 2;
	}
	for(int i = 0; i < len; i++)
		cout << a[i];
	return 0;
}

算法分析:

  • 时间复杂度:最坏情况 O(n^2)
  • 空间复杂度只需要一个辅助空间O(1).

算法特点:

  • 不稳定排序。 *只能用于顺序结构。
  • 记录总的比较次数和移动次数都比直接插入排序要少n越大时效果越明显。适合初始记录无序、较大时的情况。

交换排序

交换排序的基本思想是:两两比较待排序记录的关键字,一旦发现两个记录不满足次序要求时则进行交换,直到整个序列全部满足要求为止。

冒泡排序(Bubble Sort)

核心:冒泡,持续比较相邻元素,大的挪到后面,因此大的会逐步往后挪,故称之为冒泡。
每一次循环将未排序数组中最大的移到最后,所以下一次循环可以只比较到前一位

BubbleSort

算法实现

#include <iostream>
using namespace std;
int main(){
	int a[] = {6, 5, 2, 8, 4, 1, 3, 7};
	int len = sizeof(a) / sizeof(a[0]);
	int m = len - 1, flag = 1;
	while((m > 0) && (flag == 1)){
		flag = 0;
		for(int i = 1; i <= m; i++){
			if(a[i - 1] > a[i]){
				flag = 1;
				int temp = a[i - 1];
				a[i - 1] = a[i];
				a[i] = temp;
			}
		}
		m--;
	}
	for(int i = 0; i < len; i++)
		cout << a[i];
	return 0;
}

算法分析

  • 时间复杂度: 最好情况下只进行 n-1 次比较; 最坏情况下,进行 n(n-1)/2 次比较,移动 3n(n-1)/2次。时间复杂度为 O(n^2).
  • 空间复杂度: 只需要一个暂存空间, O(1).

算法特点

  • 稳定排序
  • 可用于链式存储
  • 移动次数较多算法平均时间性能比直接插入排序差。当初始记录无序n较大时不宜采用.

快速排序(Quick Sort)

quickSort.gif