剑指 Offer(第 2 版)完整题解笔记 & C++代码实现(LeetCode版)

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指 Offer(第 2 版)完整题解笔记 & C++代码实现(LeetCode版)相关的知识,希望对你有一定的参考价值。

文章目录

原书目录

剑指 Offer 03. 数组中重复的数字
剑指 Offer 04. 二维数组中的查找
剑指 Offer 05. 替换空格
剑指 Offer 06. 从尾到头打印链表
剑指 Offer 07. 重建二叉树
剑指 Offer 09. 用两个栈实现队列
剑指 Offer 10- I. 斐波那契数列
剑指 Offer 10- II. 青蛙跳台阶问题
剑指 Offer 11. 旋转数组的最小数字
剑指 Offer 12. 矩阵中的路径
剑指 Offer 13. 机器人的运动范围
剑指 Offer 14- I. 剪绳子
剑指 Offer 14- II. 剪绳子 II
剑指 Offer 15. 二进制中1的个数
剑指 Offer 16. 数值的整数次方
剑指 Offer 17. 打印从1到最大的n位数
剑指 Offer 18. 删除链表的节点
剑指 Offer 19. 正则表达式匹配
剑指 Offer 20. 表示数值的字符串
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
剑指 Offer 22. 链表中倒数第k个节点
剑指 Offer 24. 反转链表
剑指 Offer 25. 合并两个排序的链表
剑指 Offer 26. 树的子结构
剑指 Offer 27. 二叉树的镜像
剑指 Offer 28. 对称的二叉树
剑指 Offer 29. 顺时针打印矩阵
剑指 Offer 30. 包含min函数的栈
剑指 Offer 31. 栈的压入、弹出序列
剑指 Offer 32 - I. 从上到下打印二叉树
剑指 Offer 32 - II. 从上到下打印二叉树 II
剑指 Offer 32 - III. 从上到下打印二叉树 III
剑指 Offer 33. 二叉搜索树的后序遍历序列
剑指 Offer 34. 二叉树中和为某一值的路径
剑指 Offer 35. 复杂链表的复制
剑指 Offer 36. 二叉搜索树与双向链表
剑指 Offer 37. 序列化二叉树
剑指 Offer 38. 字符串的排列
剑指 Offer 39. 数组中出现次数超过一半的数字
剑指 Offer 40. 最小的k个数
剑指 Offer 41. 数据流中的中位数
剑指 Offer 42. 连续子数组的最大和
剑指 Offer 43. 1~n 整数中 1 出现的次数
剑指 Offer 44. 数字序列中某一位的数字
剑指 Offer 45. 把数组排成最小的数
剑指 Offer 46. 把数字翻译成字符串
剑指 Offer 47. 礼物的最大价值
剑指 Offer 48. 最长不含重复字符的子字符串
剑指 Offer 49. 丑数
剑指 Offer 50. 第一个只出现一次的字符
剑指 Offer 51. 数组中的逆序对
剑指 Offer 52. 两个链表的第一个公共节点
剑指 Offer 53 - I. 在排序数组中查找数字 I
剑指 Offer 53 - II. 0~n-1中缺失的数字
剑指 Offer 54. 二叉搜索树的第k大节点
剑指 Offer 55 - I. 二叉树的深度
剑指 Offer 55 - II. 平衡二叉树
剑指 Offer 56 - I. 数组中数字出现的次数
剑指 Offer 56 - II. 数组中数字出现的次数 II
剑指 Offer 57 - II. 和为s的连续正数序列
剑指 Offer 57. 和为s的两个数字
剑指 Offer 58 - I. 翻转单词顺序
剑指 Offer 58 - II. 左旋转字符串
剑指 Offer 59 - I. 滑动窗口的最大值
剑指 Offer 59 - II. 队列的最大值
剑指 Offer 60. n个骰子的点数
剑指 Offer 61. 扑克牌中的顺子
剑指 Offer 62. 圆圈中最后剩下的数字
剑指 Offer 63. 股票的最大利润
剑指 Offer 64. 求1+2+…+n
剑指 Offer 65. 不用加减乘除做加法
剑指 Offer 66. 构建乘积数组
剑指 Offer 67. 把字符串转换成整数
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
剑指 Offer 68 - II. 二叉树的最近公共祖先

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

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

限制:

2 <= n <= 100000

class Solution 
public:
    int findRepeatNumber(vector<int>& nums) 
        set<int>se;
        int ok = nums[0];
        for(int x : nums)
            if(se.count(x))
                ok = x;
                break;
            
            se.insert(x);
        
        return ok;
    
;

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

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。

给定 target = 20,返回 false。

限制:

0 <= n <= 1000

0 <= m <= 1000

class Solution 
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) 
        for(const auto& row : matrix)
            auto it = lower_bound(row.begin(),row.end(),target);
            if(it!=row.end() && *it==target)
                return true;
            
        
        return false;
    
;

剑指 Offer 05. 替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例 1:

输入:s = “We are happy.”
输出:“We%20are%20happy.”

限制:

0 <= s 的长度 <= 10000

class Solution 
public:
    string replaceSpace(string s) 
        string t = "";
        for(char ch : s)
            if(ch!=' ')t += ch;
            else t += "%20";
        
        return t;
    
;

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

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

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

限制:

0 <= 链表长度 <= 10000

/**
 * Definition for singly-linked list.
 * struct ListNode 
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) 
 * ;
 */
class Solution 
public:
    vector<int> reversePrint(ListNode* head) 
        vector<int>res;
        ListNode* cur = head;
        while(cur)
            res.push_back(cur->val);
            cur = cur->next;
        
        reverse(res.begin(), res.end());
        return res;
    
;

剑指 Offer 07. 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

示例 1:

Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
示例 2:

Input: preorder = [-1], inorder = [-1]
Output: [-1]

限制:

0 <= 节点个数 <= 5000

/**
 * Definition for a binary tree node.
 * struct TreeNode 
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) 
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) 
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) 
 * ;
 */
class Solution 
public:
    unordered_map<int, int> index;
    TreeNode * build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir)
        if(pl > pr)return nullptr;
        //根节点
        int prt = pl;
        int irt = index[preorder[prt]];
        //
        TreeNode* root = new TreeNode(preorder[prt]);
        int lsz = irt-il;
        root->left = build(preorder, inorder, pl+1,pl+lsz, il, irt-1);
        root->right = build(preorder, inorder, pl+lsz+1,pr, irt+1,ir);
        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 09. 用两个栈实现队列

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:

输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
提示:

1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用

class CQueue 
public:
    stack<int>in, out;

    CQueue() 
    
    void appendTail(int value) 
        in.push(value);
    
    
    int deleteHead() 
        if(out.empty())
            if(in.empty())return -1;
            else
                while(!in.empty())
                    out.push(in.top());  in.pop();
                
            
        
        int x = out.top();
        out.pop();
        return x;
    
;

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue* obj = new CQueue();
 * obj->appendTail(value);
 * int param_2 = obj->deleteHead();
 */

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

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:1
示例 2:

输入:n = 5
输出:5

提示:

0 <= n <= 100

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

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

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:2
示例 2:

输入:n = 7
输出:21
示例 3:

输入:n = 0
输出:1
提示:

0 <= n <= 100

class Solution 
public:
    int mod = 1e9+7;
    int numWays(int n) 
        if(n<=1)return 1;
        vector<int>f(n+1,0);
        f[0] = 1;  f[1] = 1;
        for(int i = 2; i <= n; i++)
            f[i] = (f[i-1]+f[i-2])%mod;
        
        return f[n];
    
;

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

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。

注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。

示例 1:

输入:numbers = [3,4,5,1,2]
输出:1
示例 2:

输入:numbers = [2,2,2,0,1]
输出:0

提示:

n == numbers.length
1 <= n <= 5000
-5000 <= numbers[i] <= 5000
numbers 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

class Solution 
public:
    int minArray(vector<int>& numbers) 
        int l = 0, r = numbers.size()-1;
        while(l < r)
            int mid = l+r>>1;
            if(numbers[r]>numbers[mid])
                r = mid;
            else if(numbers[r]<numbers[mid])
                l = mid+1;
            else r--;
        
        return numbers[l];
    
;

剑指 Offer 12. 矩阵中的路径

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

例如,在下面的 3×4 的矩阵中包含单词 “ABCCED”(单词中的字母已标出)。

示例 1:

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:

输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false

提示:

m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board 和 word 仅由大小写英文字母组成

class Solution 
public:
    //check(i,j,k)表示从(i,j)出发,能否搜索到单词word[k..n]
    bool check(vector<vector<char>>&board, string& word, vector<vector<int>>&vis, int i, int j, int k)
        int n = board.size(), m = board[0].size();
        if(board[i][j] != word[k])return false;
        else if(k==word.size()-1)return true;
        vis[i][j] = true;
        vector<pair<int,int> >d0,1,0,-1,1,0,-1,0;
        bool ok = false;
        for(auto dd : d)
            int ni = i+dd.first, nj = j+dd.second;
            if(ni>=0&&ni<n&&nj>=0&&nj<m&&!vis[ni][nj])
                if(check(board,word,vis,ni,nj,k+1))
                    ok = true;
                    break;
                
            
        
        vis[i][j] = false;
        return ok;
    
    bool exist(vector<vector<char>>& board, string word) 
        int n = board.size(), m = board[0].size();
        vector<vector<int>> vis(n, vector<int>(m));
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
                if(check(board,word,vis,i,j,0))
                    return true;
                
            
        
        return false;
    
;

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

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

示例 1:

输入:m = 2, n = 3, k = 1
输出:3
示例 2:

输入:m = 3, n = 1, k = 0
输出:1
提示:

1 <= n,m <= 100
0 <= k <= 20

class Solution 
public:
    int get(int x)
        int res = 0;
        while(x) res += x%10;  x /= 10; 
        return res;
    
    int movingCount(int m, int n, int k) 
        if(!k)return 1;
        vector<vector<int>>vis(m,vector<int>(n,0));
        vis[0][0] = 1;
        int ans = 1;
        for(int i = 0; i < m; i++《剑指offer刷题笔记》33之字形打印二叉树c++详细题解

乱序版 ● 剑指offer每日算法题打卡题解—— 栈 (题号9,6,30)

算法---- Leetcode剑指offer-Java版题解

算法---- Leetcode剑指offer-Java版题解

《剑指Offer:专项突破版》 - 数组部分 JavaScript 题解

《剑指Offer:专项突破版》 - 哈希表部分 JavaScript 题解