2021年9月LeetCode每日一题

每日一题

  • 2021年9月
    • (模拟-中等)比较版本号
    • (链表-简单)链表中倒数第k个节点
    • (排序-中等)面试题 17.14. 最小K个数
    • (模拟-简单)斐波那契数列
    • (模拟-中等)用 Rand7() 实现 Rand10()
    • (二分-简单)二分查找
    • (模拟-简单)分割平衡字符串
    • (贪心-困难)IPO
    • (模拟-困难)文本左右对齐
    • (模拟-中等)找到需要补充粉笔的学生编号
    • (DP-困难)不含连续1的非负整数
      • 暴力搜索
      • 数位DP
    • (贪心-中等)有效的括号字符串
    • (哈希-中等)回旋镖的数量
    • (字符串-中等)通过删除字母匹配到字典里最长单词
    • (二分-中等)寻找峰值
    • (Tree-困难)单词搜索
      • 简单版本
      • 困难版本
    • (模拟-中等)有效的数独
    • (博弈-简单)Nim 游戏
    • (DP-中等)只有两个键的键盘
    • (DP-中等)最长递增子序列的个数
    • (模拟-简单)最后一个单词的长度
    • (链表-中等)分隔链表
    • (模拟-简单)3的幂
    • (链表-中等)扁平化多级双向链表
    • (DP-中等)两个字符串的删除操作
    • (加法-中等)两整数之和
    • (DP-困难)解码方法 II
    • (DFS-中等)路径总和 III
    • (贪心-困难)超级洗衣机
    • (数学-中等)矩形面积

2021年9月

(模拟-中等)比较版本号

题目大意就是比较两个字符串大小,一点隔开作为单元进行比较,有前导零。题目链接
代码很好理解。

class Solution {
public:
    int compareVersion(string vs1, string vs2) {
        vector<int> v1,v2;
        int n1 = vs1.size(),n2 = vs2.size();
        for (int i = 0; i < n1; i ++){
            int t = 0;
            for (; i < n1 && vs1[i] != '.'; i ++){
                t = t*10+vs1[i]-'0';
            }
            v1.push_back(t);
        }
        for (int i = 0; i < n2; i ++){
            int t = 0;
            for (; i < n2 && vs2[i] != '.'; i ++){
                t = t*10+vs2[i]-'0';
            }
            v2.push_back(t);
        }
        int i,j;
        for (i = 0,j = 0; i < v1.size() && j < v2.size(); i ++,j ++){
            if (v1[i] > v2[j])return 1;
            if (v1[i] < v2[j])return -1;
        }
        for (; i < v1.size(); i ++)if (v1[i])return 1;
        for (; j < v2.size(); j ++)if (v2[j])return -1;
        return 0;
    }
};

(链表-简单)链表中倒数第k个节点

题目链接

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode* pre = nullptr;
        ListNode* last = nullptr;
        pre = head;
        last = head;
        int f = 1;
        while(last->next){
            last = last->next;
            f++;
            if (f > k)pre = pre->next;
        }
        return pre;
    }
};

(排序-中等)面试题 17.14. 最小K个数

题目链接
直接暴力也能过。

class Solution {
public:
    vector<int> smallestK(vector<int>& arr, int k) {
        sort(arr.begin(),arr.end());
        vector<int> vt;
        for (int i = 0; i < k; i ++)vt.push_back(arr[i]);
        return vt;
    }
};

利用 的思想 时间复杂度 O ( N l o g K ) O(Nlog K) O(NlogK) 空间复杂度 O ( K ) O(K) O(K)

class Solution {
public:
    vector<int> smallestK(vector<int>& arr, int k) {
        vector<int> vec(k, 0);
        if (k == 0) return vec;
        priority_queue<int> Q;
        for (int i = 0; i < k; ++i) Q.push(arr[i]);		//先加入k个数
        for (int i = k; i < (int)arr.size(); ++i) {		//后面每个数个对顶的数逐一比较,交换
            if (Q.top() > arr[i]) {
                Q.pop();
                Q.push(arr[i]);
            }
        }
        for (int i = 0; i < k; ++i) {
            vec[i] = Q.top();
            Q.pop();
        }
        return vec;
    }
};

利用快排思想 的思想 时间复杂度 O ( N ) O(N) O(N) 空间复杂度 O ( l o g K ) O(log K) O(logK)

class Solution {
    int partition(vector<int>& nums, int l, int r) {
        int pivot = nums[r];
        int i = l - 1;
        for (int j = l; j <= r - 1; ++j) {
            if (nums[j] <= pivot) {
                i = i + 1;
                swap(nums[i], nums[j]);
            }
        }
        swap(nums[i + 1], nums[r]);
        return i + 1;
    }
    // 基于随机的划分
    int randomized_partition(vector<int>& nums, int l, int r) {
        int i = rand() % (r - l + 1) + l;
        swap(nums[r], nums[i]);
        return partition(nums, l, r);
    }
    void randomized_selected(vector<int>& arr, int l, int r, int k) {
        if (l >= r) return;
        int pos = randomized_partition(arr, l, r);
        int num = pos - l + 1;
        if (k == num) return;
        else if (k < num) randomized_selected(arr, l, pos - 1, k);
        else randomized_selected(arr, pos + 1, r, k - num);
    }
public:
    vector<int> smallestK(vector<int>& arr, int k) {
        srand((unsigned)time(NULL));
        randomized_selected(arr, 0, (int)arr.size() - 1, k);
        vector<int> vec;
        for (int i = 0; i < k; ++i) vec.push_back(arr[i]);
        return vec;
    }
};

(模拟-简单)斐波那契数列

题目链接

const int mod = 1e9+7;
class Solution {
public:
    int fib(int n) {
        if (n==1)return 1;
        if (n==0)return 0;
        int a = 0,b = 1;
        for (int i = 2; i <= n; i ++){
            swap(a,b);
            b = (a + b)%mod;
        }
        return b;
    }
};

(模拟-中等)用 Rand7() 实现 Rand10()

题目链接
用一个Rand7()生成[1,7]的函数,来生成[1,10]的函数Rand10()
方法: 拒绝采样法
就是利用两个Rand7()函数组成7行7列49个数,取前40个数映射[1,10]这10个数。

class Solution {
public:
    int rand10() {
        int row, col, idx;
        do {
            row = rand7();  //行
            col = rand7();  //列
            idx = col + (row - 1) * 7; //第几个数字
        } while (idx > 40);			   //舍弃后9个数
        return 1 + (idx - 1) % 10;	   //取到[1,10]
    }
};

更优的解法:减少Rand7()函数的使用也就是缩小被拒绝数的数量。

class Solution {
public:
    int rand10() {
        int a, b, idx;
        while (true) {
            a = rand7();
            b = rand7();
            idx = b + (a - 1) * 7;
            if (idx <= 40) {
                return 1 + (idx - 1) % 10;
            }
            a = idx - 40;		//被拒绝9个数,结合下面b,缩小被拒绝数的数量
            b = rand7();
            // 可以得到 1 - 63 个数
            idx = b + (a - 1) * 7;
            if (idx <= 60) {	//取前60
                return 1 + (idx - 1) % 10;
            }
            a = idx - 60;		//被拒接3个数,结合下面b,缩小被拒绝数的数量
            b = rand7();
            // 可以得到 1 - 21 个数
            idx = b + (a - 1) * 7;
            if (idx <= 20) {	//取前20    被拒绝数只剩下了一个,概率就很小了.
                return 1 + (idx - 1) % 10;
            }
        }
    }
};

(二分-简单)二分查找

题目链接
简单的模板题

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

(模拟-简单)分割平衡字符串

题目链接

class Solution {
public:
    int balancedStringSplit(string s) {
        int n = s.size();
        int res = 0;
        int l = 0,r = 0;
        for (int i = 0; i < n; i ++ ){
            if (s[i] == 'L')l ++;
            else r ++;
            if (l == r){
                res ++;
                l = 0,r = 0;
            }
        }
        return res;
    }
};

(贪心-困难)IPO

题目链接
说实话这一题达不到,困难的难度。题目链接
解法:先对启动资本及其编号组合按照启动资本从小到大排序。把启动资本小于W的加入优先队列中。按照这样一直下去,直到队列为空或者k等于0,才返回w。

class Solution {
public:
    int findMaximizedCapital(int k, int w, vector<int>& p, vector<int>& c) {
        vector<pair<int,int> > vc;
        int n = c.size();
        for (int i = 0; i < n; i ++) vc.push_back({c[i],i});
        sort(vc.begin(),vc.end(),[](pair<int,int> a,pair<int,int> b){
            return a.first < b.first;
        });
        int i = 0;
        priority_queue<int> q;
        for (; i < n; i ++){
            if (vc[i].first <= w){
                q.push(p[vc[i].second]);
            }else break;
        }
        while(k--){
            if (!q.size())return w;
            w += q.top();
            q.pop();
            for (; i < n; i ++){
                if (vc[i].first <= w){
                    q.push(p[vc[i].second]);
                }else break;
            }
        }
        return w;
    }
};

(模拟-困难)文本左右对齐

题目链接
大模拟,不难,但是麻烦,这里建议看官方题解。

class Solution {
    // blank 返回长度为 n 的由空格组成的字符串
    string blank(int n) {
        return string(n, ' ');
    }

    // join 返回用 sep 拼接 [left, right) 范围内的 words 组成的字符串
    string join(vector<string> &words, int left, int right, string sep) {
        string s = words[left];
        for (int i = left + 1; i < right; ++i) {
            s += sep + words[i];
        }
        return s;
    }

public:
    vector<string> fullJustify(vector<string> &words, int maxWidth) {
        vector<string> ans;
        int right = 0, n = words.size();
        while (true) {
            int left = right; // 当前行的第一个单词在 words 的位置
            int sumLen = 0; // 统计这一行单词长度之和
            // 循环确定当前行可以放多少单词,注意单词之间应至少有一个空格
            while (right < n && sumLen + words[right].length() + right - left <= maxWidth) {
                sumLen += words[right++].length();
            }

            // 当前行是最后一行: 单词左对齐,且单词之间应只有一个空格,在行末填充剩余空格
            if (right == n) {
                string s = join(words, left, n, " ");
                ans.emplace_back(s + blank(maxWidth - s.length()));
                return ans;
            }

            int numWords = right - left;
            int numSpaces = maxWidth - sumLen;

            // 当前行只有一个单词:该单词左对齐,在行末填充剩余空格
            if (numWords == 1) {
                ans.emplace_back(words[left] + blank(numSpaces));
                continue;
            }

            // 当前行不只一个单词
            int avgSpaces = numSpaces / (numWords - 1);
            int extraSpaces = numSpaces % (numWords - 1);
            string s1 = join(words, left, left + extraSpaces + 1, blank(avgSpaces + 1)); // 拼接额外加一个空格的单词
            string s2 = join(words, left + extraSpaces + 1, right, blank(avgSpaces)); // 拼接其余单词
            ans.emplace_back(s1 + blank(avgSpaces) + s2);
        }
    }
};

(模拟-中等)找到需要补充粉笔的学生编号

说真的,每日一题质量评估一点都不准确,这最多只能算是简单题。题目链接

class Solution {
public:
    int chalkReplacer(vector<int>& c, int k) {
        int n = c.size();
        long long sum = 0;
        for (int i = 0; i < n; i ++)sum += c[i];
        k = k%sum;
        int i;
        for (i = 0; i < n; i ++){
            k -= c[i];
            if (k < 0)break;
        }
        return i;
    }
};

(DP-困难)不含连续1的非负整数

题目链接

暴力搜索

class Solution {
public:
    int ans = 0;
    int N;
    void dfs(int cur){
        if(cur > N) return;
        ans++;
        if((cur & 1)){
            dfs(cur << 1);
        } 
        else{
            dfs(cur << 1);
            dfs((cur << 1)+1);
        }
        return;
    }
    int findIntegers(int n) {
        N = n;
        ans = 1;
        dfs(1);
        return ans;
    }
};

数位DP

题解我不会

const int N = 50;
//f[i][j] 为考虑二进制长度为 i,而且最高位为 j(0 or 1)时的合法数个数
long long f[N][2];
class Solution {
public:
    void init(){
        f[1][0] = 1; f[1][1] = 2;
        for (int i = 1; i < N - 1; i++) {
            f[i + 1][0] = f[i][1];	 //避免有前导零
            f[i + 1][1] = f[i][0] + f[i][1]; //预处理出所有情况
        }
    }
    int getLen(int n) {
        for (int i = 31; i >= 0; i--) {
            if (((n >> i) & 1) == 1) return i;
        }
        return 0;
    }
    int findIntegers(int n) {
        init();
        if (n == 0) return 1;
        int len = getLen(n);
        int ans = 0, prev = 0;
        for (int i = len; i >= 0; i--) {
            // 当前位是 0 还是 1
            int cur = ((n >> i) & 1); 
            // 如果当前位是 1,那么填 0 的话,后面随便填都符合,将方案数累加
            if (cur == 1) ans += f[i + 1][0];
            // 出现连续位为 1,方案数被计算完了
            if (prev == 1 && cur == 1) break; 
            prev = cur;
            if (i == 0) ans++;	//本身也是一个
        }
        return ans;
    }
};

(贪心-中等)有效的括号字符串

题目链接

不严谨证明:
从左向右遍历:
如 果 字 符 串 所 有 前 缀 中 ( 和 ∗ 的 数 量 大 于 等 于 ) 的 数 量 , 说 明 左 括 号 可 以 匹 配 完 全 右 括 号 如果字符串所有前缀中(和*的数量大于等于)的数量,说明左括号可以匹配完全右括号 ()
从右向左遍历:
如 果 字 符 串 所 有 后 缀 中 ) 和 ∗ 的 数 量 大 于 等 于 ( 的 数 量 , 说 明 右 括 号 可 以 匹 配 完 全 左 括 号 如果字符串所有后缀中)和*的数量大于等于(的数量,说明右括号可以匹配完全左括号 )(
综合考虑
左 右 遍 历 都 符 合 , 答 案 即 为 所 求 左右遍历都符合,答案即为所求

class Solution {
public:
    bool checkValidString(string s) {
        int le = s.size();
        int l = 0,r = 0;
        for (int i = 0; i < le; i ++){
            if (s[i]=='(')l++;
            if (s[i]==')')r++;
            if (s[i]=='*')l++;
            if (l < r)return false;
        }
        l = 0,r = 0;
        for (int i = le-1; i >= 0; i --){
            if (s[i]=='(')l++;
            if (s[i]==')')r++;
            if (s[i]=='*')r++;
            if (r < l)return false;
        }
        return true;
    }
};

(哈希-中等)回旋镖的数量

题目链接
题意很容易明白。
解法:就是在n个点里,找出2个点为一对,问能找出多少对距离是相同的相同,然后计算出m对选2对的方案数之和。
假 设 有 m 对 相 同 的 距 离 , 选 择 其 中 2 对 , 那 么 组 合 就 有 m ( m − 1 ) 种 情 况 符 合 条 件 。 假设有m对相同的距离,选择其中2对,那么组合就有m(m-1)种情况符合条件。 m2m(m1)
最 少 就 是 2 对 , ( a , b , c ) 符 合 , 那 么 ( c , b , a ) 就 符 合 。 最少就是2对,(a,b,c)符合,那么(c,b,a)就符合。 2(a,b,c)(c,b,a)
补 充 : m ( m − 1 ) = 2 ∗ ( 0 + 1 + 2 + 3... + m − 1 ) 补充:m(m-1) = 2*(0+1+2+3 ...+m-1) :m(m1)=2(0+1+2+3...+m1)
直接枚举,哈希记录。

class Solution {
public:
    int numberOfBoomerangs(vector<vector<int>> &points) {
        int ans = 0;
        unordered_map<int, int> cnt;
        for (auto &x : points) {
            cnt.clear();
            for (auto &y : points) {//这里不用特殊判断x和y是否一个元素,cnt[0]只会有一次不会2*cnt[0]++也是0,所以不用特判
                ans += 2 * cnt[(x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1])]++;
            }
        }
        return ans;
    }
};

(字符串-中等)通过删除字母匹配到字典里最长单词

中等中的简单题 题目链接

class Solution {
public:
    string findLongestWord(string s, vector<string>& d) {
        sort(d.begin(),d.end(),[](string &a,string &b){
            if (a.size()==b.size())return a < b;
            return a.size() > b.size();
        });
        int n = d.size(),le = s.size();
        for (int i = 0; i < n; i ++){
            int l = 0,r = d[i].size();
            for (int j = 0; j < le; j ++){
                if (d[i][l]==s[j]) l ++;
                if (l == r)return d[i];
            }
        }
        return "";
    }
};

(二分-中等)寻找峰值

题意不难理解,但是要求在 l o g ( N ) log(N) log(N)时间复杂度内完成。题目链接
二分查找,直接看代码。

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int l = 0,r = nums.size() - 1;
        while(l < r){
            int mid = l + r >> 1;//因为nums[i] != nums[i + 1],必然只有两种情况大于和小于
            if (nums[mid] < nums[mid+1])l = mid + 1;  //在mid+1之后一定有符合条件的
            else r = mid;	//mid之前一定有符合条件的,开头和结尾默认最小值
        }
        return r;
    }
};

(Tree-困难)单词搜索

简单版本

单词搜索 I(就是一个简单的搜索)

class Solution {
public:
    int n,m;
    int vis[20][20],tmp[4][2] = {-1,0,0,1,1,0,0,-1};
    bool dfs(vector<vector<char>>& b,string w,int x,int y,int pos){
        if (pos == w.size())return true;
        if (b[x][y]!=w[pos])return false;
        for (int i = 0; i < 4; i ++){
            int x_ = x + tmp[i][0],y_ = y + tmp[i][1];
            if (x_ >= 0 && x_ < n && y_ >= 0 && y_ < m && !vis[x_][y_]){
                vis[x_][y_] = 1;
                if(dfs(b,w,x_,y_,pos+1))return true;
                vis[x_][y_] = 0;
            }
        }
        if (pos + 1 == w.size())return true;
        return false;
    }
    bool exist(vector<vector<char>>& b, string w) {
        n = b.size(),m = b[0].size();
        for (int i = 0; i < n; i ++)
            for (int j = 0; j < m; j ++){
                memset(vis,0,sizeof vis);
                vis[i][j] = 1;
                if (dfs(b,w,i,j,0))return true;
                vis[i][j] = 0;
            }      
        return false;   
    }
};

困难版本

单词搜索 II(借鉴官方的写法)学习了

struct TrieNode {			//字典树节点
    string word = "";
    unordered_map<char,TrieNode *> children;
};

void insertTrie(TrieNode * root,const string & word) {//添加字符串进树
    TrieNode * node = root;
    for (auto c : word){
        if (node->children[c]==NULL) {
            node->children[c] = new TrieNode();	  //为空,需要创建节点
        }
        node = node->children[c];
    }
    node->word = word;
}
class Solution {
public:
    int tmp[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    int n,m;
    bool dfs(vector<vector<char>>& board, int x, int y, TrieNode * root, vector<string> & res) {
        char ch = board[x][y];        
        if (root->children[ch] == NULL) return false;

        root = root->children[ch];
        if (root->word.size() > 0){
            res.push_back(root->word);
            root->word="";		//找到了,就把这个字符串从树种去除
        }

        board[x][y] = '#';		//搜索过得本轮递归就不能在使用
        for (int i = 0; i < 4; ++i) {
            int x_ = x + tmp[i][0];
            int y_ = y + tmp[i][1];
            if (x_ >= 0 && x_ < n && y_ >= 0 && y_ < m && board[x_][y_] != '#')
                dfs(board, x_, y_, root,res);	//递归
        }
        board[x][y] = ch;		//复原
        return true;
    }
    vector<string> findWords(vector<vector<char>> & board, vector<string> & words) {
        TrieNode * root = new TrieNode();
        vector<string> res;
        n = board.size(),m = board[0].size();
        for (auto & word: words) insertTrie(root,word);
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                dfs(board, i, j, root, res);    
        return res;        
    }
};

(模拟-中等)有效的数独

题目链接
判断每个行,每一列,每个小方格,是否有重复的。

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& b) {
        for (int i = 0; i < 9; i ++){
            int vis[10] = {0};
            for (int j = 0; j < 9; j ++){
                if (b[i][j] != '.'){
                    if (vis[b[i][j]-'0'])return false;
                    vis[b[i][j]-'0'] = 1;
                }
            }
            memset(vis,0,sizeof vis);
            for (int j = 0; j < 9; j ++){
                if (b[j][i] != '.'){
                    if (vis[b[j][i]-'0'])return false;
                    vis[b[j][i]-'0'] = 1;
                }
            }
        }
        for (int i = 0; i <= 6; i += 3){
            for (int j = 0; j <= 6; j += 3){
                int vis[10] = {0};
                for (int k = i; k < i+3; k ++){
                    for (int l = j; l < j+3; l ++){
                        if (b[k][l] != '.'){
                            if (vis[b[k][l]-'0'])return false;
                            vis[b[k][l]-'0'] = 1;
                        }
                    }
                }
            }
        }
        return true;
    }
};

(博弈-简单)Nim 游戏

题目链接
简单题:模4 余 0则必输,反之必赢。

class Solution {
public:
    bool canWinNim(int n) {
        return n%4;
    }
};

(DP-中等)只有两个键的键盘

题目链接
DP

class Solution {
public:
    int dp[1001];
    int minSteps(int n) {
        dp[1] = 0;
        for (int i = 2; i <= n; i ++){
            dp[i] = i;
            for (int j = i/2; j >= 2; j --)
                if (i%j==0) {
                    dp[i] = min(dp[i],dp[j]+i/j);
                    break;
                }
        }
        return dp[n];
    }
};

分解质因数
证明

class Solution {
public:
    int minSteps(int n) {
        int ans = 0;
        for (int i = 2; i * i <= n; ++i) {
            while (n % i == 0) {
                n /= i;
                ans += i;
            }
        }
        if (n > 1) ans += n;
        return ans;
    }
};

(DP-中等)最长递增子序列的个数

题目链接

class Solution {
public:
    int findNumberOfLIS(vector<int> &nums) {
        int n = nums.size(), maxLen = 0, ans = 0;
        vector<int> dp(n), cnt(n);
        for (int i = 0; i < n; ++i) {
            dp[i] = 1;
            cnt[i] = 1;
            for (int j = 0; j < i; ++j) {
                if (nums[i] > nums[j]) {
                    if (dp[j] + 1 > dp[i]) {
                        dp[i] = dp[j] + 1;
                        cnt[i] = cnt[j]; // 重置计数
                    } else if (dp[j] + 1 == dp[i]) {
                        cnt[i] += cnt[j];
                    }
                }
            }
            if (dp[i] > maxLen) {
                maxLen = dp[i];
                ans = cnt[i]; // 重置计数
            } else if (dp[i] == maxLen) {
                ans += cnt[i];
            }
        }
        return ans;
    }
};

(模拟-简单)最后一个单词的长度

题目链接

class Solution {
public:
    int lengthOfLastWord(string s) {
        int le = s.size(),ans = 0;
        for (int i = le - 1; i >= 0;i --){
            if (ans&&s[i] == ' ')break;
            if (!ans&&s[i] == ' ')continue;
            ans ++;
        }
        return ans;
    }
};

(链表-中等)分隔链表

题目链接
好 久 没 写 过 链 表 了 , 都 陌 生 了 好久没写过链表了, 都陌生了 ,

/**
 * 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:
    vector<ListNode*> splitListToParts(ListNode* head, int k) {
        int n = 0;
        ListNode *temp = head;
        while (temp != nullptr) {	//计算链表中有多少点
            n++;
            temp = temp->next;
        }
        int div = n / k, mod = n % k;
        vector<ListNode*> res(k,nullptr);
        ListNode *p = head;
        for (int i = 0; i < k && p != nullptr; i++) {
            res[i] = p;
            int partSize = div + (i < mod ? 1 : 0);
            for (int j = 1; j < partSize; j++) {	
                p = p->next;
            }				//这里采用的把原链表拆分成若干个子链表
            ListNode *next = p->next;
            p->next = nullptr;
            p = next;
        }
        return res;
    }
};

(模拟-简单)3的幂

题目链接

class Solution {
public:
    bool isPowerOfThree(int n) {
        if (n == 0)return false;
        while (n % 3 == 0)n /= 3;
        if (n == 1) return true;
        return false;
    }
};

(链表-中等)扁平化多级双向链表

一 道 不 错 的 链 表 题 一道不错的链表题 题目链接

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* prev;
    Node* next;
    Node* child;
};
*/

class Solution {
public:
    Node* dfs(Node* head){
        if (head == nullptr)return nullptr;
        Node* p = head;
        Node* t = head->next;	//记录当前结点的下一个
        if (p->child != nullptr){	//有孩子的情况

            head->next = head->child;	//连接孩子结点
            head->child->prev = head;
            head->child = nullptr;		//把这个指针赋值为空,要不然会报一个非法链表错误
            
            p = dfs(head->next);
            
            if (t != nullptr){
                p->next = t;
                t->prev = p;
                return dfs(t);
            }else return p;
        }else if (p->next != nullptr)return dfs(p->next);//当前结点下一个存在,就继续dfs
        else if (p->next == nullptr)return p;//如果当前链表走到最后一个,直接返回
        return nullptr;		//根本不会走到这一步,但是不写编译器就不会编译通过,它担心我没有返回值。
    }
    Node* flatten(Node* head) {
        Node* p = head;
        dfs(p);
        return p;
    }
};

(DP-中等)两个字符串的删除操作

题目链接
模板题:最长公共子序列

class Solution {
public:
    int dp[501][501];
    int minDistance(string w1, string w2) {
        int n = w1.size(),m = w2.size();
        for (int i = 1; i <= n; i ++){
            for (int j = 1; j <= m; j ++){
                dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                if (w1[i-1] == w2[j-1])dp[i][j] = max(dp[i][j],dp[i-1][j-1] + 1);
            }
        }
        return n - dp[n][m] + m - dp[n][m];
    }
};

(加法-中等)两整数之和

题目链接

class Solution {
public:
    int getSum(int a, int b) {
        return a+b;
    }
};
class Solution {
public:
	int getSum(int a, int b) {
        while (b != 0){
            int carry = (a&b)<<1; // 所有需要进位的bit
            a ^= b;
            b = carry;
        }
        return a;
    }
}

(DP-困难)解码方法 II

题目链接
哎,这道题写的我心累。
思路不难就是细节太多了。

const int mod = 1e9+7;
typedef long long ll;
class Solution {
public:
    ll mod_pow(ll a, ll b, ll p){//我求解过程中有一步要除9,如果取余需要用到逆元
        ll ans = 1;
        a %= p;
        while(b>0){
            if(b & 1) 
                ans = ans * a % p;
            a = a * a % p;
            b >>= 1;
        }
        return ans;
    }
    ll dp[100010][2];//1 代表结合上一个,0 代表不结合上一个
    int numDecodings(string s) {
        int n = s.size();
        if (s[0] == '0')return 0;
        //初始化
        if (s[0] == '*'){
            dp[0][0] = 9;	//不与上一个结合
            dp[0][1] = 0;	//上一个没有,那情况就是0
        }else {
            dp[0][0] = 1;	//不与上一个结合
            dp[0][1] = 0;
        }
        for (int i = 1; i < n; i ++){
        	//考虑当前字符是*的情况
            if (s[i] == '*'){
            	//前一个字符是*
                if (s[i-1] == '*'){
                    dp[i][0] = ((dp[i-1][0]+dp[i-1][1])%mod*9)%mod;
                    dp[i][1] = (dp[i-1][0]*mod_pow(9,mod-2,mod)*15)%mod;
                    continue;
                }
                if (s[i-1] == '1')dp[i][1] = (dp[i-1][0] * 9)%mod;
                if (s[i-1] == '2')dp[i][1] = (dp[i-1][0] * 6)%mod;
                dp[i][0] = ((dp[i-1][1]+dp[i-1][0])*9)%mod;
            }
            //当前字符是[1,6]
            if ('1' <= s[i] && s[i] <= '6') {
                dp[i][0] = (dp[i-1][0] + dp[i-1][1])%mod;
                if (s[i-1]=='1'||s[i-1]=='2')dp[i][1] = dp[i-1][0];
                if (s[i-1]=='*')dp[i][1] = (dp[i-1][0]*mod_pow(9,mod-2,mod)*2)%mod;
            }
            //当前字符是[7,9]
            if ('7' <= s[i] && s[i] <= '9'){
                if (s[i-1]=='*')dp[i][1] = dp[i-1][0]*mod_pow(9,mod-2,mod);
                dp[i][0] = (dp[i-1][0] + dp[i-1][1])%mod;
                if (s[i-1]=='1')dp[i][1] = dp[i-1][0];
            }
            //当前字符是0
            if (s[i] == '0'){
                if (s[i-1] == '0') return 0;
                if ('1' <= s[i-1] && s[i-1] <= '2')dp[i][1] = dp[i-1][0];
                if (s[i-1] == '*') dp[i][1] = dp[i-1][0]*mod_pow(9,mod-2,mod)*2%mod;
            }
        }
        return (dp[n-1][0] + dp[n-1][1])%mod;
    }
};

(DFS-中等)路径总和 III

题目链接
D F S DFS DFS

/**
 * 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 rootSum(TreeNode* root, int targetSum) {
        if (!root) return 0;
        int ret = 0;
        if (root->val == targetSum) ret++;
        ret += rootSum(root->left, targetSum - root->val);
        ret += rootSum(root->right, targetSum - root->val);
        return ret;
    }
    int pathSum(TreeNode* root, int targetSum) {
        if (!root) return 0;
        int ret = rootSum(root, targetSum);     //以当前结点为起点
        ret += pathSum(root->left, targetSum);  //以左孩子为起点
        ret += pathSum(root->right, targetSum); //以右孩子为起点
        return ret;
    }
};

前 缀 和 前缀和

class Solution {
public:
    unordered_map<int, int> sum;		//前缀和数组
    int dfs(TreeNode *root, long long curr, int targetSum) {
        if (!root) return 0;
        int ret = 0;
        curr += root->val;
        if (sum.count(curr - targetSum)) ret = sum[curr - targetSum];//记录以当前结点为结束时的数量
        
        sum[curr]++;	//当前点计数
        ret += dfs(root->left, curr, targetSum);
        ret += dfs(root->right, curr, targetSum);
        sum[curr]--;	//用完必须减掉,要不然会影响其他分支。

        return ret;
    }
    int pathSum(TreeNode* root, int targetSum) {
        sum[0] = 1;
        return dfs(root, 0, targetSum);
    }
};

(贪心-困难)超级洗衣机

题目链接
题解链接
说 实 话 贪 心 是 真 的 难 说实话贪心是真的难

class Solution {
public:
    int findMinMoves(vector<int>& m) {
        int n = m.size(),sum = 0;
        for (int i = 0; i < n; i ++)sum+=m[i];
        if (sum % n)return -1;
        int res = 0,avg = sum/n;
        sum = 0;
        for (int i = 1; i <= n; i ++){
            sum += m[i-1];
            res = max(res,max((m[i-1]-avg),abs(sum-i*avg)));
        }
        return res;
    }
};

(数学-中等)矩形面积

题目链接

两个矩形覆盖的总面积等于两个矩形的面积之和减去两个矩形的重叠部分的面积。由于两个矩形的左下顶点和右上顶点已知,因此两个矩形的面积可以直接计算。如果两个矩形重叠,则两个矩形的重叠部分也是矩形,重叠部分的面积可以根据重叠部分的边界计算。
通过对x和y轴进行投影可以找出重叠的部分。
在x轴投影的线段 [ m a x ( a x 1 , b x 1 ) , m i n ( a x 2 , b x 2 ) ] [max(ax1,bx1),min(ax2,bx2)] [max(ax1,bx1),min(ax2,bx2)]
在y轴上投影的线段 [ m a x ( a y 1 , b y 1 ) , m i n ( a y 2 , b y 2 ) ] [max(ay1,by1),min(ay2,by2)] [max(ay1,by1),min(ay2,by2)]

class Solution {
public:
    int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
        int res = 0;
        int aS = (ax2-ax1)*(ay2-ay1),bS = (bx2-bx1)*(by2-by1);
        int x1 = max(ax1,bx1),x2 = min(ax2,bx2),y1 = max(ay1,by1),y2 = min(ay2,by2);
        if (x1 >= x2 || y1 >= y2)return aS + bS;
        int cS = (x2-x1)*(y2-y1);
        return aS + bS - cS;

    }
};

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