剑指offer
ChasonPc
-
展开
-
完全二叉树的最后一个节点
题目:给定一棵完全二叉搜索树,返回最后一个最右边的节点。如下图,则返回8。 5 / \ 3 7 / \ / \2 4 6 8解析:方法一: 递归,求子树的高度:如果左子树高度>右子树高度,则在左子树继续递归过程;否则在右子树继续递归。如果当前节点为叶子节点,则返回;由于是完全二叉树,求高度时只需一直往左遍历即可。每次递归都下降一层,每次都求树的高度。时间复杂度为O(lgN * lgN)。参考答案:/**** typedef struct原创 2020-08-04 23:03:21 · 2115 阅读 · 1 评论 -
判断链表是否有环
题目:给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。输入:head = [3,2,0,-4], pos = 1输出:true解释:链表中有一个环,其尾部连接到第二个节点。解析:双指针解法(快慢指针)。参考答案:/*struct ListNode { int val; struct ListNode *next;};*/class S原创 2020-08-04 16:06:40 · 109 阅读 · 0 评论 -
删除链表中重复的结点
题目:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5。输入: 1 2 3 3 4 4 5输出: 1 2 5解析:双指针解法。参考答案:/*struct ListNode { int val; struct ListNode *next;};*/class Solution {public:原创 2020-07-23 16:07:29 · 81 阅读 · 0 评论 -
礼物的最大价值(一维动态规划&二维动态规划)
题目:在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?输入: [ [1,3,1], [1,5,1], [4,2,1]]输出: 12解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物解析:方法一: 动态规划(二维)。动归解题的三大步骤,第一步,列出确定状态dp[i][j]表示到达i原创 2020-06-24 12:13:05 · 208 阅读 · 0 评论 -
最长不含重复字符的子字符串
题目:请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。输入: "abcabcbb"输出: 3解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。输入: "bbbbb"输出: 1解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。解析:方法一: 双指针的滑动窗口解法。方法二: 对方法一进行优化。参考答案:class Solution{public: int lengthOfLongestSubstring(string s)原创 2020-06-22 12:00:18 · 184 阅读 · 0 评论 -
队列的最大值
题目:请定义一个队列并实现函数max_value得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。若队列为空,pop_front 和max_value需要返回-1输入: ["MaxQueue","push_back","push_back","max_value","pop_front","max_value"][[],[1],[2],[],[],[]]输出: [null,null,null,2,1,2]输入: ["M原创 2020-06-20 22:18:21 · 517 阅读 · 0 评论 -
股票的最大利润
题目:假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?输入: [7,1,5,3,6,4]输出: 5解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。(注意:利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。)输入: [7,6,4,3,1]输出: 0解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。解析:如果使用暴力求解即两层for循环找到最大值和原创 2020-06-18 11:39:50 · 328 阅读 · 0 评论 -
构建乘除数组
题目:给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B 中的元素 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。输入: [1, 2, 3, 4, 5]输出: [120, 60, 40, 30, 24]解析:本题目的是求数组中除了当前元素的所有元素的乘积。解题思路:对称遍历。参考答案:class Solution{public: vector<int> constructArr(vecto原创 2020-06-18 10:30:20 · 130 阅读 · 0 评论 -
二叉树(或二叉搜索树)的最近公共祖先
题目:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。例如,给定如下二叉搜索树: root = [6, 2, 8, 0, 4, 7, 9, null, null, 3, 5]输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1输出: 3解释: 节点 5 和节点 1 的最近公共祖先是节点 3。输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4输出: 5解释: 节点原创 2020-06-17 10:49:19 · 284 阅读 · 0 评论 -
二叉树搜索树的最近公共祖先
题目:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。例如,给定如下二叉搜索树: root = [6, 2, 8, 0, 4, 7, 9, null, null, 3, 5]输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8输出: 6 解释: 节点 2 和节点 8 的最近公共祖先是 6。输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4输出: 2解释: 节点 2原创 2020-06-17 10:03:01 · 161 阅读 · 0 评论 -
把字符串转换成整数
题目:写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。要求: 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。注意:假如该字符原创 2020-06-16 12:08:29 · 502 阅读 · 0 评论 -
不用加减乘除做加法
题目:写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。输入: a = 1, b = 1输出: 2解析:如果碰见类似的题目,首先就要想到位于运算求解。先求两个数的不进位加法(异或),再求两个数进位值(与运算并左移一位)。直接看代码,简单易懂。参考答案:class Solution{public: int add(int a, int b){ int sum = 0, carry = 0; while(b != 0){ //su原创 2020-06-14 10:40:08 · 82 阅读 · 0 评论 -
求1+2+...+n
题目:求1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。输入: n = 3输出: 6输入: n = 9输出: 45解析:使用递归算法和位运算来结束递归终止条件。参考答案:class Solution{public: int sumNums(int n){ n && (n += sumNums(n - 1)); return n; }};...原创 2020-06-14 10:29:40 · 161 阅读 · 0 评论 -
圆圈中最后剩下的数字
题目:0, 1, ... , n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。输入: n = 5, m = 3输出: 3输入: n = 10, m = 17输出: 2解析:主要考察数学思维能力。解法一: 递归算法。解法一: 迭代算法。参考答案:class Solution{p原创 2020-06-12 11:01:39 · 94 阅读 · 0 评论 -
扑克牌中的顺子
题目:从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。输入: [1, 2, 3, 4, 5]输出: true输入: [0, 0, 1, 2, 5]输出: true解析:先对数组排序,统计出0的个数,然后从0后面的数遍历数组,如果相邻两个数相同的(扑克中成为对子),则直接返回false。如果相邻元素不相等,则相减之后和0的个数比较,若大于0的个数返回fals原创 2020-06-11 10:54:14 · 428 阅读 · 0 评论 -
n个骰子的点数
题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。输入: 1输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]输入: 2输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.0原创 2020-06-07 12:09:40 · 132 阅读 · 0 评论 -
左旋转字符串
题目:字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。输入: s = "abcdefg", k = 2输出: "cdefgab"解析:方法一:利用数学取余的思维求解,直接看代码,简单易懂。方法二:使用工具直接截取(有点投机取巧,不适合面试回答)。参考答案:class Solution{ string reverseLeftWords(s原创 2020-06-06 10:29:23 · 207 阅读 · 0 评论 -
旋转单词顺序
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/fan-zhuan-dan-ci-shun-xu-lcof著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。输入:"the sky is blue"输出:"blue is sk原创 2020-06-05 09:52:36 · 210 阅读 · 0 评论 -
和为s的连续正数序列
题目二:输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数)。例如输入15,由于1+2+3+4+5 = 4+5+6 = 7+8 = 15,所以结果打印出3个连续序列{1, 2, 3, 4, 5}, {4, 5, 6}, {7, 8}。输入:target = 15输出:[[1, 2, 3, 4, 5 ], [4, 5, 6], [7, 8]]解析:利用滑动窗口方法求解。定义两个指针i和j,指向第一个元素1,这两个指针指向的元素之和sum,如果sum小于目标值target,则j向右移动,原创 2020-06-04 11:06:15 · 129 阅读 · 0 评论 -
和为s的两个数字
题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。输入:nums = {2, 7, 11, 15},target = 9输出:[2, 7] 或者 [7, 2]解析:双指针法。一个指针L第一个元素,另一个指针R指向最后一个元素。如果两个指针指向的元素之和大于s,则把R指针向左移动,如果小于s,则把L指针向右移动,如果等于s,则输出L和R指向的元素。参考答案:class Solution{ vector<in原创 2020-06-03 21:45:51 · 130 阅读 · 0 评论 -
数组中数字出现的次数
题目:一个整型数组里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。解析:利用位运算的异或关系求解,把两个只出现一次的数分别放入两个数组中(有出现两次的必须放在一个数组),再对这两个数组分别使用异或,即可求出这个两个只出现一次的数。参考答案:class Solution{public: vector<int> singleNumbers(vector<int> nums){ int s = 0原创 2020-06-02 09:28:16 · 462 阅读 · 0 评论 -
平衡二叉树的判断
题目:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。解析:递归,深度遍历(dfs),比较左右子树高度差是否不大于1(即小于等于1)。参考答案:/**** struct TreeNode{* int val;* TreeNode *left;* TreeNode *right;* }*/class Solution{public: bool isBalanced(TreeNode *root){原创 2020-06-01 09:08:38 · 242 阅读 · 0 评论 -
二叉树的深度(DFS)
题目:输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。解析:递归,深度遍历(dfs)。参考答案:/**** struct TreeNode{* int val;* TreeNode *left;* TreeNode *right;* };*/class Solution{public: int maxDepth(TreeNode *root){ if(root == NULL) ret原创 2020-05-31 23:50:46 · 379 阅读 · 0 评论 -
数字在排序数组中出现的次数
题目:统计一个数字在排序数组中出现的次数。例如输入排序数组{1, 2, 3, 3, 3, 3, 4, 5}和数字3,由于3在这个数组中出现了4次,因此输出4。解析:两种解法:方法一:哈希表。方法二:二分查找。参考答案:class Solution{ //方法一:哈希表 int search(vector<int> &nums, int target){ if(nums.empty()) return 0; unordered_map<int, int原创 2020-05-29 22:14:13 · 110 阅读 · 0 评论 -
两个链表的第一个公共节点
题目:输入两个链表,找出它们的第一个公共节点。如上图A链表和B链表的公共节点为8。解析:分别采用两种方法:方法一:即剑指解法,计算出两个链表的长度,然后让较长的链表先走长出来的部分,然后两个链表同时走,两个链表第一次相遇的节点即为所求。方法二:双指针法,定义两个指针,node1和node2,分别指向链表headA和链表headB的头结点,然后分别开始逐个遍历,如果node1先走到headA的尾结点,则重新定位到headB的头结点。如果node2先走到headB的尾结点,则重新定位到headA的原创 2020-05-28 22:55:01 · 128 阅读 · 0 评论 -
数组中的逆序对
题目:在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。解析:使用归并排序来求解,只需加一个统计出现逆序对次数的条件。参考答案:class Solution{public: int count = 0; void mergeSort(vector<int>& nums, vector<int>& temp, int L, int R){ if(L >= R) ret原创 2020-05-27 12:33:46 · 170 阅读 · 0 评论 -
第一个出现一次的字符
题目:在字符串中找出第一个只出现一次的字符。如输入"abaccdeff",则输出'b'。解析:利用hash表的原理。开一个数组,设下标为字符,数组为相应字符出现的次数,最后遍历数组中为1的下标即可。参考答案:class Solution{public: char firstUniqChar(string s){ if(s.empty()) return ' '; vector<int> arr(128, 0); for(int i = 0; i < s.si原创 2020-05-26 12:10:06 · 127 阅读 · 0 评论 -
丑数
题目:我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。例如6、8都是丑数,但14不是,因为他包含因子7。习惯上我们把1当做第一个丑数。解析:普通方法例如依次判断每一个数是否为丑数,在进行统计,这样时间复杂度很高。因为每一个丑数都是要乘2,3或5。这时候为了避免重复判断丑数,则可以利用动态规划解决此问题,这样就能把时间复杂度降低到O(n)。参考答案:class Solution{public: int nthUglyNumber(in原创 2020-05-24 22:39:16 · 101 阅读 · 0 评论 -
把数组排成最小的数
题目:输入一个正整数数组,把数组里所有数组拼接起来排成一个数,打印能拼接出的数字中最小的一个。例如输入数组{3, 32, 321},则打印出这3个数字能排成的最小数字321323。解析:首先把数组转换成字符串数组,进行字符串自定义排序,使用lambda表达式进行自定义排序,auto Lambda = [](类型 变量, 类型 变量) {return 排序条件;};,就能自动推导出排序规则。参考答案:class Solution{public: string minNumber(vector&l原创 2020-05-23 22:48:17 · 102 阅读 · 0 评论 -
从1到n整数中1出现的次数
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1数字有1、10、11和12,1一共出现了5次。解析:因为有时间复杂度的限制,所以不能直接暴力求解。通过分析数据规律,把n分为低位low,当前位cur,高位hight,i代表需要统计的位置数,count为计数器,统计1出现的次数。然后分别从右往左对每一位进行公式推导,公式如下:cur = 0, count = hight * i; cur = 1, count = hight * i +原创 2020-05-22 20:21:31 · 171 阅读 · 0 评论 -
最小的k个数
题目:输入n个整数,找出其中最小的k个数。例如输入{4, 5, 1, 6, 2, 7, 3, 8}这8个数字,则最小的4个数字是{1, 2, 3, 4}。解析:此题可以用三种方法进行求解,1、排序后输出前k个元素。2、利用快速排序中partition的方法确定k的位置,然后输出k之前的元素。3、大顶堆维护前k个元素,接下来把k之后的元素分别与堆顶比较,进行替换。c++中的堆(优先队列)即为大顶堆。(第3种方法的优点是不会改变原数组的元素顺序)参考答案:class Solution{public:原创 2020-05-21 14:28:11 · 105 阅读 · 0 评论 -
数组中出现数字超过一半的数字
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, 4, 2}。 由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。解析:此题可以用三种方法进行求解,1、排序后中间元素一定是出现次数超过数组中一半的元素。2、哈希表求解。3、超过一半元素的次数要比其他所有元素总和的次数还要多。参考答案:class Solution{public: int majorityElement(vector<int&原创 2020-05-20 10:58:18 · 213 阅读 · 0 评论 -
字符串的全排列
题目:输入一个字符串,打印出该字符串中字符的全排列。例如输入字符串abc,则打印出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab、cba。解析:剑指的解法中没有去重操作,本题进行去重处理。解题主要思路就是回溯算法,话不多说,直接上代码,简单粗暴。参考答案:class Solution{public: vector<string> res; vector<string> permutation(string s){ dfs(s, 0)原创 2020-05-19 12:03:40 · 139 阅读 · 0 评论 -
二叉搜索树与双向链表
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。 4 / \ 2 5 / \ 1 3则输出为:解析:剑指的解法稍微有点麻烦,本题使用容器vector求解。首先使用中序遍历把每一个节点放进vector容器中,最后把容器前后互相连接即可,话不多说,直接上代码,简单粗暴且易懂。参考答案:/**** typedef struct no原创 2020-05-18 10:53:45 · 74 阅读 · 0 评论 -
二叉树中和为某一值的路径
题目:输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。 5 / \ 4 8 / / \ 11 13 4 / \ / \ 7 2 5 1假设目标和为22,则输出路径为[5, 4, 11, 2 ]和[5, 8, 4, 5]。解析:先序遍历变形和DFS,话不多说,直接上代码,递原创 2020-05-08 22:45:13 · 101 阅读 · 0 评论 -
二叉搜索树的后序遍历序列
题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。 5 / \ 2 6 / \ 1 3 解析:二叉搜索树中,后序遍历的最后一个值为根结点,左子树的值都比根结点小,右子树的值都比根结点大,然后分别对左右子树进行递归操作即可。参考答案:class Solu...原创 2020-05-07 22:44:13 · 95 阅读 · 0 评论 -
从上到下打印二叉树(二叉树层序遍历)
题目:从上到下打印出二叉树的每一个节点,同一层的节点按照从左到右的顺序打印。如下打印出来的结果为[3, 9, 20, 16, 15, 7]。 3 / \ 9 20 / / \16 15 7解析:利用队列的先入先出思想来求解,先把根节点放入队列中的队首,如果有子结点,则把子结点依次(先左儿子后右儿子)放入队尾,然后弹出队首元素。接下来重复前面的操作,直到...原创 2020-05-06 22:09:44 · 244 阅读 · 0 评论 -
栈的压入、弹出序列
题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。解析:先新建一个栈,把第一个序列逐个压入栈中,当压入的元素等于第二个序列元素时,则弹出该值,当不相...原创 2020-05-05 11:19:45 · 330 阅读 · 0 评论 -
包含min函数的栈
题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数在该栈中,调用min、push、及pop的时间复杂度为O(1)。解析:创建两个栈,一个为数据栈s_data,一个为最小栈s_min,接下来往栈里面放数据,第一个元素分别放入两个栈,接下来的数据先放入数据栈s_data,然后比较当前元素和上一个元素,如果此元素大于先前元素,则往最小站s_min中放入自己的栈顶元素,如果...原创 2020-05-03 10:40:18 · 85 阅读 · 0 评论 -
二叉树的镜像
题目:请完成一个函数,输入一个二叉树,该函数输出它的镜像。解析:经典递归(剑指上的解):二叉树的镜像就是通过交换所有结点的左右子树而来的,所以利用递归思想便可求解。参考答案:/*** * typedef struct node{* int m_Value;* struct node *m_Left;* struct node *m_Right;* }TreeNode;...原创 2020-05-02 09:32:37 · 131 阅读 · 0 评论