首页 > 其他分享 >纯C 生成二叉树广义表 根据广义表重构二叉树

纯C 生成二叉树广义表 根据广义表重构二叉树

时间:2024-09-16 08:53:04浏览次数:12  
标签:重构 arr return int BiTree next 二叉树 广义 root

讲解很多都写在注释里了,重构二叉树的过程后面单独拿出来讲

直接上代码:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <limits.h>
typedef struct BiTree
{
	int data;
	struct BiTree* next[2];
} BiTree;
BiTree* BiTree_init(int val)//节点初始化
{
	BiTree* p = (BiTree*)malloc(sizeof(BiTree));
	if (p == NULL) return NULL;
	p->data = val;
	p->next[0] = p->next[1] = NULL;
	return p;
}
void BiTree_clear(BiTree* root)
{
	if (root == NULL) return;
	BiTree_clear(root->next[0]);
	BiTree_clear(root->next[1]);
	free(root);
}
BiTree* BiTree_insert(BiTree* root, int val)
{
	if (root == NULL) return BiTree_init(val);
	int pos = rand() % 2;
	root->next[pos] = BiTree_insert(root->next[pos], val);
	return root;
}
void BiTree_BFS(BiTree* root)
{
	if (root == NULL) return;
	BiTree* q[100] = { NULL };
	int head = 0, tail = 0;
	q[tail++] = root;
	printf("%2d\n", root->data);
	while (head != tail)
	{
		if (q[head]->next[0])
		{
			q[tail] = q[head]->next[0];
			printf("%2d > %2d (L)\n", q[head]->data, q[tail]->data);
			tail++;
		}
		if (q[head]->next[1])
		{
			q[tail] = q[head]->next[1];
			printf("%2d > %2d (R)\n", q[head]->data, q[tail]->data);
			tail++;
		}
		head++;
	}
}
void BiTree_convertToGeneList_sub(BiTree* root, int* ret, int* top)
{//就是一个深搜的过程,只不过是在前中后分别加上'(' ',' ')'
	//多封装一层是为了在不使用全局变量的前提下实现入栈
	if (root == NULL) return;
	ret[++(*top)] = root->data + 128;//为了避免数据的ASCII码值和'(' ',' ')'重合,数据存储时+128,打印时-128
	if (root->next[0] || root->next[1])//左右子树都空就不带'(,)'
	{
		ret[++(*top)] = '(';
		BiTree_convertToGeneList_sub(root->next[0], ret, top);
		ret[++(*top)] = ',';
		BiTree_convertToGeneList_sub(root->next[1], ret, top);
		ret[++(*top)] = ')';
	}
}
int* BiTree_convertToGeneList(BiTree* root)
{
	if (root == NULL) return NULL;
	int* ret = (int*)malloc(sizeof(int) * 1000);//临时变量出函数后空间就被释放了,必须malloc
	int top = -1;
	BiTree_convertToGeneList_sub(root, ret, &top);
	ret[++top] = INT_MAX;//以INT_MAX作为广义表形式的结束标志,防止占用其他数据
	return ret;
}
BiTree* BiTree_rebuild_GeneList_sub(int* arr, int* p)
{
	if (arr == NULL || arr[*p] == INT_MAX) return NULL;
	BiTree* root = BiTree_init(arr[*p] - 128);
	if (arr[*p + 1] == ',' && arr[*p] >= 128) return root;
	if (arr[*p + 1] == INT_MAX) return root;
	int memory = arr[*p + 1];
	if (arr[*p + 1] == '(') *p += 2;
	if (arr[*p] == ',')
	{
		*p += 1;
		root->next[1] = BiTree_rebuild_GeneList_sub(arr, p);
		if (memory == '(') *p += 1;
	}
	else if (arr[*p + 1] != ')')//arr[*p] == data
	{
		root->next[0] = BiTree_rebuild_GeneList_sub(arr, p);
		*p += 2;
		if (arr[*p] >= 128)
		{
			root->next[1] = BiTree_rebuild_GeneList_sub(arr, p);
			if (memory == '(') *p += 1;
		}
	}
	return root;
}
BiTree* BiTree_rebuild_GeneList(int* arr)
{
	if (arr == NULL || arr[0] == INT_MAX) return NULL;
	int p = 0;//多封装一层也是为了不用全局变量
	BiTree* root = BiTree_rebuild_GeneList_sub(arr, &p);
	return root;
}
void BiTree_test()
{
	BiTree* root = NULL;
	int i;
	for (i = 0; i < 30; i++) root = BiTree_insert(root, rand() % 100);//随机生成一颗有30个节点的二叉树
	BiTree_BFS(root);//打印二叉树,以便检验重构二叉树是否正确
	printf("\n\n");
	int* arr = BiTree_convertToGeneList(root);//生成二叉树广义表形式
	BiTree* rebuild = BiTree_rebuild_GeneList(arr);//根据二叉树广义表形式重构二叉树
	BiTree_BFS(rebuild);
	BiTree_clear(root);
	BiTree_clear(rebuild);
}
int main()
{
	//不写srand()是为了方便Debug
	BiTree_test();
	return 0;
}

重构二叉树:

先定义函数功能:以根节点+括号对为单位进行处理

根据 arr & p 还原二叉树,返回其根节点,并令 *p 指向队列中的合适位置

(如碰到 '(' 就要在函数结束前再令 *p 后移一位,以跳出括号对)

再写函数框架:

以根节点为单位进行处理时,先看第一个节点,因为确保这个节点是根节点

所以直接创建并初始化

它的右边要么是 INT_MAX (结束),要么是 '(' (左右子树)

BiTree* BiTree_rebuild_GeneList_sub(int* arr, int* p)
{
	if (arr == NULL || arr[*p] == INT_MAX) return NULL;//特殊情况判断
	BiTree* root = BiTree_init(arr[*p] - 128);//节点创建并初始化
	if (arr[*p + 1] == INT_MAX) return root;//下一个位置就结束时直接返回
    int memory = arr[*p + 1];//memory记录下一位置信息,如果是'('
    //若下一位置是'(',函数结束前 *p 要向后移动一位,以跳出')'
	if (arr[*p + 1] == '(') *p += 2;//下一个位置是'('时令指针指向括号内部,准备生成左右子树
	return root;
}

再详细完善进入第一个括号对后应进行的操作

BiTree* BiTree_rebuild_GeneList_sub(int* arr, int* p)
{
	if (arr == NULL || arr[*p] == INT_MAX) return NULL;
	BiTree* root = BiTree_init(arr[*p] - 128);
	if (arr[*p + 1] == INT_MAX) return root;
	int memory = arr[*p + 1];
	if (arr[*p + 1] == '(') *p += 2;
	if (arr[*p] == ',')
	{//进入括号对内部后,若第一个碰到的就是',',说明该根节点无左子树,*p += 1 后生成右子树
		*p += 1;
		root->next[1] = BiTree_rebuild_GeneList_sub(arr, p);
		if (memory == '(') *p += 1;//跳出')'
	}
	else//arr[*p] == data,生成左子树,并根据情况生成右子树
	{
		root->next[0] = BiTree_rebuild_GeneList_sub(arr, p);
		*p += 2;//移动到','的下一位
		if (arr[*p] >= 128)//','下一位仍是数据,说明该根节点左右子树都有,生成右子树
		{
			root->next[1] = BiTree_rebuild_GeneList_sub(arr, p);
			if (memory == '(') *p += 1;//也要注意跳出')'
		}
	}
	return root;
}

最后补充后续根节点+括号对会碰到的特殊情况

原根节点的左孩子若无左右子树,队列后面紧跟着的是 ','

原根节点的右孩子若无左右子树,队列后面紧跟着的是 ')'

BiTree* BiTree_rebuild_GeneList_sub(int* arr, int* p)
{
	if (arr == NULL || arr[*p] == INT_MAX) return NULL;
	BiTree* root = BiTree_init(arr[*p] - 128);
	if (arr[*p + 1] == ',' && arr[*p] >= 128) return root;//原根节点左孩子无左右子树
	if (arr[*p + 1] == INT_MAX) return root;
	int memory = arr[*p + 1];
	if (arr[*p + 1] == '(') *p += 2;
	if (arr[*p] == ',')
	{
		*p += 1;
		root->next[1] = BiTree_rebuild_GeneList_sub(arr, p);
		if (memory == '(') *p += 1;
	}
	else if (arr[*p + 1] != ')')//原根节点右孩子有左右子树
	{
		root->next[0] = BiTree_rebuild_GeneList_sub(arr, p);
		*p += 2;
		if (arr[*p] >= 128)
		{
			root->next[1] = BiTree_rebuild_GeneList_sub(arr, p);
			if (memory == '(') *p += 1;
		}//这两个对 *p 的操作都是让其指向合适的位置
	}
	return root;
}

思路正确后耐心 Debug 即可,主要就是各种 if

标签:重构,arr,return,int,BiTree,next,二叉树,广义,root
From: https://blog.csdn.net/qwq_ovo_pwp/article/details/142298348

相关文章

  • 力扣热题100 - 二叉树:验证二叉搜索树
    题目描述:题号:98给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。有效 二叉搜索树定义如下:节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。解题思路:思路一......
  • LeetCode_0144. 二叉树前序遍历 & LeetCode_0096. 二叉树中序遍历 & LeetCode_0145.
    题目描述  给你二叉树的根节点root,返回它节点值的前序/中序/后序遍历。递归写法LeetCode_0144.前序中左右voidmyPreorder(TreeNode*root,vector<int>&ans){if(!root){return;}ans.emplace_back(root->val);myPre......
  • 二叉树的所有路径(所有从根节点到叶子节点的路径)-257
    题目描述给你一个二叉树的根节点root,按任意顺序,返回所有从根节点到叶子节点的路径。叶子节点是指没有子节点的节点。解题思路这道题我们采用二叉树里的前序遍历方式,我们要遍历所有到叶子节点的路径,我们采用复用的思想,就是让这里的几个数据结构我们可以重复使用,但是重复使......
  • C++: 二叉树进阶面试题
    做每件事之前都心存诚意,就会事半功倍.目录前言1.根据二叉树创建字符串2.二叉树的层序遍历Ⅰ3.二叉树的层序遍历Ⅱ4.二叉树的最近公共祖先5.二叉搜索树与双向链表6.根据一棵树的前序遍历与中序遍历构造二叉树7.根据一棵树的中序遍历与后序遍历构造二叉树8.二......
  • 二叉树的 Morris 中序遍历
    回顾问题陈述:给定一棵二叉树,实现中序遍历并返回包含其中序序列的数组例如给定下列二叉树:我们按照左、根、右的顺序递归遍历二叉树,得到以下遍历:最终中序遍历结果可以输出为:[3,1,9,2,4,7,5,8,6]MorristrickMorris中序遍历是一种树遍历算法,旨在实现O(1)的空间......
  • 【二叉树的最大深度】带你理解递归奥妙!
    ......
  • 代码随想录 -- 二叉树 -- 二叉搜索树中的众数
    501.二叉搜索树中的众数-力扣(LeetCode)思路:定义一个字典1,key 为二叉树的值,value 为二叉树的值出现的次数。定义一个字典2,key 为二叉树的值出现的次数,value (列表)为二叉树的值。找出字典2中最大的key,返回其 value 即可。 classSolution(object):defcreate......
  • 代码随想录算法 - 二叉树5
    题目1530.二叉搜索树的最小绝对差给你一个二叉搜索树的根节点root,返回树中任意两不同节点值之间的最小差值。差值是一个正数,其数值等于两值之差的绝对值。示例1:输入:root=[4,2,6,1,3]输出:1示例2:输入:root=[1,0,48,null,null,12,49]输出:1提示:树中节点的数......
  • 代码随想录算法 - 二叉树4
    题目1654.最大二叉树给定一个不重复的整数数组nums。最大二叉树可以用下面的算法从nums递归地构建:创建一个根节点,其值为nums中的最大值。递归地在最大值左边的子数组前缀上构建左子树。递归地在最大值右边的子数组后缀上构建右子树。返回nums构建的*......
  • dfs 验证搜索二叉树——leetcode98
    代码来自leetcode官方一开始我自己写这个代码时只注意当前节点是否会存在空指针,并没有注意到他的孩子节点也有可能为空,绕了我好久。。。。。。/***Definitionforabinarytreenode.*structTreeNode{*intval;*TreeNode*left;*TreeNode*right;......