十大排序总结

总结在这里插入图片描述
1.冒泡排序
【实现】

//冒泡排序
	public static void bubbleSort(int[] arr){
		if(arr== null || arr.length<2){
			return;
		}
		for(int end = arr.length-1;end>0;end--){//O(n)
			for(int i = 0;i<end;i++){//O(n)
				if(arr[i]>arr[i+1]){
					swap(arr,i,i+1);
				}
			}
		}
	}

【结果】<时间复杂度O(n^2) 空间复杂度O(1),稳定>
在这里插入图片描述
【特点】
每一趟排序最大的到最后面(沉底)

优化1:定义一个flag,用来判断有没有进行交换,如果在某一次内层循环中没有交换操作,就说明此时数组已经是有序的了,不用再进行判断,可以节省时间。
优化2:每一次交换记录最后一次交换的位置为0时候就停止
优化3:鸡尾酒排序,从低到高,再从高到低排序。

2.选择排序
【实现】

//选择排序
	public static void selectSort(int[] arr){
		if(arr == null ||arr.length < 2) return;
		for(int i = 0;i<arr.length;i++){//O(n)
			int minIndex= i;
			for(int j = i+1;j<arr.length;j++){//O(n)
				minIndex = arr[j]<arr[minIndex] ? j:minIndex;
			}
			swap(arr,i,minIndex);
		}
	}

【结果】<时间复杂度O(n^2) 空间复杂度O(1),不稳定>
在这里插入图片描述
【特点】
每一趟排序都找到最小值

3.插入排序
【实现】

//插入排序
	public static void insertSort(int[] arr){
		if(arr == null || arr.length < 2) return;
		for(int i = 1;i<arr.length;i++){//O(n)
			for(int j = i-1;j>=0 && arr[j]>arr[j+1];j--){//O(n)
				swap(arr,j,j+1);
			}
		}
	}

【结果】<时间复杂度O(n^2) 空间复杂度O(1),稳定>
在这里插入图片描述
【特点】
每次找到下一个数字在前面已经排好序的应该在的位置

4.希尔排序
【实现】

//希尔排序
	public static void shellSort(int[] arr){
		if(arr == null ||arr.length < 2) return;
		int n = arr.length;
		int gap = n/2;
		while(gap > 0){
			for(int j = gap;j<n;j++){
				int i = j;
				while(i>=gap && arr[i-gap]>arr[i]){
					swap(arr,i-gap,i);
					i -= gap;
				}
			}
			gap = gap/2;
		}
	}

【结果】<时间复杂度O(nlogn) 空间复杂度O(nlog^2 n),不稳定>
在这里插入图片描述

5.归并排序
【实现】

//归并排序
	public static void mergeSort(int[] arr){
		if(arr ==null ||arr.length<2) return;
		sortProcess(arr,0,arr.length-1);
	}
	public static void sortProcess(int[] arr,int L,int R){
		if(L == R) return;
		if(L<R){
			int mid = (L+R)/2;
			sortProcess(arr,L,mid);
			sortProcess(arr,mid+1,R);
			merge(arr,L,mid,R);
		}
	}
	public static void merge(int[] arr,int L,int mid,int R){
		int[] help = new int[R-L+1];
		int i = 0;
		int p1 = L;
		int p2 = mid+1;
		while(p1 <= mid && p2<=R){
			help[i++] = arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
		}
		//两个必有一个越界
		while(p1<=mid){
			help[i++] = arr[p1++];
		}
		while(p2<=R){
			help[i++] = arr[p2++];
		}
		for(i=0;i<help.length;i++){
			arr[L+i] = help[i];
		}
	}

【结果】<时间复杂度O(nlogn) 空间复杂度O(n),稳定>
在这里插入图片描述

扫描二维码关注公众号,回复: 5855200 查看本文章

6.快速排序
【实现】

//快速排序
	public static void quickSort(int[] arr){
		if(arr == null || arr.length < 2){
			return;
		}
		quickSort(arr,0,arr.length-1);
	}
	public static void quickSort(int[] arr,int l,int r){
		if(l < r){
			swap(arr,l+(int)Math.random()*(r-l+1),r);
			int[] p = partition(arr,l,r);
			quickSort(arr,l,p[0] - 1);
			quickSort(arr,p[1] + 1,r);
		}
	}
	public static int[] partition(int[] arr,int l,int r){
		int less = l-1;
		int more = r;
		while(l < more){
			if(arr[l] < arr[r]){
				swap(arr,++less,l++);
			}else if(arr[l] >arr[r]){
				swap(arr,--more,l);
			}else{
				l++;
			}
		}
		swap(arr,more,r);
		return new int[] {less+1,more};
	}

【结果】<时间复杂度O(nlogn) 空间复杂度O(logn),不稳定>
在这里插入图片描述
在这里插入图片描述
【特点】
找到一个基准数,左右哨兵,从右往左走,遇到小于基准数的放左边,从左往右走,遇到大于基准数的放到右边,一趟结果为基准数左边的比基准数小,基准数右边的比基准数大。
7.堆排序
【实现】

//堆排序
	public static void heapSort(int[] arr){
		if(arr == null || arr.length < 2){
			return;
		}
		for(int i = 0;i < arr.length;i++){
			heapInsert(arr,i);
		}
		int size = arr.length;
		swap(arr,0,--size);
		while(size>0){
			heapify(arr,0,size);
			swap(arr,0,--size);
		}
	}
		
	public static void heapInsert(int[] arr,int index){
		while(arr[index] >arr[(index-1)/2]){
			swap(arr,index,(index-1)/2);
			index = (index-1)/2;
		}
	}
	
	public static void heapify(int[] arr,int index,int size){
		int left = index*2+1;
		while(left<size){
			int largest = left +1<size && arr[left+1]>arr[left]?left+1:left;
			largest = arr[largest]>arr[index]?largest:index;
			if(largest == index){
				break;
			}
			swap(arr,largest,index);
			index = largest;
			left = index*2+1;
		}
	}

【结果】<时间复杂度O(nlogn) 空间复杂度O(1),不稳定>
在这里插入图片描述
在这里插入图片描述
8.计数排序
【实现】

//计数排序
	/*
	 * 计数排序有如下四个步骤。
	 * 		首先会对每个输入进行频率统计,得到元素的频率表;
	 * 		然后将频率表转换为该元素的开始索引;
	 * 		根据各个元素的开始索引,将相同元素分类到临时数组中。
	 * 		最后将临时数组中的元素写回到原数组中
	 */
	public static void countingSort(int[] arr){
		//计算最大最小值,用ifPresent检查一下
		int max = Arrays.stream(arr).max().getAsInt();
		int min = Arrays.stream(arr).min().getAsInt();
		int N = arr.length;
		int R = max - min +1;//最大最小元素之间范围【min,max】的长度
		//1.计算频率,再需要的数组长度上额外加1
		int[] count = new int[R+1];
		for(int i = 0;i < N;i++){
			//使用加1 后的索引,有重复的该位置就自增
			count[arr[i] - min + 1]++;
		}
		//2.频率 -> 元素的开始索引
		for(int r = 0;r < R;r++){
			count[r+1] += count[r];//小于当前元素r+1的个数等于上一个元素的个数(count[r+1])与小于上一个元素的个数(count[r])之和
		}
		//3.元素按照开始索引分类,用到一个和待排数组一样大临时数组存放数据
		int[] aux = new int[N];
		for(int i = 0;i<N;i++){
			//填充一个数据后,自增,以便相同的数据可以填到下一个空位
			aux[count[arr[i]-min]++]=arr[i];
		}
		//4.数据回写
		for(int i = 0;i<N;i++){
			arr[i] = aux[i];
		}
	}

【结果】<时间复杂度O(n+k) 空间复杂度O(k),稳定>
在这里插入图片描述

9.桶排序
【实现】

/*
	 * 思想:
	 * 根据输入建立适当个数的桶,每个桶可以存放某个范围内的元素;
	 * 将落在特定范围内的所有元素放入对应的桶中;
	 * 对每个非空的桶中元素进行排序,可以选择通用的排序方法,比如插入、快排;
	 * 按照划分的范围顺序,将桶中的元素依次取出。排序完成。
	 */
	/*
	 * 桶的数量为数组长度arr.length
	 * 映射函数使用bucketIndex =(value * arr.length)/(maxvalue+1),加1是为了保证最大元素可以存到数组最后一个位置
	 */
	public static void BucketSort(int[] arr){
		//建立桶,个数和待排序的数组长度一样
		int N = arr.length;
		LinkedList<Integer>[] bucket = (LinkedList<Integer>[]) new LinkedList[N];
		
		//待排序数组中的最大值
		int maxValue = Arrays.stream(arr).max().getAsInt();
		System.out.println(maxValue);
		//根据每个元素的值,分配到对应范围的桶中
		for(int i = 0;i < arr.length;i++){
			int index = toBucketIndex(arr[i],maxValue,N);
			System.out.println("放入"+index+"号桶");
			//没有桶才建立(延时)
			if(bucket[index] == null){
				bucket[index] = new LinkedList<>();
			}
			//有桶直接使用
			bucket[index].add(arr[i]);
		}
		//对每个非空的桶排序,排序后顺便存入临时的List,则list中已经有序
		List<Integer> temp = new ArrayList<>();
		for(int i = 0;i<N;i++){
			if(bucket[i] != null){
				Collections.sort(bucket[i]);
				temp.addAll(bucket[i]);
			}
			System.out.println("加入"+i+"号桶");
			System.out.println(temp);
		}
		System.out.println();
		System.out.println("最终");
		System.out.println(temp);
		//将temp中的数据写入原数组
		for(int i = 0;i<N;i++){
			arr[i] = temp.get(i);
		}
		System.out.println();
		printList(arr);
	}
	
	//映射函数,将值转换为对应存放到的同数组的索引
	private static int toBucketIndex(int value,int maxValue,int N){
		return (value * N)/(maxValue + 1);
	}

【结果】<时间复杂度O(n+k) 空间复杂度O(n+k),稳定>
在这里插入图片描述
在这里插入图片描述
【特点】
待排序的数据要分散一些

10.基数排序
【实现】

//基数排序
	public static void RadixSort(int[] arr){
		//每位数字范围0~9,基位10
		int R = 10;
		int N = arr.length;
		int[] aux = new int[N];
		int[] count = new int[R+1];
		
		//以关键字来排序的轮数,由位数最多的数字决定,其余位数少的数字在比较高位时,自动用0进行比较
		//将数字转换成字符串,字符串的长度就是数字的位数,字符串最长的哪个数字也拥有最多的位数
		int W = Arrays.stream(arr).map(s -> String.valueOf(s).length()).max().getAsInt();
		//共需要d轮计数排序,从d = 0开始,说明时从个位开始比较,符号从右到左的顺序
		for(int d = 0;d < W;d++){
			//1.计算频率,在需要的数组长度上额外加1
			for(int i = 0;i<N;i++){
				//使用加1后的索引,有重复的该位置就自增
				count[digitAt(arr[i],d)+1]++;
			}
			//2.频率 ->元素的开始索引
			for(int r = 0;r<R;r++){
				count[r+1]+=count[r];
			}
			
			//3.元素按照开始索引分类,用到一个和待排序数组一样大临时数组存放数据
			for(int i = 0;i<N;i++){
				//填充一个数据后,自增,以便相同的数据可以填到下一个空为
				aux[count[digitAt(arr[i],d)]++] = arr[i];
			}
			//4.数据回写
			for(int i= 0;i<N;i++){
				arr[i] = aux[i];
			}
			//重置count[],以便下一轮统计使用
			for(int i =0;i<count.length;i++){
				count[i] = 0;
			}
		}
		System.out.println();
		printList(arr);
	}
	//根据d,获取某个值的个位,十位,百位等,d = 0取出个位,d = 1取出十位,一次类推,对于不存在的高位,用0补
	private static int digitAt(int value,int d){
		return (value / (int) Math.pow(10, d))%10;
	}

【结果】<时间复杂度O(n*k) 空间复杂度O(n+k),稳定>
在这里插入图片描述
【整体代码】

package 不会的东西;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class 排序 {
	
	public static void main(String[] args) {
		int[] arr = {4,3,9,8,7,1,5,10,2,6};
		System.out.print("原数组为:");
		printList(arr);
		System.out.println();
		System.out.println("=================");
		//System.out.println("冒泡排序为:");
		//bubbleSort(arr);
		//System.out.println();
		//System.out.println("选择排序为:");
		//selectSort(arr);
		//System.out.println();
		//System.out.println("插入排序为:");
		//insertSort(arr);
		//System.out.println();
		//System.out.println("希尔排序为:");
		//shellSort(arr);
		//System.out.println();
		//System.out.println("归并排序为:");
		//mergeSort(arr);
		//System.out.println();
		//System.out.println("快速排序为:");
		//quickSort(arr);
		//System.out.println();
		//System.out.println("堆排序为:");
		//heapSort(arr);
		//System.out.println();
		//System.out.println("计数排序为:");
		//countingSort (arr);
		int[] arr1 = {44, 67, 32, 21, 9, 98, 44, 111, 99, 6};
		System.out.println();
		System.out.print("原数组为:");
		printList(arr1);
		//System.out.println();
		//System.out.println("桶排序为:");
		//BucketSort(arr1);
		int[] arr2 = {244, 167, 1234, 321, 29, 98, 1444, 111, 99, 6};
		System.out.println();
		System.out.print("原数组为:");
		printList(arr2);
		System.out.println();
		System.out.println("基数排序为:");
		RadixSort(arr2);
	}
	
	//冒泡排序
	public static void bubbleSort(int[] arr){
		if(arr== null || arr.length<2){
			return;
		}
		for(int end = arr.length-1;end>0;end--){
			for(int i = 0;i<end;i++){
				if(arr[i]>arr[i+1]){
					swap(arr,i,i+1);
				}
			}
			System.out.println();
			printList(arr);
		}
	}
	
	//选择排序
	public static void selectSort(int[] arr){
		if(arr == null ||arr.length < 2) return;
		for(int i = 0;i<arr.length;i++){
			int minIndex= i;
			for(int j = i+1;j<arr.length;j++){
				minIndex = arr[j]<arr[minIndex] ? j:minIndex;
			}
			swap(arr,i,minIndex);
			System.out.println();
			printList(arr);
		}
	}
	//插入排序
	public static void insertSort(int[] arr){
		if(arr == null || arr.length < 2) return;
		for(int i = 1;i<arr.length;i++){
			for(int j = i-1;j>=0 && arr[j]>arr[j+1];j--){
				swap(arr,j,j+1);
			}
			System.out.println();
			printList(arr);
		}
	}
	
	//希尔排序
	public static void shellSort(int[] arr){
		if(arr == null ||arr.length < 2) return;
		int n = arr.length;
		int gap = n/2;
		while(gap > 0){
			for(int j = gap;j<n;j++){
				int i = j;
				while(i>=gap && arr[i-gap]>arr[i]){
					swap(arr,i-gap,i);
					i -= gap;
				}
			}
			System.out.println();
			System.out.println(gap);
			printList(arr);
			System.out.println();
			gap = gap/2;
		}
	}
	//归并排序
	public static void mergeSort(int[] arr){
		if(arr ==null ||arr.length<2) return;
		sortProcess(arr,0,arr.length-1);
	}
	public static void sortProcess(int[] arr,int L,int R){
		if(L == R) return;
		if(L<R){
			int mid = (L+R)/2;
			sortProcess(arr,L,mid);
			System.out.print("L="+L);
			System.out.print("mid="+mid);
			sortProcess(arr,mid+1,R);
			System.out.print("R="+R);
			System.out.println("mid+1="+(mid+1));
			merge(arr,L,mid,R);
		}
	}
	public static void merge(int[] arr,int L,int mid,int R){
		
		int[] help = new int[R-L+1];
		int i = 0;
		int p1 = L;
		int p2 = mid+1;
		while(p1 <= mid && p2<=R){
			help[i++] = arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
		}
		//两个必有一个越界
		while(p1<=mid){
			help[i++] = arr[p1++];
		}
		while(p2<=R){
			help[i++] = arr[p2++];
		}
		for(i=0;i<help.length;i++){
			arr[L+i] = help[i];
		}
		//System.out.println();
		printList(arr);
		System.out.println();
		System.out.println();
	}
	//快速排序
	public static void quickSort(int[] arr){
		if(arr == null || arr.length < 2){
			return;
		}
		quickSort(arr,0,arr.length-1);
		printList(arr);
	}
	
	public static void quickSort(int[] arr,int l,int r){
		if(l < r){
			System.out.print("l = "+l);
			System.out.println("r = "+r);
			printList(arr);
			System.out.println();
			System.out.println();
			swap(arr,l+(int)Math.random()*(r-l+1),r);
			int[] p = partition(arr,l,r);
			quickSort(arr,l,p[0] - 1);
			quickSort(arr,p[1] + 1,r);
		}
		
	}
	
	public static int[] partition(int[] arr,int l,int r){
		int less = l-1;
		int more = r;
		while(l < more){
			if(arr[l] < arr[r]){
				swap(arr,++less,l++);
			}else if(arr[l] >arr[r]){
				swap(arr,--more,l);
			}else{
				l++;
			}
		}
		swap(arr,more,r);
		return new int[] {less+1,more};
	}
	
	//堆排序
	public static void heapSort(int[] arr){
		if(arr == null || arr.length < 2){
			return;
		}
		for(int i = 0;i < arr.length;i++){
			heapInsert(arr,i);
		}
		int size = arr.length;
		swap(arr,0,--size);
		while(size>0){
			heapify(arr,0,size);
			swap(arr,0,--size);
		}
	}
		
	public static void heapInsert(int[] arr,int index){
		while(arr[index] >arr[(index-1)/2]){
			swap(arr,index,(index-1)/2);
			index = (index-1)/2;
		}
		System.out.println("heapInsert");
		printList(arr);
		System.out.println();
	}
	
	public static void heapify(int[] arr,int index,int size){
		int left = index*2+1;
		while(left<size){
			int largest = left +1<size && arr[left+1]>arr[left]?left+1:left;
			largest = arr[largest]>arr[index]?largest:index;
			if(largest == index){
				break;
			}
			swap(arr,largest,index);
			index = largest;
			left = index*2+1;
		}
		System.out.println("heapify");
		printList(arr);
		System.out.println();
	}
	
	//计数排序
	/*
	 * 计数排序有如下四个步骤。
	 * 		首先会对每个输入进行频率统计,得到元素的频率表;
	 * 		然后将频率表转换为该元素的开始索引;
	 * 		根据各个元素的开始索引,将相同元素分类到临时数组中。
	 * 		最后将临时数组中的元素写回到原数组中
	 */
	public static void countingSort(int[] arr){
		//计算最大最小值,用ifPresent检查一下
		int max = Arrays.stream(arr).max().getAsInt();
		int min = Arrays.stream(arr).min().getAsInt();
		int N = arr.length;
		int R = max - min +1;//最大最小元素之间范围【min,max】的长度
		//1.计算频率,再需要的数组长度上额外加1
		int[] count = new int[R+1];
		for(int i = 0;i < N;i++){
			//使用加1 后的索引,有重复的该位置就自增
			count[arr[i] - min + 1]++;
		}
		System.out.println();
		System.out.println("count1 加1后的索引");
		printList(count);
		//2.频率 -> 元素的开始索引
		for(int r = 0;r < R;r++){
			count[r+1] += count[r];//小于当前元素r+1的个数等于上一个元素的个数(count[r+1])与小于上一个元素的个数(count[r])之和
		}
		System.out.println();
		System.out.println("元素的开始索引");
		printList(count);
		//3.元素按照开始索引分类,用到一个和待排数组一样大临时数组存放数据
		int[] aux = new int[N];
		for(int i = 0;i<N;i++){
			//填充一个数据后,自增,以便相同的数据可以填到下一个空位
			aux[count[arr[i]-min]++]=arr[i];
		}
		System.out.println();
		System.out.println("辅助数组");
		printList(aux);
		//4.数据回写
		for(int i = 0;i<N;i++){
			arr[i] = aux[i];
		}
		System.out.println();
		System.out.println("写回数组");
		printList(arr);
	}
	
	/*
	 * 思想:
	 * 根据输入建立适当个数的桶,每个桶可以存放某个范围内的元素;
	 * 将落在特定范围内的所有元素放入对应的桶中;
	 * 对每个非空的桶中元素进行排序,可以选择通用的排序方法,比如插入、快排;
	 * 按照划分的范围顺序,将桶中的元素依次取出。排序完成。
	 */
	/*
	 * 桶的数量为数组长度arr.length
	 * 映射函数使用bucketIndex =(value * arr.length)/(maxvalue+1),加1是为了保证最大元素可以存到数组最后一个位置
	 */
	public static void BucketSort(int[] arr){
		//建立桶,个数和待排序的数组长度一样
		int N = arr.length;
		LinkedList<Integer>[] bucket = (LinkedList<Integer>[]) new LinkedList[N];
		
		//待排序数组中的最大值
		int maxValue = Arrays.stream(arr).max().getAsInt();
		System.out.println(maxValue);
		//根据每个元素的值,分配到对应范围的桶中
		for(int i = 0;i < arr.length;i++){
			int index = toBucketIndex(arr[i],maxValue,N);
			System.out.println("放入"+index+"号桶");
			//没有桶才建立(延时)
			if(bucket[index] == null){
				bucket[index] = new LinkedList<>();
			}
			//有桶直接使用
			bucket[index].add(arr[i]);
		}
		//对每个非空的桶排序,排序后顺便存入临时的List,则list中已经有序
		List<Integer> temp = new ArrayList<>();
		for(int i = 0;i<N;i++){
			if(bucket[i] != null){
				Collections.sort(bucket[i]);
				temp.addAll(bucket[i]);
			}
			System.out.println("加入"+i+"号桶");
			System.out.println(temp);
		}
		System.out.println();
		System.out.println("最终");
		System.out.println(temp);
		//将temp中的数据写入原数组
		for(int i = 0;i<N;i++){
			arr[i] = temp.get(i);
		}
		System.out.println();
		printList(arr);
	}
	
	//映射函数,将值转换为对应存放到的同数组的索引
	private static int toBucketIndex(int value,int maxValue,int N){
		return (value * N)/(maxValue + 1);
	}

	//基数排序
	public static void RadixSort(int[] arr){
		//每位数字范围0~9,基位10
		int R = 10;
		int N = arr.length;
		int[] aux = new int[N];
		int[] count = new int[R+1];
		
		//以关键字来排序的轮数,由位数最多的数字决定,其余位数少的数字在比较高位时,自动用0进行比较
		//将数字转换成字符串,字符串的长度就是数字的位数,字符串最长的哪个数字也拥有最多的位数
		int W = Arrays.stream(arr).map(s -> String.valueOf(s).length()).max().getAsInt();
		//共需要d轮计数排序,从d = 0开始,说明时从个位开始比较,符号从右到左的顺序
		for(int d = 0;d < W;d++){
			//1.计算频率,在需要的数组长度上额外加1
			for(int i = 0;i<N;i++){
				//使用加1后的索引,有重复的该位置就自增
				count[digitAt(arr[i],d)+1]++;
			}
			//2.频率 ->元素的开始索引
			for(int r = 0;r<R;r++){
				count[r+1]+=count[r];
			}
			
			//3.元素按照开始索引分类,用到一个和待排序数组一样大临时数组存放数据
			for(int i = 0;i<N;i++){
				//填充一个数据后,自增,以便相同的数据可以填到下一个空为
				aux[count[digitAt(arr[i],d)]++] = arr[i];
			}
			//4.数据回写
			for(int i= 0;i<N;i++){
				arr[i] = aux[i];
			}
			//重置count[],以便下一轮统计使用
			for(int i =0;i<count.length;i++){
				count[i] = 0;
			}
		}
		System.out.println();
		printList(arr);
	}
	//根据d,获取某个值的个位,十位,百位等,d = 0取出个位,d = 1取出十位,一次类推,对于不存在的高位,用0补
	private static int digitAt(int value,int d){
		return (value / (int) Math.pow(10, d))%10;
	}
	
	
	
	
	
	//交换两个数字
	public static void swap(int[] arr,int i,int j){
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	//打印数组
	public static void printList(int[] arr){
		System.out.print("[");
		for(int i = 0;i<arr.length;i++){
			if(i < arr.length-1){
				System.out.print(+arr[i]);
				System.out.print(",");
			}else if(i == arr.length-1){
				System.out.print(+arr[i]);
			}
			
		}
		System.out.print("]");
	}

}

猜你喜欢

转载自blog.csdn.net/weixin_39795049/article/details/89111633