当前位置: 首页 > article >正文

快速排序(二叉树的前序递归遍历思想)

思路

之前我们从选择排序,到选择排序的稳定性优化,到冒泡排序,到插入排序,到插入排序的提前截止时间,到希尔排序,虽然逐步一直都在优化,但是时间复杂度还是N得平方,力扣提交的结果一直都是时间超时,因为时间复杂度并没有发生量级级别的减少,都是通过常规的优化思维,说白一点,就是没有创新的优化点,都是一步一步,一点一点优化,想方设法能能提高一点效率就提高一点。
那么从快速排序就是要开始坐飞机了往前冲了,直接打破一个量级的时间复杂度,从N的平方,到Nlogn,N就是二叉树的深度,logn就是每一层的时间复杂度。
为什么说快速排序是结合了二叉树前序递归思想的排序,后面我把快速排序的代码写出来吗,你对比一下二叉树的前序遍历,结构基本都是差不多的。
快速排序的解题思路就是:一般默认选择第一个数组元素作为起点,将第一个元素处理一下,使得左边的元素都小于它,右边的元素都大于它。第二就开最左右的数组继续处理,左边的数据选择当前第一个元素再左边找到位置是的左边都小于它,右边的都大于它,右边同理。你看这不就是二叉树的前序递归遍历吗?
接下来直接看代码,如果说你明白了在快排中使用到了二叉树的前序遍历思想,那你成功的解决了50的问题,剩下的50%是看你嫩不能解决数组中的一个点,如何找到它所在的位置,并且交换好数据吗,这个还需要主要的是一个数组的边界问题(如果数组题好好刷了,知道数组边界的处理,不混乱),那第二个难点也就不是什么难点,我还是讲一下找到中间位置的函数的思路吧,但是边界为就不讲了,这块儿还是有点技巧的,回头再来刷这个题目的时候再看吧

代码

class Solution {
    public int[] sortArray(int[] nums) {
        sort(nums,0,nums.length-1);
        return nums;
    }

    //定义一个递归遍历的函数sort
    void sort(int[] nums,int lo,int hi){
        if(lo > hi){
            return;
        }
        int p = partition(nums, lo, hi);
        sort(nums,lo,p-1);
        sort(nums,p+1,hi);
    }

    //定义一个找到位置的partition函数
    int partition(int[] nums, int lo, int hi){
        int pivot = nums[lo];
        // 关于区间的边界控制需格外小心,稍有不慎就会出错
        // 我这里把 i, j 定义为开区间,同时定义:
        // [lo, i) <= pivot;(j, hi] > pivot
        // 之后都要正确维护这个边界区间的定义
        int i = lo + 1, j = hi;
        // 当 i > j 时结束循环,以保证区间 [lo, hi] 都被覆盖
        while (i <= j) {
            while (i < hi && nums[i] <= pivot) {
                i++;
                // 此 while 结束时恰好 nums[i] > pivot
            }
            while (j > lo && nums[j] > pivot) {
                j--;
                // 此 while 结束时恰好 nums[j] <= pivot
            }

            if (i >= j) {
                break;
            }
            // 此时 [lo, i) <= pivot && (j, hi] > pivot
            // 交换 nums[j] 和 nums[i]
            swap(nums, i, j);
            // 此时 [lo, i] <= pivot && [j, hi] > pivot
        }
        // 最后将 pivot 放到合适的位置,即 pivot 左边元素较小,右边元素较大
        swap(nums, lo, j);
        return j;
    }

    // 原地交换数组中的两个元素
    private static void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

在这里插入图片描述
这个运行后还是超出运行限制,到底是哪里还可以继续优化呢,肯定是可以的,我们先不纠结了,能和面试官谈到这里,说不定他还没有懂得深的。差不多了,优化的点我先提一下,就是在上面的基础上加了一个洗牌算法
直接看代码:

class Solution {
    public int[] sortArray(int[] nums) {
        // 为了避免出现耗时的极端情况,先随机打乱
        shuffle(nums);
        sort(nums,0,nums.length-1);
        return nums;
    }

    //定义一个递归遍历的函数sort
    void sort(int[] nums,int lo,int hi){
        if(lo > hi){
            return;
        }
        int p = partition(nums, lo, hi);
        sort(nums,lo,p-1);
        sort(nums,p+1,hi);
    }

    //定义一个找到位置的partition函数
    int partition(int[] nums, int lo, int hi){
        int pivot = nums[lo];
        // 关于区间的边界控制需格外小心,稍有不慎就会出错
        // 我这里把 i, j 定义为开区间,同时定义:
        // [lo, i) <= pivot;(j, hi] > pivot
        // 之后都要正确维护这个边界区间的定义
        int i = lo + 1, j = hi;
        // 当 i > j 时结束循环,以保证区间 [lo, hi] 都被覆盖
        while (i <= j) {
            while (i < hi && nums[i] <= pivot) {
                i++;
                // 此 while 结束时恰好 nums[i] > pivot
            }
            while (j > lo && nums[j] > pivot) {
                j--;
                // 此 while 结束时恰好 nums[j] <= pivot
            }

            if (i >= j) {
                break;
            }
            // 此时 [lo, i) <= pivot && (j, hi] > pivot
            // 交换 nums[j] 和 nums[i]
            swap(nums, i, j);
            // 此时 [lo, i] <= pivot && [j, hi] > pivot
        }
        // 最后将 pivot 放到合适的位置,即 pivot 左边元素较小,右边元素较大
        swap(nums, lo, j);
        return j;
    }

    // 原地交换数组中的两个元素
    private static void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    // 洗牌算法,将输入的数组随机打乱
    private static void shuffle(int[] nums) {
        Random rand = new Random();
        int n = nums.length;
        for (int i = 0 ; i < n; i++) {
            // 生成 [i, n - 1] 的随机数
            int r = i + rand.nextInt(n - i);
            swap(nums, i, r);
        }
    }
}

加上洗牌算法果然就通过了
在这里插入图片描述
接下来就继续分析一下,为什么加了一个洗牌算法就能提交通过呢,这里面到底优化什么?
明天继续~


http://www.kler.cn/a/579606.html

相关文章:

  • 【three.js】动画系统完全指南 - 从事件循环到工业级动画架构
  • MobileBERT: 一种适用于资源有限设备的紧凑型任务无关BERT
  • 关于C/C++语言的初学者在哪刷题,怎么刷题
  • 软件系统压力测试方案,压力测试报告模版(Word原件)
  • OSPF-单区域的配置
  • 反射是什么?
  • 数学建模-1:对变化建模
  • Python正则表达式完全指南:从入门到精通
  • 【Linux网络(一)】初始网络
  • Linux:多线程(单例模式,其他常见的锁,读者写者问题)
  • ESP8266UDP透传
  • 华为Mate 60 Pro+ 等机型适配支持运营商北斗卫星短信功能
  • C++:vector容器(下篇)
  • Milvus的匹配语法
  • 二维码识别OCR接口:开启高效信息提取的新篇章
  • RK Android14 在计算器内输入特定字符跳转到其他应用
  • 文件上传漏洞测试
  • Java 大视界 -- Java 大数据在智慧交通信号灯智能控制中的应用(116)
  • TCP/IP 5层协议簇:网络层(ICMP协议)
  • 论文阅读-秦汉时期北方边疆组织的空间互动模式与直道的定位(中国)