Java排序算法

这次学习下Java中几种经典的排序算法,参考十大经典排序算法最强总结(含JAVA代码实现),大致分为比较排序与非比较排序两种。

比较排序

在排序的最终结果里,元素之间的次序依赖于它们之间的比较。每个数都必须和其他数进行比较,才能确定自己的位置。

  • 冒泡排序:重复地走访过要排序的数列,一次比较两个元素,并将这两个元素按顺序排列,不断重复地进行直到没有再需要交换即排序完成。
    在这里插入图片描述

代码如下:

	    public class test {
		public static void main(String[] args) {
		      int[] arr = {5,3,2,0,4,7,1,9,8};
		      
		      int[] sorted = bubbleSort(arr);
		      System.out.print("result: ");
		      for(int i=0;i<sorted.length;i++) {
		    	  System.out.print(sorted[i]+" ");
		      }  
		}	
		public static int[] bubbleSort(int[] array) {
			if(array.length == 0) {
				return array;
			}
			for(int i=0;i<array.length;i++) {
				for(int j=0;j<array.length-1-i;j++) {
					if(array[j+1]<array[j]) {
						int temp = array[j+1];
						array[j+1] = array[j];
						array[j] = temp;
					}
				}
				for(int k=0;k<array.length;k++) {
			    	 System.out.print(array[k]+" ");
			    }
				System.out.println();
			}
			return array;
		}
	}

运行结果如下:
在这里插入图片描述
上述程序中还有个小问题,就是缺少判断排序完成的标识,这个会导致算法最快的时间复杂度退化成O(n^2),可以设置一个判断交换标志:

    ...
 	public static int[] bubbleSort(int[] array) {
			if(array.length == 0) {
				return array;
			}
			for(int i=0;i<array.length;i++) {
				boolean flag = true;
				for(int j=0;j<array.length-1-i;j++) {
					if(array[j+1]<array[j]) {
						int temp = array[j+1];
						array[j+1] = array[j];
						array[j] = temp;
						flag = false;
					}
				}
				for(int k=0;k<array.length;k++) {
			    	 System.out.print(array[k]+" ");
			    }
				System.out.println();
				if(flag == true) {
					break;
				}
			}
			return array;
		}

运行结果如下:
在这里插入图片描述
冒泡排序时间复杂度:最佳情况:T(n) = O(n);最差情况:T(n) = O(n^2);平均情况:T(n) = O(n^2);空间复杂度为S(n) = O(1)

  • 选择排序:无论什么数据进去都是O(n^2)的时间复杂度。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
    在这里插入图片描述

代码如下:

public class test {
	public static void main(String[] args) {
	      int[] arr = {5,3,2,0,4,7,1,9,8};
	      int[] sorted = selectionSort(arr);
	      System.out.print("result: ");
	      for(int i=0;i<sorted.length;i++) {
	    	  System.out.print(sorted[i]+" ");
	      }  
	}

	public static int[] selectionSort(int[] array) {
        if (array.length == 0)
            return array;
        for (int i = 0; i < array.length; i++) {
            int minIndex = i;
            for (int j = i+1; j < array.length; j++) {
                if (array[j] < array[minIndex]) 
                    minIndex = j; 
            }
            int temp = array[minIndex];
            array[minIndex] = array[i];
            array[i] = temp;
            
            for(int k=0;k<array.length;k++) {
            	System.out.print(array[k]+" ");
            }
            System.out.println();
        }
        return array;
    }
}

运行结果如下:
在这里插入图片描述
选择排序不稳定,时间复杂度最佳情况:T(n) = O(n^2);最差情况:T(n) = O(n^2);平均情况:T(n) = O(n^2);空间复杂度为S(n) = O(1)

  • 快速排序:在待排序的数组选取一个元素作为基准,将待排序的元素进行分区,比基准元素大的元素放在一边,比其小的放另一边,递归调用快速排序对两边的元素排序。选取基准元素并分区的过程采用双指针左右交换。。
    在这里插入图片描述

代码如下:

public class test {
	
	public static void main(String[] args) {
	      int[] arr = {5,3,2,0,4,7,1,9,8};
	      quickSort(arr);
	      System.out.print("result: ");
	      for(int i=0;i<arr.length;i++) {
	    	  System.out.print(arr[i]+" ");
	      }  
	}
	public static void quickSort(int[] arr){
		  quickSort(arr,0,arr.length-1);
	}
	private static void quickSort(int[] arr, int low, int high) {
		  if(low>=high) {
			  return;
		  }
		  int index = partition(arr,low,high);//将数组分为两部分
		  quickSort(arr,low,index-1); //递归排序左子数组
		  quickSort(arr,index+1,high);//递归排序右子数组
	}
	private static int partition(int[] arr, int low, int high) {
	    int index = arr[low];//指定基准为该次partition数组的第一个数字
	    while(low<high) {
	    	while(low<high && arr[high]>=index) {
	    		high--;
	    	}
	    	arr[low] = arr[high];//交换比基准大的记录到左端
	    	while(low<high && arr[low]<=index) {
	    		low++;
	    	}
	    	arr[high] = arr[low];//交换比基准小的记录到右端
	    }
	    arr[low] = index;//基准到位,此时基准左边均小于它,右边均大于它
		return low;//返回基准的位置
	}
}

执行效果如下:
在这里插入图片描述
快速排序不稳定,时间复杂度最佳情况下:T(n) = O(nlogn);最差情况(序列都已经有序或完全倒序):T(n) = O(n^2) 此时选择排序就退化成了冒泡排序;平均情况:T(n) = O(nlogn),空间复杂度S(n) = O(logn)。

  • 归并排序:分解待排序的数组成两个各具 n/2 个元素的子数组,递归调用归并排序两个子数组,合并两个已排序的子数组成一个已排序的数组。此外在归并时需要开辟一个临时数组来辅助归并操作,因此需要额外的内存空间。
    在这里插入图片描述
    代码如下:
public class test {
	
	public static void main(String[] args) {
	      int[] arr = {5,3,2,0,4,7,1,9,8};
	      mergeSort(arr);
	      System.out.print("result: ");
	      for(int i=0;i<arr.length;i++) {
	    	  System.out.print(arr[i]+" ");
	      }  
	}

	private static void mergeSort(int[] arr) {
		int[] temp = new int[arr.length];//开辟一个临时数组来辅助归并操作
		internalMergeSort(arr,temp,0,arr.length-1);
	}

	private static void internalMergeSort(int[] arr, int[] temp, int left, int right) {
		if(left<right) {
			int mid = (left+right)/2;
			internalMergeSort(arr,temp,left,mid);
			internalMergeSort(arr,temp,mid+1,right);
			mergeSortedArray(arr,temp,left,mid,right);
		}		
	}

	private static void mergeSortedArray(int[] arr, int[] temp, int left, int mid, int right) {
		int i = left;
		int j = mid+1;
		int k = 0;
		while(i<=mid&&j<=right) {
			temp[k++] = arr[i]<arr[j]?arr[i++]:arr[j++];
		}
		while (i <= mid) {
		    temp[k++] = arr[i++];//左边数组还有剩余
		}
		while (j <= right) {
		    temp[k++] = arr[j++];//右边数组还有剩余
		}
		for (i = 0; i < k; i++) {
	        arr[left + i] = temp[i];// 把temp数据复制回原数组
	    }
	}
	
}

运行结果为:
在这里插入图片描述
这里再看下归并的过程,其实就是开辟了一个新临时数组来辅助归并操作,采用双索引法,例如下图所示,将左边数组与右边数组归并,先同时指向第一个数并将两数中小的一方赋值到临时数组的第一个(这里是右边数组数字小):
在这里插入图片描述
接着右边数组索引++,指向4,比较2和4:
在这里插入图片描述
2比4小,那么2赋值到临时数组的下一个位置,左边数组索引++:
在这里插入图片描述
这就是大致思路,由于使用了额外的空间,因此空间复杂度较高,S(n)=O(n);时间复杂度最佳情况:T(n) = O(nlogn) ;最差情况:T(n) = O(nlogn);平均情况:T(n) = O(nlogn)。

线性排序(非比较排序)

非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组arr,计算arr[i]之前有多少个元素,则唯一确定了arr[i]在排序后数组中的位置。非比较排序只要确定每个元素之前的已有的元素个数即可,所有一次遍历即可解决。算法时间复杂度O(n)。

  • 计数排序:根据待排序的数组中最大和最小的元素,统计数组中每个值为i的元素出现的次数,存入数组C的第i项,对所有的计数累加,然后反向填充目标数组。
    在这里插入图片描述
public class test {
	public static void main(String[] args) {
	      int[] arr = {5,3,2,0,4,7,1,9,8};
	      countSort(arr);
	      System.out.print("result: ");
	      for(int i=0;i<arr.length;i++) {
	    	  System.out.print(arr[i]+" ");
	      }  
	}

	public static void countSort(int[] arr) {
	    int max = Integer.MIN_VALUE;
	    int min = Integer.MAX_VALUE;
	    for(int i = 0; i < arr.length; i++){
	        max = Math.max(max, arr[i]);
	        min = Math.min(min, arr[i]);
	    }

	    int[] b = new int[arr.length]; // 存储数组
	    int[] count = new int[max - min + 1]; // 计数数组

	    for (int num = min; num <= max; num++) {
	        // 初始化各元素值为0,数组下标从0开始因此减min
	        count[num - min] = 0;
	    }

	    for (int i = 0; i < arr.length; i++) {
	        int num = arr[i];
	        count[num - min]++; // 每出现一个值,计数数组对应元素的值+1
	        // 此时count[i]表示数值等于i的元素的个数
	    }

	    for (int i = min + 1; i <= max; i++) {
	        count[i - min] += count[i - min - 1];
	        // 此时count[i]表示数值<=i的元素的个数
	    }

	    for (int i = 0; i < arr.length; i++) {
	            int num = arr[i]; // 原数组第i位的值
	            int index = count[num - min] - 1; //加总数组中对应元素的下标
	            b[index] = num; // 将该值存入存储数组对应下标中
	            count[num - min]--; // 加总数组中,该值的总和减少1。
	    }

	    // 将存储数组的值替换给原数组
	    for(int i=0; i < arr.length;i++){
	        arr[i] = b[i];
	    }
	}
	
}

执行结果为:
在这里插入图片描述
这里借鉴一文弄懂计数排序算法!中的图来解释上述代码,比如一组数据{101,109,108,102,110,107,103}:
首先找出数组中的最大值max、最小值min,并创建一个新数组count,其长度是max-min加1,其元素默认值都为0;接着遍历原数组中的元素,以原数组中的元素作为count数组的索引,以原数组中的元素出现次数作为count数组的元素值。
在这里插入图片描述

......

在这里插入图片描述
接着对count数组变形,新元素的值是前面元素累加之和的值:
在这里插入图片描述
接着创建结果数组result,长度和原始数组一样,并遍历原始数组中的元素,当前元素A[j]减去最小值min,作为索引,在计数数组中找到对应的元素值count[A[j]-min],再将count[A[j]-min]的值减去1,就是A[j]在结果数组result中的位置,做完上述这些操作,count[A[j]-min]自减1:
在这里插入图片描述

...

在这里插入图片描述
计数排序时间复杂度最佳情况:T(n) = O(n+k) ;最差情况:T(n) = O(n+k) ;平均情况:T(n) = O(n+k),空间复杂度S(n) = O(k)

  • 桶排序:计数排序的扩展版本,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个对应的桶中(数组ArrayList作为桶),然后找出待排序数组中的最大值max、最小值min,计算每个元素 arr[i] 放的桶,每个桶各自排序,遍历桶数组,把排序好的元素放进输出数组。

代码如下:

import java.util.ArrayList;
import java.util.Collections;

public class test {
	
	public static void main(String[] args) {
	      int[] arr = {5,3,2,0,4,7,1,9,8};
	      bucketSort(arr);
	      System.out.print("result: ");
	      for(int i=0;i<arr.length;i++) {
	    	  System.out.print(arr[i]+" ");
	      }  
	}

	public static void bucketSort(int[] arr){
		    int max = Integer.MIN_VALUE;
		    int min = Integer.MAX_VALUE;
		    for(int i = 0; i < arr.length; i++){
		        max = Math.max(max, arr[i]);
		        min = Math.min(min, arr[i]);
		    }
		    
		    // 计算桶的数量
		    int bucketNum = (max - min) / arr.length + 1;
		    ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
		    for(int i = 0; i < bucketNum; i++){
		        bucketArr.add(new ArrayList<Integer>());
		    }
		    
		    // 将每个元素放入桶
		    for(int i = 0; i < arr.length; i++){
		        int num = (arr[i] - min) / (arr.length);
		        bucketArr.get(num).add(arr[i]);
		    }
		    
		    // 对每个桶进行排序
		    for(int i = 0; i < bucketArr.size(); i++){
		        Collections.sort(bucketArr.get(i));
		    }
		    
		    // 将桶中的元素赋值到原序列
			int index = 0;
			for(int i = 0; i < bucketArr.size(); i++){
				for(int j = 0; j < bucketArr.get(i).size(); j++){
					arr[index++] = bucketArr.get(i).get(j);
			}
		}  
	}
}

执行结果如下:
在这里插入图片描述
这里借鉴【排序】图解桶排序中的图解释上述代码:
在这里插入图片描述
桶排序时间复杂度最佳情况:T(n) = O(n+k) ;最差情况:T(n) = O(n+k) ; 平均情况:T(n) = O(n^2),空间复杂度为O(n+k)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值