要求
编写程序,实现二叉树类及若干应用算法。
要求采用二叉链表结构,实现以下功能:
(1)构造函数:根据带空指针标记的先序遍历序列构造对象;根据先序和中序遍历序列构造对象;
(2)析构函数:释放所有结点空间;
(3)先序、中序、后序、层次遍历算法;
(4)统计叶子结点、单分支结点、双分支结点的个数;
(5)计算二叉树的高度;
(6)根据关键值查找结点;
(7)查找某个结点的父结点。
以下图二叉树为例:
带空指针的形式abd**e**cf***:
1.先序遍历:abdecf(根->左子树->右子树)
中序遍历:dbeafc(左子树->根->右子树)
后序遍历:debfca(左子树->右子树->根)
层次遍历:abcdef(逐层遍历,从左到右依次访问每个节点)
2.叶子结点、单分支结点、双分支结点的个数分别为:3、1、2
3.高度:3
.h代码拆解
构造函数
(1)带空指针的先序序列
private:
template<class T>
BiNode<T>* BiTree<T>::CreateByPreNull(vector<T>& pre, int& i)
{
T e = pre[i];
i++;
if (e == '*') return NULL;
BiNode<T>* p = new BiNode<T>(e);
p->lchild = CreateByPreNull(pre, i);
p->rchild = CreateByPreNull(pre, i);
return p;
}
public:
BiTree(vector<T>& pre) // 由先序序列构造二叉树
{
int i = 0;
root = CreateByPreNull(pre, i);
}
(2)先序和中序
private:
template<class T>
BiNode<T>* BiTree<T>::CreateByPreMid(vector<T>& pre, vector<T>& mid, int ipre, int imid, int n)
{
if (n == 0)
return NULL;
BiNode<T>* p = new BiNode<T>;
p->data = pre[ipre];
int i = 0;
for (; i < n; i++) {
if (pre[ipre] == mid[imid + i])
break;
}
p->lchild = CreateByPreMid(pre, mid, ipre + 1, imid, i);
p->rchild = CreateByPreMid(pre, mid, ipre + i + 1, imid + i + 1, n - i - 1);
return p;
}
public:
template<class T>
inline BiTree<T>::BiTree(vector<T>& pre, vector<T>& mid)
{
int n = pre.size();
root = CreateByPreMid(pre, mid, 0, 0, n);
}
析构函数
public:
~BiTree() { Destory(root); }
private:
template<class T>
void BiTree<T>::Destory(BiNode<T>* p)
{
if (p == NULL) return;
Destory(p->lchild);
Destory(p->rchild);
delete p;
}
遍历:先序、中序、后续、层次
public:
void PreOrder() { PreOrder(root); }
void InOrder() { InOrder(root); }
void PostOrder() { PostOrder(root); }
void LevelOrder() {
if (root == NULL) return;
LinkQueue<BiNode<T>*> Q;
Q.EnQueue(root);
while (!Q.Empty()){
auto p = Q.DeQueue();
cout << p->data << " ";
if (p->lchild != NULL) Q.EnQueue(p->lchild);
if (p->rchild != NULL) Q.EnQueue(p->rchild);
}
}
private:
void PreOrder(BiNode<T>* p);
void InOrder(BiNode<T>* p);
void PostOrder(BiNode<T>* p);
template<class T>
void BiTree<T>::PreOrder(BiNode<T>* p)
{
if (p == NULL) return;
cout << p->data << " ";
PreOrder(p->lchild);
PreOrder(p->rchild);
}
template<class T>
void BiTree<T>::InOrder(BiNode<T>* p)
{
if (p == NULL) return;
InOrder(p->lchild);
cout << p->data << " ";
InOrder(p->rchild);
}
template<class T>
void BiTree<T>::PostOrder(BiNode<T>* p)
{
if (p == NULL) return;
PostOrder(p->lchild);
PostOrder(p->rchild);
cout << p->data << " ";
}
统计叶子结点、单分支结点、双分支结点的个数
public:
int leafNodeCount() { return leafNodeCount(root); }
int singleNodeCount() { return singleNodeCount(root); }
int doubleNodeCount() { return doubleNodeCount(root); }
private:
template<class T>
inline int BiTree<T>::leafNodeCount(BiNode<T>* p)
{
if (!p) return 0;
if (p->lchild == NULL && p->rchild == NULL) return 1;
int left = leafNodeCount(p->lchild);
int right = leafNodeCount(p->rchild);
return left + right;
}
template<class T>
inline int BiTree<T>::singleNodeCount(BiNode<T>* p)
{
if (!p) return 0;
int res = 0;
if ((p->lchild == NULL && p->rchild) || (p->lchild && p->rchild == NULL)) res++;
int left = singleNodeCount(p->lchild);
int right = singleNodeCount(p->rchild);
return left + right + res;
}
template<class T>
inline int BiTree<T>::doubleNodeCount(BiNode<T>* p)
{
if (!p) return 0;
int res = 0;
if (p->lchild && p->rchild) res++;
int left = doubleNodeCount(p->lchild);
int right = doubleNodeCount(p->rchild);
return res + left + right;
}
高度
public:
int Height() { return Height(root); }
private:
template<class T>
int BiTree<T>::Height(BiNode<T>* p)
{
if (p == NULL) return 0;
int left = Height(p->lchild);
int right = Height(p->rchild);
return max(left, right) + 1; // #include <algorithm>
}
查找结点
private:
template<class T>
BiNode<T>* BiTree<T>::Search(BiNode<T>* p, T e){
if (p == NULL) return NULL;
if (p->data == e) return p;
BiNode<T>* q = Search(p->lchild, e);
if (q != NULL) return q;
return Search(p->rchild, e);
}
public:
bool Search(T e) {
BiNode<T>* q = Search(root, e);
if (q == NULL)
{
cout << "未找到该结点" << endl;
return false;
}
else
{
cout << "该结点为:" << q->data << endl;
return true;
}
}
查找父结点
根据孩子结点的值查找
public:
bool SearchParent(T e)
{
BiNode<T>* q = SearchParent(root, e);
if (q == NULL){
cout << "该结点无父结点" << endl;
return false;
}
else{
cout << "该结点的父结点为:" << q->data << endl;
return true;
}
}
private:
template<class T>
BiNode<T>* BiTree<T>::SearchParent(BiNode<T>* p, T e){
if (p == NULL) return NULL;
if (p->lchild && p->lchild->data == e) return p;
if (p->rchild && p->rchild->data == e) return p;
BiNode<T>* q = SearchParent(p->lchild, e);
if (q != NULL) return q;
return SearchParent(p->rchild, e);
}
根据孩子结点的指针查找
private:
template<class T>
BiNode<T>* BiTree<T>::SearchParent(BiNode<T>* p, BiNode<T>* child){
if (p == NULL || child == NULL) return NULL;
if (p->lchild == child || p->rchild == child) return p;
BiNode<T>* q = SearchParent(p->lchild, child);
if (q != NULL) return q;
return SearchParent(p->rchild, child);
}
public:
BiNode<T>* SearchParent(BiNode<T>* child) { return SearchParent(root, child); }
.h全部代码
#pragma once
#include <vector>
#include "LinkQueue.h"
#include <algorithm>
template <class T>
struct BiNode
{
T data;
BiNode<T>* lchild;
BiNode<T>* rchild;
BiNode() :lchild(NULL), rchild(NULL) {}
BiNode(const T& x) :data(x), lchild(NULL), rchild(NULL) {}
};
template <class T>
class BiTree
{
private:
BiNode<T>* root;
void PreOrder(BiNode<T>* p);
void InOrder(BiNode<T>* p);
void PostOrder(BiNode<T>* p);
BiNode<T>* CreateByPreNull(vector<T>&, int&);
BiNode<T>* CreateByPreMid(vector<T>&, vector<T>&, int, int, int);
void Destory(BiNode<T>* p);
int Count(BiNode<T>* p);
int leafNodeCount(BiNode<T>* p);
int singleNodeCount(BiNode<T>* p);
int doubleNodeCount(BiNode<T>* p);
int Height(BiNode<T>* p);
BiNode<T>* Search(BiNode<T>* p, T e);
BiNode<T>* SearchParent(BiNode<T>* p, BiNode<T>* child);
BiNode<T>* SearchParent(BiNode<T>* p, T e);
public:
BiTree() { root = NULL; }
BiTree(vector<T>& pre) // 由先序序列构造二叉树
{
int i = 0;
root = CreateByPreNull(pre, i);
}
BiTree(vector<T>& pre, vector<T>& mid);
BiTree(BiTree<T>& tree);
~BiTree() { Destory(root); }
void PreOrder() { PreOrder(root); }
void InOrder() { InOrder(root); }
void PostOrder() { PostOrder(root); }
void LevelOrder() {
if (root == NULL) return;
LinkQueue<BiNode<T>*> Q;
Q.EnQueue(root);
while (!Q.Empty())
{
auto p = Q.DeQueue();
cout << p->data << " ";
if (p->lchild != NULL) Q.EnQueue(p->lchild);
if (p->rchild != NULL) Q.EnQueue(p->rchild);
}
}
int Count() { return Count(root); } // 统计节点个数
int leafNodeCount() { return leafNodeCount(root); }
int singleNodeCount() { return singleNodeCount(root); }
int doubleNodeCount() { return doubleNodeCount(root); }
int Height() { return Height(root); } // 计算二叉树的高度
bool Search(T e) {
BiNode<T>* q = Search(root, e);
if (q == NULL)
{
cout << "未找到该结点" << endl;
return false;
}
else
{
cout << "该结点为:" << q->data << endl;
return true;
}
}
BiNode<T>* SearchParent(BiNode<T>* child) { return SearchParent(root, child); }
bool SearchParent(T e)
{
BiNode<T>* q = SearchParent(root, e);
if (q == NULL)
{
cout << "该结点无父结点" << endl;
return false;
}
else
{
cout << "该结点的父结点为:" << q->data << endl;
return true;
}
}
};
template<class T>
void BiTree<T>::PreOrder(BiNode<T>* p)
{
if (p == NULL) return;
cout << p->data << " ";
PreOrder(p->lchild);
PreOrder(p->rchild);
}
template<class T>
void BiTree<T>::InOrder(BiNode<T>* p)
{
if (p == NULL) return;
InOrder(p->lchild);
cout << p->data << " ";
InOrder(p->rchild);
}
template<class T>
void BiTree<T>::PostOrder(BiNode<T>* p)
{
if (p == NULL) return;
PostOrder(p->lchild);
PostOrder(p->rchild);
cout << p->data << " ";
}
template<class T>
BiNode<T>* BiTree<T>::CreateByPreNull(vector<T>& pre, int& i)
{
T e = pre[i];
i++;
if (e == '*') return NULL;
BiNode<T>* p = new BiNode<T>(e);
p->lchild = CreateByPreNull(pre, i);
p->rchild = CreateByPreNull(pre, i);
return p;
}
template<class T>
BiNode<T>* BiTree<T>::CreateByPreMid(vector<T>& pre, vector<T>& mid, int ipre, int imid, int n)
{
if (n == 0)
return NULL;
BiNode<T>* p = new BiNode<T>;
p->data = pre[ipre];
int i = 0;
for (; i < n; i++) {
if (pre[ipre] == mid[imid + i])
break;
}
p->lchild = CreateByPreMid(pre, mid, ipre + 1, imid, i);
p->rchild = CreateByPreMid(pre, mid, ipre + i + 1, imid + i + 1, n - i - 1);
return p;
}
template<class T>
inline BiTree<T>::BiTree(vector<T>& pre, vector<T>& mid)
{
int n = pre.size();
root = CreateByPreMid(pre, mid, 0, 0, n);
}
template<class T>
void BiTree<T>::Destory(BiNode<T>* p)
{
if (p == NULL) return;
Destory(p->lchild);
Destory(p->rchild);
delete p;
}
template<class T>
int BiTree<T>::Count(BiNode<T>* p)
{
if (p == NULL) return 0;
int left = Count(p->lchild);
int right = Count(p->rchild);
return left + right + 1;
}
template<class T>
inline int BiTree<T>::leafNodeCount(BiNode<T>* p)
{
if (!p) return 0;
if (p->lchild == NULL && p->rchild == NULL) return 1;
int left = leafNodeCount(p->lchild);
int right = leafNodeCount(p->rchild);
return left + right;
}
template<class T>
inline int BiTree<T>::singleNodeCount(BiNode<T>* p)
{
if (!p) return 0;
int res = 0;
if ((p->lchild == NULL && p->rchild) || (p->lchild && p->rchild == NULL)) res++;
int left = singleNodeCount(p->lchild);
int right = singleNodeCount(p->rchild);
return left + right + res;
}
template<class T>
inline int BiTree<T>::doubleNodeCount(BiNode<T>* p)
{
if (!p) return 0;
int res = 0;
if (p->lchild && p->rchild) res++;
int left = doubleNodeCount(p->lchild);
int right = doubleNodeCount(p->rchild);
return res + left + right;
}
template<class T>
int BiTree<T>::Height(BiNode<T>* p)
{
if (p == NULL) return 0;
int left = Height(p->lchild);
int right = Height(p->rchild);
return max(left, right) + 1;
}
template<class T>
BiNode<T>* BiTree<T>::Search(BiNode<T>* p, T e)
{
if (p == NULL) return NULL;
if (p->data == e) return p;
BiNode<T>* q = Search(p->lchild, e);
if (q != NULL) return q;
return Search(p->rchild, e);
}
template<class T>
BiNode<T>* BiTree<T>::SearchParent(BiNode<T>* p, BiNode<T>* child)
{
if (p == NULL || child == NULL) return NULL;
if (p->lchild == child || p->rchild == child) return p;
BiNode<T>* q = SearchParent(p->lchild, child);
if (q != NULL) return q;
return SearchParent(p->rchild, child);
}
template<class T>
BiNode<T>* BiTree<T>::SearchParent(BiNode<T>* p, T e)
{
if (p == NULL) return NULL;
if (p->lchild && p->lchild->data == e) return p;
if (p->rchild && p->rchild->data == e) return p;
BiNode<T>* q = SearchParent(p->lchild, e);
if (q != NULL) return q;
return SearchParent(p->rchild, e);
}
.cpp测试代码
#define _CRT_SECURE_NO_WARNINGS
#include "LinkQueue.h"
#include "BiTree.h"
#include <iostream>
#include <string>
using namespace std;//abd**e**cf***
void PrintMenu()
{
cout << "---------------------------" << endl;
cout << "请选择需要进行的操作" << endl;
cout << "1. 用四种方法遍历二叉树" << endl;
cout << "2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高" << endl;
cout << "3. 根据关键值查找结点" << endl;
cout << "4. 求某个结点的父节点" << endl;
cout << "5. 退出当前操作" << endl;
cout << "---------------------------" << endl;
}
template <class T>
void operate(BiTree<T>& BT) {
while (1)
{
bool flag = true;
PrintMenu();
int op; cin >> op;
switch (op) {
case 1:
{
cout << "先序遍历结果:";
BT.PreOrder();
cout << endl;
cout << "中序遍历结果:";
BT.InOrder();
cout << endl;
cout << "后序遍历结果:";
BT.PostOrder();
cout << endl;
cout << "层序遍历结果:";
BT.LevelOrder();
cout << endl;
break;
}
case 2:
{
cout << "结点总数为:" << BT.Count() << endl
<< "叶子结点总数:" << BT.leafNodeCount() << endl <<
"单分支结点总数:" << BT.singleNodeCount() << endl <<
"双分支结点总数:" << BT.doubleNodeCount() << endl;
cout << "树高为:" << BT.Height() << endl;
break;
}
case 3:
{
cout << "输入关键值:";
char s; cin >> s;
flag = BT.Search(s);
break;
}
case 4:
{
cout << "输入结点";
char s; cin >> s;
flag = BT.SearchParent(s);
break;
}
case 5:
{
op = -1;
break;
}
}
if (!flag)
{
cout << "操作失败" << endl;
cout << "---------------------------\n";
}
if (op == -1) break;
cout << endl;
}
return;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
while (1)
{
cout << "1.根据带空指针标记的先序遍历序列构造对象--------" << endl;
cout << "2.根据先序遍历和中序遍历序列构造对象--------" << endl;
cout << "3.退出--------" << endl;
cout << "请输入操作:" << endl;
int t; cin >> t;
if (t == 1) {
cout << "输入带空指针标记(*)的先序序列:";
string str; cin >> str;
vector<char> pre;
for (int i = 0; i < str.size(); i++)
pre.push_back(str[i]);
BiTree<char> BT(pre);
operate(BT);
}
else if (t == 2) {
cout << "输入先序序列和中序序列:";
string str; cin >> str;
vector<char> pre;
for (int i = 0; i < str.size(); i++)
pre.push_back(str[i]);
string str2; cin >> str2;
vector<char> mid;
for (int i = 0; i < str2.size(); i++)
mid.push_back(str2[i]);
BiTree<char> BT(pre, mid);
operate(BT);
}
else {
break;
}
}
return 0;
}
测试结果
1.根据带空指针标记的先序遍历序列构造对象--------
2.根据先序遍历和中序遍历序列构造对象--------
3.退出--------
请输入操作:
1
输入带空指针标记(*)的先序序列:abd**e**cf***
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
1
先序遍历结果:a b d e c f
中序遍历结果:d b e a f c
后序遍历结果:d e b f c a
层序遍历结果:a b c d e f
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
2
结点总数为:6
叶子结点总数:3
单分支结点总数:1
双分支结点总数:2
树高为:3
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
3
输入关键值:a
该结点为:a
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
4
输入结点b
该结点的父结点为:a
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
5
1.根据带空指针标记的先序遍历序列构造对象--------
2.根据先序遍历和中序遍历序列构造对象--------
3.退出--------
请输入操作:
2
输入先序序列和中序序列:abdecf dbeafc
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
1
先序遍历结果:a b d e c f
中序遍历结果:d b e a f c
后序遍历结果:d e b f c a
层序遍历结果:a b c d e f
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
2
结点总数为:6
叶子结点总数:3
单分支结点总数:1
双分支结点总数:2
树高为:3
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
3
输入关键值:f
该结点为:f
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
3
输入关键值:p
未找到该结点
操作失败
---------------------------
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
4
输入结点p
该结点无父结点
操作失败
---------------------------
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
4
输入结点a
该结点无父结点
操作失败
---------------------------
---------------------------
请选择需要进行的操作
1. 用四种方法遍历二叉树
2. 计算结点总数、叶子结点总数、单分支结点数、双分支结点数和树高
3. 根据关键值查找结点
4. 求某个结点的父节点
5. 退出当前操作
---------------------------
5
1.根据带空指针标记的先序遍历序列构造对象--------
2.根据先序遍历和中序遍历序列构造对象--------
3.退出--------
请输入操作:
3
程序异常终止
可能是输入序列不正确构成二叉树的原因。#输入的数据一定要保证能构成正确的二叉树#
基本知识点总结
二叉树是一种常用的树形数据结构,它由节点(Node)组成,每个节点最多有两个子节点,分别称为左子节点和右子节点。
1. 二叉树的基本概念:
- 根节点(Root):二叉树的顶层节点。
- 叶子节点(Leaf):没有子节点的节点。
- 父节点(Parent):有子节点的节点。
- 子节点(Child):在父节点下方的节点。
- 兄弟节点(Sibling):具有相同父节点的节点。
- 深度(Depth):从根节点到当前节点的路径长度。
- 高度(Height):根节点到最远叶子节点的路径长度。
2. 二叉树的分类:
- 满二叉树:除了叶子节点外,所有节点都有两个子节点,并且所有叶子节点在同一层级上。
- 完全二叉树:除了最后一层外,其他层节点全部填满,并且最后一层的节点从左到右依次填充。
3. 二叉树的遍历:
- 前序遍历(Pre-order):根节点 -> 左子树 -> 右子树。
- 中序遍历(In-order):左子树 -> 根节点 -> 右子树。
- 后序遍历(Post-order):左子树 -> 右子树 -> 根节点。
- 层序遍历(Level-order):逐层遍历,从左到右依次访问每个节点。
4. 二叉搜索树(Binary Search Tree,BST):
- 二叉搜索树是一种特殊的二叉树,它满足以下条件:
- 左子树上的所有节点的值小于根节点的值。
- 右子树上的所有节点的值大于根节点的值。
- 左右子树也分别是二叉搜索树。
- 在二叉搜索树中,查找、插入和删除操作的时间复杂度为O(log n)。
5. 平衡二叉树(Balanced Binary Tree):
- 平衡二叉树是一种特殊的二叉搜索树,它的左子树和右子树的高度差不超过1。
- 常见的平衡二叉树包括红黑树、AVL树等,通过平衡操作保持树的平衡性。
6. 二叉树的应用:
- 二叉树广泛应用于数据结构和算法中,例如排序算法、查找算法、哈夫曼编码等。
- 表达式树(Expression Tree):用于解析和计算数学表达式。
- 文件系统的目录结构:每个目录都可以看作是一个节点,子目录和文件为其子节点。