算法笔记-第九章-二叉树的遍历
二叉树的先序遍历
//二叉树的先序遍历
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 50;
struct Node //用结构体表示左子树和右子树的数据
{
int l, r;
} nodes[MAXN];
vector<int> pre;//设置一个数组用来存储先序遍历的数据
void preOrder(int root)
{
if (root == -1)
{
return;
}
pre.push_back(root);
preOrder(nodes[root].l);//用递归的方式将输入的左子树和右子树都进行重新判断,然后输入到数组当中
preOrder(nodes[root].r);
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d%d", &nodes[i].l, &nodes[i].r);
}
preOrder(0);//直接将头结构体带入
//输出数组中数据
for (int i = 0; i < (int)pre.size(); i++)
{
printf("%d", pre[i]);
if (i < (int)pre.size() - 1)
{
printf(" ");
}
}
return 0;
}
二叉树的中序遍历
//思路还是递归的思路
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 50;
struct Node
{
int l, r;
} nodes[MAXN];
vector<int> in;
//思路其实就是利用递归遍历左中右,然后存入到数组当中
void inOrder(int root)
{
if (root == -1)
{
return;
}
inOrder(nodes[root].l);
in.push_back(root);
inOrder(nodes[root].r);
}
//主要是一个函数就是主要的
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d%d", &nodes[i].l, &nodes[i].r);
}
inOrder(0);
for (int i = 0; i < (int)in.size(); i++)
{
printf("%d", in[i]);
if (i < (int)in.size() - 1)
{
printf(" ");
}
}
return 0;
}
二叉树的后序遍历
//这道题目因为还是用递归的方式进行的,所以还是
//简答用递归左右中的方式进行
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 50;
struct Node {
int l, r;
} nodes[MAXN];
vector<int> post;
void postOrder(int root) {
if (root == -1) {
return;
}
postOrder(nodes[root].l);
postOrder(nodes[root].r);
post.push_back(root);
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d%d", &nodes[i].l, &nodes[i].r);
}
postOrder(0);
for (int i = 0; i < (int)post.size(); i++) {
printf("%d", post[i]);
if (i < (int)post.size() - 1) {
printf(" ");
}
}
return 0;
}
二叉树的层次遍历
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 50;
struct Node
{
int l, r;
} nodes[MAXN];
vector<int> layer;
//就是根据遍历的思路来就好
void layerOrder(int root)
{
queue<int> q;
q.push(root);
while (!q.empty())
{
int front = q.front();//去除首元素
q.pop();//删除首元素
layer.push_back(front);
//加入到队列中
if (nodes[front].l != -1)
{
q.push(nodes[front].l);
}
if (nodes[front].r != -1)
{
q.push(nodes[front].r);
}
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d%d", &nodes[i].l, &nodes[i].r);
}
layerOrder(0);
//输入首地址0
for (int i = 0; i < (int)layer.size(); i++
{
printf("%d", layer[i]);
if (i < (int)layer.size() - 1)
{
printf(" ");
}
}
return 0;
}
注意点一:
queue q;和queue<node*> q两者之间的区别是
一个是不可以改变队列中的元素的,一个数带入的时候就是可以直接改变原函数的
注意点二:
层次遍历使用的是队列,基本思路是
访问root,加入队列
如果有左节点,加入左节点
如果有右节点,加入右节点//这里就是要递归方式进行了,
//然后重新进行放问(当然是要用while)
二叉树的高度
//二叉树的高度
//题目本身就是简单的递归遍历
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 50;
struct Node
{
int l, r;
} nodes[MAXN];
int getHeight(int root)
{
if (root == -1)
{
return 0;
}
int leftHeight = getHeight(nodes[root].l);
int rightHeight = getHeight(nodes[root].r);
return max(leftHeight, rightHeight) + 1;
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d%d", &nodes[i].l, &nodes[i].r);
}
printf("%d", getHeight(0));
return 0;
}
二叉树的结点层号
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 50;
struct Node
{
int l, r;
} nodes[MAXN];
int layers[MAXN];
void layerOrder(int root)
{
queue<int> q;
q.push(root);
int layer = 1;
while (!q.empty())
{
int cnt = q.size();//一层中有多少个结点
for (int i = 0; i < cnt; i++)
{
int front = q.front();
q.pop();
layers[front] = layer;//结点代表的层数
//
if (nodes[front].l != -1)
{
q.push(nodes[front].l);
}
if (nodes[front].r != -1)
{
q.push(nodes[front].r);
}
}
layer++;//一层一层的来
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d%d", &nodes[i].l, &nodes[i].r);
}
layerOrder(0);
for (int i = 0; i < n; i++)
{
printf("%d", layers[i]);
if (i < n - 1)
{
printf(" ");
}
}
return 0;
}
翻转二叉树
本题就是使用了递归的思路进行翻转二叉树,
再设置两个数组进行遍历二叉树然后输入到数组当中
#include <cstdio>
#include <vector>
#includ e <algorithm>
using namespace std;
const int MAXN = 50;
struct Node
{
int l, r;
} nodes[MAXN];
vector<int> pre, in, post;//
void preOrder(int root)
{
if (root == -1)
{
return;
}
pre.push_back(root);
preOrder(nodes[root].l);
preOrder(nodes[root].r);
}
void inOrder(int root)
{
if (root == -1)
{
return;
}
inOrder(nodes[root].l);
in.push_back(root);
inOrder(nodes[root].r);
}
void revert(int root) //利用的递归将左右孩子节点分别翻转
{
if (root == -1)
{
return;
}
revert(nodes[root].l);
revert(nodes[root].r);
swap(nodes[root].l, nodes[root].r);
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d%d", &nodes[i].l, &nodes[i].r);
}
revert(0);//用递归的思想进行翻转
preOrder(0);//也都是用递归的思路将二叉树的数据存储到数组当中
inOrder(0);
for (int i = 0; i < (int)pre.size(); i++) //先序遍历
{
printf("%d", pre[i]);
if (i < (int)pre.size() - 1)
{
printf(" ");
}
}
printf("\n");
for (int i = 0; i < (int)in.size(); i++)
{
printf("%d", in[i]);
if (i < (int)in.size() - 1)
{
printf(" ");
}
}
return 0;
}
翻转二叉树不同的方法
方法一:用栈实现
思路:左右节点进行交换,循环翻转每个节点的左右子节点,将未翻转的子节点存入栈中,循环直到栈里所有节点都循环交换完为止
public TreeNode invertTree(TreeNode root)
{
if (root == null)//开始进行判断是否为空
{
return null;
}
Stack<TreeNode> stack = new Stack<>();//设置一个栈
stack.push(root);//将根节点进行入栈
while (!stack.isEmpty())
{
final TreeNode node = stack.pop();//出栈进行访问和翻转孩子结点
final TreeNode left = node.left;
node.left = node.right;
node.right = left;
//下面:从内到外进行翻转,所有先将右变的节点进行入栈,然后进行翻转,然后再是左边的
if (node.left != null)
{
stack.push(node.left);
}
if (node.right != null)
{
stack.push(node.right);
}
}
return root;
}
方法二:用队列实现
/用队列实现
//队列和栈的思路是一样的,都是根据进行入队和出队进行的
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
TreeNode left = node.left;
node.left = node.right;
node.right = left;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
return root;
}
方法三:
因为翻转所有的节点的孩子都是翻转左右节点,
所有在递归的思路中=确立了节点,用前序的思路直接遍历和
翻转二叉树,知道节点为NULL
public TreeNode invertTree(TreeNode node) {
if (node == null) {
return null;
}
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
invertTree(node.left);
invertTree(node.right);
return node;
}
先序中序还原二叉树
//先序中序还原二叉树
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 50;
struct Node {
int l, r;
} nodes[MAXN];
vector<int> pre, in, post;//创建数组
//这里就是用递归的思想,下面的知识点有提到
int buildTree(int preL, int preR, int inL, int inR) {
if (preL > preR) {
return -1;
}
int root = pre[preL];
int inIndexOfRoot;
for (int i = inL; i <= inR; i++) {
if (in[i] == root) {
inIndexOfRoot = i;
break;
}
}
int leftCount = inIndexOfRoot - inL;
nodes[root].l = buildTree(preL + 1, preL + leftCount, inL, inIndexOfRoot - 1);
nodes[root].r = buildTree(preL + leftCount + 1, preR, inIndexOfRoot + 1, inR);
return root;
}
void postOrder(int root) {//输出后序的数组
if (root == -1) {
return;
}
postOrder(nodes[root].l);
postOrder(nodes[root].r);
post.push_back(root);
}
int main() {
int n, x;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &x);
pre.push_back(x);
}
for (int i = 0; i < n; i++) {
scanf("%d", &x);
in.push_back(x);
}
int root = buildTree(0, n - 1, 0, n - 1);
postOrder(root);
for (int i = 0; i < (int)post.size(); i++) {
printf("%d", post[i]);
if (i < (int)post.size() - 1) {
printf(" ");
}
}
return 0;
}
基础知识点
从前序与中序遍历序列构造二叉树
力扣题目
官方题解
class Solution {
private:
unordered_map<int, int> index;
public:
TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return nullptr;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = index[preorder[preorder_root]];
// 先把根节点建立出来
TreeNode* root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
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 myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
};
大佬总结前中后序还原二叉树
后序中序还原二叉树
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 50;
struct Node {
int l, r;
} nodes[MAXN];
vector<int> pre, in, post;
int buildTree(int postL, int postR, int inL, int inR) {
if (postL > postR) {
return -1;
}
int root = post[postR];
int inIndexOfRoot;
for (int i = inL; i <= inR; i++) {
if (in[i] == root) {
inIndexOfRoot = i;
break;
}
}
int leftCount = inIndexOfRoot - inL;
nodes[root].l = buildTree(postL, postL + leftCount - 1, inL, inIndexOfRoot - 1);
nodes[root].r = buildTree(postL + leftCount, postR - 1, inIndexOfRoot + 1, inR);
return root;
}
void preOrder(int root) {
if (root == -1) {
return;
}
pre.push_back(root);
preOrder(nodes[root].l);
preOrder(nodes[root].r);
}
int main() {
int n, x;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &x);
post.push_back(x);
}
for (int i = 0; i < n; i++) {
scanf("%d", &x);
in.push_back(x);
}
int root = buildTree(0, n - 1, 0, n - 1);
preOrder(root);
for (int i = 0; i < (int)pre.size(); i++) {
printf("%d", pre[i]);
if (i < (int)pre.size() - 1) {
printf(" ");
}
}
return 0;
}
力扣中序后序还原二叉树
力扣题目
力扣题解实在nb
class Solution {
public:
unordered_map<int, int>hash;//保存根节点
TreeNode* buildTreeHelper(vector<int>& inorder, int inleft, int inright, vector<int>& postorder, int postleft, int postright) {
if (postleft == postright)return nullptr;
TreeNode* root = new TreeNode(postorder[postright - 1]);
int index = hash[postorder[postright - 1]];
//构建他的左子树
root->left = buildTreeHelper(inorder, inleft, index, postorder, postleft, postright - inright + index);
//构建他的右子树
root->right = buildTreeHelper(inorder, index + 1, inright, postorder, postright - inright + index, postright - 1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
for (int i = 0; i < inorder.size(); i++) {
hash[inorder[i]] = i;//记录根节点
}
return buildTreeHelper(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
};
二叉树的静态实现
知识点整理:
所谓的静态二叉树指;节点左右指针域使用int型代替,用来
表示左右子树的根节点数组中的下标
结点的定义可以是:
struct node
{
typedef data;//数据域
int lchild;
int rchild;
}Node[maxn];//结点数组,maxn为结点上限的个数
在这样的定义之下,节点动态生成就可以转变为如下的静态指定
int index = 0;
int newnode(int v)//分配一个node数组的节点给新的节点
{
node[index].data = v;//数据为空
node[index].lchild = -1;
node[index].rchild = -1;
return index++;
}
利用静态进行二叉树的查找,插入,建立的代码
查找
void search(int root, int x, int newdata)
{
if (root == -1)
{
return;
}
if (node[root].data == x)//这个就是条件:找到了对应的节点,然后修改成newnode
{
node[root].data = newdata;
}
search(node[root].lchild, x, newdata);
search(node[root].rchild, x, newdata);
}
插入
//插入
void insert(int& root, int x)
{
if (root == -1)//空树,说明查找失败,也就是插入的位置
{
root = newnode;
return;
}
if (有二叉树的性质x应该插入在左子树)
{
insert(node[root]. lchild, x);
}
else
{
insert(node[root].rchild, x);
}
}
//二叉树的建立,函数返回节点root的下标
int creat(int data[], int n)
{
int root = -1;//建立新的节点
for (int i = 0; i < n; i++)
{
insert(root, data[i]);//将data【0】-data[n-1]插入到二叉树中
}
return root;
}
关于二叉树的先序中序和后序遍历操作
//先序的例子
void preorder(int root)
{
if (root == -1)
{
return;
}
printf("%d\n",node[root].data);
preorder(node[root].lchild);
preorder(node[root].rchild);
}
//再举一个例子
//层序遍历
void lagerorder(int root)
{
queue<int> q;//此处队列中存放结点的下标
q.push(root);//将根节点地址入队
while (q.empty())
{
int now = q.front();//取出首元素
q.pop();
printf("%d ", node[now].data);//访问对首元素
if (node[now].lchild != -1) q.push(node[now].lchild);//左子树非空
if (node[now].rchild != -1) q.push(node[now].rchild);
}
}