• 数据结构之二叉树,赫夫曼树(C++版)


    #include <iostream>
    #include <windows.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAXLISTSIZE 100 //预设的存储空间最大容量
    #define FALSE 0
    #define TRUE 1
    using namespace std;
    typedef char ElemType;

    typedef struct BiTNode{
    ElemType data;
    struct BiTNode *Lchild, *Rchild; //左、右孩子指针
    }BiTNode, *BiTree;

    typedef struct{
    BiTree *base; //存储空间基址
    int top; //栈顶指针
    int stacksize; //允许的最大存储空间以元素为单位
    }Stack;

    //Link(0):表示指向左右孩子的指针
    //Thread(1):表示指向前驱后继的线索
    typedef enum PointerTag{Link, Thread};
    //定义指针类型,以 Link 表示指针,Thread 表示线索
    typedef struct BiThrNode{
    ElemType data;
    struct BiThrNode *Lchild, *Rchild; //左右指针
    PointerTag LTag, RTag; //左右指针类型标志
    }BiThrNode, *BiThrTree;

    typedef struct{
    char ch;
    int weight; //用来存放各个结点的权值
    int parent, LChild, RChild; //指向双亲、孩子结点的指针
    }HTNode, *HuffmanTree;
    typedef char **HuffmanCode; //动态分配数组存储哈夫曼编码

    void CreateBiTree(BiTree &T)
    {
    //在先序遍历二叉树的过程中输入二叉树的"先序字符串",建立根指针为 T的二叉链表存储结构。
    //在先序字符串中,字符'#'表示空树,其它字母字符为结点的数据元素
    char ch;
    ch = getchar();
    if(ch=='#') T=NULL; //建空树
    else{
    T = new BiTNode; //"访问"操作为生成根结点
    T->data = ch;
    CreateBiTree(T->Lchild); //递归建(遍历)左子树
    CreateBiTree(T->Rchild); //递归建(遍历)右子树
    }//else
    }//CreateBiTree

    void InOrder(BiTree T)
    { //中序递归遍历以T为根指针的二叉树并输出二叉树
    if(T)
    { //T=NULL时,二叉树为空树,不做任何操作
    InOrder(T->Lchild); //中序遍历左子树
    cout << T->data; //输出T
    InOrder(T->Rchild); //中序遍历右子树
    }//if
    }

    void InitStack(Stack &S)
    {
    // 构造一个最大存储容量为maxsize的空栈S
    int maxsize;
    maxsize = MAXLISTSIZE;
    S.base = new BiTree[maxsize];
    if (!S.base) exit(1); //存储分配失败
    S.stacksize = maxsize;
    S.top = 0; //空栈中元素个数为0
    }

    bool Push(Stack &S, BiTree e)
    {
    // 若栈的存储空间不满,则插入元素 e 为新的栈顶元素,并返回 TRUE;否则返回 FALSE
    if (S.top == S.stacksize) //栈已满,无法进行插入
    return FALSE;
    S.base[S.top] = e; //插入新的元素
    ++S.top; //栈顶指针后移
    return TRUE;
    }

    bool Pop(Stack &S, BiTree &e)
    {
    //若栈不空,则删除S的栈顶元素,用 e 返回其值,并返回 TRUE;否则返回 FALSE
    if (S.top == 0)
    return FALSE;
    e = S.base[S.top-1]; //返回非空栈中栈顶元素
    --S.top;//栈顶指针前移
    return TRUE;
    }

    int StackLength(Stack S)
    {
    //返回S的元素个数,即栈的长度。
    return S.top;
    }

    void InOrderTraverse(BiTree T)
    { //中序非递归(采用栈)遍历以T为根指针的二叉树并输出二叉树
    BiTree p;
    Stack S;
    InitStack(S);
    p = T;
    while(p||StackLength(S))
    {
    if(p)
    { //根指针进栈,遍历左子树
    Push(S, p);
    p = p->Lchild;
    }
    else
    { //根指针退栈,访问根结点,遍历右子树
    Pop(S, p);
    cout << p->data;
    p = p->Rchild;
    }
    }
    }//InOrderTraverse

    //创建一棵树
    void CreatBiThrTree(BiThrTree &BT)
    {
    char ch;
    ch = getchar();
    if(ch=='#') BT=NULL; //建空树
    else{
    BT = new BiThrNode; //"访问"操作为生成根结点
    BT->data = ch;
    BT->LTag = Link;
    BT->RTag = Link;
    CreatBiThrTree(BT->Lchild); //递归建(遍历)左子树
    CreatBiThrTree(BT->Rchild); //递归建(遍历)右子树
    }
    }

    void InThreading(BiThrTree p, BiThrTree &pre)
    { //对 p 指向根结点的二叉树进行中序遍历,遍历过程中进行"中序线索化"。
    //若 p 所指结点的左指针为空,则将它改为"左线索",
    //若 pre 所指结点的右指针为空,则将它改为"右线索"。
    //指针 pre 在遍历过程中紧随其后,即始终指向 p 所指结点在中序序列中的前驱。
    if(p){
    InThreading(p->Lchild, pre); //对左子树进行线索化
    if(!p->Lchild)
    {
    p->LTag = Thread;
    p->Lchild = pre;
    } //建前驱线索
    if(!pre->Rchild)
    {
    pre->RTag = Thread;
    pre->Rchild = p;
    } //建后继线索
    pre = p; //保持 pre 指向 p 的前驱
    InThreading(p->Rchild, pre); //对右子树进行线索化
    }//if
    }//InThreading

    //中序遍历线索化
    void InOrderThreading(BiThrTree &Thrt, BiThrTree BT)
    {
    //BT为指向二叉树根结点的指针,由此二叉链表建立二叉树的中序线索链表,Thrt指向线索链表中的头结点。
    BiThrTree pre;
    if(!(Thrt = new BiThrNode))
    exit(1); //存储分配失败
    Thrt->LTag = Link;
    Thrt->RTag =Thread; //建头结点
    Thrt->Rchild = Thrt; //右指针回指
    if(!BT)
    Thrt->Lchild = Thrt; //若二叉树空,则左指针回指
    else{
    Thrt->Lchild = BT;
    pre = Thrt;
    InThreading(BT, pre); //中序遍历进行中序线索化
    pre->Rchild = Thrt;
    pre->RTag = Thread; //对中序序列中最后一个结点进行线索化
    Thrt->Rchild = pre; //建非空树的头结点的"右线索"
    }//else
    }//InOrderThreading

    //中序遍历并输出线索二叉树
    void InOrderTraverse_Thr(BiThrTree T)
    { //T指向中序线索链表中的头结点,头结点的左指针 Lchild 指向二叉树的根结点,
    //头结点的右线索 Rchild 指向中序遍历访问的最后一个结点。
    //本算法对此二叉树进行中序遍历并输出线索二叉树
    BiThrTree p;
    p = T->Lchild; //p指向二叉树的根结点
    while(p!= T)
    { //空树或遍历结束时,p==Thead
    while(p->LTag == Link)
    p = p->Lchild;
    cout << p->data; //访问其左子树为空的结点
    while(p->RTag == Thread && p->Rchild != T)
    {
    p = p->Rchild;
    cout << p->data;; //访问"右线索"所指后继结点
    }//while
    p = p->Rchild; //p进至其右子树根
    }//while
    }//InOrderTraverse_Thr

    void select(HuffmanTree &HT, int a, int &p1, int &p2)
    {
    int i, j, x, y;
    for(j = 1; j <= a; ++j)
    {
    if(HT[j].parent == 0)
    {
    x = j;
    break;
    }
    }
    for(i = j + 1; i <= a; ++i)
    {
    if(HT[i].weight < HT[x].weight && !HT[i].parent)
    {
    x=i; //选出最小的节点
    }
    }
    for(j = 1; j <= a; ++j)
    {
    if(HT[j].parent==0 && x!=j)
    {
    y = j;
    break;
    }
    }
    for(i = j + 1; i <= a; ++i)
    {
    if(HT[i].weight < HT[y].weight&&HT[i].parent == 0 && x != i)
    {
    y = i; //选出次小的节点
    }
    }
    if(x > y)
    {
    p1 = y;
    p2 = x;
    }
    else
    {
    p1 = x;
    p2 = y;
    }
    }

    void CrtHuffmanTree(HuffmanTree &HT, HuffmanCode &HC, int n)
    { //w存放n个权值, 构造哈夫曼树HT, 并求出哈夫曼编码HC
    int i, start, c, p, m, w;
    int p1,p2;
    char *cd,z;
    m = 2 * n - 1;
    HT = new HTNode[m+1]; //0号单元未使用
    cout << "<<请输入" << n << "个字符和权值>>" << endl;
    for(i = 1; i <= n; i++)
    {
    cout << "请输入第" << i << "字符和权值:";
    cin >> z >> w;
    HT[i].ch = z;
    HT[i].weight = w;
    HT[i].parent = 0;
    HT[i].LChild = 0;
    HT[i].RChild = 0;
    }//叶子结点初始化,数组前n个
    for(i = n + 1; i <= m; i++)
    {
    HT[i].ch = '0';
    HT[i].weight = 0;
    HT[i].parent = 0;
    HT[i].LChild = 0;
    HT[i].RChild = 0;
    }//非叶子结点初始化,数组后n-1个
    for(i = n + 1; i <= m; i++) //创建非叶子结点, 建哈夫曼树
    { //在HT[1]~HT[i-1]的范围内选择两个parent为0且weight最小的结点
    //其序号分别赋值给p1、 p2返回
    select(HT, i-1, p1, p2);
    HT[p1].parent = i;
    HT[p2].parent = i;
    HT[i].LChild = p1;
    HT[i].RChild = p2;
    HT[i].weight = HT[p1].weight + HT[p2].weight;
    }//哈夫曼树建立完毕
    //从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码
    HC = new char*[n + 1]; //分配n个编码的头指针
    cd = new char[n]; //分配求当前编码的工作空间
    cd[n - 1] = ''; //从右向左逐位存放编码,首先存放编码结束符
    for(i = 1; i <= n; i++) //求n个叶子结点对应的哈夫曼编码
    { start = n - 1; //初始化编码起始指针
    for(c = i, p = HT[i].parent; p != 0; c = p, p = HT[p].parent) //从叶子到根结点求编码
    if(HT[p].LChild == c)
    cd[--start] = '0'; //左分支标0
    else
    cd[--start] = '1'; //右分支标1
    HC[i] = new char[n - start]; //为第i个编码分配空间
    strcpy(HC[i], &cd[start]);
    }
    free(cd);
    }

    int main()
    {
    BiTree T;
    BiThrTree Thrt, BT;
    int i, n;
    HuffmanTree HT;
    HuffmanCode HC;
    cout << "先序输入二叉树('#'表示空树): ";
    CreateBiTree(T);
    cout << "中序递归遍历输出二叉树为: ";
    InOrder(T);
    cout << endl;
    cout << "中序非递归(采用栈)遍历输出二叉树为: ";
    InOrderTraverse(T);
    cout << endl;
    getchar(); //读取回车符
    cout << "先序输入线索二叉树('#'表示空树): ";
    CreatBiThrTree(BT);
    InOrderThreading(Thrt, BT);
    cout << "中序递归遍历输出线索二叉树为: ";
    InOrderTraverse_Thr(Thrt);
    cout << endl;
    cout << "请输入赫夫曼树叶子节点个数:";
    cin >> n;
    CrtHuffmanTree(HT, HC, n);
    for(i = 1; i <= n; i++)
    {
    cout << HT[i].ch << "的赫夫曼编码为:" << HC[i] << endl;
    }
    return 0;
    }

  • 相关阅读:
    Java实现邮箱找回密码
    使用Java程序发送Email
    SSH返回Json格式的数据
    destoon二次开发基础指南
    揭秘:中国企业家十大顶级圈子
    Windows环境下完全手工配置Apache、MySQL和PHP
    windowns 查看端口占用
    MySQL主从同步机制及同步中的问题处理
    Apache Rewrite 规则详解
    PHP输出当前进程所有变量 / 常量 / 模块 / 函数 / 类
  • 原文地址:https://www.cnblogs.com/wwttsqt/p/7783201.html
Copyright © 2020-2023  润新知