二叉树(bibary Tree)

二叉树是面试中最容易被问道的问题,这里同样给出高频而且有代表性的10道题目。
二叉树介绍:

  1. 百度百科:二叉树
  2. wikipedia: binary Tree

定义二叉树:

struct TreeNode {int data;TreeNode *left, *right;TreeNode(){}TreeNode(int _data, TreeNode* _left, TreeNode* _right):data(_data), left(_left), right(_right){}
};

1. 二叉树的遍历

题目: 给出二叉树的层次遍历, 前序, 中序, 后序 遍历.
扩展: 前序遍历的迭代形式,希望大家自行手写中序和后序的迭代代码, 很多公司会问道非递归代码.

// 前序遍历
void printPostorder(struct TreeNode* node) { if (node == NULL) return;   printPostorder(node->left); printPostorder(node->right);   cout << node->data << " ";
}
// 中序遍历
void printInorder(struct TreeNode* node) { if (node == NULL) return; printInorder(node->left);   cout << node->data << " ";   printInorder(node->right);
}
// 后序遍历
void printPreorder(struct TreeNode* node) { if (node == NULL) return; cout << node->data << " "; printPreorder(node->left);  printPreorder(node->right);
}  // 层次遍历
void printLevelOrder(struct TreeNode* node) {queue<TreeNode *> q;if(!node) q.push(node);while(!q.empty()) {// 当前的长度是上一层的个数,这一点很重要,可以解决很多层次遍历相关的问题int len = q.size(); for(int i = 0; i < len; i ++) {TreeNode * tmp = q.top();q.pop();cout << tmp->data << " ";if(tmp->left) q.push(tmp->left);if(tmp->right) q.push(tmp->right);}}
}// 迭代的前序遍历, root left right
void iterativePreorder(struct TreeNode *root) {if(root == NULL) return;stack<TreeNode *> sta;sta.push(root);while(!sta.empty()) {// 注意先进后出, 所以先right后leftTreeNode * tmp = sta.top();sta.pop();cout << tmp->data << " ";if(tmp->right) sta.push(tmp->right);if(tmp->left) sta.push(tmp->left);}
}

2. 二叉树的Z型遍历

题目: 二叉树的Z型遍历.
扩展: 层次遍历的从下到上遍历, 层次遍历的奇数层遍历, 层次遍历的从右到左遍历等,都可以使用这个代码进行变形

/***3/ \9  20/  \15   7
Z型遍历: 3, 20, 9, 15, 7
**/
vector<int> printLevelOrder(struct TreeNode* node) {queue<TreeNode *> q;vector<int> ans;stack<int> sta;if(!node) q.push(node);int k = 1;while(!q.empty()) {// 当前的长度是上一层的个数,这一点很重要,可以解决很多层次遍历相关的问题int len = q.size(); for(int i = 0; i < len; i ++) {TreeNode * tmp = q.top();q.pop();if(k % 2 == 1) {ans.push_back(tmp->data);}else {sta.push(tmp->data);}if(tmp->left) q.push(tmp->left);if(tmp->right) q.push(tmp->right);}if(k % 2 == 0) {while(!sta.empty()) {ans.push_back(sta.top());sta.pop();}}k ++;} return ans;
}

3. 平衡二叉树

题目: 给出一个二叉树,判断是否是平衡二叉树.
一棵高度平衡的二叉树的定义是:一棵二叉树中每个节点的两个子树的深度相差不会超过1。
扩展: 二叉树的最大高度, 也是使用类似的递归思想, 二叉树的最大宽度是使用层次遍历,

// 二叉树的最大高度
int getHeight(TreeNode *root) {if(root == NULL) return 0;    return max(getHeight(root->left),getHeight(root->right))+1;
}
bool isBalanced(TreeNode *root) {if(root == NULL) return true; if(abs(getHeight(root->left) - getHeight(root->right))>1){return false;}return isBalanced(root->left) && isBalanced(root->right);
}

4. 前序遍历的第k个结点

题目: 给个二叉树, 找到其前序遍历的第k个结点.
扩展: 中旬的遍历的第k个结点, 前序遍历的结点a的前一个结点 等

TreeNode* KthPostordernode(struct Node* root, int k) { static int flag = 0; if (root == NULL) return; if (flag <= k) { kthPostordernode(root->left, k); kthPostordernode(root->right, k); flag++; if (flag == k) return root;   }
}

5. 二叉树的对角线遍历

题目: 根据对角线顺序遍历二叉树.
扩展: 根据垂线从左到右遍历二叉树.

输入:

输出:
8 10 14
3 6 7 13
1 4

我们从右上向左下看进行层次划分,可以看出, root和root->right都是同一层, root->left是下一层, 我们可以使用map,将层数作为key, 每一层对应的节点作为vector<TreeNode *>作为values, 最后打印出来map中的值即可.

void diagOrderUtil(Node* root, int d, map<int, vector<int>> &diagVec) { if (!root) return; diagVec[d].push_back(root->data); diagOrderUtil(root->left, d+1, diagVec); diagOrderUtil(root->right, d, diagVec)
}
void diagOrder(Node* root) { map<int, vector<int> > diagVec; diagOrderUtil(root, 0, diagVec);   cout << "Diagonal Traversal of binary tree: \n";for (auto it = diagVec.begin(); it != diagVec.end(); ++it) { for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) {cout << *itr  << ' '; }  cout << 'n'; }
}

6. 构造二叉树

题目: 给出二叉树的前序和中序遍历,构造二叉树.
扩展: 其他几种构造二叉树的方式,建议多熟练掌握.
中旬: [1,2,3]
前序: [2,1,3]
return {2,1,3}.
知道一点,前序的第一个A是根节点, 在中序中找到前序的第一个节点A,就可以将中序分成左右两个子树,只有进行递归即可,

class Solution {/***@param preorder : A list of integers that preorder traversal of a tree*@param inorder : A list of integers that inorder traversal of a tree*@return : Root of a tree*/
public:typedef vector<int>::iterator Iter;TreeNode *buildTreeRecur(Iter istart, Iter iend, Iter pstart, Iter pend){if(istart == iend)return NULL;int rootval = *pstart;Iter iterroot = find(istart, iend, rootval);TreeNode *res = new TreeNode(rootval);res->left = buildTreeRecur(istart, iterroot, pstart+1, pstart+1+(iterroot-istart));res->right = buildTreeRecur(iterroot+1, iend, pstart+1+(iterroot-istart), pend);return res;}TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {return buildTreeRecur(inorder.begin(), inorder.end(), preorder.begin(), preorder.end());}
};

7. 对称二叉树

题目: 判断给定的二叉树是否是对称二叉树.
扩展: 二叉树的镜像

     1/   \2     2/ \   / \
3   4 4   3
是对称的, True1/ \2   2\   \3    3
不是对称的, False

// 比较两个二叉树是否互为镜像
bool isMirror(struct Node *root1, struct Node *root2) {
if (root1 == NULL && root2 == NULL)
return true;
if (root1 && root2 && root1->key == root2->key)
return isMirror(root1->left, root2->right) &&
isMirror(root1->right, root2->left);
return false;
}

// 是不是自身互为镜像,就是对称的二叉树
bool isSymmetric(struct Node* root)
{
return isMirror(root, root);
}

## 8. 最近公共祖先
> **题目**: 给定两个节点a和b,找出a和b的最近公共祖先.
> 最近公共祖先有很多方法,这里给出最好理解一个方法, 分别找到a和
> b所有祖先, 进行比较. 这里我们需要找到从root到A的路径,和从root到B的路径, 进行比较即可.
> **扩展**: 两个节点a和b的最近距离.
> a和b之间的距离, 我们可以先从a--root, root--b, 主要这个时候,多走了很多无用路径, 其实可以 a--lca(a,b)--b,这是最短路径, a--lca(a,b)--b = a--root--b - 2*lca(a,b)

bool findPath(Node *root, vector &path, int k) {
if (root == NULL) return false;
path.push_back(root->key);
if (root->key == k)
return true;
if ( (root->left && findPath(root->left, path, k)) ||
(root->right && findPath(root->right, path, k)) )
return true;
path.pop_back();
return false;
}
int findLCA(Node *root, int a, int b) {
vector patha, pathb;
if (!findPath(root, patha, n1) || !findPath(root, pathb, n2)) return -1;
int i;
for (i = 0; i < path1.size() && i < pathb.size(); i++) {
if(patha[i] != pathb[i]){
return patha[i-1];
}
}
return -1;
}

## 9. 寻找树中最左下结点的值
> **题目**: 给定一棵二叉树,找到这棵树最中最后一行中最左边的值.
> **扩展**: 最右边的值.
> **解释**: 使用深度优先搜索dfs,当我们第一次访问一个深度为depth的节点x(之前只访问过深度小于depth的节点)时,x一定是depth深度的最左节点,用这个节点更新Ans。即我们维护一个最大深度,当遍历到一个点的深度大于最大深度时,用这个节点来更新答案,并更新最大深度即可。时间复杂度O(n)。

int findBottomLeftValue(TreeNode * root) {
int ans_data = 0, ans_depth = 0;
return findBottomLeftValue(root, 1, ans_data, ans_depth);
}
int findBottomLeftValue(TreeNode * root, int depth, int &ans_data, int &ans_depth) {
if (ans_depth < depth) {
ans_data = root->val;
ans_depth = depth;
}
if (root->left) findBottomLeftValue(root->left, depth+1, ans_data, ans_depth);
if (root->right) findBottomLeftValue(root->right, depth+1, ans_data, ans_depth);
return ans_data;
}

## 10. 二叉树的最长连续子序列
> **题目**: 给一棵二叉树,找到最长连续路径的长度。
这条路径是指 任何的节点序列中的起始节点到树中的任一节点都必须遵循 **父-子** 联系。最长的连续路径必须是从父亲节点到孩子节点(不能逆序)。

样例1:

输入:
{1,#,3,2,4,#,#,#,5}
输出:3
说明:
这棵树如图所示
1

3
/
2 4

5
最长连续序列是3-4-5,所以返回3.
样例2:

输入:
{2,#,3,2,#,1,#}
输出:2
说明:
这棵树如图所示:
2

3
/
2
/
1
最长连续序列是2-3,而不是3-2-1,所以返回2.


void longestConsecutiveUtil(Node* root, int curLength, int expected, int& res) {
if (root == NULL)
return;
if (root->data == expected)
curLength++;
else
curLength = 1;
res = max(res, curLength);
longestConsecutiveUtil(root->left, curLength,
root->data + 1, res);
longestConsecutiveUtil(root->right, curLength,
root->data + 1, res);
}


**扩展**: 给定一棵二叉树,找到最长连续序列路径的长度(节点数)。
路径起点跟终点可以为二叉树的任意节点。

例1:

输入:
{1,2,0,3}
输出:
4
解释:
1
/
2 0
/
3
0-1-2-3
例2:

输入:
{3,2,2}
输出:
2
解释:
3
/
2 2
2-3


class Solution {
public:
int longestConsecutive2(TreeNode * root) {
// write your code here
int res = 0;
helper(root, root, res);
return res;
}
pair<int, int> helper(TreeNode* node, TreeNode* parent, int& res) {
if (!node) return {0, 0};
auto left = helper(node->left, node, res);
auto right = helper(node->right, node, res);
res = max(res, left.first + right.second + 1);
res = max(res, left.second + right.first + 1);
int inc = 0, dec = 0;
if (node->val == parent->val + 1) {
inc = max(left.first, right.first) + 1;
} else if (node->val + 1 == parent->val) {
dec = max(left.second, right.second) + 1;
}
return {inc, dec};
}
};

## 11. 左边看到的二叉树结点
> 题目: 给你一个二叉树,打印出来从左边视角看到的所有结点.

Input 1:
1
/
2 3
/ \
4 5 6
Output 1: 1 2 4

Input 2:
1
/
2 3
\
4

5

6
Output 2: 1 2 4 5 6

> 解析:
> 方法一: 用我们上面提到的层次遍历, 打印出来每一层的第一个结点即可.
> 方法二: 维护一个从左到右的最大等级, 如果当前等级大于最大等级,则是左边看到的,否则不是.
> 这里只给出第二种方法的代码.

// leftView(root, 1, 0, ans)
void leftView(struct node *root, int level, int &max_level, vector &ans) {
if (root==NULL) return;
if (max_level < level) {
ans.push_back(root->data);
max_level = level;
}
leftView(root->left, level+1, max_level, ans);
leftView(root->right, level+1, max_level, ans);
}


## 参考
1. http://www.cnblogs.com/grandyang/p/6864398.html
2. https://www.jiuzhang.com/solution/
3. https://www.geeksforgeeks.org/binary-tree-data-structure/
4. https://www.geeksforgeeks.org/print-left-view-binary-tree/

Coing-二叉树(bibary Tree)相关推荐

  1. 【数据结构】二叉树 (Binary Tree)

    目录 一. 什么是树? 二. 二叉树 特殊二叉树 二叉树的性质 二叉树的存储 二叉树的遍历 二叉树的基本操作 一.什么是树? 之前咱们学习了一些简单的数据结构,如顺序表,链表,这些都是线性结构,线性结 ...

  2. 二叉树 Binary Tree

    我怀着激动的心 走上了这颗树 今天是2021年11月12日 今天开始上树!!!!!! 生活中的树形结构: 树:  根节点: 一棵树有且只有一个根节点就是最上面的节点 兄弟节点: 每一行的节点  它们具 ...

  3. 『数据结构与算法』解读树(Tree)和二叉树(Binary Tree)!

    『数据结构与算法』解读树(Tree)和二叉树(Binary Tree)! 文章目录 一. 树 1.1. 树的定义 1.2. 树的基本术语 1.3. 树的性质 二. 二叉树 2.1. 二叉树的定义 2. ...

  4. [Swift]LeetCode968.监控二叉树 | Binary Tree Cameras

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblog ...

  5. 数据结构--二叉树 Binary Tree

    文章目录 1.概念 2.存储方式 2.1 链式存储(二叉树代码大部分是链式实现的) 2.2 顺序存储(基于数组) 3.二叉树的遍历 3.1 基于链表的二叉树实现代码 3.2 基于数组的二叉树实现代码 ...

  6. 由任意二叉树的前序遍历序列和中序遍历序列求二叉树的思想方法_算法与数据结构基础 - 二叉树(Binary Tree)...

    二叉树基础 满足这样性质的树称为二叉树:空树或节点最多有两个子树,称为左子树.右子树, 左右子树节点同样最多有两个子树. 二叉树是递归定义的,因而常用递归/DFS的思想处理二叉树相关问题,例如Leet ...

  7. 树和二叉树(TreeBinary Tree)

    树是一种非线性的数据结构,它在表示机构的组织关系图等方面非常好用,树的示意图如图1所示. 图1 - 树的示意图 一.表达方式 树主要由自身的数据域和其父节点构成,所以表示方法可以利用一个结构体数据表示 ...

  8. c++ 结构体遍历_二叉树(Binary Tree)的建立与遍历——C语言实现

    一.运行环境简介 编辑器:VSCode + MicroSoft原生插件; :cat:‍:dragon:运行环境: MinGW ; :cat:‍:bust_in_silhouette:常用指令: gcc ...

  9. 【Algo】二叉树(Binary Tree)

    Backto Algo Index 先上代码, 定义 typedef struct TNode {ElemType data;TNode* lchild;TNode* rchild; } TNode, ...

最新文章

  1. 2022-2028年中国丙烯酸酯橡胶行业市场深度分析及投资前景分析报告
  2. MIT开放式课程“自然语言处理”介绍
  3. 用户界面设计十大基本原则
  4. 如何确定最初克隆本地Git存储库的URL?
  5. 用 LaTeX 排版编程技术书籍的一些个人经验
  6. anaconda python2.7,安装在windows中的Python 2.7 Anaconda2
  7. vb怎么抓取html的class,vbs怎么获取网页内容
  8. java 继承和内部类_Java自学-接口与继承 内部类
  9. c语言设计指导实训,C语言程序设计实训指导
  10. ajax提交前先验证,jQuery验证AJAX之前提交(jQuery validation before AJAX sub
  11. Linux 进程间通信(IPC)---大总结
  12. Linux操作系统下进程讲解(史上最强总结)
  13. NYOJ 46 最少乘法次数
  14. SQL Server 自增字段归零等问题
  15. es search delete
  16. 在写csdn博客时,如何实现用超链接进行页面内跳转?
  17. Latex中文期刊论文模板
  18. linux内网穿透(内外网服务器端口映射)
  19. excel怎么能把字竖着打出来_excel表格怎么把字竖着 怎么把excel表格里的字变成竖的?...
  20. http协议1.0和1.1的区别

热门文章

  1. html css 布局知识概况
  2. Eclipse:xml文件中添加.xsd约束文件
  3. ubuntu 14.04 使用apt-get出现如下问题解决办法
  4. 如何在VMware虚拟机上安装Linux操作系统(Ubuntu)
  5. Matlab与线性代数 -- 零矩阵
  6. 【ACM】杭电OJ 1789(Doing Homework again)
  7. 关于 AIOps 的过去与未来,微软亚洲研究院给我们讲了这些故事
  8. 吊打一切:YOLOv4的tricks汇总
  9. 154 万 AI 开发者用数据告诉你,中国 AI 如何才能弯道超车?| 中国 AI 应用开发者报告...
  10. 人工智能技术在内容行业的应用:AI对中长尾内容平台还是奢侈品