算法-排序

算法稳定性

什么是算法稳定性;假设Ki=Kj(1<=i<=n,1<=j<=n,i!=j),且在排序前的序列中Ri领先Rj(i<j)。

如果排序后Ri依然领先Rj,则称所用的排序方法是稳定的;反之不稳定;

如:排序前4,5,2, 1,2,3

排序后1,2,2,3,4,5其中第二个2 是之前第三个2那就是稳定的;

基础的三种排序

冒泡排序

public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int e = arr.length - 1; e > 0; e--) {
			for (int i = 0; i < e; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);//调换
				}
			}
		}
}

选择排序

每一轮选择最小的

public static void selectionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length - 1; i++) {
			int minIndex = I;
			for (int j = i + 1; j < arr.length; j++) { // i ~ N-1 上找最小值的下标 
				minIndex = arr[j] < arr[minIndex] ? j : minIndex;
			}
			swap(arr, i, minIndex);//调换
		}
}

插入排序

像插入扑克牌一样,从左到右排序

public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		// 不只1个数
		for (int i = 1; i < arr.length; i++) { // 0 ~ i 做到有序
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
				swap(arr, j, j + 1);
			}
		}
	}

以上三种常用时间复杂度都是O()

改进的排序

希尔排序

在插入排序基础上进行优化,将相距某个增量的记录组成一个子序列,这样能保证在子序列内分别进行直接插入排序后得到的结果基本有序,时间复杂度在O()

public static void shellSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int gap = arr.length / 2; gap >  0; gap /= 2) {
		    for (int i = gap; i < arr.length; i++) { // 0 ~ i 做到有序
			    for (int j = i - 1; j >= 0 && arr[j] > arr[j + gap]; j-= gap) {
				    swap(arr, j, j + gap);
			    }
		    }
        }
}

堆排序

堆的数据结构

堆通常是一个可以被看做一棵完全二叉树的数组对象。

堆满足下列性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值。
  • 堆总是一棵完全二叉树

假设当前元素的索引位置为 i,可以得到规律:

复制代码
parent(i) = i/2-1
left child(i) = 2*i+1
right child(i) = 2*i +2

堆中取出一个元素

private void heapify(int[] arr, int index, int heapSize) {
	int left = index * 2 + 1;
	while (left < heapSize) { // 如果有左孩子,有没有右孩子,可能有可能没有!
	    // 把较大孩子的下标,给largest
	    int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
        //孩子和当前结点比较
	    largest = arr[largest] > arr[index] ? largest : index;
	    if (largest == index) {
		    break;
        }
	    // index和较大孩子,要互换
	    swap(arr, largest, index);
	    index = largest;
	    left = index * 2 + 1;
	}
}

堆排序的实现

// 堆排序额外空间复杂度O(1)
	public static void heapSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		// O(N)
		for (int i = arr.length - 1; i >= 0; i--) {
			heapify(arr, i, arr.length);
		}
		int heapSize = arr.length;
		swap(arr, 0, --heapSize);
		// O(N*logN)
		while (heapSize > 0) { // O(N)
			heapify(arr, 0, heapSize); // O(logN)
			swap(arr, 0, --heapSize); // O(1)
		}
	}

合并排序

归并排序(Merge sort)是建立在归并操作上的一种有效、稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

// 递归方法实现
	public static void mergeSort1(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		process(arr, 0, arr.length - 1);
	}

	// 请把arr[L..R]排有序
	// l...r N
	// T(N) = 2 * T(N / 2) + O(N)
	// O(N * logN)
	public static void process(int[] arr, int L, int R) {
		if (L == R) { // base case
			return;
		}
		int mid = L + ((R - L) >> 1);
		process(arr, L, mid);
		process(arr, mid + 1, R);
		merge(arr, L, mid, R);
	}

	public static void merge(int[] arr, int L, int M, int R) {
		int[] help = new int[R - L + 1];
		int i = 0;
		int p1 = L;
		int p2 = M + 1;
		while (p1 <= M && p2 <= R) {
			help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
		}
		// 要么p1越界了,要么p2越界了
		while (p1 <= M) {
			help[i++] = arr[p1++];
		}
		while (p2 <= R) {
			help[i++] = arr[p2++];
		}
		for (i = 0; i < help.length; i++) {
			arr[L + i] = help[i];
		}
	}

快速排序

**随机化快速排序基本思想:**通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

	// 快排递归版本
	public static void quickSort1(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		process(arr, 0, arr.length - 1);
	}

	public static void process(int[] arr, int L, int R) {
		if (L >= R) {
			return;
		}
		swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
		int[] equalArea = netherlandsFlag(arr, L, R);
		process(arr, L, equalArea[0] - 1);
		process(arr, equalArea[1] + 1, R);
	}

汇总对比

|--------|----------------------------------------------------------------------------------------------------------------------------------------|-----|
| 排序方法 | 平均情况 | 稳定性 |
| 冒泡排序 | O() | 稳定 |
| 简单选择排序 | O() | 稳定 |
| 直接插入排序 | O() | 稳定 |
| 希尔排序 | O() | 不稳定 |
| 堆排序 | O(nlogn) | 不稳定 |
| 归并排序 | O(nlogn) | 稳定 |
| 快速排序 | O(nlogn) | 不稳定 |

相关推荐
何事驚慌几秒前
2024/10/5 数据结构打卡
java·数据结构·算法
结衣结衣.1 分钟前
C++ 类和对象的初步介绍
java·开发语言·数据结构·c++·笔记·学习·算法
TJKFYY3 分钟前
Java.数据结构.HashSet
java·开发语言·数据结构
kylinxjd5 分钟前
spring boot发送邮件
java·spring boot·后端·发送email邮件
OLDERHARD13 分钟前
Java - MyBatis(上)
java·oracle·mybatis
杨荧14 分钟前
【JAVA开源】基于Vue和SpringBoot的旅游管理系统
java·vue.js·spring boot·spring cloud·开源·旅游
大二转专业2 小时前
408算法题leetcode--第24天
考研·算法·leetcode
zaim12 小时前
计算机的错误计算(一百一十四)
java·c++·python·rust·go·c·多项式
凭栏落花侧2 小时前
决策树:简单易懂的预测模型
人工智能·算法·决策树·机器学习·信息可视化·数据挖掘·数据分析
hong_zc3 小时前
算法【Java】—— 二叉树的深搜
java·算法