Java 常用排序算法的实现(动图+代码+带详细备注)

1.常用的排序算法

2.冒泡排序
在这里插入图片描述

/**
 * 冒泡排序
 * 思路:相邻两两比较,不断的将整个数组中最大的数找出来,确定在最后。
 * @param test
 * @return
 */
public static int[] bubblesort(int []test){
	int []arr=Arrays.copyOf(test, test.length);
	int len=arr.length;
	//外层循环:每次冒泡都能找到当前最大的数,确定最大数的位置,所以总共需要循环len-1次
	for(int i=1;i<len;i++){
		//内层循环:如果相邻的两个元素(前大后小)则两两交换,否则继续,直至最后(不包含已完成的位置)
		//(第i次排序确定了后面i位的位置,将后i位排除在外)
		for(int j=0;j<len-i;j++){
			if(arr[j]>arr[j+1]){
				int tmp=arr[j];
				arr[j]=arr[j+1];
				arr[j+1]=tmp;
			}
		}
	}
	return arr;
}

/**
 * 增强冒泡排序
 * 思路:在冒泡排序的基础上,若某一次内层循环未发生交换,即表示整体有序,提前退出
 * @param test
 * @return
 */
public static int[] bubblesort_pro(int []test){
	int []arr=Arrays.copyOf(test, test.length);
	int len=arr.length;
	boolean flag=false;            //内层是否发生交换操作
	//外层循环:每次冒泡都能找到当前最大的数,确定最大数的位置,所以总共需要循环len-1次
	for(int i=1;i<len;i++){
		//内层循环:如果相邻的两个元素(前大后小)则两两交换,否则继续,直至最后(不包含已完成的位置)
		//(第i次排序确定了后面i位的位置,将i位排除在外)
		for(int j=0;j<len-i;j++){
			if(arr[j]>arr[j+1]){
				int tmp=arr[j];
				arr[j]=arr[j+1];
				arr[j+1]=tmp;
				flag=true;
			}
		}
		
		//判断是否需要进行下一次循环
		if(flag==false){
			break;                 //提前退出
		}
		else{
			flag=false;            //重置flag,进行下一次循环
		}
	}
	return arr;
}

3.选择排序
在这里插入图片描述

/**
 * 选择排序
 * 思路:进行n-1次循环,第i次循环将数组中(除去已排序好的元素)最小的数找出来,放在第i位,
 * @param test
 * @return
 */
public static int[] selectsort(int []test){
	int []arr=Arrays.copyOf(test, test.length);
	int len=arr.length;
	/*//实现方法一:频繁交换,只要arr[j]<arr[i]即交换
	for(int i=0;i<len-1;i++){
		for(int j=i+1;j<len;j++){
			//遍历寻找最小的数放在第i个位置上
			if(arr[i]>arr[j]){
				int tmp=arr[i];
				arr[i]=arr[j];
				arr[j]=tmp;
			}
		}
	}*/
	
	//实现方法二:先遍历找到最小的数和索引。arr[i]    
	//eg.遍历一次得到arr[j]最小           则将arr[i]的arr[j]内容交换
	for(int i=0;i<len-1;i++){
		int min=arr[i];
		int minIndex=i;
		//寻找剩下的最小的数
		for(int j=i;j<len;j++){
			if(arr[j]<min){
				min=arr[j];
				minIndex=j;
			}
		}
		arr[minIndex]=arr[i];
		arr[i]=min;
	}
	return arr;
}

4.插入排序
在这里插入图片描述

/**
 * 插入排序
 * 思路:抽取一个元素从后向前与已排好序的元素进行比较,直到找到正确的插入位置
 * 前面的元素是已排好序了,从后向前比较,这时若该元素小于之前的元素,则将元素位置调换,重复比较,直至找到正确的位置。
 * @param test
 * @return
 */
public static int[] insertsort(int []test){
	int []arr=Arrays.copyOf(test, test.length);
	int len=arr.length;
	
	//对于数组中的每一个元素,从后向前与已排好序的元素进行比较,直到找到正确的插入位置
	for(int i=1;i<len;i++){
		int tmp=arr[i];              //备份好arr[i],此后该位置可看为空,可写入
		int pre=i-1;                 //已经排序好的元素的最后一个元素的索引值
		int next=i;                  //当前需要排序元素的索引值
		//寻找适当的插入位置
		while(pre>=0&&tmp<arr[pre]){
			//位找到合适位置
			arr[next--]=arr[pre--];  //将arr[pre]向后移动一位,pre和next整体向前移动一位
		}
		arr[next]=tmp;               //插入数据
	}
	return arr;
}

5.希尔排序
在这里插入图片描述

/**
 * 希尔排序(改进版的插入排序)
 * 希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,(预处理)
 * 待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
 * 
 * 为什么需要希尔排序?(为什么需要预处理?)
 * eg.考虑该情况(*,*,*,*,*,*,*,*,*,1)
 *    插入排序:最后一位1需要 向前比较9次
 *    希尔排序:最后一位1在预处理时就会向前移动(9->4->2->0,比较3次),
 *    真正插入排序时,不需要向前比较
 * 结论:当序列长度较长时,“基本有序”可大大减少直接插入的效率,预处理很有必要
 * @param test
 * @return
 */
public static int[] shellsort(int []test){
	int []arr=Arrays.copyOf(test, test.length);
	int len=arr.length;
	
	/*关于希尔排序increment(增量)的取法:增量increment的取法有各种方案。
	最初shell提出取increment=n/2向下取整,increment=increment/2向下取整,直到increment=1。
	但由于直到最后一步,在奇数位置的元素才会与偶数位置的元素进行比较,这样使用这个序列的效率会很低。
	后来Knuth提出取increment=n/3向下取整+1.*/
	
	int gap=len/2;                   //步长
	//int gap=len/3+1;
	while(gap>0){
		//步长为gap的插入排序
		//第一步:步长为5【0,5】【1,6】【2,7】【3,8】【4,9】分别有序===========>步长为5的插入排序
		//第二步:步长为2【0,2,4,6,8】【1,3,5,7,9】分别有序================>步长为2的插入排序
		//第三步:步长为1【0,1,2,3,4,5,6,7,8,9】有序====================>步长为1的插入排序(就是插入排序)
		//第四步:步长为0,完成,退出
		for(int i=gap;i<len;i++){
			int tmp=arr[i];
			int pre=i-gap;
			int next=i;
			//寻找适当的插入位置
			while(pre>=0&&tmp<arr[pre]){
				//位找到合适位置
				arr[next]=arr[pre];  //将arr[pre]向后移动gap位,pre和next整体向前移动一位
				next-=gap;           //pre整体向前移动gap位
				pre-=gap;            //next整体向前移动gap位
			}
			arr[next]=tmp;
		}
		gap=gap/2;
		/*gap=gap/3;
		if(gap==2){
			gap=1;
		}*/
	}
	return arr;
}

6.归并排序
在这里插入图片描述

/**
 * 归并排序
 * 分治思想:先分再治
 * @param test
 * @return
 */
public static int[] mergesort(int []test){
	int len=test.length;
	
	if(len<2){
		return test;
	}
       //分:用mid将数组分为left和right(二路归并)
	int mid=len/2;
	int []left=Arrays.copyOfRange(test, 0, mid);    //数组左部分
	int []right=Arrays.copyOfRange(test, mid, len); //数组右部分
	
	return merge(mergesort(left),mergesort(right));
}

//治:将left[]数组和right[]数组进行合并排序
//   left[]和right[]已经有序,此时问题等价为给定两个有序的数组,合并使得其整体有序 
public static int[] merge(int[] left, int[] right) {
	int l_len=left.length;
	int r_len=right.length;
	int []new_arr=new int [l_len+r_len];
	
	int l_index=0;
	int r_index=0;
	int n_index=0;
	
	//从left数组和right数组的首部开始遍历,哪个小就放入new_arr数组中,然后继续,
	//直至某个数组到达尾部,跳出循环
	while(l_index!=l_len&&r_index!=r_len){
		if(left[l_index]<right[r_index]){
			new_arr[n_index]=left[l_index];
			l_index++;
		}
		else{
			new_arr[n_index]=right[r_index];
			r_index++;
		}
		n_index++;
	}
	
	//将剩余的元素添加到末尾
	if(l_index==l_len){
		while(n_index<new_arr.length){
			new_arr[n_index++]=right[r_index++];
		}
	}
	else{
		while(n_index<new_arr.length){
			new_arr[n_index++]=left[l_index++];
		}
	}
	return new_arr;
}

7.快速排序
在这里插入图片描述

/**
 * 快速排序(递归)
 * 算法步骤:从数列中挑出一个元素,称为 “基准”(pivot),选用数组的第一个元素;
 * 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
 * 在这个分区(partition)操作退出之后,该基准就处于数列的中间位置。
 * 递归地(recursive)把小于基准值元素的子数列【left,pivot-1】和大于基准值元素的子数列【pivot+1,right】排序;
 * @param test
 * @return
 */
public static int[] quicksort(int []test){
	int []arr=Arrays.copyOf(test, test.length);
	arr=quicksort(arr,0,test.length-1);
	return arr;
}
//定义快速排序递归内容
public static int[] quicksort(int []arr,int left,int right){
	if(left<right){
		int pivot = partition(arr,left,right);
		quicksort(arr, left, pivot-1);
		quicksort(arr, pivot+1, right);
	}
	return arr;
}
//重排,并返回确定的基准值的索引,为下一次递归确定边界值(left,pivot-1)(pivot+1,right)
public  static int partition(int[] arr, int left, int right) {
	//使用数组第一位为“基准”(pivot)
	int pivot=left;
	int tmp=arr[pivot];
	//将所有小于基准值的数组元素放在基准值之前,所有大于基准值的放在基准值之后,并且需要保证arr[pivot]可写入
	for(int i=left+1;i<=right;i++){
		if(arr[i]<tmp){
			arr[pivot]=arr[i];//将小于基准值的数组元素放在基准值之前  (此时arr[i]可写入)
			pivot++;          //基准值索引右移                                              (arr[pivot]>pivot)
			arr[i]=arr[pivot];//将大于基准值的数组元素放在基准值之后  (此时arr[pivot]的位置可写入)                                                         
		}
	}
	arr[pivot]=tmp;
	return pivot;
}

8.堆排序
在这里插入图片描述

/**
 * 堆排序
 * 最大堆:根节点总是大于,左右孩子节点
 * 最大堆调整:使得堆满足最大堆所做的调整
 * 堆排序步骤:
 * 1.最大堆调整后,将堆顶元素与末尾元素进行交换,最后的叶子节点即为最大值,位置确定,该节点不再考虑
 * 2.将剩余的结点重复进行1步骤,直至结束
 * 我的理解:堆排序是特殊的选择排序
 * 
 * 为了简化操作:可用数组映射为堆结构,索引为i的结点的子节点索引为2i+1,2i+2
 * @param test
 * @return
 */
public static int[] heapsort(int []test){
	int []arr=Arrays.copyOf(test, test.length);
	int len=arr.length;
	
	while(len>1){
		//从第一个非叶子节点出发(最后一个根出发)由后向前,进行堆调整
		for(int i=len/2-1;i>=0;i--){
			heapadjust(arr, i, len);
		}
		//在得到最大堆后,将堆顶元素与末尾元素进行交换,
		int max=arr[0];
		arr[0]=arr[len-1];
		arr[len-1]=max;
		//最大元素放在末尾,不再考虑和调整,len-1,
		len--;
	}
	return arr;
}
//堆调整(根 ,左孩子,右孩子,保证根孩子是三个数中最大的)
public static int[] heapadjust(int []arr,int i,int len){
	//如果存在左孩子且大于根,根与左孩子替换
	if((2*i+1)<len&&arr[2*i+1]>arr[i]){
		int r1=arr[i];
		arr[i]=arr[2*i+1];
		arr[2*i+1]=r1;
	}
	//如果存在右孩子且大于根,根与右孩子替换
	if((2*i+2)<len&&arr[2*i+2]>arr[i]){
		int r1=arr[i];
		arr[i]=arr[2*i+2];
		arr[2*i+2]=r1;
	}
	return arr;
}

9.记数排序+桶排序
在这里插入图片描述

/**
 * 记数排序+桶排序
 * 思想:空间换时间
 * 问题:将班级中的人的分数由从高到低排列(分数:0-100,整数,人数:50人)?
 * 分数0-100,可看成101个桶(数组表示),将分数放到对应的桶中,然后根据桶遍历输出。
 * eg  gradeBucket[100]=1:表示考100分的有1个人
 * @return
 */
public static void bucketsort(){
	//将50人的分数初始化
	int []gradeTable=new int[50];   
	for(int i=0;i<50;i++){
		gradeTable[i]=(int)(100*Math.random());
	}
	
	//将50个人的分数放到对应的桶中
	int []gradeBucket=new int[101];
	for(int grade:gradeTable){
		gradeBucket[grade]++;           //对应的桶记数+1
	}
	
	//按从大到小的顺序输出
	for(int grade=100;grade>=0;grade--){
		for(int count=0;count<gradeBucket[grade];count++){
			System.out.print(grade+" ");
		}
		System.out.println();
	}
}

10.基数排序
在这里插入图片描述

/**
 * 基数排序
 * 问题:排序的范围进一步扩大,那么桶排序的一维数组也会进一步扩大,如待排序的数是稀疏的,会造成内存的大量浪费,如何解决?
 * 答:
 *	 特殊的桶排序:10个桶(0-9)
 * 	过程:个位进桶——>顺序出桶(个位有序)——>十位进桶——>十位出桶(十位+个位 有序)———>······——>最高位进桶,最高位出桶——>从最高位到最低位都有序,整体有序,输出
 * 	实现:1.高位不够,用0表示
 *     	2.定义一个二维数组     eg.int buckets[1][0]=61     个位为1,第一个进"1桶"
 *     	(二维数组包含10个一维数组,每个一维数组长度为要排序数组的长度,以空间换时间)
 *     	3.定义一个一维数组     存放每个桶放置的元素个数   bucket_count[10]    
 */
public static int[] radixsort(int []test){
	int []arr=Arrays.copyOf(test, test.length);
	
	int [][]buckets=new int[10][arr.length];  //二维数组,10个桶,每个桶里放属于该桶的       
	int []bucket_count=new int[10];           //记录每个桶内放的元素个数,便于入桶,出桶的操作
	
	//获取数组中最大的位数
	int max=arr[0];
	for(int tmp:arr){
		if(max<tmp){
			max=tmp;
		}
	}
	int max_len=(max+"").length();
		
	int divisor=1;                            //除数
	while((max_len)!=0){
		divisor=divisor*10;
		//入桶
		for(int tmp:arr){
			//取数的某位位作为bucket_no   eg.12345取十位   12345%100=45   45/10=4
			int bucket_no=tmp%divisor/(divisor/10);
			buckets[bucket_no][bucket_count[bucket_no]++]=tmp;
		}
		//出桶
		int index=0;
		for(int i=0;i<10;i++){
			for (int j=0;j<bucket_count[i];j++){
				arr[index++]=buckets[i][j];   //再次写入数组(此时该位有序)
			}
			bucket_count[i]=0;                //置零,便于下一次操作
		}
		max_len=max_len-1;                    //个位->十位->百位----->记录循环条件结束的时间
	}
	return arr;
}

如有错误,欢迎指正。
参考:
https://github.com/hustcc/JS-Sorting-Algorithm
https://www.cnblogs.com/onepixel/articles/7674659.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值