一、二叉树的创建及遍历
【问题描述】
给出一个按照先序遍历得出的字符串,’#’ 代表空的子节点,大写字母代表节点内容。请通过这个字符串建立二叉树,并分别采用“递归”和“非递归”的先序、中序、后序遍历的算法分别输出每一个非空节点。
【输入形式】
输入只有一行,包含一个字符串S,用来建立二叉树。保证S为合法的二叉树先序遍历字符串,节点内容只有大写字母,且S的长度不超过100。
【输出形式】
共有6行,每一行包含一串字符,表示分别按递归和非递归的先序、中序、后序遍历得出的节点内容,每个字母后输出一个空格。请注意行尾输出换行。
【样例输入】
ABC##DE#G##F###
【样例输出】
A B C D E G F
C B E G D F A
C G E F D B A
A B C D E G F
C B E G D F A
C G E F D B A
【完整代码】
#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;
//构建二叉树
typedef struct BiNode
{
char date;
BiNode* LChild;
BiNode* RChild;
}*Bitree;
//递归创建树
void CreateBitree(Bitree &bt)
{
char ch = getchar();
if (ch == '#') bt = NULL;
else
{
bt = new BiNode;
bt->date = ch;
CreateBitree(bt->LChild);
CreateBitree(bt->RChild);
}
}
//前序递归遍历
void PreOrder(Bitree root)
{
if (root != NULL)
{
cout<<root->date<<" ";
PreOrder(root->LChild);
PreOrder(root->RChild);
}
}
//非递归前序遍历
void FPreOrder(Bitree root)
{
stack<BiNode*> s;
BiNode* cur = root;
while (cur != NULL)
{
cout<< cur->date <<" ";
s.push(cur);
if (cur->LChild != NULL)
{
cur = cur->LChild;
}
else if (!s.empty())
{
while (!s.empty())
{
cur = s.top();
s.pop();
cur = cur->RChild;
if (cur != NULL)
{
break;
}
}
}
else cur = NULL;
}
}
//中序递归遍历
void InOrder(Bitree root)
{
if (root!= NULL)
{
InOrder(root->LChild);
cout << root->date << " ";
InOrder(root->RChild);
}
}
//非递归中序遍历
void FInOrder(Bitree root)
{
stack<BiNode*> s;
BiNode* cur = root;
while (cur != NULL)
{
s.push(cur);
if (cur->LChild != NULL)
{
cur = cur->LChild;
}
else if (!s.empty())
{
while (!s.empty())
{
cur = s.top();
cout << cur->date << " ";
s.pop();
cur = cur->RChild;
if (cur != NULL)
{
break;
}
}
}
else cur = NULL;
}
}
//后续递归遍历
void PostOrder(Bitree root)
{
if (root != NULL)
{
PostOrder(root->LChild);
PostOrder(root->RChild);
cout << root->date<<" ";
}
}
//非递归后续遍历
void FPostOrder(Bitree root)
{
stack<BiNode*> s;
BiNode* p = root;
BiNode* r = NULL;
while (p || !s.empty())
{
if (p)
{
s.push(p);
p = p->LChild;
}
else
{
p = s.top();
if (p->RChild && p->RChild != r)
{
p = p->RChild;
}
else
{
s.pop();
cout << p->date << " ";
r = p;
p = NULL;
}
}
}
}
int main()
{
Bitree root;
//创建二叉树
CreateBitree(root);
//递归前序遍历
PreOrder(root);
cout << endl;
//递归中序遍历
InOrder(root);
cout << endl;
//递归后序遍历
PostOrder(root);
cout << endl;
//非递归前序遍历
FPreOrder(root);
cout << endl;
//非递归中序遍历
FInOrder(root);
cout << endl;
//非递归后序遍历
FPostOrder(root);
return 0;
}
二、二叉树结点的共同祖先问题
【问题描述】假设二叉树采用二叉链表方式存储,root指向根结点,p所指结点和q所指结点为二叉树中的两个不同结点,且互不成为根到该结点的路径上的点,编程求解距离它们最近的共同祖先。
【输入形式】二叉树的前序和中序遍历序列,用以创建该二叉树的链式存储结构;以及二叉树的两个结点数据 x 和 y
【输出形式】结点数据值为 x 和结点数据值为 y 的最近的共同祖先,若没有共同祖先则输出NULL
【样例输入】
GABDCEF
BDAEFCG
DF
【样例输出】
A
【完整代码】
#include<iostream>
#include<stdio.h>
using namespace std;
//构建二叉树
typedef struct BiNode
{
char date;
BiNode* LChild;
BiNode* RChild;
}*BiTree;
//寻找祖先
BiNode * SearchAncientNode(BiTree root,char p,char q)
{
if (root == NULL) return NULL;
//判断待查找的节点是否为根节点
else if (root->date == p || root->date == q)
{
return root;
}
else
{
//递归查找祖先
BiNode* Left = SearchAncientNode(root->LChild, p, q);
BiNode* Right = SearchAncientNode(root->RChild, p, q);
//左右子树均为空
if (Left == NULL && Right == NULL)
{
return NULL;
}
//左右子树均不为空
else if (Left!=NULL && Right!=NULL)
{
return root;
}
//左右子树有一个不为空
else
{
if (Left)
{
return Left;
}
else
{
return Right;
}
}
}
}
//判断是否为共同祖先
void IsSearchAncientNode(BiTree root, char p, char q)
{
if (SearchAncientNode(root, p, q))
{
if (SearchAncientNode(root, p, q)->date == p ||SearchAncientNode(root,p,q)->date== q)
{
cout << "NULL" << endl;
}
else
{
cout << SearchAncientNode(root, p, q)->date << endl;
}
}
else
{
cout << "NULL" << endl;
}
}
//根据前序和中序序列创建二叉树
BiNode* CreateBiTree(char pre[], char in[], int preLeft, int preRight, int inLeft, int inRight)
{
if (preLeft > preRight) return NULL;
int mid = -1;
BiNode* root = new BiNode;
for (int i = inLeft; i <= inRight; i++)
{
if (in[i] == pre[preLeft])
{
mid = i;
}
}
root->date = in[mid];
//递归创建
root->LChild = CreateBiTree(pre, in, preLeft + 1, preLeft + mid - inLeft, inLeft, mid - 1);
root->RChild = CreateBiTree(pre, in, preRight + mid - inRight + 1, preRight, mid + 1, inRight);
return root;
}
int main()
{
char pre[100], in[100];
cin >> pre;
cin >> in;
char p, q;
cin >> p >> q;
int preLeft = 0, preRight = 0, inLeft = 0, inRight = 0;
for (int i = 0; i < 100; i++)
{
if (pre[i] != '\0') preRight++;
else break;
}
preRight = preRight - 1;
inRight = preRight;
BiTree root = CreateBiTree(pre, in, preLeft, preRight, inLeft, inRight);
//输出最后结果
IsSearchAncientNode(root, p, q);
return 0;
}
三、哈夫曼编码
【问题描述】读入n个字符所对应的权值,自底向上构造一棵哈夫曼树,自顶向下生成每一个字符对应的哈夫曼编码,并依次输出。另,求解某字符串的哈夫曼编码,求解某01序列的译码。
【输入形式】输入的第一行包含一个正整数n,表示共有n个字符需要编码。其中n不超过100。第二行中有n个用空格隔开的正整数,分别表示n个字符的权值,依次按照abcd…的默认顺序给出。然后是某字符串和某01序列。
【输出形式】前n行,每行一个字符串,表示对应字符的哈夫曼编码。然后是某字符串的哈夫曼编码,某01序列的译码。
【注意】保证每次左子树比右子树的权值小;如出现相同权值的,则先出现的在左子树,即下标小的在左子树。
【样例输入】
8
5 29 7 8 14 23 3 11
aabchg
00011110111111001
【样例输出】
0001
10
1110
1111
110
01
0000
001
000100011011100010000
acdef
【完整代码】
#include<iostream>
#include<stack>
#include<vector>
#define maxsize 300
using namespace std;
//创建哈夫曼树
struct HFNode
{
int weight; //权重
int parent; //双亲
int leftChild; //左子树
int rightChild;//右子树
};
//初始化哈夫曼树
void InitalHFTree(HFNode HFTree[])
{
for (int i = 0; i < maxsize; i++)
{
HFTree[i].leftChild = 0;
HFTree[i].rightChild = 0;
HFTree[i].parent = 0;
HFTree[i].weight =-1;
}
}
//获得最小值
void Getmin(HFNode HFTree[], int& s1, int& s2)
{
int min1 = 10000;
int i = 1;
//找寻目前第一个最小值
while (HFTree[i].weight!=-1)
{
if (HFTree[i].weight < min1 && HFTree[i].parent == 0)
{
min1 = HFTree[i].weight;
s1 = i;
}
i++;
}
int min2 = 10000;
i = 1;
//找寻目前第二个最小值
while (HFTree[i].weight != -1)
{
if (HFTree[i].weight < min2 && i != s1 && HFTree[i].parent == 0)
{
min2 = HFTree[i].weight;
s2 = i;
}
i++;
}
}
//构造哈夫曼树
void CreateHFTree(HFNode HFTree[], int& num)
{
int top = num + 1;
for (int i = 1; i < num; i++)
{
int s1; int s2;
//每次找最小的两个
Getmin(HFTree, s1, s2);
//更新哈夫曼树
HFTree[top].weight = HFTree[s1].weight + HFTree[s2].weight;
HFTree[top].leftChild = s1;
HFTree[top].rightChild = s2;
HFTree[s1].parent = top;
HFTree[s2].parent = top;
top++;
}
num = top - 1;
}
//为每个元素构造哈夫曼编码
void GetHuffmanCode(HFNode HFTree[],int num,int i,char str[])
{
num = (num + 1) / 2;
stack<char> s;
int j = HFTree[i].parent;
//为字母进行01序列编码
while (j)
{
if (HFTree[j].leftChild == i) s.push('0');
else if (HFTree[j].rightChild == i) s.push('1');
i = j;
j = HFTree[j].parent;
}
int k = 0;
while (!s.empty())
{
str[k] = s.top();
s.pop();
k++;
}
str[k] = '\0';
}
//根据输入的01序列判断原字符序列
void GetHuffmandecode(HFNode HFTree[], int num, string s)
{
int i = 0;
while (i<(signed)s.size())
{
int cur = num;
while (HFTree[cur].leftChild!=0&& HFTree[cur].rightChild!=0)
{
if (s[i] == '0')
{
cur = HFTree[cur].leftChild;
}
else if (s[i] == '1')
{
cur = HFTree[cur].rightChild;
}
i++;
}
char ch = 'a' + cur - 1;
cout << ch;
}
}
int main()
{
int num, arr[maxsize];
cin >> num;
for (int i = 0; i < num; i++) cin >> arr[i];
HFNode HFTree[maxsize];
InitalHFTree(HFTree);
//哈夫曼树权重初始化
for (int i = 0; i < num; i++)
{
HFTree[i + 1].weight = arr[i];
}
//创建哈夫曼树
CreateHFTree(HFTree, num);
string s1;
cin >> s1;
string s2;
cin >> s2;
char str[100];
//为字母进行编码
for (int i = 0; i <(num+1)/2; i++)
{
GetHuffmanCode(HFTree, num, i+1, str);
cout << str << endl;
}
cout << endl;
for (int i = 0; i < (signed)s1.size(); i++)
{
int j = s1[i] - 96;
GetHuffmanCode(HFTree, num,j, str);
cout << str;
}
cout << endl << endl;
//根据输入的01序列推导原字符串
GetHuffmandecode(HFTree, num, s2);
return 0;
}
四、二叉树左右子树交换操作
【问题描述】二叉树按照二叉链表的方式存储。编写程序,计算二叉树中叶子结点的数目并输出;编写程序,将二叉树的左右子树进行交换,并输出交换后的二叉树的后序遍历序列。
【输入形式】二叉树的前序遍历序列,空指针的位置输入字符#
【输出形式】叶子结点的数目;左右子树交换后,后序遍历的序列,空子树的位置输出字符#
【样例输入】
ABE##F##CG###
【样例输出】
3
###GC##F##EBA
【完整代码】
#include<iostream>
using namespace std;
//构建二叉树类
class bintree
{
private:
struct node
{
char data;
node * left_child; //左子树
node * right_child; //右子树
//构造函数初始化
node()
{
left_child = right_child = NULL;
}
}*root, *p;
void input(node ** r);
//统计叶子节点个数
int leaf_count(node * r);
//交换左右子树
void swap_child(node ** r);
void Show(node * r);
public:
bintree();
void prompt()
{
input(&root);
}
int lcount()
{
return leaf_count(root);
}
void swap_c()
{
swap_child(&root);
}
void show()
{
Show(root);
cout<<endl;
}
};
//根据前序遍历序列构建二叉树
void bintree::input(node ** r)
{
char d;
cin>>d;
if (d != '#')
{
*r = new node;
(*r)->data = d;
input(&(*r)->left_child);
input(&(*r)->right_child);
}
}
//初始化
bintree::bintree()
{
root = NULL;
p = NULL;
}
//统计叶子节点个数
int bintree::leaf_count(node * r)
{
if (r == NULL)
return 0;
else if (r->left_child == NULL && r->right_child == NULL)
return 1;
else
return leaf_count(r->left_child) + leaf_count(r->right_child);
}
//交换左右子树
void bintree::swap_child(node ** r)
{
node * temp;
if (*r != NULL)
{
//通过temp交换
temp = (*r)->left_child;
(*r)->left_child = (*r)->right_child;
(*r)->right_child = temp;
//递归交换
swap_child(&(*r)->left_child);
swap_child(&(*r)->right_child);
}
}
//输出
void bintree::Show(node * r)
{
if (r != NULL)
{
Show(r->left_child);
Show(r->right_child);
cout<<r->data;
}
else
cout << '#';
}
int main()
{
bintree bt;
//初始化
bt.prompt();
//输出叶子节点个数
cout<<bt.lcount()<< endl;
//左右子树交换
bt.swap_c();
//输出后序序列
bt.show();
return 0;
}