【LeetCode】《LeetCode 101》第二章:最易懂的贪心算法

news2024/9/28 9:22:08

文章目录

  • 2.1 算法解释
  • 2.2 分配问题
    • 455. 分发饼干 (简单)
    • 135. 分发糖果 (困难)
  • 2.3 区间问题
    • 435. 无重叠区间(中等)
  • 2.4 练习
    • 605. 种花问题(简单)
    • 452. 用最少数量的箭引爆气球(中等)
    • 763. 划分字母区间(中等)
    • 122. 买卖股票的最佳时机 II (中等)
    • 406. 根据身高重建队列(中等)
    • 665. 非递减数列(中等)
  • 2.5 总结

2.1 算法解释

顾名思义,贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的。

2.2 分配问题

455. 分发饼干 (简单)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. 题解

    因为饥饿度最小的孩子最容易吃饱,所以我们先考虑这个孩子。为了尽量使得剩下的饼干可以满足饥饿度更大的孩子,所以我们应该把大于等于这个孩子饥饿度的、且大小最小的饼干给这个孩子。满足了这个孩子之后,我们采取同样的策略,考虑剩下孩子里饥饿度最小的孩子,直到没有满足条件的饼干存在。

    简而言之,这里的贪心策略是,给剩余孩子里最小饥饿度的孩子分配最小的能饱腹的饼干

    至于具体实现,因为我们需要获得大小关系,一个便捷的方法就是把孩子和饼干分别排序。这样我们就可以从饥饿度最小的孩子和大小最小的饼干出发,计算有多少个对子可以满足条件。

  2. 代码

    class Solution {
    public:
        int findContentChildren(vector<int>& g, vector<int>& s) {
            int ans = 0;
            sort(g.begin(), g.end());
            sort(s.begin(), s.end());
            int i = 0;
            for(int biscuit : s){
                if(i < g.size() && biscuit >= g[i]){
                    ans ++;
                    i ++;
                }
            }
            return ans;
        }
    };
    

135. 分发糖果 (困难)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 题解

    做完了题目455,你会不会认为存在比较关系的贪心策略一定需要排序或是选择?虽然这一道题也是运用贪心策略,但我们只需要简单的两次遍历即可︰

    • 把所有孩子的糖果数初始化为1
    • 从左往右遍历一遍,如果右边孩子的评分比左边的高,则右边孩子的糖果数更新为左边孩子的糖果数加 1 ;
    • 从右往左遍历一遍,如果左边孩子的评分比右边的高,且左边孩子当前的糖果数不大于右边孩子的糖果数,则左边孩子的糖果数更新为右边孩子的糖果教加1。
    • 通过两次遍历分配的糖果就可以满足题目要求了。这里的贪心策略即为,在每次遍历中,只考虑并更新相邻一侧的大小关系。
  2. 代码

    class Solution {
    public:
        int candy(vector<int>& ratings) {
            int n = ratings.size();
            vector ans(n, 1);
            // 从左往右遍历
            for(int i=0; i<n-1; ++i){
                if(ratings[i+1] > ratings[i]){
                    ans[i+1] = ans[i] + 1;
                }
            }
            // 从右往左遍历
            for(int i=n-1; i>0; --i){
                // 左边孩子评分比右边孩子高 且 左边孩子糖果数不大于右边孩子   
                if(ratings[i-1] > ratings[i] && ans[i-1] <= ans[i]){
                    ans[i-1] = ans[i] + 1;
                }
            }
            for(int i=0; i<n; ++i){
                cout<<ans[i]<<" ";
            }
            int sum = accumulate(ans.begin(), ans.end(), 0);
            return sum;
        }
    };
    
  3. 收获

    • 复习了 accumulate(ans.begin(), ans.end(), 0);

2.3 区间问题

435. 无重叠区间(中等)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 题解

    • 求最少的移除区间个数,等价于尽量多保留不重叠的区间。在选择要保留区间时,区间的结尾十分重要:选择的区间结尾越小,余留给其它区间的空间就越大,就越能保留更多的区间。因此,我们采取的贪心策略为,优先保留结尾小且不相交的区间
    • 具体实现方法为,先把区间按照结尾的大小进行增序排序,每次选择结尾最小且和前一个选择的区间不重叠的区间。我们这里使用C++的Lambda,结合std : :sort()函数进行自定义排序。
    • 在样例中,排序后的数组为[[1.2],[1.3],[2.4]]。按照我们的贪心策略,首先初始化为区间[1.2];由于[1,3]与[1,2]相交,我们跳过该区间;由于[2.4]与[1,2]不相交,我们将其保留。因此最终保留的区间为[[1,2],[2,4]]。
  2. 代码

    class Solution {
    public:
        int eraseOverlapIntervals(vector<vector<int>>& intervals) {
            sort(intervals.begin(), intervals.end(), [](vector<int>&a, vector<int>&b){return a[1]<b[1];});
            int ans = 0;
            int prev = intervals[0][1];
            for(int i=1; i<intervals.size(); ++i){
               if(intervals[i][0] < prev){
                   ans ++;
               }
               else{
                   prev = intervals[i][1];
               }
            }
            return ans;
        }
    };
    
  3. 收获

    • 复习了对二维数组的第二个维度进行排序的方法:sort(intervals.begin(), intervals.end(), [](vector<int>&a, vector<int>&b){return a[1]<b[1];});

2.4 练习

605. 种花问题(简单)

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

  1. 题解

    • 为了判断能否在不打破规则的情况下种入 n 朵花,从贪心的角度考虑,应该在不打破规则的情况下尽可能地种花,然后判断种入的花是否大于等于 n。
    • 为了方便后续判断,可以先设置边界条件,当 i 为 0 的时候,将 prev 设置为 0;当 i 为 1 的时候,将 later 设置为 0;
    • 遍历数组的时候,如果当前遍历到的值为 0 , 说明可能可以种花,此时继续判断它的 prev 和 later 是否也都是 0,如果符合条件,那么说明此处可以种下一朵花, n – ;如果当前遍历到的值为 1 ,说明这个地方和下一个位置都不能种花, 那就直接跳过两个位置,i++ ,for 循环里会再自加一次,因此一共自加了两次。
    • 最后判断 n 是否小于等于 0 ,表示能够在不打破规则的情况下种入 n 多花,否则表示不能, 返回 false。
  2. 代码

    class Solution {
    public:
        bool canPlaceFlowers(vector<int>& flowerbed, int n) {
            int len = flowerbed.size();
            for(int i=0; i<len; ++i){
                int prev = i == 0? 0 : flowerbed[i-1];
                int later = i == len-1? 0 : flowerbed[i+1];
                if(flowerbed[i] == 0){
                    if(!prev && !later){
                        n-- ;
                        flowerbed[i] = 1;
                    }
                }
                if(flowerbed[i] == 1)
                    ++i;
            }
            return n<=0;
        }
    };
    
  3. 收获

    • 能够模仿着前几天的题设置边界条件。

452. 用最少数量的箭引爆气球(中等)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 题解

    • 这道题和 435 类似,但是这道题需要按照区间开头排序
    • 重叠的区间用一支箭就可以引爆,不重叠的区间需要单独用一根箭引爆。求最小的弓箭数,等价于求尽可能多的区间重叠个数
    • 选择区间左端升序排序,然后判断当前区间是否和上一个区间有重叠 if(points[i][0] <= prev),需要满足当前区间的左边界 <= 上一区间的右边界。
      • 如果存在重叠,说明这两个区间用一支箭就可以引爆。由于加入了新的区间,此时需要更新更新右边界,prev = min(prev, points[i][1]);
      • 如果不存在重叠,那么当前区间和上一区间需要用不同的箭引爆,因此箭的数量 ans ++,更新上一个区间的右边界 prev = points[i][1];
  2. 代码

    class Solution {
    public:
        int findMinArrowShots(vector<vector<int>>& points) {
            int ans = 1;
            sort(points.begin(), points.end());
            int prev = points[0][1];
            for(int i=1; i<points.size(); ++i){
                if(points[i][0] <= prev){
                    prev = min(prev, points[i][1]);
                }
                else{
                    ans ++;
                    prev = points[i][1];
                }
            }
            return ans;
        }
    };
    
  3. 收获

    • 这道题我用了左端升序排序,但是题解很多用右端升序排序。不过思路都是类似的,就不多做记录了。

763. 划分字母区间(中等)

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

  1. 题解

    • 划分字符串,要得到尽可能多的片段,并且同一字母只能出现在一个片段中。因此每个片段要尽可能小,也就是说一个片段中包含尽可能少的字母,那么就能得到尽可能多的片段。
    • 考虑某个字符 ch ,使用函数 s.find_first_of(ch) 找到该字符第一次出现的位置,记为 begin ,使用函数 s.find_last_of(ch) 找到该字符最后一次出现的位置, 记为 end ,这样就划分出了一个片段。
    • 接着考虑 [begin, end] ,判断其中包含的字符是否仅出现在这个片段 ,通过 pos = s.find_last_of(ch) 来确定该字符最后一次出现的位置,并与 end 做比较,如果end < pos, 说明该字符在该片段后还有出现,此时需要更新 end ,扩大片段范围 ,end = max((int)s.find_last_of(s[i]), end);
    • 在扩大片段范围的过程中,为了不重复判断同一字符,我使用了哈希表 cnt 来记录字符是否出现过。
    • 当一次 for 循环结束, 说明我们找到了当前可划分的最小片段,此时我们计算该片段的长度,并存入答案数组 ans 中。如果当前已经把整个字符串 s 都遍历了,那么直接返回答案数组;否则更新下一个片段的第一个字符 ch = s[end + 1];
  2. 代码

    class Solution {
    public:
        vector<int> partitionLabels(string s) {
            vector<int> ans;
            vector<int> cnt(26);
            char ch = s[0];
            cnt[ch - 'a'] = 1;
            while(1){
                int start = s.find_first_of(ch);
                int end = s.find_last_of(ch);
                for(int i=start+1; i<=end; ++i){
                    if(!cnt[s[i] - 'a']){
                        // 说明还未遍历
                        end = max((int)s.find_last_of(s[i]), end);
                        // int newEnd =  s.find_last_of(s[i]);
                        // if(newEnd > end)   end = newEnd;
                        cnt[s[i] - 'a'] = 1;
                    }
                }
                ans.push_back(end - start + 1);
                if(end == s.size() - 1) return ans;
                ch = s[end + 1];
            }
        }
    };
    
  3. 收获

    • 这道题学习了 string.find() 的变形,也就是 s.find_first_of()s.find_last_of() ,但是它们的返回类型为size_type ,并不是完全等同于 int ,因此在判断是否更新 end 的时候,需要将返回值强制转换成 int ,才可以使用 max 函数。

122. 买卖股票的最佳时机 II (中等)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 题解

    • 如果要使得利润最大,那么就要使能够产生利润的两个天数差距越小,也就是卖出的时间尽可能接近购买的时间,为后续利润增加留出更多的空间。
    • 这里我用 prev 记录前一天的价格,如果当天价格 prices[i] 大于 prev ,说明这时候卖出去会产生利润,因此更新当前的利润值,并更新 prev ,prev = prices[i]; ,注意 ,由于题目中提到在当天抛售完这支股票后可以再次买入,因此无论当天是否售出股票,都要更新 prev 为当天的价格。
  2. 代码

    class Solution {
    public:
        int maxProfit(vector<int>& prices) {
            int ans = 0;
            int n = prices.size();
            int prev = prices[0];
            for(int i=1; i<n; ++i){
                if(prices[i] > prev){
                    ans += prices[i] - prev;
                }
                prev = prices[i];
            }
            return ans;
        }
    };
    
  3. 收获

    • 这题一开始我还复杂化了,想着要去排序,然后使用字典记录价格和对应的时间,事实上简单很多。
    • 看了一下题解,有一个值得理清的点,利润增加可以分成两种情况:
      • 单独交易日,当天买入,隔天售出:profit = prices[i] - prices[i-1];

      • 连续上涨交易日,当天买了,隔几天才售出,profit = prices[i] - prices[j] = (prices[i] -prices[i-1]) + (prices[i-1] - prices[i-2]) + ... + (prices[j+1] - prices[j])

        这个公式佐证了我的题解的正确性。

406. 根据身高重建队列(中等)

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. 题解

    • 题目的意思不难理解,面对有两个维度的贪心问题,通常有个诀窍:其中一个维度按照升序排序,另外一个维度按照降序排序
    • 这道题可以选择按照 h 降序排序, 按照 k 升序排序
      • 按照 h 降序排序,可以保证后面加入的元素都不会影响当前元素的位置;
      • 按照 k 升序排序,既减少了插入的次数,也保证了正确性。比如 [5,2] , [5,3] ,如果我们按照 k 降序排序的话,即 [5,3], [5,2],先将 [5,3] 插入在 idx = 3 的位置上,再将 [5,2] 插入在 idx = 2 的位置上,此时 [5,3] 前面就有 4 个大于或等于它的元素,和 自身位置矛盾。
    • 对于答案数组,如果当前元素的 k 大于或等于 ans 的长度,说明前面元素的个数还没有超过当前元素的 k,所以该元素可以直接插入到 ans 的末尾;否则 ans 插入到 index = k 的位置上。
  2. 代码

    class Solution {
    public:
        vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
            // 按照第一维度 h 降序,第二维度 k 升序
            sort(people.begin(), people.end(), [](const vector<int> &a, const vector<int> &b){
                if(a[0] == b[0])    return a[1] < b[1];
                return a[0] > b[0];
            });
            vector<vector<int>> ans;
            for(auto p : people){
                if(p[1] < ans.size()){
                    // 插入
                    ans.insert(ans.begin() + p[1], p);
                }
                else ans.push_back(p);
            }
            return ans;
        }
    };
    
  3. 收获

    • 学会了二维数组的贪心处理诀窍,对两个维度都要排序;
    • insert 函数的使用 :ans.insert(ans.begin() + p[1], p);
    • 二维数组也是可以直接插入它的元素,无需再转换成一维数组,上次我可能是混淆了,比如 for(auto p : people) ,p 已经是一维数组了。

665. 非递减数列(中等)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 题解

    • 非递减数列,其实就是不严格的单调递增序列,只需要满足 nums[i] <= nums[i+1] 即可。 对于这道题,其实就是求出将该数组变成非递减数列所需要的最小次数,那么为了使得元素的改变次数最小,对于不满足题目要求的元素,如nums[i] > nums[i+1] ,我们就将其改为 nums[i] = nums[i+1]
    • 对于nums[i] > nums[i+1] 这种情况,我们既可以改变 nums[i] ,也可以改变 nums[i+1] 。因此我们分成两种情况考虑:
      • 从前往后遍历, 改变 nums[i+1]
      • 从后往前遍历, 改变 nums[i]
    • 最后取两种遍历情况改变元素的最小值,判断它是否 >= 1 。
  2. 代码

    class Solution {
    public:
        bool checkPossibility(vector<int>& nums) {
            int cnt1 = 0, cnt2 = 0;
            int n = nums.size();
            vector<int> copy(n);
            copy = nums;
            for(int i=0; i<n-1; ++i){
                if(nums[i] > nums[i+1]){
                    ++ cnt1;
                    nums[i+1] = nums[i]; 
                } 
            }
            for(int i = n-1; i>0; --i){
                if(copy[i] < copy[i-1]){
                    ++ cnt2;
                    copy[i-1] = copy[i]; 
                }
            }
            return min(cnt1, cnt2) <= 1;
        }
    };
    
  3. 收获

    • 看了题解的另外一种思考方法,每次考虑 连续的 3 个元素,遵循以下两个原则:
      • 需要尽可能不放大nums[i + 1],这样会让后续非递减更困难;
      • 如果缩小nums[i],但不破坏前面的子序列的非递减性
      if (nums[i] > nums[i + 1])  // 出现递减
      {
      	if (flag)   // 如果还有修改机会,进行修改
        	{
      	    if (nums[i + 1] >= nums[ i - 1])// 修改方案1
      			nums[i] = nums[i + 1];
            	else                            // 修改方案2
              	nums[i + 1] = nums[i];      
               	flag = false;                   // 用掉唯一的修改机会
           }   
      else        // 没有修改机会,直接结束
      	return false;
      

2.5 总结

用了三天才把贪心算法学完,确实不太难,而且代码也相对简练,练习题中只有 406 我是看了题解才明白,不过 655 的另外一种思路也很值得我借鉴。

总的来说有三种题型:

  • 一维数组的简单排序;
  • 区间问题,一般选择其中一个维度进行排序;
  • 二维数组,通常是其中一个维度升序,另外一个维度降序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/399195.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFmpeg介绍及入门知识

1、简介 FFmpeg是一套由c语言编写的&#xff0c;可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序,自身采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案&#xff0c;包含了非常先进的音频/视频编解码库libavcodec&#xf…

【Unity Android Platform:关于Android权限来源(安卓)如何查找】

Android权限来源 问题描述&#xff1a;当项目接入的插件逐渐变多&#xff0c;不仅仅是AndroidManifest会影响Android Permission的个数&#xff0c;甚至有些API也会影响最终的权限个数&#xff0c;例如下图所示&#xff1a; 当国内权限问题涉及到一些安全隐私问题时&#xff0…

构造函数与普通函数,显式原型与隐式原型,原型与原型链

原型与原型链1 学前先了解一些概念1.1 构造函数和普通函数的区别1.1.1 调用方式1.1.2 函数中this的指向不同1.1.3 写法不同1.2 问题明确2 原型与原型链2.1 原型2.2 显式原型与隐式原型2.3 原型链3 原型链环形结构1 学前先了解一些概念 1.1 构造函数和普通函数的区别 构造函数…

全流程基于最新导则下的生态环境影响评价技术方法及图件制作与案例

目录 专题一、生态环境影响评价框架及流程 专题二、基于遥感解译的土地利用现状图的编制 专题三、生物多样性测定及R语言分析 专题四、植被类型及植被覆盖度图的编制 专题五、生物量与净初级生产力测定&#xff1a;实测及模型 专题六、生态系统类型及服务价值评估 专题七…

MOT学习笔记 — 行人检测及行人跟踪数据集总结

1. 行人红外数据集总结 &#xff08;1&#xff09;OSU Thermal Pedestrian Database 下载链接&#xff1a;http://vcipl-okstate.org/pbvs/bench/Data/01/download.html &#xff08;2&#xff09;IRIS Thermal/Visible Face Database 下载链接&#xff1a;http://vcipl-o…

React 服务端渲染

React 服务器端渲染概念回顾什么是客户端渲染CSR(Client Side Rendering)服务器端只返回json数据&#xff0c;Data和Html的拼接在客户端进行&#xff08;渲染&#xff09;。什么是服务器端渲染SSR(Server Side Rendering)服务器端返回数据拼接过后的HTML&#xff0c;Data和Html…

Ubuntu20.04下安装vm17+win10/11

一、安装vmware17 1、官网下载 vmware官网&#xff1a;https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html 2、安装依赖 sudo apt update sudo apt install build-essential linux-headers-generic gcc make3、权限和安装 到下载的目录下…

vector你得知道的知识

vector的基本使用和模拟实现 一、std::vector基本介绍 1.1 常用接口说明 std::vector是STL中的一个动态数组容器&#xff0c;它可以自动调整大小&#xff0c;支持在数组末尾快速添加和删除元素&#xff0c;还支持随机访问元素。 以下是std::vector常用的接口及其说明&#xf…

品牌软文怎么写?教你几招

软文是什么&#xff1f;软文的本质就是广告&#xff0c;当然不是明晃晃的推销&#xff0c;而是自然隐晦地植入产品信息&#xff0c;引导更多用户自愿下单。 品牌软文对于写手的经验、内容的质量要求都相对较高&#xff0c;否则写出来的软文无法达到预期的效果。品牌软文怎么写…

一个古老的html后台的模板代码

效果图下&#xff1a; css部分代码&#xff1a;/* CSS Document / body{font-family:“宋体”, Arial,Verdana, sans-serif, Helvetica;font-size:12px;margin:0;background:#f4f5eb;color:#000;} dl,ul,li{list-style:none;} a img{border:0;} a{color:#000;} a:link,a:visit…

[css]通过网站实例学习以最简单的方式构造三元素布局

文章目录二元素布局纵向布局横向布局三元素布局b站直播布局实例左右-下 布局左-上下 布局上下-右 布局方案一方案二后言二元素布局 在学习三元素布局之前&#xff0c;让我们先简单了解一下只有两个元素的布局吧 两个元素的相对关系非常简单&#xff0c;不是上下就是左右 纵向布…

Anaconda配置Python科学计算库SciPy的方法

本文介绍在Anaconda环境中&#xff0c;安装Python语言SciPy模块的方法。 SciPy是基于Python的科学计算库&#xff0c;用于解决科学、工程和技术计算中的各种问题。它建立在NumPy库的基础之上&#xff0c;提供了大量高效、易于使用的功能&#xff0c;包括统计分析、信号处理、优…

用一个例子告诉你 怎样在spark中创建累加器

目录 1.说明 1.1 什么是累加器 1.2 累加器的功能 2. 使用累加器 3. 累加器和reduce、fold算子的区别 1.说明 1.1 什么是累加器 累加器是Spark提供的一个共享变量(Shared Variables) 默认情况下&#xff0c;如果Executor节点上使用到了Driver端定义的变量(通过算子传…

Redis常用命令及数据类型参数

1. 针对于string SET key value / GET key SET k1 v1 GET k1 // v1String是二进制安全的&#xff0c;是可变长度的&#xff0c; 底层类似于ArrayList 是可扩容的&#xff0c;最大存储内存为 512MB。 2. 判断key中是否存在某个内容 EXISTS key SET k1 v1 EXISTS k1 // …

Noah-MP陆面过程模型建模方法与站点、区域模拟

陆表过程的主要研究内容以及陆面模型在生态水文研究中的地位和作用 熟悉模型的发展历程&#xff0c;常见模型及各自特点&#xff1b; Noah-MP模型的原理 Noah-MP模型所需的系统环境与编译环境的搭建方法您都了解吗&#xff1f;&#xff1f; linux系统操作环境您熟悉吗&…

Linux驱动中的fasync(异步通知)和fsync

一、fsync用来同步设备的写入操作&#xff0c;考虑把一块设局写入到硬盘的操作&#xff0c;如果使用write函数&#xff0c;函数返回后只能保证数据被写入到驱动程序或者内核管理的数据缓存中&#xff0c;而无法保证数据被真正写入到硬盘的存储块里。但是fync可以做到这一点&…

查找、排序、二叉树的算法,统统记录于此。

文章目录一、查找1. 无序表的顺序查找2. 折半查找3. 分块查找4. 二叉排序树BST5. 哈希表查找二、排序1. 不带哨兵的直接插入排序2. 带哨兵的直接插入排序3. 带哨兵、折半查找的直接插入排序4. 希尔排序5. 冒泡排序6. 快速排序7. 选择排序8. 堆排序9. 归并排序二叉树1. 递归先序…

八,iperf3源代码分析:状态机及状态转换过程--->运行正向TCP单向测试时的客户端代码

本文目录一、测试用命令二、iperf3客户端状态机中各个状态解析状态机迁移图运行正向TCP单向测试时的客户端的状态列表三、iperf3客户端状态机迁移分析A-初始化测试对象&#xff08;NA--->初始化状态&#xff09;:B-建立控制连接&#xff0c;等待服务端PARAM_EXCHANGE的指令&…

西电机试数据结构核心算法与习题代码汇总(机考真题+核心算法)

文章目录前言一、链表问题1.1 反转链表1.1.1 题目1.1.2 代码1.2 多项式加减法1.2.1 题目1.2.2 代码二、队列和栈2.1 学生退学2.1.1 问题2.1.2 代码三、矩阵和串题目3.1 矩阵对角线求和3.1.1 问题3.1.2 代码四、排序问题4.1 多元素排序4.1.1 问题4.1.2 代码五、二叉树5.1 相同二…

synchronize优化偏向锁

偏向锁 轻量级锁在没有竞争时&#xff08;只有自己一个线程&#xff09;&#xff0c;仍然会尝试CAS替换mark word&#xff1b; 会造成一定的性能的损耗&#xff1b; JDK6之中引入了偏向锁进行优化&#xff0c;第一次使用时线程ID注入到Mark word中&#xff0c;之后重入不再进…