二叉树 双向链表 java_【Java】 剑指offer(36) 二叉搜索树与双向链表

本文参考自《剑指offer》一书,代码采用Java语言。

题目

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

思路

二叉搜索树、排序链表,想到使用中序遍历。

要实现双向链表,必须知道当前结点的前一个结点。根据中序遍历可以知道,当遍历到根结点的时候,左子树已经转化成了一个排序的链表了,根结点的前一结点就是该链表的最后一个结点(这个结点必须记录下来,将遍历函数的返回值设置为该结点即可),链接根结点和前一个结点,此时链表最后一个结点就是根结点了。再处理右子树,遍历右子树,将右子树的最小结点与根结点链接起来即可。左右子树的转化采用递归即可。

大概思想再理一下:首先想一下中序遍历的大概代码结构(先处理左子树,再处理根结点,之后处理右子树),假设左子树处理完了,就要处理根结点,而根结点必须知道左子树的最大结点,所以要用函数返回值记录下来;之后处理右子树,右子树的最小结点(也用中序遍历得到)要和根结点链接。

注意搞清楚修改后的中序遍历函数的意义(见代码注释)

测试算例

1.功能测试(一个结点;左右斜树;完全二叉树;普通二叉树)

2.特殊测试(根结点为null)

Java代码

//题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求

//不能创建任何新的结点,只能调整树中结点指针的指向。

public class ConvertBinarySearchTree {

public class TreeNode {

int val = 0;

TreeNode left = null;

TreeNode right = null;

public TreeNode(int val) {

this.val = val;

}

}

public TreeNode convert(TreeNode head) {

if(head==null)

return head;

TreeNode lastNodeInList=null;

lastNodeInList=convertHelper(head,lastNodeInList);

TreeNode firstNodeInList=lastNodeInList;

while(firstNodeInList.left!=null) {

firstNodeInList=firstNodeInList.left;

}

return firstNodeInList;

}

//将以node为根结点的树转化为排序链表,链表头部与lastNode链接,并返回最后一个结点

private TreeNode convertHelper(TreeNode node,TreeNode lastNode) {

//处理左子树,获得最大结点

if(node.left!=null)

lastNode=convertHelper(node.left, lastNode);

//链接最大结点和根结点

node.left=lastNode;

if(lastNode!=null)

lastNode.right=node;

//处理右子树

lastNode=node;

if(node.right!=null)

lastNode=convertHelper(node.right, lastNode);

return lastNode;

}

}

上面的代码是参考《剑指OFFER》写的,下面的代码是复习时重新写过的,思路比较简洁一点。非递归方法的核心是中序遍历的非递归实现。

public class Solution {

/*

* 递归版本

* 1.已知函数返回的是转换好的双向链表头结点

* 2.左子树处理完后与根结点连接

* 3.右子树处理,也与根结点连接

* 4.最后返回头结点

*/

public TreeNode Convert(TreeNode root) {

if (root == null)

return root;

// 处理左子树,获得左子树链表的头结点

TreeNode left = Convert(root.left);

TreeNode p = left;

if (left != null) {

// 找到左子树链表的末尾结点

while (p.right != null)

p = p.right;

// 连接结点

p.right = root;

root.left = p;

}

// 处理右子树,获得右子树链表的头结点

TreeNode right = Convert(root.right);

// 连接结点

if (right != null) {

root.right = right;

right.left = root;

}

return left == null ? root : left;

}

/* 非递归版本

* 1.利用非递归中序遍历来连接结点

*/

public TreeNode Convert1(TreeNode root) {

TreeNode head = null;

TreeNode pre = null;

LinkedList stack = new LinkedList<>();

while (root != null || !stack.isEmpty()) {

// 把root当作指针使用

while (root != null) {

stack.push(root);

root = root.left;

}

TreeNode node = stack.pop();

if (head == null) {

head = node;

pre = node;

} else {

node.left = pre;

pre.right = node;

pre = node; // 别漏写了

}

root = node.right;

}

return head;

}

}

收获

题目较复杂时,不要慌。这道题和中序遍历有关,把树分为三部分:根结点、左子树和右子树,思考在中序遍历中根结点应该如何处理,这是关键——要将左子树的最大结点、根结点、右子树的最小结点链接起来。左右子树的处理是相同的,因此采用递归。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值