linux内核设计与实现 epub_Linux内核-二叉树概念及其实现

3565d3b596197db7dedead56152cfb50.png

一、二叉树定义

在计算机科学中,树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为节点)按分支关系组织起来的结构。二叉树(Binary Tree)是每个节点最多有两个子树的有序树。通常子树被称作"左子树"(left subtree)和"右子树"(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。值得注意的是,二叉树不是树的特殊情形。在图论中,二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根节点的度不大于2。有了根节点后,每个顶点定义了唯一的根节点,和最多2个子节点。然而,没有足够的信息来区分左节点和右节点。

(1)二叉树的存储结构

二叉树常用链式存储。如下图;

0d68ac913d8e20d6e38c8ff8af9f3f7e.png

最常用的是图a的二叉链表法。

(2)几种遍历

1.前序遍历(根左右)

递归定义;

1、访问根节点
2、前序遍历根节点的左子树
3、前序遍历根节点的右子树

2.中序遍历(左根右)
递归定义;

1、中序遍历根节点的左孩子
2、访问根节点
3、中序遍历根节点的右孩子

3.后序遍历(左右根)
递归定义;

1、后序遍历根节点的左子树
2、后序遍历根节点的右子树
3、访问根节点

4.层次遍历
对整棵二叉树按从上到下,对每一层按从左到右的顺序进行遍历。这里我们需要借助队列:先访问节点的孩子节点也应比后访问节点的孩子节点先访问。故这就类似于队列先进先出的性质,每访问一个节点时,它的孩子节点(如果存在)就随着入队。

(3)代码实现

下面的代码中我们提供了如下操作:

1、查找 find(),获取父节点 parent(),左孩子 lchild(),右孩子 rchild()。
2、各种遍历:前序、中序、后序、层次。
3、获取树的高度,节点总数。

代码说明

1、在代码中我们用到了STL中的栈和队列,因为具体的算法实现,需要用到它们。
如过看过栈的实现:顺序栈以及队列的实现:顺序队列,对栈和队列的常用操作应该不会陌生。
2、由于普通二叉树的插入、删除,并无规定操作。故把这些操作留到下一篇二叉搜索树中去实现。
代码有点长,有些代码被重复使用了,其中使用队列的层次遍历、查找父节点、左孩子、右孩子是重要的。
3、有些算法的实现确实比较复杂。
一些讲解附在代码后,可以针对解释再仔细看代码,并且针对各种遍历的非递归的实现也附在最后。

代码

类定义:

#include<iostream>
#include<iomanip>
#include<stack>
#include<queue>
using namespace std;
typedef int ElemType;
//二叉树节点
class BTNode   //Binary Tree Node
{
public:
	ElemType data;
	BTNode* lchild;   //左孩子
	BTNode* rchild;   //右孩子
	BTNode(ElemType d, BTNode* left=NULL, BTNode* right=NULL) 
		:data(d), lchild(left), rchild(right){}
};
//二叉树
class BinaryTree
{
private:
	//根节点指针
	BTNode* Root;
	//节点个数
	int size;
public:
	//构造函数
	BinaryTree();
	BTNode* buildTree();
	//析构函数
	~BinaryTree();
	//求树的高度
	int height();
	//获取节点个数
	int getSize()
	{return size;}
	void getHeight(BTNode*, int, int&);
	//指定遍历方式
	void traverse();
	//前序遍历
	void preOrderWithoutRecursion();
	void preOrder();
	void preorder(BTNode*);
	//中序遍历
	void inOrderWithoutRecursion();
	void inOrder();
	void inorder(BTNode*);
	//后序遍历
	void postOrderWithoutRecursion();
	void postOrder();
	void postorder(BTNode*);
	//层次遍历
	void levelOrder();
	//判断树是否为空
	bool empty()
	{return Root == NULL;}
	//获取根节点
	BTNode* root()
	{return Root;}
	//查找指定节点
	bool find(ElemType);
	//获取父节点
	BTNode* parent(ElemType);
	//获取左孩子
	BTNode* lchild(ElemType);
	//获取右孩子
	BTNode* rchild(ElemType);
};

类实现:

//构造函数
BinaryTree::BinaryTree()
{
	size = 0;
	cout << "输入根节点 ";
	Root = buildTree();
}
BTNode* BinaryTree::buildTree()
{
	ElemType data;
	BTNode* p = NULL;
	cin >> data;
	//输入0结束
	if (data == 0)
		return p;
	else
	{
		p = new BTNode(data);
		size++;
		if (!p)
		{
			cerr << "内存分配失败!" << endl;
			exit(0);
		}
		else
		{
			cout << "请输入 " << data << " 节点的左孩子 ";
			p->lchild = buildTree();
			cout << "请输入 " << data << " 节点的右孩子 ";
			p->rchild = buildTree();
		}
	}
	return p;
}
//析构函数
BinaryTree::~BinaryTree()
{
	queue<BTNode*> q;
	q.push(Root);
	BTNode* p = NULL;
	while (!q.empty())
	{
		p = q.front();
		q.pop();
		//左孩子不为空,则左孩子入队
		if (p->lchild)
			q.push(p->lchild);
		//右孩子不为空,则右孩子入队
		if (p->rchild)
			q.push(p->rchild);
		//释放内存
		delete p;
	}
}
//求树的高度
int BinaryTree::height()
{
	if (empty())
		return 0;
	int h = 0;
	getHeight(Root, 0, h);
	return h;
}
void BinaryTree::getHeight(BTNode* p, int level, int &h)
{
	if (p)
	{
		if (level > h)
			h = level;
		getHeight(p->lchild, level + 1, h);
		getHeight(p->rchild, level + 1, h);
	}
}
//指定遍历方式
void BinaryTree::traverse()
{
	int choice;
	cout << "输入遍历方式:0(前序)、1(中序)、2(后序)、3(层次):";
	cin >> choice;
	while (choice < 0 || choice > 3)
	{
		cout << "输的不对!请重新输入: ";
		cin >> choice;
	}
	switch(choice)
	{
	case 0:preOrder(); break;
	case 1:inOrder(); break;
	case 2:postOrder();break;
	case 3:levelOrder(); break;
	}
}
//前序遍历:根->左->右
void BinaryTree::preOrder()
{
	BTNode* pnode = Root;
	preorder(pnode);
	cout << endl;
}
void BinaryTree::preorder(BTNode* p)
{
	if (p)
	{
		cout << setw(4) << p->data;
		preorder(p->lchild);
		preorder(p->rchild);
	}
}
//中序遍历:左->根->右
void BinaryTree::inOrder()
{
	BTNode* pnode = Root;
	inorder(pnode);
	cout << endl;
}
void BinaryTree::inorder(BTNode* p)
{
	if (p)
	{
		inorder(p->lchild);
		cout << setw(4) << p->data;
		inorder(p->rchild);
	}
}
//后序遍历:左->右->根
void BinaryTree::postOrder()
{
	BTNode* pnode = Root;
	postorder(pnode);
	cout << endl;
}
void BinaryTree::postorder(BTNode* p)
{
	if (p)
	{
		postorder(p->lchild);
		postorder(p->rchild);
		cout << setw(4) << p->data;
	}
}
//层次遍历
void BinaryTree::levelOrder()
{
	//使用STL中的队列
	queue<BTNode*> q;
	//根节点入队
	q.push(Root);
	BTNode* p = NULL;
	while (!q.empty())
	{
		p = q.front();
		//打印
		cout << setw(4) << p->data;
		q.pop();
		//左孩子不为空,则左孩子入队
		if (p->lchild)
			q.push(p->lchild);
		//右孩子不为空,则右孩子入队
		if (p->rchild)
			q.push(p->rchild);
	}
	cout << endl;
}
//查找指定节点
bool BinaryTree::find(ElemType data)
{
	if (!empty())
	{
		//按层次遍历查找
		queue<BTNode*> q;
		q.push(Root);
		BTNode* p = NULL;
		while (!q.empty())
		{
			p = q.front();
			//比较
			if(p->data==data)
				return true;
			q.pop();
			if (p->lchild)
				q.push(p->lchild);
			if (p->rchild)
				q.push(p->rchild);
		}
	}
	//在树空和不存在该节点的情况下,都返回false
	return false;
}
//获取父节点
BTNode* BinaryTree::parent(ElemType data)
{
	if (!empty())
	{
		//根节点的父节点为空
		if (Root->data == data)
			return NULL;
		stack<BTNode*> s;
		BTNode* p = Root;
		while (!s.empty() || p)
		{
			if (p)
			{
				if ((p->lchild && p->lchild->data == data) || (p->rchild && p->rchild->data == data))
					return p;
				s.push(p);
				p = p->lchild;
			}
			else
			{//左子树访问完后,访问右子树
				p = s.top();
				s.pop();
				p = p->rchild;
			}
		}
	}
	return NULL;
}
//获取左孩子
BTNode* BinaryTree::lchild(ElemType data)
{
	if (!empty())
	{
		//按层次遍历查找
		queue<BTNode*> q;
		q.push(Root);
		BTNode* p = NULL;
		while (!q.empty())
		{
			p = q.front();
			//比较
			if (p->data == data)
				return p->lchild;
			q.pop();
			if (p->lchild)
				q.push(p->lchild);
			if (p->rchild)
				q.push(p->rchild);
		}
	}
	//在树空和不存在该节点的情况下,都返回NULL
	return NULL;
}
//获取右孩子
BTNode* BinaryTree::rchild(ElemType data)
{
	if (!empty())
	{
		//按层次遍历查找
		queue<BTNode*> q;
		q.push(Root);
		BTNode* p = NULL;
		while (!q.empty())
		{
			p = q.front();
			//比较
			if (p->data == data)
				return p->rchild;
			q.pop();
			if (p->lchild)
				q.push(p->lchild);
			if (p->rchild)
				q.push(p->rchild);
		}
	}
	//在树空和不存在该节点的情况下,都返回NULL
	return NULL;
}

主函数:

int main()
{
	cout << "******二叉树******" << endl;
	BinaryTree tree;
	cout << "树的高度是 " << tree.height() << endl;
	cout << "树中共有节点个数 " << tree.getSize() << endl;
	tree.traverse();
	ElemType data = 2;
	cout << "对节点 " << data << " 进行查询" << endl;
	cout << "存在于树中吗?";
	tree.find(data) ? cout << "Yes!" <<endl: cout << "No!" << endl;
	BTNode* p = NULL;
	p = tree.parent(data);
	p ? cout << "父节点是 " << p->data << endl : cout << data << "是根节点,故并无父节点" << endl;
	p = tree.lchild(data);
	p ? cout << "左孩子是 " << p->data << endl : cout << "无左孩子!" << endl;
	p = tree.rchild(data);
	p ? cout << "右孩子是 " << p->data << endl : cout << "无右孩子!" << endl;
	system("pause");
	return 0;
}

运行

ed99887fc0413d4022e5930c8bb31808.png

7e9b595f4742e8ac9a43871a2234af91.png

一些算法的解释

上述方法的实现基本上是两两组合:一个对外调用,另一个才是具体实现。有些比较复杂,涉及到递归,要想彻底弄清算法原理,得明白递归过程。

1.构造函数

构造函数的这种建树写法值得注意:buildTree()可是递归的。

2.析构函数

按层次遍历的过程来释放节点空间,关于层次遍历过程的理解可先看层次遍历的解释。当然,也可按其它的遍历形式来释放空间。

3.求树高度

对外调用是int height(),具体实现是void getHeight(BTNode* p,int level,int& h)特别注意:第三个参数是引用类型。

一个示例图如下:

ccf2ece969387b1462f5e692400c53ee.png

4.前、中、后序遍历的递归写法,比较简单,只要明白递归的过程,就很容易理解。难点在于它的非递归写法,若是能毫不费力地写出非递归形式,说明真的是对递归清楚掌握。

5.查找指定数据域的节点

查找时,按层次遍历的方式进行。若是有多个节点数据域符合,则查找最上层、最左边的。

6.获取父节点

本质上也是一种查找。仔细看代码,会发现它的查找过程其实是中序遍历的。结合下面的中序遍历的非递归写法,你会明白的。

7.获取指定数据域的孩子节点

它的查找用的也是层次遍历。当然不排除可用其它的遍历方法。

小结:对树的操作,基本上都是在遍历的基础上完成的。掌握各种遍历方式,也就是重中之重。

二、遍历算法的非递归实现

前序遍历非递归实现:

//前序遍历非递归算法
void BinaryTree::preOrderWithoutRecursion()
{
	if (!empty())
	{
		stack<BTNode*> s;
		BTNode* p = Root;
		s.push(p);
		while (!s.empty())
		{
			cout << setw(4) << p->data;
			if (p->rchild)
				s.push(p->rchild);
			if (p->lchild)
				p = p->lchild;
			else
			{//左子树访问完了,访问右子树
				p = s.top();
				s.pop();
			}
		}
		cout << endl;
	}
}

中序遍历非递归实现:

//中序遍历的非递归实现
void BinaryTree::inOrderWithoutRecursion()
{
	if (!empty())
	{
		stack<BTNode*> s;
		BTNode* p = Root;
		while (!s.empty() || p)
		{
			if (p)
			{//一直下降到最左边
				s.push(p);
				p = p->lchild;
			}
			else
			{
				p = s.top();
				s.pop();
				cout << setw(4) << p->data;
				p = p->rchild;
			}
		}
		cout << endl;
	}
}

后序遍历非递归实现:

//后序遍历的非递归实现
void BinaryTree::postOrderWithoutRecursion()
{
	if (!empty())
	{
		stack<BTNode*> s;
		BTNode* p = Root;
		while (p != NULL)//遍历到左边最下边  
		{
			s.push(p);
			p = p->lchild;
		}
		while (!s.empty())
		{
			BTNode* pLastVisit = p;  //判断当前该访问左节点还是右节点  
 
			p = s.top();
			s.pop();
 
			if (p->rchild == NULL || p->rchild == pLastVisit)
				cout << setw(4) << p->data;
			else if (p->lchild == pLastVisit)
			{
				s.push(p);
				p = p->rchild;
				s.push(p);
				while (p != NULL)
				{
					if (p->lchild != NULL)
					{
						s.push(p->lchild);
					}
					p = p->lchild;
				}
			}
		}
		cout << endl;
	}
}

首先恭喜您,能够认真的阅读到这里,如果对部分理解不太明白,建议先将文章收藏起来,然后对不清楚的知识点进行查阅,然后在进行阅读,相应你会有更深的认知。如果您喜欢这篇文章,就点个赞或者【关注我】吧!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值