数据结构 · 二叉树转换为双向链表
1. 二叉树转换为双向链表
1. 1 二叉树节点数据类型声明
struct TreeNode
{
int data; //数据域
TreeNode *left; //左子树
TreeNode *right; //右子树
};
从二叉树节点的数据结构声明可得知,它与双向链表节点的结构声明是相同的。二叉树中的left(左子树)指针对应双向链表中的prev(直接前驱)指针,而rigth(右子树)对应双向链表中的next(直接后继)指针。
因此,对于给定的一棵二叉树,通过修改该树节点中的左、右指针,使它们表示双向链接。双向链表(Doubly Linked List, DLL)中节点的顺序必须与二叉树中序遍历的节点顺序相同。在转换过程中,二叉树中的left、right节点将分别作为链表中的prev、next节点。且中序遍历的第一个节点(即二叉树BT最左边的节点)必须是双向链表(DLL)的头节点。如下图中的二叉树中序遍历第一个节点是"D"。
▲ 待转换为DLL的二叉树
则中序遍历后,其节点的顺序是:
D, B, E, A, F, C, G
按照二叉树“中序遍历(Inorder Traversal)”的规则,通过修改二叉树中节点的指针指向,最终可得到的一个相同顺序的双向链表如下,且该双向链表返回一个头结点指针。
▲ 二叉树转换为双向链表DLL
整个转换过程详细步骤如下:
-
若左子树存在,则处理左子树
-
递归地将左子树转换为双向链表DLL; -
然后在左子树中找到中序遍历规则的根的前驱结点(根中序前驱是左子树中的最右边的结点);
-
将中序前驱作为根的前驱(prev),并将根作为中序前驱的next。
-
若右子树存在,则处理右子树
-
递归将右子树转换为双向链表DLL; -
然后在右子树中找到根的中序后继(中序后继节点是右子树中最左边节点) ;即上图中的节点“F”。 -
然后使中序后继结点作为根的next,且根(root)作为中序后继结点的prve 。
-
找到最左边的节点并返回它(最左边的节点总是转换DLL的头)。
1. 2 示例代码
1. 2.1 初始化二叉树节点
如下图所示:
▲ 二叉树初始化节点
/*
* fn: CreateBTree
* desc: 初始化二叉树节点
* date: 2020/12/19/02:27:08
* author: lixiaogang5
* mail: [email protected]
* CSDN: lixiaogang_theanswer
*/
TreeNode *NewBTreeNode(char iEle)
{
assert(iEle);
TreeNode *bTree = (TreeNode *)calloc(1, sizeof(TreeNode));
assert(bTree);
bTree->data = iEle;
bTree->left = bTree->right = NULL;
return bTree;
}
1. 2.2 转换为DLL过程
/*
* fn: BtreeConvertToDLL
* desc: 二叉树转换为双向链表
* date: 2020/12/19/02:42:08
* author: lixiaogang5
* mail: lxiaogang5@gmail.com
* CSDN: lixiaogang_theanswer
*/
void BtreeConvertToDLL(TreeNode *bTree, TreeNode **pHead)
{
if (bTree == NULL)
return;
BtreeConvertToDLL(bTree->right, pHead);
bTree->right = *pHead;
if (*pHead != NULL) {
(*pHead)->left = bTree;
}
*pHead = bTree;
BtreeConvertToDLL(bTree->left, pHead);
}
1. 2.3 打印链表节点值
/*
* fn: TraversalBtree
* desc: 遍历双向链表
* date: 2020/12/19/02:34:56
* author: lixiaogang5
* mail: [email protected]
* CSDN: lixiaogang_theanswer
*/
void TraversalBtree(const TreeNode *bTree)
{
assert(bTree);
const TreeNode *pHead = bTree;
while(pHead)
{
printf("%c ", pHead->data);
pHead = pHead->right;
}
return;
}
1. 2.4 中序遍历二叉树
/*
* fn: InorderBTree
* desc: 中序遍历二叉树
* date: 2020/12/19/02:35:15
* author: lixiaogang5
* mail: lxiaogang5@gmail.com
* CSDN: lixiaogang_theanswer
*/
void InorderBTree(TreeNode *bTree)
{
if(bTree)
{
InorderBTree(bTree->left);
printf("%c ", bTree->data);
InorderBTree(bTree->right);
}
}
1. 2.5 完整demo
TreeNode *bTRoot = NewBTreeNode('A');
bTRoot->left = NewBTreeNode('B');
bTRoot->right = NewBTreeNode('C');
bTRoot->left->left = NewBTreeNode('D');
bTRoot->left->right = NewBTreeNode('E');
bTRoot->right->left = NewBTreeNode('F');
bTRoot->right->right = NewBTreeNode('G');
下面3张图为这7句语句的内部详细逻辑。
完整的demo如下:
#include <stdio.h>
#include <assert.h>
typedef struct TreeNode
{
char data; //数据域
TreeNode *left; //左子树
TreeNode *right; //右子树
}TreeNode;
/*
* fn: BtreeConvertToDLL
* desc: 二叉树转换为双向链表
* date: 2020/12/19/02:42:08
* author: lixiaogang5
* mail: lxiaogang5@gmail.com
* CSDN: lixiaogang_theanswer
*/
void BtreeConvertToDLL(TreeNode *bTree, TreeNode **pHead)
{
if (bTree == NULL)
return;
BtreeConvertToDLL(bTree->right, pHead);
bTree->right = *pHead;
if (*pHead != NULL) {
(*pHead)->left = bTree;
}
*pHead = bTree;
BtreeConvertToDLL(bTree->left, pHead);
}
/*
* fn: CreateBTree
* desc: 初始化二叉树节点
* date: 2020/12/19/02:27:08
* author: lixiaogang5
* mail: lxiaogang5@gmail.com
* CSDN: lixiaogang_theanswer
*/
TreeNode *NewBTreeNode(char iEle)
{
TreeNode *bTree = (TreeNode *)calloc(1, sizeof(TreeNode));
assert(bTree);
bTree->data = iEle;
bTree->left = bTree->right = NULL;
return bTree;
}
/*
* fn: TraversalBtree
* desc: 遍历双向链表
* date: 2020/12/19/02:34:56
* author: lixiaogang5
* mail: lxiaogang5@gmail.com
* CSDN: lixiaogang_theanswer
*/
void TraversalBtree(const TreeNode *bTree)
{
assert(bTree);
const TreeNode *pHead = bTree;
while(pHead)
{
printf("%c ", pHead->data);
pHead = pHead->right;
}
return;
}
/*
* fn: InorderBTree
* desc: 中序遍历二叉树
* date: 2020/12/19/02:35:15
* author: lixiaogang5
* mail: lxiaogang5@gmail.com
* CSDN: lixiaogang_theanswer
*/
void InorderBTree(TreeNode *bTree)
{
if(bTree)
{
InorderBTree(bTree->left);
printf("%c ", bTree->data);
InorderBTree(bTree->right);
}
}
int main()
{
//1. 初始化二叉树
TreeNode *bTRoot = NewBTreeNode('A');
bTRoot->left = NewBTreeNode('B');
bTRoot->right = NewBTreeNode('C');
bTRoot->left->left = NewBTreeNode('D');
bTRoot->left->right = NewBTreeNode('E');
bTRoot->right->left = NewBTreeNode('F');
bTRoot->right->right = NewBTreeNode('G');
//2. 中序遍历该二叉树
printf("Inorder Traversal:\n");
InorderBTree(bTRoot);
//3. 二叉树转换为DLL
printf("\nConvert BT to DLL:\n");
TreeNode *dLL = NULL;
BtreeConvertToDLL(bTRoot, &dLL);
//3.1 遍历DLL
TraversalBtree(dLL);
return 0;
}
打印结果:
1. 3 运行时复杂度
线性,O(n)。
运行时复杂性基于树中的节点数量。
1. 4 内存复杂度
线性,O(n)。
递归解决方案具有O(h)的内存复杂性,因为它将消耗堆栈上的内存,直到二叉树“ h”的高度。对于平衡树,它将为O(log n),在最坏的情况下可以为O(n)。