二叉树的本质就是链表,只不过它的next节点换成了一左一右两个叶子节点,首先为了应付选择题,需要了解二叉树的性质:
1.二叉树中度为0,1,2,的结点分别有a,b,c个,则a=c+1
2.树的结点数为树的总度数+1
3.二叉树的第i层至多有2的i-1次方个结点(层数从1开始算)
4.m叉树第i层至多有m的i-1次方个结点(层数从1开始算)
5.高度为h的二叉树至多有2的h次方-1个结点(满二叉树)
6.高度为h的M叉树至多有m的h次方-1除m-1个结点(等比数列求和)
7.具有n个结点的完全二叉树的高度为⌈log2(n+1)⌉或⌊log2n⌋+1
8.高为h的满二叉树有2的h次方-1个结点
9.高为h的完全二叉树至多有2的h次方-1个节点(满二叉树),至少有2的h-1次方个节点(最后一层一个节点)
10.第 i 个结点所在层次为⌈log2(n+1)⌉或⌊log2n⌋+1
11.设完全二叉树度为0,1,2的节点有a,b,c个,则b等于0或1,a+c一定是奇数
12.完全二叉树有奇数个节点(2k-1),则a=k,b=0,c=k-1,如果有偶数个节点(2k)则a=k,b=1,c=k-1
再来看看二叉树的创建,第一种是根据前序序列创建(不唯一,但用到了),另一种是根据两种遍历序列创建,首先给出前序遍历的代码‘
#include<iostream>
using namespace std;
typedef struct tree
{
char data;
tree* left;
tree* right;
}tree;
tree* fcreat(string s,int &i)
{
if (s[i] == '#')
{
i++;
return nullptr;
}
tree* point = new tree;
point->data = s[i];
point->left = point->right = nullptr;
i++;
point->left = fcreat(s, i);
point->right = fcreat(s, i);
return point;
}
接着是三种序列创建,注意形参以区分
#include<iostream>
#include<vector>
using namespace std;
typedef struct tree
{
int data;
tree* left;
tree* right;
}tree;
tree* set1(vector<char>f, vector<char>i, int left1, int right1, int left2, int right2)
{
if (left1 > right1||left2>right2)
{
return nullptr;
}
tree* root = new tree;
root->data = f[left1];
int mid = left2;
while (i[mid] != f[left1])
{
mid++;
}
int leftsize = mid - left2 + 1;
root->left = set1(f, i, left1 + 1, left1 + leftsize - 1, left2, mid - 1);
root->right = set1(f, i, left1 + leftsize, right1, mid + 1, right2);
}
tree* set2(vector<char>f, vector<char>b, int left1, int right1, int left2, int right2)
{
if (left1 > right1 || left2 > right2)
{
return nullptr;
}
tree* root = new tree;
root->data = f[left1];
int mid = left2;
while (b[mid] != f[left1+1])
{
mid++;
}
int leftsize = mid - left2 + 1;
root->left = set1(f, b, left1 + 1, left1 + leftsize, left2, mid);
root->right = set1(f, b, left1 + leftsize+1, right1, mid + 1, right2-1);
}
tree* set3(vector<char>i, vector<char>b, int left1, int right1, int left2, int right2)
{
if (left1 > right1 || left2 > right2)
{
return nullptr;
}
tree* root = new tree;
root->data = b[right2];
int mid = left1;
while (b[mid] != root->data)
{
mid++;
}
int leftsize = mid - left2 + 1;
root->left = set1(i, b, left1, mid-1, left2,left2+leftsize-1);
root->right = set1(i, b, mid+1, right1, left2+leftsize, right2 - 1);
}
}
遍历分为七种,前序中序后序的递归,非递归以及层序遍历
以下为递归状态下的前序,中序以及后序遍历
#include<iostream>
#include<vector>
using namespace std;
typedef struct tree
{
int data;
tree* left;
tree* right;
}tree;
void set(tree* root,vector<int>&str)
{
if (root == nullptr)
{
return;
}
str.push_back(root->data);
set(root->left, str);
set(root->right, str);
}
void set1(tree* root, vector<int>& str)
{
if (root == nullptr)
{
return;
}
set1(root->left, str);
str.push_back(root->data);
set1(root->right, str);
}
void set2(tree* root, vector<int>& str)
{
if (root == nullptr)
{
return;
}
set2(root->left, str);
set2(root->right, str);
str.push_back(root->data);
}
以下为非递归的中序遍历,前序遍历与后序遍历
中序遍历思路:中序遍历的基本逻辑是左中右,所以先将左树节点入栈,再出栈并加入vector(此时代表中间节点),然后将节点移动到右侧
前序遍历思路,前序遍历的基本逻辑是中左右,所以先将根节点入栈并入vector,再将其移向左侧,再出栈并将元素移动到右侧
后序遍历思路:后序遍历的基本逻辑是左右中,实现比较复杂,需要一前驱节点来记录之前访问的路径,先将左侧节点入栈,再出栈并判断是否右子树为空或者已经访问,如果是正确的需要出栈加入vector并更新前驱节点,否则向右侧移动
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
typedef struct tree
{
int data;
tree* left;
tree* right;
}tree;
void set(tree* root, vector<int> &str)
{
stack<tree*>s;
tree* cur = root;
while (cur != nullptr || !s.empty())
{
while (cur != nullptr)
{
s.push(cur);
cur = cur->left;
}
cur = s.top();
s.pop();
str.push_back(cur->data);
cur = cur->right;
}
}
void set1(tree* root, vector<int>& str)
{
stack<tree*>s;
tree* cur = root;
while (cur || !s.empty())
{
while (cur)
{
str.push_back(cur->data);
s.push(cur);
cur = cur->left;
}
tree* point1 = s.top();
s.pop();
cur = point1->right;
}
}
void set2(tree* root, vector<int>& str)
{
stack<tree*>st;
tree* prev = nullptr;
tree* cur = root;
while (cur || !st.empty())
{
while (cur)
{
st.push(cur);
cur = cur->left;
}
tree* top = st.top();
if (top->right == nullptr || top->right == prev)
{
st.pop();
str.push_back(top->data);
prev = top;
}
else
{
cur = top->right;
}
}
以下为层序遍历代码,层序遍历使用队列作为载体,并先将节点入队列,在队列不为空的时候,首先记录队列的元素数并且为返回数组开新的一层,然后遍历剩余元素(每次队列都是一层),每次出队列并且更新返回数组,如果有左右节点,则入队列准备成为新的一层
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
typedef struct tree
{
int data;
tree* left;
tree* right;
}tree;
void set(tree* root, vector<vector<int>>&str)
{
if (root == nullptr)
{
return;
}
queue<tree*>cur;
cur.push(root);
while (!cur.empty())
{
int n = cur.size();
str.push_back(vector<int>());
for (int i = 0; i < n; i++)
{
tree* point = cur.front();
cur.pop();
str.back().push_back(point->data);
if (point->left)
{
cur.push(point->left);
}
if (point->right)
{
cur.push(point->right);
}
}
}
}
二叉树的共同祖先问题
【问题描述】假设二叉树采用二叉链表方式存储,root指向根结点,p所指结点和q所指结点为二叉树中的两个不同结点,且互不成为根到该结点的路径上的点,编程求解距离它们最近的共同祖先。
【输入形式】二叉树的前序和中序遍历序列,用以创建该二叉树的链式存储结构;以及二叉树的两个结点数据 x 和 y
【输出形式】结点数据值为 x 和结点数据值为 y 的最近的共同祖先,若没有共同祖先则输出NULL,请注意一个结点本身不能成为另一个结点的共同祖先。
【样例输入】
GABDCEF
BDAEFCG
DF
【样例输出】
A
关键就是寻找公共祖先的函数,这是一个递归函数,当root为两节点任意一节点或者为空时返回root,然后递归调用左右节点,此时,这两个节点有两种情况,都在左,都在右或者一左一右,第一种返回左,第二种返回右,第三种返回根节点
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
typedef struct tree
{
int data;
tree* left;
tree* right;
}tree;
tree* find(tree* root, int data1, int data2)
{
if (!root || root->data == data1 || root->data == data2)
{
return root;
}
tree* left1 = find(root->left, data1, data2);
tree* right1 = find(root->right, data1, data2);
if (!left1)
{
return right1;
}
if (!right1)
{
return left1;
}
return root;
}
计算叶子节点数目与交换左右子树
【问题描述】二叉树按照二叉链表的方式存储。编写程序,计算二叉树中叶子结点的数目并输出;编写程序,将二叉树的每个结点的左、右子树进行交换,请注意不是只交换结点的data值,而是左、右孩子指针指向的交换,最后输出交换后的二叉树的后序遍历序列。
【输入形式】二叉树的前序遍历序列,空指针的位置输入字符#
【输出形式】叶子结点的数目;左右子树交换后,后序遍历的序列,空子树的位置输出字符#
【样例输入】
ABE##F##CG###
【样例输出】
3
###GC##F##EBA
计算叶子节点的函数
int leave(tree* root)
{
if (root == nullptr)
{
return 0;
}
if (root->left == nullptr && root->right == nullptr)
{
return 1;
}
return leave(root->left) + leave(root->right);
}
交换左右节点函数
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
typedef struct tree
{
int data;
tree* left;
tree* right;
}tree;
void swap(tree* root)
{
if (root == nullptr)
{
return;
}
tree* point = root->left;
root->left = root->right;
root->right = point;
swap(root->left);
swap(root->right);
}