基本概念介绍
- 稳定排序
对于待排序的数据,如果有元素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;
}
}
堆选择排序核心是建立一个初始化堆和每次交换元素后重新调整为堆。
归并排序
import java.util.Arrays;
/**
* @Author: jiangcw
* @Date: 2019-7-28 下午 6:04
* @Version 1.0
*
* 归并排序
*
*采用递归方式写,归并排序写法还是挺简单的,
* 还是快排稍微难理解些
*/
public class MergeSorting {
public static void main(String[] args) {
int[] a = {50, 36, 66,76,95,12,25,36};
sort(a);
System.out.println(Arrays.toString(a));
}
public static void sort(int[] a) {
if(a == null || a.length < 2) {
return;
}
int[] tmp = new int[a.length];
merge(a, 0, a.length-1, tmp);
}
private static void merge(int[] a, int lo, int hi, int[] tmp) {
if(lo >= hi) {
return;
}
int mid = (lo + hi) >> 1;
merge(a, lo, mid, tmp);
merge(a, mid + 1, hi, tmp);
int firstStart = lo;
int secondStart = mid + 1;
int j = lo;
while(firstStart <= mid && secondStart <= hi) {
if(a[firstStart] < a[secondStart]) {
tmp[j++] = a[firstStart++];
}else {
tmp[j++] = a[secondStart++];
}
}
if(firstStart <= mid) {
while(firstStart <= mid) {
tmp[j++] = a[firstStart++];
}
}else {
while(secondStart <= hi) {
tmp[j++] = a[secondStart++];
}
}
while(lo <= hi) {
a[lo] = tmp[lo];
lo++;
}
}
}