【C++代码】最接近的三数之和,括号生成,合并两个有序链表,合并 K 个升序链表

题目:最长公共前缀

  • 编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""

  • class Solution {
    public:
        string longestCommonPrefix(vector<string>& strs) {
            string res="";
            int index = 0; 
            for(int i=0;i<strs[0].size();i++){
                char temp = strs[0][i];
                for(int j=1;j<strs.size();j++){
                    if(strs[j][i] != temp ){
                        return strs[0].substr(0,i);
                    }
                }
            }
            return strs[0];
        }
    };
    
  • 纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。

    • 在这里插入图片描述
  • 时间复杂度:O(mn),其中 m 是字符串数组中的字符串的平均长度,n 是字符串的数量。最坏情况下,字符串数组中的每个字符串的每个字符都会被比较一次。空间复杂度:O(1)。使用的额外空间复杂度为常数。

  • 对于问题 LCP(Si⋯Sj),可以分解成两个子问题 LCP(Si…Smid) 与 LCP(Smid+1…Sj),其中 m i d = i + j 2 mid=\frac{i+j}{2} mid=2i+j。对两个子问题分别求解,然后对两个子问题的解计算最长公共前缀,即为原问题的解。

  • class Solution {
    public:
    
        string pr(const vector<string> &s,int start,int end){
            if(start==end){
                return s[start];
            }else{
                int mid = start+(end-start)/2;
                string left = pr(s,start,mid);
                string right = pr(s,mid+1,end);
                cout<<left<<" "<<right<<endl;
                return camp(left,right);
            }
        }
        string camp(const string &left,const string &right){
            int min_len = min(left.size(),right.size());
            for(int i=0;i<min_len;i++){
                if(left[i]!=right[i]){
                    return left.substr(0,i);
                }
            }
            return left.substr(0,min_len);
        }
        string longestCommonPrefix(vector<string>& strs) {
            if(!strs.size()){
                return "";
            }else{
                return pr(strs,0,strs.size()-1);
            }       
        }
    };
    
    
    

题目:最接近的三数之和

  • 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在恰好一个解。

  • 借助双指针,我们就可以对枚举的过程进行优化。我们用 pb 和 pc 分别表示指向 b 和 c 的指针,初始时,pb 指向位置 i+1,即左边界;pc 指向位置 n−1,即右边界。在每一步枚举的过程中,我们用 a+b+c 来更新答案,并且:

    • 如果 a+b+c≥target,那么就将 pc 向左移动一个位置;
    • 如果 a+b+c<target,那么就将 pb 向右移动一个位置。
  • 如果 a+b+c≥target,并且我们知道 pb 到 pc 这个范围内的所有数是按照升序排序的,那么如果 pc 不变而 pb 向右移动,那么 a+b+c 的值就会不断地增加,显然就不会成为最接近 target 的值了。因此,我们可以知道在固定了 pc 的情况下,此时的 pb 就可以得到一个最接近 target 的值,那么我们以后就不用再考虑 pc 了,就可以将 pc 向左移动一个位置。

  • class Solution {
    public:
        int threeSumClosest(vector<int>& nums, int target) {
            sort(nums.begin(),nums.end());
            int len_nums=nums.size();
            int res = 1e7;
            // 根据差值的绝对值来更新答案
            auto update=[&](int cur){
                if(abs(cur-target)<abs(res-target)){
                    res=cur;
                }
            };
            for(int i=0;i<len_nums;i++){
                if(i>0 && nums[i]==nums[i-1]){
                    continue;
                }
                int pb=i+1,pc=len_nums-1;
                while(pb<pc){
                    int sum=nums[i]+nums[pb]+nums[pc];
                    if(sum==target){
                        return target;
                    }
                    update(sum);
                    if(sum>target){
                        int newpc = pc-1;
                        while(pb<newpc && nums[newpc] == nums[pc]){
                            newpc--;
                        }
                        pc = newpc;
                    }else{
                        int newpb = pb+1;
                        while(newpb<pc && nums[newpb] == nums[pb]){
                            newpb++;
                        }
                        pb=newpb;
                    }
                }
            }
            return res;
        }
    };
    
  • 时间复杂度:O(N2),其中 N 是数组 nums 的长度。我们首先需要 O(Nlog⁡N) 的时间对数组进行排序,随后在枚举的过程中,使用一重循环 O(N) 枚举 a,双指针 O(N) 枚举 b 和 c,故一共是 O(N2)。空间复杂度:O(log⁡N)。排序需要使用 O(log⁡N) 的空间。然而我们修改了输入的数组 nums,在实际情况下不一定允许,因此也可以看成使用了一个额外的数组存储了 nums 的副本并进行排序,此时空间复杂度为 O(N)。

题目:合并两个有序链表

  • 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

  • 在这里插入图片描述

  • /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode() : val(0), next(nullptr) {}
     *     ListNode(int x) : val(x), next(nullptr) {}
     *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     * };
     */
    class Solution {
    public:
        ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
            ListNode *res =new ListNode();
            ListNode *head = res;
            while(list1 && list2){
                if(list1->val<list2->val){
                    ListNode *temp1 = new ListNode(list1->val);
                    res->next = temp1;
                    list1 = list1->next;
                }else{
                    ListNode *temp2 = new ListNode(list2->val);
                    res->next = temp2;
                    list2 = list2->next;
                }
                res = res->next;
            }
            if(list1){
                res->next = list1;
            }
            if(list2){
                res->next = list2;
            }
            return head->next;
        }
    };
    
  • 时间复杂度:O(n+m),其中 n 和 m 分别为两个链表的长度。因为每次循环迭代中,l1 和 l2 只有一个元素会被放进合并链表中, 因此 while 循环的次数不会超过两个链表的长度之和。所有其他操作的时间复杂度都是常数级别的,因此总的时间复杂度为 O(n+m)。空间复杂度:O(1)。我们只需要常数的空间存放若干变量。

  • 如果 l1 或者 l2 一开始就是空链表 ,那么没有任何操作需要合并,所以我们只需要返回非空链表。否则,我们要判断 l1 和 l2 哪一个链表的头节点的值更小,然后递归地决定下一个添加到结果里的节点。如果两个链表有一个为空,递归结束。

  • /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode() : val(0), next(nullptr) {}
     *     ListNode(int x) : val(x), next(nullptr) {}
     *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     * };
     */
    class Solution {
    public:
        ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
            if(!list1){
                return list2;
            }else if(!list2){
                return list1;
            }else if(list1->val < list2->val){
                list1->next = mergeTwoLists(list1->next,list2);
                return list1;
            }else{
                list2->next = mergeTwoLists(list1,list2->next);
                return list2;
            }
    
        }
    };
    
  • 时间复杂度:O(n+m),其中 n 和 m 分别为两个链表的长度。因为每次调用递归都会去掉 l1 或者 l2 的头节点(直到至少有一个链表为空),函数 mergeTwoList 至多只会递归调用每个节点一次。因此,时间复杂度取决于合并后的链表长度,即 O(n+m)。

  • 空间复杂度:O(n+m),其中 n 和 m 分别为两个链表的长度。递归调用 mergeTwoLists 函数时需要消耗栈空间,栈空间的大小取决于递归调用的深度。结束递归调用时 mergeTwoLists 函数最多调用 n+m 次,因此空间复杂度为 O(n+m)

题目:括号生成

  • 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

  • 为了生成所有序列,我们可以使用递归。长度为 n 的序列就是在长度为 n−1 的序列前加一个 ‘(’ 或 ‘)’。为了检查序列是否有效,我们遍历这个序列,并使用一个变量 balance 表示左括号的数量减去右括号的数量。如果在遍历过程中 balance 的值小于零,或者结束时 balance 的值不为零,那么该序列就是无效的,否则它是有效的。

  • class Solution {
    public:
        bool valid(const string &str){
            int temp=0;
            for(char c:str){
                if(c=='('){
                    temp++;
                }else{
                    temp--;
                }
                if(temp<0){
                    return false;
                }
            }
            return temp==0;
        }
        void generate(string &curr,int n,vector<string> &res){
            if(n==curr.size()){
                if(valid(curr)){
                    res.push_back(curr);
                }
                return ;
            }
            curr += '(';
            generate(curr,n,res);
            curr.pop_back();
            curr += ')';
            generate(curr,n,res);
            curr.pop_back();
        }
        vector<string> generateParenthesis(int n) {
            vector<string> res;
            string curr;
            generate(curr,n*2,res);
            return res;
        }
    };
    
  • 时间复杂度: O ( 2 2 n n ) O(2^{2n}n) O(22nn),对于 2 2 n 2^{2n} 22n 个序列中的每一个,我们用于建立和验证该序列的复杂度为 O(n)。空间复杂度:O(n),除了答案数组之外,我们所需要的空间取决于递归栈的深度,每一层递归函数需要 O(1) 的空间,最多递归 2n 层,因此空间复杂度为 O(n)。

  • 如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。

  • class Solution {
    public:
        void track(vector<string> &res,string &cur,int n ,int open,int close){
            if(cur.size()==2*n){
                res.push_back(cur);
                return ;
            }
            if(open<n){
                cur.push_back('(');
                track(res,cur,n,open+1,close);
                cur.pop_back();
            }
            if(close < open){
                cur.push_back(')');
                track(res,cur,n,open,close+1);
                cur.pop_back();
            }
        }
        vector<string> generateParenthesis(int n) {
            vector<string> res;
            string curr;
            track(res,curr,n,0,0);
            return res;
        }
    };
    
  • 时间复杂度: O ( 4 n n ) O(\dfrac{4^n}{\sqrt{n}}) O(n 4n),在回溯过程中,每个答案需要 O(n) 的时间复制到答案数组中。空间复杂度:O(n),除了答案数组之外,我们所需要的空间取决于递归栈的深度,每一层递归函数需要 O(1) 的空间,最多递归 2n 层,因此空间复杂度为 O(n)。

题目:合并 K 个升序链表

  • 给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

  • 可以想到一种最朴素的方法:用一个变量 res 来维护以及合并的链表,第 i 次循环把第 i 个链表和 ans 合并,答案保存到 res 中。

  • /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode() : val(0), next(nullptr) {}
     *     ListNode(int x) : val(x), next(nullptr) {}
     *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     * };
     */
    class Solution {
    public:
        ListNode* mergeTwo(ListNode *a,ListNode *b){
            if((!a) || (!b)){
                return a?a:b;
            }
            ListNode head,*tail = &head,*ap=a,*bp=b;
            while(ap && bp){
                if(ap->val < bp->val){
                    tail->next = ap;
                    ap = ap->next;
                }else{
                    tail->next =bp;
                    bp = bp->next;
                }
                tail = tail->next;
            }
            tail->next = (ap?ap:bp);
            return head.next;
        }
        ListNode* mergeKLists(vector<ListNode*>& lists) {
            ListNode *res=nullptr;
            for(size_t i=0;i<lists.size();i++){
                res = mergeTwo(res,lists[i]);
            }
            return res;
        }
    };
    
  • 使用优先队列合并:需要维护当前每个链表没有被合并的元素的最前面一个,k 个链表就最多有 k 个满足这样条件的元素,每次在这些元素里面选取 val 属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。

  • /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode() : val(0), next(nullptr) {}
     *     ListNode(int x) : val(x), next(nullptr) {}
     *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     * };
     */
    class Solution {
    public:
        struct sta{
            int val;
            ListNode *ptr;
            bool operator < (const sta &r) const{
                return val >r.val;
            }
        };
        priority_queue <sta> q;
        ListNode* mergeKLists(vector<ListNode*>& lists) {
            for(auto node:lists){
                if(node){
                    q.push({node->val,node});
                }
            }
            ListNode head,*tail = &head;
            while(!q.empty()){
                auto temp = q.top();
                q.pop();
                tail->next = temp.ptr;
                tail = tail->next;
                if(temp.ptr->next){
                    q.push({temp.ptr->next->val,temp.ptr->next});
                }
            }
            return head.next;
        }
    };
    
  • 时间复杂度:考虑优先队列中的元素不超过 k 个,那么插入和删除的时间代价为 O(log⁡k),这里最多有 kn 个点,对于每个点都被插入删除各一次,故总的时间代价即渐进时间复杂度为 O(kn×log⁡k)。空间复杂度:这里用了优先队列,优先队列中的元素不超过 k 个,故渐进空间复杂度为 O(k)。

  • 将 k 个链表配对并将同一对中的链表合并;第一轮合并以后, k 个链表被合并成了$ \frac{k}{2} $ 个链表,平均长度为 2 n k \frac{2n}{k} k2n ,然后是 k 4 \frac{k}{4} 4k 个链表,$ \frac{k}{8}$ 个链表等等;重复这一过程,直到我们得到了最终的有序链表。

    • 在这里插入图片描述
  • /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode() : val(0), next(nullptr) {}
     *     ListNode(int x) : val(x), next(nullptr) {}
     *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     * };
     */
    class Solution {
    public:
        ListNode* merge(ListNode *a,ListNode *b){
            if((!a) || (!b)){
                return a?a:b;
            }
            ListNode head,*tail=&head,*ap=a,*bp=b;
            while(ap && bp){
                if(ap->val < bp->val){
                    tail->next = ap;
                    ap = ap->next;
                }else{
                    tail->next = bp;
                    bp = bp->next;
                }
                tail = tail->next;
            }
            tail->next = (ap?ap:bp);
            return head.next;
        }
        ListNode* opt(vector<ListNode*> &ls,int l,int r){
            if(l==r){
                return ls[l];
            }
            if(l>r){
                return nullptr;
            }
            int mid = l+(r-l)/2;
            return merge(opt(ls,l,mid),opt(ls,mid+1,r));
        }
        ListNode* mergeKLists(vector<ListNode*>& lists) {
            return opt(lists,0,lists.size()-1);
        }
    };
    
  • 时间复杂度:考虑递归「向上回升」的过程——第一轮合并 k 2 \frac{k}{2} 2k 组链表,每一组的时间代价是 O(2n);第二轮合并 $\frac{k}{4} $ 组链表,每一组的时间代价是 O(4n)…所以总的时间代价是 O ( ∑ i = 1 ∞ k 2 i × 2 i n ) = O ( k n × log ⁡ k ) O(\sum_{i = 1}^{\infty} \frac{k}{2^i} \times 2^i n) = O(kn \times \log k) O(i=12ik×2in)=O(kn×logk),故渐进时间复杂度为 O ( k n × log ⁡ k ) O(kn \times \log k) O(kn×logk)。空间复杂度:递归会使用到$ O(\log k) $空间代价的栈空间。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

专题知识点-二叉树-(非常有意义的一篇文章)

这里写目录标题 二叉树的基础知识知识点一(二叉树性质 )树与二叉树的相互转换二叉树的遍历层次优先遍历树的深度和广度优先遍历中序线索二叉树二叉树相关遍历代码顺序存储和链式存储二叉树的遍历二叉树的相关例题左右两边表达式求值求树的深度找数找第k个数二叉树非递归遍历代码…

Swift 警惕“隐式异步(implicitly asynchronous)”方法的执行陷阱

概览 actor 是 Swift 5.5 中一个“不可思议”的新类型&#xff0c;可以把它看做成一个数据同步器。actor 中所有属性和方法都会被自动“串行”&#xff08;serializes&#xff09;访问和执行&#xff0c;从而有效避免了数据竞争的发生。 不过&#xff0c;在一些微妙的情境下使…

笔记51:循环神经网络入门

本地笔记地址&#xff1a;D:\work_file\DeepLearning_Learning\03_个人笔记\3.循环神经网络\循环神经网络 a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a

29.第三方登录

1►第三方登录 当今社会&#xff0c;微信登录、QQ登录、抖音登录等等三方登录已经层出不穷&#xff0c;学会三方登录势在必行。 微信登录要认证开发者&#xff0c;必须为企业&#xff0c;个人不行&#xff0c;而且还要交300块钱。 QQ登录也要申请、微博登录也要申请。 还好…

ppt画思路图 流程图 医学药学生画图素材

关注微信&#xff0c;回复: 素材 &#xff0c;即可领取

基于 React 的 HT for Web ,由厦门图扑团队开发和维护 - 用于 2D/3D 图形渲染和交互

本心、输入输出、结果 文章目录 基于 React 的 HT for Web &#xff0c;由厦门图扑团队开发和维护 - 用于 2D/3D 图形渲染和交互前言什么是 HT for WebHT for Web 的特点如何使用 HT for Web相关链接弘扬爱国精神 基于 React 的 HT for Web &#xff0c;由厦门图扑团队开发和维…

基于闪电搜索算法优化概率神经网络PNN的分类预测 - 附代码

基于闪电搜索算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于闪电搜索算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于闪电搜索优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

任意文件读取漏洞 (Arbitrary File Read/Download Vulnerability)

任意文件读取漏洞 文章目录 任意文件读取漏洞漏洞场景漏洞危害漏洞分类任意文件读取重要函数readfile()file_get_contents()fread()$_GET任意文件读取 任意文件下载html实现a标签PHP实现任意文件下载 任意⽂件读取攻防过滤../防守绕过 任意文件读取挖掘漏洞防御 ​ 一些网站的需…

十年测试告诉你35岁测试程序员,互联网技术岗,何去何从?

今年的就业情形&#xff0c;想必大家都深有感触。企业裁员&#xff0c;求职市场岗位大幅减少&#xff1b;薪资降低&#xff0c;岗位能力要求越来越高&#xff1b;好像一瞬间大家都从万米高空坠落&#xff0c;失重带来的眩晕和迷茫&#xff0c;让求职者和招聘企业都显得有点手忙…

路由器ipsec|vpn实验分析

AR1 和 AR2代表两个公司的出口&#xff0c;R2模拟互联&#xff0c;两个公司通信&#xff0c;通过ipsec vpn 加密隧道进行业务通信 切记&#xff1a;ipsec 路由器一定用AR系列&#xff0c;千万别用R&#xff0c;否则会给你惊喜 R2只有接口配ip&#xff0c;无任何配置&#xff…

【Err】jetBrains远程开发报错:Failed to exec spawn helper: pid: 18310, exit value: 1

最近双11阿里云打折&#xff0c;买了台服务器做了下远程开发环境&#xff0c;在IDEA远程开发时遇到了个问题&#xff0c;导致项目启动失败&#xff0c;报错如下&#xff1a; JetBrains远程开发报错 Failed to exec spawn helper: pid: 18310, exit value: 1 &#xff08;我改好…

解析SQL 获取表、字段及SQL查询参数

解析SQL 获取表、字段及SQL查询参数 1. 执行效果2. 使用2.1 引入依赖2.2 相关实体2.3 工具类 1. 执行效果 2. 使用 2.1 引入依赖 <!-- sql 解析处理--><dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifa…

Potrace:一个基于多边形的跟踪算法

Potrace算法通过几个步骤将位图转换为矢量轮廓。 第一步&#xff0c;将位图分解为若干条路径&#xff0c;在黑白区域间形成边界。 在第二步中&#xff0c;每条路径由一个最优多边形逼近。 在第三步中&#xff0c;每个多边形被转换成光滑的轮廓。 在可选的第四步中&#xff0c;通…

【管理运筹学】运筹学“背诵手册”(二) | 对偶理论与灵敏度分析

二、对偶理论与灵敏度分析 用矩阵形式表示原问题和对偶问题&#xff1a; max ⁡ z C X s . t . { A X ≤ b X ≥ 0 \max z\pmb{CX}\\ s.t.\begin{cases} \pmb{AX\leq b} \\ \pmb{X}\geq\pmb{0} \end{cases} maxzCXs.t.{AX≤bX≥0​ 其中 C ( c 1 , c 2 , ⋯ , c n ) , X (…

Java入门篇 之 继承

本篇碎碎念&#xff1a;最近的课程遇到瓶颈了&#xff0c;看的时候感觉自己会了&#xff0c;但是结束仔细一回顾还是一知半解&#xff0c;一点一点来吧&#xff0c;基础必须要打好(自己给自己好的心里暗示&#xff0c;结局一定是好的) 今日份励志文案:慢慢改变&#xff0c;慢慢…

四、Ribbon负载均衡

目录 一、负载均衡流程 1、我通过浏览器直接访问userservice/user/1&#xff0c;无法访问&#xff0c;说明是负载均衡做了相应的处理 2、我们来看一下代码中负载均衡的流程是怎样的 3、图像流程 二、负载均衡策略 1、修改负载均衡策略 &#xff08;方式一&#xff09; &a…

Spring面试题:(七)Spring AOP思想及实现

AOP思想的概念 AOP的实现&#xff1a;动态代理技术 通过spring容器获取目标对象和增强对象&#xff0c;通过动态代理生产代理对象&#xff0c;在目标对象的目标方法执行增强方法&#xff0c;返回生成代理对象给spring容器&#xff0c;在获取bean时则获取代理对象。 JDK代理和…

【源码运行打包】kkFileView 下载与安装

目录导航 1、源码下载2、IDEA部署2.1、克隆代码2.2、配置maven2.3、下载依赖报错2.4、执行maven打包 3、Centos7.9部署启动3.1、环境要求3.2、部署jdk环境3.3、上传部署包3.4、解压部署包3.5、访问测试3.6、解决乱码 4、使用指南5、部署包下载 文件预览服务 kkFileView &#x…

【Spring进阶系列丨第一篇】初识Spring开发

前言 小伙伴们大家好&#xff0c;我是陈橘又青&#xff0c;今天起 《Spring进阶系列》 开始更新。本专栏将涵盖Spring框架的核心概念、配置管理、Web开发、AOP、Boot、Security、Data、Integration和Batch等多个主题。通过理论讲解和实际案例的剖析&#xff0c;帮助读者深入理解…

【Linux】Ubuntu16.04下完美安装python高版本及对应版本的pip

Ubuntu16.04下完美安装python高版本及对应版本的pip 方法一:直接用命令安装python3.6&#xff08;但我没安装成功&#xff09; 好像是因为Ubuntu16.04的软件仓库&#xff08;源&#xff09;中python的最高版本就是python3.5&#xff0c;所以无法直接用apt来安装 #方法一 sudo…