二叉树 常见操作

二叉树经典操作实现及总结
这篇博客主要总结了二叉树的一些经典算法题目,包括但不限于先序遍历、森林与二叉树的转换等。作者指出,这些题目中的某些问题可以通过观察满二叉树和完全二叉树的特性来简化解决,比如利用先序遍历记录特定节点。博客内容以代码实现和算法思路为主,是作者完成数据结构作业后的思考和分享。

一般是没有什么动力自己写代码来实践数据结构的,可能还是因为数据结构老师留了新的作业2333.,作业是这样的:


嗯。。其实每一道题都是很经典的算法,一篇博文应该是放不下的,先大体总结一下系列做法,很多都是借鉴的大牛2333,个人的一点小想法其实就是3和4.3中对应森林的叶子结点其实就是二叉树结构中没有左子树的结点,在先序遍历的过程中稍加记录就好。而4呢,就在于满二叉树和完全二叉树的对应结点序号一致就好

代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
using namespace std;
#define STACK_INIT_SIZE 100
#define maxn 100
int cnt=0;
int len=0;
int res=0;
int height;
int tag[maxn];
typedef char TElemType;
typedef struct BiTNode{
	TElemType data;
	struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
typedef struct{
	int top;
	BiTree data[maxn];
}SqStack;
typedef struct{
	BiTree *base;
	BiTree *top;
	int stacksize;
	int llen;
}stack;
void InitStack(stack &s){
	s.base=(BiTree*)malloc(STACK_INIT_SIZE*sizeof(BiTree));
	if(!s.base) exit(0);
	s.top=s.base;
	s.stacksize=STACK_INIT_SIZE;
	s.llen=0;	
}
void push(stack &s,BiTree e){
	if(s.top-s.base>=s.stacksize){
		s.base=(BiTree*)realloc(s.base,(s.stacksize+STACK_INIT_SIZE)*sizeof(BiTree));
		if(!s.base) exit(0);
		s.top=s.base+s.stacksize;
		s.stacksize+=STACK_INIT_SIZE;
	}
	*s.top++=e;
	s.llen++;
}
void pop(stack &s){
	if(s.top==s.base) exit(0);
	//e=*--s.top;
	s.top--;
	s.llen--;
}
BiTree Top(stack s){
	if(s.top==s.base) exit(0);
	BiTree e;
	e=*(s.top-1);
	return e;
}
int isEmpty(stack s)
{
	if(s.base==s.top)
	return 1;
	else
	return 0;
}
BiTree CreateBiTree(BiTree &T) { 
  // 按先序次序输入二叉树中结点的值(一个字符),空格字符表示空树,
  // 构造二叉链表表示的二叉树T。
  char ch;
  scanf("%c",&ch);
  fflush(stdin);
  if (ch=='#') T = NULL;
  else {
    if (!(T = (BiTNode *)malloc(sizeof(BiTNode)))) exit(0);
    T->data = ch;              // 生成根结点
    CreateBiTree(T->lchild);   // 构造左子树
    CreateBiTree(T->rchild);   // 构造右子树
  }
  return T;
} // CreateBiTree
void PreorderTraverse(BiTree T)
{
	SqStack s;
	BiTree p;
	s.top=-1;
	p=T;
	while(p)
	{
		while(p){
		printf("%c",p->data);
		if(p->rchild)
		{
			if(s.top==maxn) exit(0);
			else s.data[++s.top]=p->rchild;
		}
		else
		res++;
		p=p->lchild;
		}
		if(s.top!=-1) p=s.data[s.top--];
	}
	printf("\n");
}
int TreeDeep(BiTree BT ){
      int deeptree=0;
      stack S;
      InitStack(S);
      BiTree p=BT;
      while(p!=NULL||!isEmpty(S)){
          while(p!=NULL){
              push(S,p);
              //push(tag,0);
              tag[len++]=0;
             p=p->lchild;
        }
         if(tag[len-1]==1){
             deeptree=deeptree>S.llen?deeptree:S.llen;
             pop(S);
             len--;
             p=NULL;
         }else{
             p=Top(S);
             p=p->rchild;
             len--;
             //push(tag,1);
             tag[len++]=1;
         }
     }
     height=deeptree;
     printf("%d\n",deeptree);
     return deeptree;
}
int getlevelnode(BiTree proot, int k)
{
    if(proot == NULL || k == 0)
        return 0;

    int cur_level_size = 0;//当前层的节点个数
    int cur_level = 0;  //当前层数

    queue <BiTree> que;
    que.push(proot);
    while (!que.empty())
    {
        ++cur_level;
        cur_level_size = que.size();
        if(cur_level == k)//当到第k层时
            break;

        int temp_count = 0;
        //当前层的所有节点出队,下一层的结点入队
        while (temp_count < cur_level_size) 
        {
            ++temp_count;
            proot = que.front();
            que.pop();
            if (proot->lchild != NULL)
                que.push(proot->lchild);
            if (proot->rchild != NULL)
                que.push(proot->rchild);
        }       
    }
    while(!que.empty())
        que.pop();//清空队列
    if(cur_level == k)
        return cur_level_size;
    return 0;
}
bool IsFullBitree(BiTree  T)//
{
     queue<BiTree> Q;
     BiTree p;
     bool flag=false;
     if(T!=0)
     {
 		Q.push(T);
	    while(!Q.empty())
 		{
 			p=Q.front();
 			Q.pop();
      		if(!p)
  			flag=true;
  			else if(flag)
    		return false;
  			else
  			{	  	
    			Q.push(p->lchild);
				Q.push(p->rchild);    
			}
 		}
 }
 return true;
}
int main()
{
	BiTree al;
	printf("先序建树2333:\n");
	CreateBiTree(al);
	printf("先序遍历结果为:\n");
	PreorderTraverse(al);
	printf("树的深度为");
	TreeDeep(al);
	for(int i=1;i<=height;i++)
	printf("第%d层结点数为%d\n",i,getlevelnode(al,i));
	printf("二叉树对应的森林的叶子结点数为:");
	printf("%d\n",res);
	if(IsFullBitree(al))
	printf("这是一棵完全二叉树\n");
	else
	printf("这不是一棵完全二叉树\n");
	return 0; 
}
/*
struct node {
    int val;
    struct node* left;
    struct node* right;
};
 
int height(struct node* root)
{
    int h, lh, rh;
    if ( root == NULL)
        return -1;//这里返回-1表示叶子节点的高度为0,若规定叶子节点的高度为1,这里返回0即可
    lh = height(root->left);
    rh = height(root->right);
    if (lh > rh) 
        h = lh + 1;
    else 
        h = rh + 1;
    return h;
}*/ 

输入内容:


运行结果:



<think>首先,用户查询的是数据结构考研中二叉树常见操作的代码实现。我需要基于提供的引用内容来回答,但引用是有限的,所以我会尽量使用这些信息并结合我的知识。 关键引用: - 引用[1]:提到了线索二叉树的结点结构。 - 引用[2]:讨论了二叉树的遍历、节点个数计算、高度差等操作。 - 引用[3]:涉及二叉树的遍历和线索二叉树,包括真题实战。 用户要求中文回答,结构清晰,帮助用户逐步解决问题。系统指令指定了数学表达式的格式、LaTeX语法正确、生成相关问题等。 回答结构: 1. **介绍二叉树**:简要解释二叉树在数据结构中的重要性,特别是在考研中的常见考点。 2. **常见操作列表**:列出用户可能需要的常见操作,如创建、遍历、节点计数等。 3. **代码实现**:为每个操作提供代码实现。代码应使用代码块格式,如示例中的Python代码。但用户查询的是数据结构考研,通常使用C或C++,所以我应该用C语言来实现,因为这是考研中常见的。 4. **逐步解释**:对每个操作简要解释其原理和实现。 5. **引用标识**:在回答中自然地添加引用标识,如[^1]、[^2]等。 6. **相关问题**:在最后生成相关问题,类似于示例。 常见二叉树操作(基于引用和标准知识): - 遍历:前序、中序、后序、层序(引用[2]和[3]提到)。 - 节点计数:总节点数、叶子节点数、第k层节点数(引用[2]提到)。 - 查找节点(引用[2])。 - 销毁二叉树(引用[2])。 - 高度计算(引用[2]提到高度差)。 - 线索二叉树(引用[1]和[3])。 代码实现:我将使用C语言,因为它是考研数据结构中的标准语言。 结构: - 定义二叉树结点结构:先定义基本结点结构。 - 然后为每个操作提供函数实现。 引用标识: - 当提到线索二叉树时,引用[1]。 - 当提到遍历和节点操作时,引用[2]。 - 当提到遍历效率和线索二叉树时,引用[3]。 最后,生成相关问题。 完整回答大纲: 1. **引言**:二叉树是数据结构中的核心概念,考研中常考其操作实现。 2. **二叉树结点定义**:给出结构体定义。 3. **常见操作实现**: - 创建二叉树 - 遍历:前序、中序、后序(递归和迭代)、层序 - 节点计数:总节点数、叶子节点数、第k层节点数 - 查找节点 - 计算高度 - 销毁二叉树 - (可选)线索二叉树相关,如线索化 4. **代码示例**:提供完整或部分代码。 5. **总结**:强调重点。 6. **相关问题**:生成3-5个相关问题。 数学表达式:如果有数学内容,如高度计算,使用$...$或$$...$$格式。但二叉树操作主要是代码,可能数学不多。 确保LaTeX语法正确:例如,在解释高度时,如果有公式。 现在,构建代码: - 结点定义:基于引用[2],但引用[1]是线索二叉树,所以先做基本二叉树。 - 基本结点:`struct TreeNode { int data; struct TreeNode* left; struct TreeNode* right; };` - 操作函数: - `createNode(int data)`: 创建新节点。 - 遍历函数:`preorder`, `inorder`, `postorder`(递归),`levelorder`(使用队列)。 - `countNodes`, `countLeafNodes`, `countNodesAtLevel`(第k层节点数)。 - `findNode`:查找值为x的节点。 - `height`:计算高度。 - `destroyTree`:销毁二叉树。 引用[1]有线索二叉树的定义,我可以简要包括它。 最终回答: - 先定义基本二叉树。 - 然后操作。 - 最后提到线索二叉树作为扩展。 生成相关问题:如“如何实现二叉树的非递归遍历?”、“线索二叉树如何提高效率?”等。</think>以下是针对数据结构考研中二叉树常见操作的代码实现详解。二叉树是数据结构中的核心内容,考研中常考其基本操作,包括遍历、节点计数、高度计算等。我将基于C语言(考研常用语言)逐步解释每个操作的原理,并提供清晰、可靠的代码实现。代码实现参考了考研资料中常见的实践方法[^2][^3],并确保语法正确。二叉树操作通常包括创建、遍历、统计节点、查找、销毁等,我还简要介绍了线索二叉树作为扩展(参考线索二叉树的结点结构[^1])。 ### 1. 二叉树基本结构定义 首先,定义二叉树结点的结构体。每个结点包含数据域和左右子结点指针。 ```c #include <stdio.h> #include <stdlib.h> // 定义二叉树结点结构体 typedef struct TreeNode { int data; // 结点数据(假设为整型) struct TreeNode* left; // 左子结点指针 struct TreeNode* right; // 右子结点指针 } TreeNode; ``` ### 2. 常见操作代码实现 下面列出二叉树在考研中的常见操作,每个操作包括: - **原理简述**:简要解释算法逻辑。 - **代码实现**:提供C语言函数实现,使用递归(考研重点)和迭代方法。 - **时间复杂度**:分析算法效率($O(n)$ 表示线性时间,$n$ 为结点数)。 #### (1) 创建新结点 - **原理**:动态分配内存,初始化结点数据。 - **代码**: ```c TreeNode* createNode(int data) { TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode)); if (newNode == NULL) { perror("Memory allocation failed"); exit(EXIT_FAILURE); } newNode->data = data; newNode->left = NULL; newNode->right = NULL; return newNode; } ``` - **时间复杂度**:$O(1)$。 #### (2) 二叉树遍历(递归实现) 遍历是二叉树的基础操作,考研中常考递归版本[^2][^3]。 - **前序遍历**(根-左-右): - **原理**:先访问根结点,再递归遍历左子树和右子树。 - **代码**: ```c void preorderTraversal(TreeNode* root) { if (root == NULL) return; printf("%d ", root->data); // 访问根结点 preorderTraversal(root->left); preorderTraversal(root->right); } ``` - **中序遍历**(左-根-右): - **原理**:先递归遍历左子树,再访问根结点,最后递归遍历右子树。常用于二叉搜索树的排序。 - **代码**: ```c void inorderTraversal(TreeNode* root) { if (root == NULL) return; inorderTraversal(root->left); printf("%d ", root->data); // 访问根结点 inorderTraversal(root->right); } ``` - **后序遍历**(左-右-根): - **原理**:先递归遍历左子树和右子树,最后访问根结点。常用于销毁树或计算表达式树。 - **代码**: ```c void postorderTraversal(TreeNode* root) { if (root == NULL) return; postorderTraversal(root->left); postorderTraversal(root->right); printf("%d ", root->data); // 访问根结点 } ``` - **层序遍历**(使用队列): - **原理**:按层访问结点,使用队列辅助实现(递归不适合层序)。 - **代码**(需要简单队列实现): ```c #include <queue.h> // 假设有队列库,或自定义队列 void levelorderTraversal(TreeNode* root) { if (root == NULL) return; Queue* queue = createQueue(); // 创建队列 enqueue(queue, root); // 根结点入队 while (!isEmpty(queue)) { TreeNode* current = dequeue(queue); // 出队当前结点 printf("%d ", current->data); if (current->left != NULL) enqueue(queue, current->left); // 左子结点入队 if (current->right != NULL) enqueue(queue, current->right); // 右子结点入队 } freeQueue(queue); // 释放队列内存 } ``` - **时间复杂度**:所有遍历均为 $O(n)$。 #### (3) 节点计数操作 考研中常考节点统计问题,如叶子结点数或第k层结点数[^2]。 - **计算二叉树结点总数**: - **原理**:递归统计左子树和右子树的结点数加1(根结点)。 - **代码**: ```c int countNodes(TreeNode* root) { if (root == NULL) return 0; return 1 + countNodes(root->left) + countNodes(root->right); } ``` - **计算叶子结点数**(无子结点的结点): - **原理**:如果结点无左右子结点,则为叶子结点;否则递归统计。 - **代码**: ```c int countLeafNodes(TreeNode* root) { if (root == NULL) return 0; if (root->left == NULL && root->right == NULL) return 1; // 叶子结点 return countLeafNodes(root->left) + countLeafNodes(root->right); } ``` - **计算第k层结点数**: - **原理**:递归到第k层时计数。高度为 $h$ 的树,第k层结点数满足递推关系。 - **代码**: ```c int countNodesAtLevel(TreeNode* root, int k) { if (root == NULL) return 0; if (k == 1) return 1; // 当前层为k return countNodesAtLevel(root->left, k - 1) + countNodesAtLevel(root->right, k - 1); } ``` - **时间复杂度**:计数操作均为 $O(n)$。 #### (4) 查找结点 - **原理**:递归查找值为x的结点,先查根,再左子树,最后右子树。 - **代码**: ```c TreeNode* findNode(TreeNode* root, int x) { if (root == NULL) return NULL; if (root->data == x) return root; // 找到结点 TreeNode* leftResult = findNode(root->left, x); // 左子树查找 if (leftResult != NULL) return leftResult; return findNode(root->right, x); // 右子树查找 } ``` - **时间复杂度**:$O(n)$。 #### (5) 计算二叉树高度 - **原理**:高度定义为从根到最深叶子结点的路径长度。递归计算左右子树高度,取最大值加1。 - **公式**:高度 $h$ 满足: $$ h(root) = \begin{cases} 0 & \text{if } root = \text{NULL} \\ \max(h(left), h(right)) + 1 & \text{otherwise} \end{cases} $$ - **代码**: ```c int height(TreeNode* root) { if (root == NULL) return 0; int leftHeight = height(root->left); int rightHeight = height(root->right); return (leftHeight > rightHeight) ? leftHeight + 1 : rightHeight + 1; } ``` - **时间复杂度**:$O(n)$。 #### (6) 销毁二叉树 - **原理**:使用后序遍历递归释放内存,避免内存泄漏。 - **代码**: ```c void destroyTree(TreeNode* root) { if (root == NULL) return; destroyTree(root->left); destroyTree(root->right); free(root); // 释放当前结点 } ``` ### 3. 扩展:线索二叉树简介 线索二叉树是考研中的优化考点,它利用空指针存储遍历线索,提高遍历效率[^1][^3]。结点结构包含线索标志(`ltag` 和 `rtag`): ```c typedef struct ThreadNode { int data; struct ThreadNode* lchild, *rchild; int ltag, rtag; // tag=0 表示指针指向孩子,tag=1 表示指针是线索 } ThreadNode, *ThreadTree; ``` - **应用场景**:中序线索化可实现在 $O(n)$ 时间内完成遍历,无需递归栈,适合内存受限环境[^3]。代码实现较复杂,建议参考专门资料。 ### 4. 完整示例代码 以下是一个简单的主函数示例,演示如何使用上述操作: ```c int main() { // 构建一个简单二叉树: 根(1), 左(2), 右(3) TreeNode* root = createNode(1); root->left = createNode(2); root->right = createNode(3); root->left->left = createNode(4); root->left->right = createNode(5); // 测试操作 printf("前序遍历: "); preorderTraversal(root); // 输出: 1 2 4 5 3 printf("\n总节点数: %d\n", countNodes(root)); // 输出: 5 printf("叶子节点数: %d\n", countLeafNodes(root)); // 输出: 3 (4,5,3) printf("高度: %d\n", height(root)); // 输出: 3 destroyTree(root); // 销毁树 return 0; } ``` ### 总结 二叉树操作是数据结构考研的重点,核心在于递归实现遍历和统计。本文提供了常见操作的C语言代码,包括遍历、节点计数、查找、高度计算和销毁,覆盖了考研高频考点[^2][^3]。线索二叉树作为优化手段,可进一步提升遍历效率[^1]。练习时,注意边界条件(如空树)和递归深度,避免栈溢出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值