C 데이터 구조와 알고리즘 - 통합의 기초 - 트리-06 : 스레드 이진의 (a)

线索二叉树的原理和实现代码

정보 이진 트리의 다음 문서를 볼 수 있습니다

C 데이터 구조와 알고리즘 - 통합의 기초 - 트리-02 : 이진 트리 탐색 및 다른 설립

특정 프리앰블 시퀀스와 단서의 방법으로, 당신은 다음 문서를 참조 할 수 있습니다 :

C 데이터 구조와 알고리즘 - 통합의 기초 - 트리-07 : 스레드 이진 (이)

0x01로. 이진 트리의 기원에 대한 단서

일반적 이진 트리에서 , 리프 노드 또는 하나 개의 자식 노드가 비어 포인터 필드의 많은,이 공간이 만들어졌다, 그러나 효과적으로 결과, 사용되지 않은있을 것입니다 공간의 낭비 .

해결 방법 :

선행 부모 노드와 설정에 널 포인터 지점의 왼쪽 자식 Ltag의 플래그 변수는 왼쪽 아이가 전구체 또는 실제 존재를 가리키는 나타냅니다.

다음은 널 포인터 후속 부모 노드의 권리 아동 및 설정 rtag는의 플래그 변수는 바로 아이가 후계자 또는 실제 가리키는 나타냅니다.

관련 개념 노트 :

선구자 : 특정 이진 트리 탐색 순서 탐색, 노드에서 노드의 통과를 다음과 같은 경우.

이후 : 특정 이진 트리 탐색 순서 탐색, 다음 노드 탐색에 노드를 다음과 같은 경우.

단서 : 전임자와 후임자라는 단서에 대한 포인터를 가리 킵니다.

스레드 이진 : 바이너리 추가 단서.

단서 : 이진 트리가 널 포인터 플러스 단서의 과정에 단서를했다.

는 0x02. 진 나사산 구조체

차이점은 일반 이진 트리가하는 Ltag 및 rtag는 플래그 변수보다 더 이진 트리 구조의 단서가 아이의 좌우 어린이의 신원을 표시하는 것입니다.

또한, 진 큐 과정, 단지 방문 요소에 글로벌 사전 포인터의 필요성을 스레드.

typedef struct TreeNode
{
	char data;
	struct TreeNode* Left;
	struct TreeNode* Right;
	int Ltag;//用于线索化的标志变量  左为1表示前驱 右为1表示后继,0表示自身是树中元素
	int Rtag;
}TreeNode,*BinTree;//定义了这个结构体为 TreeNode ,这个二叉树指针为 BinTree
BinTree pre;

0x03으로. 빌드 바이너리 스레드

빌드 바이너리 스레드 및 일반 이진 트리가 기본적으로 동일 설정, 차이는 이진 트리 노드에 각각의 추가 단서, 당신은 변수 초기화에 대한 로그인해야한다는 것입니다.

특정 유사한 설립 코드와 일반 이진 트리, 참조 장을 구축 : 이번 방문의 점을

설명 :하지 충돌을 통과하는 방법을 확립 단서의 이진 트리가 본질적으로 여전히 이진 트리이며, 설치 순서는 동일합니다.

//二叉树的建立:递归实现,先序遍历的创建方法
//注意此处BT为指针的指针,因为没有返回值,所以要把二叉树的指针完全改变掉,需要指针的指针
//str为传入的字符串,该字符串用于创建二叉树的数据域
//n为当前创建节点数据域时使用到str的位数
//返回值为记录二叉树创建完毕后,使用了多少字符
int CreateTree(BinTree *BT,char *str,int n)
{
	printf("创建二叉树第  %d  个节点   ", n);
	char ch = str[n];//每次使用一个字符赋值数据域
	printf("%c\n", ch);//输出该数据
	n++;
	if (ch != '\0')//字符串没完就一直创建
	{
		if (ch == '#')//如果遇到字符 # ,说明下方没有了,是叶节点
		{
			*BT = NULL;//代表二叉树指针为空
		}
		else
		{
			*BT = (BinTree)malloc(sizeof(TreeNode));//*BT代表二叉树指针的值,BinTree表示这是一个指针,大小为一个二叉树结构体
			if (!*BT)
			{
				exit(-1);//若分配空间失败,异常退出
			}
			(*BT)->data = ch;
			(*BT)->Ltag = 0;//注意一定要有这个初始化步骤
			(*BT)->Rtag = 0;
			n=CreateTree(&(*BT)->Left,str, n);//取指针的地址给此函数的第一个参数,因为此参数为二级指针
			n=CreateTree(&(*BT)->Right, str, n);
		}
	}
	return n;
}

 

를 0x04.에서 주문 스레드 스레드 이진

단서는 다양한 순서로 수행 될 수 있지만, 단서 순회 순서의 시퀀스는 하나의 대응 일 수있다.

//中序遍历线索化
void inThreading(BinTree BT)
{
	if(BT)
	{
		inThreading(BT->Left);
		if (!BT->Left)//若左孩子不存在,则左孩子线索化,指向前驱节点,即上一个访问过的节点
		{
			BT->Ltag = 1;
			BT->Left = pre;
		}
		if (pre && !pre->Right)//当遍历到BT时,若它的前驱pre存在,且前驱右孩子不存在,那么前驱的右孩子指向BT,即BT为pre的后继
		{
			pre->Rtag = 1;
			pre->Right = BT;
		}
		pre = BT;//当准备遍历下一个节点时,用pre保存当前的节点
		inThreading(BT->Right);//继续中序遍历完成线索化
	}
}

의 0x05. 순서 이진 트리 탐색의 단서

이진 시퀀스 나사 방식에 대응.

//中序遍历线索二叉树,非递归实现
void InOrderThreading(BinTree BT)
{
	BinTree p = BT;//让p指向根节点
	while (p)
	{
		while (p->Ltag == 0)
		{
			p = p->Left;
		}//首先遍历到中序遍历第一个开始的节点
		printf("%c -> ", p->data);//打印其数据
		while (p->Rtag == 1&&p->Right)//如果右孩子是线索,那么一直遍历至右孩子,因为右孩子指向后继
		{
			p = p->Right;
			printf("%c -> ", p->data);
		}//右孩子不是线索了,说明右孩子存在,左孩子刚遍历到了,所以接着右孩子的遍历,然后开始下一轮的遍历
		p = p->Right;
	}
}

0x06 임. 게다가 머리 노드 나사 진

이진 트리 루트 전에 헤드 노드를 증가, 루트 노드의 왼쪽 자식 점은, 주문 탐색의 마지막 노드에서 오른쪽 자식 점, 그래서 이진 트리 처음 트래버스에서뿐만 아니라 꼬리 하나, 이중 연결리스트 같은 것을 통과.

단서 :

//增加头结点的中序线索化,Head为二级指针,指向指针的指针,目的是真正改变一级指针的内容
void InOrderThreading(BinTree* Head, BinTree BT)
{
	*Head =(BinTree)malloc(sizeof(TreeNode));//给头结点分配空间
	(*Head)->Ltag = 0;//左孩子是根结点
	(*Head)->Rtag = 1;//右孩子是线索
	(*Head)->Right = (*Head);//右孩子先指向自身,防止原二叉树为空
	if (!BT)//二叉树为空
	{
		(*Head)->Left = (*Head);//左孩子也指向自身
		return;
	}
	pre = (*Head);//前指针指向头结点
	(*Head)->Left = BT;
	inThreading(BT);//开始普通的中序线索化二叉树
	//此时二叉树中序序列的第一个结点的左孩子指向头结点
	//结束普通的线索化后,此时的pre指针指向最后一个元素
	pre->Right = *Head;
	pre->Rtag = 1;
	(*Head)->Right = pre;
	//现在整个二叉树就是一个双向链表,既可以从头结点开始往后遍历,也可以从中序序列最后一个结点开始往前遍历
}

순회 :

//有头结点的中序遍历线索二叉树,此时的BT是头结点
void InOrderThreading1(BinTree BT)
{
	BinTree p = BT->Left;//让p指向根节点
	while (p!=BT)//循环到尾部时,因为尾部右孩子指向头结点,所以,p等于头结点的时候遍历完成
	{
		while (p->Ltag == 0)
		{
			p = p->Left;
		}//首先遍历到中序遍历第一个开始的节点
		printf("%c -> ", p->data);//打印其数据
		while (p->Rtag == 1 && p->Right!=BT)//此处右孩子的条件应为不等于BT
		{
			p = p->Right;
			printf("%c -> ", p->data);
		}//右孩子不是线索了,说明右孩子存在,左孩子刚遍历到了,所以接着右孩子的遍历,然后开始下一轮的遍历
		p = p->Right;
	}
}

주요 기능 노드 테스트 코드 부품 :

int main()
{
	BinTree BT;//注意BT为指针
	int k;//记录创建函数的返回值
	char str[100];
	printf("\n\n**********\t请输入一串字符用于创建二叉树:");
	gets_s(str);
	k = CreateTree(&BT, str, 0);
	printf("\n\n**********\t二叉树创建成功!!!共使用了  %d  个字符",k);
	BinTree Head;
	printf("\n\n**********\t开始中序线索化该二叉树......");
	InOrderThreading(&Head,BT);
	printf("\n\n**********\t线索化成功!!!");
	printf("\n\n**********\t开始该线索二叉树的中序遍历......");
	printf("\n\n**********\t中序遍历结果为:");
	InOrderThreading1(Head);
	
}

테스트 데이터 : AB # D ## C ##

0x07 인. 의심과 통찰력

1. 이진 트리 널 포인터가 제한되어야한다, 왜에만 전체 트리에이 널 포인터가 후계자를 찾을 수있는 각 노드에 도달 할 단서 단서 할 수 있습니까?

A는 : 관찰 전에 예약 주문 기능, 아이들이 노드 정보를 인쇄하려면 왼쪽에서 반환,주의를 기울이 노드는 노드에 실제 횡단입니다 인쇄 속도로 끝난 아이를 반환 할 수있는 권리, 그것은이다 아이가 비어, 또는 부분적으로 아이가 비어있는 경우 아이들이 다음 단지 이전에 아이 포인트를 왼쪽, 함수 호출의 전체 프로세스를 완료 한 왼쪽 때 아이들이 그것을 왼쪽 반환하는 방법을 부분적으로 함수 호출을 완료 왼쪽 경우 아이, 그 다음 아이가 아이의 왼쪽과 오른쪽 아이들이 전체 호출의 완료 후 반환 등, 항상 수익의 루트를 찾을 수 있습니다 왼쪽 : 잎 노드에서 반환, 아이들이 존재하는 노드 주위에 통과하는 것을 알 수있다, 그래서 노드와 그것의 특정 부분의 다음 노드 무료 두 개의 노드가 거기 아이들이 후계자 및 이전 포인터의 존재를해야합니다 있도록, 모든 어린이에 대해이되지 않도록에 있습니다. 또한, 당신은 볼 수 있습니다 양적 관계 사이 클릭 : 이진 트리 n 개의 노드, 2N 포인터 필드의 총 n 개의 지점의 노드 N-1의 총을 2N-에 대한 (N-1) = N + 1 포인터 실제로 널 포인터 필드 있지만 실제로 n-1 개의 포인터가 가리키는 노드가 두 개의 자식이 경우에도 (관계 트리 완료에 따른 순서로 이송 될 수있다 제 1 노드는 더 전구체를 통과하지 특징 위해, 후속 전구체를 찾을 수)를 아래를 내려다 보면서, 마지막 노드는 후계자를 통과하지 않습니다. 그래서, 실제로 널 포인터 도메인은 충분하다.

2. 아래에서 어떤 상황이 진 스레드?

A는 : 종종 이송 접합 또는 포인트의 필요성을 찾을 때, 훨씬 덜 재귀 방법의 시간 복잡도보다, 사용할 수있다.

이후 동일한 원리에 대해, 순서 전에, 사실, 순서 주사로 단서를 이해합니다.

 

게시 19 개 원래 기사 · 원 찬양 7 · 전망 (414)

추천

출처blog.csdn.net/ATFWUS/article/details/104291897