排序算法
堆排序
图解
解决方案
package com.company;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args)
{
int arr[] ={19,5,6,4,1,2,88,99};
System.out.println(Arrays.toString(arr)); // [19, 5, 6, 4, 1, 2, 88, 99]
sort(arr);
System.out.println(Arrays.toString(arr)); // [1, 4, 2, 19, 5, 6, 88, 99]
}
public static void sort(int arr[])
{
int n = arr.length;
// int i=(n-1)/2
for(int i=(n-1)/2; i>=0;i--){
adjust(arr, i);
}
}
public static void adjust(int arr[], int cur)
{
int n = arr.length;
int val = arr[cur];
for(int i=2*cur+1; i<n ; i=2*i+1)
{
if(i+1 < n && arr[i] > arr[i+1])
i++;
if(arr[i] < val){
arr[cur] = arr[i];
arr[i] = val;
cur = i;
}
}
}
}
归并排序
- 分解(一分到底)
- 排序(排序)
- 合并
图解
解决方案
递归方式:
从图片看,可以使用遍历树的时候的 后序遍历 的思想;
注意:
- 分解
- 合并
- 辅助数组
public class Main {
public static void main(String[] args)
{
Main man = new Main();
int[] arr = {5,8,1,4,3,2,9,71,45,23,468,15,5661};
int[] ret = new int[arr.length];
System.out.println(Arrays.toString(arr)); // [5, 8, 1, 4, 3, 2, 9, 71, 45, 23, 468, 15, 5661]
man.divide(arr, ret, 0, arr.length-1);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 8, 9, 15, 23, 45, 71, 468, 5661]
System.out.println(Arrays.toString(ret)); // [1, 2, 3, 4, 5, 8, 9, 15, 23, 45, 71, 468, 5661]
}
public void divide(int[] arr, int[] result, int start, int end)
{// arr 是原始数组,每次合并会进行更改 // result 在合并时候作为辅助数组 // [start, end] 表示arr中的当前段
if( start >= end) return;
// 1. 分
int mid = start + ((end - start)>>1); // 1.1 计算中间下标位置
divide(arr, result, start, mid); // 1.2 分左边
divide(arr, result,mid+1, end); // 1.3 分右边
// 2. 治
int start1 = start, start2 = mid+1; // 2.1 设置开始、结束位置 开始合并 [start1 ... mid] [mid+1 ... end]
int r_start = start;
while(start1 <= mid && start2 <= end) // 2.2 考虑长短不一的情况 [start1 ....... mid] [mid+1 ... end]
result[r_start++] = arr[start1] < arr[start2] ? arr[start1++]:arr[start2++];
while (start1<= mid) // 2.2.1 左边长一点时候
result[r_start++] = arr[start1++] ;
while (start2 <= end) // 2.2.2 右边边长一点时候
result[r_start++] = arr[start2++] ;
for(int i = start; i<=end; i++) // 2.3 数据从 result 拷贝回 arr
arr[i] = result[i]; // 在主函数main()中,arr 和 ret 一致,可以删除这两行代码嘛? 不行,因为计算过程中使用到了 arr[i], result主要起辅助存储的作用。
// 若删除最后两行,则:主函数中会输出
// arr = [5, 8, 1, 4, 3, 2, 9, 71, 45, 23, 468, 15, 5661]
// ret = [5, 8, 1, 4, 3, 2, 9, 71, 45, 23, 468, 15, 5661]
}
}
迭代方式:
下次增加
快速排序
- 从序列中,选一个记录k作为轴值pivot
- 选择策略:第一个元素、最后一个元素、中间元素、随机选择
- 将剩余的元素,分割成 左子序列 L 和 右子序列 R
- L 中所有元素都 < k, R 中所有元素都 > k
- 对 L 和 R递归进行快排,直到子序列中有 0 个 或者 1 个元素,退出
图解
代码
public class Main {
public static void main(String[] args)
{
int[] arr2 = {5,2,9,4,6,1,7,6,8};
System.out.println(Arrays.toString(arr2));
quickSort( arr2, 0, arr2.length-1); // 一般来说,在快排中使用 [start, end] 更好一点
System.out.println(Arrays.toString(arr2));
}
public static void quickSort(int[] arr, int start, int end){
if(start < end)
{
int mid = partition(arr, start, end);
System.out.println(start+ " -- " + mid + " -- " + end );
quickSort(arr, start, mid-1);
quickSort(arr, mid + 1, end);
}
}
public static int partition(int[] arr, int start, int end){
int val = arr[start]; // 选择第一个为基准
while(start < end) // 当start == end 则退出
{
while(start< end && arr[end] >= val) end--; //右边的指针移动 这儿的 >= 符号可以保持排序的稳定性
arr[start] = arr[end];
while(start< end && arr[start] <= val) start++; //左边的指针移动 这儿的 <= 符号可以保持排序的稳定性
arr[end] = arr[start];
}
arr[start] = val; // 重新将值填进去
return start;
}
}
快速排序不稳定
例子: 5 1 4 4 8 6 2 9 4 —> 4 1 4 4 2 5 6 9 8
例子2: 5 1 4 4 —> 1 4 4 5
排序总结
总结表
排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 | 特征1: | 特征2: | ||
最好 | 平均 | 最坏 | |||||
直接插入 | O(n) | O(n^2) | O(n^2) | O(1) | 1 | 每趟确定一个最值 | 特征2: |
冒泡 | O(n) | O(n^2) | O(n^2) | O(1) | 1 | 每趟确定一个最值 | 可能循环完成之前就排好序了 |
简单选择 | O(n^2) | O(n^2) | O(n^2) | O(1) | 0 | 每趟确定一个最值 | 特征2: |
希尔 | O(n^(1.3)) | O(1) | 0 | 最后一趟才有序 | 最小增量排序 | ||
快速 | O(nlog2^n) | O(nlog2^n) | O(n^2) | O(log2^n) | 0 | 最后一趟才有序 | 分治法,需要一个枢轴 |
堆排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n) | O(1) | 0 | 每趟确定一个最值 | 建堆-取根-调整堆 |
归并排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n) | O(n) | 1 | 最后一趟才有序 | 1、划分 2、归并 |
基数排序 | O(d(n+r)) | O(d(n+r)) | O(d(n+r)) | O(r) | 1 | 每一趟按不同关键字有序 | 分配、收集 |
最后一趟才有序:最后一趟之前,整体都是无序的(不存在连续的顺序子序列)
每趟确定一个最值:在最后一趟前,存在连续的顺序子序列
是否稳定 | 方法 | 稳定Or不稳定 的条件 |
---|---|---|
稳定 | 冒泡排序 | 相等的时候不进行交换 |
直接插入排序 | 相等的时候则跳过 | |
折半插入排序 | (同直插) | |
归并排序 | 归并的时候,左边与右边相等的时候先放左边 | |
不稳定 | 堆排序 、希尔排序、直接选择排序 why、快速排序 |
基数排序
LSD: 先排次要的关键字,最后排主要的关键字
MSD:先排主关键字,最后排最低层次的关键字基数排序中LSD方法:若有N种关键字类型(就是存在个位、十位、百位。。。)那么对于Key(i)【0<= i <= N-2】时候要用稳定的排序方式。
代码
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 将给定数组排序
* @param arr int整型一维数组 待排序的数组
* @return int整型一维数组
*/
public int[] MySort (int[] arr) {
// write code here
// 冒泡
// return bubble(arr);
// 选择
// return choise(arr);
// 插入
// return insert(arr);
// 快排(最快)
quickSort(arr, 0, arr.length-1);
return arr;
// 堆排
// heapSort(arr);
// return arr;
// 归并
// mergeSort(arr);
// return arr;
}
// 归并排序
public void mergeSort(int[] arr)
{
int[] tmp = new int[arr.length];
merge(arr, 0, arr.length-1, tmp);
}
public void divide(int[] arr, int left, int right, int[] tmp)
{
if(left >= right)
return;
int mid = left + ((right - left) >>> 1);
divide(arr, left, mid, tmp);
divide(arr, mid + 1, right, tmp);
int x = left, y = mid + 1;
int ind = left;
while(x <= mid && y <= right){
tmp[ind++] = arr[x] < arr[y] ? arr[x++] : arr[y++];
}
while(x<=mid)
{
tmp[ind++] = arr[x++];
}
while(y<=right)
{
tmp[ind++] = arr[y++];
}
for(int i = left; i<= right; i++)
arr[i] = tmp[i];
}
// 堆排序
public void heapSort(int[] arr)
{
int n= arr.length;
for(int i= (n-1)/2; i>=0;i--)
adjust(arr, i, n);
for(int j=arr.length-1; j > 0;j--)
{
int tmp = arr[0];
arr[0] = arr[j];
arr[j] = tmp;
adjust(arr, 0, j);
}
}
public void adjust(int[] arr, int x, int len)
{
int t = x;
for(int i=2*x+1; i< len; i=2*i+1)
{
if(i+1 < len && arr[i] < arr[i+1])
i++;
if(arr[i] > arr[t]){
int tmp = arr[t];
arr[t] = arr[i];
arr[i] = tmp;
}
t = i;
}
}
// 快速排序
public void quickSort(int[] arr, int left, int right)
{
if(left >=right)
return;
int mid = getMid(arr, left, right);
quickSort(arr, left, mid-1);
quickSort(arr, mid+1, right);
}
public int getMid(int[] arr, int left, int right)
{
int val = arr[left];
while(left < right){
while( left < right && arr[right] >= val)
right--;
if(left < right )
arr[left++] = arr[right];
while( left < right && arr[left] < val)
left++;
if(left < right )
arr[right--] = arr[left];
}
arr[left] = val;
return left;
}
// 插入排序
public int[] insert(int[] arr){
for(int i =0; i<arr.length; i++)
{
int val = arr[i];
for(int j=0; j<i; j++)
{
if(val < arr[j])
{
for(int x = i; x > j;x--)
{
arr[x] = arr[x-1];
}
arr[j] = val;
break;
}
}
}
return arr;
}
// 选择排序
public int[] choise(int[] arr)
{
for(int i =0; i<arr.length; i++)
{
int min = i;
for(int j=i+1;j<arr.length; j++)
{
if(arr[j] < arr[min])
{
min = j;
}
}
int temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
return arr;
}
// 冒泡排序
public int[] bubble(int[] arr)
{
for(int i=0; i<arr.length;i++)
{
for(int j=1;j<arr.length-i; j++)
{
if(arr[j-1] > arr[j])
{
int temp = arr[j-1];
arr[j-1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
}