【数据结构】普通二叉树的实现

一、问题概述

       树是n个有限个数据的集合,形如:


       它像不像倒着的树呢?我们把它看成是一种数据结构----树。它的第一个节点称作树的根,最底下的那些节点称作树的叶子。

       我们今天所要研究的是二叉树,即父节点最多只有两个孩子(左孩子和右孩子)。



二叉树还有两种特殊的结构,分为完全二叉树和满二叉树。

如:


满二叉树:高度为N的满二叉树有2^N-1个节点。

完全二叉树:高度为N,前N-1层为满二叉树,第N层为连续的叶子节点。

二叉树有四种遍历方式:

前序遍历(根左右),中序遍历(左根右),后序遍历(左右根),层序遍历(从上往下,从左往右)。

那么,如何实现一个二叉树的数据结构呢?

二、数据结构的设计

       在这里,我们采取链表的方式,即创建节点,将节点与节点链接起来的方式实现二叉树。

节点的结构:


(1)将要创建的节点的数据部分存储到数组里,然后创建节点。读取数组,我们将指针指向空的部分当作是非法字符,在这里非法字符是‘#’;

(2)如果不是非法字符,则创建节点并链接到二叉树的根上,按照先序遍历的方式先创建根,再创建根的左,最后创建根的右。

(3)创建完成后,对二叉树进行相应的操作。如:求叶子节点数,第k层节点数,四种遍历方式,递归和非递归实现等。

三、实现代码

//BinaryTree.h

#pragma once
#include<assert.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;template<typename T>
struct BinaryTreeNode  //创建节点
{T _data;BinaryTreeNode<T> *_left;BinaryTreeNode<T> *_right;BinaryTreeNode(const T& data):_data(data), _left(NULL), _right(NULL){}
};template<typename T>
class BinaryTree
{typedef BinaryTreeNode<T> Node;
public:BinaryTree():_root(NULL){}BinaryTree(T* arr,size_t size,const T& invalid = T()){assert(arr);size_t index = 0;_root = CreateTree(arr,size,invalid,index);}BinaryTree(BinaryTree<T> &t){assert(t._root);_root = _Copy(t._root);}//传统写法/*BinaryTree<T>& operator=(BinaryTree<T>& t){if (this != &t){Node* tmp = _Copy(t._root);_root = _Destroy(_root);_root = tmp;}return *this;}*///现代写法BinaryTree<T>& operator=(BinaryTree<T>& t){if (this != &t){BinaryTree<T> tmp(t);std::swap(tmp._root, _root);}return *this;}~BinaryTree(){if (_root){_root = _Destroy(_root);}}
public:size_t Size(){return _Size(_root);}size_t Depth(){return _Depth(_root);}void PreOrder(){_PreOrder(_root);cout << endl;}void InOrder(){_InOrder(_root);cout << endl;}void PostOrder(){_PostOrder(_root);cout << endl;}void LevelOrder(){_LevelOrder(_root);cout << endl;}Node* Find(const T& x){return _Find(_root,x);}public://创建二叉树Node* CreateTree(T* arr, size_t size, const T& invalid,size_t& index){Node* root = NULL;if (index < size){if (arr[index] != invalid){root = new Node(arr[index]);root->_left = CreateTree(arr, size, invalid, ++index);root->_right = CreateTree(arr, size, invalid, ++index);}}return root;}//拷贝二叉树Node* _Copy(Node* root){Node* cur = NULL;if (root){cur = new Node(root->_data);cur->_left = _Copy(root->_left);cur->_right = _Copy(root->_right);}return cur;}//释放二叉树节点Node* _Destroy(Node* root){if (root != NULL){root->_left = _Destroy(root->_left);root->_right = _Destroy(root->_right);delete root;root = NULL;}return root;}//递归求解二叉树节点的个数size_t _Size(Node* root)     {if (root == NULL)return 0;return _Size(root->_left) + _Size(root->_right) + 1;}//二叉树的深度求解size_t _Depth(Node* root){size_t maxDepth = 1;if (root){size_t depth = 1;if (root->_left)             //左不为空则遍历左树的深度{depth += _Depth(root->_left);}if (depth > maxDepth){maxDepth = depth;}if (root->_right)          //右不为空则在左树的深度基础上+1{depth = _Depth(root->_right) + 1;}if (depth > maxDepth){maxDepth = depth;}}return maxDepth;}//二叉树前序遍历的递归实现void _PreOrder(Node* root){if (root){cout << root->_data << " ";_PreOrder(root->_left);_PreOrder(root->_right);}}//二叉树中序遍历的递归实现void _InOrder(Node* root){if (root){_InOrder(root->_left);cout << root->_data << " ";_InOrder(root->_right);}}//二叉树后序遍历的递归实现void _PostOrder(Node* root){if (root){_PostOrder(root->_left);_PostOrder(root->_right);cout << root->_data << " ";}}//二叉树层序遍历的实现void _LevelOrder(Node* root){queue<Node*> q;if (root)q.push(root);while (!q.empty()){Node* front = q.front();cout << front->_data << " ";q.pop();if (front->_left){q.push(front->_left);}if (front->_right){q.push(front->_right);}}}//二叉树中查找某个值的节点Node* _Find(Node* root,const T& data){Node* cur = root;if(root == NULL)return NULL;if(root->_data == data)         //找到则返回节点return root;Node* ret = _Find(cur->_left,data);if(ret == NULL){ret = _Find(cur->_right,data);}return ret;}
public:void PreOrderNonR(){_PreOrderNonR(_root);cout<<endl;}void InOrderNonR(){_InOrderNonR(_root);cout<<endl;}void PostOrderNonR(){_PostOrderNonR(_root);cout<<endl;}
public://二叉树前序遍历的非递归实现void _PreOrderNonR(Node* root){Node* cur = root;stack<Node*> s;while(!s.empty() || cur){while(cur){cout<<cur->_data<<" ";s.push(cur);cur = cur->_left;}Node* top = s.top();s.pop();cur = top->_right;}}//二叉树中序遍历的非递归实现void _InOrderNonR(Node* root){Node* cur = root;stack<Node*> s;while(!s.empty() || cur){while(cur){s.push(cur);cur = cur->_left;}Node* top = s.top();cout<<top->_data<<" ";s.pop();cur = top->_right;}}//二叉树后序遍历的非递归实现void _PostOrderNonR(Node* root){Node* cur = root;stack<Node*> s;Node* prev = NULL;while(!s.empty() || cur){while(cur){s.push(cur);cur = cur->_left;}Node* top = s.top();if(top->_right == NULL || top->_right == prev){cout<<top->_data<<" ";prev = top;s.pop();}else{cur = top->_right;}}}
public:size_t GetKLevelSize(size_t k){assert(_root);size_t level = 1;size_t count = 0;_GetKLevelSize(_root,k,level,count);return count;}//获取第k层节点的个数(当k等于层数level时,count++,否则分别遍历左树和右树)void _GetKLevelSize(Node* root,size_t k,size_t level,size_t& count){if(root == NULL)return;if(k == level){count++;return;}_GetKLevelSize(root->_left,k,level+1,count);_GetKLevelSize(root->_right,k,level+1,count);}size_t GetLeafSize(){size_t count = 0;_GetLeafSize(_root,count);return count;}//获取叶子节点(当节点的左树为空且右树为空时,即叶子数加1)void _GetLeafSize(Node* root,size_t& count){if(root == NULL)return;if(root->_left == NULL && root->_right == NULL){count++;return;}_GetLeafSize(root->_left,count);_GetLeafSize(root->_right,count);}size_t SizePrev(){size_t count = 0;_SizePrev(_root,count);return count;}//用引用的方法获取二叉数节点的个数(需要入栈)void _SizePrev(Node* root,size_t& count){if(root == NULL)return;Node* cur = root;stack<Node*> s;while(!s.empty() || cur){while(cur){s.push(cur);count++;cur = cur->_left;}Node* top = s.top();s.pop();cur = top->_right;}}
private:Node* _root;
};void FunTest()
{int arr[] = {1,2,3,'#','#',4,'#','#',5,6};int arr1[] = { 1, 2,'#', 3, '#', '#', 4, 5,'#',6 ,'#', 7,'#','#',8};BinaryTree<int> b1(arr,sizeof(arr)/sizeof(arr[0]),'#');BinaryTree<int> b6(arr1, sizeof(arr1) / sizeof(arr1[0]), '#');BinaryTree<int> b2(b1);BinaryTree<int> b3;b3 = b2;cout << b1.Size() << endl;cout << b2.Size() << endl;cout << b3.Size() << endl;cout << b1.Depth() << endl;cout << b6.Depth() << endl;cout<<"b1:递归先序遍历->";b1.PreOrder();cout<<"b1:递归中序遍历->";b1.InOrder();cout<<"b1:递归后序遍历->";b1.PostOrder();cout<<"b1:层序遍历->";b1.LevelOrder();cout<<"b1:非递归先序遍历->";b1.PreOrderNonR();cout<<"b1:非递归中序遍历->";b1.InOrderNonR();cout<<"b1:非递归后序遍历->";b1.PostOrderNonR();cout<<"Find(4)?"<<endl;cout<<b1.Find(4)->_data<<endl;cout<<"GetLeafSize:"<<b1.GetLeafSize()<<endl;cout<<"_SizePrev:"<<b1.SizePrev()<<endl;cout<<"b6:递归先序遍历->";b6.PreOrder();cout<<"b6:递归中序遍历->";b6.InOrder();cout<<"b6:递归后序遍历->";b6.PostOrder();cout<<"b6:递归层序遍历->";b6.LevelOrder();cout<<"第三层节点数:"<<b6.GetKLevelSize(3)<<endl;cout<<"第四层节点数:"<<b6.GetKLevelSize(4)<<endl;cout<<"第五层节点数:"<<b6.GetKLevelSize(5)<<endl;cout<<"GetLeafSize:"<<b6.GetLeafSize()<<endl;cout<<"_SizePrev:"<<b6.SizePrev()<<endl;
}

//BinaryTree.cpp

#include<iostream>
using namespace std;
#include"BinaryTree.h"
int main()
{FunTest();return 0;
}

四、运行结果



前一篇广义表的数据结构和二叉树的数据结构也有一些类似哦。大家可以看看哒^-^



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

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

相关文章

setitimer用法说明

函数原型&#xff1a; int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value) 函数作用&#xff1a; 可用来实现延时和定时的功能 头文件&#xff1a; #include <sys/time.h> 参数详解 用一把&#xff1a;一个例子 #include &…

哈希表(闭散列、拉链法--哈希桶)

哈希表&#xff0c;也称散列表&#xff0c;是一种通过key值来直接访问在内存中的存储的数据结构。它通过一个关键值的函数&#xff08;被称为散列函数&#xff09;将所需的数据映射到表中的位置来访问数据。 关于哈希表&#xff0c;主要为以下几个方面&#xff1a; 一、哈希表…

僵尸进程的产生和SIGCHLD信号

核心句子 子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自 定义SIGCHLD信号的处理函数。 僵尸进程的产生&#xff1a; #include "head.h" #include <unistd.h> #include <signal.h>int main() {key_t key ftok(&quo…

海量数据处理--位图(BitMap)

对于海量数据这个词&#xff0c;大家不难理解吧。主要是针对给定的数据量特别大&#xff0c;占用内存特别大的情况。那么和位图有什么关系呢。看下面一个腾讯的海量数据的例子吧。 例&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0…

ps命令与top命令参数意义详解

文章目录1.ps -l2.ps aux3.top面试经常被问道&#xff0c;特别是top。1.ps -l 参数解释F代表这个程序旗标 (process flags)&#xff0c;说明这个程序的总结权限&#xff0c;常见号码有&#xff1a;o 若为 4 表示此程序的权限为 root &#xff1b;o 若为 1 则表示此子程序仅进行…

哈希拓展--布隆过滤器

一、问题概述 布隆过滤器是由布隆提出来的&#xff0c;是由一个很长的二进制序列和一系列的映射函数组成。主要用于检测一个元素是否在一个集合中。当然在设计计算机软件时&#xff0c;我们也经常会判断一个元素是否在一个集合中。比如&#xff1a;在字处理软件中&#xff0c;…

排序(Sort)--【一】

排序&#xff0c;对于大家再熟悉不过了吧。我们之前在学习c语言的时候接触过的冒泡排序&#xff0c;选择排序等。今天给大家介绍两种新的排序。 1、直接插入排序 升序排列&#xff1a;将第一个数确定好&#xff0c;从下标为1的数开始插入&#xff0c;如果插入的数比前一个数大…

快速排序--全集

快速排序&#xff1a;一听名字就知道这种排序很快的&#xff0c;是吧&#xff1f;没错&#xff0c;它是一种效率比较高的排序算法。 快速排序采用的是分治的思想。 比如&#xff0c;将一串数中的一个元素作为基准&#xff0c;然后将比它小的数排在它的左边&#xff0c;比它大…

task_struct结构体查找

网上有很多解析task_struct结构体的文章&#xff0c;可是都没有说这个结构体到底在哪里&#xff1f; 这个结构体位于头文件 shced.h cd / find -name sched.h 显示结果如下 注意只有 位于内核中的include 才是正确的。 /usr/src/kernels/2.6.32-431.el6.i686/include/linux…

闹钟函数alarm()的解释与实践

alarm 定义 也称为闹钟函数&#xff0c;它可以在进程中设置一个定时器&#xff0c;当定时器指定的时间到时&#xff0c;它向进程发送SIGALRM信号。可以设置忽略或者不捕获此信号&#xff0c;如果采用默认方式其动作是终止调用该alarm函数的进程。 #include "head.h&quo…

Linux下如何设置权限让用户只删除自己的文件(粘滞位)

之前我们知道如何针对用户和用户组来设置文件权限。通常是用三个八进制来设置权限的&#xff0c;这里我要说的是&#xff0c;其实是由四个八进制表示的。其中第一个八进制我们通常是忽略的。第二个到第四个是对应于SUID,SGID,sticky-bit。 SUID&#xff1a;设置了SUID 位的文件…

Vim简单配置

vim配置&#xff1a; &#xff08;在Centos6.5下配置vim&#xff09; 1.找到用户的主工作目录&#xff0c;ls看是否有.vimrc文件&#xff0c;有的话打开即可。没有的话自己touch一个。vim进入.vimrc中&#xff1a; set nu 设置行数 colorscheme desert syntax enabl…

[WPS笔试题]实现栈的push,pop,max且时间复杂度为O(1)

今天做了一下WPS的笔试题&#xff0c;遇到了一道关于栈的题&#xff0c;觉得挺有意思的&#xff0c;就写篇博客分享一下吧~~ 题目要求&#xff1a;要求实现栈的数据结构&#xff0c;在该类型中实现一个能够得到栈的最大元素的max函数&#xff0c;在该栈中&#xff0c;调用max,…

[剑指Offer]替换空格

今天看题的时候&#xff0c;遇到一个替换空格的题目&#xff0c;分析一下哈。 题目要求&#xff1a;把字符串中的每个空格替换成“%20”。例如输入“we are happy”&#xff0c;则输出“we%20are%20happy”。 解题思路&#xff1a;我们首先想到的是&#xff1a;移位思想。遇到…

C语言关键字 ISO/ANSI C90 C99 C11

面试考点 https://blog.csdn.net/csdn_kou/article/details/81113215 * 有的常用的我们都不知道是关键字&#xff0c;比如sizeof.这是面试中的考点&#xff0c;要注意。 * 同时当回答C语言中有多少关键字时&#xff0c;要回答前题条件&#xff0c;时针对哪一个版本

关于sudo

之前&#xff0c;我们使用sudo的时候&#xff0c;是因为其用户本身具有root权限&#xff0c;所以可以sudo后执行相关操作&#xff0c;但是对于普通用户来说&#xff0c;它是既不具有sudo权限&#xff0c;又不在sudo用户组中&#xff0c;那么我们来研究一下如何将新创建的用户添…

Bash入门

Bash简介&#xff1a; Bash&#xff08;GNU Bourne-Again Shell&#xff09;是一个为GNU计划编写的Unix shell&#xff0c;它是许多Linux平台默认使用的shell。 shell是一个命令解释器&#xff0c;是介于操作系统内核与用户之间的一个绝缘层。准确地说&#xff0c;它也是能力…

线程之售票系统pthread_mutex,_lock,_unlock

先看一下这篇文章 https://blog.csdn.net/csdn_kou/article/details/81148268 四个人同时买票票&#xff0c;引出线程 #include "head.h" int ticket 100; void * route(void *arg) {char *id (char *)arg;while(1){if(ticket>0){usleep(1000);printf("…

Bash基本语法

1. 变量赋值 a375 hello$a 这里需要注意的是&#xff0c;等号两边不能有空格 还有一个例子是这样的 例1&#xff1a; 结果为&#xff1a; 关于上述&#xff0c;主要有如下几点&#xff1a; $hello和${hello}是一样的&#xff0c;在bash中如果遇到空格&#xff0c;tab键时&a…