一、堆排序
import java.util.*;
public class Main {
public static void main(String[] args) {
int[] arr = {27,15,19,18,28,34,65,49,25,37};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void createBigHeap(int[] arr) {
for (int i = (arr.length-1-1)/2; i >= 0 ; i--) {
adjustDown(arr,i,arr.length);
}
}
public static void adjustDown(int[] arr,int parent,int len) {
int child = 2 * parent + 1;
if (child + 1 < len && arr[child] < arr[child + 1]) {
child++;
}
while (child < len) {
if (arr[child] > arr[parent]) {
int tmp = arr[child];
arr[child] = arr[parent];
arr[parent] = tmp;
parent = child;
child = 2 * parent + 1;
} else {
break;
}
}
}
//时间复杂度(不管最好还是最坏):O(n * log2(n))
//空间复杂度:O(1)
public static void heapSort(int[] arr) {
createBigHeap(arr);
int end = arr.length - 1;
while (end > 0) {
int tmp = arr[0];
arr[0] = arr[end];
arr[end] = tmp;
adjustDown(arr,0,end);
end--;
}
}
}
二、插入排序
1.直接插入排序
/**
* 时间复杂度:最坏情况下:o(n^2)
* 最好情况下:o(n)
* 结论:越有序越快
* (1)会用在大部分有序的情况下 (2)也会用在一些排序的优化上(快速排序)
* 空间复杂度:o(1)
* 稳定性:稳定的排序
* @param arr
*/
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int tmp = arr[i];
int j = i - 1;
for (; j >= 0 ; j--) {
if (tmp < arr[j]) {
arr[j+1] = arr[j];
} else {
break;
}
}
arr[j+1] = tmp;
}
}
2.希尔排序
/**
* 希尔
* 时间复杂度:最好:o(n) 最坏:o(n^2)
* 空间复杂度:o(1)
* 稳定性:不稳定
* @param arr
*/
public static void shellSort(int[] arr) {
int gap = arr.length;
while (gap > 1) {
shell(arr,gap);
gap = gap / 2;
}
shell(arr,1);
}
public static void shell(int[] arr,int gap) {
for (int i = gap; i < arr.length; i++) {
int tmp = arr[i];
int j = i-gap;
for (; j >= 0; j-=gap) {
if (tmp < arr[j]) {
arr[j+gap] = arr[j];
} else {
break;
}
}
arr[j+gap] = tmp;
}
}
三、选择排序
1.直接选择排序
/**
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 稳定性:不稳定
* @param arr
*/
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length-1; i++) {
for (int j = i+1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
}
2.双向选择排序(了解)
四、冒泡排序
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length-1; i++) {
boolean isSorted = true;//判断数列是否有序
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j+1]) {
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
isSorted = false;
}
}
if (isSorted) {
break;
}
}
}
五、快速排序(重要)
/**
分治思想:什么时候效率最高,可以把待排序序列均匀的划分
* 时间复杂度:最好情况:O(nlog2(n)) 最坏情况(有序序列):O(n^2)
* 空间复杂度: 最好情况:O(log2(n)) 最坏情况:O(n)
* 稳定性:不稳定
* @param arr
*/
public static void quickSort(int[] arr) {
quick(arr,0,arr.length-1);
}
public static void quick(int[] arr,int low,int end) {
if (low < end) {
int pivot = pivot(arr,low,end);
quick(arr,low,pivot-1);
quick(arr,pivot+1,end);
}
}
public static int pivot(int[] arr,int start,int end) {
int tmp = arr[start];
while (start < end) {
while (arr[end] >= tmp && end > start) {
end--;
}
arr[start] = arr[end];
while (tmp >= arr[start] && start < end) {
start++;
}
arr[end] = arr[start];
}
arr[start] = tmp;//start == end
return start;
}
非递归:
public static void quickSort(int[] arr) {
Stack<Integer> stack = new Stack<>();
int low = 0;
int high = arr.length-1;
int piv = pivot(arr,low,high);
if (piv > low+1) {//左侧超过两个数据
stack.push(low);
stack.push(piv-1);
}
if (piv < high-1) {//右侧超过两个数据
stack.push(piv+1);
stack.push(high);
}
while (!stack.empty()) {
high = stack.pop();
low = stack.pop();
piv = pivot(arr,low,high);
if (piv > low+1) {
stack.push(low);
stack.push(piv-1);
}
if (piv < high-1) {
stack.push(piv+1);
stack.push(high);
}
}
}
优化算法:三数取中法
public static void medianOfThree(int[] arr,int low,int high) {
int mid = (low + high) / 2;
if (arr[mid] > arr[low]) {
swap(arr,low,mid);
}
if (arr[mid] > arr[high]) {
swap(arr,mid,high);
}
if (arr[low] > arr[high]) {
swap(arr,low,high);
}
}
public static void quick(int[] arr,int low,int end) {
if (low < end) {
medianOfThree(arr,low,end);
int pivot = pivot(arr,low,end);
quick(arr,low,pivot-1);
quick(arr,pivot+1,end);
}
}
六、归并排序(重要)
/**
* 时间复杂度:O(n*log2(n))
* 空间复杂度:O(n)
* @param arr
*/
public static void mergeSort(int[] arr) {
mergeSortInternal(arr,0,arr.length-1);
}
public static void mergeSortInternal(int[] arr,int low,int high) {
if (low >= high) return;
int mid = (low + high) / 2;
mergeSortInternal(arr,low,mid);
mergeSortInternal(arr,mid+1,high);
merge(arr,low,mid,high);
}
public static void merge(int[] arr,int low,int mid,int high) {
int s1 = low;
int s2 = mid+1;
int[] tmp = new int[high-low+1];
int k = 0;//tmp的下标
while (s1 <= mid && s2 <= high) {
if (arr[s1] <= arr[s2]) {
tmp[k++] = arr[s1++];
} else {
tmp[k++] = arr[s2++];
}
}
//结束循环后可能s1还有值
while (s1 <= mid) {
tmp[k++] = arr[s1++];
}
//结束循环后可能s2还有值
while (s2 <= high) {
tmp[k++] = arr[s2++];
}
for (int i = 0; i < tmp.length; i++) {
arr[i+low] = tmp[i];
}
}
海量数据排序问题
外部排序:排序过程需要在磁盘等外部存储进行的排序
前提:内存只有 1G,需要排序的数据有 100G
因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序
- 先把文件切分成 200 份,每个 512 M
- 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以
- 进行 200 路归并,同时对 200 份有序文件做归并过程,最终结果就有序了