leetcode剑指offer合集+题解

目录

    • 一、用两个栈实现队列
        • 题目
        • 解题思路
        • AC代码
    • 二、包含min函数的栈
        • 题目
        • 解题思路
        • AC代码
    • 三、从尾到头打印链表
        • 题目
        • 解题思路
        • AC代码
    • 四、反转链表
        • 题目
        • 解题思路
        • AC代码
    • 五、复杂链表的复制
        • 题目
        • 解题思路
        • AC代码
    • 六、替换空格
        • 题目
        • 解题思路
        • AC代码
    • 七、左旋转字符串
        • 题目
        • 解题思路
        • AC代码
    • 八、数组中重复的数字
        • 题目
        • 解题思路
        • AC代码
    • 九、I. 在排序数组中查找数字
        • 题目
        • 解题思路
        • AC代码
    • 十、II. 0~n-1中缺失的数字
        • 题目
        • 解题思路
        • AC代码
    • 十一、二维数组中的查找
        • 题目
        • 解题思路
        • AC代码
    • 十二、旋转数组的最小数字
        • 题目
        • 解题思路
        • AC代码
    • 十三、第一个只出现一次的字符
        • 题目
        • 解题思路
        • AC代码
    • 十四、I. 从上到下打印二叉树
        • 题目
        • 解题思路
        • AC代码
    • 十五、从上到下打印二叉树 II
        • 题目
        • 解题思路
        • AC代码
    • 十六、从上到下打印二叉树 III
        • 题目
        • 解题思路
        • AC代码
    • 十七、树的子结构
        • 题目
        • 解题思路
        • AC代码
    • 十八、二叉树的镜像
        • 题目
        • 解题思路
        • AC代码
    • 十九、对称的二叉树
        • 题目
        • 解题思路
        • AC代码
    • 二十、斐波那契数列
        • 题目
        • 解题思路
        • AC代码
    • 二十一、青蛙跳台阶问题
        • 题目
        • 解题思路
        • AC代码
    • 二十二、股票的最大利润
        • 题目
        • 解题思路
        • AC代码
    • 二十三、连续子数组的最大和
        • 题目
        • 解题思路
        • AC代码
    • 二十四、礼物的最大价值
        • 题目
        • 解题思路
        • AC代码
    • 二十五、把数字翻译成字符串
        • 题目
        • 解题思路
        • AC代码
    • 二十六、最长不含重复字符的子字符串
        • 题目
        • 解题思路
        • AC代码
    • 二十七、删除链表的节点
        • 题目
        • 解题思路
        • AC代码
    • 二十八、链表中倒数第k个节点
        • 题目
        • 解题思路
        • AC代码
    • 二十九、合并两个排序的链表
        • 题目
        • 解题思路
        • AC代码
    • 三十、两个链表的第一个公共节点
        • 题目
        • 解题思路
        • AC代码
    • 三十一、调整数组顺序使奇数位于偶数前面
        • 题目
        • 解题思路
        • AC代码
    • 三十二、和为s的两个数字
        • 题目
        • 解题思路
        • AC代码
    • 三十三、翻转单词顺序
        • 题目
        • 解题思路
        • AC代码
    • 三十四、矩阵中的路径
        • 题目
        • 解题思路
        • AC代码
    • 三十五、机器人的运动范围
        • 题目
        • 解题思路
        • AC代码
    • 三十六、二叉树中和为某一值的路径
        • 题目
        • 解题思路
        • AC代码
    • 三十七、二叉搜索树与双向链表
        • 题目
        • 解题思路
        • AC代码
    • 三十八、二叉搜索树的第k大节点
        • 题目
        • 解题思路
        • AC代码
    • 三十九、把数组排成最小的数
        • 题目
        • 解题思路
        • AC代码
    • 四十、扑克牌中的顺子
        • 题目
        • 解题思路
        • AC代码
    • 四十一、最小的k个数
        • 题目
        • 解题思路
        • AC代码
    • 四十二、数据流中的中位数
        • 题目
        • 解题思路
        • AC代码
    • 四十三、二叉树的深度
        • 题目
        • 解题思路
        • AC代码
    • 四十四、平衡二叉树
        • 题目
        • 解题思路
        • AC代码
    • 四十五、求1+2+…+n
        • 题目
        • 解题思路
        • AC代码
    • 四十六、二叉搜索树的最近公共祖先
        • 题目
        • 解题思路
        • AC代码
    • 四十七、二叉树的最近公共祖先
        • 题目
        • 解题思路
        • AC代码
    • 四十八、重建二叉树
        • 题目
        • 解题思路
        • AC代码
    • 四十九、数值的整数次方
        • 题目
        • 解题思路
        • AC代码
    • 五十、二叉搜索树的后序遍历序列
        • 题目
        • 解题思路
        • AC代码
    • 五十一、二进制中1的个数
        • 题目
        • 解题思路
        • AC代码
    • 五十二、不用加减乘除做加法
        • 题目
        • 解题思路
        • AC代码
    • 五十三、数组中数字出现的次数
        • 题目
        • 解题思路
        • AC代码
    • 五十四、数组中数字出现的次数 II
        • 题目
        • 解题思路
        • AC代码
    • 五十五、数组中出现次数超过一半的数字
        • 题目
        • 解题思路
        • AC代码
    • 五十六、构建乘积数组
        • 题目
        • 解题思路
        • AC代码
    • 五十七、剪绳子
        • 题目
        • 解题思路
        • AC代码
    • 五十八、和为s的连续正数序列
        • 题目
        • 解题思路
        • AC代码
    • 五十九、圆圈中最后剩下的数字
        • 题目
        • 解题思路
        • AC代码
    • 六十、顺时针打印矩阵
        • 题目
        • 解题思路
        • AC代码
    • 六十一、栈的压入、弹出序列
        • 题目
        • 解题思路
        • AC代码
    • 六十二、表示数值的字符串
        • 题目
        • 解题思路
        • AC代码
    • 六十三、把字符串转换成整数
        • 题目
        • 解题思路
        • AC代码
    • 六十四、滑动窗口的最大值
        • 题目
        • 解题思路
        • AC代码
    • 六十五、队列的最大值
        • 题目
        • 解题思路
        • AC代码
    • 六十六、序列化二叉树
        • 题目
        • 解题思路
        • AC代码
    • 六十七、字符串的排列
        • 题目
        • 解题思路
        • AC代码
    • 六十八、正则表达式匹配
        • 题目
        • 解题思路
        • AC代码
    • 六十九、丑数
        • 题目
        • 解题思路
        • AC代码
    • 七十、n个骰子的点数
        • 题目
        • 解题思路
        • AC代码
    • 七十一、打印从1到最大的n位数
        • 题目
        • 解题思路
        • AC代码
    • 七十二、数组中的逆序对
        • 题目
        • 解题思路
        • AC代码
    • 七十三、剪绳子 II
        • 题目
        • 解题思路
        • AC代码
    • 七十四、1~n 整数中 1 出现的次数
        • 题目
        • 解题思路
        • AC代码
    • 七十五、数字序列中某一位的数字
        • 题目
        • 解题思路
        • AC代码

一、用两个栈实现队列

题目

剑指 Offer 09. 用两个栈实现队列

解题思路

维护两个栈,一个维护插入,一个维护删除。需要删除的时候可以把插入栈里的元素全部倒进删除栈里,注意只有删除栈为空了,才会倒,否则会破坏元素的顺序。很巧妙。

AC代码

class CQueue
{
    stack<int> st1, st2;

public:
    CQueue()
    {
        while (st1.size())
            st1.pop();
        while (st2.size())
            st2.pop();
    }
    void appendTail(int value)
    {
        st1.push(value);
    }
    int deleteHead()
    {
        if (!st2.size()) //为空才会倒,才能保证顺序正确
            while (st1.size())
            {
                st2.push(st1.top());
                st1.pop();
            }
        if (!st2.size())
            return -1;
        int res = st2.top();
        st2.pop();
        return res;
    }
};

二、包含min函数的栈

题目

剑指 Offer 30. 包含min函数的栈

为栈新增功能, O ( 1 ) O(1) O(1)得到栈中最小值。

解题思路

如果当前压入的元素不是最小值,那么它一定不会作为最小值。所以我们只需要开一个辅助单调栈维护好每个“在压入时是最小值”的元素。

AC代码

class MinStack
{

public:
    stack<int> st, st2;

    MinStack()
    {
        while (!st.empty())
            st.pop();
        while (!st2.empty())
            st2.pop();
    }

    void push(int x)
    {
        st.push(x);
        if (!st2.size() || x <= st2.top())
            st2.push(x);
    }

    void pop()
    {
        if (st2.size() && st.top() == st2.top())
            st2.pop();
        st.pop();
    }

    int top()
    {
        return st.top();
    }

    int min()
    {
        return st2.top();
    }
};

三、从尾到头打印链表

题目

剑指 Offer 06. 从尾到头打印链表

解题思路

简单题,可以遍历一遍reverse,或者递归回溯打印。

AC代码

class Solution
{
public:
    vector<int> reversePrint(ListNode *head)
    {
        vector<int> ans;
        if (head == nullptr)
            return ans;
        if (head->next != NULL)
        {
            ans = reversePrint(head->next);
            ans.emplace_back(head->val);
        }
        else
            ans.emplace_back(head->val);
        return ans;
    }
};

四、反转链表

题目

剑指 Offer 24. 反转链表

解题思路

遍历并反转即可。

AC代码

class Solution
{
public:
    ListNode *reverseList(ListNode *head)
    {
        vector<ListNode *> vec;
        ListNode *pre = nullptr;
        while (head != nullptr)
        {
            vec.emplace_back(head);
            head = head->next;
            vec.back()->next = pre;
            pre = vec.back();
        }
        return pre;
    }
};

五、复杂链表的复制

题目

剑指 Offer 35. 复杂链表的复制

解题思路

模拟一遍即可。

AC代码

class Solution
{
public:
    const int N = 1e5 + 5;
    Node *copyRandomList(Node *head)
    {
        vector<Node *> vec;
        for (int i = 0; i < N; i++)
        {
            Node *ptr = new Node(1);
            vec.emplace_back(ptr);
        }
        int id = 1, cnt = 1;
        map<Node *, int> mp;
        map<int, int> to;
        Node *tmp = head;
        vec[0] = nullptr;
        while (head != nullptr)
        {
            mp[head] = cnt++;
            head = head->next;
        }
        vec[cnt] = nullptr;
        head = tmp;
        while (head != nullptr)
        {
            vec[id]->next = vec[id + 1]; //指向后一个元素
            vec[id]->val = head->val;    //赋值
            // to[i]=j表示i号结点random指向j号结点
            to[id++] = mp[head->random];
            head = head->next;
        }
        id = 1;
        while (vec[id] != nullptr)
        {
            vec[id]->random = vec[to[id]];
            id++;
        }
        return vec[1];
    }
};

六、替换空格

题目

剑指 Offer 05. 替换空格

解题思路

AC代码

class Solution
{
public:
    string replaceSpace(string s)
    {
        string ans;
        for (auto it : s)
        {
            if (it == ' ')
                ans += "%20";
            else
                ans += it;
        }
        return ans;
    }
};

七、左旋转字符串

题目

剑指 Offer 58 - II. 左旋转字符串

解题思路

AC代码

class Solution
{
public:
    string reverseLeftWords(string s, int n)
    {
        return s.substr(n) + s.substr(0, n);
    }
};

八、数组中重复的数字

题目

剑指 Offer 03. 数组中重复的数字

解题思路

AC代码

class Solution
{
public:
    int findRepeatNumber(vector<int> &nums)
    {
        set<int> st;
        for (auto it : nums)
        {
            if (st.count(it))
                return it;
            st.insert(it);
        }
        return -1;
    }
};

九、I. 在排序数组中查找数字

题目

剑指 Offer 53 - I. 在排序数组中查找数字

解题思路

AC代码

class Solution
{
public:
    int search(vector<int> &nums, int target)
    {
        map<int, int> mp;
        for (auto it : nums)
            mp[it]++;
        return mp[target];
    }
};

十、II. 0~n-1中缺失的数字

题目

剑指 Offer 53 - II. 0~n-1中缺失的数字

解题思路

AC代码

class Solution
{
public:
    int missingNumber(vector<int> &nums)
    {

        int ans = 0;
        while (ans < nums.size() && nums[ans] == ans)
            ans++;
        return ans;
    }
};

十一、二维数组中的查找

题目

剑指 Offer 04. 二维数组中的查找

解题思路

遍历每一列,采用二分,时间复杂度 O ( n l o g m ) O(nlogm) O(nlogm)
官方题解:从右上角出发,边走边选择,时间复杂度 O ( n l o g m ) O(nlogm) O(nlogm)

AC代码

class Solution
{
public:
    bool findNumberIn2DArray(vector<vector<int>> &matrix, int target)
    {
        if (!matrix.size())
            return 0;
        int n = matrix.size();
        int m = matrix[0].size();
        for (int i = 0; i < n; i++)
        {
            int p = lower_bound(matrix[i].begin(), matrix[i].end(), target) - matrix[i].begin();
            if (p != m && matrix[i][p] == target)
                return 1;
        }
        return 0;
    }
};

十二、旋转数组的最小数字

题目

剑指 Offer 11. 旋转数组的最小数字

解题思路

判断一下转折位置和corner test即可。

AC代码

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

十三、第一个只出现一次的字符

题目

剑指 Offer 50. 第一个只出现一次的字符

解题思路

AC代码

class Solution
{
public:
    char firstUniqChar(string s)
    {
        map<char, int> mp;
        for (auto it : s)
            mp[it]++;
        for (auto it : s)
            if (mp[it] == 1)
                return it;
        return ' ';
    }
};

十四、I. 从上到下打印二叉树

题目

剑指 Offer 32 - I. 从上到下打印二叉树

解题思路

遍历一遍即可。、
好吧,其实可以直接BFS。

AC代码

class Solution
{
public:
    map<int, vector<int>> mp;
    void dfs(TreeNode *u, int dep)
    {
        mp[dep].emplace_back(u->val);
        if (u->left)
            dfs(u->left, dep + 1);
        if (u->right)
            dfs(u->right, dep + 1);
    }
    vector<int> levelOrder(TreeNode *root)
    {
        vector<int> ans;
        if (root == nullptr)
            return ans;
        dfs(root, 0);
        int dep = 0;
        while (mp[dep].size())
        {
            for (auto it : mp[dep])
                ans.emplace_back(it);
            dep++;
        }
        return ans;
    }
};

十五、从上到下打印二叉树 II

题目

剑指 Offer 32 - II. 从上到下打印二叉树 II

解题思路

同上一题

AC代码

class Solution
{
public:
    vector<vector<int>> ans;
    vector<int> tmp;
    void dfs(TreeNode *u, int dep)
    {
        if (ans.size() <= dep)
            ans.emplace_back(tmp);
        ans[dep].emplace_back(u->val);
        if (u->left)
            dfs(u->left, dep + 1);
        if (u->right)
            dfs(u->right, dep + 1);
    }
    vector<vector<int>> levelOrder(TreeNode *root)
    {
        if (root == nullptr)
            return ans;
        dfs(root, 0);
        return ans;
    }
};

十六、从上到下打印二叉树 III

题目

Offer 32 - III. 从上到下打印二叉树 III

解题思路

对奇数深度的reverse一下即可。
也可以使用deque。

AC代码

class Solution
{
public:
    vector<vector<int>> ans;
    void bfs(TreeNode *root)
    {
        queue<pair<TreeNode *, int>> q;
        q.push(make_pair(root, 0));
        while (q.size())
        {
            TreeNode *now = q.front().first;
            int depth = q.front().second;
            q.pop();
            if (ans.size() <= depth)
                ans.push_back(vector<int>(0));
            ans[depth].push_back(now->val);
            if (now->left != nullptr)
                q.push(make_pair(now->left, depth + 1));
            if (now->right != nullptr)
                q.push(make_pair(now->right, depth + 1));
        }
    }
    vector<vector<int>> levelOrder(TreeNode *root)
    {
        if (root == nullptr)
            return ans;
        bfs(root);
        return ans;
    }
};

十七、树的子结构

题目

剑指 Offer 26. 树的子结构

解题思路

枚举A树上的一个节点作为根,和B数进行递归比较。

AC代码

class Solution
{
public:
    bool check(TreeNode *rt1, TreeNode *rt2)
    {
        if (rt1 == nullptr || rt2 == nullptr)
            return 0;
        if (rt1->val != rt2->val)
            return 0;
        int s1 = 2, s2 = 2;
        //如果A树有左儿子,B数没有,return 0
        if (rt1->left == nullptr && rt2->left != nullptr)
            return 0;
        //如果A树有右儿子,B数没有,return 0
        if (rt1->right == nullptr && rt2->right != nullptr)
            return 0;
        //有左儿子,进行递归
        if (rt2->left != nullptr && !check(rt1->left, rt2->left))
            return 0;
        //有右儿子,进行递归
        if (rt2->right != nullptr && !check(rt1->right, rt2->right))
            return 0;
        //剩下的情况都是满足的
        return 1;
    }
    int ans = 0;
    //枚举A上的结点 进行比较
    void dfs(TreeNode *A, TreeNode *B)
    {
        if (ans)
            return;
        if (check(A, B))
            ans = 1;
        if (A->left != nullptr)
            dfs(A->left, B);
        if (A->right != nullptr)
            dfs(A->right, B);
    }
    bool isSubStructure(TreeNode *A, TreeNode *B)
    {
        dfs(A, B);
        return ans;
    }
};

十八、二叉树的镜像

题目

剑指 Offer 27. 二叉树的镜像

解题思路

遍历树并交换所有左右儿子即可。

AC代码

class Solution
{
public:
    void dfs(TreeNode *root)
    {
        swap(root->left, root->right);
        if (root->left != nullptr)
            dfs(root->left);
        if (root->right != nullptr)
            dfs(root->right);
    }
    TreeNode *mirrorTree(TreeNode *root)
    {
        if (root == nullptr)
            return root;
        dfs(root);
        return root;
    }
};

十九、对称的二叉树

题目

剑指 Offer 28. 对称的二叉树

解题思路

双指针即可。

AC代码

class Solution
{
public:
    int ans = 1;
    void dfs(TreeNode *A, TreeNode *B)
    {
        if (!ans)
            return;
        if (A->val != B->val)
        {
            ans = 0;
            return;
        }
        if (A->left != nullptr && B->right == nullptr)
        {
            ans = 0;
            return;
        }
        if (A->left == nullptr && B->right != nullptr)
        {
            ans = 0;
            return;
        }
        if (A->right != nullptr && B->left == nullptr)
        {
            ans = 0;
            return;
        }
        if (A->right == nullptr && B->left != nullptr)
        {
            ans = 0;
            return;
        }
        if (A->right)
            dfs(A->right, B->left);
        if (A->left)
            dfs(A->left, B->right);
    }
    bool isSymmetric(TreeNode *root)
    {
        if (root == nullptr)
            return ans;
        dfs(root, root);
        return ans;
    }
};

二十、斐波那契数列

题目

剑指 Offer 10- I. 斐波那契数列

解题思路

AC代码

class Solution
{
public:
    const int mod = 1e9 + 7;
    int fib(int n)
    {
        if (n == 0)
            return 0;
        else if (n == 1)
            return 1;
        int a, b, c;
        a = 0, b = 1;
        n--;
        while (n--)
        {
            c = (a + b) % mod;
            a = b;
            b = c;
        }
        return c;
    }
};

二十一、青蛙跳台阶问题

题目

剑指 Offer 10- II. 青蛙跳台阶问题

解题思路

AC代码

class Solution
{
public:
    int numWays(int n)
    {
        const int mod = 1e9 + 7;
        vector<int> dp(n + 5);
        dp[0] = 1, dp[1] = 1, dp[2] = 2;
        for (int i = 3; i <= n; i++)
            dp[i] = (dp[i - 1] + dp[i - 2]) % mod;
        return dp[n];
    }
};

二十二、股票的最大利润

题目

剑指 Offer 63. 股票的最大利润

解题思路

维护前缀最小值。

AC代码

class Solution
{
public:
    int maxProfit(vector<int> &prices)
    {
        int minn = 1e9 + 7, ans = 0;
        for (auto it : prices)
        {
            ans = max(ans, it - minn);
            minn = min(minn, it);
        }
        return ans;
    }
};

二十三、连续子数组的最大和

题目

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

解题思路

当sum为负数时,丢弃肯定是最优的。

AC代码

class Solution
{
public:
    int maxSubArray(vector<int> &nums)
    {
        int maxx = *max_element(nums.begin(), nums.end());
        if (maxx < 0)
            return maxx;
        int ans = 0, sum = 0;
        for (auto it : nums)
        {
            if (sum + it < 0)
                sum = 0;
            else
                sum += it;
            ans = max(ans, sum);
        }
        return ans;
    }
};

二十四、礼物的最大价值

题目

剑指 Offer 47. 礼物的最大价值

解题思路

动态规划。

AC代码

class Solution
{
public:
    int maxValue(vector<vector<int>> &grid)
    {
        for (int i = 0; i < grid.size(); i++)
        {
            for (int j = 0; j < grid[0].size(); j++)
            {
                if (i - 1 >= 0 && j - 1 >= 0)
                    grid[i][j] += max(grid[i - 1][j], grid[i][j - 1]);
                else if (i - 1 >= 0)
                    grid[i][j] += grid[i - 1][j];
                else if (j - 1 >= 0)
                    grid[i][j] += grid[i][j - 1];
            }
        }
        return grid[grid.size() - 1][grid[0].size() - 1];
    }
};

二十五、把数字翻译成字符串

题目

剑指 Offer 46. 把数字翻译成字符串

解题思路

动态规划。

dp[i] = (val <= 25 && val >= 10 ?
dp[i - 1] + dp[i - 2] : dp[i - 1]);

AC代码

class Solution
{
public:
    int translateNum(int num)
    {
        if (num == 0)
            return 1;
        vector<int> vec;
        while (num)
        {
            vec.emplace_back(num % 10);
            num /= 10;
        }
        reverse(vec.begin(), vec.end());
        int n = vec.size();
        vector<int> dp(20);
        dp[0] = 1;
        if (n >= 2)
        {
            int val = vec[0] * 10 + vec[1];
            dp[1] = (val <= 25 && val >= 10 ? 2 : 1);
        }
        for (int i = 2; i < n; i++)
        {
            int val = vec[i - 1] * 10 + vec[i];
            dp[i] = (val <= 25 && val >= 10 ? dp[i - 1] + dp[i - 2] : dp[i - 1]);
        }
        return dp[n - 1];
    }
};

二十六、最长不含重复字符的子字符串

题目

剑指 Offer 48. 最长不含重复字符的子字符串

解题思路

尺取

AC代码

class Solution
{
public:
    int lengthOfLongestSubstring(string s)
    {
        map<char, int> mp;
        int n = s.size();
        int l = 0, r = 0, ans = 0;
        while (l < n)
        {
            while (r < n && mp[s[r]] == 0)
            {
                mp[s[r]]++;
                r++;
            }
            ans = max(ans, r - l);
            if (r >= n)
                break;
            while (mp[s[r]])
            {
                mp[s[l]]--;
                l++;
            }
        }
        return ans;
    }
};

二十七、删除链表的节点

题目

剑指 Offer 18. 删除链表的节点

解题思路

AC代码

class Solution
{
public:
    ListNode *deleteNode(ListNode *head, int val)
    {
        if (head->val == val)
            return head->next;
        ListNode *tmp = head, *pre = head;
        head = head->next;
        while (1)
        {
            if (head->val == val)
            {
                pre->next = head->next;
                break;
            }
            pre = head;
            head = head->next;
        }
        return tmp;
    }
};

二十八、链表中倒数第k个节点

题目

剑指 Offer 22. 链表中倒数第k个节点

解题思路

维护size = k 的队列。
双指针更优。

AC代码

class Solution
{
public:
    ListNode *getKthFromEnd(ListNode *head, int k)
    {
        queue<ListNode *> q;
        while (head != nullptr)
        {
            q.push(head);
            head = head->next;
        }
        while (q.size() > k)
            q.pop();
        return q.front();
    }
};

二十九、合并两个排序的链表

题目

剑指 Offer 25. 合并两个排序的链表

解题思路

双指针。

AC代码

class Solution
{
public:
    ListNode *mergeTwoLists(ListNode *l1, ListNode *l2)
    {
        ListNode *ans = new ListNode;
        ListNode *tail = ans;
        while (l1 != nullptr || l2 != nullptr)
        {
            if (l1 == nullptr)
                tail->next = l2, l2 = l2->next;
            else if (l2 == nullptr)
                tail->next = l1, l1 = l1->next;
            else
            {
                if (l1->val < l2->val)
                    tail->next = l1, l1 = l1->next;
                else
                    tail->next = l2, l2 = l2->next;
            }
            tail = tail->next;
        }
        return ans->next;
    }
};

三十、两个链表的第一个公共节点

题目

剑指 Offer 52. 两个链表的第一个公共节点

解题思路

哈希表记录。

AC代码

class Solution
{
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
    {
        map<ListNode *, int> mp;
        while (headA != nullptr)
        {
            mp[headA] = 1;
            headA = headA->next;
        }
        while (headB != nullptr)
        {
            if (mp[headB])
                return headB;
            headB = headB->next;
        }
        return nullptr;
    }
};

三十一、调整数组顺序使奇数位于偶数前面

题目

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

解题思路

AC代码

class Solution
{
public:
    vector<int> exchange(vector<int> &nums)
    {
        vector<int> ans;
        for (auto it : nums)
            if (it & 1)
                ans.emplace_back(it);
        for (auto it : nums)
            if (!(it & 1))
                ans.emplace_back(it);
        return ans;
    }
};

三十二、和为s的两个数字

题目

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

解题思路

哈希表。
可以使用双指针。

AC代码

class Solution
{
public:
    vector<int> twoSum(vector<int> &nums, int target)
    {
        unordered_map<int, int> mp;
        vector<int> ans;
        for (auto it : nums)
        {
            if (mp[target - it])
            {
                ans.emplace_back(it);
                ans.emplace_back(target - it);
                return ans;
            }
            mp[it]++;
        }
        return ans;
    }
};

三十三、翻转单词顺序

题目

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

解题思路

可以双指针。

AC代码

class Solution
{
public:
    string reverseWords(string s)
    {
        string ans, res;
        s += ' ';
        vector<string> vec;
        for (auto it : s)
        {
            if (it == ' ')
            {
                if (res.size())
                {
                    vec.emplace_back(res);
                    res.clear();
                }
            }
            else
                res += it;
        }
        reverse(vec.begin(), vec.end());
        for (auto it : vec)
            ans += it, ans += ' ';
        return ans.substr(0, ans.size() - 1);
    }
};

三十四、矩阵中的路径

题目

剑指 Offer 12. 矩阵中的路径

解题思路

dfs即可。

AC代码

class Solution
{
public:
    bool flag = 0;
    int n, m;
    void dfs(vector<vector<char>> &board, string &word, int x, int y, int p)
    {
        if (board[x][y] != word[p])
            return;
        if (p == word.size() - 1 || flag)
        {
            flag = 1;
            return;
        }
        char tmp = board[x][y];
        board[x][y] = '#';
        if (x - 1 >= 0)
            dfs(board, word, x - 1, y, p + 1);
        if (y - 1 >= 0)
            dfs(board, word, x, y - 1, p + 1);
        if (x + 1 < n)
            dfs(board, word, x + 1, y, p + 1);
        if (y + 1 < m)
            dfs(board, word, x, y + 1, p + 1);
        board[x][y] = tmp;
    }
    bool exist(vector<vector<char>> &board, string word)
    {
        n = board.size();
        m = board[0].size();
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                dfs(board, word, i, j, 0);
        return flag;
    }
};

三十五、机器人的运动范围

题目

剑指 Offer 13. 机器人的运动范围

解题思路

dfs、bfs都可以。

AC代码

class Solution
{
public:
    int ans = 0;
    int mp[110][110];
    int f(int x, int y)
    {
        int res = 0;
        while (x)
        {
            res += x % 10;
            x /= 10;
        }
        while (y)
        {
            res += y % 10;
            y /= 10;
        }
        return res;
    }
    void dfs(int x, int y, int &n, int &m, int &k)
    {
        mp[x][y] = 1;
        ans++;
        if (x + 1 < n && f(x + 1, y) <= k && !mp[x + 1][y])
            dfs(x + 1, y, n, m, k);
        if (y + 1 < m && f(x, y + 1) <= k && !mp[x][y + 1])
            dfs(x, y + 1, n, m, k);
        if (x - 1 >= 0 && f(x - 1, y) <= k && !mp[x - 1][y])
            dfs(x - 1, y, n, m, k);
        if (y - 1 >= 0 && f(x, y - 1) <= k && !mp[x][y - 1])
            dfs(x, y - 1, n, m, k);
    }
    int movingCount(int m, int n, int k)
    {
        memset(mp, 0, sizeof mp);
        dfs(0, 0, n, m, k);
        return ans;
    }
};

三十六、二叉树中和为某一值的路径

题目

剑指 Offer 34. 二叉树中和为某一值的路径

解题思路

dfs即可。

AC代码


class Solution
{
public:
    vector<vector<int>> ans;
    vector<int> vec;

    void dfs(TreeNode *now, int sum, int &target)
    {
        sum += now->val;
        vec.emplace_back(now->val);
        if (sum == target && now->left == nullptr && now->right == nullptr)
            ans.emplace_back(vec);
        if (now->left)
            dfs(now->left, sum, target);
        if (now->right)
            dfs(now->right, sum, target);
        vec.pop_back();
        sum -= now->val;
    }
    vector<vector<int>> pathSum(TreeNode *root, int &target)
    {
        if (root == nullptr)
            return ans;
        dfs(root, 0, target);
        return ans;
    }
};

三十七、二叉搜索树与双向链表

题目

剑指 Offer 36. 二叉搜索树与双向链表

解题思路

中序遍历一遍即可。

AC代码

class Solution
{
public:
    vector<Node *> vec;
    Node *head = new Node;
    void dfs(Node *now)
    {
        if (now->left)
            dfs(now->left);
        vec.emplace_back(now);
        if (now->right)
            dfs(now->right);
    }
    Node *treeToDoublyList(Node *root)
    {
        if (root == nullptr)
            return nullptr;

        dfs(root);
        int n = vec.size();
        if (n == 1)
        {
            vec[0]->left = vec[0];
            vec[0]->right = vec[0];
            return vec[0];
        }
        vec[0]->left = vec[n - 1];
        vec[0]->right = vec[1];
        vec[n - 1]->left = vec[n - 2];
        vec[n - 1]->right = vec[0];
        for (int i = 1; i < n - 1; i++)
        {
            vec[i]->left = vec[i - 1];
            vec[i]->right = vec[i + 1];
        }
        return vec[0];
    }
};

三十八、二叉搜索树的第k大节点

题目

剑指 Offer 54. 二叉搜索树的第k大节点

解题思路

中序遍历时先遍历右儿子即可。

AC代码

class Solution
{
public:
    int step = 0;
    void dfs(TreeNode *now, int &k, TreeNode **root)
    {
        if (step == k)
            return;
        if (now->right)
            dfs(now->right, k, root);
        step++;
        if (step == k)
        {
            (*root)->val = now->val;
            return;
        }
        if (now->left)
            dfs(now->left, k, root);
    }
    int kthLargest(TreeNode *root, int k)
    {
        dfs(root, k, &root);
        return root->val;
    }
};

三十九、把数组排成最小的数

题目

剑指 Offer 45. 把数组排成最小的数

解题思路

a + b > b + a 则进行交换。

AC代码

class Solution
{
public:
       string f(int x)
    {
        string a = "";
        while (x)
        {
            a = char('0' + x % 10) + a;
            x /= 10;
        }
        if (!a.size())
            a += '0';
        return a;
    }
    string minNumber(vector<int> &nums)
    {
        vector<string> vec;
        for (auto it : nums)
            vec.emplace_back(f(it));
        string ans;
        for (int j = 0; j < vec.size(); j++)
            for (int i = 0; i < vec.size() - 1; i++)
            {
                if (vec[i] + vec[i + 1] > vec[i + 1] + vec[i])
                    swap(vec[i], vec[i + 1]);
            }
        for (auto it : vec)
            ans += it;
        if (!ans.size())
            ans += '0';
        return ans;
    }
};

四十、扑克牌中的顺子

题目

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

解题思路

成立条件:

  1. 没有重复的扑克牌
  2. 去掉0,最大值减最小值 < 5

AC代码

class Solution
{
public:
    bool isStraight(vector<int> &nums)
    {
        set<int> st;
        int mx = -1, mi = 100;
        for (auto it : nums)
        {
            if (it == 0)
                continue;
            mx = max(mx, it);
            mi = min(mi, it);

            if (st.count(it))
                return 0;
            st.insert(it);
        }
        if (mx - mi < 5)
            return 1;
        return 0;
    }
};

四十一、最小的k个数

题目

剑指 Offer 40. 最小的k个数

解题思路

排序即可。

AC代码

class Solution
{
public:
    vector<int> getLeastNumbers(vector<int> &arr, int k)
    {
        sort(arr.begin(), arr.end());
        arr.resize(k);
        return arr;
    }
};

四十二、数据流中的中位数

题目

剑指 Offer 41. 数据流中的中位数

解题思路

对顶堆。

AC代码

class MedianFinder
{
public:
    /*
    min max
         5
    -6   4
    -7   3
    -8   2
    -9   1
    */
    priority_queue<int> qmax, qmin;
    void addNum(int num)
    {
        /*
        保证qmax里的元素都小于qmin里的元素
        并且qmax.size()>=qmin.size()
        */
        if (qmax.size() == qmin.size())
        {
            qmin.push(-num);
            qmax.push(-qmin.top());
            qmin.pop();
        }
        else
        {
            qmax.push(num);
            qmin.push(-qmax.top());
            qmax.pop();
        }
    }
    double findMedian()
    {
        if ((qmax.size() + qmin.size()) % 2 == 0)
            return 1.0 * (qmax.top() - qmin.top()) / 2;
        else
            return qmax.top();
    }
};

四十三、二叉树的深度

题目

剑指 Offer 55 - I. 二叉树的深度

解题思路

递归回溯统计。

AC代码

class Solution
{
public:
    int maxDepth(TreeNode *root)
    {
        if (!root)
            return 0;
        if (root->left && root->right)
            return max(maxDepth(root->left), maxDepth(root->right)) + 1;
        else if (root->left)
            return maxDepth(root->left) + 1;
        else if (root->right)
            return maxDepth(root->right) + 1;
        else
            return 1;
    }
};

四十四、平衡二叉树

题目

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

解题思路

递归求深度即可。

AC代码

class Solution
{
    int ans = 1;
    int dfs(TreeNode *now)
    {
        if (!ans)
            return -1;
        if (!now)
            return 0;
        if (now->left && now->right)
        {
            if (abs(dfs(now->left) - dfs(now->right)) > 1)
                ans = 0;
            return max(dfs(now->left), dfs(now->right)) + 1;
        }
        else if (now->left)
        {
            if (dfs(now->left) > 1)
                ans = 0;
            return dfs(now->left) + 1;
        }
        else if (now->right)
        {
            if (dfs(now->right) > 1)
                ans = 0;
            return dfs(now->right) + 1;
        }
        else
            return 1;
    }

public:
    bool isBalanced(TreeNode *root)
    {
        if (!root)
            return 1;
        dfs(root);
        return ans;
    }
};

四十五、求1+2+…+n

题目

剑指 Offer 64. 求1+2+…+n

解题思路

递归。
题解还有快速乘。

AC代码

class Solution
{
public:
    int sumNums(int n)
    {
        if (n == 1)
            return 1;
        return n + sumNums(n - 1);
    }
};

四十六、二叉搜索树的最近公共祖先

题目

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

解题思路

根据二叉搜索树的性质,
left->val < now->val < right->val

如果p->val == now->val,now就是LCA,并且和p为同一节点;
如果q->val == now->val,now就是LCA,并且和q为同一节点;
如果p->val > now->val && q->val < now->val,now就是LCA;
如果p->val < now->val && q->val > now->val,now就是LCA;

如果p->val < now->val && q->val < now->val,向左递归;
如果p->val > now->val && q->val > now->val,向右递归;

AC代码

class Solution
{
    TreeNode *ans;
    void dfs(TreeNode *now, TreeNode **p, TreeNode **q)
    {
        if ((*p)->val == now->val || (*q)->val == now->val)
        {
            ans = now;
            return;
        }
        //剩下的情况:一左一右、都左、都右
        if (((*p)->val < now->val) + ((*q)->val > now->val) != 1)
        {
            ans = now;
            return;
        }
        if ((*p)->val < now->val)
            dfs(now->left, p, q);
        else
            dfs(now->right, p, q);
    }

public:
    TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q)
    {
        dfs(root, &p, &q);
        return ans;
    }
};

四十七、二叉树的最近公共祖先

题目

剑指 Offer 68 - II. 二叉树的最近公共祖先

解题思路

先遍历一次,把所有结点的父亲用哈希表存下来。
然后两个结点都向根节点移动,第一个相遇的节点就是LCA。

AC代码

class Solution
{

    unordered_map<TreeNode *, TreeNode *> father;
    unordered_map<TreeNode *, int> vis;
    TreeNode *ans;
    void dfs(TreeNode *now, TreeNode *fa)
    {
        father[now] = fa;
        if (now->left)
            dfs(now->left, now);
        if (now->right)
            dfs(now->right, now);
    }
    void f(TreeNode *now)
    {
        if (vis[now])
        {
            ans = now;
            return;
        }
        vis[now] = 1;
        f(father[now]);
    }

public:
    TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q)
    {
        dfs(root, root);
        f(p);
        f(q);
        return ans;
    }
};

四十八、重建二叉树

题目

剑指 Offer 07. 重建二叉树

给出先序遍历和中序遍历,求后序遍历。

解题思路

先序遍历顺序为[根结点][左子树][右子树]
中序遍历顺序为[左子树][根结点][右子树]

思路:

  1. 对于所有的子树,先序遍历的第一个结点为根结点。
  2. 找到中序遍历中的根结点,可以得到左子树和右子树的 size。
  3. 这样一直递归划分左右子树,递归到叶结点返回即可。

AC代码

class Solution
{
public:
       map<int, int> index;
       TreeNode *build(vector<int> &pre, vector<int> &in, int l1, int r1, int l2, int r2)
       {
              if (l1 > r1)
                     return nullptr;
              TreeNode *root = new TreeNode(pre[l1]);
              int pos = index[pre[l1]];
              int num = pos - 1 - l2 + 1;
              root->left = build(pre, in, l1 + 1, l1 + 1 + num - 1, l2, pos - 1);
              root->right = build(pre, in, l1 + 1 + num - 1 + 1, r1, pos + 1, r2);
              return root;
       }
       TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder)
       {
              int n = preorder.size();
              for (int i = 0; i < n; i++)
                     index[inorder[i]] = i;
              return build(preorder, inorder, 0, n - 1, 0, n - 1);
       }
};

四十九、数值的整数次方

题目

剑指 Offer 16. 数值的整数次方

解题思路

快速幂,注意n为负数的情况。

AC代码

class Solution
{
public:
       double myPow(double x, int n)
       {
           long long nn = n; 
              if (nn < 0)
              {
                     nn *= -1;
                     x = 1.0 / x;
              }
              double res = 1.0;
              while (nn)
              {
                     if (nn & 1)
                            res = res * x;
                     x *= x;
                     nn /= 2;
              }
              return res;
       }
};

五十、二叉搜索树的后序遍历序列

题目

剑指 Offer 33. 二叉搜索树的后序遍历序列

解题思路

后序遍历的遍历结构为[左子树][右子树][根结点]

二叉搜索树的特点是根结点大于左子树的所有值,小于右子树的所有值

AC代码

class Solution
{
public:
       bool check(vector<int> &a, int l, int r)
       {
              //         mid
              //          |
              // [左子树][右子树][根结点]
              if (l >= r)
                     return true;
              int mid = upper_bound(a.begin() + l, a.begin() + r, a[r]) - a.begin();
              for (int i = l; i <= mid - 1; i++) //左子树都小于根结点
                     if (a[i] > a[r])
                            return false;
              for (int i = mid; i < r; i++) //右子树都大于根结点
                     if (a[i] < a[r])
                            return false;
              if (!check(a, l, mid - 1) || !check(a, mid, r - 1)) //左右子树都要满足
                     return false;
              return true;
       }
       bool verifyPostorder(vector<int> &postorder)
       {
              return check(postorder, 0, postorder.size() - 1);
       }
};

五十一、二进制中1的个数

题目

剑指 Offer 15. 二进制中1的个数

解题思路

位运算。

AC代码

class Solution
{
public:
       int hammingWeight(uint32_t n)
       {
              int ans = 0;
              for (int i = 0; i <= 32; i++)
                     ans += (1ll << i) & n ? 1 : 0;
              return ans;
       }
};

五十二、不用加减乘除做加法

题目

剑指 Offer 65. 不用加减乘除做加法

解题思路

用 & 操作找出进位。
用 ^ 操作找出除去非进位的加和结果。

循环相加到无进位即可。

AC代码

class Solution
{
public:
       int add(int a, int b)
       {
              while (b)
              {
                     int c = ((unsigned int)(a & b) << 1);
                     a = (a ^ b);
                     b = c;
              }
              return a;
       }
};

五十三、数组中数字出现的次数

题目

剑指 Offer 56 - I. 数组中数字出现的次数

解题思路

考虑将两个数分成两组,并分别将两组值异或起来,即可得到两个数。

先将所有值进行异或,可以得到这两个数的异或值res。找到res为1的某个位,因为两个数异或值为1,那么必然有一个数这个位上为0,另一个数这个位上为1。按照这一位进行分组即可。

AC代码

class Solution
{
public:
       vector<int> singleNumbers(vector<int> &nums)
       {
              int res = 0;
              for (auto it : nums)
                     res ^= it;
              int id = 0, ans1 = 0, ans2 = 0;
              while (((1 << id) & res) == 0)
                     id++;
              for (auto it : nums)
              {
                     if ((1 << id) & it)
                            ans1 ^= it;
                     else
                            ans2 ^= it;
              }
              return vector<int>{ans1, ans2};
       }
};

五十四、数组中数字出现的次数 II

题目

剑指 Offer 56 - II. 数组中数字出现的次数 II

解题思路

把每个数每一位拆出来,出现次数不是3的倍数的位就是这个数的位。

tips:其实两个数的异或也是这个原理,把3改成2即可。

AC代码

class Solution
{
public:
       int singleNumber(vector<int> &nums)
       {
              int a[40];
              memset(a, 0, sizeof a);
              for (auto it : nums)
                     for (int i = 0; i <= 31; i++)
                            if ((1ll << i) & it)
                                   a[i]++;
              int ans = 0;
              for (int i = 0; i <= 31; i++)
                     if (a[i] % 3)
                            ans += (1ll << i);
              return ans;
       }
};

五十五、数组中出现次数超过一半的数字

题目

剑指 Offer 39. 数组中出现次数超过一半的数字

解题思路

按位拆分

AC代码

class Solution
{
public:
       int majorityElement(vector<int> &nums)
       {
              int a[40];
              memset(a, 0, sizeof a);
              for (auto it : nums)
                     for (int i = 0; i <= 31; i++)
                            if ((1ll << i) & it)
                                   a[i]++;
              int ans = 0;
              for (int i = 0; i <= 31; i++)
                     if (a[i] * 2 >= (int)nums.size())
                            ans += (1ll << i);
              return ans;
       }
};

五十六、构建乘积数组

题目

剑指 Offer 66. 构建乘积数组

解题思路

特判0即可。

AC代码

class Solution
{
public:
       vector<int> constructArr(vector<int> &a)
       {
              long long sum = 1, zero = 0, sum2 = 1;
              vector<int> ans(a.size());
              for (auto it : a)
              {
                     sum *= it;
                     if (it == 0)
                            ++zero;
                     else
                            sum2 *= it;
              }
              if (zero >= 2)
                     return ans;
              for (int i = 0; i < (int)a.size(); i++)
              {
                     if (!a[i])
                            ans[i] = sum2;
                     else
                            ans[i] = sum / a[i];
              }
              return ans;
       }
};

五十七、剪绳子

题目

剑指 Offer 14- I. 剪绳子

解题思路

猜想尽可能等分是最优的。

好像经过证明,最多分成三段。

AC代码

class Solution
{
public:
    int cuttingRope(int n)
    {
        long long ans = -1;
        for (int m = 2; m <= n; m++)
        {
            int len = n / m;
            int len2 = len;
            if (n % m)
                len2 = n / m + 1;
            long long sum = 1;
            for (int i = 1; i <= n % m; i++)
                sum *= len2;
            for (int i = 1; i <= m - n % m; i++)
                sum *= len;
            ans = max(ans, sum);
        }
        return ans;
    }
};

五十八、和为s的连续正数序列

题目

剑指 Offer 57 - II. 和为s的连续正数序列

解题思路

枚举起点。
可以解方程O1,数学不好放弃。

AC代码

class Solution
{
public:
    long long  f(int l, int r)
    {
        return 1ll*r * (r + 1) / 2 - 1ll*l * (l - 1) / 2;
    }
    vector<vector<int>> ans;
    void save(int l, int r)
    {
        vector<int> tmp;
        for (int i = l; i <= r; i++)
            tmp.emplace_back(i);
        ans.emplace_back(tmp);
    };
    vector<vector<int>> findContinuousSequence(int target)
    {
        for (int i = 1; i <= target / 2; i++)
        {
            int l = 1, r = target;
            while (l <= r)
            {
                int mid = (l + r) >> 1;
                if (f(i, i + mid) == target)
                {
                    save(i,i+mid);
                    break;
                }
                else if (f(i, i + mid) < target)
                    l = mid + 1;
                else
                    r = mid - 1;
            }
        }
        return ans;
    }
};

五十九、圆圈中最后剩下的数字

题目

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

解题思路

AC代码


六十、顺时针打印矩阵

题目

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

解题思路

模拟

AC代码

class Solution
{
public:
    int mov[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
    vector<int> ans;
    void print(vector<vector<int>> &matrix, int x, int y, int dir)
    {
        ans.emplace_back(matrix[x][y]);
        matrix[x][y] = -10086100;
        bool f = 1;
        int xx, yy;
        xx = x + mov[dir % 4][0];
        yy = y + mov[dir % 4][1];
        if (xx >= matrix.size() || yy >= matrix[0].size() || xx < 0 || yy < 0 || matrix[xx][yy] == -10086100)
        {
            dir++;
            xx = x + mov[dir % 4][0];
            yy = y + mov[dir % 4][1];
        } //最多转向一次
        if (xx >= matrix.size() || yy >= matrix[0].size() || xx < 0 || yy < 0 || matrix[xx][yy] == -10086100)
            return;
        print(matrix, xx, yy, dir);
    }
    vector<int> spiralOrder(vector<vector<int>> &matrix)
    {
        if (!matrix.size())
            return ans;
        print(matrix, 0, 0, 0);
        return ans;
    }
};

六十一、栈的压入、弹出序列

题目

剑指 Offer 31. 栈的压入、弹出序列

解题思路

模拟

AC代码


class Solution
{
public:
    bool validateStackSequences(vector<int> &pushed, vector<int> &popped)
    {
        stack<int> st;
        int n = pushed.size();
        int p = 0;
        for (int i = 0; i < n; i++)
        {
            st.push(pushed[i]);
            while (st.size() && st.top() == popped[p])
            {
                ++p;
                st.pop();
            }
        }
        if (st.size())
            return 0;
        return 1;
    }
};

六十二、表示数值的字符串

题目

剑指 Offer 20. 表示数值的字符串

解题思路

AC代码


六十三、把字符串转换成整数

题目

剑指 Offer 67. 把字符串转换成整数

解题思路

模拟

AC代码

class Solution
{
public:
    int strToInt(string str)
    {
        long long ans = 0;
        bool flag = 0, f = 0;
        for (auto it : str)
        {
            if (it == '-' && !f)
            {
                flag = 1;
                f = 1;
                continue;
            }
            if (it == '+' && !f)
            {
                flag = 0;
                f = 1;
                continue;
            }
            if (it == ' ')
            {
                if (f)
                {
                    if (flag && ans >= 2147483648)
                        return -2147483648;
                    if (!flag && ans >= 2147483647)
                        return 2147483647;
                    return ans * (flag ? -1 : 1);
                }
                continue;
            }
            f = 1;
            if (it <= '9' && it >= '0')
                ans = ans * 10 + it - '0', f = 1;
            else
                break;
            if (flag && ans >= 2147483648)
                return -2147483648;
            if (!flag && ans >= 2147483647)
                return 2147483647;
        }
        if (flag && ans >= 2147483648)
            return -2147483648;
        if (!flag && ans >= 2147483647)
            return 2147483647;
        return ans * (flag ? -1 : 1);
    }
};

六十四、滑动窗口的最大值

题目

剑指 Offer 59 - I. 滑动窗口的最大值

解题思路

单调队列。

如果队首的元素的距离和当前元素的距离超过窗口大小,应该弹出队列。
如果当前的元素大于队尾的元素,那么队尾的元素肯定不会作为最大值贡献了,也应该弹出队列。

AC代码

class Solution
{
public:
    void push(deque<int> &q, vector<int> &nums, int p, int k)
    {
        while (q.size() && q.front() + k - 1 < p)
            q.pop_front();
        while (q.size() && nums[p] >= nums[q.back()])
            q.pop_back();
        q.push_back(p);
    }
    int get(deque<int> &q, vector<int> &nums)
    {
        return nums[q.front()];
    }
    vector<int> maxSlidingWindow(vector<int> &nums, int k)
    {
        vector<int> ans;
        if (!nums.size())
            return ans;
        deque<int> q;
        for (int i = 0; i < k; i++)
            push(q, nums, i, k);
        ans.emplace_back(get(q, nums));
        for (int i = k; i < (int)nums.size(); i++)
        {
            push(q, nums, i, k);
            ans.emplace_back(get(q, nums));
        }
        return ans;
    }
};

六十五、队列的最大值

题目

剑指 Offer 59 - II. 队列的最大值

解题思路

维护单调队列

AC代码

class MaxQueue
{
public:
    deque<int> q;
    int cnt = 0, p = 0;
    vector<int> v;

    MaxQueue()
    {
    }

    int max_value()
    {
        if (!q.size())
            return -1;
        return v[q.front()];
    }

    void push_back(int value)
    {
        while (q.size() && v[q.back()] < value)
            q.pop_back();
        q.push_back(cnt++);
        v.emplace_back(value);
    }

    int pop_front()
    {
        if (!q.size())
            return -1;
        if (q.size() && q.front() <= p)
            q.pop_front();
        return v[p++];
    }
};

六十六、序列化二叉树

题目

剑指 Offer 37. 序列化二叉树

解题思路

(原来只要能解密出自己加密的字符串就行… …

把所有叶子结点悬挂两个null即可。

AC代码

class Codec
{
public:
    const int FUCK = -1008612345;
    // Encodes a tree to a single string.
    string ans;
    string atoi(int x)
    {
        string res = "";
        while (x)
        {
            res += '0' + x % 10;
            x /= 10;
        }
        reverse(res.begin(), res.end());
        return res;
    }
    void dfs(TreeNode *root)
    {
        ans += atoi(root->val) + ',';
        if (root->left)
            dfs(root->left);
        else
            ans += "null,";
        if (root->right)
            dfs(root->right);
        else
            ans += "null,";
    }
    string serialize(TreeNode *root)
    {
        ans.clear();
        if (root == nullptr)
            return "null,";
        dfs(root);
        return ans;
    }

    vector<int> vec;

    // Decodes your encoded data to tree.
    int p;
    TreeNode *build()
    {
        TreeNode *root = new TreeNode(vec[p++]);
        if (vec[p] != FUCK)
            root->left = build();
        else
            root->left = nullptr, ++p;

        if (vec[p] != FUCK)
            root->right = build();
        else
            root->right = nullptr, ++p;

        return root;
    }
    int itoa(string x)
    {
        if (x == "null")
            return FUCK;
        int res = 0;
        for (auto it : x)
            res = res * 10 + it - '0';
        return res;
    }
    TreeNode *deserialize(string data)
    {
        ans.clear();
        for (auto it : data)
        {
            if (it == ',')
            {
                vec.emplace_back(itoa(ans));
                ans.clear();
            }
            else
                ans += it;
        }
        if (vec[0] == FUCK)
            return nullptr;
        p = 0;
        return build();
    }
};

六十七、字符串的排列

题目

剑指 Offer 38. 字符串的排列

解题思路

事实证明,STL的next_permutation才是yyds。

AC代码

class Solution
{
public:
    vector<string> ans;
    string tmp;
    void dfs(string &s, int sum)
    {
        if (tmp.size() == s.size())
        {
            ans.emplace_back(tmp);
            return;
        }
        for (int i = 0; i < (int)s.size(); i++)
            if (s[i] != '#')
            {
                tmp += s[i];
                char tmp_char = s[i];
                s[i] = '#';
                dfs(s, sum + 1);
                tmp.pop_back();
                s[i] = tmp_char;
            }
    }
    vector<string> permutation(string s)
    {
        dfs(s, 0);
        sort(ans.begin(), ans.end());
        ans.erase(unique(ans.begin(), ans.end()), ans.end());
        return ans;
    }
};

六十八、正则表达式匹配

题目

剑指 Offer 19. 正则表达式匹配

解题思路

好恶心的题,情况太多了。
wa了七次,勉强算是ac吧。

AC代码

class Solution
{
public:
    void getmax(int &a, int &b) { a = max(a, b); }
    bool isMatch(string s, string p)
    {
        int n = s.size(), m = p.size();
        s = '#' + s, p = '#' + p;
        int dp[n + 5][m + 5];
        memset(dp, 0, sizeof dp);
        dp[0][0] = 1;
        for (int i = 0; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                if (i >= 1 && p[j] == '.' || p[j] == s[i])
                {
                    getmax(dp[i][j], dp[i - 1][j - 1]); //匹配1次
                    if (j >= 3 && p[j - 1] == '*')
                        getmax(dp[i][j], dp[i - 1][j - 3]); //
                }
                else if (p[j] == '*')
                {
                    if (p[j - 1] == '.') //.*组合
                    {
                        if (i >= 1)
                        {

                            getmax(dp[i][j], dp[i - 1][j - 1]); //
                            getmax(dp[i][j], dp[i - 1][j]);     //
                        }
                        if (j >= 2)
                            getmax(dp[i][j], dp[i][j - 2]); //
                        getmax(dp[i][j], dp[i][j - 1]);     //
                    }
                    else
                    {
                        if (j >= 2)
                            getmax(dp[i][j], dp[i][j - 2]); //*匹配0次
                        if (i >= 1 && s[i] == p[j - 1])
                            getmax(dp[i][j], dp[i - 1][j]);       //*匹配1次 a*匹配a
                        if (i >= 1 && j >= 2 && p[j - 1] == s[i]) //*匹配至少一次
                        {
                            getmax(dp[i][j], dp[i - 1][j - 1]);
                        }
                    }
                }
            }
        return dp[n][m];
    }
};

六十九、丑数

题目

剑指 Offer 49. 丑数

解题思路

简单思路:堆维护

优秀的思路:动态规划,dp[i]表示第i个丑数,一个丑数肯定是由前一个数乘2/3/5得来的,并且dp[i-1]是正确的,那么dp[i]一定要大于dp[i-1]。

AC代码

class Solution
{
public:
    vector<int> ans;
    int nthUglyNumber(int n)
    {
        vector<int> dp(n + 5);
        dp[1] = 1;
        int p1, p2, p3;
        p1 = p2 = p3 = 1;
        for (int i = 2; i <= n; i++)
        {
            if (dp[p1] * 2 <= dp[i - 1])
                p1++;
            if (dp[p2] * 3 <= dp[i - 1])
                p2++;
            if (dp[p3] * 5 <= dp[i - 1])
                p3++;
            dp[i] = min({dp[p1] * 2, dp[p2] * 3, dp[p3] * 5});
        }
        return dp[n];
    }
};

七十、n个骰子的点数

题目

剑指 Offer 60. n个骰子的点数

解题思路

简单的动态规划。可以用滚动数组优化。

AC代码

class Solution
{
public:
    vector<double> dicesProbability(int n)
    {
        double dp[n + 5][6 * n + 5];
        memset(dp, 0, sizeof dp);
        int maxx = 6 * n;
        const double fuck = 1.0 / 6;
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++)
            for (int k = 1; k <= maxx; k++)
                for (int j = 1; j <= 6; j++)
                    if (k - j >= 0)
                        dp[i][k] += dp[i - 1][k - j] * fuck;
        vector<double> ans;
        for (int k = n; k <= maxx; k++)
            ans.emplace_back(dp[n][k]);
        return ans;
    }
};

七十一、打印从1到最大的n位数

题目

剑指 Offer 17. 打印从1到最大的n位数

解题思路

据说应该考虑大数。

AC代码

class Solution
{
public:
    vector<int> printNumbers(int n)
    {
        int maxx = pow(10, n);
        vector<int> ans;
        for (int i = 1; i < maxx; i++)
            ans.emplace_back(i);
        return ans;
    }
};

七十二、数组中的逆序对

题目

剑指 Offer 51. 数组中的逆序对

解题思路

树状数组求逆序对板子题。

AC代码


class Solution
{
public:
    int c[(int)5e4 + 5];
    int maxx;
    void insert(int i)
    {
        while (i <= maxx)
        {
            c[i]++;
            i += (i) & (-i);
        }
    }
    int getsum(int i)
    {
        int sum = 0;
        while (i)
        {
            sum += c[i];
            i -= (i) & (-i);
        }
        return sum;
    }
    int reversePairs(vector<int> &nums)
    {
        vector<int> vec;
        for (auto it : nums)
            vec.emplace_back(it);
        sort(vec.begin(), vec.end());
        maxx = unique(vec.begin(), vec.end()) - vec.begin();
        for (int i = 0; i < (int)nums.size(); i++)
            nums[i] = lower_bound(vec.begin(), vec.begin() + maxx, nums[i]) - vec.begin() + 1;
        int ans = 0;
        for (auto it : nums)
        {
            if (it < maxx)
                ans += getsum(maxx) - getsum(it);
            insert(it);
        }
        return ans;
    }
};

七十三、剪绳子 II

题目

剑指 Offer 14- II. 剪绳子 II

解题思路

有个结论,将绳子尽可能切成若干个3的长度,如果最后剩余1, 2 × 2 > 1 × 3 2\times2 > 1\times3 2×2>1×3,因此将一个3分成2更优。

(具体证明还没看)

AC代码

class Solution
{
public:
    const long long mod = 1e9 + 7;
    long long qpow(long long a, long long b)
    {
        long long res = 1;
        while (b)
        {
            if (b & 1)
                res = res * a % mod;
            b /= 2;
            a = a * a % mod;
        }
        return res;
    }
    int cuttingRope(int n)
    {
        if (n <= 3)
            return n - 1;
        if (n % 3 == 0)
            return qpow(3, n / 3);
        else if (n % 3 == 1)
            return 4ll * qpow(3, (n / 3) - 1) % mod;
        else
            return 2ll * qpow(3, n / 3) % mod;
    }
};

七十四、1~n 整数中 1 出现的次数

题目

剑指 Offer 43. 1~n 整数中 1 出现的次数

解题思路

数位DP。对!limit进行记忆化,会更快。

AC代码

class Solution
{
public:
    int vis[20][20];
    int dfs(int a[], int pos, int s, bool limit)
    // 第pos位、 前面总共出现了多少个1 、是否状态有限制
    {
        if (!pos)
            return s;
        if (vis[pos][s] && !limit)
            return vis[pos][s];
        int sum = 0;
        for (int i = 0; i <= (limit ? a[pos] : 9); i++)
            sum += dfs(a, pos - 1, s + (i == 1), limit && a[pos] == i);
        if (!limit)
            return vis[pos][s] = sum;
        else
            return sum;
    }
    int countDigitOne(int n)
    {
        int a[20];
        int pos = 0;
        while (n)
        {
            a[++pos] = n % 10;
            n /= 10;
        }
        return dfs(a, pos, 0, 1);
    }
};

七十五、数字序列中某一位的数字

题目

剑指 Offer 44. 数字序列中某一位的数字

解题思路

AC代码


待完成:

  1. 五十九:约瑟夫环
  2. 六十二:字符串数值类型判读
  3. 七十五:数字序列中某一位

你可能感兴趣的:(#,剑指Offer,leetcode,算法)