Leecode题目整理(二)

PAGE 5-12

(简单题完结)

821. 字符的最短距离

给你一个字符串 s 和一个字符 c ,且 c 是 s 中出现过的字符。

返回一个整数数组 answer ,其中 answer.length == s.length 且 answer[i] 是 s 中从下标 i
到离它 最近 的字符 c 的 距离 。

两个下标 i 和 j 之间的 距离 为 abs(i - j) ,其中 abs 是绝对值函数。

示例1:

输入:s = “loveleetcode”, c = “e”
输出:[3,2,1,0,1,0,0,1,2,2,1,0]
解释:字符 ‘e’ 出现在下标 3、5、6 和 11 处(下标从 0 开始计数)。
距下标 0 最近的 ‘e’ 出现在下标 3 ,所以距离为 abs(0 - 3) = 3 。
距下标 1 最近的 ‘e’ 出现在下标 3 ,所以距离为 abs(1 - 3) = 3 。
对于下标 4 ,出现在下标 3 和下标 5 处的 ‘e’ 都离它最近,但距离是一样的 abs(4 - 3) == abs(4 - 5) = 1 。
距下标 8 最近的 ‘e’ 出现在下标 6 ,所以距离为 abs(8 - 6) = 2 。

示例2:

输入:s = “aaab”, c = “b”
输出:[3,2,1,0]

代码:

class Solution {
public:
    vector<int> shortestToChar(string s, char c) {
        vector<int> index;
        for(int i = 0;i<s.length();i++)
        {
            if(s[i]==c)
                index.push_back(i);
        }

        vector<int> ans;
        for(int i = 0;i<s.length();i++)
        {
            int min_dis = INT_MAX;
            for(int j = 0;j<index.size();j++)
            {
                if(abs(index[j]-i)<min_dis)
                    min_dis = abs(index[j]-i);
            }
            ans.push_back(min_dis);
        }
        return ans;
    }
};



1200. 最小绝对差

给你个整数数组 arr,其中每个元素都 不相同。

请你找到所有具有最小绝对差的元素对,并且按升序的顺序返回。

示例1:

输入:arr = [4,2,1,3]
输出:[[1,2],[2,3],[3,4]]

示例2:

输入:arr = [1,3,6,10,15]
输出:[[1,3]]

代码:

class Solution {
public:
    vector<vector<int>> minimumAbsDifference(vector<int>& arr) {
        vector<vector<int>> ans;
        if(arr.size()<3) return {arr};
        sort(arr.begin(),arr.end());
        int mindiff = arr[1]-arr[0];
        ans.push_back({arr[0],arr[1]});
        for(int i = 2;i<arr.size();i++)
        {
            int curdiff = arr[i]-arr[i-1];
            if(curdiff<mindiff)
            {
                while(ans.size())
                {
                    ans.pop_back();
                }
                ans.push_back({arr[i-1],arr[i]});
                mindiff = curdiff;
            }else if(curdiff==mindiff)
            {
                ans.push_back({arr[i-1],arr[i]});
            }
        }
        return ans;
    }
};



496. 下一个更大元素 I

给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1。

示例1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

示例2:

输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

代码:

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        map<int,int> index;
        for(int i = 0;i<nums2.size();i++)
        {
            index[nums2[i]]=i;
        }

        vector<int> ans;
        for(int i = 0;i<nums1.size();i++)
        {
            int flag = true;
            for(int j = index[nums1[i]]+1;j<nums2.size();j++)
            {
                if(nums2[j]>nums1[i])
                {
                    ans.push_back(nums2[j]);
                    flag = false;
                    break;
                }
            }
            if(flag)
            {
                ans.push_back(-1);
            }
        }
        return ans;
    }
};

1332. 删除回文子序列

给你一个字符串 s,它仅由字母 ‘a’ 和 ‘b’ 组成。每一次删除操作都可以从 s 中删除一个回文 子序列。

返回删除给定字符串中所有字符(字符串为空)的最小删除次数。

「子序列」定义:如果一个字符串可以通过删除原字符串某些字符而不改变原字符顺序得到,那么这个字符串就是原字符串的一个子序列。

「回文」定义:如果一个字符串向后和向前读是一致的,那么这个字符串就是一个回文。

示例1:

输入:s = “ababa”
输出:1
解释:字符串本身就是回文序列,只需要删除一次。

示例2:

输入:s = “abb”
输出:2
解释:“abb” -> “bb” -> “”.
先删除回文子序列 “a”,然后再删除 “bb”。

示例3:

输入:s = “baabb”
输出:2
解释:“baabb” -> “b” -> “”.
先删除回文子序列 “baab”,然后再删除 “b”。

示例4:

输入:s = “”
输出:0

代码:

#include <algorithm>
class Solution {
public:
    int removePalindromeSub(string s) {
        string s_temp = s;
        reverse(s.begin(),s.end());
        if(s=="") return 0;
        else if(s==s_temp) return 1;
        return 2;
    }
};

122. 买卖股票的最佳时机 II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例3:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int dp[n][2];
        dp[0][0] = 0, dp[0][1] = -prices[0];
        for (int i = 1; i < n; ++i) {
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
        }
        return dp[n - 1][0];
    }
};

225. 用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通队列的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

void push(int x) 将元素 x 压入栈顶。 int pop() 移除并返回栈顶元素。 int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

示例:

输入:
[“MyStack”, “push”, “push”, “top”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释: MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回False

代码:

class MyStack {
private:
    queue<int> q;
public:
    /** Initialize your data structure here. */
    MyStack() {
        while(q.size())
        {
            q.pop();
        }
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        int n = q.size();
        q.push(x);
        for(int i = 0;i<n;i++)
        {
            q.push(q.front());
            q.pop();
        }
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        if(q.empty())
            return -1;
        int t = q.front();
        q.pop();
        return t;
    }
    
    /** Get the top element. */
    int top() {
        if(q.empty())
            return -1;
        int t = q.front();
        return t;
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return q.empty();
    }
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

257. 二叉树的所有路径

给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。

示例:

输入:
             1
           /    \
         2      3
           \
             5
输出: [“1->2->5”, “1->3”]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3

代码:

class Solution {
public:
    void construct_paths(TreeNode* root, string path, vector<string>& paths) {
        if (root != nullptr) {
            path += to_string(root->val);
            if (root->left == nullptr && root->right == nullptr) {  // 当前节点是叶子节点
                paths.push_back(path);                              // 把路径加入到答案中
            } else {
                path += "->";  // 当前节点不是叶子节点,继续递归遍历
                construct_paths(root->left, path, paths);
                construct_paths(root->right, path, paths);
            }
        }
    }

    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> paths;
        construct_paths(root, "", paths);
        return paths;
    }
};

剑指 Offer 57. 和为s的两个数字

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

示例1:

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]

示例2:

输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]

代码:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int i = 0,j = nums.size()-1;
        vector<int> ans;
        while(i<j)
        {
            if(nums[i]+nums[j]<target)
                i++;
            else if(nums[i]+nums[j]>target)
                j--;
            else
            {
                ans.push_back(nums[i]);
                ans.push_back(nums[j]);
                break;
            }
        }
        return ans;
    }
};

1337. 矩阵中战斗力最弱的 K 行

给你一个大小为 m * n 的矩阵 mat,矩阵由若干军人和平民组成,分别用 1 和 0 表示。

请你返回矩阵中战斗力最弱的 k 行的索引,按从最弱到最强排序。

如果第 i 行的军人数量少于第 j 行,或者两行军人数量相同但 i 小于 j,那么我们认为第 i 行的战斗力比第 j 行弱。

军人 总是 排在一行中的靠前位置,也就是说 1 总是出现在 0 之前。

示例1:

输入:mat =
[[1,1,0,0,0],
[1,1,1,1,0],
[1,0,0,0,0],
[1,1,0,0,0],
[1,1,1,1,1]],
k = 3
输出:[2,0,3]
解释:
每行中的军人数目:
行 0 -> 2
行 1 -> 4
行 2 -> 1
行 3 -> 2
行 4 -> 5
从最弱到最强对这些行排序后得到 [2,0,3,1,4]

示例2:

输入:mat =
[[1,0,0,0],
[1,1,1,1],
[1,0,0,0],
[1,0,0,0]],
k = 2
输出:[0,2]
解释:
每行中的军人数目:
行 0 -> 1
行 1 -> 4
行 2 -> 1
行 3 -> 1
从最弱到最强对这些行排序后得到 [0,2,3,1]

代码:

class Solution {
public:
    vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
        multimap<int,int> m;
        for(int i = 0;i<mat.size();i++)
        {
            int accu = accumulate(mat[i].begin(),mat[i].end(),0);
            m.insert(pair<int,int>(accu,i));
        }
        vector<int> ans;

        multimap<int,int>::iterator ite = m.begin();
        for(int i = 0;i<k;i++)
        {
            ans.push_back(ite->second);
            ite++;
        }
        return ans;
    }
};

21. 合并两个有序链表

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

示例1:
在这里插入图片描述

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例2:

输入:l1 = [], l2 = []
输出:[]

示例3:

输入:l1 = [], l2 = [0]
输出:[0]

代码:

/**
 * 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* l1, ListNode* l2) {
        ListNode* p = new ListNode(0);
        ListNode* t = p;
        while(l1&&l2)
        {
            if(l1->val < l2->val)
            {
                p->next = l1;
                l1 = l1->next;
            }else
            {
                p->next = l2;
                l2 = l2->next;
            }
            p = p->next;
        }

        if(l1) p->next = l1;
        else p->next = l2;
        return t->next;
    }
};

面试题 08.06. 汉诺塔问题

在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。
你需要原地修改栈。

示例1:

输入:A = [2, 1, 0], B = [], C = []
输出:C = [2, 1, 0]

示例2:

输入:A = [1, 0], B = [], C = []
输出:C = [1, 0]

代码:

class Solution {
public:
    void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
        int n = A.size();
        move(n, A, B, C);
    }

    void move(int n, vector<int>& A, vector<int>& B, vector<int>& C){
        if (n == 1){
            C.push_back(A.back());
            A.pop_back();
            return;
        }

        move(n-1, A, C, B);    // 将A上面n-1个通过C移到B
        C.push_back(A.back());  // 将A最后一个移到C
        A.pop_back();          // 这时,A空了
        move(n-1, B, A, C);     // 将B上面n-1个通过空的A移到C
    }
};

剑指 Offer 62. 圆圈中最后剩下的数字

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

示例1:

输入: n = 5, m = 3
输出: 3

示例2:

输入: n = 10, m = 17
输出: 2

思路:

既然约塞夫问题就是用人来举例的,那我们也给每个人一个编号(索引值),每个人用字母代替
下面这个例子是N=8 m=3的例子
我们定义F(n,m)表示最后剩下那个人的索引号,因此我们只关系最后剩下来这个人的索引号的变化情况即可
在这里插入图片描述
从8个人开始,每次杀掉一个人,去掉被杀的人,然后把杀掉那个人之后的第一个人作为开头重新编号
第一次C被杀掉,人数变成7,D作为开头,(最终活下来的G的编号从6变成3)
第二次F被杀掉,人数变成6,G作为开头,(最终活下来的G的编号从3变成0)
第三次A被杀掉,人数变成5,B作为开头,(最终活下来的G的编号从0变成3)
以此类推,当只剩一个人时,他的编号必定为0!(重点!)


3 最终活着的人编号的反推
现在我们知道了G的索引号的变化过程,那么我们反推一下
从N = 7 到N = 8 的过程
如何才能将N = 7 的排列变回到N = 8 呢?
我们先把被杀掉的C补充回来,然后右移m个人,发现溢出了,再把溢出的补充在最前面
神奇了 经过这个操作就恢复了N = 8 的排列了!
在这里插入图片描述
因此我们可以推出递推公式f(8,3) = [f(7, 3) + 3] % 8
进行推广泛化,即f(n,m) = [f(n-1, m) + m] % n


4 递推公式的导出
再把n=1这个最初的情况加上,就得到递推公式
在这里插入图片描述

代码:

class Solution {
public:
    int lastRemaining(int n, int m) {
        //约瑟夫环问题
        int pos = 0; // 最终活下来那个人的初始位置
        for(int i = 2; i <= n; i++){
            pos = (pos + m) % i;  // 每次循环右移
        }
        return pos;
    }
};

892. 三维形体的表面积

给你一个 n * n 的网格 grid ,上面放置着一些 1 x 1 x 1 的正方体。

每个值 v = grid[i][j] 表示 v 个正方体叠放在对应单元格 (i, j) 上。

放置好正方体后,任何直接相邻的正方体都会互相粘在一起,形成一些不规则的三维形体。

请你返回最终这些形体的总表面积。

注意:每个形体的底面也需要计入表面积中。

示例1:
在这里插入图片描述

输入:grid = [[2]]
输出:10

示例2:
在这里插入图片描述

输入:grid = [[1,2],[3,4]]
输出:34

示例3:
在这里插入图片描述

输入:grid = [[1,0],[0,2]]
输出:16

示例4:
在这里插入图片描述

输入:grid = [[1,1,1],[1,0,1],[1,1,1]]
输出:32

代码:

class Solution {
public:
    int surfaceArea(vector<vector<int>>& grid) {
        int nr[4] = {-1,1,0,0};
        int nc[4] = {0,0,-1,1};
        int ans = 0;
        int N = grid.size();
        for(int r = 0;r<N;r++)
        {
            for(int c = 0;c<N;c++)
            {
                if(grid[r][c])
                    ans+=2;
                for(int k = 0;k<4;k++)
                {
                    int rn = r+nr[k];
                    int cn = c+nc[k];
                    int vn = 0;
                    if(rn>=0&&rn<N&&cn>=0&&cn<N)
                    {
                        vn = grid[rn][cn];
                    }
                    ans+=max(grid[r][c]-vn,0);
                }
            }
        }
        return ans;
    }
};

888. 公平的糖果棒交换

爱丽丝和鲍勃有不同大小的糖果棒:A[i] 是爱丽丝拥有的第 i 根糖果棒的大小,B[j] 是鲍勃拥有的第 j 根糖果棒的大小。

因为他们是朋友,所以他们想交换一根糖果棒,这样交换后,他们都有相同的糖果总量。(一个人拥有的糖果总量是他们拥有的糖果棒大小的总和。)

返回一个整数数组 ans,其中 ans[0] 是爱丽丝必须交换的糖果棒的大小,ans[1] 是 Bob 必须交换的糖果棒的大小。

如果有多个答案,你可以返回其中任何一个。保证答案存在。

示例1:

输入:A = [1,1], B = [2,2]
输出:[1,2]

示例2:

输入:A = [1,2], B = [2,3]
输出:[1,2]

示例3:

输入:A = [2], B = [1,3]
输出:[2,3]

代码:

class Solution {
public:
    vector<int> fairCandySwap(vector<int>& A, vector<int>& B) {
        int sumA = accumulate(A.begin(), A.end(), 0);
        int sumB = accumulate(B.begin(), B.end(), 0);
        int delta = (sumA - sumB) / 2;
        unordered_set<int> rec(A.begin(), A.end());
        vector<int> ans;
        for (auto& y : B) {
            int x = y + delta;
            if (rec.count(x)) {
                ans = vector<int>{x, y};
                break;
            }
        }
        return ans;
    }
};

448. 找到所有数组中消失的数字

给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。

示例:

输入:
[4,3,2,7,8,2,3,1]


输出:
[5,6]

代码:

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        vector<int> ans;
        if(nums.empty()) return ans;
        int len = nums.size();
        for(int i = 0;i<len;i++)
        {
            int index = (nums[i]-1)%len;
            nums[index]+=len;
        }
        for(int i = 0;i<len;i++)
        {
            if(nums[i]<=len)
                ans.push_back(i+1);
        }
        return ans;
    }
};

1763. 最长的美好子字符串

当一个字符串 s 包含的每一种字母的大写和小写形式 同时 出现在 s 中,就称这个字符串 s 是 美好 字符串。比方说,“abABB” 是美好字符串,因为 ‘A’ 和 ‘a’ 同时出现了,且 ‘B’ 和 ‘b’ 也同时出现了。然而,“abA” 不是美好字符串因为 ‘b’ 出现了,而 ‘B’ 没有出现。
给你一个字符串 s ,请你返回 s 最长的 美好子字符串 。如果有多个答案,请你返回 最早 出现的一个。如果不存在美好子字符串,请你返回一个空字符串。

示例1:

输入:s = “YazaAay”
输出:“aAa”
解释:“aAa” 是一个美好字符串,因为这个子串中仅含一种字母,其小写形式 ‘a’ 和大写形式 ‘A’ 也同时出现了。
“aAa” 是最长的美好子字符串。

示例2:

输入:s = “Bb”
输出:“Bb”
解释:“Bb” 是美好字符串,因为 ‘B’ 和 ‘b’ 都出现了。整个字符串也是原字符串的子字符串。

示例3:

输入:s = “c”
输出:""
解释:没有美好子字符串。

示例4:

输入:s = “dDzeE”
输出:“dD”
解释:“dD” 和 “eE” 都是最长美好子字符串。
由于有多个美好子字符串,返回 “dD” ,因为它出现得最早。

代码:

class Solution {
public:
    bool isOK(string s)   //判断是否是完美串
    {
        int aa[26] = {0};
        int AA[26] = {0};
        for (char c : s) {
            if (c>='A'&&c<='Z') {  
                AA[c - 'A']++;
            } else {
                aa[c - 'a']++;
            }
        }
        for (int i = 0; i < 26; ++i) {
            if ((aa[i] > 0 && AA[i] == 0) || (aa[i] == 0 && AA[i] > 0)) return false;
        }
        return true;
    }
    string longestNiceSubstring(string s) {   
        int left = 0, right = 0, len = s.size();
        string ret;
        if (len < 2) return "";
        for (int i = 2; i <= len; ++i) { // 固定长度的滑窗
            for (int j = 0; j + i - 1 < len; j++) {
                string str = s.substr(j, i);
                if (str.size() > ret.size() && isOK(str)) ret = str;
            }
        }
        return ret;
    }
};

面试题 17.12. BiNode

二叉树数据结构TreeNode可用来表示单向链表(其中left置空,right为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。
返回转换后的单向链表的头节点。

示例:

输入: [4,2,5,1,3,null,6,0]
输出: [0,null,1,null,2,null,3,null,4,null,5,null,6]

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode *ans = new TreeNode(0),*cur = ans;
    TreeNode* convertBiNode(TreeNode* root) {
        inOrder(root);
        return ans->right;
    }

    void inOrder(TreeNode* root)
    {
        if(!root) return;
        inOrder(root->left);
        root->left = NULL;
        cur->right = root;
        cur = root;
        inOrder(root->right);
    }
};

696. 计数二进制子串

给定一个字符串 s,计算具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是连续的。
重复出现的子串要计算它们出现的次数。

示例1:

输入: “00110011”
输出: 6
解释: 有6个子串具有相同数量的连续1和0:“0011”,“01”,“1100”,“10”,“0011” 和 “01”。
请注意,一些重复出现的子串要计算它们出现的次数。
另外,“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。

示例2:

输入: “10101”
输出: 4
解释: 有4个子串:“10”,“01”,“10”,“01”,它们具有相同数量的连续1和0。

代码:

class Solution {
public:
    int countBinarySubstrings(string s) {
        vector<int> calc;
        int index = 0;
        int len = s.length();
        while(index<len)
        {
            char c = s[index];
            int count = 0;
            while(index<len&&s[index]==c)
            {
                count++;
                index++;
            }
            calc.push_back(count);
        }
        int ans = 0;
        for(int i = 0;i<calc.size()-1;i++)
        {
            ans+=min(calc[i],calc[i+1]);
        }
        return ans;
    }
};

1758. 生成交替二进制字符串的最少操作数

给你一个仅由字符 ‘0’ 和 ‘1’ 组成的字符串 s 。一步操作中,你可以将任一 ‘0’ 变成 ‘1’ ,或者将 ‘1’ 变成 ‘0’ 。

交替字符串 定义为:如果字符串中不存在相邻两个字符相等的情况,那么该字符串就是交替字符串。例如,字符串 “010” 是交替字符串,而字符串
“0100” 不是。

返回使 s 变成 交替字符串 所需的 最少 操作数。

示例1:

输入:s = “0100”
输出:1
解释:如果将最后一个字符变为 ‘1’ ,s 就变成 “0101” ,即符合交替字符串定义。

示例2:

输入:s = “10”
输出:0
解释:s 已经是交替字符串。

示例3:

输入:s = “1111”
输出:2
解释:需要 2 步操作得到 “0101” 或 “1010” 。

代码:

class Solution {
public:
    int minOperations(string s) {
        int len = s.length(),cnt1 = 0,cnt2 = 0;
        for(int i = 0;i<len;i++)
        {
            if(s[i]%2!=i%2)  //如果不满足偶数为0,奇数为1的情况
                cnt1++;
            else cnt2++;
        }
        return min(cnt1,cnt2);
    }
};

202. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果 可以变为 1,那么这个数就是快乐数。 如果 n 是快乐数就返回 true ;不是,则返回 false 。

示例1:

输入:19
输出:true
解释:
1² + 9² = 82
8² + 2² = 68
6² + 8² = 100
1² + 0² + 0² = 1

示例2:

输入:n = 2
输出:false

代码:

class Solution {
public:
    int bitSquareSum(int n) {
        int sum = 0;
        while(n > 0)
        {
            int bit = n % 10;
            sum += bit * bit;
            n = n / 10;
        }
        return sum;
    }
    
    bool isHappy(int n) {
        int slow = n, fast = n;
        do{
            slow = bitSquareSum(slow);
            fast = bitSquareSum(fast);
            fast = bitSquareSum(fast);
        }while(slow != fast);
        
        return slow == 1;
    }
};

1185. 一周中的第几天

给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。

输入为三个整数:day、month 和 year,分别表示日、月、年。

您返回的结果必须是这几个值中的一个 {“Sunday”, “Monday”, “Tuesday”, “Wednesday”,
“Thursday”, “Friday”, “Saturday”}。

示例1:

输入:day = 31, month = 8, year = 2019
输出:“Saturday”

示例2:

输入:day = 18, month = 7, year = 1999
输出:“Sunday”

示例3:

输入:day = 15, month = 8, year = 1993
输出:“Sunday”

代码:

class Solution {
public:
    string dayOfTheWeek(int day, int month, int year) {
        if(month==1||month==2) month+=12,year--;
	int iWeek = (day+2*month+3*(month+1)/5+year+year/4-year/100+year/400)%7;                              //基姆拉尔森计算公式
       string result[]= { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday","Sunday"};
	return result[iWeek];
    }
};

697. 数组的度

给定一个非空且只包含非负数的整数数组 nums,数组的度的定义是指数组里任一元素出现频数的最大值。
你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。

示例1:

输入:[1, 2, 2, 3, 1]
输出:2
解释:
输入数组的度是2,因为元素1和2的出现频数最大,均为2.
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组[2, 2]的长度为2,所以返回2.

示例2:

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

代码:

class Solution {
public:
    int findShortestSubArray(vector<int>& nums) {
        unordered_map<int, vector<int>> mp;  //key为数字,vector有三个元素  分别为出现次数,第一次和最后一次的位置
        int n = nums.size();
        for (int i = 0; i < n; i++) {
            if (mp.count(nums[i])) {   //如果出现过
                mp[nums[i]][0]++;
                mp[nums[i]][2] = i;
            } else {  //如果没出现过
                mp[nums[i]] = {1, i, i};
            }
        }
        int maxNum = 0, minLen = 0;
        for (auto& [_, vec] : mp) {
            if (maxNum < vec[0]) {
                maxNum = vec[0];
                minLen = vec[2] - vec[1] + 1;
            } else if (maxNum == vec[0]) {
                if (minLen > vec[2] - vec[1] + 1) {
                    minLen = vec[2] - vec[1] + 1;
                }
            }
        }
        return minLen;
    }
};

面试题 03.02. 栈的最小值

请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。执行push、pop和min操作的时间复杂度必须为O(1)。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

代码:

class MinStack {
private:
    stack<int> s;
    int min = INT_MAX;
public:
    /** initialize your data structure here. */
    MinStack() {
        while(s.size())
            s.pop();
    }
    
    void push(int x) {
        if(x<=min)
        {
            if(!s.empty())
                s.push(min);
            min = x;
        }
        s.push(x);
    }
    
    void pop() {
        if(s.empty()) return;
        if(s.size()==1) min = INT_MAX;
        if(s.top()==min)
        {
            s.pop();
            min = s.top();
        }
        s.pop();
    }
    
    int top() {
        return s.top();
    }
    
    int getMin() {
        return min;
    }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

788. 旋转数字

我们称一个数 X 为好数, 如果它的每位数字逐个地被旋转 180 度后,我们仍可以得到一个有效的,且和 X 不同的数。要求每位数字都要被旋转。
如果一个数的每位数字被旋转以后仍然还是一个数字, 则这个数是有效的。0, 1, 和 8 被旋转后仍然是它们自己;2 和 5 可以互相旋转成对方(在这种情况下,它们以不同的方向旋转,换句话说,2 和 5 互为镜像);6 和 9 同理,除了这些以外其他的数字旋转以后都不再是有效的数字。
现在我们有一个正整数 N, 计算从 1 到 N 中有多少个数 X 是好数?

示例:

输入: 10
输出: 4
解释:
在[1, 10]中有四个好数: 2, 5, 6, 9。
注意 1 和 10 不是好数, 因为他们在旋转之后不变。

代码:

class Solution {
public:
    int rotatedDigits(int N) {
        int count = 0;
        for(int i = 1;i<=N;i++)
        {
            int num = i;
            bool flag = false;
            while(num)
            {
                int res = num%10;
                if(res==3||res==4||res==7)
                    break;
                if(res==5 ||res==2||res==6||res==9)
                    flag = true;
                num/=10;
            }
            if(flag&&!num)  //必须是num==0,例如例子32
                count++;
        }
        return count;
    }
};

剑指 Offer 42. 连续子数组的最大和

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。

示例:

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

代码:

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

690. 员工的重要性

给定一个保存员工信息的数据结构,它包含了员工 唯一的 id ,重要度 和 直系下属的 id 。

比如,员工 1 是员工 2 的领导,员工 2 是员工 3 的领导。他们相应的重要度为 15 , 10 , 5 。那么员工 1 的数据结构是[1, 15, [2]] ,员工 2的 数据结构是 [2, 10, [3]] ,员工 3 的数据结构是 [3, 5, []] 。注意虽然员工3 也是员工 1 的一个下属,但是由于 并不是直系 下属,因此没有体现在员工 1 的数据结构中。

现在输入一个公司的所有员工信息,以及单个员工 id ,返回这个员工和他所有下属的重要度之和。

示例:

输入:[[1, 5, [2, 3]], [2, 3, []], [3, 3, []]], 1
输出:11
解释:
员工 1 自身的重要度是 5 ,他有两个直系下属 2 和 3 ,而且 2 和 3 的重要度均为 3 。因此员工 1 的总重要度是 5 + 3 + 3 = 11 。

代码1:

class Solution {
public:
    int getImportance(vector<Employee*> employees, int id) {
        unordered_map<int, Employee*> mp;
        for (auto p : employees) {
            if (p == nullptr) continue;
            mp[p->id] = p;
        }

        int ans = 0;
        queue<int> que;
        que.push(id);

        while (!que.empty()) {
            ans += mp[que.front()]->importance;
            for (auto n : mp[que.front()]->subordinates) {
                que.push(n);
            }
            que.pop();
        }
        return ans;
    }
};

代码2:

class Solution {
public:
    int dfs(unordered_map<int, Employee*>& mp, int id) {
        if (mp.count(id) == 0 || mp[id] == nullptr) return 0;
        int ans = mp[id]->importance;
        for (auto n : mp[id]->subordinates) {
            ans += dfs(mp, n);
        }
        return ans;
    }

    int getImportance(vector<Employee*> employees, int id) {
        unordered_map<int, Employee*> mp;
        for (auto p : employees) {
            if (p == nullptr) continue;
            mp[p->id] = p;
        }

        return dfs(mp, id);
    }
};

27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

代码:

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

1071. 字符串的最大公因子

对于字符串 S 和 T,只有在 S = T + … + T(T 自身连接 1 次或多次)时,我们才认定 “T 能除尽 S”。
返回最长字符串 X,要求满足 X 能除尽 str1 且 X 能除尽 str2。

示例1:

输入:str1 = “ABCABC”, str2 = “ABC”
输出:“ABC”

示例2:

输入:str1 = “ABABAB”, str2 = “ABAB”
输出:“AB”

示例3:

输入:str1 = “LEET”, str2 = “CODE”
输出:""

代码:

class Solution {
public:
    bool match(string t,string s)   //t重复几遍是否等于s
    {
        int lenX = s.length()/t.length();
        string ans;
        for(int i = 0;i<lenX;i++)
        {
            ans+=t;
        }
        return ans==s;
    }


    string gcdOfStrings(string str1, string str2) {
        int len1 = str1.length(),len2 = str2.length();
        for(int i = min(len1,len2);i>=1;i--)
        {
            if(len1%i==0&&len2%i==0)  //如果i是len1,len2的公约数
            {
                string t = str1.substr(0,i);
                if(match(t,str1)&&match(t,str2)) return t;
            }
        }
        return "";
    }
};

剑指 Offer 55 - II. 平衡二叉树

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

示例1:

给定二叉树 [3,9,20,null,null,15,7]
            3
          /   \
        9   20
            /     \
        15       7
返回 true 。

示例2:

给定二叉树 [1,2,2,3,3,null,null,4,4]
              1
            /    \
         2       2
       /    \
     3      3
    /   \
  4     4
返回 false 。

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isBalanced(TreeNode* root) {
        if(!root) return true;
        if(abs(getHeight(root->left)-getHeight(root->right))>1) return false;
        return isBalanced(root->left)&&isBalanced(root->right);
    }

    int getHeight(TreeNode* root)
    {
        if(!root) return 0;
        int left = getHeight(root->left);
        int right = getHeight(root->right);
        return max(left,right)+1;
    }
};

剑指 Offer 28. 对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
                1
              /    \
            2     2
          /  \   /   \
       3    4 4    3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
              1
            /    \
         2       2
          \         \
            3       3

示例1:

输入:root = [1,2,2,3,4,4,3]
输出:true

示例2:

输入:root = [1,2,2,null,3,null,3]
输出:false

代码:

class Solution {
public:
    bool compare(TreeNode* left, TreeNode* right) {
        // 首先排除空节点的情况
        if (left == NULL && right != NULL) return false;
        else if (left != NULL && right == NULL) return false;
        else if (left == NULL && right == NULL) return true;
        // 排除了空节点,再排除数值不相同的情况
        else if (left->val != right->val) return false;

        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
        bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
        bool isSame = outside && inside;                    // 左子树:中、 右子树:中 (逻辑处理)
        return isSame;

    }
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        return compare(root->left, root->right);
    }
};

1089. 复写零

给你一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。
注意:请不要在超过该数组长度的位置写入元素。
要求:请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

示例1:

输入:[1,0,2,3,0,4,5,0]
输出:null
解释:调用函数后,输入的数组将被修改为:[1,0,0,2,3,0,0,4]

示例2:

输入:[1,2,3]
输出:null
解释:调用函数后,输入的数组将被修改为:[1,2,3]

代码:

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int len = arr.size();

        /*遍历数据*/
        for (int i = 0; i < len - 1; i++) {
            /*非零时继续扫描*/
            if (arr[i] != 0) continue;

            /*元素为0时,对后面的元素进行搬移*/
            for (int j = len - 2; j > i; j--) {
                arr[j+1] = arr[j];
            }
            
            /*复写0*/
            arr[++i] = 0;
        }
    }
};

455. 分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

示例1:

输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

示例2:

输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

代码:

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

121. 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if (n == 0) return 0; // 边界条件
        int minprice = prices[0];
        vector<int> dp (n, 0);

        for (int i = 1; i < n; i++){
            minprice = min(minprice, prices[i]);
            dp[i] = max(dp[i - 1], prices[i] - minprice);
        }
        return dp[n - 1];
    }
};

704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

代码:

// 常规写法 2
class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.size() == 0)
            return -1;
        int l = 0, r = nums.size() - 1;
        while(l <= r){
            int m = (l + r) / 2;
            if(nums[m] == target)  
                return m;                                                                      
            else if(nums[m] > target)
                r = m - 1;
            else
                l = m + 1;
        }
        return -1;
    }
};

606. 根据二叉树创建字符串

你需要采用前序遍历的方式,将一个二叉树转换成一个由括号和整数组成的字符串。
空节点则用一对空括号 “()” 表示。而且你需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。

示例1:

输入: 二叉树: [1,2,3,4]
           1
         /   \
       2     3
      /
    4
输出: “1(2(4))(3)”
解释: 原本将是“1(2(4)())(3())”,
在你省略所有不必要的空括号对之后,
它将是“1(2(4))(3)”。

示例2:

输入: 二叉树: [1,2,3,null,4]
                1
              /     \
            2       3
              \
                4

输出: “1(2()(4))(3)”

解释: 和第一个示例相似, 除了我们不能省略第一个对括号来中断输入和输出之间的一对一映射关系。

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    string tree2str(TreeNode* t) {
        if(!t) return "";   //如果节点为空
        else if(!t->left && !t->right)  return to_string(t->val);  //如果为叶节点
        else if(!t->right) return to_string(t->val)+"("+tree2str(t->left)+")";  //如果右节点为空
        return to_string(t->val)+"("+tree2str(t->left)+")"+"("+tree2str(t->right)+")";
    }
};

面试题 08.10. 颜色填充

编写函数,实现许多图片编辑软件都支持的「颜色填充」功能。
待填充的图像用二维数组 image 表示,元素为初始颜色值。初始坐标点的行坐标为 sr 列坐标为 sc。需要填充的新颜色为 newColor 。
「周围区域」是指颜色相同且在上、下、左、右四个方向上存在相连情况的若干元素。
请用新颜色填充初始坐标点的周围区域,并返回填充后的图像。

示例:

输入:
image = [[1,1,1],[1,1,0],[1,0,1]]
sr = 1, sc = 1, newColor = 2
输出:[[2,2,2],[2,2,0],[2,0,1]]
解释:
初始坐标点位于图像的正中间,坐标 (sr,sc)=(1,1) 。
初始坐标点周围区域上所有符合条件的像素点的颜色都被更改成 2 。
注意,右下角的像素没有更改为 2 ,因为它不属于初始坐标点的周围区域。

代码:

class Solution {
public:
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
        if(image[sr][sc]==newColor) return image;
        int old = image[sr][sc];
        image[sr][sc] = newColor;
        digui(image,sr-1,sc,newColor,old);
        digui(image,sr,sc-1,newColor,old);
        digui(image,sr+1,sc,newColor,old);
        digui(image,sr,sc+1,newColor,old);
        return image;
    }

    void digui(vector<vector<int>>& image, int sr, int sc, int newColor,int old)
    {
        if(sr>=0&&sc>=0&&sr<image.size()&&sc<image[0].size()&&image[sr][sc]==old)
        {
            image[sr][sc] = newColor;
            digui(image,sr-1,sc,newColor,old);
            digui(image,sr,sc-1,newColor,old);
            digui(image,sr+1,sc,newColor,old);
            digui(image,sr,sc+1,newColor,old);
        }
    }


};

661. 图片平滑器

包含整数的二维矩阵 M 表示一个图片的灰度。你需要设计一个平滑器来让每一个单元的灰度成为平均灰度 (向下舍入) ,平均灰度的计算是周围的8个单元和它本身的值求平均,如果周围的单元格不足八个,则尽可能多的利用它们。

示例1:

输入:
[[1,1,1],
[1,0,1],
[1,1,1]]
输出:
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
解释:
对于点 (0,0), (0,2), (2,0), (2,2): 平均(3/4) = 平均(0.75) = 0
对于点 (0,1), (1,0), (1,2), (2,1): 平均(5/6) = 平均(0.83333333) = 0
对于点 (1,1): 平均(8/9) = 平均(0.88888889) = 0

代码:

class Solution {
public:
    vector<vector<int>> imageSmoother(vector<vector<int>>& M) {
        int r=M.size(),c=M.back().size();
        vector<vector<int>> res(r,vector<int>(c,-1));
        
        for(int i=0;i<r*c;i++){
            int cur_r=i/c;
            int cur_c=i%c;
            int x1=cur_c,x2=cur_c,y1=cur_r,y2=cur_r,sum=0;
            if(cur_c-1>=0)x1=cur_c-1;//左限
            if(cur_c+1<c)x2=cur_c+1;//右限
            if(cur_r-1>=0)y1=cur_r-1;//上限
            if(cur_r+1<r)y2=cur_r+1;//下限
            for(int i=y1;i<=y2;i++){//遍历求和
                for(int j=x1;j<=x2;j++){
                    sum+=M[i][j];
                }
            }
            int count=(y2-y1+1)*(x2-x1+1);
            res[cur_r][cur_c]=sum/count;
        }
        return res;
    }
};

409. 最长回文串

给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 “Aa” 不能当做一个回文字符串。

示例1:

输入:
“abccccdd”
输出:
7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。

代码:

class Solution {
public:
    int longestPalindrome(string s) {
        unordered_map<char, int> count;
        int ans = 0;
        for (char c : s)
            ++count[c];
        for (auto p : count) {
            int v = p.second;
            ans += v / 2 * 2;
            if (v % 2 == 1 && ans % 2 == 0)
                ++ans;
        }
        return ans;
    }
};

1154. 一年中的第几天

给你一个按 YYYY-MM-DD 格式表示日期的字符串 date,请你计算并返回该日期是当年的第几天。
通常情况下,我们认为 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 2 天,依此类推。每个月的天数与现行公元纪年法(格里高利历)一致。

示例1:

输入:date = “2019-01-09”
输出:9

示例2:

输入:date = “2019-02-10”
输出:41

示例3:

输入:date = “2003-03-01”
输出:60

代码:

class Solution {
public:
    int dayOfYear(string date)
    {
        vector<string> ymd = split(date);
        int y = stoi(ymd[0]), m = stoi(ymd[1]), d = stoi(ymd[2]);
        int months[] = {31, 28, 31 ,30, 31, 30, 31, 31, 30, 31, 30, 31};
        if (isLeap(y)) months[1] = 29;
        int sum = d;
        for (int i = 0; i < m - 1; i++)
            sum += months[i];
        return sum;
    }
    bool isLeap(int y)   //判断是否为闰年  四年为一闰,百年不闰,四百年再闰
    {
        if (y % 4 == 0 && y % 100 !=0) return 1;
        if (y % 400 == 0) return 1;
        return 0;
    }
    vector<string> split(string str)   //切分-
    {
        vector<string> ans;
        int start = 0;
        for(int i = 0;i<str.length();i++)
        {
            if(str[i]=='-')
            {
                ans.push_back(str.substr(start,i-start));
                start = i+1;
            }
        }
        ans.push_back(str.substr(start,str.length()-start));
        return ans;
    }
};

面试题 10.05. 稀疏数组搜索

稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。

示例1:

输入: words = [“at”, “”, “”, “”, “ball”, “”, “”, “car”, “”, “”,“dad”, “”, “”], s = “ta”
输出:-1
说明: 不存在返回-1。

示例2:

输入:words = [“at”, “”, “”, “”, “ball”, “”, “”, “car”, “”, “”,“dad”, “”, “”], s = “ball”
输出:4

代码:

class Solution {
public:
	int findString(vector<string>& words, string s) {
		int left = 0, right = words.size() - 1;
		while (left <= right) {
			if (words[left].size() == 0) { //如果left为空,一直往右
				left++;
				continue;
			}
			if (words[right].size() == 0) { //如果右为空,一直往左
				right--;
				continue;
			}
			int mid = (right + left) / 2;
			while (words[mid].size() == 0) {  //如果中心为空 一直往右
				mid++;
				if (mid == right) {  //如果往右碰到右边界了
					right = (right + left) / 2;
					continue;
				}
			}

			if (words[mid] == s)
				return mid;
			else if (words[mid] > s) {
				right = mid - 1;
			}
			else {
				left = mid + 1;
			}
		}
		return -1;
	}
};

面试题 01.09. 字符串轮转

字符串轮转。给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成(比如,waterbottle是erbottlewat旋转后的字符串)。

示例1:

输入:s1 = “waterbottle”, s2 = “erbottlewat”
输出:True

示例2:

输入:s1 = “aa”, s2 = “aba”
输出:False

代码:

class Solution {
public:
    bool isFlipedString(string s1, string s2) {
        return s1.size()==s2.size()&&(s1+s1).find(s2)!=-1;
    }
};

746. 使用最小花费爬楼梯

数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。
每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。
请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。

示例1:

输入:cost = [10, 15, 20]
输出:15
解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。

示例2:

输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出:6
解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,一共花费 6 。

代码:

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n = cost.size();
        vector<int> dp(n + 1);
        dp[0] = dp[1] = 0;
        for (int i = 2; i <= n; i++) {
            dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
        }
        return dp[n];
    }
};

1128. 等价多米诺骨牌对的数量

给你一个由一些多米诺骨牌组成的列表 dominoes。
如果其中某一张多米诺骨牌可以通过旋转 0 度或 180 度得到另一张多米诺骨牌,我们就认为这两张牌是等价的。
形式上,dominoes[i] = [a, b] 和 dominoes[j] = [c, d] 等价的前提是 a == c 且 b == d,或是 a == d 且 b == c。
在 0 <= i < j < dominoes.length 的前提下,找出满足 dominoes[i] 和 dominoes[j] 等价的骨牌对 (i, j) 的数量。

示例:

输入:dominoes = [[1,2],[2,1],[3,4],[5,6]]
输出:1

代码:

class Solution {
public:
    int numEquivDominoPairs(vector<vector<int>>& dominoes) {
        vector<int> num(100);
        int ret = 0;
        for (auto& it : dominoes) {
            int val = it[0] < it[1] ? it[0] * 10 + it[1] : it[1] * 10 + it[0];
            ret += num[val];
            num[val]++;
        }
        return ret;
    }
};

67. 二进制求和

给你两个二进制字符串,返回它们的和(用二进制表示)。
输入为 非空 字符串且只包含数字 1 和 0。

示例1:

输入: a = “11”, b = “1”
输出: “100”

示例2:

输入: a = “1010”, b = “1011”
输出: “10101”

代码:

class Solution {
public:
    string addBinary(string a, string b) {
        int al = a.size();
        int bl = b.size();
        while(al < bl) //让两个字符串等长,若不等长,在短的字符串前补零,否则之后的操作会超出索引
        {
            a = '0' + a;
            ++ al;
        }
        while(al > bl)
        {
            b = '0' + b;
            ++ bl;
        }
        for(int j = a.size() - 1; j > 0; -- j) //从后到前遍历所有的位数,同位相加
        {
            a[j] = a[j] - '0' + b[j];
            if(a[j] >=  '2') //若大于等于字符‘2’,需要进一
            {
                a[j] = (a[j] - '0') % 2 + '0';
                a[j-1] = a[j-1] + 1;
            }
        }
        a[0] = a[0] - '0' + b[0]; //将ab的第0位相加
        if(a[0] >= '2') //若大于等于2,需要进一
        {
            a[0] = (a[0] - '0') % 2 + '0';
            a = '1' + a;
        }
        return a;
    }
};

1496. 判断路径是否相交

给你一个字符串 path,其中 path[i] 的值可以是 ‘N’、‘S’、‘E’ 或者 ‘W’,分别表示向北、向南、向东、向西移动一个单位。
机器人从二维平面上的原点 (0, 0) 处开始出发,按 path 所指示的路径行走。
如果路径在任何位置上出现相交的情况,也就是走到之前已经走过的位置,请返回 True ;否则,返回 False 。

示例1:
在这里插入图片描述

输入:path = “NES”
输出:false
解释:该路径没有在任何位置相交。

示例2:
在这里插入图片描述

输入:path = “NESWW”
输出:true
解释:该路径经过原点两次。

代码:

class Solution {
public:
    bool isPathCrossing(string path) {
        set<pair<int,int>> s;
        int x=0,y=0;
        s.insert({x,y});
        for(char c:path)
        {
            if(c=='N') y++;
            else if(c=='S') y--;
            else if(c=='W') x--;
            else if(c=='E') x++;
            if(s.count({x,y})) return true;
            s.insert({x,y});
        }
        return false;
    }
};

1137. 第 N 个泰波那契数

泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

示例:

输入:n = 4
输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4

示例2:

输入:n = 25
输出:1389537

代码:

class Solution {
public:
        int tribonacci(int n) {
        if (n <= 1) return n;
        if (n == 2) return 1;
        vector<int> dp(n + 1);
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 1;
        for (int i = 3; i <= n; ++i)
        {
            dp.at(i) = dp.at(i - 1) + dp.at(i - 2) + dp.at(i - 3);
        }

        return dp[n];
    }
};

1422. 分割字符串的最大得分

给你一个由若干 0 和 1 组成的字符串 s ,请你计算并返回将该字符串分割成两个 非空 子字符串(即 左 子字符串和 右 子字符串)所能获得的最大得分。
「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1 的数量。

示例1:

输入:s = “011101”
输出:5
解释:
将字符串 s 划分为两个非空子字符串的可行方案有:
左子字符串 = “0” 且 右子字符串 = “11101”,得分 = 1 + 4 = 5
左子字符串 = “01” 且 右子字符串 = “1101”,得分 = 1 + 3 = 4
左子字符串 = “011” 且 右子字符串 = “101”,得分 = 1 + 2 = 3
左子字符串 = “0111” 且 右子字符串 = “01”,得分 = 1 + 1 = 2
左子字符串 = “01110” 且 右子字符串 = “1”,得分 = 2 + 1 = 3

示例2:

输入:s = “00111”
输出:5
解释:当 左子字符串 = “00” 且 右子字符串 = “111” 时,我们得到最大得分 = 2 + 3 = 5

示例3:

输入:s = “1111”
输出:3

代码:

class Solution {
public:
    int maxScore(string s) {
        int right = count(s.begin(),s.end(),'1'),left = 0,ma = 0;
        for(int i = 0; i < s.size()-1; i++){
            if(s[i] == '0') ++left;
            else --right;
            ma = max(ma,left+right);
        }
        return ma;
    }
};

面试题 03.01. 三合一

三合一。描述如何只用一个数组来实现三个栈。
你应该实现push(stackNum, value)、pop(stackNum)、isEmpty(stackNum)、peek(stackNum)方法。stackNum表示栈下标,value表示压入的值。
构造函数会传入一个stackSize参数,代表每个栈的大小。

示例1:

输入:
[“TripleInOne”, “push”, “push”, “pop”, “pop”, “pop”, “isEmpty”]
[[1], [0, 1], [0, 2], [0], [0], [0], [0]]
输出:
[null, null, null, 1, -1, -1, true]
说明:当栈为空时pop, peek返回-1,当栈满时push不压入元素。

示例2:

输入:
[“TripleInOne”, “push”, “push”, “push”, “pop”, “pop”, “pop”, “peek”]
[[2], [0, 1], [0, 2], [0, 3], [0], [0], [0], [0]]
输出:
[null, null, null, null, 2, 1, -1, -1]

代码:

class TripleInOne {
private:
    int *stack;   //存放3合一元素
    int top[3];   //存放三个栈顶
    int stackSize;
public:
    
    TripleInOne(int stackSize):stackSize(stackSize) {
        stack = new int[stackSize*3];
        top[0]=top[1]=top[2]=0;
    }
    
    void push(int stackNum, int value) {
        if(top[stackNum]<stackSize)  //如果在栈内
        {
            stack[stackNum*stackSize+top[stackNum]++]=value;
        }
    }
    
    int pop(int stackNum) {
        if(top[stackNum]<=0) return -1;
        else return stack[stackNum*stackSize+(--top[stackNum])];
    }
    
    int peek(int stackNum) {
        if(top[stackNum]<=0) return -1;
        else return stack[stackNum*stackSize+(top[stackNum]-1)];
    }
    
    bool isEmpty(int stackNum) {
        return top[stackNum]==0;
    }
};

/**
 * Your TripleInOne object will be instantiated and called as such:
 * TripleInOne* obj = new TripleInOne(stackSize);
 * obj->push(stackNum,value);
 * int param_2 = obj->pop(stackNum);
 * int param_3 = obj->peek(stackNum);
 * bool param_4 = obj->isEmpty(stackNum);
 */

492. 构造矩形

作为一位web开发者, 懂得怎样去规划一个页面的尺寸是很重要的。 现给定一个具体的矩形页面面积,你的任务是设计一个长度为 L 和宽度为 W 且满足以下要求的矩形的页面。要求:

  1. 你设计的矩形页面必须等于给定的目标面积。

  2. 宽度 W 不应大于长度 L,换言之,要求 L >= W 。

  3. 长度 L 和宽度 W 之间的差距应当尽可能小。

你需要按顺序输出你设计的页面的长度 L 和宽度 W。

示例:

输入: 4
输出: [2, 2]
解释: 目标面积是 4, 所有可能的构造方案有 [1,4], [2,2], [4,1]。
但是根据要求2,[1,4] 不符合要求; 根据要求3,[2,2] 比 [4,1] 更能符合要求. 所以输出长度 L 为 2, 宽度 W 为 2。

代码:

class Solution {
public:
    vector<int> constructRectangle(int area) {
        int W = sqrt(area);
        while (area % W != 0) --W;
        return {area / W, W};
    }
};

1275. 找出井字棋的获胜者

A 和 B 在一个 3 x 3 的网格上玩井字棋。
井字棋游戏的规则如下:
玩家轮流将棋子放在空方格 (" ") 上。
第一个玩家 A 总是用 “X” 作为棋子,而第二个玩家 B 总是用 “O” 作为棋子。
“X” 和 “O” 只能放在空方格中,而不能放在已经被占用的方格上。
只要有 3 个相同的(非空)棋子排成一条直线(行、列、对角线)时,游戏结束。
如果所有方块都放满棋子(不为空),游戏也会结束。
游戏结束后,棋子无法再进行任何移动。
给你一个数组 moves,其中每个元素是大小为 2 的另一个数组(元素分别对应网格的行和列),它按照 A 和 B 的行动顺序(先 A 后 B)记录了两人各自的棋子位置。
如果游戏存在获胜者(A 或 B),就返回该游戏的获胜者;如果游戏以平局结束,则返回 “Draw”;如果仍会有行动(游戏未结束),则返回 “Pending”。
你可以假设 moves 都 有效(遵循井字棋规则),网格最初是空的,A 将先行动。

示例1:

输入:moves = [[0,0],[2,0],[1,1],[2,1],[2,2]]
输出:“A”
解释:“A” 获胜,他总是先走。
"X " "X " "X " "X " "X "
" " -> " " -> " X " -> " X " -> " X "
" " "O " "O " "OO " “OOX”

示例2:

输入:moves = [[0,0],[1,1],[0,1],[0,2],[1,0],[2,0]]
输出:“B”
解释:“B” 获胜。
"X " "X " "XX " “XXO” “XXO” “XXO”
" " -> " O " -> " O " -> " O " -> "XO " -> "XO "
" " " " " " " " " " "O "

示例3:

输入:moves = [[0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2]]
输出:“Draw”
输出:由于没有办法再行动,游戏以平局结束。
“XXO”
“OOX”
“XOX”

示例4:

输入:moves = [[0,0],[1,1]]
输出:“Pending”
解释:游戏还没有结束。
"X "
" O "
" "

代码:

class Solution {
public:
    string tictactoe(vector<vector<int>>& moves) {
        vector<vector<int>> wins = {
            {0,1,2},
            {3,4,5},
            {6,7,8},
            {0,3,6},
            {1,4,7},
            {2,5,8},
            {0,4,8},
            {2,4,6}
        };
        set<int> a,b;
        for(int i = 0;i<moves.size();i++)
        {
            int temp = moves[i][0]*3+moves[i][1];
            if(i%2==0) //如果是A
            {
                a.insert(temp);
                if(checkwin(a,wins)) return "A";
            }else
            {
                b.insert(temp);
                if(checkwin(b,wins)) return "B";
            }
        }
        return moves.size()==9?"Draw":"Pending";
    }

    bool checkwin(set<int> &s,vector<vector<int>> wins)
    {
        for(vector<int> win:wins)
        {
            bool flag = true;
            for(int i:win)
            {
                if(!s.count(i))
                {
                    flag = false;
                    break;
                }
            }
            if(flag)
            {
                return true;
            }
        }
        return false;
    }
};

401. 二进制手表

二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。
每个 LED 代表一个 0 或 1,最低位在右侧。

在这里插入图片描述

例如,上面的二进制手表读取 “3:25”。
给定一个非负整数 n 代表当前 LED 亮着的数量,返回所有可能的时间。

示例:

输入: n = 1
返回: [“1:00”, “2:00”, “4:00”, “8:00”, “0:01”, “0:02”, “0:04”, “0:08”, “0:16”, “0:32”]

代码:

class Solution {
public:
    vector<string> readBinaryWatch(int num) {
        vector<string> res;
        //直接遍历  0:00 -> 12:00   每个时间有多少1
        for (int i = 0; i < 12; i++) {
            for (int j = 0; j < 60; j++) {
                if (count1(i) + count1(j) == num) {
                    res.push_back(to_string(i)+":"+
                                  (j < 10 ? "0"+to_string(j) : to_string(j)));
                }
            }
        }
        return res;
    }
    //计算二进制中1的个数
    int count1(int n) {
        int res = 0;
        while (n) {
            n = n & (n - 1);
            res++;
        }
        return res;
    }
};

543. 二叉树的直径

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

示例:

给定二叉树
               1
             /    \
           2      3
         /   \
       4     5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
注意:两结点之间的路径长度是以它们之间边的数目表示。

代码:

/**
 * 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 {
    int maxd=0;
public:
    int diameterOfBinaryTree(TreeNode* root) {
        depth(root);
        return maxd;
    }
    
    int depth(TreeNode* root){
        if(!root){
            return 0;
        }
        int Left = depth(root->left);
        int Right = depth(root->right);
        maxd=max(Left+Right,maxd);//将每个节点最大直径(左子树深度+右子树深度)当前最大值比较并取大者
        return max(Left,Right)+1;//返回节点深度
    }
};

面试题 17.16. 按摩师

一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。

示例1:

输入: [1,2,3,1]
输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。

示例2:

输入: [2,7,9,3,1]
输出: 12
解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。

示例3:

输入: [2,1,4,5,3,1,1,3]
输出: 12
解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。

思路:

定义dp[i][0] 表示第 i 个预约不接的最长预约时间,dp[i][1] 表示第 i 个预约接的最长预约时间。


首先考虑dp[i][0] 的转移方程,由于这个状态下第 i 个预约是不接的,所以第 i-1个预约接或不接都可以,故可以从dp[i−1][0] 和 dp[i−1][1] 两个状态转移过来,转移方程即为:
dp[i][0]=max(dp[i−1][0],dp[i−1][1])


对于dp[i][1] ,由于这个状态下第 i 个预约要接,根据题目要求按摩师不能接受相邻的预约,所以第 i-1个预约不能接受,故我们只能从dp[i−1][0] 这个状态转移过来,转移方程即为:
dp[i][1]=dp[i−1][0]+nums i


最后答案即为max(dp[n][0],dp[n][1]) ,其中 n 表示预约的个数。
再回来看转移方程,我们发现计算dp[i][0/1] 时,只与前一个状态dp[i−1][0/1] 有关,所以我们可以不用开数组,只用两个变量 dp 0 ,dp 1
分别存储dp[i−1][0] 和 dp[i−1][1] 的答案,然后去转移更新答案即可。

代码:

class Solution {
public:
    int massage(vector<int>& nums) {
        int n = (int)nums.size();
        if (!n) {
            return 0;
        }
        int dp0 = 0, dp1 = nums[0];   //0表示不接  1表示接

        for (int i = 1; i < n; ++i){
            int tdp0 = max(dp0, dp1); // 计算 dp[i][0]
            int tdp1 = dp0 + nums[i]; // 计算 dp[i][1]

            dp0 = tdp0; // 用 dp[i][0] 更新 dp_0
            dp1 = tdp1; // 用 dp[i][1] 更新 dp_1
        }
        return max(dp0, dp1);
    }
};

628. 三个数的最大乘积

给你一个整型数组 nums ,在数组中找出由三个数组成的最大乘积,并输出这个乘积。

示例1:

输入:nums = [1,2,3]
输出:6

示例2:

输入:nums = [1,2,3,4]
输出:24

示例3:

输入:nums = [-1,-2,-3]
输出:-6

代码:

class Solution {
public:
    int maximumProduct(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        return max(nums[0] * nums[1] * nums[n - 1], nums[n - 3] * nums[n - 2] * nums[n - 1]);
    }
};

112. 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。
叶子节点 是指没有子节点的节点。

示例1:
在这里插入图片描述

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true

示例2:
在这里插入图片描述

输入:root = [1,2,3], targetSum = 5
输出:false

示例3:

输入:root = [1,2], targetSum = 0
输出:false

代码:

class Solution {
public:
    bool hasPathSum(TreeNode *root, int sum) {
        if (root == nullptr) {
            return false;
        }
        if (root->left == nullptr && root->right == nullptr) {
            return sum == root->val;
        }
        return hasPathSum(root->left, sum - root->val) ||
               hasPathSum(root->right, sum - root->val);
    }
};

415. 字符串相加

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。

代码:

class Solution {
public:
    string addStrings(string num1, string num2) {
        int i = num1.length() - 1, j = num2.length() - 1, add = 0;
        string ans = "";
        while (i >= 0 || j >= 0 || add != 0) {
            int x = i >= 0 ? num1[i] - '0' : 0;
            int y = j >= 0 ? num2[j] - '0' : 0;
            int result = x + y + add;
            ans.push_back('0' + result % 10);
            add = result / 10;
            i -= 1;
            j -= 1;
        }
        // 计算完以后的答案需要翻转过来
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

1018. 可被 5 整除的二进制前缀

给定由若干 0 和 1 组成的数组 A。我们定义 N_i:从 A[0] 到 A[i] 的第 i 个子数组被解释为一个二进制数(从最高有效位到最低有效位)。
返回布尔值列表 answer,只有当 N_i 可以被 5 整除时,答案 answer[i] 为 true,否则为 false。

示例1:

输入:[0,1,1]
输出:[true,false,false]
解释:
输入数字为 0, 01, 011;也就是十进制中的 0, 1, 3 。只有第一个数可以被 5 整除,因此 answer[0] 为真。

示例2:

输入:[1,1,1]
输出:[false,false,false]

示例3:

输入:[0,1,1,1,1,1]
输出:[true,false,false,false,true,false]

代码:

class Solution {
public:
    vector<bool> prefixesDivBy5(vector<int>& A) {
        vector<bool> answer;
        int prefix = 0;
        int length = A.size();
        for (int i = 0; i < length; i++) {
            prefix = ((prefix << 1) + A[i]) % 5;
            answer.emplace_back(prefix == 0);
        }
        return answer;
    }
};

997. 找到小镇的法官

在一个小镇里,按从 1 到 N 标记了 N 个人。传言称,这些人中有一个是小镇上的秘密法官。
如果小镇的法官真的存在,那么:
小镇的法官不相信任何人。
每个人(除了小镇法官外)都信任小镇的法官。
只有一个人同时满足属性 1 和属性 2 。
给定数组 trust,该数组由信任对 trust[i] = [a, b] 组成,表示标记为 a 的人信任标记为 b 的人。
如果小镇存在秘密法官并且可以确定他的身份,请返回该法官的标记。否则,返回 -1。

示例1:

输入:N = 2, trust = [[1,2]]
输出:2

示例2:

输入:N = 3, trust = [[1,3],[2,3]]
输出:3

示例3:

输入:N = 3, trust = [[1,3],[2,3],[3,1]]
输出:-1

示例4:

输入:N = 3, trust = [[1,2],[2,3]]
输出:-1

示例5:

输入:N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]]
输出:3

代码:

class Solution {
public:
    int findJudge(int N, vector<vector<int>>& trust) {
        //法官入度为N-1,出度为0
        vector<int> g(N+1, 0);//入度-出度,初始值为0
        for (auto t : trust) {
            --g[t[0]];//出度增加,则和减1
            ++g[t[1]];//入度增加,则和加1
        }
        for (int i = 1; i <= N; ++i) {
            if (g[i] == N-1) {
                return i;
            }
        }
        return -1;            
    }
};

459. 重复的子字符串

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

示例1:

输入: “abab”
输出: True
解释: 可由子字符串 “ab” 重复两次构成。

示例2:

输入: “aba”
输出: False

示例3:

输入: “abcabcabcabc”
输出: True
解释: 可由子字符串 “abc” 重复四次构成。 (或者子字符串 “abcabc” 重复两次构成。)

代码:

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        return (s + s).find(s, 1) != s.size();   //破坏第一个循环节点
    }

// 如果一个字符串可以由多个重复子串构成,即具有循环节 设最小循环节用a来表示,他代表通过子串a重复多次可以构成s 即s换成a来表示就是aa···aaa,由多少个最小循环节a构成s,那么就有几个a

// 找循环节一个一个对比比较麻烦,最简单方法就是s+s就可以直接增加多一倍的循环节

// 假设原来s=aaaa,那ss=s+s=aaaa aaaa 因为是不断重复的循环节,可以通过简单的屏蔽的第一个字符,然后再在ss中寻找s 因为屏蔽第一个字符,即第一个最小循环节被破坏,所以找到的s应该是从第二个循环节开始

// 但倘若不是由一个子串重复构成 即s=abcd,那ss=abcd abcd=s+s 屏蔽掉第一个字符,又因不匹配,所以在ss中寻找s,一定是对应着新增s的位置,即s.size()处
};

141. 环形链表

给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。

示例1:
在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例2:
在这里插入图片描述

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例3:
在这里插入图片描述

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    //使用快慢指针实现
    bool hasCycle(ListNode* head) 
    {
        if (!head)  
            return false;
        ListNode* fast = head;  
        ListNode* slow = head;  
        while (fast && fast->next)
        {
            fast = fast->next->next;  
            slow = slow->next;  
            if (fast == slow)
                return true;
        }
        return false;  
    }
};

392. 判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

示例1:

输入:s = “abc”, t = “ahbgdc”
输出:true

示例2:

输入:s = “axc”, t = “ahbgdc”
输出:false

代码:

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int s_len = s.length(), t_len = t.length();
        int i = 0, j = 0;
        while (i < s_len && j < t_len) {
            if (s[i] == t[j]) {
                i++;
            }
            j++;
        }
        return i == s_len;
    }
};

703. 数据流中的第 K 大元素

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。
请实现 KthLargest 类:
KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

示例:

输入:
[“KthLargest”, “add”, “add”, “add”, “add”, “add”]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]
解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3); // return 4
kthLargest.add(5); // return 5
kthLargest.add(10); // return 5
kthLargest.add(9); // return 8
kthLargest.add(4); // return 8

代码:

class KthLargest {
    multiset<int> s;
    int K;
public:
    KthLargest(int k, vector<int>& nums) {
        for(int i:nums)
        {
            s.insert(i);
            if(s.size()>k) s.erase(s.begin());
        }
        K = k;
    }
    
    int add(int val) {
        s.insert(val);
        if(s.size()>K) s.erase(s.begin());
        return *s.begin();
    }
};

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest* obj = new KthLargest(k, nums);
 * int param_1 = obj->add(val);
 */

263. 丑数

编写一个程序判断给定的数是否为丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。

示例1:

输入: 6
输出: true
解释: 6 = 2 × 3

示例2:

输入: 8
输出: true
解释: 8 = 2 × 2 × 2

示例3:

输入: 14
输出: false
解释: 14 不是丑数,因为它包含了另外一个质因数 7。

代码:

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

205. 同构字符串

给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。
每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

示例1:

输入:s = “egg”, t = “add”
输出:true

示例2:

输入:s = “foo”, t = “bar”
输出:false

示例3:

输入:s = “paper”, t = “title”
输出:true

代码:

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        if (0 == s.size() && 0 == t.size())
        {
            return true;
        }

        for (int index = 0; index <= s.size() - 1; index++)
        {
            if (s.find(s[index]) != t.find(t[index]))
            {
                return false;
            }
        }

        return true;
    }
};

1360. 日期之间隔几天

请你编写一个程序来计算两个日期之间隔了多少天。
日期以字符串形式给出,格式为 YYYY-MM-DD,如示例所示。

示例1:

输入:date1 = “2019-06-29”, date2 = “2019-06-30”
输出:1

示例2:

输入:date1 = “2020-01-15”, date2 = “2019-12-31”
输出:15

代码:

class Solution {
public:
    int toDay(const string& dateStr) {
        int year, month, day;
        sscanf(dateStr.c_str(), "%d-%d-%d", &year, &month, &day);
        if (month <= 2) {
            year--;
            month += 10;
        }
        else month -= 2;
        return 365 * year + year / 4 - year / 100 + year / 400
             + 30 * month + (3 * month - 1) / 5 + day /* -584418 */;
    }
    int daysBetweenDates(string date1, string date2) {
        return abs(toDay(date1) - toDay(date2));
    }
};

720. 词典中最长的单词

给出一个字符串数组words组成的一本英语词典。从中找出最长的一个单词,该单词是由words词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。
若无答案,则返回空字符串。

示例1:

输入:
words = [“w”,“wo”,“wor”,“worl”, “world”]
输出:“world”
解释:
单词"world"可由"w", “wo”, “wor”, 和 "worl"添加一个字母组成。

示例2:

输入:
words = [“a”, “banana”, “app”, “appl”, “ap”, “apply”, “apple”]
输出:“apple”
解释:
“apply"和"apple"都能由词典中的单词组成。但是"apple"的字典序小于"apply”。

代码:

//使用字典树

class Trie{
private:
    bool is_string;
    Trie *next[26];
public:
    Trie(){
        is_string=false;
        memset(next,0,sizeof(next));
    }
    
    void insert(string word){
        Trie *root=this;
        for(const auto& w:word){
            if(root->next[w-'a']==nullptr)root->next[w-'a']=new Trie();
            root=root->next[w-'a'];
        }
        root->is_string=true;
    }
    
    bool search(string word){
        Trie *root=this;
        for(const auto& w:word){
            //当节点值存在时,判断该节点是否表示为一个字符串,不是的话,直接返回false,否则继续循环;当节点值不存在时直接返回false
            if(root->next[w-'a']==nullptr||root->next[w-'a']->is_string==false)return false;
            root=root->next[w-'a'];
        }
        return true;
    }
};
class Solution {
public:
    string longestWord(vector<string>& words) {
        if(words.size()==0)return "";
        Trie* root=new Trie();
        //第一次遍历,建立前缀树
        for(const auto& word:words)
            root->insert(word);
        string result="";
        //第二次遍历,寻找最长单词
        for(const auto& word:words){
            if(root->search(word))
            {
                if(word.size()>result.size())result=word;//更新最长单词
                else if(word.size()==result.size()&&word<result)result=word;//长度相等的单词,取字典序小的单词
            }    
        }
        return result;
    }
};


836. 矩形重叠

矩形以列表 [x1, y1, x2, y2] 的形式表示,其中 (x1, y1) 为左下角的坐标,(x2, y2) 是右上角的坐标。矩形的上下边平行于 x 轴,左右边平行于 y 轴。
如果相交的面积为 正 ,则称两矩形重叠。需要明确的是,只在角或边接触的两个矩形不构成重叠。
给出两个矩形 rec1 和 rec2 。如果它们重叠,返回 true;否则,返回 false 。

示例1:

输入:rec1 = [0,0,2,2], rec2 = [1,1,3,3]
输出:true

示例2:

输入:rec1 = [0,0,1,1], rec2 = [1,0,2,1]
输出:false

示例3:

输入:rec1 = [0,0,1,1], rec2 = [2,2,3,3]
输出:false

代码:

class Solution {
public:
    bool isRectangleOverlap(vector<int>& rec1, vector<int>& rec2) {
        if(rec1[0]==rec1[2]||rec1[1]==rec1[3]||rec2[0]==rec2[2]||rec2[1]==rec2[3])return false;
        bool x_overlap = !(rec1[2] <= rec2[0] || rec2[2] <= rec1[0]);
        bool y_overlap = !(rec1[3] <= rec2[1] || rec2[3] <= rec1[1]);
        return x_overlap && y_overlap;
    }       
};

989. 数组形式的整数加法

对于非负整数 X 而言,X 的数组形式是每位数字按从左到右的顺序形成的数组。例如,如果 X = 1231,那么其数组形式为 [1,2,3,1]。
给定非负整数 X 的数组形式 A,返回整数 X+K 的数组形式。

示例1:

输入:A = [1,2,0,0], K = 34
输出:[1,2,3,4]
解释:1200 + 34 = 1234

示例2:

输入:A = [2,7,4], K = 181
输出:[4,5,5]
解释:274 + 181 = 455

示例3:

输入:A = [2,1,5], K = 806
输出:[1,0,2,1]
解释:215 + 806 = 1021

示例4:

输入:A = [9,9,9,9,9,9,9,9,9,9], K = 1
输出:[1,0,0,0,0,0,0,0,0,0,0]
解释:9999999999 + 1 = 10000000000

代码:

class Solution {
public:
    vector<int> addToArrayForm(vector<int> &A, int K) {
        vector<int> res;
        int n = A.size();
        for (int i = n - 1; i >= 0; --i) {
            int sum = A[i] + K % 10;
            K /= 10;
            if (sum >= 10) {
                K++;
                sum -= 10;
            }
            res.push_back(sum);
        }
        for (; K > 0; K /= 10) {
            res.push_back(K % 10);
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

面试题 01.06. 字符串压缩

字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串aabcccccaaa会变为a2b1c5a3。若“压缩”后的字符串没有变短,则返回原先的字符串。你可以假设字符串中只包含大小写英文字母(a至z)。

示例1:

输入:“aabcccccaaa”
输出:“a2b1c5a3”

示例2:

输入:“abbccd”
输出:“abbccd”
解释:“abbccd"压缩后为"a1b2c2d1”,比原字符串长度更长。

代码:

class Solution {
public:
    string compressString(string S) {
        string ans;
        int len = S.length();
        int i = 0;
        while(i<len)
        {
            int j = i;
            while(j<len&&S[i]==S[j])
            {
                j++;
            }
            ans+=S[i];
            ans+=to_string(j-i);
            i = j;
        }

        if(ans.length()<S.length()) return ans;
        else return S;
    }
};

572. 另一个树的子树

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

示例1:

给定的树 s:
         3
        /   \
      4     5
     /   \
   1     2
给定的树 t:
        4
       /   \
     1     2
返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。

代码:

class Solution {
public:
    bool check(TreeNode *o, TreeNode *t) {   //判断树o与树t是否相等
        if (!o && !t) {
            return true;
        }
        if ((o && !t) || (!o && t) || (o->val != t->val)) {
            return false;
        }
        return check(o->left, t->left) && check(o->right, t->right);
    }

    bool dfs(TreeNode *o, TreeNode *t) {
        if (!o) {
            return false;
        }
        return check(o, t) || dfs(o->left, t) || dfs(o->right, t);
    }

    bool isSubtree(TreeNode *s, TreeNode *t) {
        return dfs(s, t);  //深度遍历s树的每个节点
    }
};

面试题 16.05. 阶乘尾数

设计一个算法,算出 n 阶乘有多少个尾随零。

示例1:

输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。

示例2:

输入: 5
输出: 1
解释: 5! = 120, 尾数中有 1 个零.

代码:

class Solution {
public:
    int trailingZeroes(int n) {
        int sum = 0;
        while (n >= 5) {
    	    n /= 5;
    	    sum += n;
        }
        return sum;
    }
};

66. 加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。

示例1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。

示例2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。

示例3:

输入:digits = [0]
输出:[1]

代码:

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        for(int i = digits.size()-1;i>=0;i--)
        {
            digits[i]++;
            if(digits[i]==10) digits[i]=0;
            else return digits;
        }
        //如果以上步骤执行完了还没有return 则表示数字是999。。的形式,需要进位
        digits.insert(digits.begin(),1);
        return digits;
    }
};

剑指 Offer 61. 扑克牌中的顺子

从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

示例1:

输入: [1,2,3,4,5]
输出: True

示例2:

输入: [0,0,1,2,5]
输出: True

代码:

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        bool m[15] = {false};  //15张牌 A-10 JQK大小王
        int minValue = 14,maxValue = 0;
        for(int num:nums)
        {
            if(num==0)
                continue;
            if(m[num]) //如果num牌已经有了,则重复返回false
                return false;
            m[num] = true;
            minValue = min(minValue,num);
            maxValue = max(maxValue,num);
        }
        return maxValue-minValue+1<=5;
    }
};

1796. 字符串中第二大的数字

给你一个混合字符串 s ,请你返回 s 中 第二大 的数字,如果不存在第二大的数字,请你返回 -1 。
混合字符串 由小写英文字母和数字组成。

示例1:

输入:s = “dfa12321afd”
输出:2
解释:出现在 s 中的数字包括 [1, 2, 3] 。第二大的数字是 2 。

示例2:

输入:s = “abc1111”
输出:-1
解释:出现在 s 中的数字只包含 [1] 。没有第二大的数字。

代码:

class Solution 
{
public:
    int secondHighest(string s) 
    {   
        set<int> a;   //建立集合set,只导入数字
        for (auto c: s)  //导入  因为集合是默认排序的
        {
            if (isdigit(c))  //如果c是数字
                a.insert(c - '0');  //则导入数字
        }
        if (a.size() < 2)  
            return -1;
        
        auto it = a.rbegin();   //rbegin,rend  反向迭代器
        it ++;
        return *it;
    }
};

643. 子数组最大平均数 I

给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数。

示例:

输入:[1,12,-5,-6,50,3], k = 4
输出:12.75
解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75

代码:

class Solution {
public:
    //滑动窗口
    double findMaxAverage(vector<int>& nums, int k) {
        int sum = 0;
        int n = nums.size();
        for (int i = 0; i < k; i++) {
            sum += nums[i];
        }
        int maxSum = sum;
        for (int i = k; i < n; i++) {
            sum = sum - nums[i - k] + nums[i];
            maxSum = max(maxSum, sum);
        }
        return static_cast<double>(maxSum) / k;
    }
};

1592. 重新排列单词间的空格

给你一个字符串 text ,该字符串由若干被空格包围的单词组成。每个单词由一个或者多个小写英文字母组成,并且两个单词之间至少存在一个空格。题目测试用例保证 text 至少包含一个单词 。
请你重新排列空格,使每对相邻单词之间的空格数目都 相等 ,并尽可能 最大化 该数目。如果不能重新平均分配所有空格,请 将多余的空格放置在字符串末尾 ,这也意味着返回的字符串应当与原 text 字符串的长度相等。
返回 重新排列空格后的字符串 。

示例1:

输入:text = " this is a sentence "
输出:“this is a sentence”
解释:总共有 9 个空格和 4 个单词。可以将 9 个空格平均分配到相邻单词之间,相邻单词间空格数为:9 / (4-1) = 3 个。

示例2:

输入:text = " practice makes perfect"
输出:"practice makes perfect "
解释:总共有 7 个空格和 3 个单词。7 / (3-1) = 3 个空格加上 1 个多余的空格。多余的空格需要放在字符串的末尾。

示例3:

输入:text = “hello world”
输出:“hello world”

示例4:

输入:text = " walks udp package into bar a"
输出:"walks udp package into bar a "

代码:

class Solution {
public:
    string reorderSpaces(string text) {
        vector<string> S;
        int i=0;  //当前指向的text字符
        int c=0;  //统计text中字符的数量
        while(i<text.size()){
            while(i<text.size() && text[i]==' ') ++i;
            string x;
            while(i<text.size() && text[i]!=' ') x+=text[i],++i;
            if(x.size()) S.push_back(x),c+=x.size();
        }
        string ans;
        int r=text.size()-c;  //空格的数量
        if(S.size()==1){  //如果字符串只有一个
            ans += S.front();
            ans += string(r,' ');
        }else{  //有多个字符串
            int a=r/(S.size()-1);  //平均值
            int b=r%(S.size()-1);  //剩余值
            for(int j=0;j<S.size();++j){
                if(j)  ans += string(a,' ');
                ans += S[j];
            }
            if(b)  ans += string(b,' ');
        }
        return ans;
    }
};

//思路:
// 先提取出单词,存入数组,得出单词的个数。
// 计算空格的总个数。
// 重新组合单词,在单词间填上平均空格数。多余的空格放在后面。

剑指 Offer 29. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

代码:

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if(matrix.empty()) return {};
        int directions[4][2]={{0,1},{1,0},{0,-1},{-1,0}};  //行列 →↓←↑
        int rows = matrix.size(),columns = matrix[0].size();
        vector<vector<bool>> visited(rows,vector<bool>(columns));
        int total = rows*columns;
        vector<int> ans(total);

        int row = 0,column = 0;   //定义当前指向的行列
        int directionIndex = 0;
        for(int i = 0;i<total;i++)
        {
            ans[i] = matrix[row][column];
            visited[row][column] = true;
            int nextRow = row + directions[directionIndex][0];   //下一个行
            int nextColumn = column + directions[directionIndex][1];  //下一个列
            if(nextRow<0||nextRow>=rows||nextColumn<0||nextColumn>=columns||visited[nextRow][nextColumn])   //如果下一步超出边界了或者碰到已访问的位置了
                directionIndex = (directionIndex+1)%4;  //则转向
            row += directions[directionIndex][0];
            column += directions[directionIndex][1];
        }
        return ans;
    }
};

1037. 有效的回旋镖

回旋镖定义为一组三个点,这些点各不相同且不在一条直线上。
给出平面上三个点组成的列表,判断这些点是否可以构成回旋镖。

示例1:

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

示例2:

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

代码:

class Solution {
public:
    bool isBoomerang(vector<vector<int>>& points) {
        //斜率公式变形: (x3-x2)(y2-y1) = (x2-x1)(y3-y2)
        return (points[2][0] - points[1][0]) * (points[1][1] - points[0][1]) != (points[1][0] - points[0][0]) * (points[2][1] - points[1][1]);
    }
};

1566. 重复至少 K 次且长度为 M 的模式

给你一个正整数数组 arr,请你找出一个长度为 m 且在数组中至少重复 k 次的模式。
模式 是由一个或多个值组成的子数组(连续的子序列),连续 重复多次但 不重叠 。 模式由其长度和重复次数定义。
如果数组中存在至少重复 k 次且长度为 m 的模式,则返回 true ,否则返回 false 。

示例1:

输入:arr = [1,2,4,4,4,4], m = 1, k = 3
输出:true
解释:模式 (4) 的长度为 1 ,且连续重复 4 次。注意,模式可以重复 k 次或更多次,但不能少于 k 次。

示例2:

输入:arr = [1,2,1,2,1,1,1,3], m = 2, k = 2
输出:true
解释:模式 (1,2) 长度为 2 ,且连续重复 2 次。另一个符合题意的模式是 (2,1) ,同样重复 2 次。

示例3:

输入:arr = [1,2,1,2,1,3], m = 2, k = 3
输出:false
解释:模式 (1,2) 长度为 2 ,但是只连续重复 2 次。不存在长度为 2 且至少重复 3 次的模式。

代码:

class Solution {
public:
    bool containsPattern(vector<int>& a, int m, int k) {
        int n = a.size();
        if(n < m * k) return false;
        int i, j;
        for(i = 0; i <= n - m * k; ++i) {
            for(j = i + m; j < i + m * k; ++j) {
                if(a[j] != a[j - m]) break;
            }
            if(j == i + m * k) return true;
        }
        return false;
    }
};

// 考虑到数组长度不超过100,直接暴力枚举pattern。
// (1)由于pattern的长度为m,且需要重复k次,所以pattern起始位置应该在[0, n - m * k]之间。
// (2)假设pattern起始位置为i,判断后续序列[i + m, i + m * k)是否满足条件,其实只需要判断arr[j]与arr[j - m]是否相同。

剑指 Offer 58 - I. 翻转单词顺序

输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。

示例1:

输入: “the sky is blue”
输出: “blue is sky the”

示例2:

输入: " hello world! "
输出: “world! hello”
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

代码:

class Solution {
public:
    string reverseWords(string s) {
        istringstream ss(s);
        string res,str;
        while(ss>>str)
        {
            res = str+" "+res;
        }
        return res.substr(0,res.length()-1);
    }
};

367. 有效的完全平方数

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

示例1:

输入:num = 16
输出:true

示例2:

输入:num = 14
输出:false

代码:

class Solution 
{
public:
    bool isPerfectSquare(int num) 
    {
        int num1 = 1;
        while(num > 0) 
        {
            num -= num1;
            num1 += 2;
        }
        return num == 0;
    }
};


// 1 4=1+3 9=1+3+5 16=1+3+5+7以此类推,模仿它可以使用一个while循环,不断减去一个从1开始不断增大的奇数,若最终减成了0,说明是完全平方数,否则,不是。

819. 最常见的单词

给定一个段落 (paragraph) 和一个禁用单词列表 (banned)。返回出现次数最多,同时不在禁用列表中的单词。
题目保证至少有一个词不在禁用列表中,而且答案唯一。
禁用列表中的单词用小写字母表示,不含标点符号。段落中的单词不区分大小写。答案都是小写字母。

示例:

输入:
paragraph = “Bob hit a ball, the hit BALL flew far after it was hit.”
banned = [“hit”]
输出: “ball”
解释:
“hit” 出现了3次,但它是一个禁用的单词。
“ball” 出现了2次 (同时没有其他单词出现2次),所以它是段落里出现次数最多的,且不在禁用列表中的单词。
注意,所有这些单词在段落里不区分大小写,标点符号需要忽略(即使是紧挨着单词也忽略, 比如 “ball,”),
"hit"不是最终的答案,虽然它出现次数更多,但它在禁用单词列表中。

代码:

class Solution {
public:
    string mostCommonWord(string paragraph, vector<string>& banned) {
        unordered_map<string,int> cnt;
        unordered_set<string> ban;
        for(auto& w:banned)
        {
            ban.insert(w);
        }

        string ans;
        int maxcnt = 0;
        for(auto& c:paragraph)  //去除标点符号
        {
            c=isalpha(c)?c:' ';
        }
        stringstream ss(paragraph);
        string temp;
        while(ss>>temp)   //temp表示每个单词
        {
            for(auto& c:temp)   //转换为小写
            {
                c = tolower(c);
            }
            if(ban.count(temp)) continue; //如果是禁用单词  则直接跳过
            cnt[temp]++;
            if(cnt[temp]>maxcnt)
            {
                maxcnt = cnt[temp];
                ans = temp;
            }
        }
        return ans;
    }
};

219. 存在重复元素 II

给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

示例1:

输入: nums = [1,2,3,1], k = 3
输出: true

示例2:

输入: nums = [1,0,1,1], k = 1
输出: true

示例3:

输入: nums = [1,2,3,1,2,3], k = 2
输出: false

代码:

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_set<int> set; //搜索、插入和移除平均常数时间复杂度,不会超时
        for(int i = 0; i < nums.size(); i++)
        {
            if(set.find(nums[i]) != set.end())
                return true;
            set.insert(nums[i]);
            if(set.size() > k )
                set.erase(nums[i-k]); //滑动窗口长度最大为k 
        }
        return false;
    }
};

680. 验证回文字符串 Ⅱ

给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。

示例1:

输入: “aba”
输出: True

示例2:

输入: “abca”
输出: True
解释: 你可以删除c字符。

代码:

class Solution {
public:
    bool checkPalindrome(const string& s, int low, int high) {
        for (int i = low, j = high; i < j; ++i, --j) {
            if (s[i] != s[j]) {
                return false;
            }
        }
        return true;
    }

    bool validPalindrome(string s) {
        int low = 0, high = s.size() - 1;
        while (low < high) {
            if (s[low] == s[high]) {
                ++low;
                --high;
            } else {
                return checkPalindrome(s, low, high - 1) || checkPalindrome(s, low + 1, high);
            }
        }
        return true;
    }
};

14. 最长公共前缀

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

示例1:

输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”

示例2:

输入:strs = [“dog”,“racecar”,“car”]
输出:""
解释:输入不存在公共前缀。

代码:

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        if(strs.empty()) return string();
        sort(strs.begin(), strs.end());
        string st = strs.front(), en = strs.back();
        int i, num = min(st.size(), en.size());
        for(i = 0; i < num && st[i] == en[i]; i++);
        return string(st, 0, i);
    }
};

925. 长按键入

你的朋友正在使用键盘输入他的名字 name。偶尔,在键入字符 c 时,按键可能会被长按,而字符可能被输入 1 次或多次。
你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字(其中一些字符可能被长按),那么就返回 True。

示例1:

输入:name = “alex”, typed = “aaleex”
输出:true
解释:‘alex’ 中的 ‘a’ 和 ‘e’ 被长按。

示例2:

输入:name = “saeed”, typed = “ssaaedd”
输出:false
解释:‘e’ 一定需要被键入两次,但在 typed 的输出中不是这样。

代码:

class Solution {
public:
    bool isLongPressedName(string name, string typed) {
        int i = 0, j = 0;
        while (j < typed.length()) {
            if (i < name.length() && name[i] == typed[j]) {
                i++;
                j++;
            } else if (j > 0 && typed[j] == typed[j - 1]) {
                j++;
            } else {
                return false;
            }
        }
        return i == name.length();
    }
};

168. Excel表列名称

给定一个正整数,返回它在 Excel 表中相对应的列名称。
例如,
1 -> A
2 -> B
3 -> C

26 -> Z
27 -> AA
28 -> AB

示例1:

输入: 1
输出: “A”

示例2:

输入: 28
输出: “AB”

示例3:

输入: 701
输出: “ZY”

代码:

class Solution {
public:
    string convertToTitle(int columnNumber) {
        string ans;
        while(columnNumber)
        {
            columnNumber-=1;   //注意要减一
            ans+=('A'+columnNumber%26);
            columnNumber/=26;
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

7. 整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。

示例1:

输入:x = 123
输出:321

示例2:

输入:x = -123
输出:-321

示例3:

输入:x = 120
输出:21

代码:

class Solution {
public:
    int reverse(int x) {
        long temp = 0;
        long sum = 0;
        while(x!=0){
            temp = x%10;
            sum = sum*10+temp;
            x = x/10;
        }
        if(sum>INT_MAX || sum <INT_MIN){
            sum = 0;
        }
        return sum;
    }
};

605. 种花问题

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。

示例1:

输入:flowerbed = [1,0,0,0,1], n = 1
输出:true

示例2:

输入:flowerbed = [1,0,0,0,1], n = 2
输出:false

代码:

class Solution {
public:
    bool canPlaceFlowers(vector<int>& flowerbed, int n) {
        // 每次跳两格
         for (int i = 0; i < flowerbed.size(); i += 2) {
             // 如果当前为空地
            if (flowerbed[i] == 0) {
                // 如果是最后一格或者下一格为空
                if (i == flowerbed.size() - 1 || flowerbed[i + 1] == 0) {
                    n--;
                } else {
                    i++;
                }
            }
        }
        return n <= 0;
    }
};

// 首先这里我用的是连跳两格的方法,因为如果遇到1,那么下一格子一定是0,这是毋庸置疑的(规则限定),所以如果遇到最后一个格子,或者下个格子不是1,果断填充,

LCP 22. 黑白方格画

小扣注意到秋日市集上有一个创作黑白方格画的摊位。摊主给每个顾客提供一个固定在墙上的白色画板,画板不能转动。画板上有 n * n 的网格。绘画规则为,小扣可以选择任意多行以及任意多列的格子涂成黑色,所选行数、列数均可为 0。
小扣希望最终的成品上需要有 k 个黑色格子,请返回小扣共有多少种涂色方案。
注意:两个方案中任意一个相同位置的格子颜色不同,就视为不同的方案。

示例1:

输入:n = 2, k = 2
输出:4
解释:一共有四种不同的方案:
第一种方案:涂第一列;
第二种方案:涂第二列;
第三种方案:涂第一行;
第四种方案:涂第二行。

示例2:

输入:n = 2, k = 1
输出:0
解释:不可行,因为第一次涂色至少会涂两个黑格。

示例3:

输入:n = 2, k = 4
输出:1
解释:共有 2*2=4 个格子,仅有一种涂色方案。

代码:

class Solution {
public:
    int paintingPlan(int n, int k) {
        if(k==0) return 1;
        if(k<n) return 0;
        if(k==n*n) return 1;
        float i,j;  
        int res = 0;
        for(i=0;i<n;i++)
        {
            float x = (k-n*i)/(n-i);
            if(x!=(int)x) continue;
            j = (int)x;
            res+=calcC(i,n)*calcC(j,n);
        }
        return res;
    }

    int calcC(int m,int n)
    {
        if(m==0) return 1;
        if(m<0) return 0;
        int x = 1,y = 1,z = n-m+1;
        while(n>=z)
        {
            x*=n;
            n--;
        }
        while(m>0)
        {
            y*=m;
            m--;
        }
        return x/y;
    }
};

859. 亲密字符串

给定两个由小写字母构成的字符串 A 和 B ,只要我们可以通过交换 A 中的两个字母得到与 B 相等的结果,就返回 true ;否则返回 false 。
交换字母的定义是取两个下标 i 和 j (下标从 0 开始),只要 i!=j 就交换 A[i] 和 A[j] 处的字符。例如,在 “abcd” 中交换下标 0 和下标 2 的元素可以生成 “cbad” 。

示例1:

输入: A = “ab”, B = “ba”
输出: true
解释: 你可以交换 A[0] = ‘a’ 和 A[1] = ‘b’ 生成 “ba”,此时 A 和 B 相等。

示例2:

输入: A = “ab”, B = “ab”
输出: false
解释: 你只能交换 A[0] = ‘a’ 和 A[1] = ‘b’ 生成 “ba”,此时 A 和 B 不相等。

示例3:

输入: A = “aa”, B = “aa”
输出: true
解释: 你可以交换 A[0] = ‘a’ 和 A[1] = ‘a’ 生成 “aa”,此时 A 和 B 相等。

示例4:

输入: A = “”, B = “aa”
输出: false

代码:

class Solution {
public:
    bool buddyStrings(const string& a, const string& b) {
        if (a.size() != b.size()) return false;
        vector<int> index_of_mismatch;
        for (int i = 0; i < a.size(); i++)
            if (a[i] != b[i]) {
                index_of_mismatch.push_back(i);
                if (2 < index_of_mismatch.size()) return false;
            }
        if (index_of_mismatch.size() == 0) {
            return set<char>(a.begin(), a.end()).size() < a.size();
        } else if (index_of_mismatch.size() == 2) {
            return a[index_of_mismatch[0]] == b[index_of_mismatch[1]] &&
                   a[index_of_mismatch[1]] == b[index_of_mismatch[0]];
        }
        return false;
    }
};

// 1统计字符串A,B中字符不匹配的下标。
// 2不匹配的下标个数不等于 0 或 2 时,不能由A得到B。
// 3不匹配的下标个数等于0时,A与B中字符完全相同,还需要A中有重复字符。
// 4不匹配的下标个数等于2时,判断交换两对字符后是否匹配。

665. 非递减数列

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中任意的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。

示例1:

输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

示例2:

输入: nums = [4,2,1]
输出: false
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

代码:

class Solution {
public:
    bool checkPossibility(vector<int>& nums) {
        int sum=0;
        for(int i=1;i<nums.size();i++){
            if(nums[i-1]>nums[i]){  //如果第i-1个位置出现了拐点
                sum++;
                if(sum>=2) return false;
                if(i-2>=0 && nums[i-2]>nums[i]){
                    nums[i]=nums[i-1];                
                    }
                else nums[i-1]=nums[i];
            }
        }
        return true;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值