二叉树:一种一对多的非线性结构,主要采取链式结构存储
相关操作(核心:把握递归思想):
- 二叉树的创建和遍历(通过含中序的序列组来创建二叉树,以及二叉树的前序中序后序的遍历输出)
- 二叉树的左右子树交换操作
- 寻找两个节点的共同祖先问题
- 求解二叉树的深度和最远的节点距离
- 二叉树的宽度和二叉树的层次遍历
- 哈夫曼树(难点+重点)
- 最佳前缀码(难点+重点)
二叉树的创建相关操作
1.纯先序创建
void createBiTree_Pre(BiTree &T) { char x; cin>>x; if(x == '#'){ T = NULL; } else{ T = new Node; T->data = x; createBiTree_Pre(T->left); createBiTree_Pre(T->right); } }
2.用含中序的序列组建立二叉树(示例用后序和先序)
BiTree createBiTree(char *s1,char *s2,int len) { //递归调用的函数中注意一定要给一个出口 if(len <= 0) return NULL; BiTree root = new Node; root->data = *s2; char *p; for(p = s1;p != NULL;p++){ if(*p == *s2) break; } int sum1 = p - s1; //记录左子树的节点数量 int sum2 = len - sum1 - 1; //记录右子树的节点数量 root->left = createBiTree(s1,s2+len-sum1,sum1); root->right = createBiTree(p+1,s2+1,sum2); return root; } int main() { BiTree T; char s1[N],s2[N]; scanf("%s",s1); scanf("%s",s2); //将后序逆转,确保每次取得的第一个是当前的根节点 reverse(s2,s2+strlen(s2)); T = createBiTree(s1,s2,strlen(s1)); preTraver(T); return 0; }
3.遍历序列(以先序为例进行调整)
void preTravel(Bitree root) { if(root){ cout<<root->data; preTravel(root->left); preTravel(root->right); }
寻找两个节点的共同祖先
//注意理解寻找祖先节点的思想!!!!!(属于自己写是绕晕了但是 //代码利用了递归后是很简洁的!重点在于return 的设置,也就是递归回调和递归出口的设置) int flag = 0;BiTree anc; bool Search_Ancient(BiTree &root,char p,char q) { if(root == NULL) return 0; //一直采用递归来到最底层寻找,本题由于是查找祖先节点 //所以应该自底向上,从叶子节点开始检索 bool left = Search_Ancient(root->left,p,q); bool right = Search_Ancient(root->right,p,q); //如果左子树有,右子树有,则该节点即为祖先节点 if(left && right){ anc = root; flag = 1; } //找到字符所在节点,或者当前节点的左树存在目标元素,或者当前节点的右树存在目标元素,该节点都 //有可能为祖先节点 return (root->data == p || root->data == q) || left || right; }
二叉树的深度和最远节点信息
问题描述】考研真题:求二叉树的深度及二叉树中最远两个结点的距离。
【输入形式】拓展的前序遍历序列
【输出形式】深度和距离
【样例输入】AB#C##DE#G#H##F##
【样例输出】5 6
tip:求距离,其实也就是求左右节点的高度差,可应用于二叉排序树的平衡因子求法
#include<iostream>
using namespace std;
typedef char ElemType;
struct Node{
Node *right;
Node *left;
ElemType data;
};
typedef Node* BiTree;
void createBiTree_Pre(BiTree &T)
{
char x;
cin>>x;
if(x == '#'){
T = NULL;
}
else{
T = new Node;
T->data = x;
createBiTree_Pre(T->left);
createBiTree_Pre(T->right);
}
}
int depth = 0;
void TreeDepth(BiTree root,int h)
{
if(root != NULL){
if(h > depth) depth = h;
TreeDepth(root->left,h+1);
TreeDepth(root->right,h+1);
}
}
//该做法是跨越根节点的
/*
*注意理解算法思想
* 最开始的版本适用于跨越root节点的,但若没有跨越root节点(注意递归思想!!)
* -左子树为root节点寻找+右子树为root节点寻找
*/
static int maxDistance = 0;
int LongestDistance(BiTree root)
{
if(root == NULL)
return -1;
int max_left = 1 + LongestDistance(root->left);
int max_right = 1 + LongestDistance(root->right);
int distance = max_left + max_right;
if(distance > maxDistance) maxDistance = distance;
return (max_left > max_right ? max_left : max_right);
}
int main()
{
BiTree T;
createBiTree_Pre(T);
TreeDepth(T,1);
LongestDistance(T);
cout<<depth<<" ";
cout<<maxDistance;
return 0;
}
线索二叉树
二叉树的线索化
//将二叉树线索化
Node *pre = NULL;
//始终设置一个先行节点
void Inthread(BiTree root)
{
if(root != NULL){
Inthread(root->left);
if(root->left == NULL){
root->left = pre;
root->Ltag = 1;
}
if(pre != NULL && pre->right == NULL){
pre->right = root;
pre->Rtag = 1;
}
pre = root;
Inthread(root->right);
}
}
//寻找后继节点
BiTree In_next(BiTree p)
{
Node *q;
if(p->Rtag == 1){
//cout<<p->data<<"的后继节点1:"<<p->right->data<<endl;
return p->right;
}
else{
//按照左中右输出,该节点的后继节点是右子树的最左子树节点
for(q = p->right;q && q->Ltag == 0;q = q->left);
//cout<<p->data<<"后继节点2:"<<q->data<<endl;
return q;
}
}
二叉树的层次遍历
层次遍历
* 指按照每层,从左到右的顺序遍历所有的节点的一种遍历方式,按照这种方式,可以很好的求解树的宽度
* 层次遍历中,需要用到 队列 的结构
* 保留上一层的根节点信息,取队首元素,如果左孩子有,入队,如果右孩子有,右孩子入队;队列中的元素数量就是宽度,并且入队方式是从左到右的
* 可设立一个数组保留每层的元素数量int Tree_maxWidth(BiTree root) { queue<BiTree> Q; int nowWidth = 1,tempWidth,maxWidth = 0; BiTree p; p = root; if(p != NULL) Q.push(p); while(!Q.empty()){ //当前循环是为了遍历二叉树中的所有节点 tempWidth = nowWidth; //now标记上一个层次的节点个数 while(tempWidth){ p = Q.front(); Q.pop(); if(p->left != NULL) Q.push(p->left); if(p->right != NULL) Q.push(p->right); tempWidth--; } //上一层已经完全出队,取当前队列的长度即下一层次的宽度 nowWidth = Q.size(); if(nowWidth > maxWidth) maxWidth = nowWidth; } return maxWidth; }
哈夫曼树
【问题描述】
已知输入一串正整数,正整数之间用空格键分开,请建立一个哈夫曼树,以输入的数字为叶节点,求这棵 哈夫曼树 的带权路径长度。
【输入形式】
首先输入正整数的个数,然后接下来为接下来的正整数,正整数个数不超过10个
【输出形式】
输出相应的权值
【样例输入】
5 4 5 6 7 8
【样例输出】
69*/
/*解题关键
* 1.哈夫曼树的构建(采用一维结构数组的方法)
* 2.如何生成哈夫曼树
* - 原理:找出最小的两个节点(select函数),相加得到新的生成节点
* 3.代码实现
* - select函数:遍历前面有数据的节点,返回两个较小值,这里可以不用启用一个备用的weight数组,可以用找到节点的
* parent域是否为0来判断。
* 4.实现原理(其实权重和就等于新的生成节点的值相加,可以证明,一些值其实在反复相加,反复的过程包含了权重的思想(应该可以证明))
#include<bits/stdc++.h>
#define N 1000
#define MAX 10001
using namespace std;
struct HTNode{
int weight;
int parent;
int left;
int right;
};
int n;
HTNode ht[N];
void select(int &s1,int &s2, int m)
{
int min1 = MAX,min2 = MAX,flag1,flag2,i;
//跑一遍找到最小的
for(i = 1;i <= n+m;i++){
if(ht[i].weight < min1 && ht[i].parent == 0){
min1 = ht[i].weight;
flag1 = i;
}
}
//跑一遍找到次小的
for(i = 1;i <= n+m;i++){
//头几次写没有用到parent,老是得出错误值
if(ht[i].weight < min2 && i != flag1 && ht[i].parent == 0){
min2 = ht[i].weight;
flag2 = i;
}
}
s1 = flag1;
s2 = flag2;
}
int main()
{
int s1,s2,sum = 0;
//完成前面数据二叉树的基本创建
cin>>n;
for(int i = 1;i <= n;i++){
cin>>ht[i].weight;
ht[i].parent = 0;
ht[i].left = 0;
ht[i].right = 0;
}
//完成哈夫曼树的创立
for(int i = n+1;i <= 2*n - 1;i++)
{
select(s1,s2,i-n-1);
ht[i].weight = ht[s1].weight + ht[s2].weight;
ht[i].left = s1; ht[i].right = s2;
ht[s1].parent = i; ht[s2].parent = i;
}
for(int i = n+1;i <= 2*n - 1;i++){
sum += ht[i].weight;
//cout<<sum<<endl;
}
cout<<sum;
return 0;
}
最佳前缀码
/*【问题描述】 读入n个字符所对应的权值,构造一棵哈夫曼树,自底向上生成每一个字符对应的哈夫曼编码,并依次输出。 【输入形式】 输入的第一行包含一个正整数n,表示共有n个字符需要编码。其中n不超过100。 第二行中有n个用空格隔开的正整数,分别表示n个字符的权值。 【输出形式】 共n行,每行一个字符串,表示对应字符的哈夫曼编码。 【注意】保证每次左子树比右子树的权值小;如出现相同权值的,则先出现的在左子树。 【样例输入】 8 5 29 7 8 14 23 3 11 【样例输出】 0001 10 1110 1111 110 01 0000 001*/ #include<bits/stdc++.h> #define N 1000 #define MAX 10000 using namespace std; struct HTNode{ int weight; int parent; int left; int right; }; int n; HTNode ht[N]; vector<int> prefix[2*N]; void select(int &s1,int &s2, int m) { int min1 = MAX,min2 = MAX,flag1,flag2,i; //跑一遍找到最小的 for(i = 1;i <= n+m;i++){ if(ht[i].weight < min1 && ht[i].parent == 0){ min1 = ht[i].weight; flag1 = i; } } //跑一遍找到次小的 for(i = 1;i <= n+m;i++){ if(ht[i].weight < min2 && i != flag1 && ht[i].parent == 0){ min2 = ht[i].weight; flag2 = i; } } s1 = flag1; s2 = flag2; } void Search_Prefix() { int i,j; for(i = 1;i <= n;i++){ j = i; while(ht[j].parent){ //只有根节点的parent值为0; int parent = ht[j].parent; if(j == ht[parent].left){ prefix[i].push_back(0); } else if(j == ht[parent].right){ prefix[i].push_back(1); } j = parent; } } } int main() { int s1,s2; //完成前面数据二叉树的基本创建 cin>>n; for(int i = 1;i <= n;i++){ cin>>ht[i].weight; ht[i].parent = 0; ht[i].left = 0; ht[i].right = 0; } //完成哈夫曼树的创立 for(int i = n+1;i <= 2*n - 1;i++) { select(s1,s2,i-n-1); ht[i].weight = ht[s1].weight + ht[s2].weight; ht[i].left = s1; ht[i].right = s2; ht[s1].parent = i; ht[s2].parent = i; } //标识根节点 ht[2*n-1].parent = 0; Search_Prefix(); for(int i = 1;i <= n;i++){ for(int j = prefix[i].size() - 1;j >= 0;j--){ //逆向输出 cout<<prefix[i][j]; } cout<<endl; } /*for(int i = 1;i <= 2*n - 1;i++){ cout<<ht[i].weight<<"节点的parent的编号"<<ht[i].parent<<" 以及它的权重"<<ht[ht[i].parent].weight<<; cout<<endl; }*/ return 0; }
![]()