判断部分
- 二叉树就是度为二的有序树 false
一棵度为二的有序树与一棵二叉树的区别在于:有序树的结点次序是相对于另一结点而言的,如果有序树中的子树只有一个孩子时,这个孩子结点就无须区分其左右次序,而二叉树无论其孩子数是否为2,均需确定其左右次序,也就是说二叉树的结点次序不是相对于另一结点而言而是确定的
- 树是表示多对多关系的数据结构 false
树的结构是一对多,树有多个指向的节点,所以非线性数据结构
- 一棵树中位于同一层上的结点称为兄弟结点 false
同一个双亲的孩子之间互称兄弟(Sibling),双亲在同一层次的结点互为堂兄弟。结点的祖先是从根到该结点所经分支上的所有结点。反之,以某结点为根的子树中的任一结点都称为该结点的子孙。
- 一棵树中,某结点位置上方各层中的所有结点都是该结点的祖先。
不是各层,只有从根结点到该结点的所有结点才是该结点的祖先。 结点的祖先是从根到该结点所经分支上的所有结点。反之,以某结点为根的子树中的任一结点都称为该结点的子孙。
- 存在一棵总共有2016个结点的二叉树,其中有16个结点只有一个孩子。
假设没有孩子的结点(叶结点)个数为n₀,只有一个孩子的结点(度为1的结点)个数为n₁,有两个孩子的结点(度为2的结点)个数为n₂。
则n₀+n₁+n₂=2016∵
n₀=n₂+1
(二叉树的性质:叶结点个数等于度为2的结点个数加1 多一个度为2的结点就能多一条分支)
n₀+n₁+n₂=2016
没有其余条件(如完全二叉树)所以只能代入验证 得 2n₂ + 1 + 16 = 2016
结果不为整数 答案错误
- 已知一棵二叉树的先序遍历结果是ABC, 则CAB不可能是中序遍历结果 true
由中序遍历画出子树,不符合先序 CAB 先序ABC, B在C前面
- 若一个结点是某二叉树的中序遍历序列的最后一个结点,则它必是该树的前序遍历序列中的最后一个结点。 false
反例:若为左单支树则最后一个为根节点。题干改为一个叶结点时正确,反例不存在
- 若
A
和B
都是一棵二叉树的叶子结点,则存在这样的二叉树,其前序遍历序列为...A...B...
,而中序遍历序列为...B...A... false
例子:A,B都是叶子结点所以A只有可能A在B的左边 中序遍历位置不变
- 某二叉树的后序和中序遍历序列正好一样,则该二叉树中的任何结点一定都无右孩子 true
写出顺序出答案
- 一棵有n个结点的完全二叉树,其叶结点个数是确定的。 true
已知:n1 + n2 + n0 = n
n0 = n2 + 1
满二叉树中 n1只能有0或1个 ,n0 = (n - n1 + 1) / 2;
- 将一棵完全二叉树存于数组中(根结点的下标为1)。则下标为23和24的两个结点是兄弟。
对于n个结点的完全二叉树tree,有如下特点:
(1)若i为奇数且i>1,那么tree的左兄弟为tree[i-1];
(2)若i为偶数且i<n,那么tree的右兄弟为tree[i+1];
(3)若i>1,tree的父亲节点为tree[i div 2];(div:整除)
(4)若2 * i<=n,那么tree的左孩子为tree[2 * i];若2 * i+1<=n,那么tree的右孩子为tree[2 * i+1];
(5)若i>(n div 2),那么tree[i]为叶子结点(对应(3));
(6)若i<((n-1) div 2),那么tree[i]必有两个孩子(对应(4));
选择部分
对于先序遍历与中序遍历结果相同的二叉C)
A.一般二叉树
B.任一结点均无右孩子的二叉树
C.任一结点均无左子树的二叉树
D.以上都不是
一、前序序列与后序序列
1.前序序列和后序序列相同
空树或者只有根节点的二叉树。
2.前序序列和后序序列相反
(1)当且仅当二叉树中只有一个叶子节点。
(2)二叉树的高度和其节点个数相同。
二、前序序列与中序序列
1.前序序列和中序序列相同
空树或缺左子树的单支二叉树。
2.前序序列和中序序列相反
(1)二叉树为空或者只有一个节点。
(2)若二叉树不为空,则任意节点不能同时用于左孩子和右孩子。
(3)若二叉树不为空,则任意节点没有右孩子。
三、中序序列与后序序列
1.中序序列和后序序列相同
空树或者缺右子树的单支二叉树。
2.中序序列和后序序列相反
任意节点没有左孩子节点。
408
若将一棵树 T 转化为对应的二叉树 BT,则下列对 BT 的遍历中,其遍历序列与 T 的后根遍历序列相同的是:(B)
A.先序遍历 B.中序遍历 C.后序遍历 D.按层遍历
后根遍历树可分为两步:①从左到右访问双亲结点的每个孩子(转化为二叉树后就是先访问根结点再访问右子树);②访问完所有孩子后再访问它们的双亲结点(转化为二叉树后就是先访问左子树再访问根结点),因此树T的后根遍历序列与其相应二叉树BT的中序遍历序列相同。对于此类题,采用特殊值法求解通常会更便捷,左下图树T转换为二叉树BT的过程如下图所示,树T的后序遍历序列显然和其相应二叉树BT的中序遍历序列相同,均为5,6,7,2,3,4, 1。因此选B.
若结点 p 与 q 在二叉树 T 的中序遍历序列中相邻, 且 p 在 q 之前,则下列 p 与 q 的关系中,不可能的是(B)
I. q 是 p 的双亲
II. q 是 p 的右孩子
III. q 是 p 的右兄弟
IV. q 是 p 的双亲的双亲
A.仅 I B.仅 III C.仅 II、III D.仅 II、IV
编程部分
根据后序和中序遍历输出先序遍历
题干:
根据后序遍历和先序遍历将二叉树还原,数据量小于30
输入格式:
第一行给出正整数N(≤30),是树中结点的个数。随后两行,每行给出N个整数,分别对应后序遍历和中序遍历结果,数字间以空格分隔。题目保证输入正确对应一棵二叉树。
输出格式:
在一行中输出
Preorder:
以及该树的先序遍历结果。数字间有1个空格,行末不得有多余空格。
样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
// Preorder: 4 1 3 2 6 5 7
代码:
#include <stdio.h>
#include <stdlib.h>
int postOrder[31], inOrder[31], n;
typedef struct BiNode {
int data;
struct BiNode *left;
struct BiNode *right;
}bNode, *bTree;
void preOrder(bTree t)
{
if (!t) return ;
printf(" %d", t->data);
preOrder(t->left);
preOrder(t->right);
}
bTree creat()
{
bTree t = (bTree)malloc(sizeof(bNode));
t->left = NULL;
t->right = NULL;
return t;
}
//后序遍历中该子树根节点的下标 root
//中序遍历中该子树结点的范围 [l, r]
bTree build(int root, int l, int r)
{
bTree t = creat();
t->data = postOrder[root];
//寻找根节点在中序遍历的idx 以此分隔左右子树
for (int i = l; i <= r; i ++ )
{
if (inOrder[i] == postOrder[root])
{
if (i > l) //左子树存在
t->left = build(root - r + i - 1, l, i - 1);
if (i < r) //右子树存在
t->right = build(root - 1 ,i + 1, r);
}
}
return t;
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &postOrder[i]);
for (int i = 0; i < n; i ++ ) scanf("%d", &inOrder[i]);
bTree t = build(n - 1, 0, n - 1);
printf("Preorder:");
preOrder(t);
return 0;
}
本题的重点在于区间判读 根据后续遍历根节点的下标判断出左右子树各自的范围
再根据左右子树的范围 在后续遍历中反推出左右子树各自根结点的下标
以此不断递归调用函数,重新构建二叉树
还原二叉树
题干:
给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度。
输入格式:
输入首先给出正整数N(≤50),为树中结点总数。下面两行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区别大小写)的字符串。
输出格式:
输出为一个整数,即该二叉树的高度。
样例:
9
ABDFGHIEC
FDHGIBEAC
// 5
思路:如上题
代码:
#include <stdio.h>
#include <stdlib.h>
char preOrder[51], inOrder[51];
int n;
typedef struct BiNode {
char data;
struct BiNode *left;
struct BiNode *right;
}bNode, *bTree;
int getHeight(bTree t)
{
if (!t) return 0;
int l = getHeight(t->left);
int r = getHeight(t->right);
return l > r ? l + 1 : r + 1;
}
bTree creat()
{
bTree t = (bTree)malloc(sizeof(bNode));
t->left = NULL;
t->right = NULL;
return t;
}
bTree build(int root, int l, int r)
{
bTree t = creat();
t->data = preOrder[root];
for (int i = l; i <= r; i ++ )
{
if (inOrder[i] == preOrder[root])
{
if (i > l)
t->left = build(root + 1, l, i - 1);
if (i < r)
t->right = build(i - l + root + 1, i + 1, r);
}
}
return t;
}
int main()
{
scanf("%d", &n);
scanf("%s", preOrder);
scanf("%s", inOrder);
bTree t = build(0, 0, n - 1);
printf("%d", getHeight(t));
return 0;
}
玩转二叉树
题干:
给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。
输入格式:
输入第一行给出一个正整数
N
(≤30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。
输出格式:
在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。
样例:
7
1 2 3 4 5 6 7
4 1 3 2 6 5 7
// 4 6 1 7 5 3 2
思路:镜面反转,是指将所有非叶结点的左右孩子对换。这句话是这道题的核心
前面的实现部分全部相同,已知叶结点没有左右孩子,所以这句话的意思是
将所有左孩子替换为右孩子,
这里就是说若左子树存在则还原的二叉树的右子树是原二叉树的左子树。
原二叉树右子树同理
代码:
#include <stdio.h>
#include <stdlib.h>
int inOrder[31], preOrder[31], n;
typedef struct BiNode {
int data;
struct BiNode *left;
struct BiNode *right;
}bNode, *bTree;
bTree creat()
{
bTree t = (bTree)malloc(sizeof(bNode));
t->left = NULL;
t->right = NULL;
return t;
}
void levelOrder(bTree t)
{
if (!t) return ;
bTree q[31];
int rear = 0, head = 0, flag = 0;
q[rear ++ ] = t;
while (head != rear)
{
bTree top = q[head ++ ];
if (!flag) flag = 1;
else putchar(' ');
printf("%d", top->data);
if (top->left) q[rear ++ ] = top->left;
if (top->right) q[rear ++ ] = top->right;
}
}
bTree build(int root, int l, int r)
{
bTree t = creat();
t->data = preOrder[root];
for (int i = l; i <= r; i ++ )
{
if (inOrder[i] == preOrder[root])
{
if (i > l)
t->right = build(root + 1, l, i - 1);
if (i < r)
t->left = build(root + i - l + 1, i + 1, r);
}
}
return t;
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &inOrder[i]);
for (int i = 0; i < n; i ++ ) scanf("%d", &preOrder[i]);
bTree t = build (0, 0, n - 1);
levelOrder(t);
return 0;
}