二叉树的实现

树的概念:树是NN>=0)个有限数组元素的集合,形状像一棵倒过来的树

节点:节点包含数据和指向其他节点的指针,树的第一个节点称为根节点

:节点拥有的子节点的数

树的高度:树中距离根节点最远节点的路径


二叉树:二叉树是一棵特殊的数,二叉树每个节点最多有两个孩子节点,为左孩子和有孩子。满二叉树:高度为N的慢二叉树有2*N-1个节点的二叉树

完全二叉树:若二叉树的深度为H,除第H层以外,其他各层的节点都达到最大个数,第H层的所有节点都连续集中在最左边。

二叉树的实现:

节点构造:

template <class T>
struct BinaryTreeNode
{
	BinaryTreeNode<T>* _left;
	BinaryTreeNode<T>* _right;
	T _data;

	BinaryTreeNode(const T& x)
		: _left(NULL)
		, _right(NULL)
		, _data(x)
		{}
};


前序遍历

前序遍历过程:a.先访问根节点b.前序访问左子树c.前序访问右子树

A.递归前序遍历:优点:代码量少,易实现。缺点:难理解,建议画图理解(与栈帧联系紧密)

void _PrevOrder(Node* root)
	{
		if (root == NULL)
			return;
		cout << root->_data << " ";
		_PrevOrder(root->_left);
		_PrevOrder(root->_right);
	}
先访问根节点,输出节点的值,再访问左子树,遇空返回,访问右子树。每次的递归都可以认为是访问子树的根节点并输出。

B.非递归前序遍历:利用栈的后进先出的原则,实现前序遍历。

PS:节点插入栈的顺序便是前序遍历过程。


void PrevOrderNonR()
	{
		stack<Node*> s;
		Node* cur = _root;
		while (cur || !s.empty())
		{
			while (cur)
			{
				cout << cur->_data << " ";
				s.push(cur);
				cur = cur->_left;
			}

			Node* top = s.top();
			s.pop();
			cur = top->_right;
		}
		cout << endl;
	}


优先访问左子树,并将左子树的根节点存入栈,判断节点和栈是否为空,进行循环,输出节点的值,每当内层循环退出时,代表该子树的左子树访问完,此时取出栈顶节点,并释放栈顶,栈顶节点为该子树的根节点,此时继续外层循环,重复上述动作,实现非递归遍历。

中序遍历

中序遍历过程:a.中序访问左子树b.访问根节点c.中序访问访问右子树

A.递归中序遍历:优点:代码量少,易实现。缺点:难理解,建议画图理解(与栈帧联系紧密)

递归时传入的左孩子都可以被认为是该子树的根节点,当递归到最左节点时判空退出本次递归,输出当前节点的值,带入当前节点的右孩子,继续递归。

void _InOrder(Node* root)
	{
		if (root == NULL)
			return;
		_InOrder(root->_left);
		cout << root->_data << " ";
		_InOrder(root->_right);
	}

B.非递归中序遍历:利用栈的后进先出原则实现遍历。

PS:从栈顶取出数据的顺序便是中序遍历的顺序

void InOrderNonR()
{
stack<Node*> s;
Node* cur = _root;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
cur = cur->_left;
}


Node* top = s.top();
s.pop();
cout << top->_data << " ";


cur = top->_right;
}
cout << endl;
}

访问当前节点,存入栈中,判断当前节点和栈是否为空,进行循环,内层循环退出时取出栈顶,输出栈顶值,释放栈顶,设当前节点指向右孩子,进行外层循环,重复上述过程。

后序遍历

后续遍历过程:a.后序访问左子树b.后序访问右子树c.访问根节点

后序遍历过程中,根节点会被访问多次,因此递归的方法难以实现,只写出了非递归的后序遍历。

A.递归后序遍历

void _PostOrder(Node* root)
	{
		if (root == NULL)
			return;
		_PostOrder(root->_left);
		_PostOrder(root->_right);
		cout << root->_data << " ";
	}

原理类似前序和后序递归遍历。


B.非递归后序遍历:利用栈的后进先出原则实现遍历。

PS:非递归后序遍历难点在于,由于子树的根节点会被访问多次,因此在输出节点值和释放栈顶是应进行判断是否执行上述操作。

void PostOrder()
	{
		stack<Node*> s;
		Node* prev = NULL;
		Node* cur = _root;
		
		while (cur || !s.empty())
		{
			while (cur)
			{
				s.push(cur);
				cur = cur->_left;
			}
			Node* front = s.top();
			if (front->_right == NULL || front->_right == prev)
			{
				cout << front->_data << " ";
				prev = front;
				s.pop();
			}
			else
			{
				cur = front->_right;
			}
		}
		cout << endl;
	}

设置父亲节点变量保存前次访问的节点指针,当内层循环结束时,当前节点为最左节点,再此取出栈顶并进行判断,节点右节点为空或者右节点已被访问过,便可以输出并释放栈顶,继续外层循环。

层序遍历

层序遍历过程:a.访问根节点b.访问下一层所有节点C.....

void LevelOrder()
	{
		queue<Node*> q;
		if (_root)
			q.push(_root);
		while (!q.empty())
		{
			Node* front = q.front();
			cout << front->_data << " ";
			if (front->_left)
				q.push(front->_left);
			if (front->_right)
				q.push(front->_right);

			q.pop();
		}
		cout << endl;
	}

利用队列的先进先出原则实现遍历。循环开始时先取出队头,输出当前的节点值,每次根据条件,尾插入当前节点的左孩子和右孩子。循环结束时释放队列的头,以队列是否为空为循环结束的条件。

二叉树整体代码实现如下:

#pragma once 
#include <queue>
#include <stack>
using namespace std;

template <class T>
struct BinaryTreeNode
{
	BinaryTreeNode<T>* _left;
	BinaryTreeNode<T>* _right;
	T _data;

	BinaryTreeNode(const T& x)
		: _left(NULL)
		, _right(NULL)
		, _data(x)
		{}
};

template <class T>
class BinaryTree
{
	typedef BinaryTreeNode<T> Node;
public:
	BinaryTree()
		: _root(NULL)
	{}
	BinaryTree(T* a, size_t n, const T& invalid)
	{
		size_t index = 0;
		_root = CreateTree(a, n, invalid, index);
	}
	BinaryTree(const BinaryTree<T>& t)
	{
		_root = Copy(t._root);
	}
	Node* Copy(Node* root)
	{
		if (root == NULL)
			return NULL;
		Node* newNode = new Node(root->_data);
		newNode->_left = Copy(root->_left);
		newNode->_right = Copy(root->_right);
		return newNode;
	}
	~BinaryTree()
	{
		Destroy(_root);
	}
	void Destroy(Node* root)
	{
		if (root == NULL)
			return;
		Destroy(root->_left);
		Destroy(root->_right);

		delete root;
	}
	void PrevOrder()
	{
		_PrevOrder(_root);
		cout << endl;
	}
	void PrevOrderNonR()
	{
		stack<Node*> s;
		Node* cur = _root;
		while (cur || !s.empty())
		{
			while (cur)
			{
				cout << cur->_data << " ";
				s.push(cur);
				cur = cur->_left;
			}

			Node* top = s.top();
			s.pop();
			cur = top->_right;
		}
		cout << endl;
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	void InOrderNonR()
	{
		stack<Node*> s;
		Node* cur = _root;
		while (cur || !s.empty())
		{
			while (cur)
			{
				s.push(cur);
				cur = cur->_left;
			}

			Node* top = s.top();
			s.pop();
			cout << top->_data << " ";

			cur = top->_right;
		}
		cout << endl;
	}
	void PostOrder()
	{
		_PostOrder(_root);
		cout << endl;
	}
	void PostOrderNonR()
	{
		stack<Node*> s;
		Node* prev = NULL;
		Node* cur = _root;
		
		while (cur || !s.empty())
		{
			while (cur)
			{
				s.push(cur);
				cur = cur->_left;
			}
			Node* front = s.top();
			if (front->_right == NULL || front->_right == prev)
			{
				cout << front->_data << " ";
				prev = front;
				s.pop();
			}
			else
			{
				cur = front->_right;
			}
		}
		cout << endl;
	}
	//层序遍历
	void LevelOrder()
	{
		queue<Node*> q;
		if (_root)
			q.push(_root);
		while (!q.empty())
		{
			Node* front = q.front();
			cout << front->_data << " ";
			if (front->_left)
				q.push(front->_left);
			if (front->_right)
				q.push(front->_right);

			q.pop();
		}
		cout << endl;
	}
	Node* Find(const T& x)
	{
		return _Find(_root, x);
	}
	size_t Size()
	{
		return _Size(_root);
	}
	size_t LeafSize()
	{
		return _LeafSize(_root);
	}
	size_t Depth()
	{
		return _Depth(_root);
	}
	size_t KLeafSize(size_t k)
	{
		return _KLeafSize(_root, k);
	}
protected:
	size_t _KLeafSize(Node* root,size_t k)
	{
		if (root == NULL)
			return 0;
		if (k == 1)
			return 1;
		return _KLeafSize(root->_left, k - 1) + _KLeafSize(root->_right, k - 1);
	}
	size_t _Depth(Node* root)
	{
		if (root == NULL)
			return 0;
		if (root->_left == NULL&&root->_right == NULL)
			return 1;
		size_t left = _Depth(root->_left);
		size_t right = _Depth(root->_right);

		return left > right ? left + 1 : right + 1;
	}
	size_t _LeafSize(Node* root)
	{
		if (root == NULL)
			return 0;
		if (root->_left == NULL && root->_right == NULL)
			return 1;

		return _LeafSize(root->_left) + _LeafSize(root->_right);
	}
	size_t _Size(Node* root)
	{
		if (root == NULL)
			return 0;
		return _Size(root->_left) + _Size(root->_right) + 1;
	}
	Node* _Find(Node* root, const T& x)
	{
		if (root == NULL)
			return NULL;
		if (root->_data == x)
			return root;
		Node* ret = _Find(root->_left, x);
		if (ret)
			return ret;
		return _Find(roo->_right, x);
	}
	void _PrevOrder(Node* root)
	{
		if (root == NULL)
			return;
		cout << root->_data << " ";
		_PrevOrder(root->_left);
		_PrevOrder(root->_right);
	}
	void _InOrder(Node* root)
	{
		if (root == NULL)
			return;
		_InOrder(root->_left);
		cout << root->_data << " ";
		_InOrder(root->_right);
	}
	void _PostOrder(Node* root)
	{
		if (root == NULL)
			return;
		_PostOrder(root->_left);
		_PostOrder(root->_right);
		cout << root->_data << " ";
	}

	Node* CreateTree(T* a,size_t n,const T& invalid,size_t& index)
	{
		Node* root = NULL;

		if (index < n && a[index] != invalid)
		{
			root = new Node(a[index]);
			root->_left = CreateTree(a, n, invalid, ++index);
			root->_right = CreateTree(a, n, invalid, ++index);
		}
		return root;
	}

protected:
	Node* _root;
};

void TestBinaryTreeThd()
{
	int array[10] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6 };
	BinaryTree<int> t1(array, sizeof(array) / sizeof(array[0]), '#');
	t1.PrevOrder();				//递归前序遍历
	t1.PrevOrderNonR();			//非递归前序遍历
	t1.InOrder();				//递归中序遍历
	t1.InOrderNonR();			//非递归中序遍历
	t1.PostOrder();				//递归后序遍历
	t1.PostOrderNonR();			//非递归后序遍历
	t1.LevelOrder();			//层序遍历

	cout << t1.Size() << endl;  //树的大小
	cout << t1.LeafSize() << endl;//叶子大小
	cout << t1.Depth() << endl; //树的深度
	cout << t1.KLeafSize(3) << endl;//第3层叶子数

	BinaryTree<int> t2(t1);		//拷贝构造
	t2.PrevOrder();
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值