线索二叉树

当我们建立二叉树的时候,会有很多多出来的空指针没有利用好,这就不符合我们勤俭节约的好习惯了

例如:

其中∧表示的就是空指针

 

我们可以对其进行线索化,从而达到充分利用好空指针

 

首先,

我们可以使左非空指针指向它的左孩子右非空指针指向它的右孩子

我们可以使左空指针指向它的前驱右空指针指向它的后继

经过某种遍历后会发现,二叉树就会变成一个双向链表

其中:虚线箭头指向前驱实线箭头指向后继

 

那么问题来了,我们如何让计算机知道左指针是指向左孩子,还是指向前驱呢

我们可以在结点下手:

struct Node {
	Eletype data;
	Node *lchild;
	Node *rchild;
	bool LTag = 0;
	bool RTag = 0;
};

LTag = 0,lchild 指向左孩子;LTag = 1,lchild 指向前驱

RTag = 0,rchild 指向右孩子;RTag = 1,rchild 指向后继

 既然是双向链表,我们就给链表加一个头结点,方便后面遍历。

并且令头结点左指针指向树根右指针指向中序遍历最后一个结点

同时,第一个结点左指针指向头结点最后一个结点右指针指向头结点

 

建立二叉树: 输入(ABDH##I##EJ###CF##G##)

	void CreateBiTree(Node<T>*q) {
		T data;
		cin >> data;

		if (data == '#') { //自己找个符号当作 虚结点
			q->data = '#'; //标志为 空结点
		}
		else {
			q->data = data;
			q->lchild = new Node<T>;
			q->rchild = new Node<T>;

			CreateBiTree(q->lchild);
			CreateBiTree(q->rchild);
		}
	}

 

二叉树线索化:

//Pre为全局变量,并且始终指向当前结点,

初始化的时候Pre设置为head,那么第一个结点的前驱自然就会指向head


	void InThreading(Node<T> *p) {
		if (p->data!='#') {
			//-------------------------------------------
			InThreading(p->lchild);
			//----------------------------------------------
			if (p->lchild->data=='#') {  // 如果左指针为空,那么就要利用好这空指针,让它指向前驱
				p->LTag = Thread; //标志存在前驱
				p->lchild = Pre; // 当前结点的前驱结点为上一个结点
			}
			if (Pre->rchild->data=='#') { //如果上一个结点的右指针为空,那么就要利用好这空指针,让它指向后继,即指向当前结点
				Pre->RTag = Thread;//标志存在后继
				Pre->rchild = p;  // 则 前驱结点的后继就是当前结点
			}

			Pre = p; //Pre 指向当前结点
			//-----------------------------------------------
			InThreading(p->rchild);
			//-------------------------------------------
		}
	}

接下来,让头结点和最后一个结点相互指向(线段②④)

注意:线索化后,Pre已经指向最后一个结点

	void HeadToTail() {
		head->rchild = Pre; // 头结点 的 右孩子 指向最后一个结点
		head->rchild->RTag = Thread;//标记 最后一个结点存在后继结点
		head->rchild->rchild = head;//最后一个结点的后继指向头结点
	}

 

完整代码:

#include<iostream>
using namespace std;
#define Link 0
#define Thread 1

template<typename T>
struct Node {
	T data;
	Node *lchild;
	Node *rchild;
	bool LTag = 0;
	bool RTag = 0;
};

//线索二叉树
template<class T>
class Tree {
private:
	Node<T>*head;
    Node<T>*root;
	Node<T>*Pre; //始终指向刚刚访问的结点
public:
	//初始化头结点和树根
	Tree() {
		head = new Node<T>;
		root = new Node<T>;
		head->lchild = root;//头结点的左孩子 指向 树根
		head->rchild = root;
		Pre = head;//初始化的时候指向头结点
	}

	//前序遍历建立二叉树
	void CreateBiTree(Node<T>*q) {
		T data;
		cin >> data;

		if (data == '#') { //自己找个符号当作 虚结点
			q->data = '#'; //标志为 空结点
		}
		else {
			q->data = data;
			q->lchild = new Node<T>;
			q->rchild = new Node<T>;

			CreateBiTree(q->lchild);
			CreateBiTree(q->rchild);
		}
	}

	//采用 中序遍历 进行 中序线索化
	void InThreading(Node<T> *p) {
		if (p->data!='#') {
			//-------------------------------------------
			InThreading(p->lchild);
			//----------------------------------------------
			if (p->lchild->data=='#') {  // 如果左指针为空,那么就要利用好这空指针,让它指向前驱
				p->LTag = Thread; //标志存在前驱
				p->lchild = Pre; // 当前结点的前驱结点为上一个结点
			}
			if (Pre->rchild->data=='#') { //如果上一个结点的右指针为空,那么就要利用好这空指针,让它指向后继,即指向当前结点
				Pre->RTag = Thread;//标志存在后继
				Pre->rchild = p;  // 则 前驱结点的后继就是当前结点
			}

			Pre = p; //Pre 指向当前结点
			//-----------------------------------------------
			InThreading(p->rchild);
			//-------------------------------------------
		}
	}

	//把 最后一个结点 和 头结点 连接
	void HeadToTail() {
		head->rchild = Pre; // 头结点 的 右孩子 指向最后一个结点
		head->rchild->RTag = Thread;//标记 最后一个结点存在后继结点
		head->rchild->rchild = head;//最后一个结点的后继指向头结点
	}

	//根据线索进行遍历
	void InOrderTraverse_Thr(Node<T> *H) {
		Node<T>*p;
		p = H->lchild; //指向树根
		while (p != H) { //如果 当前结点 不是 头结点
			while (p->LTag == Link)p = p->lchild;  //LTag == Thread 的时候 p指向中序的第一个结点

			cout << p->data;

			//存在后继的时候循环,直到结点不存在后继
			while (p->RTag == Thread && p->rchild != H) { //RTag == Thread 即存在后继,且后继不为头结点
				p = p->rchild; // 当前结点p指向后继
				cout << p->data;
			}

			p = p->rchild;//p指向右孩子
		}
	}

	Node<T> *GetRootaddr() { return root; }
	Node<T> *GetHeadaddr() { return head; }
};

int main() {
	Tree<char> T;
	T.CreateBiTree(T.GetRootaddr());
	T.InThreading(T.GetRootaddr());
	T.HeadToTail();
	T.InOrderTraverse_Thr(T.GetHeadaddr());
	return 0;
}

 

运行结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值