数据结构 · 二叉树转换为双向链表
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. 二叉树转换为DLLprintf("\nConvert BT to DLL:\n");TreeNode *dLL = NULL;BtreeConvertToDLL(bTRoot, &dLL);//3.1 遍历DLLTraversalBtree(dLL);return 0;}
打印结果:
1. 3 运行时复杂度
线性,O(n)。
运行时复杂性基于树中的节点数量。
1. 4 内存复杂度
线性,O(n)。
递归解决方案具有O(h)的内存复杂性,因为它将消耗堆栈上的内存,直到二叉树“ h”的高度。对于平衡树,它将为O(log n),在最坏的情况下可以为O(n)。
