剑指Offer
- 面试题46. 把数字翻译成字符串
- 面试题47. 礼物的最大价值
- 面试题48. 最长不含重复字符的子字符串
- 面试题49. 丑数
- 面试题50. 第一个只出现一次的字符
- 面试题51. 数组中的逆序对
- 面试题52. 两个链表的第一个公共节点
- 面试题53 - I. 在排序数组中查找数字 I
- 面试题53 - II. 0~n-1中缺失的数字
- 面试题54. 二叉搜索树的第k大节点
- 面试题55 - I. 二叉树的深度
- 面试题55 - II. 平衡二叉树
- 面试题56 - I. 数组中数字出现的次数
- 面试题56 - II. 数组中数字出现的次数 II
- 面试题57.和为s的两个数字
- 面试题57 - II. 和为s的连续正数序列
- 面试题58 - I. 翻转单词顺序
- 面试题58 - II. 左旋转字符串
- 面试题59 - I. 滑动窗口的最大值
- 面试题59 - II. 队列的最大值
- 面试题60. n个骰子的点数
- 面试题61. 扑克牌中的顺子
- 面试题62. 圆圈中最后剩下的数字
- 面试题63. 股票的最大利润
- 面试题64. 求1+2+…+n
- 面试题65. 不用加减乘除做加法
- 面试题66. 构建乘积数组
- 面试题68 - I. 二叉搜索树的最近公共祖先
- 面试题68 - II. 二叉树的最近公共祖先
面试题46. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
思路:动态规划,对于一个数num[i]我们有两种选择:
1.只翻译自己;
2.和前面的数字组合翻译,前提是组合的数在10-25;
1.如果只翻译自己如12258,本来有5种翻译方法,如果8单独翻译则还是有5种翻译方法,这样以来 dp[5]=dp[4];
2.如果和前面数字组合,例如1225有5种翻译方法,其中只翻译5的有3种方法也就是dp[3],其实也是122组成的翻译数量,翻译25的有dp[2]种方法,其实也就是翻译12的数量。故状态转移函数为
再考虑边界条件:
dp(0)=dp(1)=1
dp(0)=dp(1)=1
class Solution {
public:
int translateNum(int num) {
string str_num = to_string(num);
vector<int> dp(str_num.size()+1, 0);
dp[0] = 1, dp[1] = 1;
for (int i = 2; i < str_num.size()+1; i++) {
if (str_num[i - 2] == '1' || (str_num[i - 2] == '2' && str_num[i - 1] < '6')) {
dp[i] = dp[i - 2] + dp[i - 1];
}
else {
dp[i] = dp[i - 1];
}
}
return dp[str_num.size()];
}
};
面试题47. 礼物的最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
思路:动态规划
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==0&&j==0)continue;
if(i==0){
grid[i][j]+=grid[i][j-1];
}
else if(j==0){
grid[i][j]+=grid[i-1][j];
}
else{
grid[i][j]+=max(grid[i][j-1],grid[i-1][j]);
}
}
}
return grid[grid.size()-1][grid[0].size()-1];
}
};
面试题48. 最长不含重复字符的子字符串
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
思路:滑动窗口
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> hash;
int res = 0;
for (int i = 0, j = 0; i < s.size(); i ++){
hash[s[i]] ++;
while (hash[s[i]] > 1) hash[s[j ++]] --;
res = max(res, i - j + 1);
}
return res;
}
};
面试题49. 丑数
我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
思路:动态规划
class Solution {
public:
int nthUglyNumber(int n) {
vector<int>dp(n,0);
dp[0]=1;
int a=0,b=0,c=0;
for(int i=1;i<n;i++){
int n2=dp[a]*2,n3=dp[b]*3,n5=dp[c]*5;
dp[i]=min(min(n2,n3),n5);
if(dp[i]==n2)a++;
if(dp[i]==n3)b++;
if(dp[i]==n5)c++;
}
return dp[n-1];
}
};
面试题50. 第一个只出现一次的字符
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。
思路:哈希表
class Solution {
public:
char firstUniqChar(string s) {
unordered_map<char,int> hashmap;
for(int i=0;i<s.size();i++){
hashmap[s[i]]++;
}
for(auto x:s){
if(hashmap[x]==1){
return x;
}
}
return ' ';
}
};
面试题51. 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
思路:参考
class Solution {
public:
int mergeSort(vector<int>&nums,vector<int>&temp,int l,int r){
if(l>=r){
return 0;
}
int mid=(l+r)/2;
int inv_count=mergeSort(nums,temp,l,mid)+mergeSort(nums,temp,mid+1,r);
int i=l,j=mid+1,pos=l;
while(i<=mid&&j<=r){
//注意这而要小于等于
if(nums[i]<=nums[j]){
temp[pos]=nums[i];
i++;
}
else{
temp[pos]=nums[j];
inv_count+=(mid-i+1);//注意这儿参考文字题解思路
j++;
}
pos++;
}
for(int k=i;k<=mid;k++){
temp[pos++]=nums[k];
}
for(int k=j;k<=r;k++){
temp[pos++]=nums[k];
inv_count+=(mid-i+1);
}
copy(temp.begin() + l, temp.begin() + r + 1, nums.begin() + l);
return inv_count;
}
int reversePairs(vector<int>& nums) {
int n=nums.size();
vector<int>temp(n);
return mergeSort(nums,temp,0,n-1);
}
};
面试题52. 两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
思路:弹栈
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==headB)return headA;
stack<ListNode *> stack1,stack2;
while(headA!=NULL){
stack1.push(headA);
headA=headA->next;
}
while(headB!=NULL){
stack2.push(headB);
headB=headB->next;
}
ListNode * tempA;
ListNode *tempB;
while(!stack2.empty()&&!stack1.empty()){
tempA=stack1.top();
stack1.pop();
tempB=stack2.top();
stack2.pop();
if(tempB!=tempA){
return tempA->next;
}
}
if(!stack2.empty()){
tempB=stack2.top();
return tempB->next;
}
if(!stack1.empty()){
tempA=stack1.top();
return tempA->next;
}
return NULL;
}
};
面试题53 - I. 在排序数组中查找数字 I
思路:参考
class Solution {
public:
int search(vector<int>& nums, int target) {
int res=0;
int left;
int right;
int start=0;
int end=nums.size()-1;
while(start<=end){
int mid=(start+end)/2;
if(nums[mid]<=target){
start=mid+1;
}
else{
end=mid-1;
}
}
right=start;
start=0;
end=nums.size()-1;
while(start<=end){
int mid=(start+end)/2;
if(nums[mid]<target){
start=mid+1;
}
else{
end=mid-1;
}
}
left=end;
return right-left-1;
}
};
面试题53 - II. 0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
思路:书上的思路
class Solution {
public:
int missingNumber(vector<int>& nums) {
if(nums.size()==0)return -1;
int left=0;
int right=nums.size()-1;
int mid=0;
while(left<=right){
mid=(left+right)/2;
if(nums[mid]!=mid){
//mid==0为左边界条件
if(mid==0||nums[mid-1]==mid-1){
return mid;
}
right=mid-1;
}
else{
left=mid+1;
}
}
if(left==nums.size())return nums.size();
return -1;
}
};
面试题54. 二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第k大的节点。
思路:后序遍历(一次过)
/**
* 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:
void backtrack(TreeNode *root,vector<int> &res){
if(root==NULL){
return ;
}
if(root->left!=NULL)backtrack(root->left,res);
res.push_back(root->val);
if(root->right!=NULL)backtrack(root->right,res);
}
int kthLargest(TreeNode* root, int k) {
vector<int>res;
backtrack(root,res);
return res[res.size()-k];
}
};
面试题55 - I. 二叉树的深度
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
思路:书上的思路
/**
* 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:
int maxDepth(TreeNode* root) {
if(root==NULL)return 0;
int leftLength=maxDepth(root->left);
int rightLength=maxDepth(root->right);
return max(leftLength,rightLength)+1;
}
};
面试题55 - II. 平衡二叉树
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
思路:参考
/**
* 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:
int backtrack(TreeNode *root){
if(root==NULL){
return 0;
}
int left=backtrack(root->left);
if(left==-1)return -1;
int right=backtrack(root->right);
if(right==-1)return -1;
return abs(left-right)<2? max(left,right)+1 : -1;
}
bool isBalanced(TreeNode* root) {
return backtrack(root)!=-1;
}
};
面试题56 - I. 数组中数字出现的次数
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
思路:参考
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int res=0;
for(auto x:nums){
res^=x;
}
int div=1;
while((div&res)==0){
div<<=1;
}
int a=0,b=0;
for(auto x:nums){
if(div&x){
a^=x;
}
else{
b^=x;
}
}
return vector<int>{a,b};
}
};
面试题56 - II. 数组中数字出现的次数 II
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
思路:书上的思路
class Solution {
public:
int singleNumber(vector<int>& nums) {
if(nums.size()==0)return -1;
vector<int>res(32,0);
for(int i=0;i<nums.size();i++){
//leetcode 不支持负值左移必须转换为无符号
unsigned int bitMask=1;
for(int j=31;j>=0;j--){
int bit=nums[i]&bitMask;
if(bit!=0){
res[j]+=1;
}
bitMask=bitMask<<1;
}
}
int result=0;
for(auto x:res){
result=result<<1;
result+=x%3;
}
return result;
}
};
面试题57.和为s的两个数字
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
思路: 书上的思路
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int start=0,end=nums.size()-1;
while(start <end){
if(nums[start]+nums[end]<target){
start++;
}
else if(nums[start]+nums[end]>target){
end--;
}
else{
break;
}
}
if(start>=end){
return vector<int>{};
}
else{
return vector<int>{nums[start],nums[end]};
}
}
};
面试题57 - II. 和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
思路:参考
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target){
int l=1;
int r=2;
int sum=0;
vector<vector<int>>res;
for(l=1,r=2;l<r;){
vector<int>temp;
int sum=(l+r)*(r-l+1)/2;
if(sum<target){
r++;
}
else if(sum>target){
l++;
}
else{
for(int i=l;i<=r;i++){
temp.push_back(i);
}
res.push_back(temp);
r++;
}
}
return res;
}
};
面试题58 - I. 翻转单词顺序
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。
思路:参考
class Solution {
public:
string reverseWords(string s) {
int k=0;
for(int i=0;i<s.size();i++){
while(i<s.size()&&s[i]==' ')i++;
if(i==s.size())break;
int j=i;
while(j<s.size()&&s[j]!=' ')j++;
reverse(s.begin()+i,s.begin()+j);
if(k)s[k++]=' ';
while(i<j)s[k++]=s[i++];
}
s.erase(s.begin()+k,s.end());
reverse(s.begin(),s.end());
return s;
}
};
面试题58 - II. 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
思路:书上思路(一次过)
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(),s.begin()+n);
reverse(s.begin()+n,s.end());
reverse(s.begin(),s.end());
return s;
}
};
面试题59 - I. 滑动窗口的最大值
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
思路:双端队列
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(k==1){return nums;}
vector<int>res;
deque<pair<int,int>>dq;
int n = nums.size();
for(int i=0;i<n;i++){
//弹出比num[i]小的值
while (!dq.empty() && nums[i] > dq.back().first)dq.pop_back();
//判断队列头是不是超过k
if(!dq.empty()&&dq.front().second<i-k+1)dq.pop_front();
dq.push_back(make_pair(nums[i],i));
if(i>=k-1)res.push_back(dq.front().first);
}
return res;
}
};
面试题59 - II. 队列的最大值
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
思路:参考
class MaxQueue {
public:
MaxQueue() {}
int max_value() {
if (d.empty()){return -1;}
return d.front();
}
void push_back(int value) {
while(!d.empty()&&d.back()<value)d.pop_back();
d.push_back(value);
q.push(value);
}
int pop_front() {
if(q.empty())return -1;
int front =q.front();
if(front==d.front()){
d.pop_front();
}
q.pop();
return front;
}
private:
queue<int> q;
deque<int> d;
};
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue* obj = new MaxQueue();
* int param_1 = obj->max_value();
* obj->push_back(value);
* int param_3 = obj->pop_front();
*/
面试题60. n个骰子的点数
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。
思路:参考
class Solution {
public:
vector<double> twoSum(int n) {
vector<vector<double>>dp(n+1,vector<double>(6*n+1,0));
vector<double>ans;
for(int i=1;i<=n;i++){
for(int j=i;j<=6*i;j++){
if(i==1){
dp[i][j]=1;
continue;
}
for(int k=1;k<=6;k++){
if(j-k>0){
dp[i][j]+=dp[i-1][j-k];
}
}
}
}
for(int i=n;i<=6*n;i++){
ans.push_back(dp[n][i]*pow(1.0/6,n));
}
return ans;
}
};
内存优化:参考
class Solution {
public:
vector<double> twoSum(int n) {
//vector<vector<double>>dp(n+1,vector<double>(6*n+1,0));
vector<double>dp(6*n+1,0);
vector<double>ans;
for(int i=1;i<=n;i++){
for(int j=6*i;j>=i;j--){
if(i==1){
dp[j]=1;
continue;
}
//计算前清零
dp[j]=0;
for(int k=1;k<=6;k++){
//条件例如n为2时,判断像dp[5]就只能由,dp[1],dp[2],dp[3],dp[4]相加
if(j-k<i-1){
break;
}
dp[j]+=dp[j-k];
}
}
}
for(int i=n;i<=6*n;i++){
ans.push_back(dp[i]*pow(1.0/6,n));
}
return ans;
}
};
面试题61. 扑克牌中的顺子
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
思路:书上的思路
class Solution {
public:
bool isStraight(vector<int>& nums) {
int numZero=0;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size()-1;i++){
if(nums[i]==0)numZero++;
else if(nums[i+1]==nums[i]){return false;}
else numZero-=(nums[i+1]-nums[i]-1);
}
return numZero>=0;
}
};
面试题62. 圆圈中最后剩下的数字
0,1,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
思路:参考
class Solution {
public:
int lastRemaining(int n, int m) {
int last=0;
for(int i=2;i<=n;i++){
last=(last+m)%i;
}
return last;
}
};
面试题63. 股票的最大利润
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
思路:动态规划
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()==0)return 0;
vector<int>dp(prices.size(),0);
int minPrice=prices[0];
for(int i=1;i<prices.size();i++){
minPrice=min(prices[i],minPrice);
dp[i]=max(dp[i-1],prices[i]-minPrice);
}
return dp[prices.size()-1];
}
};
优化动态规划:
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()==0)return 0;
int profit=0;
int minPrice=prices[0];
for(int i=1;i<prices.size();i++){
minPrice=min(prices[i],minPrice);
profit=max(profit,prices[i]-minPrice);
}
return profit;
}
};
面试题64. 求1+2+…+n
求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
思路:短路法
class Solution {
public:
int sumNums(int n) {
flag=n>1&&sumNums(n-1);
res+=n;
return res;
}
protected:
int res=0;
bool flag;
};
面试题65. 不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
思路:书上思路
class Solution {
public:
int add(int a, int b) {
int sum=0;
int carry=0;
while(b!=0){
sum=a^b;
carry=(unsigned int)(a&b)<<1;//进位,注意:C++不支持负值左移!!这里要加上unsigned int
a=sum;
b=carry;
}
return a;
}
};
面试题66. 构建乘积数组
给定一个数组 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]。不能使用除法。
思路:参考
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
if(a.size()==0)return {};
vector<int>res(a.size(),0);
res[0]=1;
int temp=1;
for(int i=1;i<a.size();i++){
res[i]=a[i-1]*res[i-1];
}
for(int j=a.size()-2;j>=0;j--){
temp*=a[j+1];
res[j]=temp*res[j];
}
return res;
}
};
面试题67. 把字符串转换成整数
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−2^31 , 2^31 − 1]。如果数值超过这个范围,请返回 INT_MAX (2^31 − 1) 或 INT_MIN (−2^31) 。
class Solution {
public:
int strToInt(string str) {
int k=0;
while(str[k]==' ') ++k; //找到第一个非空格字符
int res = 0, flag = 1; //res记录结果的值,flag记录符号位
if(str[k]=='-') flag=-1;//如果第一个字符是-号,则符号位为-1
if(str[k]=='-' || str[k]=='+') ++k;//如果第一个字符是符号位,则从下一个字符开始遍历寻找最长数字串
while(k<str.size() && isdigit(str[k]))//当k未到达字符串末尾并且当前字符是数字时
{
int r = str[k] - '0';//记录当前数字
if(res>INT_MAX/10 || (INT_MAX/10==res && r>7))//判断当前结果是否溢出,溢出直接返回最大值或者最小值
return flag==1?INT_MAX:INT_MIN;
res =res*10+r;//累加当前数字,末尾多一位数字等价将原来数字*10再加上所加的数字即可
++k;
}
return res*flag;
}
};
面试题68 - I. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
思路:参考
/**
* 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* backtrack(TreeNode *root,TreeNode *p,TreeNode *q){
if(p->val>root->val&&q->val>root->val){
return backtrack(root->right,p,q);
}
if(p->val<root->val&&q->val<root->val){
return backtrack(root->left,p,q);
}
return root;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
// TreeNode *temp;
// //保证p<q
// if(p->val>q->val){
// temp=p;
// p=q;
// q=temp;
// }
return backtrack(root,p,q);
}
};
面试题68 - II. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8 : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
TreeNode* DFS(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == NULL || root == p || root == q)return root;
TreeNode* left = DFS(root->left, p, q);
TreeNode* right = DFS(root->right, p, q);
//下面的可以和最后一行合并
if (left == NULL && right == NULL)return NULL;
if (left == NULL) return right;
if (right == NULL)return left;
//当 leftleft 和 rightright 同时不为空 :说明 p, qp,q 分列在 rootroot 的 异侧 (
//分别在 左 / 右子树),因此 rootroot 为最近公共祖先,返回 rootroot ;
return root;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
return DFS(root, p, q);
}
};
int main() {
TreeNode* root = new TreeNode(3);
TreeNode* a = new TreeNode(5);
TreeNode* b = new TreeNode(1);
TreeNode* c = new TreeNode(6);
TreeNode* d = new TreeNode(2);
TreeNode* e = new TreeNode(0);
TreeNode* f = new TreeNode(8);
TreeNode* g = new TreeNode(7);
TreeNode* h = new TreeNode(4);
root->left = a;
root->right = b;
a->left = c;
a->right = d;
b->left = e;
b->right = f;
d->left = g;
d->right = h;
Solution sol;
TreeNode* res = sol.lowestCommonAncestor(root, g, h);
int cc = 1;
}