C++二叉树与线索二叉树

C++二叉树与线索二叉树

二叉树

1.学过数据结构的朋友应该都知道二叉树吧。 在这里插入图片描述
这是一颗满二叉树,原理自己看书去,了解原理才能够更好的理解代码。我就不在这说原理了。
上代码。

构造一棵二叉树(左子树先构造)

1.这个是模板函数,模板函数的作用是你给定一个类型参数就行了,提高代码复用。就不用为了一个类型重载一个函数啦。

template<typename T>
void Tree<T>::CreateTree(TreeNode<T> *(&Node), list<T> &val)//利用list中栈的原理配置二叉树
{
	if (val.size())
	{
		auto beg = val.begin();
		T c = *beg;
		if (c == ' ')//表示该节点为空,他的父节点没有该叶子节点
		{
			val.pop_front();
			Node = NULL;
		}
		else
		{
			Node = new TreeNode<T>;
			Node->LChild = NULL;
			Node->RChild = NULL;
			Node->Ltag = LINK;//这个是线索树的标记
			Node->Rtag = LINK;//这个是线索树的标记
			Node->data = c;
			val.pop_front();//将顶层数据弹出

			CreateTree(Node->LChild, val);//构造Node的左子树
			CreateTree(Node->RChild, val);//同理构造右子树
		}
	}
}
/*
	list<T>:构造二叉树的原始数据;
	Node:一般是树的根Root;
	通过递归的方式先构造左子树再构造右子树。
*/

在这里插入图片描述
注释:LC=要调用CreateTree(Node->LChild, val);
RC=CreateTree(Node->RChild, val);但凡节点的孩子为空都是用LC,和RC替换。
如果c==’ ',表示该节点为空。

二叉树的非递归遍历

由于递归遍历书上就有,所以不多做解释。
中序遍历的规则:先访问左节点,到根节点,再到右节点,

template<typename T>
void Tree<T>::StackInOrderPrint(TreeNode<T> *Node, stack<TreeNode<T>*> &val)//非递归中序遍历
{
	while (Node || val.size())//存在二叉树时进行遍历
	{
		//当节点存在先入栈,将其左节点地址赋值给Node,直至遍历至最左边的叶子节点
		while (Node)//1,2,3步
		{
			val.push(Node);
			Node = Node->LChild;
		}

		if (val.size())//4,5步
		{
			//当Node为空时,也就是栈顶元素节点的左子树为空时,弹出栈顶元素。
			//Node指向弹出的栈顶元素,打印出数据值,再对其右子树进行遍历。
			Node = val.top();//过去栈顶元素地址
			val.pop();//弹出栈顶元素
			cout << Node->data << " ";
			Node = Node->RChild;
		}
	}
}

/*
	利用压栈以及出栈的方式进行遍历
*/

在这里插入图片描述
到此基本结束了,关闭时要记得delete内存,不要让内存泄露了。

线索二叉树

1.所谓线索二叉树就是提升查找节点前驱和后继的效率,牺牲一点内存,但是充分利用了空指针域。

enum { LINK, CLUES };//LINK表示链接的孩子节点,CLUES链接的是线索节点
template<typename T>
void Tree<T>::CreateCluesHeadNode()//创建一个标记节点,标记作用当前节点的前驱。
{
	if (HeadNode)
	{
		m_PreCluesNode = new TreeNode<T>;
		m_PreCluesNode->Ltag = LINK;//LINK是指向孩子节点
		m_PreCluesNode->Rtag = LINK;
		m_PreCluesNode->RChild = m_PreCluesNode;//指向自己是防止第一次构造线索树的时候出现乱指的问题
		m_PreCluesNode->LChild = HeadNode;
		m_TempCluesNode = m_PreCluesNode;//让中间节点指向线索头节点,在构造线索树中用中间节点,是防止m_PreCluesNode在线索化时地址被篡改,无法被正确析构。
	}
}
/*
	Build一个头指针m_PreCluesNode,使其左孩子指向树的根部。而右孩子指向自己。防止第					一次线索的时候会指向树的左子树的最左叶子节点。
*/	
template<typename T>
TreeNode<T>* Tree<T>::BuildCluesTree(TreeNode<T> *Node)
{
	if(Node)
	{
		BuildCluesTree(Node->LChild);//一直递归到左子树的叶子节点
		
		if (!Node->LChild)//如果Node节点没有左孩子
		{
			Node->Ltag = CLUES;//将标记设置为线索
			Node->LChild = m_TempCluesNode;//左孩子节点指向前一个节点。
		}

		if (!m_TempCluesNode->RChild)//前一个节点的右孩子为空时指向Node节点
		{
			m_TempCluesNode->Rtag = CLUES;
			m_TempCluesNode->RChild = Node;
		}

		m_TempCluesNode = Node;
		BuildCluesTree(Node->RChild);//以右孩子节点作为根节点递归
	}
	return Node;
}

源码:
编译环境:VS2017

//Tree.h
#pragma once
#include<list>
#include<stack>
using namespace std;
enum { LINK, CLUES };//LINK表示链接的孩子节点,CLUES链接的是线索节点

template<typename T>
struct TreeNode
{
	TreeNode<T> *LChild;
	TreeNode<T> *RChild;
	int Ltag;
	int Rtag;
	T data;
};

template<typename T>
class Tree
{
public:
	Tree();
	Tree(list<T> &val);
	~Tree();
	void CreateTree(TreeNode<T> *(&Node), list<T> &val);//创建一个二叉树
	void CreateCluesHeadNode();//创建标记节点记录当前节点的前驱。
	TreeNode<T>* BuildCluesTree(TreeNode<T> *Node);//创建线索树,要先创建标记节点,
	void AddLNode(Tree<T> *Node, T c);//扩充左孩子
	void AddRNode(Tree<T> *Node, T c);//扩充右孩子
	void StackInOrderPrint(TreeNode<T> *, stack<TreeNode<T>*> &val);//非递归中序遍历
	void InOrderPrint(TreeNode<T> *(&));//递归中序遍历
	void Release(TreeNode<T> *(&Node));//释放资源
public:
	TreeNode<T> *HeadNode;//头节点
	TreeNode<T> *m_PreCluesNode;//前驱节点
	TreeNode<T> *m_TempCluesNode;//中间节点,无需析构。
};

template<typename T>
Tree<T>::Tree()
{
}

template<typename T>
Tree<T>::Tree(list<T> &val)
{
	CreateTree(HeadNode, val);
}

template<typename T>
Tree<T>::~Tree()
{
	if (HeadNode)
	{
		Release(HeadNode);
	}

	if (m_PreCluesNode)
	{
		delete m_PreCluesNode;
		m_PreCluesNode = NULL;
	}
}

template<typename T>
void Tree<T>::Release(TreeNode<T> *(&Node))
{
	if (!Node) return;
	TreeNode<T> *LChild=NULL;
	TreeNode<T> *RChild=NULL;
	if (Node->Ltag == LINK)
	{
		LChild = Node->LChild;
	}

	if (Node->Rtag == LINK)
	{
		RChild = Node->RChild;
	}

	if (Node)
	{
		cout << "删除了:" << Node->data << endl;
		delete Node;
		Node = NULL;
	}

	if (LChild)
	{
		Release(LChild);
	}

	if (RChild)
	{
		Release(RChild);
	}
}

template<typename T>
void Tree<T>::CreateTree(TreeNode<T> *(&Node), list<T> &val)//利用list中栈的原理配置二叉树
{
	if (val.size())
	{
		auto beg = val.begin();
		T c = *beg;
		if (c == ' ')
		{
			val.pop_front();
			Node = NULL;
		}
		else
		{
			Node = new TreeNode<T>;
			Node->LChild = NULL;
			Node->RChild = NULL;
			Node->Ltag = LINK;
			Node->Rtag = LINK;
			Node->data = c;
			val.pop_front();

			CreateTree(Node->LChild, val);
			CreateTree(Node->RChild, val);
		}
	}
}
template<typename T>
void Tree<T>::StackInOrderPrint(TreeNode<T> *Node, stack<TreeNode<T>*> &val)//非递归中序遍历
{
	while (Node || val.size())
	{
		while (Node)
		{
			val.push(Node);
			Node = Node->LChild;
		}

		if (val.size())
		{
			Node = val.top();
			val.pop();
			cout << Node->data << " ";
			Node = Node->RChild;
		}
	}
}

template<typename T>
void Tree<T>::AddLNode(Tree<T> *Node, T c)
{

}

template<typename T>
void Tree<T>::AddRNode(Tree<T> *Node, T c)
{

}

template<typename T>
void Tree<T>::InOrderPrint(TreeNode<T> *(&tree))//递归调用
{
	if (tree)
	{
		InOrderPrint(tree->LChild);
		cout << tree->data << " ";
		InOrderPrint(tree->RChild);
	}
}

template<typename T>
TreeNode<T>* Tree<T>::BuildCluesTree(TreeNode<T> *Node)
{
	if(Node)
	{
		BuildCluesTree(Node->LChild);//一直递归到左子树的叶子节点
		
		if (!Node->LChild)//如果Node节点没有左孩子
		{
			Node->Ltag = CLUES;//将标记设置为线索
			Node->LChild = m_TempCluesNode;//左孩子节点指向前一个节点。
		}

		if (!m_TempCluesNode->RChild)//前一个节点的右孩子为空时指向Node节点
		{
			m_TempCluesNode->Rtag = CLUES;
			m_TempCluesNode->RChild = Node;
		}

		m_TempCluesNode = Node;
		BuildCluesTree(Node->RChild);//以右孩子节点作为根节点递归
	}
	return Node;
}

template<typename T>
void Tree<T>::CreateCluesHeadNode()//创建一个标记节点,标记作用当前节点的前驱。
{
	if (HeadNode)
	{
		m_PreCluesNode = new TreeNode<T>;
		m_PreCluesNode->Ltag = LINK;
		m_PreCluesNode->Rtag = LINK;
		m_PreCluesNode->RChild = m_PreCluesNode;//指向自己是防止第一次构造线索树的时候出现乱指的问题
		m_PreCluesNode->LChild = HeadNode;
		m_TempCluesNode = m_PreCluesNode;//让中间节点指向前驱节点,在构造线索树中用中间节点,是防止析构的时候m_PreCluesNode内存改变而无法析构
	}
}
//.cpp
/*
	1.如果你要在函数体中new一个对象的话记得要传引用指针(*(&)或者返回一个指针,
	不然你new的内存就会找不到了,会导致内存泄露。
*/

#include<iostream>
#include"Tree.h"
using namespace std;

int main()
{
	list<char> val{'A','B','D',' ',' ','E',' ',' ','C','F',' ',' ','G',' ',' '};
	Tree<char> *tree;
	stack<TreeNode<char>*> NodeArray;
	tree = new Tree<char>;
	tree->CreateTree(tree->HeadNode,val);
	tree->InOrderPrint(tree->HeadNode);
	cout << endl;
	tree->StackInOrderPrint(tree->HeadNode, NodeArray);
	cout << endl;
	tree->CreateCluesHeadNode();
	tree->BuildCluesTree(tree->HeadNode);
	delete tree;
	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值