首页 > 其他分享 >[数据结构] 树、森林的遍历

[数据结构] 树、森林的遍历

时间:2023-02-04 23:46:08浏览次数:46  
标签:遍历 Show BinaryTree CSTree 二叉树 数据结构 Order 森林

树的遍历

树的遍历方式有先根遍历和后根遍历。在下面树的遍历中,采用的都是孩子兄弟表示法构建的树。

树的先根遍历

树的先根遍历步骤

先根遍历就是先访问树的根节点,然后再依次访问树的孩子们。在这里我们需要用递归函数来实现树的先根遍历,先打印当前节点的数据,然后再递归访问其第一个孩子,再递归访问当前节点的兄弟。注意树的根节点是没有兄弟的,在第一层递归中实际只会递归访问其 firstchild,这一点在创建树的过程中也需注意。
递归函数停止递归的边界条件很简单,遇到空指针 NULL 停止即可。

假设函数名为 CStree_preorder ,则大致步骤如下:
(1)print(root->data)
(2)CStree_preorder(root->firstchild)
(3)CStree_preorder(root->nextsibling)

树的先根遍历图解

(1)

(2)

(3)

(4)

(5)

(6)

(7)

(8)

树的先根遍历小技巧

树的先根遍历可以看成是以根节点为起点,围绕整个树跑一圈,所经历的节点值的序列就是树的先根遍历的结果(重复出现过的节点值不再加入到序列中)。这一点和二叉树的前序遍历很像。

树的先根遍历代码

void CSTree_Show_Pre_Order(CSTree T){
    if(T == NULL)
        return;
    
    printf("%c ", T->data);
    CSTree_Show_Pre_Order(T->firstchild);	
    CSTree_Show_Pre_Order(T->nextsibling);
}

树的后根遍历

树的后根遍历步骤

后根遍历的顺序是先访问根节点的孩子们,然后访问根节点。我们还是用递归函数来解决,先递归访问当前节点的第一个孩子,然后打印当前节点的数据,最后再递归访问当前节点的兄弟。递归函数停止递归的边界条件很简单,遇到空指针 NULL 停止即可。

假设函数名为 CStree_postorder ,则大致步骤如下:
(1)CStree_postorder(root->firstchild)
(2)print(root->data)
(3)CStree_postorder(root->nextsibling)

树的后根遍历图解

(1)

(2)

(3)

(4)

(5)

(6)

(7)

树的后根遍历小技巧

树的后根遍历可以看成是剪葡萄一样,将节点依次剪下后得到的序列就是树的后根遍历的结果,这一点和二叉树的后序遍历有点相似。

树的后根遍历代码

void CSTree_Show_Post_Order(CSTree T){
    if(T == NULL)
        return;

    CSTree_Show_Post_Order(T->firstchild);
    printf("%c ", T->data);
    CSTree_Show_Post_Order(T->nextsibling);
}

树的遍历与二叉树的遍历

树的先根遍历与二叉树的前序遍历

由于我们使用的是孩子兄弟表示法构建的树,实际上我们将树以二叉链式的结构存储了起来,其本质上已经转换为一棵二叉树。树的 firstchild 对应了二叉树的 leftchild,树的 nextsibling 对应了二叉树的 rightchild

结合上文先根遍历的代码和其对应关系,我们可以发现其递归操作的顺序和二叉树的前序遍历是一样的。所以我们其实可以先将树转换为二叉树,然后进行前序遍历,得到的遍历结果和树的先根遍历是一样的。

树的后根遍历与二叉树的中序遍历

同理,结合上文后根遍历的代码和其对应关系,我们可以发现其递归操作的顺序和二叉树的中序遍历是一样的。所以我们其实可以先将树转换为二叉树,然后进行中序遍历,得到的遍历结果和树的后根遍历一样。



森林的遍历

森林的先序遍历

森林的先序遍历步骤

森林的先序遍历顺序即按照森林的每棵树的顺序,对每一棵树进行先根遍历。

森林的先序遍历代码

//森林的先序遍历
void Forest_Show_Pre_Order(Forest F){
    for(int i = 0; i < F->n; i++)
        CSTree_Show_Pre_Order(F->ct[i]);
}

森林的中序遍历

森林的中序遍历步骤

森林的先序遍历顺序即按照森林的每棵树的顺序,对每一棵树进行后根遍历。

森林的中序遍历代码

//森林的中序遍历
void Forest_Show_Post_Order(Forest F){
    for(int i = 0; i < F->n; i++)
        CSTree_Show_Post_Order(F->ct[i]);
}

森林的遍历与二叉树的遍历

森林的先序遍历与二叉树的前序遍历

森林在转换为二叉树的过程中,将每个树根节点用 rightchild 进行了连接。转换成二叉树递归遍历完根节点的左子树时,接下来递归遍历其右子树,而此右子树也是森林中第二个树的根节点,以此类推,可以得出转换成二叉树的前序遍历正好就是森林先序遍历的结果。

森林的先序遍历

转换为二叉树的前序遍历

森林的中序遍历与二叉树的中序遍历

同理,森林转换为二叉树的中序遍历,正好对应了森林中序遍历的结果。

森林的中序遍历

转换为二叉树的中序遍历



程序测试

程序代码

#include<stdio.h>
#include<stdlib.h>
#define MAX 100

typedef char Elemtype;
//树 (孩子兄弟表示法)
typedef struct CSNode{

    Elemtype data;
    struct CSNode *firstchild;      //第一个孩子
    struct CSNode *nextsibling;     //下一个兄弟

}CSNode, *CSTree;


//二叉树 结构体
typedef struct BiNode{

    Elemtype data;
    struct BiNode *leftchild;       //左儿子
    struct BiNode *rightchild;      //右儿子

}BiNode, *BinaryTree;              


//森林 结构体
typedef struct {

    CSTree ct[MAX];
    int n;   //树的个数
	
}forest, *Forest;



/*—————————————————————————————————————————————————————————————————————————————————————*/
//创建 树
CSTree Create_CSTree(){
    Elemtype data;
    CSTree T;
    scanf("%c", &data);                       //输入节点数据
    getchar();

    if(data == '#')        //输入 - 以停止创建子树
        return NULL;
    T = (CSTree)malloc(sizeof(CSNode));
    T->data = data;

    printf("输入 %c 的第一个孩子数据(#停止): ", data);  //递归创建
    T->firstchild = Create_CSTree();
    printf("输入 %c 的下一个兄弟数据(#停止): ", data);  
    T->nextsibling = Create_CSTree();
 
    return T;
}



/*—————————————————————————————————————————————————————————————————————————————————————*/
//树 转化为 二叉树
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;
}


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

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

    return T;
}


//森林 转化为 二叉树
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;
}


//二叉树 转化为 森林
Forest BinaryTree_Transform_to_Forest(BinaryTree bt){
    Forest F = (Forest)malloc(sizeof(forest));
    BinaryTree p = bt;	
    BinaryTree q = NULL;

    int count = 0;
    while(p){
	q = p->rightchild;    //q指向要切断连接的下一个节点(即其右儿子)
	p->rightchild = NULL; //切断连接 形成单独的树

	F->ct[count++] = BinaryTree_Transform_to_CSTree(p);//二叉树 转化为 树存到森林中
	p = q;    //p指向下一个节点 重复操作
    }

    F->n = count; //记录森林中 树的个数
    return F;
}



/*—————————————————————————————————————————————————————————————————————————————————————*/
//前序 递归遍历二叉树
void BinaryTree_Show_Pre_Order(BinaryTree T){
    if(T == NULL)
	return;

    printf("%c ", T->data);	
    BinaryTree_Show_Pre_Order(T->leftchild);
    BinaryTree_Show_Pre_Order(T->rightchild);
}


//中序 递归遍历二叉树
void BinaryTree_Show_Infix_Order(BinaryTree T){
    if(T == NULL)
        return;

    BinaryTree_Show_Infix_Order(T->leftchild);
    printf("%c ", T->data);
    BinaryTree_Show_Infix_Order(T->rightchild);
}


//后序 递归遍历二叉树
void BinaryTree_Show_Post_Order(BinaryTree T){
    if(T == NULL)
	return;

    BinaryTree_Show_Post_Order(T->leftchild);
    BinaryTree_Show_Post_Order(T->rightchild);
    printf("%c ", T->data);
}



/*—————————————————————————————————————————————————————————————————————————————————————*/
//树的先根遍历  (相当于将firstchild当做left, 将nextsibling当做right)
void CSTree_Show_Pre_Order(CSTree T){
    if(T == NULL)
	return;
    
    printf("%c ", T->data);
    CSTree_Show_Pre_Order(T->firstchild);	
    CSTree_Show_Pre_Order(T->nextsibling);
}


//树的后根遍历 写法和二叉树的中序遍历一样(相当于将firstchild当做left, 将nextsibling当做right)
void CSTree_Show_Post_Order(CSTree T){
    if(T == NULL)
	return;

    CSTree_Show_Post_Order(T->firstchild);
    printf("%c ", T->data);
    CSTree_Show_Post_Order(T->nextsibling);
}



/*—————————————————————————————————————————————————————————————————————————————————————*/
//森林的先序遍历
void Forest_Show_Pre_Order(Forest F){
    for(int i = 0; i < F->n; i++)
	CSTree_Show_Pre_Order(F->ct[i]);
}


//森林的中序遍历
void Forest_Show_Post_Order(Forest F){
    for(int i = 0; i < F->n; i++)
	CSTree_Show_Post_Order(F->ct[i]);
}



/*—————————————————————————————————————————————————————————————————————————————————————*/
int main(){
    CSTree F[3];
    for(int i = 0; i < 3; i++){
        printf("创建第 %d 棵树 输入根节点(注意根节点无兄弟):\n", i + 1);
        F[i] = Create_CSTree();
        printf("\n");
    }

    printf("第一棵普通树后根遍历: \n");
    CSTree_Show_Post_Order(F[0]);
    printf("\n");

    printf("第二棵普通树先根遍历: \n");
    CSTree_Show_Pre_Order(F[1]);
    printf("\n");

    printf("三棵树组成的森林转化为二叉树之后中序遍历: \n");
    //如果是一个Forset结构体指针F  Foresr_Transform_to_BinaryTree(F->ct, 0, (F->n) - 1)
    BinaryTree bt = Forest_Transform_to_BinaryTree(F, 0, 2);
    BinaryTree_Show_Infix_Order(bt);
    printf("\n");

    printf("二叉树再次转换为森林之后先根遍历: \n");
    Forest backF = BinaryTree_Transform_to_Forest(bt);
    Forest_Show_Pre_Order(backF);
    printf("\n");
}

程序运行结果

标签:遍历,Show,BinaryTree,CSTree,二叉树,数据结构,Order,森林
From: https://www.cnblogs.com/MAKISE004/p/17091971.html

相关文章

  • 数据结构-实现逆波兰计算器
     packagecom.stack;importjava.util.ArrayList;importjava.util.List;importjava.util.Stack;publicclassPoland{publicstaticvoidmain(String[]a......
  • Fabric2.x中Raft共识算法核心数据结构
    一、共识算法可插拔的代码体现Chain接口HyperledgerFabric的共识算法是可插拔的,在代码上体现为Chain接口,所有不同的共识算法均可根据Chain接口进行具体实现,目前fabric支......
  • [数据结构] 树、二叉树、森林的转换
    树树的表示方法双亲表示法用一组地址连续的存储单元来存放树中的各个节点,每一个节点中有一个数据域和一个指针域,数据域用来存储树中该节点本身的值;另一个指针域用来存储......
  • 代码随想录算法训练营第十八天 | 513.找树左下角的值,112. 路径总和 113.路径总和ii,10
    一、参考资料找树左下角的值题目链接/文章讲解/视频讲解:https://programmercarl.com/0513.%E6%89%BE%E6%A0%91%E5%B7%A6%E4%B8%8B%E8%A7%92%E7%9A%84%E5%80%BC.html......
  • 【C语言 数据结构】数组与对称矩阵的压缩存储
    文章目录​​数组的定义​​​​数组的顺序表示和实现​​​​顺序表中查找和修改数组元素​​​​矩阵的压缩存储​​​​特殊矩阵​​​​稀疏矩阵​​数组的定义提到数组......
  • 秋招备战——数据结构
    二叉树满二叉树,深度为i,总共有pow(2,i)-1个节点的二叉树称为满二叉树哈夫曼树:带权路径最短的二叉树称为哈夫曼树或最优二叉树。终端结点数为n0,度为2的结点数为n2,那么n0=......
  • 二叉树的层次遍历
    文章目录​​二叉树的层次遍历​​​​二叉树的层次遍历​​​​107.二叉树的层序遍历II​​​​199.二叉树的右视图​​​​637.二叉树的层平均值​​​​429.N叉树的......
  • app自动化遍历工具appcrawler
    monkey测试的缺点,测试太随机,功能比较深的页面可能点击不到自动遍历可以制定规则,指定页面,指定点击控件元素范围(Textview显示文字,EditText输入框,Button按钮,ImageView图片等),......
  • 数据结构-小孩出圈问题(约瑟夫环问题)
    假设有m个小孩,数到n的小孩出列,直到全部出去为止。使用环形链表解决约瑟夫环问题。packagecom.linkedlist;publicclassJosephuLinkeslist{publicstaticvoid......
  • 数据结构-基础
    1.什么是数据结构?数据结构是计算机存储、组织数据的方式。数据结构是指数据之间存在一种换种多种特定关系的数据元素的集合。结构包括物理结构和逻辑结构。数据逻辑结构......