数据结构—二叉树算法题总结

创建和递归遍历(扩展先序遍历序列创建二叉链表)

题目描述

从键盘接收扩展先序序列,以二叉链表作为存储结构,建立二叉树。输出这棵二叉树的先序、中序和后序遍历序列。
二叉树结点的data是字符类型数据, 其中#表示空格字符。

#include <stdio.h>
#include "stdlib.h"
//二叉链表结点结构
typedef struct Node {
    char data;
    struct Node *Lchild;
    struct Node *Rchild;
}BiTNode, *BiTree;
//用一级指针来创建
/*BiTNode *CreateBiTree(BiTree root) {
    char ch;
    ch = getchar();
    if (ch == '#') {
        root = NULL;
    } else {
        root = (BiTree)malloc(sizeof(BiTNode));
        root->data = ch;
        printf("1");
        //和与二级指针不同的是
        //一级指针要求有返回值,所以一定要接收
        root->Lchild = CreateBiTree(root->Lchild);
		//有返回值,所以一定要接收
        root->Rchild = CreateBiTree(root->Rchild);
    }
    return root;
}*/
void CreateBiTree(BiTree *root) {
    char ch;
    ch = getchar();
    if (ch == '#') {
        *root = NULL;
    } else {
        *root = (BiTree)malloc(sizeof(BiTNode));
        (*root)->data = ch;
        //以左子树域地址为参数,可使被调用函数中建立的结点指针置于该域中
        CreateBiTree(&((*root)->Lchild));
        //以右子树域地址为参数,可使被调用函数中建立的结点指针置于该域中
        CreateBiTree(&((*root)->Rchild));
    }
}
void PreOrder(BiTree root) {
    //printf("进来了");
    if (root) {
        printf("%c", root->data);
        PreOrder(root->Lchild);
        PreOrder(root->Rchild);
    }
}
void InOrder(BiTree root) {
    if (root) {
        InOrder(root->Lchild);
        printf("%c", root->data);
        InOrder(root->Rchild);
    }
}
void PostOrder(BiTree root) {
    if (root) {
        PostOrder(root->Lchild);
        PostOrder(root->Rchild);
        printf("%c", root->data);
    }
}
int main(int argc, const char * argv[]) {
    BiTree root;
    //root = (BiTree)malloc(sizeof(BiTNode));
    CreateBiTree(&root);
    PreOrder(root);
    printf("\n");
    InOrder(root);
    printf("\n");
    PostOrder(root);
    printf("\n");
    return 0;
}

验证结果:
请注意在验证时,一定要输入正确的扩展的先序遍历序列,比如这里的ABD#G###CE#H##F##。随便输一个是不行的,因为它没办法建立起来这棵二叉树
运行结果

创建和非递归遍历(扩展先序遍历序列创建二叉链表)

先序遍历二叉树的非递归实现:
从根开始,当前结点存在或栈非空,重复以下两步
1.访问根节点,根节点入栈,进入根节点左子树,直至为空
2.如果栈不为空,退栈顶结点,进入其右子树。

中序遍历二叉树非递归实现:
从根开始,当前结点存在或栈非空。重复以下两步
1.入栈,进入左子树,直至左子树为空
2.栈不为空,出栈顶元素,访问出栈结点,并进入其右子树

后序遍历二叉树非递归实现:
首先需要一个q指针,一直保持更新指向刚访问的结点
1.入栈,访问其左子树,重复直至左子树为空
2.如栈非空,取栈顶元素,判断他的右子树是否为空或者右子树是否等于q。
如果右子树不为空或等于q,说明没有右子树或者右子树还没有被访问,出栈,访问,更新q = p(q要一直保持更新为刚才访问的结点), p置空(避免再次访问)否则,进入p右子树.


#include <stdio.h>
#include "stdlib.h"
//二叉链表结点结构
typedef struct Node {
    char data;
    struct Node *Lchild;
    struct Node *Rchild;
}BiTNode, *BiTree;
typedef struct Stack {
    BiTNode *stack[100];
    int top;
}SeqStack;
void CreateBiTree(BiTree *root) {
    char ch;
    ch = getchar();
    if (ch == '#') {
        *root = NULL;
    } else {
        *root = (BiTree)malloc(sizeof(BiTNode));
        (*root)->data = ch;
        //以左子树域地址为参数,可使被调用函数中建立的结点指针置于该域中
        CreateBiTree(&((*root)->Lchild));
        //以右子树域地址为参数,可使被调用函数中建立的结点指针置于该域中
        CreateBiTree(&((*root)->Rchild));
    }
}
void init(SeqStack **s) {
    *s = (SeqStack *)malloc(sizeof(SeqStack));
    (*s)->top = -1;
}
int Empty(SeqStack *s) {
    if (s->top == -1) {
        return 0;
    } else {
        return 1;
    }
}
void Push(SeqStack *s ,BiTree pNew) {
    s->top++;
    s->stack[s->top] = pNew;
}
void pop(SeqStack *s, BiTree *p) {
    *p = s->stack[s->top];
    s->top--;
}
void preOrder(BiTree root) {
    SeqStack *s;
    BiTree p;
    init(&s);
    p = root;
    while (p != NULL || Empty(s)) {
        while (p != NULL) {
            printf("%c", p->data);
            Push(s, p);
            p = p->Lchild;
        }
        if (Empty(s)) {
            pop(s, &p);
            p = p->Rchild;
        }
    }
}
void InOrder1(BiTree root) {
    SeqStack *s;
    init(&s);
    BiTree p;
    p = root;
    while (p != NULL || Empty(s)) {
        while (p != NULL) {
            Push(s, p);
            p = p->Lchild;
        }
        if (Empty(s)) {
            pop(s, &p);
            printf("%c", p->data);
            p = p->Rchild;
        }
    }
}
void top(SeqStack *s, BiTree *p) {
    (*p) = s->stack[s->top];
}
void post(BiTree root) {
    SeqStack *s;
    BiTree p, q;
    init(&s);
    p = root;
    q = NULL;
    while (p != NULL || Empty(s)) {
        while (p != NULL) {
            Push(s, p);
            p = p->Lchild;
        }
        if (Empty(s)) {
            top(s, &p);
            if (p->Rchild == NULL || p->Rchild == q) {
                pop(s, &p);
                printf("%c", p->data);
                //q时刻记录刚被访问的结点
                q = p;
                //置空,避免再次被访问
                p = NULL;
            } else {
                p = p->Rchild;
            }
        }
    }
}
int main(int argc, const char * argv[]) {
    BiTree root;
    //root = (BiTree)malloc(sizeof(BiTNode));
    CreateBiTree(&root);
    preOrder(root);
    printf("\n");
    InOrder1(root);
    printf("\n");
    post(root);
    printf("\n");
    return 0;
}

运行结果:在这里插入图片描述

层次遍历(利用队列)

算法思想:
首先根结点入队,当队列非空时,重复如下两步操作
1.队头结点出队,并访问出队结点
2.出队结点的非空左,右孩子依次入队。



#include <stdio.h>
#include "stdlib.h"
//二叉链表结点结构
typedef struct Node {
    char data;
    struct Node *Lchild;
    struct Node *Rchild;
}BiTNode, *BiTree;
typedef struct Queue {
    BiTNode *Queue[100];
    int front;
    int rear;
}SeqQueue;
void CreateBiTree(BiTree *root) {
    char ch;
    ch = getchar();
    if (ch == '#') {
        *root = NULL;
    } else {
        *root = (BiTree)malloc(sizeof(BiTNode));
        (*root)->data = ch;
        //以左子树域地址为参数,可使被调用函数中建立的结点指针置于该域中
        CreateBiTree(&((*root)->Lchild));
        //以右子树域地址为参数,可使被调用函数中建立的结点指针置于该域中
        CreateBiTree(&((*root)->Rchild));
    }
}
void init(SeqQueue **s) {
    (*s) = (SeqQueue *)malloc(sizeof(SeqQueue));
    (*s)->front = (*s)->rear = -1;
}
void EnterQueue(SeqQueue *s, BiTree root) {
    s->rear++;
    s->Queue[s->rear] = root;
}
int Empty(SeqQueue *s) {
    if (s->front == s->rear) {
        return 0;
    } else {
        return 1;
    }
}
void DeleteQueue(SeqQueue *s, BiTree *p) {
    s->front++;
    *(p) = s->Queue[s->front];
}
void LevelOrder(BiTree root) {
    SeqQueue *s;
    BiTree p;
    init(&s);
    EnterQueue(s, root);
    while (Empty(s)) {
        DeleteQueue(s, &p);
        printf("%c", p->data);
        if (p->Lchild != NULL) {
            EnterQueue(s, p->Lchild);
        }
        if (p->Rchild != NULL) {
            EnterQueue(s, p->Rchild);
        }
    }
}
int main(int argc, const char * argv[]) {
    BiTree root;
    //root = (BiTree)malloc(sizeof(BiTNode));
    CreateBiTree(&root);
    LevelOrder(root);
    printf("\n");
    return 0;
}

运行结果:
在这里插入图片描述

结点个数

#include <stdio.h>
#include "stdlib.h"
//二叉链表结点结构
typedef struct Node {
    char data;
    struct Node *Lchild;
    struct Node *Rchild;
}BiTNode, *BiTree;
void CreateBiTree(BiTree *root) {
    char ch;
    ch = getchar();
    if (ch == '#') {
        *root = NULL;
    } else {
        *root = (BiTree)malloc(sizeof(BiTNode));
        (*root)->data = ch;
        //以左子树域地址为参数,可使被调用函数中建立的结点指针置于该域中
        CreateBiTree(&((*root)->Lchild));
        //以右子树域地址为参数,可使被调用函数中建立的结点指针置于该域中
        CreateBiTree(&((*root)->Rchild));
    }
}
//定义全局变量
int a = 0, b = 0, c = 0;
void InOrder1(BiTree root) {
    if (root) {
        InOrder1(root->Lchild);
        //度为2的结点
        if ((root->Lchild != NULL && root->Rchild != NULL)) {
            c++;
        }
        //度为1的结点
        if ((root->Lchild == NULL && root->Rchild != NULL) || (root->Lchild != NULL && root->Rchild == NULL)) {
            b++;
        }
        //叶子结点
        if (root->Lchild == NULL && root->Rchild == NULL) {
            a++;
        }
        InOrder1(root->Rchild);
    }
}
//中序输出叶子结点
void InOrder2(BiTree root) {
    if (root) {
        InOrder2(root->Lchild);
        //叶子结点
        if (root->Lchild == NULL && root->Rchild == NULL) {
            printf("%c", root->data);
            a++;
        }
        InOrder2(root->Rchild);
    }
}
int main(int argc, const char * argv[]) {
    BiTree root;
    //root = (BiTree)malloc(sizeof(BiTNode));
    CreateBiTree(&root);
    InOrder1(root);
    printf("%d %d %d\n", a, b, c);
    InOrder2(root);
    return 0;
}

运行结果:
在这里插入图片描述

二叉树的最大深度

方法一:递归

int Max(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

int maxDepth(struct TreeNode* root){
    if (root == NULL) {
        return 0;
    }
    int left = maxDepth(root->left);
    int right = maxDepth(root->right);
    return 1 + Max(left, right);
}

方法二:队列(层序遍历)

#define MAX 1000
int maxDepth(struct TreeNode* root) {
    struct TreeNode *list[MAX];
    int count = 0;
    int front, rear;
    front = rear = 0;
    if (root != NULL) {
        list[rear % MAX] = root;
        rear++;
    }
    while (front < rear) {
        count++;
        int j = rear - front;
        for (int i = 0; i < j; i++) {
            struct TreeNode *t;
            t = list[front % MAX];
            front++;
            if (t->left) {
                list[rear % MAX] = t->left;
                rear++;
            }
            if (t->right) {
                list[rear % MAX] = t->right;
                rear++;
            }
        }
    }
    return count;
}

二叉树的最小深度

题目链接
//队列(层次遍历)
总结:相比求最大深度,求最小深度主要是要及时退出循环

#define MAX 1000
int minDepth(struct TreeNode* root){
    struct TreeNode *list[MAX];
    int front, rear;
    front = rear = 0;
    int count = 0;
    if (root) {
        list[rear % MAX] = root;
        rear++;
    }
    while (front < rear) {
        int flag = 0;
        count++;
        int j = rear - front;
        for (int i = 0; i < j; i++) {
            struct TreeNode *t;
            t = list[front % MAX];
            front++;
            if (t->left == NULL && t->right == NULL) {
                flag = 1;
                break;
            } else {
                if (t->left != NULL) {
                    list[rear % MAX] = t->left;
                    rear++;
                } 
                if (t->right != NULL) {
                    list[rear % MAX] = t->right;
                    rear++;
                }
            }
        }
        if (flag == 1) {
            break;
        }
    }
    return count;
}

对称二叉树

题目链接
方法一:递归。
首先判断一个二叉树是否对称是看它的左子树和右子树是否对称,然后左子树和右子树又有相应的左右子树去判断它是否对称。。。依次推导。显然可以用递归解决。

bool isMirror(struct TreeNode *t1, struct TreeNode *t2) {
    //递推终止条件
    //左右子树为空,表示已经遍历结束。
    if (t1 == NULL && t2 == NULL) {
        return true;
    } else if (t1 == NULL || t2 == NULL) {
         //剪支。
        //其中一个为空,显然已经不对称了
        return false;
    } else {
    //否则进一步推导它的左右子树。
    //&&有短路判断。就是说如果t1->val == t2->val为false。则直接return false;
        return ((t1->val == t2->val) && isMirror(t1->left, t2->right) && isMirror(t1->right, t2->left)); 
    }
}
bool isSymmetric(struct TreeNode* root) {
	//传入根结点
    return isMirror(root, root);
}

方法二:迭代
关于递推,迭代,递归的区别详见这篇博客
//使用队列逐层遍历比较

#define MAX 1000
bool isSymmetric(struct TreeNode* root) {
    struct TreeNode *list[MAX];
    int front, rear;
    front = rear = 0;
    list[rear % MAX] = root;
    rear++;
    list[rear % MAX] = root;
    rear++;
    while (front < rear) {
        struct TreeNode *t1;
        t1 = list[front % MAX];
        front++;
        struct TreeNode *t2;
        t2 = list[front % MAX];
        front++;
        if (t1 == NULL && t2 == NULL) {
            //如果两个节点均为空,那么它们
            //肯定没有左子树和右子树了
            //直接进入下一轮循环
            continue;
        } else if (t1 == NULL || t2 == NULL) {
            //其中一个为空,那么肯定不对称了
            //直接返回false;
            return false;
        } else if (t1->val == t2->val) {
            //先判断它们的根节点的val是否相等
            //如果相等,把它们的左子树和右子树入队列
            //注意是交叉入队,因为就是想要比较它们的对称值是否相等
            //画个图就知道了
            list[(rear++) % MAX] = t1->left;
            list[(rear++) % MAX] = t2->right;
            list[(rear++) % MAX] = t1->right;
            list[(rear++) % MAX] = t2->left;
        } else {
            //这里t1->val != t2->val
            //直接false
            return false;
        }
    }
    //如果程序可以走到这里,说明二叉树已经遍历结束
    //并且均符合上面分类讨论的情况
    //返回true
    return true;
}


平衡二叉树

题目链接
这个题是基于求解二叉树的最大深度解决的
这里就是判断他的左右子树的高度差绝对值小于等于1
所以我们可以求得每个左右子树的高度差,然后进行判断。

int Max(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

int maxDepth(struct TreeNode* root){
    if (root == NULL) {
        return 0;
    }
    int left = maxDepth(root->left);
    int right = maxDepth(root->right);
    return 1 + Max(left, right);
}
bool isBalanced(struct TreeNode* root){
    if (root == NULL) {
        return true;
    } else {
        int t1 = maxDepth(root->left);
        int t2 = maxDepth(root->right);
        int t = t1 - t2;
        if (abs(t) <= 1) {
            return isBalanced(root->left) && isBalanced(root->right);
        } else {
            return false;
        }
    }
}

翻转二叉树

题目链接
感觉总结二叉树题目是总结递归了
思想:翻转一个二叉树,就是把它的左子树和右子树交换。
然后左子树对应的二叉树又要交换它的左子树和右子树
右子树对应的二叉树又要交换它的左子树和右子树…
以下是我对递归的个人理解:
我感觉递归非常考验一个人的全局意识,
在思考是否要用递归时
你一定不要局限于当前,而应该想它后面的是不是和当前的处理想法是一样的
在实现递归的时候
你又必须要回到当前,想一想当前的代码怎么实现就行。
不要深究后面怎么办
要不然你会觉得这个代码好像又会特别多而很难实现
实际上递归正是帮我们省去了哪些多余代码
交给递归工作栈就行啦

struct TreeNode* invertTree(struct TreeNode* root){
    //递归终止条件
    //如果遍历借宿,返回NULL。
    //开始回溯过程
    if (root == NULL) {
        return NULL;
    }
    //当前我需要二叉树的左子树
    //但是我又希望这个左子树的二叉树它的左子树和二叉树是交换了的
    //所以递归调用
    struct TreeNode *left = invertTree(root->left);
    //同上,当前我需要二叉树的右子树
    //但是我也希望这个右子树的二叉树它的左子树和右子树是交换了的
    //所以递归调用
    struct TreeNode *right = invertTree(root->right);
    //下面两行代码,就是把一个二叉树的左子树和右子树对调
    root->left = right;
    root->right = left;
    //你不用但是你的root是否经过这么
    //多层递归是否还是起点位置
    //因为最后返回的root是经过回溯得到的
    //虽然我们前面不停的root->left
    //root->right
    //但是那是深度搜索的过程
    //最后回溯过程root又回到了起点位置
    return root;
}

依据先序、中序遍历序列创建二叉树

先感谢这位博主的思路
题目思路来源
虽然总结了好几个二叉树的题目,但是每到一个递归新题的时候总是无处下手。
吐槽自己对递归的理解“一看就懂,一写就懵”
拿到这个题自己也想着递归八九不离十了。
拿这个题来说,按照先序根在第一个的特点,递推:不断得把中序拆分。然后逐层返回。最后得出结果。
题目中用到了C库函数中的strchr.用法可以参考我的这篇博客C/C++常用库函数
关于这个题的分析在代码中已详细给出

#include <stdio.h>
#include "stdlib.h"
#include <string.h>
//二叉链表结点结构
typedef struct Node {
    char data;
    struct Node *Lchild;
    struct Node *Rchild;
}BiTNode, *BiTree;
BiTree CreateBiTree(char *pre, char *in, long n) {
//递归终止条件
//只要遇到递归必不可少。
    if (n <= 0) {
        return NULL;
    }
    BiTree root;
    root = (BiTree)malloc(sizeof(BiTNode));
    //先序序列(根左右)中的第一个元素必为树根结点
    root->data = pre[0];
    //计算根结点在中序序列中出现的位置
    //“拆分”中序序列
    char *p = strchr(in, pre[0]);
    //“拆分”中序序列后得到左子树的长度
    //为了下面的递归(自己调用自己)做准备.
    long len = p - in;
    //printf("%ld\n", len);
    //递归求左子树
    //参数一:先序序列(拆分后得到的左子树),
    //参数二:中序序列(拆分后得到的左子树)
    //参数三:长度
    root->Lchild = CreateBiTree(pre + 1, in, len);
    //递归求右子树
    //同理
    root->Rchild = CreateBiTree(pre + len + 1, p + 1, n - len - 1);
    return root;
}
void postOrder(BiTree root) {
    if (root) {
        postOrder(root->Lchild);
        postOrder(root->Rchild);
        printf("%c", root->data);
    }
}
int main(int argc, const char * argv[]) {
    char preStr[100], inStr[100];
    BiTree root;
    scanf("%s", preStr);
    scanf("%s", inStr);
    root = CreateBiTree(preStr, inStr, strlen(preStr));
    postOrder(root);
    printf("\n");
    return 0;
}

依据中序、后序遍历序列创建二叉树

由上题可以很容易得到此题。


#include <stdio.h>
#include "stdlib.h"
#include <string.h>
//二叉链表结点结构
typedef struct Node {
    char data;
    struct Node *Lchild;
    struct Node *Rchild;
}BiTNode, *BiTree;
BiTree CreateBiTree(char *in, char *post, long n) {
    if (n <= 0) {
        return NULL;
    }
    BiTree root;
    root = (BiTree)malloc(sizeof(BiTNode));
    root->data = post[n - 1];
    char *p;
    p = strchr(in, post[n - 1]);
    long len = p - in;
    //printf("%ld\n", len);
    root->Lchild = CreateBiTree(in, post, len);
    root->Rchild = CreateBiTree(in + len + 1, post + len, n - len - 1);
    return root;
}
void preOrder(BiTree root) {
    if (root) {
        printf("%c", root->data);
        preOrder(root->Lchild);
        preOrder(root->Rchild);
    }
}
int main(int argc, const char * argv[]) {
    char inStr[100], postStr[100];
    BiTree root;
    scanf("%s", inStr);
    scanf("%s", postStr);
    root = CreateBiTree(inStr, postStr, strlen(inStr));
    preOrder(root);
    printf("\n");
    return 0;
}

结点及其所在层次

题目描述

从键盘接收扩展先序序列,以二叉链表作为存储结构,建立二叉树。按先序遍历次序输出各结点的内容及相应的层次数,要求以二元组的形式输出,其所对应的输出结果为:(data,level)
data是二叉树结点数据域值,level是该结点所在的层次。
设根节点在第一层。
输出的元素间不用间隔,()都是英文字符

样例输入 Copy

AB#DG###CE##FH###
样例输出 Copy

(A,1)(B,2)(D,3)(G,4)(C,2)(E,3)(F,3)(H,4)

void PreOrder(BiTree root, int level) {
    if (root) {
        printf("(%c,%d)", root->data, level);
        PreOrder(root->Lchild, level + 1);
        PreOrder(root->Rchild, level + 1);
    }
}

int main(int argc, const char * argv[]) {
    BiTree root;
    //root = (BiTree)malloc(sizeof(BiTNode));
    CreateBiTree(&root);
    int level = 1;
    PreOrder(root, level);
    printf("\n");
    return 0;
}

某层叶子结点个数

题目描述

从键盘接收扩展先序序列,以二叉链表作为存储结构,建立二叉树。并计算指定的第K层的叶子结点个数。设根结点在第一层。
第一行:扩展先序序列
第二行:k的值
样例输入 Copy

ABC##DE#G##F###
3

样例输出 Copy

1

int k, num = 0;
void PreOrder(BiTree root, int level) {
    //printf("进来了");
    if (root) {
        if (level == k && root->Lchild == NULL && root->Rchild == NULL) {
            num++;
        }
        //printf("(%c,%d)", root->data, level);
        PreOrder(root->Lchild, level + 1);
        PreOrder(root->Rchild, level + 1);
    }
}
int main(int argc, const char * argv[]) {
    BiTree root;
    //root = (BiTree)malloc(sizeof(BiTNode));
    CreateBiTree(&root);
    int level = 1;
    scanf("%d", &k);
    PreOrder(root, level);
    printf("%d", num);
    printf("\n");
    return 0;
}


哈夫曼

题目描述

假设某通信报文的字符集由A,B,C,D,E,F这6个字符组成,它们在报文中出现的频度(频度均为整数值)。
(1)构造一棵哈弗曼树,依次给出各字符编码结果。
(2)给字符串进行编码。
(3)给编码串进行译码。

规定:
构建哈弗曼树时:左子树根结点权值小于等于右子树根结点权值。
生成编码时:左分支标0,右分支标1。

输入

第一行:依次输入6个整数,依次代表A,B,C,D,E,F的频度,用空格隔开。
第二行:待编码的字符串
第三行:待译码的编码串
输出

前6行依次输出各个字符及其对应编码,格式为【字符:编码】(冒号均为英文符号)
第7行:编码串
第8行:译码串
样例输入 Copy

3 4 10 8 6 5
BEE
0010000100111101
样例输出 Copy

A:000
B:001
C:10
D:01
E:111
F:110
001111111
BADBED


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 30
#define M 2 * N - 1
typedef struct {
    int index;
    int weight;
    int Parent, Lchild, Rchild;
}HTNode, HuffmanTree[M + 1];
typedef struct {
    HTNode data[1000];
    int top;
}SeqStack;
typedef char * Huffmancode[100];
void init(SeqStack **s) {
    (*s) = (SeqStack *)malloc(sizeof(SeqStack));
    (*s)->top = -1;
}
void push(SeqStack *s, HTNode x) {
    s->top++;
    s->data[s->top] = x;
}
void pop(SeqStack *s, int *p) {
    (*p) = s->data[s->top].index;
    s->top--;
}
void pop2(SeqStack *s) {
    //(*p) = s->data[s->top];
    s->top--;
}
int top1(SeqStack *s) {
    return s->data[s->top].weight;
}
int top2(SeqStack *s) {
    return s->data[s->top - 1].weight;
}
void twoMinSelect(HTNode ht[], int n, int *s1, int *s2) {
    SeqStack *s;
    init(&s);
    HTNode q, p;
    q.weight = 999;
    p.weight = 999;
    push(s, q);
    push(s, p);
    for (int i = 1; i <= n; i++) {
        if (ht[i].Parent == 0) {
            if (ht[i].weight <= top1(s) && top1(s) >= top2(s)) {
                pop2(s);
                push(s, ht[i]);
            } else if (ht[i].weight <= top1(s) && top1(s) <= top2(s)){
                push(s, ht[i]);
            } else if (ht[i].weight < top1(s) || ht[i].weight < top2(s)) {
                push(s, ht[i]);
            }
        }
    }
    if (top1(s) > top2(s)) {
        pop(s, s2);
        pop(s, s1);
    } else {
        pop(s, s1);
        pop(s, s2);
    }
}

//建立哈夫曼树
void CrtHuffmanTree(HuffmanTree ht, int w[], int n) {
    int m = 2 * n - 1;
    for (int i = 1; i <= n; i++) {
        ht[i].index = i;
        ht[i].weight = w[i];
        ht[i].Parent = 0;
        ht[i].Lchild = 0;
        ht[i].Rchild = 0;
    }
    for (int i = n + 1; i <= m; i++) {
        ht[i].index = i;
        ht[i].weight = 0;
        ht[i].Parent = 0;
        ht[i].Lchild = 0;
        ht[i].Rchild = 0;
    }
    for (int i = n + 1; i <= m; i++) {
        int s1, s2;
        twoMinSelect(ht, i - 1, &s1, &s2);
        ht[i].weight = ht[s1].weight + ht[s2].weight;
        ht[i].Lchild = s1;
        ht[i].Rchild = s2;
        ht[s1].Parent = i;
        ht[s2].Parent = i;
    }
}
//哈夫曼编码
void CrtHuffmanCode1(HuffmanTree ht, Huffmancode hc, int n) {
    char *cd;
    int start;
    cd = (char *)malloc(n * sizeof(char));
    cd[n - 1] = '\0';
    for (int i = 1; i <= n; i++) {
        start = n - 1;
        int c = i;
        int p = ht[i].Parent;
        while (p != 0) {
            --start;
            if (ht[p].Lchild == c) {
                cd[start] = '0';
            } else {
                cd[start] = '1';
            }
            c = p;
            p = ht[p].Parent;
        }
        hc[i] = (char *)malloc((n - start) * sizeof(char));
        strcpy(hc[i], &cd[start]);
    }
    free(cd);
}
//译码
void Output(Huffmancode hc) {
    char ch = 65;
    for (int i = 1; i <= 6; i++) {
        printf("%c:%s\n", ch, hc[i]);
//        if (i != 6) {
//            printf("\n");
//        }
        ch++;
    }
}
void secondQuestion(char will[], Huffmancode hc) {
    int lenth = 0;
    int j = 0;
    while (will[j]) {
        lenth++;
        j++;
    }
    for (int i = 0; i < lenth; i++) {
        for (int j = 65; j <= 71; j++) {
            if (will[i] == j) {
                printf("%s", hc[j - 64]);
            }
        }
    }
}
//16 5 10 20 3 1
//ABC
//11000110010
void thirdQuestion(HuffmanTree ht, char *third) {
//    for (int i = 1; i <= 11; i++) {
//        printf("%d:%d %d %d %d\n", i, ht[i].weight, ht[i].Parent, ht[i].Lchild, ht[i].Rchild);
//    }
    int length = 0;
    int j = 0;
    while (third[j]) {
        length++;
        j++;
    }
    //printf("length:%d\n", length);
    int t = 11;
    int k = 0;
    for (int i = 0; i < length; i = k) {
        while (1) {
            if (ht[t].Lchild == 0 && ht[t].Rchild == 0) {
            //下标t与字母之间是有关系的,t-1+'A'即对应字符的ASCII码
                printf("%c", t - 1 + 'A');
                t = 11;
                break;
            } else {
                if (third[k] == '1') {
                    t = ht[t].Rchild;
                    k++;
                } else {
                    t = ht[t].Lchild;
                    k++;
                }
            }
        }
    }
}
int main(int argc, const char * argv[]) {
    int w[100] = {0};
    for (int i = 1; i <= 6; i++) {
        scanf("%d", &w[i]);
    }
    char will[50];
    char third[50];
    scanf("%s", will);
    scanf("%s", third);
    HuffmanTree ht;
    Huffmancode hc;
    CrtHuffmanTree(ht, w, 6);
    CrtHuffmanCode1(ht, hc, 6);
    Output(hc);
    secondQuestion(will, hc);
    printf("\n");
    thirdQuestion(ht, third);
    printf("\n");
    return 0;
}

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值