1. 程式人生 > >關於二叉樹的幾個基本操作

關於二叉樹的幾個基本操作

//Geeksun 2018.05.13 輸入樣例 AB#D##C## 詳情見擴充套件二叉樹
#include <iostream>
using namespace std;
enum flag{Child = 0,Thread = 1};//兩種列舉型別,表示該節點的指標是指向下一個節點,還是作為線索。
struct node
{
	char data;
	struct node *leftChild, *rightChild;
	flag ltag, rtag;
};
class Tree	
{
public:
	Tree()
	{
		this->root = treeCreat(getRoot());//此處需要在類裡再定義一個建立樹的函式,因為建構函式不能被遞迴呼叫。
		pre = NULL;
		threadTreeCreat(getRoot());
	 }
	Tree(const Tree& tree)//複製建構函式
	{
		this->root = treeCopy(this->root,tree.root);
	}
	~Tree()
	{
		destroyTree(getRoot());
	}
	void destroyTree(struct node* node);//析構整個樹
	bool isCompleteBinaryTree(struct node* node);//判斷是否為完全二叉樹
	void threadInorder_travelsal(struct node* node);//根據線索二叉樹中序遍歷
	void deleteNodeTree(char data, struct node* node, struct node* pre);刪除子樹
	bool searchNode(char data, struct node* node, struct node*& cur, bool &isFind);//查詢某個節點
	struct node** arrayReserve(struct node* node,int& count);//將樹按中序儲存在陣列中
	struct node* getRoot()
	{
		return root;
	}
private:
	struct node* root;
	struct node* pre;
	struct node* treeCreat(struct node* node);
	struct node* treeCopy(struct node* son_node,struct node* father_node);
	struct node* threadNext(struct node* p);//中序遍歷時查詢後繼節點的位置
	void threadTreeCreat(struct node* node);
};
bool Tree::isCompleteBinaryTree(struct node* node)
{
	int head = 0, tail = 0;
	struct node* queue[20];
	if (!node)
	{
		return false;
	}
	queue[tail++] = node;
	while (head != tail)
	{
		struct node* p;
		p = queue[head++ % 20];
		if (p->leftChild != NULL&&p->rightChild != NULL)//若左孩子,右孩子不為空則直接入佇列
		{
			queue[tail++ % 20] = p->leftChild;
			queue[tail++ % 20] = p->rightChild;
		}
		if (p->leftChild == NULL&&p->rightChild != NULL)//若有右孩子沒有左孩子則不是完全二叉樹
		{
			return false;
		}
		if (p->leftChild != NULL && p->rightChild == NULL|| p->leftChild == NULL && p->rightChild == NULL)//若左孩子和右孩子存在或左孩子和右孩子都不存在,則在此節點後的全是葉子結點
		{
			while (head != tail)
			{
				p = queue[head];
				if (p->leftChild == NULL && p->rightChild == NULL)
				{
					head++;
				}
				else
				{
					return false;
				}
			}
			return true;
		}
	}
	return true;
}
struct node* Tree::treeCopy(struct node* son_node, struct node* father_node)
{
	if (father_node)
	{
		son_node = new struct node;
		son_node->data = father_node->data;
		son_node->leftChild = treeCopy(son_node->leftChild, father_node->leftChild);//此處的struct node* son_node 可以用引用,因為要修改它的指向,也可以在修改後將它傳出來,覆蓋之前的指標。
		son_node->rightChild = treeCopy(son_node->rightChild, father_node->rightChild);
	}
	else
	{
		son_node = NULL;//此處一定要加上新節點為空,不然它就是一個野指標了。
	}
	return son_node;
}
bool Tree::searchNode(char data, struct node* node, struct node*& cur,bool &isFind)//此處isFind傳引用較為新穎,只要isFind被修改為True,那麼就可以知道,在節點中有該資料。
{
	if (!node)
	{
		return isFind;
	}
	if (node->data == data)
	{
		cur = node;
		isFind = true;
		return isFind;
	}
	searchNode(data, node->leftChild, cur, isFind);
	searchNode(data, node->rightChild, cur, isFind);
	return isFind;
}
void Tree::deleteNodeTree(char data,struct node* node,struct node* pre)
{
	if (node != NULL)
	{
		if (node->data == data && node != getRoot())//如果刪除的不是根節點
		{ 
			if (pre->leftChild == node) //防止刪除節點的父節點的指標懸空
				pre->leftChild = NULL;
			else if (pre->rightChild == node) 
				pre->rightChild = NULL;
			destroyTree(node);
			return;
		}
		else if (node->data == data)
		{
			destroyTree(node);
			return;
		}
		if(node->ltag == 0)
			deleteNodeTree(data,node->leftChild,node);
		if(node->rtag == 0)
			deleteNodeTree(data,node->rightChild,node);
	}
}
void Tree::threadInorder_travelsal(struct node* node)
{
	struct node* p;
	p = node;
	if (!p)
	{
		return;
	}
	while (p->ltag == 0)
	{
		p = p->leftChild;//先找到樹最左下方的節點。
	}
	cout << p->data;
	while (p->rightChild != NULL)
	{
		p = threadNext(p);
		cout << p->data;
	}
}
struct node** Tree::arrayReserve(struct node* node,int &count)
{
	int head = 0, tail = 0;
	count = 0;
	struct node**  array = new struct node*[20];//這裡需要在堆中建立陣列,因為如果在棧中建立陣列,當函式結束後array中的資料都會消失,而堆中資料需自己清空。
	struct node* queue[20];
	if (!node)
	{
		return NULL;
	}
	queue[tail++ % 20] = node;
	while (head != tail)
	{
		struct node* q = queue[head++];
		array[count++] = q;
		if (q->leftChild != NULL) queue[tail++] = q->leftChild;
		if (q->rightChild != NULL) queue[tail++] = q->rightChild;
	}
	return array;
}
struct node* Tree::threadNext(struct node* p)
{
	struct node* q;
	if (p->rtag == 1)
		q = p->rightChild;
	else
	{
		q = p->rightChild;//此處p的右孩子不可能為空,若為空則他的rtag一定為1
		while (q->ltag == 0)
		{
			q = q->leftChild;
		}
	}
	return q;
}
void Tree::threadTreeCreat(struct node* node)
{
	if (node == NULL)
		return;
	threadTreeCreat(node->leftChild);
	if (node->leftChild == NULL)
	{
		node->ltag = (flag)1;//這裡要使用強轉型別轉換,不然會報錯!
		node->leftChild = pre;
	}
	if (node->rightChild == NULL) 
		node->rtag = (flag)1;//右孩子指標指向的是下一個中序結點的地址,但是暫時還不知道下一個是誰,因此只能在下一輪迴圈指向下一個結點。
	if (pre != NULL&& pre->rtag == 1)//當pre為指向空的時候,當然不會有資料,所以要判斷一下!
	{
			pre->rightChild = node;
	}	
	pre = node;
	threadTreeCreat(node->rightChild);
	return;
}
struct node* Tree::treeCreat(struct node* node)
{
	char ch;
	cin >> ch;
	if (ch == '#')
	{
		node = NULL;
	}
	else
	{
		node = new struct node;
		node->data = ch;
		//node->ltag = (flag)0; node->rtag = (flag)0;
		node->leftChild = treeCreat(node->leftChild);
		node->rightChild = treeCreat(node->rightChild);
	}
	return node;
}
void Tree::destroyTree(struct node* node)
{
	if (node == NULL)
	{
		return;
	}
	if (node->leftChild != NULL&&node->ltag == 0)
	{
		destroyTree(node->leftChild);
	}
	if (node->rightChild != NULL&&node->rtag == 0)
	{
		destroyTree(node->rightChild);
	}
	delete node;
	node = NULL;
	return;
}