排序算法优化(二)

本文深入探讨了冒泡排序的优化方法,包括选择排序和内部有序判断,以及快速排序的优化,如三数取中、交换改赋值和减少递归。此外,还介绍了简单插入排序的优化,如折半插入和希尔排序。通过这些优化,提高了排序算法的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

给你一个整数数组 nums,请你将该数组升序排列。

冒泡排序优化

概念:把最大的换到最后一位,第二大的换到倒数第二位;
冒泡写法:

冒泡原始写法

public int[] maopao(int[] nums){
    for(int i=0;i<nums.length-1;i++){
        //int max = 0;
        for(int j=i+1;j<nums.length;j++){
            if(nums[i]>nums[j]){
              int tmp = nums[i];
              nums[i] = nums[j];
              nums[j] = tmp;
            }
        }
    }
    return nums;
}

优化方法一

//内循环处理,每次只交换一次,这种方法其实就是选择排序
public int[] maopao(int[] nums){
    for(int i=0;i<nums.length-1;i++) {
        int max = 50000, idx = 0;
        for (int j = i + 1; j < nums.length; j++) {
            if (nums[j] < max) {
                max = nums[j];
                idx = j;
            }
        }
        if(nums[i]>max){
            int tmp = nums[i];
            nums[i] = nums[idx];
            nums[idx] = tmp;
        }
    }
    return nums;
}

优化方法二

/对已经有序的序列停止排序
public static void MaoPao(int[] arr) {
		int length = arr.length;
		boolean flag = true;
		//外层循环控制轮数:
		for (int i = 0; i < length - 1; i++) {
			  flag = true;
			  //内存循环进行比较:
			  //如果内循环整个一轮下去,一次交换也没有发生,
              //说明此时0~length-1-i之间的元素都已经是有序(升序)的了,
			  // 而且我们也知道length-1-i~length-1之间的元素也是刚刚冒泡排序好的有序序列,
			  // 那么此时说明,整个数组其实都已经是有序的了,就没有必要再继续遍历和比较下去了。
			  for (int j = 0; j < length - 1 - i; j++) {
					if (arr[j] > arr[j + 1]) {
						  int temp = arr[j];
						  arr[j] = arr[j + 1];
						  arr[j + 1] = temp;
						  flag = false;
					}
			  }
			  //所以我们设置一个flag,如果内循环整个一轮下去,一次交换也没有发生的时候,flag为true,跳出所有循环;
			  if (flag) {
					break;
			  }
		}
  }

上面是最原始的冒泡和对冒泡优化后的两种算法,极端情况下这两种算法在对数组排序后都会超时;这时候就要考虑同为交换排序的快速排序;

快速排序

快排原始写法

public void  quickSort(int[] nums,int left,int right) {
        if(left>=right){
            return;
        }
        int le = left;
        int ri = right;
        //getMid(nums,left,right);
        int povit = nums[left];

        while (le< ri) {
            while (povit <= nums[ri] && le < ri) ri--;
            swap(nums,le,ri);
            while (nums[le] <= povit && le < ri) le++;
            swap(nums,le ,ri);
        }
        nums[le] = povit;
        quickSort(nums,left,le-1);
        quickSort(nums,le+1,right);

    }
    public void swap(int[] nums,int a,int b){
        int tmp = 0;
        if(nums[a]>nums[b]){
            tmp = nums[a];
            nums[a] = nums[b];
            nums[b] = tmp;
        }
    }

优化方法一

三数取中法
三数取中即在数据中取第一个数、最后一个数和中间一个数,在这三个数据得到一个中数,以这个中数作为标准值进行排序
优点:这种方法完美解决了当标准值过大或者过小时造成数据全在标准值的左边或者全在右边,从而使得快速排序退化为冒泡排序。
缺点:当数列特别大时这种三数取中的方法的效果会变小,因为可能取到的三个数都非常小,这种时候就需要多取一些值,比如采取9数取中法;
九数取中:把数组分为前中后三段,每段都采用三值取中得到三个值,然后再从这三个数中去中数作为标准值,这样既能避免标准值偏大或者偏小的情况又能避免数组过大时取到的中值不够均匀
代码实现:

//三数取中法
public void getMid(int[]nums ,int left,int right){
    int mid=(left+right)/2;
    if (nums[left]>nums[right])swap(nums,left,right);
    if (nums[mid]>nums[right]) swap(nums,mid,right);
    if (nums[left]>nums[mid]) swap(nums,left,mid);
}
public void swap(int[] nums,int a,int b){
    int tmp = 0;
    if(nums[a]>nums[b]){
        tmp = nums[a];
        nums[a] = nums[b];
        nums[b] = tmp;
    }
}
//优化后的三数取中法
public void  quickSort(int[] nums,int left,int right) {
    if(left>=right){
        return;
    }
    int le = left;
    int ri = right;
    getMid(nums,left,right);
    int povit = nums[left];
    System.out.println("povit="+povit);
    while (le< ri) {
        while (povit <= nums[ri] && le < ri) ri--;
        nums[le] = nums[ri];
        while (nums[le] <= povit && le < ri) le++;
        nums[ri] = nums[le];
    }
    nums[le] = povit;
    quickSort(nums,left,le-1);
    quickSort(nums,le+1,right);
}

优化方法二:

把交换改成赋值
在快速排序中只有最后一次交换是有效的,即之前的操作都是将较小值放在标准值的左边,较大值放在标准值右边;

public void  quickSort(int[] nums,int left,int right) {
    if(left>=right){
        return;
    }
    int le = left;
    int ri = right;
    int povit = nums[left];
    //防止里面while满足le<ri后进行了 le++操作使得le==ri
    while (le< ri) {   
        //采用写覆盖的方法把右边小值放在左边
        while (povit <= nums[ri] && le < ri) ri--;
        nums[le] = nums[ri];
        //采用写覆盖的方法把左边大值放在右边
        while (nums[le] <= povit && le < ri) le++;
        nums[ri] = nums[le];
    }
    nums[le] = povit;
    quickSort(nums,left,le-1);
    quickSort(nums,le+1,right);
}

优化方法三

减少递归操作
当待排序的序列及其不平衡的时候,递归的深度趋近于n,而不是平衡时的log(n)。而且栈的空间而是有限的,每次递归调用都会耗费一定的栈空间,函数的参数越多,每次递归消耗的空间也就越多,如果减少了递归的操作,就能大大提升性能。所以可以使用尾递归来优化性能

public void  quickSort(int[] nums,int left,int right) {
        int povit;
        while (left< right) {
            povit = partition1(nums,left,right); 
            quickSort(nums,left,povit-1);
            left = povit+1;
        }
    }

    public  int partition1(int[] nums,int lo,int hi){
        //以第一个值为基准值,当然你也可以3取1,
        int key=nums[lo];
        while(lo<hi){
            while(nums[hi]>key&&hi>lo){//从后半部分向前扫描
                hi--;
            }
            nums[lo]=nums[hi];
            while(nums[lo]<=key&&hi>lo){//从前半部分向后扫描
                lo++;
            }
            nums[hi]=nums[lo];
        }
        nums[hi]=key;
        return hi;
    }

简单插入排序

简单插入排序原始写法

public void  chaRu(int[] nums){
		int l = nums.length;
		for(int i =0 ;i<l;i++){
			int tmp = nums[i];
			int j = i-1;
			while(j>=0 && nums[j]>tmp){
				nums[j+1] = nums[j];
				j--;
				}
			nums[j+1] = tmp;
		}
	}

优化方法一

//折半优化排序
public void MyBinaryInsertSort(int[] nums){
		for(int i=0;i<nums.length;i++){
			int tmp = nums[i];
			int j = i-1;
			int le =0 ,ri =i-1,mid = 0;
			while(le<=ri){
				mid = (le+ri)/2;
				if(tmp >nums[mid])
					le = mid+1; 
				else 
					ri = mid -1;
			}
			for(;j>ri;j--)
				nums[j+1] = nums[j];
			nums[j+1] = tmp;
		}
		
	}

优化方法二

//希尔排序
public void xier(int[] nums){
		int l = nums.length;
		for(int dk= l/2;dk>=1;dk = dk/2){
			for(int i =dk ;i<l;i++){
				int tmp = nums[i];
				int j = i-dk;
				while(j>=0 && nums[j]>tmp){
					nums[j+dk] = nums[j];
					j=j-dk;
					}
				nums[j+dk] = tmp;
			}
		}
	}

总结优化过程

冒泡------选择排序------内部有序判断
快排------三值取中------交换改赋值------减少递归次数
简单插入排序------折半优化插入排序------希尔排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值