[数据结构] 树、森林及二叉树的应用

这篇具有很好参考价值的文章主要介绍了[数据结构] 树、森林及二叉树的应用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

树、森林

树的存储结构

双亲表示法

[数据结构] 树、森林及二叉树的应用

双亲表示法的存储结构

#define MAX_TREE_SIZE 100
typedef struct {
    int data;
    int parent;
}PTNode;
typedef struct {
    PTNode nodes[MAX_TREE_SIZE];
    int n;
}PTree;

【注】 区别树的顺序存储结构与二叉树的顺序存储结构。在树的顺序存储结构中,数组下标代表节点的编号,下标中所存的内容指示了节点之间的关系。而在二叉树的顺序存储结构中, 数组下标既表达了节点的编号,又指示了二叉树中节点之间的关系。当然,二叉树属于树,因此二叉树也可以用树的存储结构来存储,但树却不能都用都用二叉树的存储结构来存储。

孩子表示法

孩子表示法是将每个节点的孩子节点视为一个线性表,且以单链表作为存储结构,则\(n\)个节点就有\(n\)个孩子链表(叶节点的孩子链表为空表)。而\(n\)个头指针又组成一个线性表。

与双亲表示法相反,孩子表示法寻找孩子的操作非常方便,而寻找双亲的操作则需要遍历\(n\)个节点中孩子链表指针域所指向的\(n\)个孩子链表。

孩子兄弟表示法

又称二叉树表示法,即以二叉链表作为树的存储结构。孩子兄弟表示法使每个节点包括三个部分的内容:节点值、指向节点第一个孩子节点的指针、指向节点下一个兄弟节点的指针

结构体如下:

typedef struct CSNode {
    inr data;
    struct CSNode *firstchild, *nextsibling;
}CSNode, *CSTree;

树、森林、二叉树的转换

树转换为二叉树

(1)在兄弟节点之间画一条线;

(2)对每个节点,只保留它与第一个孩子的连线,而与其他孩子的连线全部删除;

(3)以树根为轴心,顺时针旋转45°。

[数据结构] 树、森林及二叉树的应用
[数据结构] 树、森林及二叉树的应用

以下代码引用我大佬同学:作者:Amαdeus,出处:https://www.cnblogs.com/MAKISE004/p/17089756.html

//树 转化为 二叉树
BinaryTree CSTree_Transform_to_BinaryTree(CSTree ct){
 if(!ct) return NULL;

 BinaryTree T = (BinaryTree)malloc(sizeof(BiNode));
 T->data = ct->data;
 //相当于将left变为firstchild, 将right变为nextsibling 本质的形态没有改变
 T->leftchild = CSTree_Transform_to_BinaryTree(ct->firstchild);
 T->rightchild = CSTree_Transform_to_BinaryTree(ct->nextsibling);

 return T;
}

森林转换为二叉树

(1)将森林中的每棵树转换成相应的二叉树;

(2)每棵树的根视为兄弟关系,加上连线;

(3)以第一棵树的根为轴心顺时针旋转45°。

以下代码引用我大佬同学:作者:Amαdeus,出处:https://www.cnblogs.com/MAKISE004/p/17089756.html

//森林 转化为 二叉树
BinaryTree Forest_Transform_to_BinaryTree(CSTree ct[], int low, int high){
 if(low > high)  return NULL;

 //每个树变成二叉树
 BinaryTree T = CSTree_Transform_to_BinaryTree(ct[low]);  
 //通过rightchild连接每一个二叉树的根节点
 T->rightchild = Forest_Transform_to_BinaryTree(ct, low + 1, high);

 return T;
}

树、森林的遍历

树的遍历

  • 先根遍历。若树非空,则按如下规则遍历:

    • 先访问根节点
    • 再依次遍历根节点的每棵子树,遍历子树时仍遵循先根后子树的规则
  • 后根遍历。若树非空,则按如下规则遍历:

    • 先依次遍历根节点的每棵子树,遍历子树时仍遵循先子树后根的规则
    • 再访问根节点

树的先根遍历与对应二叉树的先序序列相同,树的后根遍历与对应二叉树的中序序列相同。

森林的遍历

  • 先序遍历森林。若森林非空,则按如下规则遍历:

    • 访问森林中第一棵树的根节点
    • 先序遍历第一棵树中根节点的子树森林
    • 先序遍历除去第一棵树之后剩余的树构成的森林
  • 中序遍历森林。若森林非空,则按如下规则遍历:

    • 中序遍历森林中第一棵树的根节点的子树森林
    • 访问第一棵树的根节点
    • 中序遍历粗去第一棵树之后剩余的树构成的森林

树与二叉树的应用

哈夫曼树和哈夫曼编码

几个概念

  • 路径:树中一个节点到另一个节点之间的分支构成

  • 路径长度:路径上的分支数目

  • 权:树中节点被赋予的一个表示某种意义的数值

  • 带权路径长度:从树的根到一个节点的路径长度与该节点上权值的乘积

    \[WPL = \sum_{i=1}^{n}w_il_i \]

    其中,\(w_i\)是第\(i\)个叶节点所带的权值,\(l_i\)是该叶节点到根节点的路径长度

在含有\(n\)个带权叶节的二叉树中,其中带权路径长度最小的二叉树称为哈夫曼树

下面看个算法:递归求WPL

int getWPL(struct TreeNode *root, int depth) {
    if (root == NULL) { // 如果节点为空,返回0
        return 0;
    }
    if (root->left == NULL && root->right == NULL) { // 如果节点是叶子节点,返回带权路径长度
        return depth * root->val;
    }
    // 如果节点不是叶子节点,递归计算左子树和右子树的WPL,并相加返回
    return getWPL(root->left, depth + 1) + getWPL(root->right, depth + 1);
}

接下来咱们举个栗子,来看一下哈夫曼编码

[数据结构] 树、森林及二叉树的应用

看题:来自北邮考研机试

3531. 哈夫曼树 - AcWing题库

题解:

// 优先队列求哈夫曼树最短带权路径长度
#include<bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    priority_queue<int, vector<int>, greater<int> > q;
    for(int i = 0; i < n; i ++) {
        int x;
        cin >> x;
        q.push(x);
    }
    int ans = 0;
    while(q.size() > 1) {
        int t1 = q.top();
        q.pop();
        int t2 = q.top();
        q.pop();
        q.push(t1 + t2);
        ans += t1 + t2;
    }
    cout << ans << endl;
    return 0;
}

并查集

这里不想做太多解释,我们看一下y总的模版(写的时候已经快要零点了,第二天还要早起)(PS:第二天果然多睡了半个小时)

(1)朴素并查集:

    int p[N]; //存储每个点的祖宗节点

    // 返回x的祖宗节点
    int find(int x) {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ ) p[i] = i;

    // 合并a和b所在的两个集合:
    p[find(a)] = find(b);


(2)维护size的并查集:

    int p[N], size[N];
    //p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量

    // 返回x的祖宗节点
    int find(int x) {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ ) {
        p[i] = i;
        size[i] = 1;
    }

    // 合并a和b所在的两个集合:
    size[find(b)] += size[find(a)];
    p[find(a)] = find(b);


(3)维护到祖宗节点距离的并查集:

    int p[N], d[N];
    //p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离

    // 返回x的祖宗节点
    int find(int x) {
        if (p[x] != x) {
            int u = find(p[x]);
            d[x] += d[p[x]];
            p[x] = u;
        }
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ ) {
        p[i] = i;
        d[i] = 0;
    }

    // 合并a和b所在的两个集合:
    p[find(a)] = find(b);
    d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量

作者:yxc
链接:https://www.acwing.com/blog/content/404/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

836. 合并集合 - AcWing题库文章来源地址https://www.toymoban.com/news/detail-837708.html

#include<bits/stdc++.h>
using namespace std;

const int N = 100010;
int p[N];//定义多个集合

int find(int x) {
    if(p[x] != x) p[x] = find(p[x]);
    /*
    经上述可以发现,每个集合中只有祖宗节点的p[x]值等于他自己,即:
    p[x]=x;
    */
    return p[x];
    //找到了便返回祖宗节点的值
}C

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++) p[i]=i;
    while(m --) {
        char op[2];
        int a, b;
        scanf("%s%d%d", op, &a, &b);
        if(*op == 'M') p[find(a)] = find(b);//集合合并操作
        else
        	if(find(a)==find(b))
        	//如果祖宗节点一样,就输出yes
        		printf("Yes\n");
       	 	else
        		printf("No\n");
    }
    return 0;
}

到了这里,关于[数据结构] 树、森林及二叉树的应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 探索树形数据结构,通识树、森林与二叉树的基础知识(专有名词),进一步利用顺序表和链表表示、遍历和线索树形结构

      结点之间有分支,具有层次关系 树的定义 : 树 (tree)是n(n≥0)个有限集。 若n = 0,则称为空树; 若n 0,则它满足如下两个条件: 有且仅有一个特定的称为根(Root)的结点; 其余结点可分为m(m≥0)个互不相交的有限集T1,T2,T3,.....,Tm,其中每一个集合本身又是一棵树,并称为根的

    2024年02月01日
    浏览(50)
  • 【数据结构】树与二叉树(一):树(森林)的基本概念:父亲、儿子、兄弟、后裔、祖先、度、叶子结点、分支结点、结点的层数、路径、路径长度、结点的深度、树的深度

    树 一棵树是结点的有限集合T: 若T非空,则: 有一个特别标出的结点,称作该树的 根 ,记为root(T); 其余结点分成若干个不相交的非空集合T1, T2, …, Tm (m0),其中T1, T2, …, Tm又都是树,称作root(T)的 子树 。 T 空时为空树,记作root(T)=NULL。 有序树、无序树   如果子树T1, T

    2024年02月05日
    浏览(55)
  • 【数据结构】树、二叉树的概念和二叉树的顺序结构及实现

    之前我们学习了顺序表、链表以及栈和队列这些数据结构,但这些数据结构都是线性的(一对一)。接下来要学习 非线性的数据结构——树(二叉树) ,相比前面的,树的结构更加复杂,话不多说,直接进入正题吧。 树是一种 非线性的数据结构 ,它是 一对多(也有可能是

    2024年02月07日
    浏览(40)
  • 【数据结构】二叉树的链式结构

    学习链式二叉树要知道三种遍历方式,便于对二叉树的节点以及左子树和右子树进行操作。 前序遍历:根、左子树、右子树 中序遍历:左子树、根、右子树 后序遍历:左子树、右子树、根 以下图为例: 得到的结果: 前序遍历:1 2 3 4 5 6 中序遍历:3 2 1 5 4 6 后序遍历:3 2

    2024年02月08日
    浏览(52)
  • 数据结构:二叉树的链式结构

    朋友们、伙计们,我们又见面了,本期来给大家解读一下链式二叉树的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! 数据结构与算法专栏 :数据结构与算法 个  人  主  页 :stackY、 C 语 言 专 栏 :C语言:从入门到精通 目录 前言

    2024年02月07日
    浏览(56)
  • 数据结构——二叉树的链式结构

      个人主页 : 日刷百题 系列专栏 : 〖C语言小游戏〗〖Linux〗〖数据结构〗  〖C语言〗 🌎 欢迎各位 → 点赞 👍+ 收藏 ⭐️+ 留言 📝  ​ 这里我们使用先序遍历的思想来创建二叉树,这里的内容对于刚接触二叉树的朋友可能有些难理解,不妨先看完下面的二叉树各种遍历

    2024年02月05日
    浏览(43)
  • 【数据结构】二叉树的介绍和二叉树堆

    💓作者简介: 加油,旭杏,目前大二,正在学习 C++ , 数据结构 等👀 💓作者主页:加油,旭杏的主页👀 ⏩本文收录在:再识C进阶的专栏👀 🚚代码仓库:旭日东升 1👀 🌹欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖        树这一概念,在我们刚开始听说的时候会觉得

    2024年01月20日
    浏览(50)
  • 【数据结构】树,二叉树,满二叉树,完全二叉树的定义和二叉树的基本操作

    🎊专栏【数据结构】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【勋章】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 目录 ⭐树 🏳️‍🌈定义  🏳️‍🌈注意 🍔树的基本术语 ⭐二叉树 🏳️‍🌈定义 🎆二叉树和树的区别 🏳️‍🌈二叉树

    2024年02月05日
    浏览(66)
  • 速学数据结构 | 树 森林 二叉树 的概念详讲篇

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,关于线性表我们已经在前面更新完了!    ⛳️ 今天就来看一下复杂一些的数据结构 “树” 他的应用主要在哪些方面

    2024年02月04日
    浏览(39)
  • 数据结构:二叉树的顺序结构--堆

    朋友们、伙计们,我们又见面了,本期来给大家解读一下二叉树--堆的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 数据结构专栏:数据结构 个  人  主  页 :stackY、 目录 前言: 1.堆的概念及

    2024年02月06日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包