刷穿LeetCode——Task06

这篇博客记录刷题第6天的思路与心得。

43. 字符串相乘

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

示例 1:

输入: num1 = “2”, num2 = “3” 输出: “6”

示例 2:

输入: num1 = “123”, num2 = “456” 输出: “56088”

说明:
num1 和 num2 的长度小于110。
num1 和 num2 只包含数字 0-9。
num1 和 num2 均不以零开头,除非是数字 0 本身。
不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。

分析:这道题我首先想到的是利用栈或者队列,但实际上只要透彻理解手算两数相乘的过程,通过构造int数组存放不同位数的乘积结果就行。最后去除前端多余的0,将int数组元素导入string中。参考评论区,该过程描述如下:

    num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
    例: 123 * 45,  123的第1位 2 和45的第0位 4 乘积 08 存放在结果的第[1, 2]位中
      index:    0 1 2 3 4  
          
                    1 2 3
                *     4 5
                ---------
                      1 5
                    1 0
                  0 5
                ---------
                  0 6 1 5
                    1 2
                  0 8
                0 4
                ---------
                0 5 5 3 5
    这样我们就可以单独都对每一位进行相乘计算把结果存入相应的index中    
class Solution {
    
    
public:
    string multiply(string num1, string num2) {
    
      
        int n1 = num1.length() - 1;
        int n2 = num2.length() - 1;
        if (n1 < 0 || n2 < 0)   return "";
        int mul_len = n1+n2+2;
        int mul[mul_len]; 
        for (int i = 0; i < mul_len; i++)   mul[i] = 0;  //初始化为0
        
        //num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
        for(int i = n1; i >= 0; i--) {
    
    
            for (int j = n2; j >= 0; j--) {
    
    
                int bitmul = (num1.at(i)-'0') * (num2.at(j)-'0');
                bitmul += mul[i+j+1];        //先加低位

                mul[i+j] += bitmul / 10;     //判断低位求和是否进位,不同位数相应存放
                mul[i+j+1] = bitmul % 10; 
            }
        } 
        
        //去掉前端的0  
        int i = 0; 
        while( i < mul_len-1 && mul[i] == 0) {
    
    
            i++;
        }
        string ans = "";
        for (; i < mul_len; i++) {
    
    
            string s = to_string(mul[i]);
            ans.append(s);
        }
        return ans;

    } 
};

值得注意的是,int数组定义后必须初始化为0,否则会各种整幺蛾子,这里记录一下bug:int* mul = new int[n1+n2+2] { 0 }这行代码会报可变长数组不可初始化的错误,如下图所示。bug1
原来C++11编译器不支持,于是我们只好用for循环初始化,当然也可以通过memset(mul,0,sizeof(mul));来赋mul数组初值全为0,需要使用头文件string.h。

46.全排列

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1],
[3,1,2], [3,2,1] ]

全排列是利用回溯法的典例,参照官方视频题解,讲得非常透彻。定义一组状态变量,深度优先搜索遍历状态树的所有节点。关于回溯法与深度优先搜索、广义有限搜索、遍历(暴力解法)、动态规化等的区别可以参考如下链接。

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/

permute via backtracking
注意回溯法是以空间换时间的做法。若序列长度为 n n n,则时间、空间复杂度均为 O ( n × n ! ) O(n \times n!) O(n×n!),详细推导如下:
backtrack_complexity
全排列图解如下:
backtrackingC++代码如下,应该能看懂。

class Solution {
    
    
public:
    vector<vector<int>> permute(vector<int>& nums) {
    
    
        vector<vector<int>> res;
        if (nums.size() == 0)
            return res;
        vector<int> path;                             //状态变量:记录选了哪些数
        vector<bool> used(nums.size(), false);       //状态变量:记录当前数字是否用过
        int depth = 0;                               //状态变量:记录递归到第几层(用过多少数)
        dfs(nums, depth, path, used, res);
        return res;
    }

 private:
    void dfs(vector<int>& nums, int depth, vector<int>& path, vector<bool>& used, vector<vector<int>>& res) {
    
    
        //递归结束条件
        if (depth == nums.size()) {
    
    
            res.push_back(path);
            return;
        }
        //依次使用当前数字
        for (int i = 0; i < nums.size(); i++) {
    
    
            if (!used[i]) {
    
         //当前节点未使用
                used[i] = true;
                path.push_back(nums[i]);
                //递归做深度优先搜索
                dfs(nums, depth+1, path, used, res);
                //回溯到上一层节点,撤销状态变量
                used[i] = false;
                path.pop_back();
            }
        }
        return;
    }
};

53.最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

这道题用贪心或动态规划可以实现时间复杂度为 O ( n ) O(n) O(n) ,空间复杂度为 O ( 1 ) O(1) O(1);若进一步使用分治,则渐进时间复杂度可提升为 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))。参考官方题解如下:

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/maximum-subarray/solution/zui-da-zi-xu-he-by-leetcode-solution/

(1).贪心法

贪心法求解最大连续子序和的核心思路就是:若当前指针所指元素之前和 < 0, 则丢弃之前的子序列;否则保留之前序列并加入当前元素,存储当前序列和,如此遍历找到最大值。原理如下:
greedy_solution
题解和代码如下:
greedy_schematic

class Solution {
    
    
public:
    int maxSubArray(vector<int>& nums) {
    
    
        int pre = 0, maxAns = nums[0];
        for (const auto &x: nums) {
    
    
            pre = max(pre + x, x);   //pre = (pre < 0) ? x : pre+x
            maxAns = max(maxAns, pre); //打擂台算法找最大值
        }
        return maxAns;
    }
};

(2).动态规划

动态规划的核心思路是:若前一个元素 > 0,则将其加到数组的当前元素上,如此遍历,找到数组最大值即为最大子序和。这个思路很好理解,讲真这也是一种始终取求最优的思路,不知道与贪心法有什么区别。。。题解和代码如下:
dynamic_schematic

class Solution {
    
    
public:
    int maxSubArray(vector<int>& nums) {
    
    
        int n = nums.size();
        if (n == 1)	return nums[0];
        int maxSubArray = INT32_MIN ;
        for (int i = 1; i < n; i++ ) {
    
    
            maxSubArray = max(maxSubArray,nums[i-1]);
            if (nums[i-1] > 0)
                nums[i] += nums[i-1];
            maxSubArray = max(maxSubArray,nums[i]);
        }
        return maxSubArray;
    }
};

(3). 分治

分治算法我还不大懂,这里直接照搬官方解答和c++代码如下:
divide-and-conquercomplexity

class Solution {
    
    
public:
    struct Status {
    
    
        int lSum, rSum, mSum, iSum;
    };

    Status pushUp(Status l, Status r) {
    
    
        int iSum = l.iSum + r.iSum;
        int lSum = max(l.lSum, l.iSum + r.lSum);
        int rSum = max(r.rSum, r.iSum + l.rSum);
        int mSum = max(max(l.mSum, r.mSum), l.rSum + r.lSum);
        return (Status) {
    
    lSum, rSum, mSum, iSum};
    };

    Status get(vector<int> &a, int l, int r) {
    
    
        if (l == r) {
    
    
            return (Status) {
    
    a[l], a[l], a[l], a[l]};
        }
        int m = (l + r) >> 1;
        Status lSub = get(a, l, m);
        Status rSub = get(a, m + 1, r);
        return pushUp(lSub, rSub);
    }

    int maxSubArray(vector<int>& nums) {
    
    
        return get(nums, 0, nums.size() - 1).mSum;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_33007293/article/details/112760742