目录
稳定性
排完序之后数据的相对顺序未发生改变,那么排序算法就具备稳定性
插入排序
直接插入排序
时间复杂度:o(N^2),空间复杂度o(1),稳定的
public static void sort(int []arr){
for (int i = 1; i < arr.length; i++) {
for (int j=i;j>=1;j--){
if(arr[j]<arr[j-1]){
int tmp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=tmp;
}else
break;
}
}
}
折半插入排序
时间复杂度:o(N^2),空间复杂度o(1),稳定的
插入元素之前,元素有序,通过二分查找到插入位置
public static void sort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
if (arr[i] < arr[i - 1]) {
//通过二分查找 找到插入位置
int left = 0;
int right = i - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] > arr[i]) {
right = mid - 1;
} else
left = mid + 1;
}
int tmp = arr[i];
for (int j = i; j > left; j--) {
arr[j] = arr[j - 1];
}
arr[left] = tmp;
}
}
}
希尔排序
时间复杂度:和增量有关,约o(N^1.3-N^1.5)
空间复杂度o(1),非稳定的排序算法
直接插入排序在数据基本有序的情况下时间复杂度小小
希尔排序是对直接插入排序的优化,通过分组,使元素趋于有序
public static void sort(int []arr,int d) {
for (int j = d; j < arr.length; j++) {
for (int k = j - d; k >= 0; k -= d) {
if (arr[k] > arr[k + d]) {
int tmp = arr[k];
arr[k] = arr[k + d];
arr[k + d] = tmp;
}
}
}
}
public static void shell(int []array) {
//设计步长
int i=array.length/2;
while(i>1) {
sort(array, i);
i/=2;
}
//最后进行直接插入排序
sort(array,1);
}
选择排序
直接选择排序
时间复杂度:o(N^2),空间复杂度o(1),不稳定的
public static void sort(int []arr){
for (int i = 0; i <arr.length ; i++) {
for (int j = i+1; j < arr.length; j++) {
if(arr[j]<arr[i]){
int tmp=arr[j];
arr[j]=arr[i];
arr[i]=tmp;
}
}
}
}
冒泡排序
时间复杂度:o(N^2),空间复杂度o(1),稳定的
每一选择最大或者最小的元素放到最后
(最后一趟,元素已经有序了 N个元素,走N-1趟)
public static void sort(int []arr) {
for (int i = 0; i < arr.length-1 ; i++) {
int flag=0;
for (int j = 0; j < arr.length-1-i; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
flag=1;
}
}
if(flag==0)
break;
}
}
快速排序
- 从数列中挑出一个元素作为基准。
- 重新排列数列,把所有的比基准小的放在基准前面,反之放在后面(一样大可任意一边)完成后基准处在分区的中间位置。
- 通过递归调用把小于基准元素和大雨基准元素的子序列进行排序
时间复杂度 最好:O(N*log2 N)最坏(数据有序)o(N^2)
空间复杂度O(log2N) 最坏o(N)
不稳定的排序
public static void sort(int []arr){
quicksort(arr,0,arr.length-1);
}
public static void quicksort(int []arr,int start,int end){
if(start>end)
return;
int index= position(arr,start,end);
quicksort(arr,start,index-1);//左边
quicksort(arr,index+1,end);//右边
}
public static int position(int []arr,int start,int end){
int tmp=arr[start];
while(start<end){
while(start<end&&arr[end]>=tmp){
end--;
}
//arr[end]找到了<tmp的值
arr[start]=arr[end];
while(start<end&&arr[start]<=tmp){
start++;
}
//arr[start]找到了>tmp的值
arr[end]=arr[start];
}
//start和end相遇了
arr[start]=tmp;
return start;
}
对于快排的优化:元素有序且数据过多时,递归深度太深,栈溢出
三数取中法:
arr【left】,arr【right】,arr【mid】找到中间值的下标
public static void sort(int[] arr) {
quicksort(arr, 0, arr.length - 1);
}
public static int find(int []arr,int start,int end) {
int mid = (start + end) /2;
if(arr[start]<arr[end]){//3 ? 8
if(arr[mid]>arr[end])//3 10 8
return end;
else if(arr[start]<arr[mid])//3 6 8
return mid;
else//3 1 8
return start;
}
else{//8 ? 3
if(arr[mid]>arr[start])// 8 10 3
return start;
else if(arr[mid]<arr[end])// 8 1 3
return end;
else //8 5 3
return mid;
}
}
public static void quicksort(int []arr,int start,int end) {
if (end < start)
return;
// 找基准之前,知道中间值下标
int index = find(arr, start, end);
int tmp = arr[start];
arr[start] = arr[index];
arr[index] = tmp;
int x = position(arr, start, end);
quicksort(arr, x + 1, end);
quicksort(arr, start, x - 1);
}
public static int position(int []arr,int start,int end) {
int x = arr[start];
while (start < end) {
while (end > start && arr[end] >= x)
end--;
arr[start] = arr[end];
while (end > start && arr[start] <= x)
start++;
arr[end] = arr[start];
}
arr[start] = x;
return start;
}
public static void main(String[] args) {
int []array =new int[100000];
for (int i = 0; i <100000 ; i++) {
array[i]=i;
}
sort(array);
for (int j : array) {
System.out.print(j + " ");
}
}
}
快排的非递归实现
public static void sortStack(int []arr,int start,int end){
Stack<Integer>stack=new Stack<>();
stack.push(end);
stack.push(start);
while(!stack.isEmpty()) {
int left = stack.pop();
int right = stack.pop();
int pivor = position(arr, left, right);
if (pivor - left > 1) {//左区间有两个以上的元素
stack.push(pivor - 1);
stack.push(left);
}
if (right - pivor > 1) {//右区间有两个以上的元素
stack.push(end);
stack.push(pivor + 1);
}
}
}
堆排序
时间复杂度:o(N^log2 N),空间复杂度o(1),不稳定
public static void sort(int []arr){
creat(arr);
int end=arr.length-1;
int sz=arr.length;
while (end>=0){
int tmp=arr[end];
arr[end]=arr[0];
arr[0]=tmp;
shifdown(arr,0,end);
end--;
}
}
public static void shifdown(int []arr,int parent,int sz) {
int left = parent * 2 + 1;
while (left <sz) {
if (left + 1 <sz) {
if (arr[left + 1] > arr[left])
left++;
}
if (arr[parent] < arr[left]) {
int tmp = arr[parent];
arr[parent] = arr[left];
arr[left] = tmp;
parent = left;
left = parent * 2 + 1;
}else
break;
}
}
public static void creat(int []arr){//创建大根堆
for (int parent =(arr.length-2)/2 ; parent >=0 ; parent--) {
shifdown(arr,parent,arr.length);
}
}
归并排序
时间复杂度:o(N^log2 N),空间复杂度o(N),稳定
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
基础:实现两个有序数组合并
public static int [] add(int []nums1,int []nums2){
int []arr=new int[nums1.length+nums2.length];
int i=0;int j=0,k=0;
//nums1[1,2,3] nums2[2,3]
while(i<nums1.length&&j< nums2.length){
if(nums1[i]<nums2[j]){
arr[k++]=nums1[i++];
}else
arr[k++]=nums2[j++];
}
while(j<nums2.length){
arr[k++]=nums2[j++];
}
while(i<nums1.length){
arr[k++]=nums1[i++];
}
return arr;
}
//0 1 2 3 [start,end]
public static void mergeadd(int[] arr, int start, int mid, int end) {
int[] array = new int[end - start + 1];
int i = start;
int j = mid + 1, k = 0;
//nums1[1,2,3] nums2[2,3]
while (i <= mid && j <= end) {
if (arr[i] < arr[j]) {
array[k++] = arr[i++];
} else
array[k++] = arr[j++];
}
while (j <= end) {
array[k++] = arr[j++];
}
while (i <= mid) {
array[k++] = arr[i++];
}
//开始位置时start,每次是0导致覆盖
System.arraycopy(array, 0, arr, start, array.length);
}
public static void merge(int[] arr, int start, int end) {
if (start >= end)
return;
int mid = (start + end) / 2;//1 0
//左
merge(arr, start, mid);//0 1 0 0
//右
merge(arr, mid + 1, end);
//合并
mergeadd(arr, start, mid, end);
}
public static void mergeSort(int[] arr) {
merge(arr, 0, arr.length - 1);
}
public static void main(String[] args) {
int[] nums1 = {1, 8, 3, 7, 5, 9, 6, 0};
mergeSort(nums1);
System.out.println(Arrays.toString(nums1));
}
非递归实现:按照递归的思想,每组元素个数减半,直到每个数组元素都只有一个之后,开始数组元素*2合并
public static void usualmerge(int[] arr) {
int gps=1;
while(gps<arr.length){
for (int i = 0; i < arr.length; i+=gps*2) {
int start=i;
int mid=i+gps-1;
if(mid>arr.length-1)//
mid=arr.length-1;
int end =i+2*gps-1;
if(end>arr.length-1)//
end=arr.length-1;
mergeadd(arr,start,mid,end);
}
gps*=2;
}
}
计数排序
时间复杂度O(N),空间复杂度O(M) 本质上稳定
public static void countSort(int[] arr) {
int max = arr[0], min = max;
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
//610 min=600 nums[10]=1
int[] nums = new int[max - min + 1];
for (int i = 0; i < arr.length; i++) {
nums[arr[i] - min]++;
}
int j = 0;
for (int i = 0; i < nums.length; i++) {
for (int k = 0; k < nums[i]; k++) {
arr[j] = i + min;
j++;
}
}
}
基数排序
//最高位为D,那么执行D次//排序数组有N个元素//收集桶中的元素到数组中
时间复杂度:给定N个D位数(取值范围为R),基数排序需要比较元素的每一位,则复杂度为O(D(N+R)),其中一轮循环分配时间复杂度为O(N),一轮循环收集时间复杂度为O(R),共需要d次循环来进行分配收集
时间复杂度:O(D(N+R)),空间复杂度O(N*R),稳定
//基数排序
public static int getLength(int []arr,int mod){
int max=0;
for (int j:arr) {
int s=0;
while(j!=0){
j/=mod;
s++;
}
max=Math.max(s,max);
}
return max;
}
public static void copy(int [][]count,int []arr,int mod){
int k=0;
for (int i = 0; i <mod*2 ; i++) {
for (int j = 0; j <count[i].length ; j++) {
arr[k++]=count[i][j];
}
}
}
public static void creat(int [][]count,int index,int value ) {
count[index] = Arrays.copyOf(count[index], count[index].length + 1);
count[index][count[index].length-1]=value;
}
public static void sort(int []arr){
int mod=10;
//如果有负数,一共mod*2
int [][]count=new int[mod*2][0];
int length=getLength(arr,mod);
int div=1;
for (int i = 0; i < length; i++) {//最高有几位数,就要执行几趟
for (int j = 0; j < arr.length; j++) {
int num = arr[j];
num /= div;
int index = (num % mod)+mod-1;//如果有负数 [0,9],[10,19]
// 两位数12 /100=0,
creat(count, index, arr[j]);
}
div*=10;
//每一次读完了,数组被重新赋值
copy(count,arr,mod);
//count也全部置空
count=new int[mod*2][0];
}
}
桶排序
时间复杂度:根据调用排序的方法而不同
桶排序:每个桶存储一定范围的数值,分别对桶内元素排序
public static void Sort(int []arr){
int mod=4;
//4个桶,装-100~100的数字
int [][]count=new int [mod][0];
for (int i = 0; i < arr.length; i++) {
if(arr[i]<-50){
set(count,0,arr[i]);
}else if(arr[i]>=-50&&arr[i]<0) {
set(count, 1, arr[i]);
} else if(arr[i]>=0&&arr[i]<50) {
set(count, 2, arr[i]);
} else
set(count,3,arr[i]);
}
int j=0;
for (int i = 0; i <mod ; i++) {
quicksort(count[i],0,count[i].length-1);
System.arraycopy(count[i],0,arr,j,count[i].length);
j+=count[i].length;
}
}
总结
外部排序:内存只有1G,需要排序的元素有10G,那么需要借助磁盘等外部设备存储排序
(数据分为多个文件,使每个文件内的元素有序,使用归并排序将所有文件归并为大文件)