4.二叉搜索树转为有序双向链表(递归算法与非递归算法)

一、题目

  要求输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建新的节点,只能调整树中结点指针的指向。

  二叉树结点定义如下:

1 struct BinaryTreeNode
2 {
3     int        m_nValue;
4     BinaryTreeNode *m_pLeft;
5     BinaryTreeNode *m_pRight;
6 };

图1、二叉搜索树转为有序双向链表

二、算法

(1)递归算法

  因为二叉搜索树每个结点的子树也是一棵二叉搜索树,所以,我们可以把问题分解为,

  • 把左子树调整为双向链表;
  • 把根和左子树调整后的链表相连;
  • 调整右子树。

图2、分解为子问题

  算法代码实现如下:

 1 // 递归实现
 2
 3 void ConvertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInList)
 4 {
 5     if (pNode == NULL)
 6         return;
 7
 8     BinaryTreeNode *pCurrent = pNode;
 9
10     // 因为要求有序,所以先调整小的结点,也就是左子树的结点
11     if (pCurrent->m_pLeft != NULL)
12         ConvertNode(pNode->m_pLeft, pLastNodeInList);
13
14     // 此时左子树已经调整好,只需把当前结点和右子树的最后一个结点相连接即可
15     pCurrent->m_pLeft = *pLastNodeInList;
16     if (*pLastNodeInList != NULL)
17         (*pLastNodeInList)->m_pRight = pCurrent;
18
19     //此时当前链表中最后一个结点,已经变为当前结点了
20     *pLastNodeInList = pCurrent;
21
22     // 再调整右子树
23     if (pCurrent->m_pRight != NULL)
24         ConvertNode(pNode->m_pRight, pLastNodeInList);
25
26 }
27
28 BinaryTreeNode* Convert(BinaryTreeNode* pRoot)
29 {
30     BinaryTreeNode* pLastNodeInList = NULL;
31     ConvertNode(pRoot, &pLastNodeInList);
32
33     // pLastNodeInList指向双向链表的尾结点
34     // 我们需要返回头结点
35     BinaryTreeNode* pHeadOfList = pLastNodeInList;
36     while (pHeadOfList != NULL && pHeadOfList->m_pLeft != NULL)
37         pHeadOfList = pHeadOfList->m_pLeft;
38
39     return pHeadOfList;
40 }

(2)非递归算法

  上面的递归算法,虽然算法看起来比较好理解,但是由于需要不断的递归,导致不断的压栈,a、结点较多时有可能出现栈溢出;b、递归算法的多次压栈导致运行的时间效率较低。一般情况下,非递归算法要比递归算法有更好的时间和空间效率。

  非递归算法的算法步骤:

  • 将没有右结点的结点的右指针指向该结点在最终有序链表中的下一个结点;
  • 本来就有右结点的结点,可能其右指针指向的不是该结点在最终有序链表中的下一个结点,调整其指向最终正确的指向;
  • 通过右指针遍历二叉搜索树,为每个结点调整其左指针。

图3、非递归算法的算法步骤

  算法的实现如下:

 1 void AddRight(BinaryTreeNode* pCurrentNode, BinaryTreeNode* lastLeftAncestor)
 2 {
 3     if (pCurrentNode->m_pLeft != NULL)
 4         AddRight(pCurrentNode->m_pLeft, pCurrentNode); // 从该节点往上回溯,第一个该节点的是其父节点的左孩子,lastLeftAncestor表示就是该节点的父节点
 5     if (pCurrentNode->m_pRight == NULL)
 6         pCurrentNode->m_pRight = lastLeftAncestor; //根据lastLeftAncestor的定义,其应该就是当前节点pRoot的在有序链表中的下一个节点
 7     else
 8         AddRight(pCurrentNode->m_pRight, lastLeftAncestor);
 9 }
10
11 void AdjustRight(BinaryTreeNode* pCurrentNode)
12 {
13     if(pCurrentNode->m_pRight == NULL)
14         return; // 经过AddRight后,只有最右节点的m_pRight为NULL,如果m_pRight为空表示遍历结束
15     else
16     {
17         BinaryTreeNode* pNode = pCurrentNode->m_pRight->m_pLeft;
18
19         // 如果当前节点的右子树的做指针为空,那么pCurrentNode的右指针不需要修改
20         if (pNode == NULL)
21             return;
22         else
23         {
24             // 如果pCurrentNode的m_pRight指向正确的话,那么pCurrentNode一定是其m_pRight节点的左子树的最右节点
25             while(pNode->m_pRight != NULL && pNode != pCurrentNode)
26                 pNode = pNode->m_pRight;
27
28             // 如果pCurrentNode的m_pRight指向正确的话,那么pCurrentNode一定是其m_pRight节点的左子树的最右节点
29             // 否则,通过不断访问m_pRight一定最后到达树的最右节点,即其右指针为NULL
30             // 此时需要调整当前节点的m_pRight为其右子树的最左节点
31             if(pNode->m_pRight == NULL)
32             {
33                 pNode = pCurrentNode->m_pRight->m_pLeft;
34                 while(pNode->m_pLeft != NULL)
35                     pNode = pNode->m_pLeft;
36
37                 // pCurrentNode的右指针调整为其右子树的最左节点
38                 pCurrentNode->m_pRight = pNode;
39             }
40         }
41     }
42 }
43
44 void AdjustLeft(BinaryTreeNode* pCurrentNode)
45 {
46     if (pCurrentNode->m_pRight != NULL)
47         pCurrentNode->m_pRight->m_pLeft = pCurrentNode;
48     AdjustLeft(pCurrentNode->m_pRight);
49 }
50
51 BinaryTreeNode* Convert(BinaryTreeNode* pRoot)
52 {
53     if (pRoot == NULL) return NULL;
54
55     // 为无右子树的节点填写其右子树指针的值,其值为有序链表中该节点的下一个节点的地址
56     AddRight(pRoot, NULL);
57
58     BinaryTreeNode* pNode = pRoot; // pNode用于遍历
59     while (pNode->m_pLeft != NULL)
60         pNode = pNode->m_pLeft;
61
62     BinaryTreeNode* ListHead = pNode;    // ListHead为有序链表的头,是二叉搜索树的最左节点
63
64     // Add后每个节点的右子树指针都不为空,除了最右节点
65     while (pNode->m_pRight != NULL)
66     {
67         AdjustRight(pNode);
68         pNode = pNode->m_pRight;
69     }
70
71     AdjustLeft(ListHead);
72     ListHead->m_pLeft = NULL;
73     return ListHead;
74 }

  入口函数为Convert(BinaryTreeNode* pRoot),这个实现代码中其实还是有递归的,就是在最后调整左指针的时候,但是这个改为非递归,非常简单,所以我就没有去实现,算是一个不足吧。

时间: 2024-10-10 14:25:12

4.二叉搜索树转为有序双向链表(递归算法与非递归算法)的相关文章

九:二叉搜索树与双向链表(二叉搜索树转为有序双向链表)

问题描述: 输入一棵二叉搜索树,现在要将该二叉搜索树转换成一个排序的双向链表.而且在转换的过程中,不能创建任何新的结点,只能调整树中的结点指针的指向来实现. 解析: 借助二叉树中序遍历,因为中序遍历二叉搜索树的特点就是从小到大访问结点.当遍历访问到根结点时,假设根结点的左侧已经处理好,只需将根结点与上次访问的最近结点(左子树中最大值结点)的指针连接好即可.进而更新当前链表的最后一个结点指针. 递归算法: (1)中序遍历. (2)原先指向左子结点的指针调整为链表中指向前一个结点的指针,原先指向右子

二叉搜索树转换为有序双向链表

http://blog.csdn.net/ljianhui/article/details/22338405 一.问题描述 输入一棵二叉搜索树,现在要将该二叉搜索树转换成一个排序的双向链表.而且在转换的过程中,不能创建任何新的结点,只能调整树中的结点指针的指向来实现. 二.实现思路 在二叉搜索树中,每个结点都有两个分别指向其左.右子树的指针,左子树结点的值总是小于父结点的值,右子树结点的值总是大于父结点的值.而在双向链表中,每个结点也有两个指针,它们分别指向前一个结点和后一个结点.所以这两种数据

二叉搜索树变成有序双向链表,要求不能创建新的结点,只调整指针的指向

二叉搜索树的结点有2个指针,分别指向左右孩子,双链表的每个结点也有2个指针,分别指向前后结点,所以在不创建新结点,只调整指针指向时可以将二叉搜索树变成双向链表:又由于二叉搜索树在中序遍历时是有序的,所以可以采用中序处理二叉搜索树调整指针指向将其变成有序双向链表.为了简化指针移动操作,我们让左孩子为前向指针,右孩子为后向指针. 二叉搜索树的最左结点即使整个树中最小的结点,所以首先找到最左结点,它就是链表的首结点,链表最后一个结点初始化为空.中序遍历时,当前结点的左孩子指向链表的最后一个结点,若最后

[leetcode]426. Convert Binary Search Tree to Sorted Doubly Linked List二叉搜索树转有序双向链表

Convert a BST to a sorted circular doubly-linked list in-place. Think of the left and right pointers as synonymous to the previous and next pointers in a doubly-linked list. Let's take the following BST as an example, it may help you understand the p

将二叉搜索树转换为有序的双向链表

题目:给出一个二叉搜索树,要求将它转换为有序的双向链表,要求不能添加新的节点空间. 思路:有序的双向链表,二叉搜索树的中序遍历是有序的. 递归: public class TreeNodeDemo { private static TreeNode head = null; private static TreeNode tail = null; public static void main(String[] args) { TreeNode tn10 = new TreeNode(10);

二叉搜索树转化为双向链表

1. 题目 输入一棵二叉搜索树,现在要将该二叉搜索树转换成一个排序的双向链表.而且在转换的过程中,不能创建任何新的结点,只能调整树中的结点指针的指向来实现. 二叉树结点的定义如下: struct BinaryTreeNode { intm_nValue; BinaryTreeNode*m_pLeft; BinaryTreeNode*m_pRight; }; 如图1-1(a)为一棵二叉搜索树,1-1(b)为对应的双向链表. 图1-1转化示意图 2. 分析 (1) 二叉搜索树中序遍历可得有序的结点序

微软算法100题01 二叉搜索树转为双向链表

提高编程能力的最佳途径就是多写代码, 就让我们从现在开始吧! 1. 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向.       10      /    |   6     14 /   |     /  |4   8   12  16 转换成双向链表4=6=8=10=12=14=16. 二叉查找树的特点:任意节点的左子树都要小于当前节点:右子树都要大于当前节点.特点:查询某个值,需要的时间复杂度为O(lgN) 网上的解决方案大部分都是C

二叉搜索树转换成双向链表

好一点点就是好一点点嘛 RT 传入3个参数 <根节点,上次访问的节点,头结点>.Yahoo二面被问到!完跪.... 1 void BST2DoubleList(TreeNode *root, TreeNode *& prev, TreeNode *& head) { 2 if (root == NULL) 3 return; 4 BST2DoubleList(root->left, prev, head); 5 root->left = prev; 6 if (pr

递归之(二叉搜索树和排序双向链表)

链接:https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId=13&tqId=11179&rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking 代码如下: 原文地址:https://www.cnblogs.com/zf-blog/p/9880108.html