Q4:站在高中生的角度如何计算时间复杂度?

一、算法复杂度是什么?

  •  算法复杂度分为时间复杂度和空间复杂度。 

       时间复杂度是指执行算法所需要的计算量(一段代码执行的次数多少透露出消耗的时间);

       空间复杂度是指执行这个算法所需要的内存空间(一个算法在运行过程中临时占用存储空间大小的量度)。直接插入排序的空间复杂度是O(1) 。而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息。

       问题1:直接插入排序的空间复杂度是O(1) ,为什么呢?

       关于O(1)的问题, O(1)是说数据规模和临时变量数目无关,并不是说仅仅定义一个临时变量。举例:无论数据规模多大,我都定义100个变量,这就叫做数据规模和临时变量数目无关。就是说空间复杂度是O(1)。 也就是说: 当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1);

      问题2:排序算法的空间复杂度难道不都是O(n)吗?

            

       看看网上这张图,空间复杂度并不是我想象的那样,对数据分配空间的大小呀o(╥﹏╥)o。那是怎样计算的呢?

        啊啊啊,我是来说时间复杂度的呀!

        问题3:稳定性又是如何判断?

        排序前后两个相等的数相对位置不变,则算法稳定。那就看具体算法如何实现了。

        啊啊啊,我是来说时间复杂度的呀!

二、时间复杂度如何计算?

        1.可爱的人都会发现上面的表格中时间复杂度,分为三种,那就说说他们的符号表示:

        Θ是平均时间复杂度(既是上界也是下界),O是最坏情况下的复杂度(表示上界),Ω是最好情况下的复杂度(表示下界)

        2.从快速排序法来说说吧?

        ①手撕代码如下:

 <script>
        
        // 交换函数
        function swap(arr, left, right) {
            let temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
        }

        // 分(最终基准值)函数
        function partiition(arr, left, right) {
            let point;

            // let point = arr[left]; 不优化默认为 左边第一个

            //基准点的优化--取一个元素作为基准点
            let m = left + parseInt((right - left) / 2);//不要忘了不是parseInt((right - left) / 2)哦,对于二分,右边要有偏移值哦,即+left
            if (arr[left] > arr[right]) {
                swap(arr, left, right);
            }
            if (arr[m] > arr[right]) {
                swap(arr, m, right);
            }
            if (arr[left] < arr[m]) {
                swap(arr, left, m);
            }
            point = arr[left]; //保证基准点是 三个数的 中间大 的那个 ,降低时间复杂度(如果一个数组是升序或降序) -----哈哈哈 先把基准值存起来

            while (left < right) {
                while (left < right && arr[right] >= point) {//过滤比基准点大的
                    right--;
                }
                // swap(arr, left, right); // 小 交换

                //无用交换的优化--赋值-------哈哈哈,把位置(右)空出来
                arr[left] = arr[right];

                while (left < right && arr[left] <= point) {
                    left++;
                }
                // swap(arr, left, right);

                //无用交换的优化--赋值-------哈哈哈,把位置(左)空出来,把空位置(右)填上左边找的大的数
                arr[right] = arr[left];
            }

            //无用交换的优化--记得赋值将基准值位置定下来(不会动了)-------哈哈哈,位置(此时左=右)空出来的基准值来凑
            arr[left] = point;//left和right指针重合,即也可以 arr[right] = point;

            return left; // 返回基准点位置
        }

        //快排
        function quickSort(arr, left, right) {
            let len = arr.length;
            let point;

            // 参数验证
            left = typeof left != 'number' ? 0 : left;
            right = typeof right != 'number' ? len - 1 : right;

            // 递归终止条件
            if (left >= right) return;

            // 重要部分
            point = partiition(arr, left, right);
            quickSort(arr, left, point - 1);
            quickSort(arr, point + 1, right);

            return arr;
        }

        // 随机生成 长度为len且值为任意值(正、0、负)的数组
        function generateRandomArr(len) {
            let arr = [];
            for (i = 0; i < len; i++) {
                let item = Math.round(Math.random() * 1000)-500;//[-500,500]
                arr.push(item);
            }
            return arr;
        }
        let len = Math.ceil(Math.random() * 100);//len>0 ,意味着数组非空
        console.log(quickSort(generateRandomArr(len)));
       
    </script>

    ②快排空间复杂度不稳定,怎么说呢?

        是因为不稳定发生在基准值ponit和arr[left==right] 交换的时刻,对应代码就是 【arr[left] = point;//left和right指针重合,即也可以 arr[right] = point;】,就是在基准值和arrleft==right]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5, 3, 1 ,1,  8 ,1,6,7,现在基准值5(第1个元素)和1(第6个元素计)交换就会把元素1的稳定性打乱。

    ③快排的最坏复杂度怎么说呢?

      比如 一个数组已经是正序或逆序-----时间复杂度都是O(n²)

      如何理解O(n²)呢?

     对于正序:比如 1,2,3,4,5-->快排每趟确定一个基准,依次确定是 1,2,3,4,5 ,范围变化【1,【2,【3,【4,【5】】】】】 所以是n*n

     对于逆序:比如5,4,3,2,1-->快排每趟确定一个基准, 依次确定是5,1,4,2,3 ,范围变化【【4,3,2,1】,5】这个不太好用中括号一次性表示,明白意思就行  所以是n*n 

     注:每趟:指跑遍数据一圈,导致其分治思想一边倒,从而时间复杂度太高。

    ④分治法是什么?

       分治法其实就是一种策略。具体一下理解:

       分:一个问题分成子问题。---------------------------对应快排:意思就是将数组分为两个数组

       治:递归的解决每个子问题。------------------------对应快排:意思就是每个子数组的排序

       合:将每个子问题的解合并成大问题的解。------对应快排:意思就是合并两个有序的数组(表象)

   ⑤快排的时间复杂度是多少呢?是O(nlogn)又是怎么计算的呢?

       可以用快排时间复杂度的递推关系式:T(n)=2⋅T(n/2)+O(n)   ,啊,补充一下“主定理”知识点

   ⑥主定理是什么?

      递推关系式  ,其中  为问题规模,  为地递推的子问题数目,  为每个子问题的规模(假设每个子问题的规模基本一样),  为递推以外(就是分、治、合三个中不包括分)进行的计算量(也就是分治的运算时间)。其中f(n)满足:

      

         

     所以,对于快排,有先分为两个子数组,也就是两个子问题规模,所以a=2,每个子问题规模,也就是每个数组在相等(或差不多相等)情况下都看做n/2个数,所有n/b=n/2,还有附加计算量:分(递归不算)、治(排序)为 n 、合(已经是左边小右边大不用手动合并,也不算)所以f(n)=O(n)  所以快排时间复杂度的递推关系式:T(n)=2⋅T(n/2)+O(n),又因为O(n)==n,所以快排时间复杂度=O(nlogn)。

   ⑦logn是什么意思?

      见过lgn代表以10为底,lnn代表以e为底,那logn是什么呢?

      这个就要用到我们高一学的换底公式:loga^b=logc^b/logc^a,所以比如时间复杂度t=log2^n=log8^n/log8^2,又因为计算时间复杂度是针对一个大大大数组,常数项可以忽略,所以t=log2^n=log8^n,那么你发现有没有底数都无所谓,所以logn就是底数无所谓取就行。

 时间复杂度总算是计算出来了。

   ⑧上面用的主定理,下面我们用通项公式计算一下(做个高中题吧)

        

 ⑨那我问用递归树分析一下(用一下高中的极限思想),计算时间复杂度。

       

       想想看,时间复杂度就是这么一回事。

 ⑩ 最后分治法中最重要的还是递归哦。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值