数据结构 C/C++(实验四:二叉树)

(大家好,今天分享的是数据结构的相关知识,大家可以在评论区进行互动答疑哦~加油!💕)

目录

提要:实验题目

一、实验目的

二、实验内容及要求

三、算法思想

实验1

实验2

四、源程序及注释

实验1代码(二叉树基本操作)

实验2代码(哈夫曼编码译码)

五、实验结果

实验1结果

实验2结果


提要:实验题目

  1. 二叉树的遍历及基本操作

  2. 二叉树中哈夫曼编码译码


一、实验目的

1.深入理解二叉树的基本概念和递归程序设计方法。

2.熟练掌握二叉树在二叉链表存储结构中的常用遍历方法:先序、中序、后序递归遍历,了解先序、中序和后序非递归遍历及层序遍历。

3.用二叉树解决实际问题,如掌握构造哈夫曼树及其编码和译码的方法。


二、实验内容及要求

1.建立一棵二叉树,并对其进行遍历(先序、中序、后序),打印输出遍历结果;

2.建立一棵二叉树,求二叉数的树的深度、统计叶子结点的个数、统计总的结点个数、进行层序遍历、交换左右子树等;

3.哈夫曼编码译码系统。

注:前两个题目必做,第3题选做。

算法 思想

实验1

本实验的主要目的是实现二叉树的基本操作,包括创建二叉树、遍历(先序、中序、后序)、统计叶子节点的个数以及计算二叉树的深度。通过这些操作,我们可以理解二叉树的结构和基本特性。

1.二叉树的定义:二叉树是一种树形结构,每个节点最多有两个子节点(左子节点和右子节点)。在本实验中,二叉树的节点由 BiNode 结构体表示,包含节点数据、左孩子、右孩子和父节点指针。

2. 树的创建:通过递归的方法构建二叉树。在输入时,用 # 表示空节点。每次输入一个节点后,递归调用创建其左子树和右子树。

3. 树的遍历

先序遍历:访问当前节点,然后递归访问左子树和右子树。

中序遍历:递归访问左子树,然后访问当前节点,最后递归访问右子树。

后序遍历:递归访问左子树和右子树后,再访问当前节点。

4. 统计叶子节点:叶子节点是没有子节点的节点。通过递归的方法统计叶子节点的个数。

5. 计算树的深度:树的深度是从根节点到最深叶子节点的最长路径。通过递归比较左子树和右子树的深度,返回较大的深度加一。

实验2

本实验的目标是实现哈夫曼编码和译码的基本功能。哈夫曼编码是一种无损数据压缩算法,通过构建哈夫曼树来为频率高的字符分配较短的编码,为频率低的字符分配较长的编码,从而有效减少编码后的数据大小。

1. 哈夫曼树的定义:哈夫曼树是一种特殊的二叉树,用于生成可变长度的哈夫曼编码。每个叶子节点代表一个字符及其出现频率,树的结构使得字符的编码长度与其频率成反比。

2. 优先队列:为了构建哈夫曼树,使用最小堆(优先队列)来存储节点。在每一步,取出频率最小的两个节点,合并成一个新的父节点,并将新节点插入优先队列。这个过程持续到队列中只剩一个节点,即哈夫曼树的根节点。

3. 编码生成:遍历哈夫曼树,利用深度优先搜索(DFS)生成每个字符的哈夫曼编码。左子树分配编码'0',右子树分配编码'1'。

4. 译码过程:根据哈夫曼树和编码字符串,逐位解析编码。每遇到'0'则转向左子节点,遇到'1'则转向右子节点,直到达到叶子节点,提取相应的字符。


四、源程序及注释

实验1 代码**(**二叉树基本操作)

cpp 复制代码
#include <iostream>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <stdbool.h>

using namespace std;
//预定义常量及预定义类型
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MAXSIZE 100
typedef int status; //Status 是函数返回值类型,其值是函数结果状态代码
typedef char TElemType;

//表示部分:
//创建结构体
typedef struct BiNode {
    TElemType data;
    struct BiNode* lchild; // 左孩子
    struct BiNode* rchild; // 右孩子
    struct BiNode* parent; // 父母
} BiNode, *BiTree;

status Print(TElemType e) {
    cout << e << " ";
    return OK;
}

//1. 创建二叉树
void CreateBiTree(BiTree &T) {
    TElemType a;
    cin >> a;
    if (a == '#') {
        T = NULL;
    } else {
        T = (BiNode*)malloc(sizeof(BiNode));
        if (!T) exit(OVERFLOW); 
        T->data = a;
        CreateBiTree(T->lchild);
        CreateBiTree(T->rchild);
    }
}

//2. 先序遍历输出
void PreOrderTraverse(BiTree T, status (*Print)(TElemType e)) {
    if (T) {
        Print(T->data);
        PreOrderTraverse(T->lchild, Print);
        PreOrderTraverse(T->rchild, Print);
    }
}

//3. 中序遍历输出
void InOrder(BiTree T, status(*Print)(TElemType e)) {
    if (T) {
        InOrder(T->lchild, Print);
        Print(T->data);
        InOrder(T->rchild, Print);
    }
}
//4. 后序遍历输出
void PostOrder(BiTree T, status(*Print)(TElemType e)) {
    if (T) {
        PostOrder(T->lchild, Print);
        PostOrder(T->rchild, Print);
        Print(T->data);
    }
}

//5. 叶子结点的个数
void countleaf(BiTree T, int &num) {
    if (T) {
        if (!T->lchild && !T->rchild) num++;
        countleaf(T->lchild, num);
        countleaf(T->rchild, num);
    }
}

//6. 二叉树的深度
int Depth(BiTree T) {
    if (!T) return 0; //树不为空
    int left = Depth(T->lchild);
    int right = Depth(T->rchild);
    return 1 + (left > right ? left : right);
}

void showMenu() {
	cout << "********************************\n";
	cout << "****      1. 创建二叉树     ****\n";
	cout << "****      2. 先序遍历输出   ****\n";
	cout << "****      3. 中序遍历输出   ****\n";
	cout << "****      4. 后序遍历输出   ****\n";
	cout << "****      5. 叶子结点的个数 ****\n";
	cout << "****      6. 二叉树的深度   ****\n";
	cout << "****      0. 退出程序       ****\n";
	cout << "********************************\n";
}

int main() {
    BiTree T = NULL; //非空
    int i;
	int choose= -1;
	showMenu();
	while (choose != 0) {
		cout << "Please select(0-6):";
		cin >> choose;
		switch (choose) {
            case 1://创建二叉树
                cout << "请输入树的结点(使用'#'表示空节点):";
                CreateBiTree(T);
                break;
            case 2://先序遍历输出
                cout << "先序遍历输出为:";
                PreOrderTraverse(T, Print);
                cout << endl;
                break;
            case 3://中序遍历输出
                cout << "中序遍历输出为:";
                InOrder(T, Print);
                cout << endl;
                break;
            case 4://后序遍历输出
                cout << "后序遍历输出为:";
                PostOrder(T, Print);
                cout << endl;
                break;
            case 5: //叶子结点的个数
			{
                int num = 0; 
                countleaf(T, num);
                cout << "叶子结点的个数为:" << num << endl;
                break;
            }
            case 6://二叉树的深度
                cout << "该二叉树的深度为:" << Depth(T) << endl;
                break;
            case 7://退出程序
                cout << "退出程序。" << endl; 
                exit(0);
                break;
            default:
                cout << "无效输入,请重新输入!" << endl;
                break;
        }
    }
}

实验2 代码**(哈夫曼编码译码**)

cpp 复制代码
#include <iostream>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <queue>
#include <unordered_map>
#include <vector>
using namespace std;

//预定义常量及预定义类型
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MAXSIZE 100
typedef int status; // Status 是函数返回值类型
typedef char TElemType;

// 哈夫曼树节点结构
struct BiNode {
    TElemType data;
    int freq;
    struct BiNode* left;  // 左孩子
    struct BiNode* right; // 右孩子
    
    // 用于构建优先队列
    bool operator<(const BiNode& other) const {
        return freq > other.freq; // 最小堆
    }
};

typedef BiNode* BiTree;

// 用于构建哈夫曼树
BiTree CreateHuffmanTree(const vector<pair<TElemType, int>>& freqTable) {
    priority_queue<BiTree> pq;

    // 将字符和频率插入优先队列
    for (const auto& item : freqTable) {
        BiTree node = new BiNode{item.first, item.second, nullptr, nullptr};
        pq.push(node);
    }

    // 构建哈夫曼树
    while (pq.size() > 1) {
        BiTree left = pq.top(); pq.pop();
        BiTree right = pq.top(); pq.pop();
        BiTree parent = new BiNode{'\0', left->freq + right->freq, left, right};
        pq.push(parent);
    }

    return pq.top(); // 返回根节点
}

// 生成哈夫曼编码
void GenerateCodes(BiTree root, const string& code, unordered_map<TElemType, string>& huffmanCodes) {
    if (root->left == nullptr && root->right == nullptr) {
        huffmanCodes[root->data] = code; // 叶子节点
        return;
    }
    if (root->left) GenerateCodes(root->left, code + '0', huffmanCodes);
    if (root->right) GenerateCodes(root->right, code + '1', huffmanCodes);
}

// 译码
string DecodeHuffman(BiTree root, const string& encodedStr) {
    string decodedStr;
    BiTree currentNode = root;

    for (char bit : encodedStr) {
        currentNode = (bit == '0') ? currentNode->left : currentNode->right;
        
        // 到达叶子节点
        if (currentNode->left == nullptr && currentNode->right == nullptr) {
            decodedStr += currentNode->data; // 添加字符
            currentNode = root; // 重置到根节点
        }
    }

    return decodedStr;
}

void showMenu() {
    cout << "********************************\n";
    cout << "****      1. 构建哈夫曼树   ****\n";
    cout << "****      2. 打印哈夫曼编码 ****\n";
    cout << "****      3. 译码哈夫曼编码 ****\n";
    cout << "****      0. 退出程序       ****\n";
    cout << "********************************\n";
}

int main() {
    BiTree huffmanRoot = nullptr;
    unordered_map<TElemType, string> huffmanCodes;
	int choose= -1;
	showMenu();
	while (choose != 0) {
		cout << "Please select(0-3):";
		cin >> choose;
		switch (choose) {
            case 1: {
                // 输入字符及其频率
                int n;
                cout << "请输入字符个数:";
                cin >> n;

                vector<pair<TElemType, int>> freqTable(n);
                cout << "请输入字符及其频率(字符 空格 频率):\n";
                for (int i = 0; i < n; i++) {
                    cin >> freqTable[i].first >> freqTable[i].second;
                }

                huffmanRoot = CreateHuffmanTree(freqTable);
                GenerateCodes(huffmanRoot, "", huffmanCodes);
                cout << "哈夫曼树构建完成!\n";
                break;
            }
            case 2: {
                cout << "哈夫曼编码:\n";
                for (const auto& pair : huffmanCodes) {
                    cout << pair.first << ": " << pair.second << endl;
                }
                break;
            }
            case 3: {
                cout << "请输入要译码的哈夫曼编码:";
                string encodedStr;
                cin >> encodedStr;

                string decodedStr = DecodeHuffman(huffmanRoot, encodedStr);
                cout << "译码结果为:" << decodedStr << endl;
                break;
            }
            case 0:
                cout << "退出程序。" << endl;
                exit(0);
            default:
                cout << "无效输入,请重新输入!" << endl;
                break;
        }
    }

    return 0;
}

五、实验 结果

实验1结果

实验2结果


(今日分享暂时到此为止啦!为不断努力的自己鼓鼓掌吧🥳。今日文案分享:得胜当以你为期,越山先悦己。)

相关推荐
神秘的土鸡18 分钟前
机器情绪及抑郁症算法
人工智能·算法
鬣主任35 分钟前
二叉树的前序遍历---一个简单高效的算法
数据结构·算法
jianbaigreat2 小时前
代码随想录打卡Day22、23、24、25
数据结构·算法
Ddddddd_1582 小时前
C++ | Leetcode C++题解之第560题和为K的子数组
c++·leetcode·题解
_OLi_2 小时前
力扣 LeetCode 206. 反转链表(Day2:链表)
算法·leetcode·链表
运维&陈同学2 小时前
【HAProxy05】企业级反向代理HAProxy调度算法之静态算法与动态算法
linux·运维·算法·nginx·云原生·负载均衡·lvs·haproxy
weixin_478689762 小时前
【贪心算法】——力扣763. 划分字母区间
算法·leetcode·贪心算法
sp_fyf_20242 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-03
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
夏天匆匆2过2 小时前
linux性能提升之sendmmsg和recvmmsg
linux·c++·单片机·网络协议·udp·tcp
Jack黄从零学c++2 小时前
设计模式——策略模式(c++)
c++·设计模式·策略模式