第37课 - 线索化二叉树
1. 问题
在一些项目中需要频繁的遍历二叉树,但是二叉树的遍历比单链表的遍历复杂多了,并且递归总是会后额外的开销。
2. 线索化二叉树
线索化二叉树指的是将二叉树中的结点进行逻辑意义上的“重新排列”,使其可以线性的方式访问每一个结点。
二叉树线索化后每个结点都有一个线性下标,用过这个下标可以快速访问结点,而不需要遍历二叉树。
3. 方法1
利用结点中的空指针,使其指向后继结点。
算法思想:
初始化位置指针:p = NULL;
前序遍历二叉树:
若p不为空,将p->left指向昂前结点,并将p置为NULl。
若当前结点的左子树为空时,将p指向当前结点。
4. 方法2
利用线性表保存二叉树的遍历顺序。
算法思想:
初始化顺序表s1。
前序遍历二叉树:遍历过程中当前结点插入顺序表s1。
5. 程序
main.c
#include <stdio.h>
#include <stdlib.h>
#include "BTree.h"
#include "SeqList.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
struct Node
{
BTreeNode header;
char v;
};
void printf_data(BTreeNode* node)
{
if( node != NULL )
{
printf("%c", ((struct Node*)node)->v);
}
}
void thread_via_left(BTreeNode* root, BTreeNode** pp)
{
if( (root != NULL) && (pp != NULL) )
{
if( *pp != NULL )
{
(*pp)->left = root;
*pp = NULL;
}
if( root->left == NULL )
{
*pp = root;
}
thread_via_left(root->left, pp);
thread_via_left(root->right, pp);
}
}
void thread_via_list(BTreeNode* root, SeqList* list)
{
if( (root != NULL) && (list != NULL) )
{
SeqList_Insert(list, (SeqListNode*)root, SeqList_Length(list));
thread_via_list(root->left, list);
thread_via_list(root->right, list);
}
}
int main(int argc, char *argv[])
{
BTree* tree = BTree_Create();
BTreeNode* current = NULL;
BTreeNode* p = NULL;
SeqList* list = NULL;
int i = 0;
struct Node n1 = {{NULL, NULL}, 'A'};
struct Node n2 = {{NULL, NULL}, 'B'};
struct Node n3 = {{NULL, NULL}, 'C'};
struct Node n4 = {{NULL, NULL}, 'D'};
struct Node n5 = {{NULL, NULL}, 'E'};
struct Node n6 = {{NULL, NULL}, 'F'};
BTree_Insert(tree, (BTreeNode*)&n1, 0, 0, 0);
BTree_Insert(tree, (BTreeNode*)&n2, 0x00, 1, 0);
BTree_Insert(tree, (BTreeNode*)&n3, 0x01, 1, 0);
BTree_Insert(tree, (BTreeNode*)&n4, 0x00, 2, 0);
BTree_Insert(tree, (BTreeNode*)&n5, 0x02, 2, 0);
BTree_Insert(tree, (BTreeNode*)&n6, 0x02, 3, 0);
printf("Full Tree: ");
BTree_Display(tree, printf_data, 4, '-');
printf("Thread via List: ");
list = SeqList_Create(BTree_Count(tree));
thread_via_list(BTree_Root(tree), list);
for(i=0; i<SeqList_Length(list); i++)
{
printf("%c, ", ((struct Node*)SeqList_Get(list, i))->v);
}
printf(" ");
printf("Thread via Left: ");
current = BTree_Root(tree);
thread_via_left(current, &p);
while( current != NULL )
{
printf("%c, ", ((struct Node*)current)->v);
current = current->left;
}
printf(" ");
BTree_Destroy(tree);
return 0;
}
BTree.h
BTree.c
SeqList.h
#ifndef _SEQLIST_H_
#define _SEQLIST_H_
typedef void SeqList;
typedef void SeqListNode;
SeqList* SeqList_Create(int capacity);
void SeqList_Destroy(SeqList* list);
void SeqList_Clear(SeqList* list);
int SeqList_Length(SeqList* list);
int SeqList_Capacity(SeqList* list);
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos);
SeqListNode* SeqList_Get(SeqList* list, int pos);
SeqListNode* SeqList_Delete(SeqList* list, int pos);
#endif
SeqList.c
#include <stdio.h>
#include <malloc.h>
#include "SeqList.h"
typedef unsigned int TSeqListNode;
typedef struct _tag_SeqList
{
int capacity;
int length;
TSeqListNode* node;
} TSeqList;
SeqList* SeqList_Create(int capacity) // O(1)
{
TSeqList* ret = NULL;
if( capacity >= 0 )
{
ret = (TSeqList*)malloc(sizeof(TSeqList) + sizeof(TSeqListNode) * capacity);
}
if( ret != NULL )
{
ret->capacity = capacity;
ret->length = 0;
ret->node = (TSeqListNode*)(ret + 1);
}
return ret;
}
void SeqList_Destroy(SeqList* list) // O(1)
{
free(list);
}
void SeqList_Clear(SeqList* list) // O(1)
{
TSeqList* sList = (TSeqList*)list;
if( sList != NULL )
{
sList->length = 0;
}
}
int SeqList_Length(SeqList* list) // O(1)
{
TSeqList* sList = (TSeqList*)list;
int ret = -1;
if( sList != NULL )
{
ret = sList->length;
}
return ret;
}
int SeqList_Capacity(SeqList* list) // O(1)
{
TSeqList* sList = (TSeqList*)list;
int ret = -1;
if( sList != NULL )
{
ret = sList->capacity;
}
return ret;
}
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos) // O(n)
{
TSeqList* sList = (TSeqList*)list;
int ret = (sList != NULL);
int i = 0;
ret = ret && (sList->length + 1 <= sList->capacity);
ret = ret && (0 <= pos);
if( ret )
{
if( pos >= sList->length )
{
pos = sList->length;
}
for(i=sList->length; i>pos; i--)
{
sList->node[i] = sList->node[i-1];
}
sList->node[i] = (TSeqListNode)node;
sList->length++;
}
return ret;
}
SeqListNode* SeqList_Get(SeqList* list, int pos) // O(1)
{
TSeqList* sList = (TSeqList*)list;
SeqListNode* ret = NULL;
if( (sList != NULL) && (0 <= pos) && (pos <= sList->length) )
{
ret = (SeqListNode*)(sList->node[pos]);
}
return ret;
}
SeqListNode* SeqList_Delete(SeqList* list, int pos) // O(n)
{
TSeqList* sList = (TSeqList*)list;
SeqListNode* ret = SeqList_Get(list, pos);
int i = 0;
if( ret != NULL )
{
for(i=pos+1; i<sList->length; i++)
{
sList->node[i-1] = sList->node[i];
}
sList->length--;
}
return ret;
}
小结;
(1) 利用结点空指针线索化的方法会破坏树的结构。
(2) 利用结点空指针线索化二叉树之后不能再恢复,这个问题可以在树结点中加入一个线索化指针而得到解决,然而线索化指针的加入优惠浪费内存空间,不够灵活。
(3) 链表线索化方法不会破坏树的结构,不需要线索化时销毁链表即可。
(4) 链表线索化方法可以很容易的以任何一种遍历顺序对二叉树进行线索化。