蒋豆芽的面试题专栏(30/数据结构与算法)

  1. 说说什么是稳定的排序?⭐⭐⭐⭐⭐

  2. 说说动态规划算法⭐⭐⭐⭐⭐

  3. 手撕归并排序⭐⭐⭐⭐⭐

  4. 手撕快速排序⭐⭐⭐⭐⭐

  5. 手撕插入排序⭐⭐⭐⭐⭐

  6. 手撕堆排序⭐⭐⭐⭐⭐

  7. 手撕二分查找⭐⭐⭐⭐⭐

  8. 快排最差时间复杂度,堆排最差时间复杂度?⭐⭐⭐⭐⭐

  9. 说说各排序算法的时间复杂度?⭐⭐⭐⭐⭐

=========================================================================================================

  • 本专栏适合于C/C++已经入门的学生或人士,有一定的编程基础。
  • 本专栏适合于互联网C++软件开发、嵌入式软件求职的学生或人士。
  • 本专栏针对面试题答案进行了优化,尽量做到好记、言简意赅。这才是一份面试题总结的正确打开方式。这样才方便背诵
  • 针对于非科班同学,建议学习本人专刊文章《蒋豆芽的秋招打怪之旅》,该专刊文章对每一个知识点进行了详细解析。
  • 如专栏内容有错漏,欢迎在评论区指出或私聊我更改,一起学习,共同进步。
  • 相信大家都有着高尚的灵魂,请尊重我的知识产权,未经允许严禁各类机构和个人转载、传阅本专栏的内容。

=========================================================================================================

  1. 说说什么是稳定的排序?⭐⭐⭐⭐⭐

    • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
    • 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。

    冒泡排序、归并排序是稳定排序;快速排序是不稳定排序。

  2. 说说动态规划算法⭐⭐⭐⭐⭐

    暴力解法有很多重复计算,动态规划就是为了帮助我们减少重复计算,所以动态规划提前存储前面计算的值,后面的值来源于已经存储的值,或者只需要在已经存储的值的基础上简单的叠加,这就是动态规划的本质,空间换时间
    三个非常重要的领悟:
    1、动态规划应用于存在重复子结构的问题中,避免重复计算法
    2、动态规划空间换时间
    3、动态规划提前存储前面计算的值,后面的值来源于已经存储的值,或者只需要在已经存储的值的基础上简单的叠加

  3. 手撕归并排序⭐⭐⭐⭐⭐

    class Solution {
        void mergeSort(vector<int>& nums, vector<int>& temp, int l, int r){
            if (l >= r) return;
            int mid = (l + r) / 2;
            mergeSort(nums, temp, l, mid);
            mergeSort(nums, temp, mid+1, r);
            int i=l,j=mid+1; int t = 0;
            while (i<=mid && j<=r){
                if (nums[i] <= nums[j]) temp[t++] = nums[i++];
                else temp[t++] = nums[j++];
            }
            while (i <= mid) temp[t++] = nums[i++];
            while (j <= r) temp[t++] = nums[j++];
    
            for (int i=l,t=0; i<=r; i++)
                nums[i] = temp[t++];
        }
    public:
        vector<int> sortArray(vector<int>& nums) {
            if (nums.empty()) return {};
            vector<int> temp(nums.size(), 0);
            mergeSort(nums, temp, 0, nums.size()-1);
            return nums;
        }
    };
    // 时间复杂度:O(nlogn)
    // 空间复杂度:O(n)
  4. 手撕快速排序⭐⭐⭐⭐⭐

    //快排递归实现
    class Solution {
        void quickSort(vector<int>& nums, int l, int r){
            if (l >= r) return;
            int mark = nums[l];
            int mark_ptr = l;
            for (int i=l; i<=r; i++){
                if (nums[i] < mark){
                    mark_ptr++;
                    swap(nums[i], nums[mark_ptr]);
                }
            }
            swap(nums[mark_ptr], nums[l]);
            q

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

- 本专栏适合于C/C++已经入门的学生或人士,有一定的编程基础。 - 本专栏特点: 本专刊囊括了C语言、C++、操作系统、计算机网络、嵌入式、算法与数据结构、数据库等一系列知识点,总结出了高频面试考点(附有答案)共计309道,事半功倍,为大家春秋招助力。 - 本专栏内容分为七章:共计309道高频面试题(附有答案)

全部评论
数据结构与算法方面的面试题,我们就总结完了,后面继续更新数据库的面试题。
1 回复 分享
发布于 2021-03-10 19:35
感谢博主
1 回复 分享
发布于 2021-03-15 18:49
这里我想分享下堆排。有几点确实思想挺好的。(1)二叉树的顺序存储--数组,一般二叉树以链表基础,因为对于稀二叉树用数组存储会造成很大的空间浪费,但是稠密二叉树,例如堆排,用顺序存储是很合适的(2)堆排有三步:建立大顶堆--向下调整[xx,end]、num[i]和num[0]交换、向下调整[0,i-1](3)向下调整到底在干嘛?就是一个反复的交换的过程,把两个孩子结点的最大值和父结点交换。整体而言,如一个简单的交换int temp = nums[k];nums[k]=nums[i];nums[i]=temp;就三句代码,但是堆排把它们拆分三部分,位于代码的开始、中间、最后,在循环中完成反复交换--这也是最有意思的。(4)第一步,建堆[n/2->0, end],从n/2开始一直到0,调整堆的范围都是到最后n-1,为什么是从n/2开始?很简单,n/2是最最后一个非叶节点,之后都是叶子节点,位于二叉树最底层,不需要移动位置。(2)交换后重新调整堆,由于最后已经有序,所以范围都是从0开始,到当前i位置(3)时间复杂度是归纳出来的O(nlogn)(3)代码在git上,欢迎给我反馈bug
1 回复 分享
发布于 2021-05-13 20:45
快排中的划分,在数据结构(C语言半)的274页是很经典的,基于此可以删改几句就形成k大、k小,因此需要把经典的代码手写出来并研究规律,找出最简单记忆方法,https://gitee.com/ve2102388688/leetcode中的sort文件夹有大部分排序代码,有问题可以反馈
1 回复 分享
发布于 2021-05-13 21:16
补充下,动态规划。特点1:一般是求最值问题,是一种智慧的穷举;特点2:存在重叠子问题及最优子结构,(1)最优子结构通俗的将就是分解的子问题是相互独立的,比如已知班级最高分求解全校最高分(这其实不是这个动态规划的问题,其实我想说的是最优子结构不是动态规划特有的)(2)重叠子问题:最简单的例子是斐波那契数列,画出递归树很容易知道有很多重叠子问题,以至于其递归算法是指数的复杂度,解决重叠子问题有两种思路:自上而下--备忘录,自下而上--动态规划;一般用的自下而上,但是不是所有的问题都可以用自下而上,这是就用自上而下。动态规划思路:定义dp数组及明确初始条件;状态转移;确定遍历方向及返回值;(能否状态压缩?--可选)
点赞 回复 分享
发布于 2021-05-13 16:26
我想给出一写建议--关于排序。简单插入和并归排序写的逻辑强,可读性也好,但是(1)快排中的划分有更清晰的写法,同时改改就可以写出k大和k小(2)堆排的向下建堆其实不需要一直执行到末尾,这里最好是一个区间。为什么呢?第一步建立大顶堆是[xxx,end],但是排序的时候取出堆顶放到最后时候,调整[xx,不再是end]了
点赞 回复 分享
发布于 2021-05-13 20:23
快排和并归的时间复杂度:T(n)=2*T(n/2)+O(n),根据主定理结果是O(nlogn),表示n个数据排序等价于2个n/2个数据排序+处理时间n(合并时间--并归,划分时间--划分),这里是n的原因,因为while(low小于high),每次合并/划分都是当前整个区间。这里提及下一个小问题:如果没有最后一项,T(n)=2*T(n/2)这种其实没有意义,一直对原问题分解,但是不处理,相当于没有任何改变--输入啥输出啥,所以递归代码的核心是最后一项,它的处理表达了递归函数的含义,比如斐波那契数列:F(n)=F(n-1)+F(n-2),即T(n)=2*T(n)+O(1),O(1)就是那个加法,明显是2的指数复杂度
点赞 回复 分享
发布于 2021-05-14 10:04
一个错误,快排空间复杂度是O(logn),其实logn大多数和树、二分有关。因为上面提及了不断把原问题分解成两个子问题,比如16每次除以2,什么时候到1呢?4次,因为2^4=16,进而4=log(16),其实这个4是二叉树的树高,也就是递归的深度,每下一层就是新的一次递归,也就是空间复杂度,因为快排的递归树高范围[logn,n],n是单支树,可以说是二叉树退化成链表,或者数组已经有序等等,所以平均是O(logn),这些内容在算法导论上都有,有闲下时间可以了解下
点赞 回复 分享
发布于 2021-05-14 10:13

相关推荐

03-03 14:59
重庆大学 后端
红鲤鱼与绿鲤鱼i:双一流后面为啥加ab,有点画蛇添足
点赞 评论 收藏
分享
评论
4
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务