Leetcode 912.排序数组
公共交换函数
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
1. 选择排序
每轮找出最小的值放在左侧
private void selectionSort(int[] nums) {
for (int i=0;i<nums.length;i++) {
int min = i;
for (int j=i+1;j<nums.length;j++) {
if (nums[j]<nums[min]) min = j;
}
swap(nums, i, min);
}
}
2. 插入排序
假设前i有序,把i+1的元素,通过交换插入到前面的i个中
private void insertSort(int[] nums) {
for (int i=1;i<nums.length;i++) {
for (int j=i;j>0 && nums[j]<nums[j-1];j--)
swap(nums, j, j-1);
}
}
3. 希尔排序
对于大规模的数组,插入排序很慢,因为它只能交换相邻的元素,每次只能将逆序数量减少 1。希尔排序的出现就是为了解决插入排序的这种局限性,它通过交换不相邻的元素,每次可以将逆序数量减少大于 1。
希尔排序使用插入排序对间隔 h 的序列进行排序。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。
private void shellSort(int[] nums) {
int h = 1;
while (h < nums.length/3) h = h*3 + 1;
while (h>=1) {
for (int i=h; i<nums.length; i++) {
for (int j=i;j>=h && nums[j]<nums[j-h];j-=h)
swap(nums, j, j-h);
}
h /= 3;
}
}
4. 冒泡排序
比较交换,大的向后交换
// 慢的超时
private void bubbleSort(int[] nums) {
for (int i=nums.length-1; i>=0; i--) {
for (int j=0; j<i; j++)
if (nums[j]>nums[j+1]) swap(nums, j, j+1);
}
}
5. 归并排序
排左排右,然后归并(将两个有序数组合并为一个)
private void mergeSort(int[] nums) {
mergeSort(nums, 0, nums.length-1);
}
private void mergeSort(int[] nums, int l, int h) {
if (h<=l) return;
int m = l + (h-l)/2;
mergeSort(nums, l, m);
mergeSort(nums, m+1, h);
merge(nums, l, m, h);
}
private void merge(int[] nums, int l, int m, int h) {
int i = l, j = m+1;
int[] tmp = new int[nums.length];
for (int k=l; k<=h; k++) tmp[k] = nums[k];
for (int k=l; k<=h; k++) {
if (i>m) nums[k] = tmp[j++];
else if (j>h) nums[k] = tmp[i++];
else if (tmp[i]<=tmp[j]) nums[k] = tmp[i++];
else nums[k] = tmp[j++];
}
}
6. 快速排序
快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。
parition返回的j左侧都是小于nums[l]的,右侧都是大于nums[l]的
private void quickSort(int[] nums) {
quickSort(nums, 0, nums.length-1);
}
private void quickSort(int[] nums, int l, int h) {
if (l>=h) return;
int j = partition(nums, l, h);
quickSort(nums, l, j-1);
quickSort(nums, j+1, h);
}
private int partition(int[] nums, int l, int h) {
int i = l, j = h+1;
while (true) {
while (nums[++i]<nums[l] && i!=h) ;
while (nums[--j]>nums[l] && j!=l) ;
if (i >= j) break;
swap(nums, i, j);
}
swap(nums, l, j);
return j;
}
7. 堆排序
public static void heapSort(int[] nums) {
// 建立大顶堆
for(int i=nums.length-1; i>=0; i--)
heapify(nums, i, nums.length);
for(int i=nums.length-1; i>=0; i--) {
// 将大顶堆的最大值,也就是数组中的第一个元素移到最尾部
int tmp = nums[0];
nums[0] = nums[i];
nums[i] = tmp;
// 由于根节点与叶子节点互换,此时需要再次对根节点进行大顶堆操作,就这样依次循环
heapify(nums, 0, i);
}
}
private static void heapify(int[] nums, int i, int bound) {
// 得到左右孩子节点数组下标
int left = 2*i+1, right = 2*i+2, max = i;
// 将最大值移动到根节点,保证所有的根节点都大于孩子节点
if (left==nums.length-1) {
if (left<bound && nums[left]>nums[max])
max = left;
} else{
if (left<bound && nums[left]>nums[max])
max = left;
if (right<bound && nums[right]>nums[max])
max = right;
}
if (max != i) {
int tmp = nums[i];
nums[i] = nums[max];
nums[max] = tmp;
// 移动之后需检查孩子节点,如果孩子节点为非叶子节点,互换之后导致孩子节点不满足大顶堆特性,需继续对该孩子节点做大顶堆操作
heapify(nums, max, bound);
}
}