数据结构实验一:线性表,堆栈和队列实现
数据结构实验二 :二叉树的操作与实现
数据结构实验三: 图的操作与实现
数据结构实验四 : 查找和排序算法实现
1、领会二叉链存储结构和掌握二叉树中的各种基本运算算法设计;
2、领会线索二叉树的构造过程以及构造二叉树的算法设计;
3、领会哈夫曼树的构造过程以及哈夫曼编码的生成过程;
4、掌握二叉树遍历算法的应用,熟练使用先序、中序、后序3种递归遍历算法进行二叉树问题的求解;
微机一台
操作系统:WinXP
编程软件:C/C++编程软件
编写一个程序btree.cpp,实现二叉树的基本运算,并在此基础上设计一个程序exp7-1.cpp完成以下功能。
(1)由图7.33所示的二叉树创建对应的二叉链存储结构b,该二叉树的括号表示串为“A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,i)))”。
(2)输出二叉树b。
(3)输出‘H’结点的左、右孩子结点值。
(4)输出二叉树b的高度。
(5)释放二叉树b。
#include
using namespace std;
#include ;
//(1)由图7.33所示的二叉树创建对应的二叉链存储结构b,该二叉树的括号表示串为“A(B(D, E(H(J, K(L, M(, N))))), C(F, G(, i)))”。
//(2)输出二叉树b。
//(3)输出‘H’结点的左、右孩子结点值。
//(4)输出二叉树b的高度。
//(5)释放二叉树b。
struct TreeNode {
char data;
TreeNode* left;
TreeNode* right;
TreeNode(char data) {
this->data = data;
this->left = NULL;
this->right = NULL;
}
};
/*
* 建树
*/
TreeNode* CreatTree(string TreeString, TreeNode*& root) {//建树
const char* Tree = TreeString.c_str();
stack<TreeNode*>temp;
TreeNode* nodeTemp = NULL;
bool flag = true;//左右孩子标记
for (int i = 0; Tree[i] != '\0'; i++) {
//遍历
//如果是'('
if (Tree[i] == '(') {
//入栈 为下一个符号做准备
temp.push(nodeTemp);//下面新建了节点
flag = true;//flag == true 为下一个字符是左孩子
}
else if (Tree[i] == ',') {
flag = false;//如果是 , 说明下一个字符是右孩子
}
else if (Tree[i] == ')') {
temp.pop();//如果是')' 节点出栈舍弃 相当于指针向根部节点移动
}
else {
nodeTemp = new TreeNode(Tree[i]);//新建节点
if (root == NULL) {//如果根节点为空 赋给根结点
root = nodeTemp;
}
else {
if (!temp.empty()) {//通过栈不空 说明还没有遍历完所以节点
if (flag) {//建左点
temp.top()->left = nodeTemp;
}
else {//建右点
temp.top()->right = nodeTemp;
}
}
}
}
}
return root;
}
//遍历二叉树
void MidShow(TreeNode* root) {
if (root == NULL) {
return;
}
else {
MidShow(root->left);
cout << root->data;
MidShow(root->right);
}
}
//某个树的两个孩子
int showTwoKids(char S, TreeNode* root) {
if (root->data == S) {//找到孩子 输出左右孩子
if (root->left) {
cout << "左孩子值:";
cout << root->left->data;
}
else {
cout << "左孩子空";
}
if (root->right) {
cout << "右孩子值:";
cout << root->right->data;
}
else {
cout << "右孩子空";
}
return 1;
}
else {//没找到就前序遍历找
showTwoKids(S, root->left);
showTwoKids(S, root->right);
}
return 0;
}
//释放树
void deleteTree(TreeNode*& root) {
if (root == NULL) {
return;
}
else {
deleteTree(root->left);
deleteTree(root->right);
cout << "删除:" << root->data << endl;
delete root;
}
}
/*
* 二叉树的高
*/
int showHigh(TreeNode* root) {
if (root) {
int m = showHigh(root->left);
int n = showHigh(root->right);
return m > n ? m + 1 : n + 1;
}
}
int main() {
string tree = "A(B(D(F,),E),C(,G))";
TreeNode* root = NULL;
CreatTree(tree, root);
cout << endl;
char findChar = 'A';
cout << "A的左右孩子"<<endl;
showTwoKids(findChar, root);
cout << endl;
cout << "树高:" << showHigh(root) << endl;
cout << "中序遍历" << endl;
MidShow(root);
cout << endl;
deleteTree(root);
return 0;
编写一个程序exp7-3.cpp,实现由先序序列和中序序列以及由中序序列和后序序列构造一棵二叉树的功能(二叉树种的每个结点值为单个字符),要求以括号表示和凹入表示法输出该二叉树,并用先序遍历序列“ABDEHJKLMNCFGI”和中序遍历序列“DBJHLKMNEAFCGI”以及由中序遍历序列“DBJHLKMNEAFCGI”和后序遍历序列“DJLNMKHEBFIGCA”进行验证。
#include
using namespace std;
struct TreeNode {
char Data;
TreeNode* l;
TreeNode* r;
TreeNode(char data) {
this->Data = data;
this->l = NULL;
this->r = NULL;
}
};
//前序和中序构造二叉树
TreeNode* CreatTreePre(string pre,string ins,TreeNode*&root) {
int index = 0;
TreeNode* p = root;
if (pre.length() == 0 || ins.length() == 0) {
return NULL;
}
else {
//先序取根
root = new TreeNode(pre[0]); //前序//A BDEHJKLMN CFGI //左右子树连续排列在一起
//中序//D BJHLKMNE A FCGI//
//中序找根
while (ins[index] != root->Data) {
index++;
}
//递归建立左节点
string leftins(ins, 0, index);
string leftPre(pre, 1, index);
//递归建立右节点
string rightins(ins, index + 1, ins.length());
string rightPre(pre,index+1,pre.length());
root->l = CreatTreePre(leftPre, leftins, root->l);
root->r = CreatTreePre(rightPre, rightins, root->r);
//递归
}
return root;
}
//后序和中序构造二叉树
TreeNode* initTbyInPost(string postorder, string inorder) {
if (postorder.length() == 0) return NULL;//没有节点
TreeNode* node = new TreeNode(postorder[postorder.length() - 1]); //后续遍历的最后一个值是自身节点的值
if (postorder.length() == 1) return node; //只有头节点
int mid = inorder.find(postorder[postorder.length() - 1]);//当下节点
string inorderLeft = inorder.substr(0, mid);//中序左节点
string inorderRight = inorder.substr(mid + 1);//中序右节点
string preordeLeft = postorder.substr(0, inorderLeft.length());//后序左节点
string preordeRight = postorder.substr(inorderLeft.length(), inorderRight.length());//后序右节点
node->l = initTbyInPost(preordeLeft, inorderLeft);
node->r = initTbyInPost(preordeRight, inorderRight);
return node;
}
//括号表示法
string bracketsShowTree(TreeNode* node) {
if (!node) return ""; //节点为空直接放回
string s = string(1, node->Data);
if (!node->l && !node->r) return s; //无子节点直接放回当下节点
s.append("(");
//括号内为当下节点的子节点
s.append(bracketsShowTree(node->l));
if (node->r) {
//利用逗号隔开左右节点
s.append(",");
s.append(bracketsShowTree(node->r));
}
s.append(")");
return s;
}
//中序遍历
void showTreePre(TreeNode*root) {
if (root == NULL) {
return;
}
else {
showTreePre(root->l);
cout << root->Data;
showTreePre(root->r);
}
}
//凹入法表示二叉树
void ConcavityShowTree(TreeNode*root,int i =0) {
if (root) {
cout << string(i * 3, ' ') << root->Data << endl;
ConcavityShowTree(root->l, i + 1);
ConcavityShowTree(root->r, i + 1);
}
}
int main() {
TreeNode* root1;
TreeNode* root2;
string pre = "ABDEHJKLMNCFGI";
string ins = "DBJHLKMNEAFCGI";
string tail ="DJLNMKHEBFIGCA";
root1= CreatTreePre(pre, ins,root1);//造树
root2= initTbyInPost(tail, ins);
string s = "";
//前序和中序
cout << bracketsShowTree(root1)<<endl;
//后序和中序
cout << bracketsShowTree(root1)<<endl;
//
ConcavityShowTree(root1);
return 0;
}
编写一个程序exp7-5.cpp,构造一棵哈夫曼树,输出对应的哈夫曼编码和平均查找长度,并对下表(表7.8)所示的数据进行验证。
表7.8 单词及出现的频度
单词 The of a to and in that he is at on for His are be
频度 1192 677 541 518 462 450 242 195 190 181 174 157 138 124 123
#include
#include
using namespace std;
/*
* tree
*/
struct TreeNode {
int data;
TreeNode* left;
TreeNode* right;
TreeNode(int data) {
this->data = data;
this->left = NULL;
this->right = NULL;
}
TreeNode(int data, TreeNode* left, TreeNode* right) {
this->data = data;
this->left = left;
this->right = right;
}
};
//1.所有的叶子节点都必须是数字。
//2.所有的非叶子节点都是符号,而且都必须有左子树和右子树。
//3.不可能会存在一个节点仅存在左子树而不存在右子树,或者仅存在右子树而不存在左子树的情况。
//4.+ -节点只存在于最左边,左孩子只能是 + -或者数字,右孩子只能是 * / 或者数字
//5.* / 节点的右孩子只能是数字,左孩子只能是 * / 或者数字
/*
* 字符变数字
*/
int StringToInt(string val) {
reverse(val.begin(), val.end());//反转
int ans = 0;
for (int i = 0; i < val.size(); i++) {//转数字
ans += (val[i] - '0') * pow(10, i);
}
return ans;
}
/*字符串种树
* 1.所有的叶子节点都必须是数字。
2.所有的非叶子节点都是符号,而且都必须有左子树和右子树。
//3.不可能会存在一个节点仅存在左子树而不存在右子树,或者仅存在右子树而不存在左子树的情况。
//4.+ -节点只存在于最左边,左孩子只能是 + -或者数字,右孩子只能是 * / 或者数字
//5.* / 节点的右孩子只能是数字,左孩子只能是 * / 或者数字
*/
TreeNode* creatTree(string s) {
//s为空 返回null
if (s == "") {
return NULL;
}
//从右往左遍历先找+-
//for(int a =s.size()-1;a>=0&& s[a] != '+' && s[a] != '-';a--){}
int i = s.size() - 1;
while (i >= 0 && s[i] != '+' && s[i] != '-') {
i--;//有可能是-1
}
if (i >= 0) {
TreeNode* root = new TreeNode((int)s[i]);//char的形式转化为int
root->left = creatTree(s.substr(0, i));
root->right = creatTree(s.substr(i + 1));
return root;
}
//找不到+/ 找*/
// 新建节点
// 左指针指向 符号左边字符串
// 右指针指向 符号右边字符串
int j = s.size() - 1;
while (j >= 0 && s[j] != '*' && s[j] != '/') {
j--;
}
if (j >= 0) {
TreeNode* root = new TreeNode((int)s[j]);//char的形式转化为int
root->left = creatTree(s.substr(0, j));
root->right = creatTree(s.substr(j + 1));
return root;
}
//只有一个字符的时候才有会出现数字
TreeNode* root = new TreeNode(StringToInt(s));
return root;
}
/*
* calculater
*/
int calculate(TreeNode*root) {
//只有符号才有左右子树
if (!root) {//空返回0
return 0;
}
//非空
//有左右子树 就是符号
int res = 0;
if (root->left && root->right) {
switch (root->data) {
case '+':
res = calculate(root->left) + calculate(root->right);
break;
case'-':
res = calculate(root->left) - calculate(root->right);
break;
case'*':
res = calculate(root->left) * calculate(root->right);
break;
case'/':
res = calculate(root->left) * calculate(root->right);
break;
}
}
else {//符号之外就是数字
res = root->data;
}
return res;
}
void Delete(TreeNode*root) {
if (root == NULL) {//空返回
return;
}
else {
Delete(root->left);
Delete(root->right);
root->left = NULL;
root->right = NULL;
delete root;
}
}
int main() {
string Test = "1+2*3-4/5";
TreeNode* root=NULL;
int sum;
root= creatTree(Test);
sum = calculate(root);
cout << "1+2*3-4/5 ="<< endl;
cout<<sum;
Delete(root);
}
编写一个程序exp7-8.cpp,先用二叉树来表示一个简单算术表达式,树的每一个结点包括一个运算符或运算数。在简单算术表达式中只包含+、-、、/和一位正整数且格式正确(不包括括号),并且要按照先乘除后加减的原则构造二叉树,图7.34所示为“1+23-4/5”代数表达式对应的二叉树,然后由对应的二叉树计算该表达式的值。
#include
#include
using namespace std;
/*
* tree
*/
struct TreeNode {
int data;
TreeNode* left;
TreeNode* right;
TreeNode(int data) {
this->data = data;
this->left = NULL;
this->right = NULL;
}
TreeNode(int data, TreeNode* left, TreeNode* right) {
this->data = data;
this->left = left;
this->right = right;
}
};
//1.所有的叶子节点都必须是数字。
//2.所有的非叶子节点都是符号,而且都必须有左子树和右子树。
//3.不可能会存在一个节点仅存在左子树而不存在右子树,或者仅存在右子树而不存在左子树的情况。
//4.+ -节点只存在于最左边,左孩子只能是 + -或者数字,右孩子只能是 * / 或者数字
//5.* / 节点的右孩子只能是数字,左孩子只能是 * / 或者数字
/*
* 字符变数字
*/
int StringToInt(string val) {
reverse(val.begin(), val.end());//反转
int ans = 0;
for (int i = 0; i < val.size(); i++) {//转数字
ans += (val[i] - '0') * pow(10, i);
}
return ans;
}
/*字符串种树
* 1.所有的叶子节点都必须是数字。
2.所有的非叶子节点都是符号,而且都必须有左子树和右子树。
//3.不可能会存在一个节点仅存在左子树而不存在右子树,或者仅存在右子树而不存在左子树的情况。
//4.+ -节点只存在于最左边,左孩子只能是 + -或者数字,右孩子只能是 * / 或者数字
//5.* / 节点的右孩子只能是数字,左孩子只能是 * / 或者数字
*/
TreeNode* creatTree(string s) {
//s为空 返回null
if (s == "") {
return NULL;
}
//从右往左遍历先找+-
//for(int a =s.size()-1;a>=0&& s[a] != '+' && s[a] != '-';a--){}
int i = s.size() - 1;
while (i >= 0 && s[i] != '+' && s[i] != '-') {
i--;//有可能是-1
}
if (i >= 0) {
TreeNode* root = new TreeNode((int)s[i]);//char的形式转化为int
root->left = creatTree(s.substr(0, i));
root->right = creatTree(s.substr(i + 1));
return root;
}
//找不到+/ 找*/
// 新建节点
// 左指针指向 符号左边字符串
// 右指针指向 符号右边字符串
int j = s.size() - 1;
while (j >= 0 && s[j] != '*' && s[j] != '/') {
j--;
}
if (j >= 0) {
TreeNode* root = new TreeNode((int)s[j]);//char的形式转化为int
root->left = creatTree(s.substr(0, j));
root->right = creatTree(s.substr(j + 1));
return root;
}
//只有一个字符的时候才有会出现数字
TreeNode* root = new TreeNode(StringToInt(s));
return root;
}
/*
* calculater
*/
int calculate(TreeNode*root) {
//只有符号才有左右子树
if (!root) {//空返回0
return 0;
}
//非空
//有左右子树 就是符号
int res = 0;
if (root->left && root->right) {
switch (root->data) {
case '+':
res = calculate(root->left) + calculate(root->right);
break;
case'-':
res = calculate(root->left) - calculate(root->right);
break;
case'*':
res = calculate(root->left) * calculate(root->right);
break;
case'/':
res = calculate(root->left) * calculate(root->right);
break;
}
}
else {//符号之外就是数字
res = root->data;
}
return res;
}
void Delete(TreeNode*root) {
if (root == NULL) {//空返回
return;
}
else {
Delete(root->left);
Delete(root->right);
root->left = NULL;
root->right = NULL;
delete root;
}
}
int main() {
string Test = "1+2*3-4/5";
TreeNode* root=NULL;
int sum;
root= creatTree(Test);
sum = calculate(root);
cout << "1+2*3-4/5 ="<< endl;
cout<<sum;
Delete(root);
}
编写一个程序exp7-9.cpp,采用一棵二叉树表示一个家谱关系(由若干家谱记录构成,每个家谱记录由父亲、母亲和儿子的姓名构成,其中姓名是关键字),要求程序具有以下功能。
文件操作功能:家谱记录的输入,家谱记录的输出,清除全部文件记录和将家谱记录存盘。要求在输入家谱记录时按从祖先到子孙的顺序输入,第一个家谱记录的父亲域为所有人的祖先。
家谱操作功能:用括号表示法输出家谱二叉树,查找某人的所有儿子,查找某人的所有祖先(这里的祖先是指所设计的二叉树结构中某结点的所有祖先结点)。
#include
#include
#include
using namespace std;
typedef long long ll;
typedef struct Node {
string name; //姓名
Node* left; //左指针
Node* right; //右指针
Node* parent; //双亲
bool isBoy; //1男 0女
Node() {
name = "";
left = NULL;
right = NULL;
parent = NULL;
isBoy = 1;
}
}TreeNode;
Node* root = NULL;
ifstream ifs;
ofstream ofs;
string level, f, m;
//根据文件内容建树
void init(TreeNode*& t, int depth = 1) {
//默认祖先一定存在
//如果丈夫没有妻子就没有孩子
//一次读入三种字符 同一层级别标准 父亲名字 母亲名字
t = new TreeNode();
t->name = f;
TreeNode* wife = 0;
if (m != ".") {//如果没有老婆用 '.'表示
//有妻子
wife = new TreeNode();
wife->name = m;
wife->isBoy = 0;
wife->parent = t;
t->left = wife;
}
level = "";
ifs >> level >> f >> m; //继续读入3个数据
if (ifs.eof()) { //如果读完了,就直接返回
return;
}
if (level.size() == depth) { //辈分一样,说明是兄弟
init(t->right, depth); //递归建树
t->right->parent = t;
}
else if (level.size() == depth + 1 && wife) {//层数符号是当前的下一层 老婆存在 说明有儿子
//建立儿子
init(wife->right, depth + 1);
wife->right->parent = wife;
}
else {
//如果都不是,那么返回去给上一层
return;
}
//上面递归return后的还剩下的a b c继续判断
if (level.size() == depth) {
init(t->right, depth);
t->right->parent = t;
}
}
//括号法输出二叉树
void bracketingPrint(TreeNode* t = root) {
if (!t) {
return;
}
cout << t->name;
if (t->left || t->right) {
cout << "(";
bracketingPrint(t->left);
if (t->right) {
cout << ",";
bracketingPrint(t->right);
}
else {
cout << ",";
}
cout << ")";
}
}
//根据名称寻找
TreeNode* findPeople(string name, TreeNode* t = root) {
if (!t) {
return 0;
}
if (t->name == name) {
return t;
}
TreeNode* n = findPeople(name, t->left);
if (n) {
return n; //如果在左边找到就返回左边的
}
return findPeople(name, t->right); //否则返回右边的
}
//查找某人的所有儿子
void findAllChildren(string name) {
cout << "---------------------------------------" << endl;
cout << "找 " << name << " 所有儿子 " << endl;
TreeNode* node = findPeople(name);
if (!node) {
cout << "查不到" << name << endl; //找不到这个人
return;
}
if (node->isBoy) {
//如果是男的
if (!(node->left)) {
cout << name << " 没有妻子!" << endl; //他没有妻子
return;
}
node = node->left;
}
//女的
if (node->right) {
node = node->right;
cout << name << "所有的儿子 " << " 在此:" << endl;
while (node) {
cout << node->name << endl;
node = node->right;
}
}
else {
//没有儿子
cout << name << " 没有儿子!" << endl;
}
}
void findAllAncestors(string name) {
cout << "----------------------------------------" << endl;
cout << "找" << name << "所有祖先" << endl;
TreeNode* node = findPeople(name);
if (!node) {
cout << "People " << name << " 没有找到!!!" << endl; //找不到这个人
return;
}
if (!node->isBoy) {
//女,与祖先们不构成血缘关系,忽略
cout << name << " 不是男性!!!" << endl;
return;
}
cout << "所有的祖先 " << name << " 在此:" << endl;
node = node->parent;
while (node) {
if (node->isBoy) {
cout << node->name << endl;
node = node->parent;
cout << node->name << endl;
}
node = node->parent; //一直往上寻找
}
}
/*
输出家谱
*/
void printGenealogy(TreeNode* t = root, int depth = 0) {
if (!t) {
return;
}
cout << string(depth + 1, '#') << " " << t->name << " ";
if (t->left) {
cout << t->left->name << endl;
printGenealogy(t->left->right, depth + 1);
}
else {
cout << "." << endl;
}
printGenealogy(t->right, depth);
}
/*
输出家谱到文件
*/
void printGenealogyToFile(TreeNode* t = root, int depth = 0) {
if (!t || !ofs.is_open()) {
return;
}
ofs << string(depth + 1, '#') << " " << t->name << " ";
if (t->left) {
ofs << t->left->name << endl;
printGenealogyToFile(t->left->right, depth + 1);
}
else {
ofs << "." << endl;
}
printGenealogyToFile(t->right, depth);
}
/*
销毁二叉树
*/
void destory(TreeNode* node = root) {
if (node == NULL) {
return; //已经为NULL,无需销毁
}
destory(node->left);
destory(node->right);
node->left = NULL; //销毁之后需要把指针置空
node->right = NULL;
delete node;
}
void showOutTree(Node* root, int i = 0) {
if (root) {
cout << string(i * 3, ' ') << root->name << endl;
showOutTree(root->left, i + 1);
showOutTree(root->right, i + 1);
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
ifs.open("Z:\\mdzz.txt", ios::in); //打开data.txt文件
if (!ifs.is_open()) {
cout << "打开失败";
return 0;
}
ifs >> level >> f >> m; //读入文件里面的3个数据
init(root); //递归建树
showOutTree(root);
ifs.close(); //使用完成后要关闭流
bracketingPrint(); //括号法输出
cout << endl;
findAllChildren("s1"); //查找zhangsan的所有儿子
findAllChildren("f3"); //查找lujiajing的所有儿子
findAllChildren("f4"); //查找所有儿子
findAllChildren("f5"); //查找所有儿子
findAllAncestors("f6"); //查找他的所有祖先
findAllAncestors("f8"); //查找他的所有祖先
cout << "输入到文件" << endl; // 输出家谱
printGenealogy();
//输出家谱到文件
ofs.open("Z:\\mdzz.txt", ios::out);
cout << "以上为输入文件内容" << endl; // 输出家谱到文件
printGenealogyToFile();
destory(); //销毁二叉树
root = 0; //置空
return 0;
}