一、交换排序
- 冒泡排序
在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序。
import java.util.Arrays;
public class BubbleSort {
public static void bubbleSort(int[] array) {
//冒泡排序:在无序区间,通过相邻数的比较,
//将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序
//时间复杂度:O(N ^ 2) 空间复杂度:O(1)
//稳定性:稳定排序
//升序排序为例:
//每次找最小元素,从后往前遍历
//每次找最大元素:从前往后遍历
//找最大元素
for (int bound = 0; bound < array.length; bound++) {
//[0, bound) 已排序区间 [bound, size) 未排序区间
for (int cur = array.length - 1; cur > bound; cur--) {
if (array[cur - 1] > array[cur]) {
swap(array, cur - 1, cur);
}
}
}
}
public static void swap(int[] array, int i, int j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static void main(String[] args) {
int[] array = {4, 7, 9, 1, 5, 6, 3, 8, 2};
bubbleSort(array);
System.out.println(Arrays.toString(array));
}
}
- 快速排序 (很重要)
a. 用递归实现
import java.util.Arrays;
public class ImportentQuickSort {
public static void quickSort(int[] array) {
// 快速排序:需要用到递归
// 1.从待排序区间选择一个数(第一个或最后一个),作为基准值(pivot);
// 2.核心 Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,
// 将比基准值大的(可以包含相等的)放到基准值的右边;
// 3.采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,
// 或者小区间的长度 == 0,代表没有数据
// 时间复杂度:
// 最坏情况下 => O(N ^ 2)(每次的基准值正好是最大值或最小值)
// 平均情况下 => O(NlogN)
// 空间复杂度:
// 最坏情况下 => O(N) 平均情况下 => O(logN)
// 稳定性:不稳定排序
//设基准值为最后一个元素(基准值为首元素则操作相反)
quickSortHelper(array, 0, array.length - 1);
}
private static void quickSortHelper(int[] array, int left, int right) {
if(left >= right) {
//区间中有 0 或 1 个元素,不需排序
return;
}
int index = partition(array, left, right);
//返回值表示整理后 基准值 所在位置
//[left, ingex - 1] [index + 1, right]
quickSortHelper(array, left, index -1);
quickSortHelper(array, index + 1, right);
}
private static int partition(int[] array, int left, int right) {
int baseValue = array[right];//设基准值为最后一个元素
int i = left;
int j = right;
while (i < j) {
//从左往右找到一个 大于基准值 的元素
while (i < j && array[i] <= baseValue) {
i++;
}
//再从右往左找到一个 小于基准值 的元素
while (i < j && array[j] >= baseValue) {
j--;
}
//此时 j 指向的元素要么和 i 重合,要么小于基准值
//交换 i 和 j 的值
if(i <j) {
swap(array, i, j);
}
}
//循环结束, i、j 重合,再与基准值交换
//重合元素一定大于基准值元素
swap(array, i, right);
return i;
}
private static void swap(int[] array, int i, int j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static void main(String[] args) {
int[] array = {4, 9, 1, 5, 6, 3, 8, 2};
quickSort(array);
System.out.println(Arrays.toString(array));
}
}
b. 非递归实现
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Stack;
public class Quicker {
//非递归实现快速排序(借助一个栈)
public static void byLoop(int[] array) {
Stack<Integer> stack = new Stack<>();
stack.push(0);
stack.push(array.length - 1);
while (!stack.isEmpty()) {
int right = stack.pop();
int left = stack.pop();
if(left >= right) {
//区间为空 或 区间内只有一个元素
continue;
}
int index = partition(array, left, right);
//把右子树入栈 [ index + 1, right)
stack.push(index + 1);
stack.push(right);
//把左子树入栈 [left, index - 1)
stack.push(left);
stack.push(index - 1);
}
}
private static int partition(int[] array, int left, int right) {
int baseValue = array[right];//设基准值为最后一个元素
int i = left;
int j = right;
while (i < j) {
//从左往右找到一个 大于基准值 的元素
while (i < j && array[i] <= baseValue) {
i++;
}
//再从右往左找到一个 小于基准值 的元素
while (i < j && array[j] >= baseValue) {
j--;
}
//此时 j 指向的元素要么和 i 重合,要么小于基准值
//交换 i 和 j 的值
if(i <j) {
swap(array, i, j);
}
}
//循环结束, i、j 重合,再与基准值交换
//重合元素一定大于基准值元素
swap(array, i, right);
return i;
}
private static void swap(int[] array, int i, int j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static void main(String[] args) {
int[] array = {4, 9, 1, 5, 6, 3, 8, 2};
byLoop(array);
System.out.println(Arrays.toString(array));
}
}
c. java 自带实现
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(6);
arrayList.add(3);
arrayList.add(7);
arrayList.add(1);
arrayList.add(5);
arrayList.add(9);
arrayList.add(8);
Collections.sort(arrayList);
System.out.println(arrayList);
int[] array = {6, 3, 7, 1, 5, 9, 8};
Arrays.sort(array);
System.out.println(arrayList);
//ctrl + 鼠标左键 跳转到 类/方法 定义
}
二、归并排序
- 归并排序
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使 子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
a. 递归实现
import java.util.Arrays;
public class MergeSort {
//归并排序
//时间复杂度:O(NlogN) 空间复杂度:O(N) + O(logN)(临时空间)=> O(N)
//稳定性:稳定排序
//特点:
// 1.能高效的针对链表进行排序
// 2. “外部排序” 的主要实现方式(数据在外存中)
public static void mergeSort(int[] array) {
// [0, length)
mergeSortHelper(array, 0, array.length);
}
private static void mergeSortHelper(int [] array, int left, int right) {
//[left, right)
if(right - left <= 1) {
//当前区间中有 0 或 1 个元素,不需要进行排序
return;
}
//对于 [left, right)区间,分成对等的两个区间
// [left, mid) [mid, right)
int mid = (left + right) / 2;
mergeSortHelper(array, left, mid);
mergeSortHelper(array, mid, right);
//已排好序,将两个有序数字进行合并
merge(array, left, mid, right);
}
private static void merge(int[] array, int left, int mid, int right) {
int cur1 = left;
int cur2 = mid;
//临时空间需要容纳下两个数组 数量之和
int[] output = new int[right - left];
int outputIndex = 0;
// 当前 output 中被插入了几个元素
while (cur1 < mid && cur2 < right) {
if (array[cur1] <= array[cur2]) {
// 如果是 < ,无法保证稳定性
// 把 cur1 位置的元素插入到 output 中
output[outputIndex] = array[cur1];
cur1++;
outputIndex++;
}else {
output[outputIndex] = array[cur2];
cur2++;
outputIndex++;
}
}
while (cur1 < mid) {
output[outputIndex] = array[cur1];
cur1++;
outputIndex++;
}
while (cur2 < right) {
output[outputIndex] = array[cur2];
cur2++;
outputIndex++;
}
//把数据从临时空间中拷贝回原来的数组中
for (int i = 0; i < right - left; i++) {
array[left + i] = output[i];
}
}
public static void main(String[] args) {
int[] array = {4, 9, 1, 5, 6, 3, 8, 2};
mergeSort(array);
System.out.println(Arrays.toString(array));
}
}
b. 非递归实现
import java.util.Arrays;
public class MregeSortByLoop {
public static void byLoop(int[] array) {
// gap 表示当前每个组中的元素个数
for (int gap = 1; gap < array.length; gap *= 2) {
for (int i = 0; i < array.length; i += 2*gap) {
//每进行一遍循环体,相当于把两个长度为 gap 的数组合并
// [i, i + gap) [i + gap, i + 2*gap)
int left = i;
int mid = i +gap;
int right = i + 2*gap;
if (mid > array.length) {
mid = array.length;
}
if (right > array.length) {
right = array.length;
}
merge(array, left, mid, right);
}
}
}
private static void merge(int[] array, int left, int mid, int right) {
int cur1 = left;
int cur2 = mid;
//临时空间需要容纳下两个数组 数量之和
int[] output = new int[right - left];
int outputIndex = 0;
// 当前 output 中被插入了几个元素
while (cur1 < mid && cur2 < right) {
if (array[cur1] <= array[cur2]) {
// 如果是 < ,无法保证稳定性
// 把 cur1 位置的元素插入到 output 中
output[outputIndex] = array[cur1];
cur1++;
outputIndex++;
}else {
output[outputIndex] = array[cur2];
cur2++;
outputIndex++;
}
}
while (cur1 < mid) {
output[outputIndex] = array[cur1];
cur1++;
outputIndex++;
}
while (cur2 < right) {
output[outputIndex] = array[cur2];
cur2++;
outputIndex++;
}
//把数据从临时空间中拷贝回原来的数组中
for (int i = 0; i < right - left; i++) {
array[left + i] = output[i];
}
}
public static void main(String[] args) {
int[] array = {4, 9, 1, 5, 6, 3, 8, 2};
byLoop(array);
System.out.println(Arrays.toString(array));
}
}