利用栈循环遍历二叉树

在阐述我的思路之前,我们先来看看递归遍历二叉树到底发生了什么,以中序遍历为例:

void InOrder(BinaryTreeNode<T>* node)
{
    if(!node)return;//第一行
    InOrder(node->leftChild);//第二行
    cout << node->data << "  ";//第三行
    InOrder(node->rightChild);//第四行
}

1:我们将节点传入这个函数后,执行第二行代码的时候程序先将当前的node压栈,然后将node的左子树传入一个新的InOrder函数

2:在新的左子树中,如果node为空,则将刚才压栈的node弹出并输出其data,并将node的左子树传入一个新的InOrder函数,执行1,否则,执行1。

简单总结一下,循环遍历函数需要做的只有以下几件事:将节点压栈,将节点弹栈,判断节点是否为空,输出节点的data。当然,还有一个最重要的环节:控制函数在什么时候执行上述四件事。

对函数的控制环节,我们采用标记的方法来记录函数当前需要做什么,下一步需要做什么。具体标记值如下:

00  什么也不做,即该节点已经访问完毕

01  输出该节点的值

10  将节点的左子树压栈

11  将节点的右子树压栈

所以,我们可以用一个统一的函数来描述这个过程:

template<typename T>
void BinarySearchTree<T>::Order(const char _flag)
{
	stack.Push(root, _flag);//0001 1110
	BinaryTreeNode<T>* p;
	char flag;
	while (!stack.IsEmpty())
	{
		stack.Pop(p, flag);
		if (!p)continue;
		switch (flag & 0x03)
		{
		case 0:
			break;
		case 1:
			cout << p->data << " ";
			stack.Push(p, flag >> 2);
			break;
		case 2:
			stack.Push(p, flag >> 2);
			stack.Push(p->leftChild, _flag);
			break;
		case 3:
			stack.Push(p, flag >> 2);
			stack.Push(p->rightChild, _flag);
			break;
		}
	}
	cout << endl;
}

栈的结构我会在稍后做介绍。利用这个函数,我们只需要传入一个_flag就可以决定对二叉树进行何种方式进行遍历,例如,中序遍历为0x36(00110110),先序遍历为0x39(00111001),后序遍历为0x1e(00011110)。只有在节点弹栈后flag的最后两位为00时,我们才确定该节点已经彻底处理完毕,不再对其进行处理,否则我们都将该节点进行压栈,并修改其对应的flag值。当然,这个函数存在一定的效率问题,可以利用这个原理将函数进行优化,分别写出对应的先序、中序、后序遍历函数。

现在我们看一下栈的相关结构。由于遍历函数需要频繁的进行弹栈与压栈,并且二叉树的节点数量我们不确定,所以我采用了链表的思路并在链表的基础上做了一点修改。每一个节点可以存储十个data,当节点存满时,新建一个节点;当节点为空时,删除该节点。栈与栈节点的代码如下:

template <typename TreeNode>
class OrderListNode 
{
	friend class OrderList<TreeNode>;
public:
	OrderListNode() { currentData = 0; }
protected:
	OrderListNode<TreeNode>* link;
	TreeNode data[10];
	int currentData;
	char flag[10];
};

template <typename TreeNode>
class OrderList
{
public:
	OrderList();
	~OrderList();
	virtual OrderList<TreeNode>& Push(__in TreeNode _data, __in char _flag);
	virtual OrderList<TreeNode>& Pop(__out TreeNode &_data, __out char& _flag);
	virtual void Output();
	virtual bool IsEmpty() { return top == nullptr; }
protected:
	OrderListNode<TreeNode>* top;
};

对应的.cpp为:

template <typename TreeNode>
OrderList<TreeNode>::OrderList()
{
	top = nullptr;
}

template<typename TreeNode>
OrderList<TreeNode>::~OrderList()
{
	OrderListNode<TreeNode>* p = top;
	while (p)
	{
		p = top->link;
		delete top;
		top = p;
	}
}

template<typename TreeNode>
OrderList<TreeNode>& OrderList<TreeNode>::Push(__in TreeNode _data, __in char _flag)
{
	if (!top)
	{
		top = new OrderListNode<TreeNode>;
		top->link = nullptr;
	}
	if (top->currentData >= 9)
	{
		OrderListNode<TreeNode>* p = new OrderListNode<TreeNode>;
		p->link = top;
		top = p;
	}	
	top->data[top->currentData] = _data;
	top->flag[top->currentData] = _flag;
	top->currentData++;
	return *this;
}

template<typename TreeNode>
OrderList<TreeNode>& OrderList<TreeNode>::Pop(__out TreeNode &_data, __out char& _flag)
{
	if (!top)
		return *this;
	_data = top->data[--top->currentData];
	_flag = top->flag[top->currentData];
	if (top->currentData == 0)
	{
		OrderListNode<TreeNode>* p = top->link;
		delete top;
		top = p;
	}
	return *this;
}

template<typename TreeNode>
void OrderList<TreeNode>::Output()
{
	OrderListNode<TreeNode>*p = top;
	while (p)
	{
		for (int i = 0; i < p->currentData; ++i)
			cout << p->data[i] << " ";
		p = p->link;
	}
	cout << endl;
}


在文章的最后,我想与诸位一起探讨一种新的二叉树删除的方法:采用二级指针对节点进行标记,用这种方法可以减少一些分支语句,即不需要判断删除的是否为根节点,以及要删除的是某一节点的左子树还是右子树,代码由鄙人独立设计,引用或复制还望诸位加以备注,如有不足或更好的方法,也望多多指教。代码如下:

template <typename T>
BinarySearchTree<T>& BinarySearchTree<T>::Delete(__in const T _data)
{
	if (!root)
		return *this;
	BinaryTreeNode<T> **pCurrent = &root;
	while ((*pCurrent) && (_data != (*pCurrent)->data))
	{
		if (_data < (*pCurrent)->data)
			pCurrent = &((*pCurrent)->leftChild);
		else
			pCurrent = &((*pCurrent)->rightChild);
	}
	if (!(*pCurrent))
	{
		cout << "Find Nothing\n";
		return *this;
	}
	BinaryTreeNode<T>* remove = (*pCurrent);
	if (!remove->leftChild)//leftchild is nullptr
	{
		if (!remove->rightChild)//remove has no child
		{
			delete remove;
			(*pCurrent) = nullptr;
			return *this;
		}
		(*pCurrent) = remove->rightChild;
		delete remove;
		return *this;
	}
	if (!remove->rightChild)//only rightchild is null
	{
		(*pCurrent) = remove->leftChild;
		delete remove;
		return *this;
	}
	//remove has two children
	BinaryTreeNode<T> **temp = &(remove->leftChild);
	while (!(*temp)->rightChild)
	{
		temp = &(*temp)->rightChild;
	}
	(*pCurrent) = (*temp);
	(*temp)->leftChild = remove->leftChild;
	(*temp)->rightChild = remove->rightChild;
	temp = nullptr;
	delete remove;
	return *this;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值