LeetCode 每日一题·4月

Leetcode 每日一题·4月

1006 笨阶乘

原题链接

模拟法,四个一组,末尾部分特殊处理。

class Solution {
public:
    int clumsy(int N) {
        if(N <= 2) return N;
        if(N == 3) return 6;
        int sum = N * (N-1) / (N-2) + N-3;
        N-=4;
        while(N >= 4)
        {
            sum += (- N * (N-1) / (N-2) + N-3);
            N-=4;
        }
        return sum - clumsy(N);
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)
————

17/21 直方图的水量

原题链接

双指针,记录从左到右和从右到左的最大值,再遍历计算面积得到结果

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.empty()) return 0;
        int n = height.size();
        vector<int> left(n), right(n);
        left[0] = height[0];
        right[n - 1] = height[n - 1];

        for(int i = 1; i < n; i++ )
            left[i] = max(left[i - 1], height[i]);
        for(int i = n - 2; i >= 0; i-- )
            right[i] = max(right[i + 1], height[i]);
        int res = 0;
        for(int i = 0; i < n; i++ )
            res += min(left[i], right[i]) - height[i];
        return res;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
————

1143 最长公共子序列

原题链接

动态规划,模板题

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int len1 = text1.size(), len2 = text2.size();
        vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));
        for(int i = 1; i <= len1; i++ )
        {
            for(int j = 1; j <= len2; j++ )
            {
                if(text1[i-1] == text2[j-1])
                    dp[i][j] = dp[i-1][j-1] + 1;
                else    
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
            }
        }
        return dp[len1][len2];
    }
};

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( n 2 ) O(n^2) O(n2)
————

781 森林中的兔子

原题链接

根据题意,若答案出现 a 次 x 只,则该颜色至少有2*(x+1)只,即需要有(a-1) / (x + 1) + 1) * (x + 1)只兔子。
例:5出现了6次,则正好不缺,每只都看到另外5只同色;若出现了3次,则需要6只兔子(补成一组);若出现次数多余一组,同理把不足一组的部分补成一组。
我用了map保存颜色和对应数量,方便统计。

class Solution {
public:
    int numRabbits(vector<int>& answers) {
        if(answers.empty()) return 0;
        map<int, int> mp;
        int res = 0;
        for(int i : answers)
            ++mp[i];
        for(auto i : mp)
            res += ((i.second - 1) / (i.first + 1) + 1) * (i.first + 1);
        return res;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
————

88 合并两个有序数组

原题链接

注意应该用尾插法防止数据覆盖

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
       int len = m + n - 1;
        int i = m - 1, j = n - 1;
        while (i >= 0 && j >= 0) {
            if (nums1[i] >= nums2[j]) nums1[len --] = nums1[i --];
            else nums1[len --] = nums2[j --];
        }
        while (i >= 0) nums1[len --] = nums1[i --];
        while (j >= 0) nums1[len --] = nums2[j --];
        return ;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)
————

80 删除有序数组中的重复项 II

原题链接

原地算法,模板题

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size() <= 2) return nums.size();
        int index = 1;
        for(int i = 2; i < nums.size(); i++)
            if(nums[i] != nums[index - 1])
                nums[++index] = nums[i];
        return index + 1;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)
————

81 搜索旋转排序数组 II

原题链接

先确认旋转情况(判断头尾),然后缩短边界(去重)。
二分查找target,查找时也要注意缩短边界。

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int l = 0, r = nums.size()-1;
        if(nums[0] >= nums.back())
        {
            if(target == nums.back()) 
                return true;
            else if(target < nums.back()) 
                while(l < r && nums[l] <= nums[l+1]) ++l;
            else if(target > nums.back())
                while(l < r && nums[r] >= nums[r-1]) --r;
        }
        while(l <= r)
        {
            while(l < r && nums[l] == nums[l+1]) ++l;
            while(l < r && nums[r] == nums[r-1]) --r;
            int mid = (l + r) / 2;
            if(nums[mid] == target)
                return true;
            else if(nums[mid] > target)
                r = mid - 1;
            else
                l = mid + 1;
        }
        return false;
    }
};

时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n)),最坏情况为 O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)
————

153 寻找旋转排序数组中的最小值

原题链接

直接二分查找即可。

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.size() == 1) return nums[0];
        int l = 0, r = nums.size() - 1;
        while(l < r)
        {
            int mid = (l + r) / 2;
            if(nums[mid] < nums[r]) 
                r = mid;
            else
                l = mid + 1;
        }
        return nums[l];
    }
};

时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
空间复杂度: O ( 1 ) O(1) O(1)
————

154 寻找旋转排序数组中的最小值 II

原题链接

结合81和153,使用二分查找最小值,查找时注意缩短边界

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.size() == 1) return nums[0];
        int l = 0, r = nums.size() - 1;
        while(l < r)
        {
            while(l < r && nums[l] == nums[l+1]) l++;
            while(l < r && nums[r] == nums[r-1]) r--;
            if(l == r) return nums[l];
            int mid = (l + r) / 2;
            if(nums[mid] < nums[r])
                r = mid;
            else
                l = mid + 1;
        }
        return nums[l];
    }
};

时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
空间复杂度: O ( 1 ) O(1) O(1)
————

263 丑数

原题链接

递归

class Solution {
public:
    bool isUgly(int n) {
        if(n <= 0) return false;
        if(n == 1) return true;
        if(n % 2 == 0) return isUgly(n / 2);
        if(n % 3 == 0) return isUgly(n / 3);
        if(n % 5 == 0) return isUgly(n / 5);
        return false;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)
————

264 丑数 II

原题链接

三指针,记录2,3,5在质因数中出现的次数,每次选择三个指针中最小的作为新的丑数,直到第n个。

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> res(n, 1);
        int num[3] = {0};
        int a,b,c;
        for(int i = 1; i < n; i++)
        {
            a = res[num[0]] * 2;
            b = res[num[1]] * 3;
            c = res[num[2]] * 5;
            int t = min(a, min(b, c));
            if(t == a) num[0]++;
            if(t == b) num[1]++;
            if(t == c) num[2]++;
            res[i] = t;
        }
        return res.back();
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
————

179 最大数

原题链接

重写比较函数,让每个数字以字符串的形式比较。

class Solution {
public:
    static bool cmp(int x, int y)
    {
        string a = to_string(x), b = to_string(y);
        return a + b > b + a;
    }
    string largestNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end(), cmp);
        if(nums[0] == 0) return "0";
        string res;
        for(auto t : nums)
            res += to_string(t);
        return res;
    }
};

时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
空间复杂度: O ( m ) O(m) O(m),m是字符串的长度
————

783 二叉搜索树节点最小距离

原题链接

递归法,pre保存一个临时值

class Solution {
public:
    int INF = 0x3f3f3f3f;
    int res = INF, pre = -INF;
    int minDiffInBST(TreeNode* root) {
        if(root)
        {
            minDiffInBST(root->left);
            res = min(res, root->val - pre);
            pre = root->val;
            minDiffInBST(root->right);
        }
        
        return res;
    }
};

————

208 实现 Trie (前缀树)

原题链接

前缀树标准流程,可能存在内存泄露,可以考虑用智能指针规避,或者手动释放。

class Trie {
private:
    Trie *child[26];
    bool isEnd;
public:
    
    /** Initialize your data structure here. */
    Trie() {
        isEnd = false;
        for(int i = 0; i < 26; i++)
            child[i] = nullptr;
    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        Trie *tmp = this;
        for(char c : word)
        {
            if(!tmp->child[c - 'a'])
                tmp->child[c - 'a'] = new Trie();
            tmp = tmp->child[c - 'a'];
        }
        tmp->isEnd = true;
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        Trie *tmp = this;
        for(char c : word)
        {
            if(!tmp->child[c - 'a'])
                return false;
            tmp = tmp->child[c - 'a'];
        }
        return tmp->isEnd;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        Trie *tmp = this;
        for(char c : prefix)
        {
            if(!tmp->child[c - 'a'])
                return false;
            tmp = tmp->child[c - 'a'];
        }
        return true;
    }
};

————

213 打家劫舍 II

原题链接

动态规划,将环状房屋分成两个队列:不拿第一个的情况和不拿最后一个的情况,需要注意的是不拿第一个的情况最后一个是可以不拿的。取这两个队列的最大值即可。

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size() == 0)    return 0;
        if(nums.size() == 1)    return nums[0];
        return max(dprange(nums, 0, nums.size()-2), //不拿最后一个
                   dprange(nums, 1, nums.size()-1));//不拿第一个
    }
    int dprange(vector<int>& nums, int start, int end)
    {
        int temp = 0;//dp[i-1]  拿了前一个,不拿这个
        int pre = 0;//dp[i-2]   前一个没拿,可以考虑这个
        int dp_i = 0;
        for(int i=start; i<=end; ++i)
        {
            dp_i = max(temp, pre+nums[i]);
            pre = temp; //没拿这个(前一个没拿的前提下)
            temp = dp_i;//当前最佳情况
        }
        return dp_i;
    }
};

————

87 扰乱字符串

原题链接

官方题解
————

220 存在重复元素 III

原题链接

利用排序快速判断值区间,再分析下标是否满足条件
(虽然是暴力法,时空意外的还可以)

class Solution {
public:
    struct Num
    {
        int num, index;
    };
    static bool cmp1(Num a, Num b)
    {
        return a.num < b.num;
    }
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        int len = nums.size();
        if(len < 2 || t < 0 || k < 0) return false;
        struct Num num[len];
        for(int i = 0; i < len; i++) 
        {
            num[i].num = nums[i];
            num[i].index = i;
        }
        sort(num, num + len, cmp1);
        int l = 0, r = 1;
        while(r <= len - 1)
        {
            if(abs((long)num[l].num - (long)num[r].num) <= t)
            {
                if(abs(num[l].index - num[r].index) <= k)
                    return true;
                else
                    r++;
            }
            else
            {
                l++;
                r = l + 1;
            }
        }
        return false;
    }
};

时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
空间复杂度: O ( n ) O(n) O(n)
————

26 删除有序数组中的重复项

原题链接
原地算法,模板题
————

27 移除元素

原题链接
原地算法

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        if(nums.size() < 1) return 0;
        int index = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            if(nums[i] != val)
                nums[index++] = nums[i];
        }
        return index;
    }
};

————

28 实现 strStr()

原题链接
虽然可以直接用STL,也可以手撕KMP实现

class Solution {
public:
    int strStr(string haystack, string needle) {
        if(needle.length() == 0) return 0;
        else if(haystack.length() == 0) return -1;
        int len_h = haystack.length(), len_n = needle.length();
        int next[len_n];
        next[0] = -1;
        for(int i = 0, j = -1; i < len_n - 1;)
        {
            if(j == -1 || needle[j] == needle[i])
            {
                j++;
                i++;
                next[i] = j;
            }
            else
            {
                j = next[j];
            }
        }
        int i = 0, j = 0;
        while(i < len_h && j < len_n)
        {
            if(j == -1 || haystack[i] == needle[j])
            {
                i++;
                j++;
            }
            else
            {
                j = next[j];
            }
        }
        if(j == len_n)
            return i - j;
        return -1;
    }
};

————

91 解码方法

原题链接

class Solution {
public:
    int numDecodings(string s) {
        if(s.size() == 0 || (s.size() == 1 && s[0] == '0')) return 0;
        if(s.size() == 1) return 1;

        int len = s.size();
        int dp[2] = {1,1};// [n-2] & [n-1] ;at least return 1
        for(int i = 0; i < len; i++)
        {
            int tmp = 0;// this case
            if(s[i] != '0')// case: 1 + n :this one
            {
                tmp += dp[1];
            }
            if(i > 0 && (s[i-1] == '1' || (s[i-1] == '2' && s[i] <= '6')))// case:2 + n :with the former
            {
                tmp += dp[0];
            }
            dp[0] = dp[1];// [n-2] = [n-1]
            dp[1] = tmp;// [n-1] = this one
        }
        return dp[1];
    }
};

————

363 矩形区域不超过 K 的最大数值和

原题链接

二维前缀和
推荐题解
————

368 最大整除子集

原题链接

推荐题解
————

377 组合总和 Ⅳ

原题链接

动态规划,背包题。

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1, 0);//建表
        dp[0] = 1;//装入0的方法有1个:什么都不加;也为了方便后续累加
        for(int i = 1; i <= target; i++)//背包从装入1到装满
        {
            for(int j = 0; j < nums.size(); j++)//枚举nums里的每个数
            {
                //如果这个数是可装入的,并且装入这个数后的组合数未溢出
                if(i >= nums[j] && dp[i - nums[j]] <= INT_MAX - dp[i])
                {
                    dp[i] += dp[i - nums[j]];
                }
            }
        }
        return dp[target];
    }
};

————

897 递增顺序搜索树

原题链接
栈模拟递归过程,模板

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* increasingBST(TreeNode* root) {
        stack<TreeNode*> st;
        TreeNode *dummy = new TreeNode(), *res = dummy;
        while(root != nullptr || !st.empty())
        {
            while(root != nullptr)
            {
                st.push(root);
                root = root->left;
            }
            root = st.top();
            st.pop();
            TreeNode *tmp = new TreeNode();
            dummy->right = tmp;
            dummy = tmp;
            dummy->val = root->val;
            root = root->right;
        }
        return res->right;
    }
};

————

1011. 在 D 天内送达包裹的能力

原题链接

前缀和+二分,二分答案区间来缩小范围,找到运送包裹的最小时间

class Solution {
public:
    bool check(int m, vector<int> &weights, int d)
    {
        int count = 1, sum = weights[0];
        for(int i = 1; i < weights.size(); )
        {
            if(sum + weights[i] > m)
            {
                sum = 0;
                count++;
            }
            else
            {
                sum += weights[i++];
            }
        }
        
        return count <= d;
    }
    int shipWithinDays(vector<int>& weights, int D) {
        int len = weights.size();
        if(len == 1) return weights[0];

        int m = 0, sum = 0;
        for(auto &num : weights)
        {
            if(num > m) m = num;
            sum += num;
        }

        int l = m, r = sum;
        while(l < r)
        {
            m = l + r >> 1;
            if(check(m, weights, D))
                r = m;
            else
                l = m + 1;
        }
        return r;
    }
};

————

938. 二叉搜索树的范围和

原题链接
同897,模板题

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int rangeSumBST(TreeNode* root, int low, int high) {
        if(root == nullptr) return 0;
        stack<TreeNode*> st;
        int sum = 0;
        while(root != nullptr || !st.empty())
        {
            while(root != nullptr)
            {
                st.push(root);
                root = root->left;
            }
            root = st.top();
            st.pop();
            if(root->val >= low && root->val <= high)
                sum += root->val;
            root = root->right;
        }

        return sum;
    }
};

————

633. 平方数之和

原题链接
二分法查找

class Solution {
public:
    bool judgeSquareSum(int c) {
        int l = 0, r = sqrt(c);
        while(l <= r)
        {
            if(l*l < c - r*r)
            {
                l++;
            }
            else if(l*l > c - r*r)
            {
                r--;
            }
            else
            {
                return true;
            }
        }
        return false;
    }
};

————

403. 青蛙过河

原题链接
动态规划,因为青蛙跳跃的距离有三种,使用三状态dp。

class Solution {
public:
    bool canCross(vector<int>& stones) {
        int len = stones.size();
        bool dp[len][len+1];
        memset(dp, false, sizeof dp);
        dp[0][1] = true;
        for(int i = 1; i < len; i++)
        {
            bool flag = false;
            for(int j = i - 1; j >= 0; j--)
            {
                int tmp = stones[i] - stones[j];
                if(tmp > i) break;
                if(dp[j][tmp])
                {
                    dp[i][tmp - 1] = true;
                    dp[i][tmp] = true;
                    dp[i][tmp + 1] = true;
                    flag = true;
                }
            }
            if(i == len - 1 && !flag)
                return false;
        }
        return true;
    }
};

————

137. 只出现一次的数字 II

原题链接

位运算法,实际上是数字电路原理,一个数视为32为二进制,把所有数字异或起来,每三次1抵消为0。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int a = 0, b = 0;
        for(auto num : nums)
        {
            b = (b ^ num) & ~a;
            a = (a ^ num) & ~b;
        }
        return b;
    }
};

你可能感兴趣的:(Leetcode每日一题,算法,leetcode)