排序算法

277 阅读3分钟

时间复杂度

(图源自微信公众:五分钟学算法)

关于稳定性

  1. 稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。

  2. 不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。

一、冒泡排序(Bubble Sort)

冒泡排序原理是从数组开头依次两两比较,遇到大的数在上边,就两两互换, 当“天平”移动到末端时,最小的数就“冒”到最上边,然后天平归位重新开始。

代码如下:

//Buble Sort by Java
for (int i = 0; i < arr.length - 1; i++) {
	for (int j = 0; j < arr.length - 1 - i; j++) {
		if (arr[j] < arr[j + 1]) {		//将小的数往右排
			int temp = arr[j];
			arr[j] = arr[j + 1];
			arr[j + 1] = temp;
		}
	}
}

貳、选择排序(Selection Sort)

遍历数组一直到倒数第二位,每次遍历时,从该位以后一直到末尾找符合条件的数, 并记录该数的下标,遍历结束后即可进行换位。

代码如下:

//Selection Sort by Java
for (int i = 0; i < arr.length - 1; i++) {
	int index = i;
	for (int j = i + 1; j < arr.length; j++) {
		if(arr[j] > arr[index]) {		//此处index不能用i替代
			index = j;
		}
	}
	if(index != i) {
		int temp = arr[index];
		arr[index] = arr[i];
		arr[i] = temp;
	}
}

叁、插入排序(Insert Sort)

将一个记录插入到已排好序的序列中,从而得到一个新的有序序列 (将序列的第一个数据看成是一个有序的子序列, 然后从第二个记录逐个向该有序的子序列进行有序的插入,直至整个序列有序)

插入排序

图片来源链接

代码如下:

//Insert Sort by Java
for (int i = 1; i < arr.length; i++) {
	// 待排元素小于有序序列的最后一个元素时,向前插入
	if (arr[i] < arr[i - 1]) {
		int temp = arr[i];
		for (int j = i; j >= 0; j--) {
			if (j > 0 && arr[j - 1] > temp) {
				arr[j] = arr[j - 1];
			} else {			//只剩下第一个位置或者找到要插入的位置
				arr[j] = temp;
				break;
			}
		}
	}
}

肆、希尔排序

希尔排序(Shell's Sort)在插入排序算法的基础上进行了改进,算法的时间复杂度与前面几种算法相比有较大的改进。其算法的基本思想是:先将待排记录序列分割成为若干子序列分别进行插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行一次直接插入排序。

图片来源链接

代码如下:

//Shell's Sort by Java
int increasement = arr.length;
	do {
		increasement = increasement / 3 + 1;		//这里+1,下面的for就不需要单独-1
		for (int i = 0; i < increasement; i++) {
			for (int j = i + increasement; j < arr.length; j += increasement) {
				if (arr[j] < arr[j - increasement]) {		//左边的比右边的大,交换位置
					int temp = arr[j], k;
					for (k = j - increasement; k >= 0 && temp < arr[k]; k -= increasement) {	//同插入排序
						arr[k + increasement] = arr[k];			//符合条件,往后挪,让前面位置空出来
					}
					arr[k + increasement] = temp;				//不符合条件,则在当前位置插入讨论的那个数
				}
			}
		}
	} while (increasement > 1);

伍、快速排序

实际上就是分治思想的冒泡,利用递归解决

代码如下:

// Quick Sort by Java
public static void QuickSort(int array[], int left, int right) {
	int i = left, j = right;
	int temp;
	int pivot; // 基准点,也就是数组的中点;

	pivot = array[(left + right) / 2];

	while (i <= j) {
		// 从左到右找到大于等于基准点的元素;
		while (array[i] < pivot) {
			i++;
		}
		// 从右到左找到小于等于基准点的元素;
		while (array[j] > pivot) {
			j--;
		}
		// 如果i <= j , 则互换;
		if (i <= j) {
			temp = array[i];
			array[i] = array[j];
			array[j] = temp;
			i++;
			j--;
		}
	}
	if (left < j) {
		QuickSort(array, left, j);
	}
	if (right > i) {
		QuickSort(array, i, right);
	}
}

陆、归并排序

“归并”的含义是将两个或两个以上的有序序列组合成一个新的有序表。假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到(表示不小于x的最小整数)个长度为2(或者是1)的有序子序列,再两两归并。如此重复,直到得到一个长度为n的有序序列为止。这种排序方法称为2-路归并排序。

归并排序
图片来源

代码如下:

public static void MergeSort(int[] arr, int start, int end, int[] temp)
{
	if (start >= end)
		return;
	int mid = (start + end) / 2;
	MergeSort(arr, start, mid, temp);
	MergeSort(arr, mid + 1, end, temp);
 
	// 合并两个有序序列
	int length = 0; // 表示辅助空间有多少个元素
	int i_start = start;
	int i_end = mid;
	int j_start = mid + 1;
	int j_end = end;
	while (i_start <= i_end && j_start <= j_end)
	{
		if (arr[i_start] < arr[j_start])
		{
			temp[length++] = arr[i_start++]; 
		}
		else
		{
			temp[length++] = arr[j_start++];
		}
	}
	while (i_start <= i_end)
	{
		temp[length++] = arr[i_start++];
	}
	while (j_start <= j_end)
	{
		temp[length++] = arr[j_start++];
	}
	// 把辅助空间的数据放到原空间
	for (int i = 0; i < length; i++)
	{
		arr[start + i] = temp[i];
	}
}