头图

1. 题目

描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。如下图所示

数据范围:输入二叉树的节点数 0:≤n≤1000,二叉树中每个节点的值 0≤val≤1000
要求:空间复杂度O(1)(即在原树上操作),时间复杂度 O(n)

注意:

  1. 要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继
  2. 返回链表中的第一个节点的指针
  3. 函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构
  4. 你不用输出双向链表,程序会根据你的返回值自动打印输出

输入描述:

二叉树的根节点

返回值描述:

双向链表的其中一个头节点。

示例1

输入:

{10,6,14,4,8,12,16}

返回值:

From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;

说明:

输入题面图中二叉树,输出的时候将双向链表的头节点返回即可。     

示例2

输入:

{5,4,#,3,#,2,#,1}

返回值:

From left to right are:1,2,3,4,5;From right to left are:5,4,3,2,1;

说明:

                    5
                  /
                4
              /
            3
          /
        2
      /
    1
树的形状如上图       

2. 解题思路

二叉树转双向链表可以通过两步来完成:首先确定双向链表的头结点(头节点:二叉树最左侧的节点);之后再通过递归将二叉树转为双向链表。

具体操作步骤如下:

假如需要转换的二叉搜索树结构如下图所示:

步骤一:首先确定双向链表的头结点。

二叉搜索树具有以下特点:

  • 每个节点最多有两个子节点:左子节点右子节点
  • 节点的值关系

    • 左子树中所有节点的值 小于 当前节点的值。
    • 右子树中所有节点的值 大于 当前节点的值。

中序遍历结果是有序序列:对二叉搜索树进行中序遍历(左 → 根 → 右),可以得到一个升序排列的数值序列。

根据题目的要求,需要将二叉搜索树转换为有序链表。而对于二叉搜索树来说最左侧的节点值是最小的,因此可以将二叉搜索树最左侧的节点作为双向链表的头结点。

步骤二:通过递归将二叉树转为双向链表。

如果将二叉搜索树通过中序遍历(左 → 根 → 右),可以得到一个升序排列的结果。因此可以通过二叉搜索树中序遍历的思想来实现链表的转换。

对于递归来说,我们写递推公式时只需考虑两层的关系(即:f(n)与f(n-1)之间的关系,递归详细内容讲解可以参考《递归》一篇的文章),这时我们可以将二叉树进行简化:

定义变量pre,初始化时为Null。pre变量指向如下图所示:

对于这样一颗二叉树来说,可以通过3步将其转换为双向链表:

①更改root的左指针,让它指向左子树,root.left=pre。

②更改左子树的右指针,让它指向root,pre.right=root。

③移动操作节点变量,让它指向root(便于进行下一轮的递归),pre=root。

对应的递推公式如下:

有了递推公式,就可以很方便的写出代码。

如果文字描述的不太清楚,你可以参考视频的详细讲解。

3. 编码实现

核心代码如下:

/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param pRootOfTree TreeNode类
* @return TreeNode类
 */
func Convert(pRootOfTree *TreeNode) *TreeNode {
    // write code here
    if pRootOfTree == nil {
        return nil
    }
  //1.查找二叉搜索树最左侧的节点
    p := pRootOfTree
    for p.Left != nil {
        p = p.Left //找到链表的头(最左侧的二叉树节点)
    }
  //2.通过中序遍历的方法转换二叉树为链表
    inOrder(pRootOfTree)
    return p

}

var pre *TreeNode //定义一个前序节点

func inOrder(root *TreeNode) {
    // 2. 递归终止条件
    if root == nil {
        return
    }

    // 1. 问题分解(递推公式)
    // 1.1 到左子树进行相似的处理
    inOrder(root.Left)
    // 1.2 对当前节点进行处理
    root.Left = pre //当前节点的左指针指向前序节点
    if pre != nil {
        pre.Right = root //前序节点的右指针指向当前节点
    }
    pre = root //移动指针变量
    // 1.3 到右子树进行相似的处理
    inOrder(root.Right)

}

具体完整代码你可以参考下面视频的详细讲解。

4.小结

二叉树转双向链表可以通过两步来完成:首先确定双向链表的头结点(头节点:二叉树最左侧的节点);之后再通过递归中序遍历的思想将二叉树转为双向链表。


《数据结构与算法》深度精讲课程正式上线啦!七大核心算法模块全解析:

✅ 链表 ✅ 二叉树 ✅二分查找、排序 ✅ 堆、栈、队列 ✅回溯算法 ✅ 哈希算法 ✅ 动态规划

无论你是备战笔试面试、提升代码效率,还是突破技术瓶颈,这套课程都将为你构建扎实的算法思维底座。🔥立即加入学习打卡,与千名开发者共同进阶!

对于二叉树的相关算法,我们总结了一套【可视化+图解】方法,依据此方法来解决相关问题,算法变得易于理解,写出来的代码可读性高也不容易出错。具体也可以参考视频详细讲解。

今日佳句:人皆知有用之用,而莫知无用之用也。


好易学数据结构
1 声望0 粉丝