基本概念介紹
- 穩定排序
對於待排序的數據,如果有元素a和b相等,如果排序前a在b的前面,在排序後a還是在b的前面,則稱之爲穩定排序。 - 非穩定排序
對於待排序的數據,如果有元素a和b相等,如果排序前a在b的前面,在排序後a可能出現在b的後面,則稱之爲非穩定排序。 - 內排序
待排文件的數據都是在內存儲器中進行的。 - 外排序
如果數據很大,內存無法直接加載完成的數據,排序過程需要藉助於內外存數據的交換來完成,則屬於外排序
插入排序
直接插入排序
public class DirectInsertSorting {
public static void main(String[] args) {
int[] arr = new int[] {2,2,1,5,9,10,3,15,24};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
if(arr == null || arr.length <= 1) {
return;
}
int tmp;
int j;
for(int i = 1; i < arr.length; i++) {
j = i - 1;
tmp = arr[i];
while (j >= 0 && tmp < arr[j]) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = tmp;
}
}
}
折半插入排序
public class BinaryInsertSorting {
public static void main(String[] args) {
int[] arr = new int[] {3,1,3,5,7,8,1,2};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
if(arr == null || arr.length <= 1) {
return;
}
int lo, hi, mid, j;
for(int i = 1; i < arr.length; i++) {
lo = 0;
hi = i - 1;
int tmp = arr[i];
while(lo <= hi) {
mid = (lo + hi) >> 1;
if(tmp < arr[mid]) {
hi = mid - 1;
}else {
lo = mid + 1;
}
}
for(j = i - 1; j >= lo; j--) {
arr[j + 1] = arr[j];
}
arr[lo] = tmp;
}
}
}
折半插入排序主要是優化了查找應該插入元素的位置,但是移動元素的時間複雜度沒有變化。
鏈表插入排序
/**
* 鏈表插入排序就是改變指針的過程中容易出錯
*/
public class LinkedInsertSorting {
public static void main(String[] args) {
Node head = new Node(-1, null);
Node node1 = new Node(3, null);
Node node2 = new Node(5, null);
Node node3 = new Node(2, null);
Node node4 = new Node(4, null);
head.next = node1;
node1.next = node2;
node2.next = node3;
node3.next = node4;
sort(head);
Node p = head.next;
while(p != null) {
System.out.println(p.val);
p = p.next;
}
}
public static void sort(Node head) {
if (head == null || head.next == null) {
return;
}
Node p, q;
p = head.next;
q = head;
q.next = null;
while(p != null) {
while(q.next != null && p.val > q.next.val) {
q = q.next;
}
Node tmp = p.next;
p.next = q.next;
q.next = p;
p = tmp;
q = head;
}
}
private static class Node {
private Node(int val, Node next) {
this.val = val;
this.next = next;
}
int val;
Node next;
}
}
鏈表插入排序主要是優化了移動元素的時間複雜度,但是查找元素的時間複雜度並沒有變化。
Shell排序
public class ShellInsertSorting {
public static void main(String[] args) {
int[] arr = new int[] {3,1,2,5,9,1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
if(arr == null || arr.length <= 1){
return;
}
int d;
d = arr.length / 2;
int len = arr.length;
int i, j;
while(d >= 1) {
for(i = d; i < len; i++) {
int tmp = arr[i];
j = i - d;
while(j >= 0 && arr[j] > tmp) {
arr[j + d] = arr[j];
j -= d;
}
arr[j + d] = tmp;
}
d /= 2;
}
}
}
shell排序稍微有點意思,通過跳躍性地對一些元素先進行排序,是時間複雜度降低,可以達到O(n^1.5)。
交換排序
最簡單的交換排序是冒泡排序,優化的交換排序就是快排了。
冒泡排序
/**
* 冒泡排序
*/
public class BubSort {
public static void main(String[] args) {
int[] arr = new int[] {1,3,5,8,3,2,1,10,7};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
for(int i = 0; i < arr.length - 1; i++) {
boolean isChange = false;
for(int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
isChange = true;
}
}
if(!isChange) {
break;
}
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
這個沒啥好說的,符合人的直觀思維,很好理解。
快排
/**
* 快排
*/
public class quickSort {
public static void main(String[] args) {
int[] arr = new int[] {3,2,1,5,9,10,3};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
sort(arr, 0, arr.length - 1);
}
private static void sort(int[] arr, int lo, int hi) {
if(lo >= hi) {
return;
}
int middle = getMiddle(arr, lo, hi);
sort(arr, lo, middle - 1);
sort(arr, middle + 1, hi);
}
private static int getMiddle(int[] arr, int lo, int hi) {
if(lo >= hi) {
return lo;
}
int val = arr[lo];
while(lo < hi) {
while(lo < hi && arr[hi] >= val) hi--;
if(lo < hi) {
arr[lo] = arr[hi];
}
while(lo < hi && arr[lo] <= val) lo++;
if(lo < hi) {
arr[hi] = arr[lo];
}
}
arr[lo] = val;
return lo;
}
}
快排的核心是如何能夠確定一個元素的位置,因爲涉及到兩個指針的移動,所以
有時容易出錯。
選擇排序
主要的就是直接選擇排序和堆選擇排序。
直接選擇排序
/**
* 直接選擇排序
*/
public class DirectSelectSorting {
public static void main(String[] args) {
int[] arr = new int[] {1,3,5,2,1,4,2,9,7};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
int len = arr.length;
int i, j, k;
for(i = 0; i < len - 1; i++) {
k = i;
for(j = i + 1; j < len; j++) {
if(arr[j] < arr[k]) {
k = j;
}
}
if(i != k) {
swap(arr, i, k);
}
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
堆選擇排序
/**
* 堆選擇排序
*/
public class HeapSelectSorting {
public static void main(String[] args) {
int[] arr = new int[] {1,3,2,1,0,10,9,8};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
int len = arr.length;
int j;
for(j = (len - 1) / 2; j >= 0; j--) {
adjust(arr, j, len - 1);
}
for(j = len - 1; j >= 0; j--) {
swap(arr, 0, j);
adjust(arr, 0, j - 1);
}
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
/**
* 這個調整是再已滿足堆定義的基礎上的調整,所有有個初始化操作建立大根堆或者小根堆。
* @param arr
* @param s
* @param n
*/
private static void adjust(int[] arr, int s, int n) {
int j = 2 * s + 1;
int tmp = arr[s];
while(j <= n) {
if(j < n && arr[j + 1] > arr[j]) {
j++;
}
if(arr[j] > tmp) {
arr[s] = arr[j];
s = j;
j = 2 * s + 1;
}else {
break;
}
}
arr[s] = tmp;
}
}
堆選擇排序核心是建立一個初始化堆和每次交換元素後重新調整爲堆。
歸併排序
/**
* 快排
*/
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[] {3,2,1,5,9,10,3};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
sort(arr, 0, arr.length - 1);
}
private static void sort(int[] arr, int lo, int hi) {
if(lo >= hi) {
return;
}
int middle = getMiddle(arr, lo, hi);
sort(arr, lo, middle - 1);
sort(arr, middle + 1, hi);
}
private static int getMiddle(int[] arr, int lo, int hi) {
if(lo >= hi) {
return lo;
}
int val = arr[lo];
while(lo < hi) {
while(lo < hi && arr[hi] >= val) hi--;
if(lo < hi) {
arr[lo] = arr[hi];
}
while(lo < hi && arr[lo] <= val) lo++;
if(lo < hi) {
arr[hi] = arr[lo];
}
}
arr[lo] = val;
return lo;
}
}