二叉树链式结构的实现

文章目录

1.前置说明

2.二叉树的遍历


文章内容

1.前置说明

       学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在我们对于二叉树的了解还处于初级阶段,所以我们手动创建一棵简单的二叉树,以便进入二叉树操作学习,等深入了解二叉树之后,我们再来研究二叉树真正建立的方法。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>#include "Queue.h"typedef int BTDataType;
typedef struct BinaryTreeNode
{struct BinaryTreeNode* left;struct BinaryTreeNode* right;BTDataType data;}BTNode;BTNode* BuyNode(BTDataType x)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL){perror("malloc fail");exit(-1);}newnode->data = x;newnode->left = NULL;newnode->right = NULL;return newnode;
}BTNode* CreatBinaryTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);
//  BTNode* node7 = BuyNode(7);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;
//	node5->right = node7;return node1;
}

注意:上述代码并不是创建二叉树的方式

再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:

 1. 空树
 2. 非空:根节点,根节点的左子树、根节点的右子树组成的。

 从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。

2.二叉树的遍历

 2.1 前序、中序以及后序遍历

      学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

 按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

访问根的时候决定了其到底是什么遍历。

由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

   递归问题,我们第一个要想的是在哪里展开递归,第二个便是回退的时机,我们想要的得到的结果是什么,这导致我们要施加什么回退的条件来结束递归。回退又可以分为递归的程序结束完回退,以及符合回退条件时回退

每一个节点都可以看成根节点,每一个节点都有其左子树和右子树。

前序遍历递归图解:(先访问根,在遍历)

 

 

     

//前序
void PreOrder(BTNode* root)
{if (root == NULL){printf("# ");return;}printf("%d ", root->data);PreOrder(root->left);PreOrder(root->right);}

中序遍历:(先遍历,在访问根节点)

 


//中序
void InOrder(BTNode* root)
{if (root == NULL){printf("# ");return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);}

后序遍历:(最后访问根)

//后序
void PostOrder(BTNode* root)
{if (root == NULL){printf("# ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);}
2.2二叉树节点数量

   在判断当前节点不为空的时候count++

   return在count++之前,所以逻辑上当当前节点为空时候,就返回了,count并不会++。

 

要注意的是,这里我们要定义一个全局变量count ,方便我们计数

int count = 0;
void TreeSize1(BTNode* root)
{if (root == NULL){return;}count++;TreeSize1(root->left);TreeSize1(root->right);}

main函数里 count 要初始化,全局变量谁都能用,避免出现差错。

int main()
{count = 0;TreeSize1(root);printf("TreeSize1:%d\n", count);return 0;
}

 第二种思路:

与第一种思路相似,遇到空就返回,不过使用了三目操作符,更加简单。

int TreeSize2(BTNode* root)
{return root == NULL ? 0 :TreeSize2(root->left) + TreeSize2(root->right) + 1;
}
2.3叶子节点的数量

  叶子节点是左右节点都为空的节点。这个定义就是回退是的条件。

  当 当前节点既不为空,左孩子节点,右孩子节点也都不为空时,继续开展递归,当前节点的左孩子右孩子都为空时(此时为叶子节点),或当前节点为为空,便回退。

//叶子节点数量
//递归往往是回退的时候才带值的
int TreeLeafSize(BTNode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL)//左子树右子树都为空是叶子节点的条件{return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);}
2.4二叉树第k层节点的数量

     要找到确定第k层节点的数量,就要先找到第k层,我们这里从第一层开始,没有第0层。

     函数递归展开后其本身并不知道当前是第几层,这时候我们就需要一个函数变量来告诉函数是第几层。

      求根节点的第k层就是求其左右子树的第k-1层,在把左右子树看成根节点,就是求k-2层......

依次下去当k等于1 时,当前层数的节点就是我们要求的

        

//k层节点
//通过控制k 来控制递归
int TreeKLevel(BTNode* root, int k)
{assert(k >= 1);if (root == NULL){return 0;}if (k == 1)//通过k控制递归的层数,程序到达这里,首先要root != null 要进去就要符合条件{return 1;}return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);}
 2.5查找值为x的数

        查找x并不需要遍历全部的二叉树,当找到对应的x之后我们要停止遍历二叉树。

        停止遍历二叉之后,我们还要把x值所在节点的地址给带回去。

//查找x
//找到就返回,如在左子树里面找到,直接返回不需要再去右子树里面BTNode* TreeFind(BTNode* root, int x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* ret1 = TreeFind(root->left, x);if (ret1){return ret1;//假如再次分支找到了,程序不会往下走,直接返回了}BTNode* ret2 = TreeFind(root->right, x);if (ret2){return ret2;}return NULL;
}
2.6二叉树的深度

        当前二叉树的深度可以转化成左子树右子树的中最高的那一颗+1。而左右子树的高度又可以转化为他们子树中最高的那一颗+1......

       从最深的那一层的节点开始找出他们最深的左右子树返回给上一层。

2.7二叉树的销毁 

        二叉树的销毁符合后序遍历,先访问在左右子树,在销毁。free要放到遍历完右子树后,如果先free而不是先遍历的话,就会造成野指针的问题。

        

//销毁
void TreeDestroy(BTNode* root)
{if (root == NULL){return ;}TreeDestroy(root->left);TreeDestroy(root->right);free(root);}

        

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/83939.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【android12-linux-5.1】【ST芯片】HAL移植后没调起来

ST传感器芯片HAL按官方文档移植后&#xff0c;测试一直掉不起来&#xff0c;加的日志没出来。经过分析&#xff0c;是系统自带了一个HAL&#xff0c;影响的。 按照官方文档&#xff0c;移植HAL后&#xff0c;在/device/<vendor\>/<board\>/device.mk*路径增加PROD…

《华为认证》配置vlan聚合

1、实验环境&#xff1a;如图所示&#xff0c;配置vlan 10、20、100 &#xff0c;vlan 100作为聚合vlan&#xff0c;vlan 10、vlan20作为子vlan&#xff0c;vlan 10和vlan 20配置成相同网段的ip地址。Vlanif 100 作为vlan 10 和vlan20的网关&#xff0c;在vlanif100上配置arp代…

共享内存 windows和linux

服务端&#xff0c;即写入端 #include <iostream> #include <string.h> #define BUF_SIZE 1024 #ifdef _WIN32 #include <windows.h> #define SHARENAME L"shareMemory" HANDLE g_MapFIle; LPVOID g_baseBuffer; #else #define SHARENAME "sh…

前端需要理解的数据治理与异常监控知识

1 数据治理 前端数据治理的重要指标是准确性和数据&#xff0c;一个数据对象包括数据值和其他元数据。 2 数据上报方式 2.1 Image 通过将采集的数据拼接在图片请求的后面&#xff0c;向服务端请求一个 1*1 px 大小的图片&#xff08;gif&#xff09;实现的&#xff0c;设置…

day01_Java概述丶环境搭建

Java背景知识 Java概述 概述&#xff1a;计算机语言就是人与计算机之间进行信息交流沟通的一种特殊语言。所谓计算机编程语言&#xff0c;就是人们可以使用编程语言对计算机下达命令&#xff0c;让计算机完成人们需要的功能。 Java语言&#xff1a;是美国Sun公司&#xff08…

并发-并发挑战及底层实现原理笔记

并发编程挑战 上下文切换 cpu通过给每个线程分配cpu时间片实现多线程执行&#xff0c;时间片是cpu分配给各个线程的时间&#xff0c;cpu通过不断切换线程执行。线程有创建和上下文切换的开销。减少上下文切换的方方法 – 无锁并发编程&#xff0c;eg&#xff1a;将数据的id按…

ubuntu学习(四)----文件写入操作编程

1、write函数的详解 ssize_t write(int fd,const void*buf,size_t count); 参数说明&#xff1a; fd:是文件描述符&#xff08;write所对应的是写&#xff0c;即就是1&#xff09; buf:通常是一个字符串&#xff0c;需要写入的字符串 count&#xff1a;是每次写入的字节数…

Linux线程 --- 生产者消费者模型(C语言)

在学习完线程相关的概念之后&#xff0c;本节来认识一下Linux多线程相关的一个重要模型----“ 生产者消费者模型” 本文参考&#xff1a; Linux多线程生产者与消费者_红娃子的博客-CSDN博客 Linux多线程——生产者消费者模型_linux多线程生产者与消费者_两片空白的博客-CSDN博客…

pytestx重新定义接口框架设计

概览 脚手架&#xff1a; 目录&#xff1a; 用例代码&#xff1a; """ 测试登录到下单流程&#xff0c;需要先启动后端服务 """test_data {"查询SKU": {"skuName": "电子书"},"添加购物车": {"sk…

一篇文章带你彻底了解Java常用的设计模式

文章目录 前言1. 工厂模式使用示例代码优势 2. 单例模式说明使用示例代码优势 3. 原型模式使用示例代码优势 4. 适配器模式使用示例代码优势 5. 观察者模式使用示例代码优势 6. 策略模式使用示例代码优势 7. 装饰者模式使用示例代码优势 8. 模板方法模式使用示例代码优势 总结 …

网络学生用品商店系统设计与实现(论文+源码)_kaic

摘 要 随着互联网的发展&#xff0c;人们的生活发生了巨大的变化&#xff0c;给人们的生活、工作等方面带来了相当大的提高&#xff0c;电子化成为了节约成本、调高效率的代名词。电子商务是利用微电脑技术和网络通讯技术进行的商务活动&#xff0c;买卖双方通过网络所进行各…

Windows商店引入SUSE Linux Enterprise Server和openSUSE Leap

在上个月的Build 2017开发者大会上&#xff0c;微软宣布将SUSE&#xff0c;Ubuntu和Fedora引入Windows 商店&#xff0c;反应出微软对开放源码社区的更多承诺。 该公司去年以铂金会员身份加入Linux基金会。现在&#xff0c;微软针对内测者的Windows商店已经开始提供 部分Linux发…