常用排序演算法
常見排序演算法:
注:快排的空間複雜度為log2N
1.選擇排序
//選擇排序
public void sort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int min = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
swap(arr, i, min);
}
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
2.氣泡排序
//氣泡排序
public void sort(int[] arr) {
for (int i = arr.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
3.插入排序
//插入排序
public void sort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0; j--) {
if (arr[j] < arr[j - 1]) {
swap(arr, j, j - 1);
}
}
}
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
樣本小且基本有序的時候效率比較高
4.希爾排序
//希爾排序
public void sort(int[] arr) {
//分組
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
//對每一組進行插入排序
for (int i = gap; i < arr.length; i++) {
for (int j = i; j >= gap; j -= gap) {
if (arr[j] < arr[j - gap]) {
swap(arr, j, j - gap);
}
}
}
}
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
圖解分析:
5.歸併排序
如果要對一個陣列進行排序,我們先把陣列從中間分成前後兩部分,然後對陣列的前後兩部分分別排序,再將排序好的兩部分合並在一起,這樣整個陣列就是有序的了
public void sort(int[] arr) {
int left = 0;
int right = arr.length - 1;
mergeSort(arr, left, right);
}
//分+和方法
private void mergeSort(int[] arr, int left, int right) {
if (left >= right) {
return;
}
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, right, mid);
}
//合併的方法
private void merge(int arr[], int left, int right, int mid) {
int i = left;
int j = mid + 1;
int k = 0;
int temp[] = new int[right - left + 1];
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= right) {
temp[k++] = arr[j++];
}
System.arraycopy(temp, 0, arr, left, temp.length);
}
5.1 歸併排序的迭代寫法
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
int n = arr.length;
int mergeSize = 1;
while (mergeSize < n) {
int l = 0;
while (l < n) {
int m = l + mergeSize - 1;
if (m >= n) {
break;
}
int r = Math.min(m + mergeSize, n - 1);
merge(arr, l, m, r);
l = r + 1;
}
mergeSize = mergeSize << 1;
}
}
private 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++];
}
while (p1 <= m) {
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
System.arraycopy(help, 0, arr, l, help.length);
}
5.2 求陣列中的逆序對個數---歸併排序擴充套件
public int reversePairs(int[] nums) {
if (nums == null || nums.length < 2) {
return 0;
}
int res = process(nums, 0, nums.length - 1);
return res;
}
private int process(int[] arr, int l, int r) {
if (l == r) {
return 0;
}
int mid = (l + r) / 2;
return process(arr, l, mid) + process(arr, mid + 1, r) + merge(arr, l, mid, r);
}
private int merge(int[] arr, int l, int mid, int r) {
int[] help = new int[r - l + 1];
int p1 = l, p2 = mid + 1, k = 0;
int res = 0;
while (p1 <= mid && p2 <= r) {
if (arr[p1] <= arr[p2]) {
help[k++] = arr[p1++];
} else {
res += mid - p1 + 1;
help[k++] = arr[p2++];
}
}
while (p1 <= mid) {
help[k++] = arr[p1++];
}
while (p2 <= r) {
help[k++] = arr[p2++];
}
System.arraycopy(help, 0, arr, l, help.length);
return res;
}
6.快排
public void sort(int[] arr, int left, int right) {
if (left >= right) {
return;
}
int mid = partition(arr, left, right);
sort(arr, left, mid - 1);
sort(arr, mid + 1, right);
}
private int partition(int[] arr, int leftBound, int rightBound) {
int pivot = arr[rightBound];
int left = leftBound;
int right = rightBound - 1;
while (left <= right) {
while (left <= right && arr[left] <= pivot) {
left++;
}
while (left <= right && arr[right] > pivot) {
right--;
}
if (left < right) {
swap(arr, left, right);
}
}
swap(arr, left, rightBound);
return left;
}
private void swap(int[] arr, int left, int right) {
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
7.計數排序
public int[] sort(int[] arr) {
int result[] = new int[arr.length];
int count[] = new int[10];
for (int i = 0; i < arr.length; i++) {
count[arr[i]]++;
}
int k = 0;
for (int i = 0; i < count.length; i++) {
while (count[i]-- > 0) {
result[k++] = i;
}
}
return result;
}
適用於量大但是範圍比較小的情況,如年齡排序,成績排序等,上面一種實現方式不穩定
public int[] sort(int[] arr) {
int[] result = new int[arr.length];
int[] count = new int[10];
for (int i = 0; i < arr.length; i++) {
count[arr[i]]++;
}
for (int i = 1; i < count.length; i++) {
count[i] = count[i] + count[i - 1];
}
for (int i = arr.length - 1; i >= 0; i--) {
result[--count[arr[i]]] = arr[i];
}
return result;
}
穩定的計數排序演算法
8.基數排序
本質上是一種多關鍵字排序,有低位優先和高位優先兩種
public int[] sort(int[] arr) {
int result[] = new int[arr.length];
int count[] = new int[10];
for (int i = 0; i < 3; i++) {
int div = (int) Math.pow(10, i);
for (int j = 0; j < arr.length; j++) {
int num = arr[j] / div % 10;
count[num]++;
}
for (int j = 1; j < count.length; j++) {
count[j] = count[j] + count[j - 1];
}
for (int j = arr.length - 1; j >= 0; j--) {
int num = arr[j] / div % 10;
result[--count[num]] = arr[j];
}
System.arraycopy(result, 0, arr, 0, arr.length);
Arrays.fill(count, 0);
}
return result;
}
物件排序 TimSort
//物件排序
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
//使用者如果請求用遺留的LegacyMergeSort,那就用遺留的
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
//否則用TimSort,TimSort是改進的MergeSort
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
T[] work, int workBase, int workLen) {
assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
//MIN_MERGE,分組的最小值
if (nRemaining < MIN_MERGE) { //private static final int MIN_MERGE = 32;
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
//二分插入,改進的插入排序
binarySort(a, lo, hi, lo + initRunLen, c);
return;
}
TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
int minRun = minRunLength(nRemaining);
do {
int runLen = countRunAndMakeAscending(a, lo, hi, c);
if (runLen < minRun) {
int force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLen, c);
runLen = force;
}
ts.pushRun(lo, runLen);
ts.mergeCollapse();
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}
流程
如果使用者使用者需要使用遺留的LegacyMergeSort,那就使用遺留的,否則使用TimSort
在使用TimSort時,如果整個資料量小於2,那麼直接返回
如果整個資料量小於MIN_MERGE(32),那麼使用二分插入排序進行排序
否則如果大於MIN_MERGE(32),使用TimSort,使用TimSort是把資料分成很多段資料,把其中的每一小段使用二分插入排序排好順序,然後把所有的段兩兩歸併
基本資料排序 雙軸快排
static void sort(int[] a, int left, int right,
int[] work, int workBase, int workLen) {
//如果陣列的長度小於286,就使用最原始的快排
if (right - left < QUICKSORT_THRESHOLD) { //private static final int QUICKSORT_THRESHOLD = 286;
sort(a, left, right, true);
return;
}
//判斷是否適合使用TimSort,如果拆分後陣列個數小於67
int[] run = new int[MAX_RUN_COUNT + 1]; //private static final int MAX_RUN_COUNT = 67;
int count = 0; run[0] = left;
//陣列不是高度結構化的,不適合用普通快排
if (++count == MAX_RUN_COUNT) {
sort(a, left, right, true);
return;
}
//普通的快排
private static void sort(int[] a, int left, int right, boolean leftmost) {
int length = right - left + 1;
//如果陣列長度小於47,則直接使用插入排序(插入排序為雙插入排序),否則用快排
if (length < INSERTION_SORT_THRESHOLD) { //private static final int INSERTION_SORT_THRESHOLD = 47;
if (leftmost) {
for (int i = left, j = i; i < right; j = ++i) {
int ai = a[i + 1];
while (ai < a[j]) {
a[j + 1] = a[j];
if (j-- == left) {
break;
}
}
a[j + 1] = ai;
}
} else {
do {
if (left >= right) {
return;
}
} while (a[++left] >= a[left - 1]);
//雙插入排序的實現
流程
雙軸快排就是我們找兩個數,把整個陣列分成三個區域
把第一個數小的放左邊,大於等於第一個數小於等於第二個數的放中間,大於第二個數的放右邊,前提是第一個數比第二個數小
先判斷陣列的長度是否小於286,如果是,就用原始的快排
判斷是否適合使用TimSort,判斷條件是開分後的陣列個數是否小於67
如果陣列不是高度結構化的,就用普通快排
在使用普通快排的時候,如果陣列長度小於47,則使用雙插入排序
使用一切都不滿足的情況下,使用雙軸快排
9. 堆結構和堆排序
import java.util.Arrays;
public class MyMaxHeap {
private int[] heap;
private final int limit;
private int heapSize;
public MyMaxHeap(int limit) {
this.limit = limit;
heap = new int[limit];
heapSize = 0;
}
public boolean isEmpty() {
return heapSize == 0;
}
public boolean isFull() {
return heapSize == limit;
}
public void push(int value) {
if (heapSize == limit) {
throw new RuntimeException("heap is full");
}
heap[heapSize] = value;
heapInsert(heap, heapSize++);
}
public int pop() {
int ans = heap[0];
swap(heap, 0, --heapSize);
heapify(heap, 0, heapSize);
return ans;
}
private void heapInsert(int[] arr, int index) {
//當arr[index]不比arr[index父]大了,或者index來到0位置了
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
//從index位置,往下看,不斷的下沉
private void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1;
while (left < heapSize) {
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
private void swap(int[] arr, int l, int r) {
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
}
//從小到大進行排序
public void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// O(N*log(N))
// for (int i = 0; i < arr.length; i++) {
// heapInsert(arr, i);
// }
// O(N)
for (int i = arr.length - 1; i >= 0; i--) {
heapify(arr, i, arr.length);
}
int size = arr.length;
swap(arr, 0, --size);
// O(N*logN)
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);
}
}
public static void main(String[] args) {
int arr[] = {3, 6, 4, 8, 4, 8, 4, 9, 4, 76, 2, 5, 1};
MyMaxHeap heap = new MyMaxHeap(arr.length);
for (int i = 0; i < arr.length; i++) {
heap.push(arr[i]);
}
for (int i = 0; i < arr.length; i++) {
int val = heap.pop();
System.out.print(val + " ");
}
System.out.println();
heap.heapSort(arr);
System.out.println(Arrays.toString(arr));
}
}
相關文章
- 常用排序演算法之桶排序排序演算法
- java常用排序演算法Java排序演算法
- 八種常用排序演算法排序演算法
- 常用演算法-選擇排序演算法排序
- 常用演算法-插入排序演算法排序
- 常用排序演算法總結排序演算法
- JavaScript實現常用排序演算法JavaScript排序演算法
- 幾種常用的排序演算法排序演算法
- python 常用的排序演算法Python排序演算法
- js中常用的演算法排序JS演算法排序
- 常用排序演算法總結(1)排序演算法
- 常用排序演算法總結(2)排序演算法
- C++常用排序演算法 (轉)C++排序演算法
- php常用的四種排序演算法PHP排序演算法
- 常用排序演算法之JavaScript實現排序演算法JavaScript
- 【演算法】Java實現七種常用排序演算法演算法Java排序
- 簡述幾種常用的排序演算法排序演算法
- 常用的比較排序演算法總結排序演算法
- 常用的 PHP 搜尋排序算演算法PHP排序演算法
- python實現常用五種排序演算法Python排序演算法
- 幾種常用的排序演算法之JavaScript實現排序演算法JavaScript
- 如何理解JavaScript中常用的4種排序演算法?JavaScript排序演算法
- 用 Java 實現的八種常用排序演算法Java排序演算法
- Java常用的7大排序演算法彙總Java排序演算法
- 常用的排序演算法和時間複雜度排序演算法時間複雜度
- 常用的排序演算法(五)--選擇排序以及最佳化(PHP實現)排序演算法PHP
- 視覺直觀感受7種常用的排序演算法視覺排序演算法
- 視覺直觀感受 7 種常用的排序演算法視覺排序演算法
- 【JAVA演算法】排序演算法 -- 快速排序Java演算法排序
- 排序演算法__桶排序排序演算法
- 排序演算法__快速排序排序演算法
- 排序演算法__希爾排序排序演算法
- 排序演算法__堆排序排序演算法
- 排序演算法:快速排序排序演算法
- 【排序演算法】- 希爾排序排序演算法
- 排序演算法-堆排序排序演算法
- 排序演算法-快速排序排序演算法
- 排序演算法 - 堆排序排序演算法