标签:双指针 return ble 父节点 一个 希尔 基数排序 length EAP
import java.util.*;
public class Sort {
static int min(int a,int b){
return Math.min(a,b);
}
static int max(int a,int b) { return Math.max(a,b); }
static void swap(int[] a,int i,int j){
int t=a[i];
a[i]=a[j];
a[j]=t;
}
static void print(int[] a){
for(int v:a){
System.out.print(v+" ");
}
System.out.println();
}
/**
* 冒泡排序,比较相邻两个元素并交换,每扫一遍将最大的元素交换到最后
* 平均O(N^2) 最好O(N) 最坏O(N^2) 空间O(1) 稳定(相同元素不会交换)
* @param a
*/
static void bubbleSort(int[] a){
int n=a.length;
for(int i=n;i>0;i--){
boolean flag=false;
for(int j=1;j<i;j++){
if(a[j-1]>a[j]){
flag=true;
swap(a,j-1,j);
}
}
//将最好时间优化到O(N)
if(!flag){
break;
}
}
}
/**
* 选择排序,每次选择剩下的最小元素放在正确位置
* 平均O(N^2) 最好O(N^2) 最坏O(N^2) 空间O(1) 不稳定(比如[5 8 5 2 9],第一个5会跟2交换,改变5的相对位置)
* @param a
*/
static void selectSort(int[] a){
int n=a.length;
for(int i=0;i<n-1;i++){
int mn=Integer.MAX_VALUE;
int idx=i;
for(int j=i;j<n;j++){
if(a[j]<mn){
mn=a[j];
idx=j;
}
}
swap(a,i,idx);
}
}
/**
* 插入排序,前面部分是有序数组,把后面部分一个个插入到正确位置
* 平均O(N^2) 最好O(N) 最坏O(N^2) 空间O(1) 稳定(相同值不会插入)
* @param a
*/
static void insertSort(int[] a){
int n=a.length;
for(int i=1;i<n;i++){
if(a[i]>a[i-1]){
//将最好时间优化到O(N)
continue;
}
int t=a[i];
int j=i-1;
for(;j>=0;j--){
if(t<a[j]){
a[j+1]=a[j];
}else{
break;
}
}
a[j+1]=t;
}
}
/**
* 希尔排序(缩小增量插入排序)
* 平均O(N^(1.3~2)) 最好O(N) 最坏O(N^2) 空间O(1) 不稳定(同一个值分在不同的组)
* @param a
*/
static void shellSort(int[] a){
int n=a.length;
//h为间隔/组数
for(int h=n/2;h>0;h/=2){
for(int i=h;i<n;i++){
//间隔为h的插入排序,普通插入排序就是间隔为1
if(a[i]>a[i-h]){
continue;
}
int t=a[i];
int j=i-h;
for(;j>0;j-=h){
if(t<a[j]){
a[j+h]=a[j];
}else{
break;
}
}
a[j+h]=t;
}
}
}
private static void mergeSort(int[] a,int l,int r){
int len=(r-l+1);
//剩一个元素,递归边界
if(len<2){
return;
}
int mid=(l+r)/2;
//分
mergeSort(a,l,mid);
mergeSort(a,mid+1,r);
//合并
int[] b=new int[len];
for(int idx=0,i=l,j=mid+1;idx<len;idx++){
if(i==mid+1){
b[idx]=a[j++];
}else if(j==r+1){
b[idx]=a[i++];
}else if(a[i]<a[j]){
b[idx]=a[i++];
}else{
b[idx]=a[j++];
}
}
System.arraycopy(b,0,a,l,len);
}
/**
* 归并排序,分治法,将两个有序的序列合并
* 平均O(NlogN) 最好O(NlogN) 最坏O(NlogN) 空间O(N) 稳定
* 空间算的是最大,所以是O(N)+O(N/2)+O(N/4)+...=O(N)
* @param a
*/
static void mergeSort(int[] a){
int n=a.length;
mergeSort(a,0,n-1);
}
//快排的排序其实是在划分这里
private static int partition(int[] a,int l,int r){
int t=a[l];
while(l<r){
while(l<r && a[r]>=t){
r--;
}
a[l]=a[r];
while(l<r && a[l]<t){
l++;
}
a[r]=a[l];
}
a[l]=t;
return l;
}
private static void quickSort(int[] a,int l,int r){
int len=(r-l+1);
if(len<2){
return;
}
int p=partition(a,l,r);
quickSort(a,l,p);
quickSort(a,p+1,r);
}
/**
* 快速排序,选定中轴进行划分,在递归划分的过程中进行排序
* 平均O(NlogN) 最好O(NlogN) 最坏O(N^2) 空间O(logN) 空间最坏O(N) 不稳定(双指针扫的时候会改变相同数的相对位置)
* @param a
*/
static void quickSort(int[] a){
int n=a.length;
quickSort(a,0,n-1);
}
/**
* 将数组a中,根为节点i的子树大顶堆化
* @param a
* @param i
*/
private static void heapify(int[] a,int i,int n){
int ls=2*i+1;
int rs=2*i+2;
int mx=i;
if(ls<n && a[ls]>a[mx]){
mx=ls;
}
if(rs<n && a[rs]>a[mx]){
mx=rs;
}
if(mx!=i){
swap(a,i,mx);
heapify(a,mx,n);
}
}
/**
* 根据数组a前n个数建立大顶堆
* @param a
*/
private static void buildMaxHeap(int[] a,int n){
//从最后一个非叶子节点开始,自底向上
//最后一个叶子是n-1,最后一个非叶子就是n-1的父节点
for(int i=(n-1)/2-1;i>=0;i--){
heapify(a,i,n);
}
}
/**
* 堆排序,根据数组建立堆(数组模拟),每次取出堆顶放在最后,调整剩下的节点重新成为堆
* 平均O(NlogN) 最好O(NlogN) 最坏O(NlogN) 空间O(1) 不稳定(每次把堆顶放在最后,有跳跃交换)
* @param a
*/
static void heapSort(int[] a){
int n=a.length;
buildMaxHeap(a,n);
for(int i=n-1;i>=0;i--){
swap(a,0,i);
n--;
heapify(a,0,n);
}
}
/**
* 计数排序,适合数据范围较小
* 平均O(n+k) 最好O(n+k) 最坏O(n+k) 空间O(k) 稳定(前缀和优化后属于稳定排序)
* @param a
*/
static void countSort(int[] a){
int n=a.length;
int mn=Integer.MAX_VALUE;
int mx=Integer.MIN_VALUE;
for(int v:a){
mn=min(mn,v);
mx=max(mx,v);
}
int len=mx-mn+1;
int[] cnt=new int[len];
Arrays.fill(cnt,0);
//计数
for(int v:a){
cnt[v-mn]++;
}
//前缀和
for(int i=1;i<len;i++){
cnt[i]+=cnt[i-1];
}
int[] b=new int[n];
//倒序遍历原数组,保证后面的元素排名靠后
for(int i=n-1;i>=0;i--){
b[cnt[a[i]-mn]-1]=a[i];
cnt[a[i]-mn]--;
}
System.arraycopy(a,0,b,0,n);
//不加优化版,不稳定
// int idx=0;
// for(int i=0;i<len;i++){
// while(cnt[i]-->0){
// a[idx++]=i+mn;
// }
// }
}
static ArrayList<Integer> bucketSort(ArrayList<Integer> a, int bucketSize){
int n=a.size();
int mn=Integer.MAX_VALUE;
int mx=Integer.MIN_VALUE;
for(int v:a){
mn=min(mn,v);
mx=max(mx,v);
}
int bucketCount=(mx-mn)/bucketSize+1;
ArrayList<ArrayList<Integer>> bucket=new ArrayList<>(bucketCount);
ArrayList<Integer> ans=new ArrayList<>();
for(int i=0;i<bucketCount;i++){
bucket.add(new ArrayList<>());
}
//装桶
for(int v:a){
bucket.get((v-mn)/bucketSize).add(v);
}
for(int i=0;i<bucketCount;i++){
//每个桶单独排序再合并,可以调用其他排序算法
Collections.sort(bucket.get(i));
ans.addAll(bucket.get(i));
}
return ans;
}
/**
* 桶排序(升级版计数排序)
* 平均O(N+k) 最好O(N) 最坏O(N^2) 空间O(N+k) 稳定
* @param a
*/
static void bucketSort(int[] a){
int n=a.length;
int bucketSize=6;
ArrayList<Integer> al=new ArrayList<>();
for(int v:a){
al.add(v);
}
ArrayList<Integer> ar=bucketSort(al,bucketSize);
for(int i=0;i<n;i++){
a[i]=ar.get(i);
}
}
/**
* 基数排序,按位,每一位分别进行计数排序
* 平均O(N*k) 最好O(N*k) 最坏O(N*k) 稳定
* @param a
*/
static void radixSort(int[] a){
int n=a.length;
int mx=0;
for(int v:a){
mx=max(mx,v);
}
int dig=0;
while(mx>0){
dig++;
mx/=10;
}
ArrayList<ArrayList<Integer>> al=new ArrayList<>();
for(int i=0;i<10;i++){
al.add(new ArrayList<>());
}
for(int i=0,mod=10,div=1;i<dig;i++,mod*=10,div*=10){
//每一位
for(int v:a){
int x=(v%mod)/div;
al.get(x).add(v);
}
int idx=0;
for(int j=0;j<10;j++){
int siz=al.get(j).size();
for(int k=0;k<siz;k++){
a[idx++]=al.get(j).get(k);
}
al.get(j).clear();
}
}
}
public static void main(String[] args) {
int[] a={1,8,6,7,15,11,4,2,13,14,12,10,5,9,3,14,8,8,6,11};
// bubbleSort(a);
// selectSort(a);
// insertSort(a);
// shellSort(a);
// mergeSort(a);
// quickSort(a);
// heapSort(a);
// countSort(a);
// bucketSort(a);
// radixSort(a);
print(a);
}
}
快排平均是O(logN) 最坏是O(N),而归并空间是O(N)。
两者的区别在于快排是先partition,然后再递归下去,每层递归都会需要partition的临时空间O(1),加起来就是O(logN),如果退化到冒泡,就是O(N)。
而归并是先两个sort递归下去,然后再merge,每层递归用的临时空间都是一样的O(N)级别。
标签:双指针 return ble 父节点 一个 希尔 基数排序 length EAP
原文地址:https://www.cnblogs.com/zxcoder/p/12259437.html