十个常用排序算法(java实现)

1. 快速排序

public  void quickSort(int[] arr,int start,int end){
        //递归终止条件
       if(start >= end){
            return;
        }
        //一、挖坑法实现快排
        int left = start;
        int right = end;
        int mid = arr[left];//取头尾位置法获得基准值(可改成随机选取基准位置或三数取中法进行初步优化)
        while(left < right){
            //一定要加上left<right,不然可能出现左右指针交叉,即错过相等的机会,导致基准值插入错误
            while(left < right && arr[right] >= mid){//一定要等于号,避免出现指针指向都等于mid时死循环
                right--;
            }
            arr[left] = arr[right];
            //一定要加上left<right,不然可能出现左右指针交叉,即错过左右指针相等的机会,导致基准值插入错误
            while (left < right && arr[left] <= mid){//一定要等于号,避免出现指针指向都等于mid时死循环
                left++;
            }
            arr[right] = arr[left];
        }
        //左右指针此时指向相同,指向即为基准数的位置
        arr[left] = mid;
        
         //左递归
        quickSort(arr,start,left - 1);
        
        //右递归
        quickSort(arr,left + 1,end);                  
}  

2. 冒泡排序

 //冒泡排序
 public void bubbleSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {//i < arr.length - 1,确认了arr.length - 1个数的位置,也即确认完了arr.length个数的位置
            for (int j = 1; j < arr.length - i; j++) {
                if (arr[j - 1] > arr[j]) {
                    int tmp = arr[j - 1];
                    arr[j - 1] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
}    

//冒泡排序优化一
 public void bubbleSort1(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
        	boolean flag = true;//标记数组是否已经有序,有序则提前结束,避免无效比较
            for (int j = 1; j < arr.length - i; j++) {
                if (arr[j - 1] > arr[j]) {
                    int tmp = arr[j - 1];
                    arr[j - 1] = arr[j];
                    arr[j] = tmp;
                    flag = false;//发生交换,即数组无序,将标记置为false
                }
            }
            if(flag){//标记未被置为false,即整轮比较过程没有发生交换,数组已然有序
            	 break;//提前退出循环
            }           
        }
}   

//冒泡排序优化二
public void bubbleSort2(int[] arr) {
	boolean flag;
	int tmp;
	int lastExchangeIndex = 0;  //用该变量记录最后一次交换的位置
	int sortBorder = arr.length - 1;  	//无序数列的边界,每次比较只需要比到这里为止
	for (int i = 0; i < n; ++i)
	{
		flag = true;
		for (int j = 0; j < sortBorder; j++)
		{
			if (a[j] > a[j + 1])
			{
				tmp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = tmp;
 
				flag = false;
				lastExchangeIndex = j;  //把无序数列的边界更新为最后一次交换元素的位置
			}
		}
		sortBorder = lastExchangeIndex;
		if (flag)
			break;
	}


3. 选择排序

public void selectionSort(int[] arr) {
        int max = 0;
        int maxID = 0;
        for (int i = 0; i < arr.length - 1 ; i++) {//i < arr.length - 1,确认了arr.length - 1个数的位置,也即确认完了arr.length个数的位置
            for (int j = 0; j < arr.length - i; j++) {//每小轮确定出一个最大值与其下标
                if(arr[j] > max){
                    max = arr[j];
                    maxID = j;
                }
            }
            int tem = arr[arr.length - i - 1];//将末尾值与最大值交换,确定一个最值位置
            arr[arr.length - i - 1] = max;
            arr[maxID] = tem;
            max = 0;  //置零
        }

4. 直接插入排序

 public void insertionSort(int[] arr, int start, int end) {
        int tmp;
        int insertIndex;
        for (int i = 1; i < arr.length; i++) {//从数组下标1开始往前即有序部分进行插入
        	tmp = arr[i];//待插入值
            insertIndex = i - 1;//待插入位置下标,预设为待插入值的前一个位置(也即数组有序部分的最后一个位置)
            while ( insertIndex >= 0 &&  tmp < arr[ insertIndex]) {//当前待插入值小于数组前部的有序部分
                arr[ insertIndex + 1] = arr[ insertIndex];//将数组前面的有序部分进行后移
                 insertIndex--;//不断向前进行比较,直到找到合适的插入位置
            }//while循环结束即找到合适的插入位置
            if ( insertIndex != i - 1) {//当 insertIndex发生变化,即可以进行一次插入
                arr[ insertIndex + 1] =  tmp;
            }
        }

5. 希尔排序

//交换实现
 public void shellSort(int[] arr) {
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {//增量gap采用gap = gap / 2的方法来进行缩小增量
            for (int i = gap; i < arr.length; i++) {//根据分组情况得出,需要比较arr.length - gap小轮
                for (int j = i - gap; j >= 0; j -= gap) {//每次交换完后还需跟前面同组的元素再比较大小
                    if (arr[j] > arr[j + gap]) {//进行比较符合交换条件时交换(跨步长比较,即同组比较)
                        int tem = arr[j];
                        arr[j] = arr[j + gap];
                        arr[j + gap] = tem;
                    }
                }
            }
        }
}   

//移位实现,该方法更优
public void shellSort1(int[] arr) {
       for (int gap = arr.length / 2; gap > 0; gap /= 2) {//增量gap采用gap = gap / 2的方法来进行缩小增量
            for (int i = gap; i < arr.length; i++) {//这里相当于直接插入排序
                int tmp = arr[i];//待插入元素的值
                int k = i-gap;//待插入元素的前一个元素下标,从这里开始比较,寻找插入位置
                while( k >= gap && tmp < arr[k] ){//当向前比较不越界, 且插入值比当前比较值小时               
                    arr[k + gap] = arr[k];//大的往后移,直到找到正确合适的位置
                    k -= gap;//依次向前比较
                }//while循环退出后找到正确的插入位置,即不满足前一个比待插入值大,此时位置正确
                if(k != i - gap){//k值发生变化
                    arr[k + gap] = tmp;//将值插入
                }
            }
        }
}        
     

6. 归并排序

 public void mergeSort(int[] arr, int left, int right) {
        if(left == right)
            return;
        int mid = left + ((right - left) >> 1);
        mergeSort(arr,left,mid);
        mergeSort(arr,mid + 1,right);
        merge(arr,left,mid,right);
}
private void merge(int[] arr, int left, int mid, int right) {
        int[] tmp = new int[right - left + 1];
        int i = 0;//用于更新tmp数组
        int p1 = left;
        int p2 = mid + 1;
        // 比较左右两部分的元素,哪个小,把那个元素填入tmp数组中
        while(p1 <= mid && p2 <= right){
            //arr[p1] <= arr[p2],取arr[p1],这样可以保证归并排序为稳定排序
            tmp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }
        // 上面的循环退出后,把剩余的元素依次填入到temp中
        // 以下两个while只有一个会执行
        while(p1 <= mid){
            tmp[i++] = arr[p1++];
        }
        while(p2 <= right){
            tmp[i++] = arr[p2++];
        }

        //将tmp数组赋值给原数组
        for (int j = 0; j < tmp.length; j++) {
            arr[left + j] = tmp[j];
        }
 }       

7. 计数排序

public  void countingSort(int[] arr) {
        //计数排序
        //找出最大值最小值
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
            if (arr[i] < min) {
                min = arr[i];
            }
        }

        //创建计数数组,大小为max - min + 1   
        int[] count = new int[max - min + 1];
        //遍历数组进行计数
        for (int i = 0; i < arr.length; i++) {
            count[arr[i] - min]++;
        }

        //提取计数结果完成排序
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0) {
                arr[index++] = i + min;
                count[i]--;
            }
        }
}        

8. 桶排序

 public static bucketSort(double[] arr){
        //得到数列的最大值和最小值,并计算出差值d
        double max=arr[0];
        double min=arr[0];
        for (int i=1;i<arr.length;i++){
            max = Math.max(max, arr[i]);
            min = Math.min(min, arr[i]);
        }
        double d=max-min;

        //初始化桶
        int bucketNum=arr.length;
        ArrayList<LinkedList<Double>> bucketList=new ArrayList<LinkedList<Double>>(bucketNum);
        for (int i=0;i<bucketNum;i++){
            bucketList.add(new LinkedList<Double>());
        }

        //遍历原始数组根据映射规则将每个元素放入桶中
        for (int i=0;i<arr.length;i++){
            int num=(int)((arr[i]-min)*(bucketNum-1)/d);
            bucketList.get(num).add(arr[i]);
        }

        //对每个桶内部进行排序
        for(int i=0;i<bucketList.size();i++){
            // 使用Collections.sort,其底层实现基于归并排序或归并排序的优化版本
            Collections.sort(bucketList.get(i));
        }

          // 将桶中的元素赋值到原数组     
        int index=0;
        for (LinkedList<Double> list:bucketList) {
            for (double element:list){
                arr[index++]=element;   
            }
        }      	
}

9. 基数排序

public static void radixSort(int[] arr) {
            // 存数组中最大的数字,为了知道循环几次
            int max = Integer.MIN_VALUE;// (整数中的最小数)
            // 遍历数组,找出最大值
            for (int i = 0; i < arr.length; i++) {
                if (max < arr[i]) {
                    max = arr[i];
                }
            }
            // 计算最大数是几位数,此方法计算绝妙
            int maxLength = (max + "").length();
            // 用于临时存储数据的数组
            int[][] tmp = new int[10][arr.length];
            // 用于存储桶内的元素位置
            int[] counts = new int[arr.length];

            // 第一轮个位数较易得到余数,第二轮就得先除以十再去取余,之后百位除以一百
            // 可以看出,还有一个变量(即以下变量n)随循环次数变化,为了取余

            // 循环的次数
            for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
                // 每一轮取余
                for (int j = 0; j < arr.length; j++) {
                    // 计算余数
                    int remainder = (arr[j] / n) % 10;
                    // 把遍历得到的数据放在指定数组中,有两个信息,放在第几个桶+数据应该放在第几位
                    tmp[remainder][counts[remainder]] = arr[j];
                    // 记录数量
                    counts[remainder]++;
                }

                // 记录取的数字应该放到位置
                int index = 0;
                // 每一轮循环之后把数字取出来
                for (int k = 0; k < counts.length; k++) {
                    // 记录数量的数组中当前余数记录不为零
                    if (counts[k] != 0) {
                        for (int l = 0; l < counts[k]; l++) {
                            // 取出元素
                            arr[index++] = tmp[k][l];
                        }
                        // 取出后把数量置为零
                        counts[k] = 0;
                    }
                }
            }
}            

10. 堆排序

public void heapSort(int[] arr) {
        //先根据升序或降序构成一个大顶堆或小顶堆
        for(int i = length / 2 - 1;i >= 0;i--){
        	//构建堆时先从第一个非叶子节点(length / 2 - 1)开始,从下至上进行构建
            adjustHead(arr,i,arr.length);
        }
        
        //下沉,并继续调整直至数组有序
        for (int j = arr.length - 1; j > 0  ; j--) {
        	//将堆顶元素下沉,即与末尾元素交换
            int tmp = arr[0];
            arr[0] = arr[j];
            arr[j] = tmp;
            //已经构成堆,可从头开始调整
            adjustHead(arr,0,j);
        }
}
//堆建立完成之后进行的调整方法(也可用其进行堆的构建)
private void adjustHead(int arr[],int adjustNode,int length) {
        int tmp = arr[adjustNode];
        for(int i = 2 * adjustNode + 1;i < length;i = 2 * i + 1 ){//i = 2 * adjustNode + 1,i为adjustNode的左子节点
            if( i + 1 < length && arr[i] < arr[i + 1]){//先与右子节点比较,将i置为左右节点中的较大值(大根堆)
                i++;
            }
            if(arr[i] > tmp){//若子节点值更大
                arr[adjustNode] = arr[i];//则将待调整节点位置置为更大的子节点,这里不必进行交换
                adjustNode = i;//继续向下进行比较,找出待调整节点的正确位置
            }else{
                break;//可以break的原因是因为该函数用于构建堆时,由于是从下至上开始构建的(下面已经建好堆),当前位置不满足更新条件时,下面位置也必不满足更新条件;当该函数用于进行下沉调整时同理
            }
        }
        //退出for循环时,即已找到待调整节点的合适位置
        arr[adjustNode] = tmp;
    }
        

11. 常用排序算法总结和对比

在这里插入图片描述

  1. 内排序:所有排序操作都在内存中完成;
  2. 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
    3)n:数据的规模;
  3. k: “桶”的个数;
  4. In-place: 不占用额外内存;
  5. Out-place: 占用额外内存。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值