面试题35. 复杂链表的复制
难度中等46
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:
输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:
输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
提示:
• -10000 <= Node.val <= 10000
• Node.random 为空(null)或指向链表中的节点。
• 节点数目不超过 1000 。
三步法:
1、把复制的结点链接在原始链表的每一对应结点后面
2、把复制的结点的random指针指向被复制结点的random指针的下一个结点
3、拆分成两个链表,奇数位置组成原链表,偶数位置组成复制链表
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
//1、把复制的结点链接在原始链表的每一对应结点后面 //复制next 如原来是A->B->C 变成A->A'->B->B'->C->C'
if(head==NULL) return NULL;
Node* node=head;
while(node){
Node* tmp=new Node(node->val);
tmp->next=node->next;
node->next=tmp;
node=tmp->next;
}
//2、把复制的结点的random指针指向被复制结点的random指针的下一个结点 //复制random:cur是原来链表的结点 cur->next是复制cur的结点
node=head;
while(node){
Node* temp=node->next;
if(node->random){
temp->random=node->random->next;
}
node=temp->next;
}
// 拆分链表,将链表拆分为原链表和复制后的链表
node=head;
Node* p=head->next;
while(node){
Node* temp=node->next;
node->next=temp->next;
if(temp->next!=NULL){
temp->next=temp->next->next;
}
node=node->next;
}
return p;
}
};
面试题36. 二叉搜索树与双向链表
难度中等54收藏分享切换为英文关注反馈
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
方法一:中序遍历
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node* treeToDoublyList(Node* root) {
if(root == NULL)
return NULL;
Node* head = NULL;
Node* pre = NULL;// 定义一个pre指针
helper(root,head,pre);
head->left = pre;
pre->right = head;
return head;
}
void helper(Node* root,Node* &head,Node* &pre){
// 中序遍历
if(root==NULL) return;
helper(root->left,head,pre);// 先递归遍历左子树
if(head==NULL){
head=root;
pre=root;
}
else
{
root->left=pre;// 修改为双向链表
pre->right=root;
pre=root;//更新双向链表尾结点
}
helper(root->right,head,pre);// 再递归遍历右子树
}
};
面试题37. 序列化二叉树
难度困难37收藏分享切换为英文关注反馈
请实现两个函数,分别用来序列化和反序列化二叉树。
示例:
你可以将以下二叉树:
1
/
2 3
/
4 5
序列化为 “[1,2,3,null,null,4,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 Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
if (root == NULL)
return "#_";
string res = to_string(root->val) + "_";
res += serialize(root->left);
res += serialize(root->right);
return res;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
std::stringstream ss(data);
std::string item;
queue<string> q;
while (std::getline(ss, item, '_'))
q.push(item);
return helper(q);
}
TreeNode* helper(queue<string>& q)
{
string val = q.front();
q.pop();
if (val == "#")
return NULL;
TreeNode* head = new TreeNode(stoi(val));
head->left = helper(q);
head->right = helper(q);
return head;
}
};
// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));
面试题54. 二叉搜索树的第k大节点
难度简单32
给定一棵二叉搜索树,请找出其中第k大的节点。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/
1 4
2
输出: 4
示例 2:
输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/
3 6
/
2 4
/
1
输出: 4
限制:
1 ≤ k ≤ 二叉搜索树元素个数
MySolution:
/**
* 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 dfs(TreeNode *root,vector<int> & res){
if(root==NULL) return;
if(root->left) dfs(root->left,res);
res.push_back(root->val);
if(root->right) dfs(root->right,res);
}
int kthLargest(TreeNode* root, int k) {
vector<int> res;
dfs(root,res);
return res[res.size()-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:
int res=0;
int cnt=0;
void dfs(TreeNode* root,int k){
if(root==NULL) return;
dfs(root->right,k);
if(++cnt==k){
res=root->val;
return;
}
dfs(root->left,k);
}
int kthLargest(TreeNode* root, int k) {
if(root==NULL||k<1) return 0;
dfs(root,k);
return res;
}
};
方法二:迭代
/**
* 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 kthLargest(TreeNode* root, int k) {
int n=0;
stack<TreeNode*> s;
TreeNode* p = root;
while (!s.empty() || p!=NULL)
{
while(p!=NULL)
{
s.push(p);
p = p->right;
}
p =s.top();
s.pop();
if (++n==k)
{
return p->val;
}
p = p->left;
}
return 0;
}
};
面试题55 - I. 二叉树的深度
难度简单25
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
提示:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root==NULL) return 0;
return max(maxDepth(root->left),maxDepth(root->right))+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 maxDepth(TreeNode* root) {
if(root==NULL) return 0;
queue<TreeNode*> q;
q.push(root);
int depth=0;
while(!q.empty()){
int len=q.size();
TreeNode* tmp;
int i=0;
while(i++<len){
tmp=q.front();
q.pop();
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
depth++;
}
return depth;
}
};
面试题55 - II. 平衡二叉树
难度简单29
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/
9 20
/
15 7
返回 true 。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/
2 2
/
3 3
/
4 4
返回 false 。
限制:
• 1 <= 树的结点个数 <= 10000
Mysolution:
/**
* 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 treeDepth(TreeNode* root){
if(root==NULL) return 0;
return max(treeDepth(root->left),treeDepth(root->right))+1;
}
bool isBalanced(TreeNode* root) {
if(root==NULL) return true;
int leftDepth=treeDepth(root->left);
int rigthDepth=treeDepth(root->right);
if(leftDepth-rigthDepth>1||leftDepth-rigthDepth<-1) return false;
return isBalanced(root->left)&&isBalanced(root->right);
}
};
解题思路有两种,只遍历一次的方法最优。
重复遍历多次:自顶向下
时间复杂度O(nlogn), 空间复杂度O(n)复杂度分析
自顶向下在遍历树的每个结点的时候,调用函数TreeDepth得到它的左右子树的深度。如果每个结点的左右子树的深度相差都不超过1,则这是一颗平衡的二叉树。这种方法的缺点是,首先判断根结点是不是平衡的,需要使用TreeDepth获得左右子树的深度,然后还需要继续判断子树是不是平衡的,还是需要使用TreeDepth获得子树的左右子树的深度,这样就导致了大量的重复遍历。
只遍历一次:自底向上
时间复杂度O(n), 空间复杂度O(n)复杂度分析
自底向上与自顶向下的逻辑相反,首先判断子树是否平衡,然后比较子树高度判断父节点是否平衡。检查子树是否平衡。如果平衡,则使用它们的高度判断父节点是否平衡,并计算父节点的高度。自底向上计算,每个子树的高度只会计算一次。可以递归先计算当前节点的子节点高度,然后再通过子节点高度判断当前节点是否平衡,从而消除冗余。
方法二:
class Solution {
public:
bool isBalanced(TreeNode* root) {
if (root == NULL)
return true;
int depth = 0;
return helper(root, depth);
}
bool helper(TreeNode* root, int &depth)
{
if (root == NULL)
{
depth = 0;
return true;
}
int left, right;
if (helper(root->left, left) && helper(root->right, right) && abs(left-right)<=1)
{
depth = max(left,right)+1;
return true;
}
return false;
}
};
输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
递归与迭代两种方法
方法一:递归
中序遍历:左子树—> 根结点 —> 右子树,比较容易利用递归写出
方法二:迭代
根据中序遍历的顺序,优先访问左子树,然后在访问根节点和右子树。所以,对于任意结点node,第一部分即直接访问之,之后在判断左子树是否为空,不为空时即重复上面的步骤,直到其为空。若为空,则需要访问右子树。注意,在访问过左孩子之后,需要反过来访问其右孩子,所以,需要栈这种数据结构的支持。对于任意一个结点node,具体步骤如下:
a)访问之,并把结点node入栈,当前结点置为左孩子;
b)判断结点node是否为空,若为空,则取出栈顶结点并出栈,将右孩子置为当前结点;否则重复a)步直到当前结点为空或者栈为空(可以发现栈中的结点就是为了访问右孩子才存储的)
/**
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
class Solution {
public:
vector<int> res;
void dfs(TreeNode* root){
if(root==NULL) return;
dfs(root->left);
res.push_back(root->val);
dfs(root->right);
}
vector<int> inorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
};
//方法二:迭代
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
/*栈S;
p= root;
while(p || S不空){
while(p){
p入S;
p = p的左子树;
}
p = S.top 出栈;
访问p;
p = p的右子树;
}
*/
stack<TreeNode*> s;
vector<int> res;
if(root==NULL) return res;
TreeNode* cur=root;
//s.push(root);
while(!s.empty()||cur!=NULL){
while(cur!=NULL){
s.push(cur);
cur=cur->left;
}
cur=s.top();
s.pop();
res.push_back(cur->val);
cur=cur->right;
}
return res;
}
};
输出: [1,2,3]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
/**
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
class Solution {
public:
void dfs(TreeNode* root,vector<int> &res){
if(root==NULL) return;
res.push_back(root->val);
dfs(root->left,res);
dfs(root->right,res);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if(root==NULL) return res;
dfs(root,res);
return res;
}
};
方法二:迭代
/**
* 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:
vector<int> preorderTraversal(TreeNode* root) {
/*栈S;
p= root;
while(p || S不空){
while(p){
访问p节点;
p的右子树入S;
p = p的左子树;
}
p = S栈顶弹出;
}
*/
vector<int> res;
if(root==NULL) return res;
stack<TreeNode*> s;
TreeNode* p=root;
while(p||!s.empty()){
while(p){
res.push_back(p->val);
s.push(p->right);
p=p->left;
}
p=s.top();
s.pop();
}
return res;
}
};
迭代法:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if (root==NULL)
return res;
stack<TreeNode*> s;
s.push(root);
while (!s.empty())
{
TreeNode* node = s.top();
s.pop();
res.push_back(node->val);
if (node->right)
{
s.push(node->right);
}
if (node->left)
{
s.push(node->left);
}
}
return res;
}
};
输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
递归与迭代两种方法
方法一:递归
后序遍历:左子树 —> 右子树 —> 根结点,比较容易利用递归写出
方法二:迭代
使用栈(先进后出)来完成,我们先将根节点放入栈中,从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压左子树再压右子树,然后在调用reverse函数。
使用一个指针lastVisited记录最后访问的节点,一个根节点被访问的前提是:无右子树或右子树已被访问过。/**
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
class Solution {
public:
void dfs(TreeNode* root,vector<int> &res){
if(root==NULL) return;
dfs(root->left,res);
dfs(root->right,res);
res.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if(root==NULL) return res;
dfs(root,res);
return res;
}
};
方法二:迭代
//前序遍历之后reverse
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if(root==NULL) return res;
stack<TreeNode*> s;
TreeNode* p=root;
while(!s.empty()||p){
while(p){
res.push_back(p->val);
s.push(p->left);
p=p->right;
}
p=s.top();
s.pop();
}
reverse(res.begin(),res.end());
return res;
}
};
按照左子树-根-右子树的方式,将其转换成迭代方式。
思路:每到一个节点 A,因为根要最后访问,将其入栈。然后遍历左子树,遍历右子树,最后返回到 A。
但是出现一个问题,无法区分是从左子树返回,还是从右子树返回。
因此,给 A 节点附加一个标记T。在访问其右子树前,T 置为 True。之后子树返回时,当 T 为 True表示从右子树返回,否则从左子树返回。
当 T 为 false 时,表示 A 的左子树遍历完,还要访问右子树。
同时,当 T 为 True 时,表示 A 的两棵子树都遍历过了,要访问 A 了。并且在 A 访问完后,A 这棵子树都访问完成了。
栈S;
p= root;
T<节点,True/False> : 节点标记;
while(p || S不空){
while§{
p入S;
p = p的左子树;
}
while(S不空 且 T[S.top] = True){
访问S.top;
S.top出S;
}
if(S不空){
p = S.top 的右子树;
T[S.top] = True;
}
}
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if (root==NULL) return res;
stack<TreeNode*> s;
TreeNode* p = root;
TreeNode* lastVisited = NULL;
while(!s.empty() || p)
{
while(p)
{
s.push(p);
p = p->left;
}
p = s.top();
if (p->right == NULL || p->right == lastVisited)
{
res.push_back(p->val);
s.pop();
lastVisited = p;
p = NULL; // p 已经访问过了,没用了设置为NULL
}
else
{
p = p->right;
}
}
return res;
}
};
/**
* 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 dfs(TreeNode* root,vector<int> &res){
if(root==NULL) return;
dfs(root->left,res);
res.push_back(root->val);
dfs(root->right,res);
}
bool isValidBST(TreeNode* root) {
vector<int> res;
dfs(root,res);
set<int> t(res.begin(),res.end());
vector<int> tmp;
tmp.assign(t.begin(),t.end());
return tmp==res;
return true;
}
};
方法二:
/**
* 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* pre=NULL;
bool isValidBST(TreeNode* root) {
if(root==NULL) return true;
if(!isValidBST(root->left)) return false;// 访问左子树, 如果左子树为false 返回false
if(pre&&pre->val>=root->val) return false; // 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST,返回 false;否则继续遍历。
pre=root;// 更新前一个节点
//if(!isValidBST(root->right)) return false;// 访问右子树
return isValidBST(root->right);
}
};
1 <—
/
2 3 <—
\
5 4 <—
方法0:
/**
* 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:
vector<int> rightSideView(TreeNode* root) {
if(root==NULL) return vector<int>();
vector<int> res;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int len=q.size();
res.push_back(q.back()->val);
int i=0;
TreeNode* tmp;
while(i<len){
tmp=q.front();
q.pop();
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
i++;
}
}
return res;
}
};
方法二:
BFS
按层来遍历,先遍历每一层的右子树
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
if(root==NULL) return vector<int>();
vector<int> res;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int len=q.size();
res.push_back(q.front()->val);
int i=0;
TreeNode* tmp;
while(i<len){
tmp=q.front();
q.pop();
if(tmp->right) q.push(tmp->right);
if(tmp->left) q.push(tmp->left);
i++;
}
}
return res;
}
};
DFS
按照根节点-右子树-左子树来遍历,逆前序遍历
/**
* 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 dfs(TreeNode* root,int depth,vector<int> &res){
if(root==NULL) return;
if(depth==res.size()){
res.push_back(root->val);// 利用结果res的size等于tree的高度的性质
}
dfs(root->right,depth+1,res);
dfs(root->left,depth+1,res);
}
vector<int> rightSideView(TreeNode* root) {
vector<int> res;
dfs(root,0,res);
return res;
}
};
/**
* 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 res=0;
int dfs(TreeNode* root){
// 函数dfs的作用是:找到以root为根节点的二叉树的最大深度
if(root==NULL) return 0;
int leftDepth=dfs(root->left);
int rightDepth=dfs(root->right);
res=max(res,leftDepth+rightDepth);
return max(leftDepth,rightDepth)+1;
}
int diameterOfBinaryTree(TreeNode* root) {
if(root==NULL) return res;
dfs(root);
return res;
}
};
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
说明:
• 所有节点的值都是唯一的。
• p、q 为不同节点且均存在于给定的二叉树中。
递归
考虑通过递归对二叉树进行后序遍历,当遇到节点 p 或 q 时返回。从底至顶回溯,当节点 p, q, 在节点 root的异侧时,节点root即为最近公共祖先,则向上返回root。
/**
* 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//左左;右右;左右
if (root==NULL ||root==p || root==q) return root;
TreeNode* left = lowestCommonAncestor(root->left,p,q);
TreeNode* right = lowestCommonAncestor(root->right,p,q);
if (left == NULL && right == NULL) return NULL; // 左右子树同时为空,都不包含p,q,返回NULL
if (left==NULL) return right; // 当 left为空 ,right不为空 :p,q都不在root的左子树中,直接返回 right
if (right==NULL) return left; // 与上一条件类似
return root; // 同时不为空,说明p,q在左右子树异侧,返回root
}
};
/**
* 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:
bool helper(TreeNode* s, TreeNode* t){
if(s==NULL&&t==NULL) return true;
/*
if(s&&t&&s->val==t->val) return helper(s->left,t->left)&&helper(s->right,t->right);
else return false;
*/
return s&&t && s->val==t->val && helper(s->left, t->left) && helper(s->right,t->right);
}
bool isSubtree(TreeNode* s, TreeNode* t) {
if(s==NULL&&t!=NULL) return false;
if(s==NULL&&t==NULL) return true;
return helper(s,t)||isSubtree(s->left,t)||isSubtree(s->right,t);
}
};
面试题34. 二叉树中和为某一值的路径
难度中等42
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/
4 8
/ /
11 13 4
/ \ /
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,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:
void dfs(TreeNode* root, int sum,vector<vector<int>> &res,vector<int> &out){
if(root==NULL) return ;
out.push_back(root->val);
// 如果是叶节点,并且路径上的节点值的和为输入的值,就像结果中添加这一out
if(sum==root->val&&root->left==NULL&&root->right==NULL){
res.push_back(out);
}// 不是叶节点就遍历他的子节点
dfs(root->left,sum-root->val,res,out);
dfs(root->right,sum-root->val,res,out);
out.pop_back();// 到这一步说明不满足要求,要返回父节点,需要删除路径上的当前节点
}
vector<vector<int>> pathSum(TreeNode* root, int sum) {
vector<vector<int>> res;
vector<int> out;
dfs(root,sum,res,out);
return res;
}
};
面试题33. 二叉搜索树的后序遍历序列
难度中等56
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5
/
2 6
/
1 3
示例 1:
输入: [1,6,3,2,5]
输出: false
示例 2:
输入: [1,3,2,6,5]
输出: true
提示:
class Solution {
public:
bool helper(vector<int>& postorder,int start,int end){
if(start>end) return false;
int root=postorder[end];
int i=start;
while(i<end){
if(postorder[i]>root){
break;
}
i++;
}
for(int j=i;j<end;j++){
if(postorder[j]<root) return false;
}
//判断左子树是不是二叉搜索树
bool left = true;
if (i>start)
{
left = helper(postorder, start, i-1);
}
//判断右子树是不是二叉搜索树
bool right = true;
if (i < end-1)
{
right = helper(postorder, i, end-1);
}
return left && right;
//return helper(postorder,start,i-1)&&helper(postorder,i,end-1);
}
bool verifyPostorder(vector<int>& postorder) {
bool res=true;
if(postorder.empty()) return res;
int len=postorder.size();
res=helper(postorder,0,len-1);
return res;
}
};
面试题32 - III. 从上到下打印二叉树 III
难度中等19
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层次遍历结果:
[
[3],
[20,9],
[15,7]
]
提示:
因为奇数层的打印顺序是从左到右,偶数层的打印顺序是从右到左,可以利用STL容器deque中push_back(),push_front(),front(),back(),pop(),popfront()来解决,实现前取后放,后取前放,因为deque队列中的元素,都是从左到右的,当zigzag==true;,从左到右打印,就从前边开始取出元素,下一层的元素从后边压入,同理,从右向左打印时,从后边取出元素,下一层的元素就从前边压入,注意先压入右子树的,后压入左子树,这样新的队列还是从左到右,然后继续,直至队列为空。
/**
* 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:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(root==NULL) return res;
deque<TreeNode*> dq;
dq.push_front(root);
int level=0;
while(!dq.empty()){
int len=dq.size();
vector<int> temp(len);
TreeNode* tmp;
for(int i=0;i<len;++i){
//注意&&优先级,加括号!!!
if((level&1)==0){
//前取后放:从左向右,所以从前边取,后边放入
tmp=dq.front();
dq.pop_front();
temp[i]=tmp->val;
if(tmp->left) dq.push_back(tmp->left);
if(tmp->right) dq.push_back(tmp->right);
}else{
// 后取前放:从右向左,从后边取,前边放入
tmp=dq.back();
dq.pop_back();
temp[i]=tmp->val;
if(tmp->right) dq.push_front(tmp->right);
if(tmp->left) dq.push_front(tmp->left);
}
}
level++;
res.push_back(temp);
}
return res;
}
};
面试题32 - II. 从上到下打印二叉树 II
难度简单25
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
提示:
/**
* 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:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(root==NULL) return res;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int len=q.size();
vector<int> temp(len);
TreeNode* tmp;
for(int i=0;i<len;++i){
tmp=q.front();
q.pop();
temp[i]=tmp->val;
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
res.push_back(temp);
}
return res;
}
};
方法二:递归
/**
* 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:
vector<vector<int>> res;
void helper(TreeNode* root,int level){
if(root==NULL) return;
if(res.size()==level){
res.push_back(vector<int>());
}
res[level].push_back(root->val);
if(root->left) helper(root->left,level+1);
if(root->right) helper(root->right,level+1);
}
vector<vector<int>> levelOrder(TreeNode* root) {
if(root==NULL) return res;
helper(root,0);
return res;
}
};
面试题28. 对称的二叉树
难度简单42
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/
2 2
/ \ /
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/
2 2
\
3 3
示例 1:
输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:
输入:root = [1,2,2,null,3,null,3]
输出:false
限制:
0 <= 节点个数 <= 1000
思路:递归
1.怎么判断一棵树是不是对称二叉树? 答案:如果所给根节点,为空,那么是对称。如果不为空的话,当他的左子树与右子树对称时,他对称
2.那么怎么知道左子树与右子树对不对称呢?在这我直接叫为左树和右树 答案:如果左树的左孩子与右树的右孩子对称,左树的右孩子与右树的左孩子对称,那么这个左树和右树就对称。
那用递归分别比较左右子树
递归结束条件:
都为空指针则返回 true
只有一个为空则返回 false
两个指针当前节点值不相等 返回false
递归过程:
判断 A 的右子树与 B 的左子树是否对称
判断 A 的左子树与 B 的右子树是否对称
方法一:递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool helper(TreeNode* left,TreeNode* right){
if(left==NULL&&right==NULL) return true;// 先写递归终止条件
if(left==NULL&&right) return false;// 如果其中之一为空,也不是对称的
if(left&&right==NULL) return false;
if(left->val!=right->val) return false;
return helper(left->left,right->right)&&helper(left->right,right->left);// 前序遍历
}
bool isSymmetric(TreeNode* root) {
bool res=true;
if(root==NULL) return true;
if(root) res=helper(root->left,root->right);
return res;
}
};
面试题27. 二叉树的镜像
面试题27. 二叉树的镜像
难度简单25
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/
2 7
/ \ /
1 3 6 9
镜像输出:
4
/
7 2
/ \ /
9 6 3 1
示例 1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
限制:
0 <= 节点个数 <= 1000
/**
* 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* mirrorTree(TreeNode* root) {
if(root==NULL) return NULL;
TreeNode* res=new TreeNode(root->val);
res->left=mirrorTree(root->right);
res->right=mirrorTree(root->left);
return res;
}
};
面试题26. 树的子结构
难度中等55
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:
3
/
4 5
/
1 2
给定的树 B:
4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
示例 1:
输入:A = [1,2,3], B = [3,1]
输出:false
示例 2:
输入:A = [3,4,5,1,2], B = [4,1]
输出:true
限制:
0 <= 节点个数 <= 10000
思路:首先现在A中找出B的根节点,也就是要遍历A找出与B根节点相同的值,然后判断树A中以R为根节点的子树是否包含和B一样的结构。 根节点值相等就继续判断剩余子树是否相等,直到B为NULL。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool helper(TreeNode* A,TreeNode*B){
if(B==NULL) return true;//B已经遍历完了都能对应的上
if(A==NULL) return false;//A完B未完
if(A->val!=B->val) return false;//如果其中有一个点没有对应上,返回false
return helper(A->left,B->left)&&helper(A->right,B->right);//如果根节点对应的上,那么就分别去子节点里面匹配
}
bool isSubStructure(TreeNode* A, TreeNode* B) {
bool res=false;
if(A&&B){
//当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
if(A->val==B->val)res= helper(A,B);//如果找到了对应B的根节点的点,
if(!res) res=isSubStructure(A->left,B);//如果找不到,那么就再去A的左子树当作起点
if(!res) res=isSubStructure(A->right,B);//如果找不到,那么就再去A的右子树当作起点
}
return res;
}
};
面试题07. 重建二叉树
难度中等108收藏分享切换为英文关注反馈
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/
9 20
/
15 7
限制:
0 <= 节点个数 <= 5000
思路分析:利用二叉树各种遍历的特点,找到根节点和左右子树 ,递归重建。注意前序遍历中的第一个数字是根节点的值,在中序遍历中根节点的值在序列中间,左子树的节点的值在根节点的值的左边,而右子树的节点的值位于根节点的右边,所以先扫描中序遍历,找到根节点所在位置,然后找到左子树和右子树的前序遍历和中序遍历即可。
方法一:递归一
/**
* 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* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty()||inorder.empty()) return NULL;
int val=preorder[0];// 前序遍历的第一个数字是根节点的值
TreeNode* root=new TreeNode(val);// 创建根节点
if(preorder.size()==1) return root;// 如果长度为1,直接返回根节点
int rootIndex=0;// 找到root所在的位置,确定好前序和中序中左子树和右子树序列的范围
for(int i=0;i<inorder.size();++i){
if(inorder[i]==val){
rootIndex=i;
break;
}
}
vector<int> leftPre,leftIn,rightPre,rightIn;
for(int i=0;i<rootIndex;++i){
leftPre.push_back(preorder[i+1]);// +1 是因为前序遍历的第一个节点是根节点
leftIn.push_back(inorder[i]);
}
for(int i=rootIndex+1;i<preorder.size();++i){
rightPre.push_back(preorder[i]);
rightIn.push_back(inorder[i]);
}
root->left=buildTree(leftPre,leftIn);
root->right=buildTree(rightPre,rightIn);
return root;
}
};
递归二:
class Solution {
public:
TreeNode *helper(vector<int> preorder,int preStart,int preEnd,vector<int> inorder,int inStart,int inEnd){
if(preStart>=preEnd||inStart>=inEnd) return NULL;/
TreeNode* root=new TreeNode(preorder[preStart]);
int rootIndex=0;
while(inorder[rootIndex]!=preorder[preStart]){
rootIndex++;
}
cout<<rootIndex<<endl;
root->left=helper(preorder,preStart+1,preStart+1+rootIndex-inStart,inorder,inStart,rootIndex);
root->right=helper(preorder,preStart+1+rootIndex-inStart,preEnd,inorder,rootIndex+1,inEnd);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size()<=0) return NULL;
//前序遍历的第一个值是根节点的值,对应中序遍历同一个值
TreeNode *root=new TreeNode(preorder[0]);
return helper(preorder,0,preorder.size(),inorder,0,inorder.size());
}
};
极简递归:
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
//
return recursionBuild(preorder.begin(),preorder.end(),inorder.begin(),inorder.end());
}
TreeNode* recursionBuild(vector<int>::iterator preBegin,vector<int>::iterator preEnd,vector<int>::iterator inBegin,vector<int>::iterator inEnd)
{
if(inEnd==inBegin) return NULL;
TreeNode* cur=new TreeNode(*preBegin);
auto root=find(inBegin,inEnd,*preBegin);
cur->left=recursionBuild(preBegin+1,preBegin+(root-inBegin)+1,inBegin,root);
cur->right=recursionBuild(preBegin+1+(root-inBegin),preEnd,root+1,inEnd);
return cur;
}
};
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路分析:
我们以上图为例进行讲解,上图二叉树的中序遍历是d,b,h,e,i,a,f,c,g。我们以这棵树为例来分析如何找出二叉树的下一个结点。
如果一个结点有右子树,那么它的下一个结点就是它的右子树的最左子结点。也就是说从右子结点出发一直沿着指向左子树结点的指针,我们就能找到它的下一个结点。例如,图中结点b的下一个结点是h,结点a的下一个结点是f。
接着我们分析一下结点没有右子树的情形。如果结点是它父结点的左子结点,那么它的下一个结点就是它的父结点。例如,途中结点d的下一个结点是b,f的下一个结点是c。
如果一个结点既没有右子树,并且它还是父结点的右子结点,这种情形就比较复杂。我们可以沿着指向父结点的指针一直向上遍历,直到找到一个是它父结点的左子结点的结点。如果这样的结点存在,那么这个结点的父结点就是我们要找的下一个结点。例如,为了找到结点g的下一个结点,我们沿着指向父结点的指针向上遍历,先到达结点c。由于结点c是父结点a的右结点,我们继续向上遍历到达结点a。由于结点a是树的根结点。它没有父结点。因此结点g没有下一个结点。
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* Node)
{
if (Node== NULL)
return NULL;
TreeLinkNode* res = NULL;
// 当前结点有右子树,那么它的下一个结点就是它的右子树中最左子结点
if (Node->right != NULL)
{
TreeLinkNode* pRight = Node->right;
while(pRight->left!=NULL)
{
pRight = pRight->left;
}
res = pRight;
}
// 当前结点无右子树,则需要找到一个是它父结点的左子树结点的结点
else if (Node->next!=NULL)
{
// 当前结点
TreeLinkNode* pCur = Node;
// 父节点
TreeLinkNode* pNext = Node->next;
while( pNext != NULL && pNext->right == pCur)
{
pCur = pNext;
pNext = pNext->next;
}
res = pNext;
}
return res;
}
};