目录
1.直接插入排序
2.希尔排序
3.选择排序
4.堆排序
5.冒泡排序
6.快速排序
6.1 递归实现——Hoare版
6.2 递归实现——挖坑法
6.3 非递归实现
6.4 优化
7.归并排序
7.1 归并排序——递归实现
7.2 归并排序——非递归实现
8.复杂度以及稳定性
基本思路
直接插入排序代码
//直接插入排序
public void insertSortDirectly(int[] array) {
if(array == null || array.length == 0) {
return;
}
for(int i=1;i= 0;j--) {
if(array[j] > temp) {
array[j+1] = array[j];
} else {
break;
}
}
//将当前元素放在j + 1 的位置上
array[j+1] = temp;
}
}
希尔排序实质上还是一种插入排序,不同的是采用了分组插入排序的思想,每次对每组进行的插入排序都会整组数据的插入排序效率更高,所以希尔排序的平均效率相对于普通的插入排序要高。
基本思路:
希尔排序代码
public void shellSort(int[] array) {
if (array == null || array.length == 0) {
return;
}
int gap = array.length;
while (gap > 1) {
gap /= 2;
shell(array, gap);
}
}
private void shell(int[] array, int gap) {
for (int i = gap; i < array.length; i++) {
int temp = array[i];
int j = i - gap;
for (; j >= 0; j -= gap) {
if (temp < array[j]) {
array[j + gap] = array[j];
} else {
break;
}
}
array[j + gap] = temp;
}
}
基本思路
选择排序代码
public static void selectSort(int[] array) {
if (array == null || array.length == 0) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
int minPos = i;
for (int j = i + 1; j < array.length; j++) {
if (array[j] < array[minPos]) {
minPos = j;
}
}
if (minPos != i) {
int temp = array[i];
array[i] = array[minPos];
array[minPos] = temp;
}
}
}
基本思路
堆排序代码
public static void heapSort(int[] array) {
if (array == null || array.length == 0) {
return;
}
//此处我们以升序排列为例,所以建大根堆
createBigHeap(array);
//进行排序操作
int end = array.length - 1;
while (end > 0) {
int temp = array[0];
array[0] = array[end];
array[end] = temp;
siftDown(array, 0, end);
end--;
}
}
private static void createBigHeap(int[] array) {
for (int i = (array.length - 1 - 1) / 2; i >= 0; i--) {
siftDown(array, i, array.length); //从下标为i的位置向下调整
}
}
private static void siftDown(int[] array, int parent, int size) {
int childLeft = parent * 2 + 1;
while (childLeft < size) {
if (childLeft + 1 < size && array[childLeft] < array[childLeft + 1]) {
childLeft = childLeft + 1;
}
if (array[parent] < array[childLeft]) {
int temp = array[parent];
array[parent] = array[childLeft];
array[childLeft] = temp;
}
parent = childLeft;
childLeft = parent * 2 + 1;
}
}
基本思路
冒泡排序代码
public static void bubbleSort(int[] array) {
if (array == null || array.length == 0) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
boolean flag = false; //标志变量。如果进行了一轮比较而没有进行交换操作,说明已经有序了
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
flag = true;
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
if(!flag) {
break;
}
}
}
基本思路
快速排序递归实现图解
快速排序递归实现代码——Hoare
public static void quickSort(int[] array) {
if (array == null || array.length == 0) {
return;
}
quickSort(array, 0, array.length - 1);
}
private static void quickSort(int[] array, int start, int end) {
if (start >= end) {
return;
}
int pivot = getPivot(array, start, end);
quickSort(array, start, pivot - 1);
quickSort(array, pivot + 1, end);
}
private static int getPivot(int[] array, int left, int right) {
int startPos = left;
int temp = array[left];
while (left < right) {
while (left < right && array[right] >= temp) {
right--;
}
while (left < right && array[left] <= temp) {
left++;
}
int cur = array[left];
array[left] = array[right];
array[right] = cur;
}
array[startPos] = array[left];
array[left] = temp;
return left;
}
基本思路
这种方法与Hoare版本的快排区别在于根据基准调整左右序列的做法。挖坑法是直接将基准位置空出,从右侧开始找到一个比基准小的数就将该数放在基准的位置上,从左侧开始找到一个比基准大的数就放在右侧空出的位置上,左右交替进行;当左右指针相遇时,就将基准值放在相遇位置上,并更新基准值下标。剩余的做法和Hoare一致,即在其左右子序列中重复上述操作。
快速排序递归实现代码——挖坑法
public static void quickSortNonRecursive(int[] array) {
if (array == null || array.length == 0) {
return;
}
Stack stack = new Stack<>();
int start = 0;
int end = array.length - 1;
int pivot = getPivot(array, start, end);
if (pivot > start + 1) {
stack.push(start);
stack.push(pivot - 1);
}
if (pivot < end - 1) {
stack.push(pivot + 1);
stack.push(end);
}
while (!stack.isEmpty()) {
end = stack.pop();
start = stack.pop();
pivot = getPivot(array, start, end);
if (pivot > start + 1) {
stack.push(start);
stack.push(pivot - 1);
}
if (pivot < end - 1) {
stack.push(pivot + 1);
stack.push(end);
}
}
}
非递归实现图解
非递归实现代码
public static void quickSortNonRecursive(int[] array) {
if (array == null || array.length == 0) {
return;
}
Stack stack = new Stack<>();
int start = 0;
int end = array.length-1;
int pivot = getPivot(array, start, end);
if (pivot > start + 1) {
stack.push(start);
stack.push(pivot - 1);
}
if (pivot < end - 1) {
stack.push(pivot + 1);
stack.push(end);
}
while (!stack.isEmpty()) {
end = stack.pop();
start = stack.pop();
pivot = getPivot(array, start, end);
if (pivot > start + 1) {
stack.push(start);
stack.push(pivot - 1);
}
if (pivot < end - 1) {
stack.push(pivot + 1);
stack.push(end);
}
}
}
三数取中法
要解决的问题:
做法:
private static int threeNum(int[] array, int left, int right) {
int mid = (left + right) / 2;
if (array[left] < array[right]) {
if (array[mid] < array[left]) {
return left;
} else if (array[mid] > array[right]) {
return right;
} else {
return mid;
}
} else {
if (array[mid] < array[right]) {
return right;
} else if (array[mid] > array[left]) {
return left;
} else {
return mid;
}
}
}
归并排序采用归并思想,将已经分割好的子序列进行合并,并且在合并的过程中对归并的子序列进行比较排序,这样,当归并结束总序列也就有序了。
基本思路
归并排序递归实现图解
归并排序递归实现代码
/**
* 归并排序
* @param array 待排序数组
*/
public static void mergeSort(int[] array) {
mergeSort(array, 0, array.length - 1);
}
private static void mergeSort(int[] array, int start, int end) {
//归
if (start >= end) {
return;
}
int mid = (start + end) / 2;
mergeSort(array, start, mid);
mergeSort(array, mid + 1, end);
//并
merge(array, start, mid, end);
}
private static void merge(int[] array, int start, int mid, int end) {
int s1 = start;
int s2 = mid + 1;
int[] temp = new int[end - start + 1];
int pos = 0;
while (s1 <= mid && s2 <= end) {
if(array[s1] <= array[s2]) {
temp[pos++] = array[s1++];
} else {
temp[pos++] = array[s2++];
}
}
while(s1 <= mid) {
temp[pos++] = array[s1++];
}
while(s2 <= end) {
temp[pos++] = array[s2++];
}
//将这个归并好的数组拷贝到排序数组中
for (int i = start; i <= end; i++) {
array[i] = temp[i-start];
}
}
归并排序非递归实现图解
归并排序非递归实现代码
public static void mergeSortNonRecursive(int[] array) {
if (array == null || array.length == 0) {
return;
}
int gap = 1;
while (gap < array.length) {
for (int i = 0; i < array.length; i += gap * 2) {
int left = i;
int mid = left + gap - 1;
int right = mid + gap;
//right和mid有可能会越界,对其进行修正
if (mid >= array.length) {
mid = array.length - 1;
}
if (right >= array.length) {
right = array.length - 1;
}
merge(array, left, mid, right);
}
gap *= 2;
}
}
private static void merge(int[] array, int start, int mid, int end) {
int s1 = start;
int s2 = mid + 1;
int[] temp = new int[end - start + 1];
int pos = 0;
while (s1 <= mid && s2 <= end) {
if (array[s1] <= array[s2]) {
temp[pos++] = array[s1++];
} else {
temp[pos++] = array[s2++];
}
}
while (s1 <= mid) {
temp[pos++] = array[s1++];
}
while (s2 <= end) {
temp[pos++] = array[s2++];
}
//将这个归并好的数组拷贝到排序数组中
for (int i = start; i <= end; i++) {
array[i] = temp[i - start];
}
}
排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
直接插入排序 | O(n²) | O(1) | 稳定 |
希尔排序 | O() | O(1) | 不稳定 |
选择排序 | O(n²) | O(1) | 不稳定 |
堆排序 | O(n*log2n) | O(1) | 不稳定 |
冒泡排序 | O(n²) | O(1) | 稳定 |
快速排序 | O(n*logn) | O(logn) | 不稳定 |
归并排序 | O(n*logn) | O(n) | 稳定 |