题目要求:
只移动树的左右指针将二叉树转换成双向链表,链表遍历和二叉树中序遍历一致。过程不能创建新的对象。
思路:
将一棵树的都抽象为父节点,左节点,右节点。中序遍历的遍历顺序是 左中右。所以:
1. 从根节点开始先检索左子树,一直检索到某个节点A的子节点是叶子节点(C、D)。对A的左以下操作:
- 将C的右节点指向其父节点A
- 将D的左指针指向A
2. 将CAD作为一个整体即一个节点1,节点1的右指针是D 的右指针。节点1的做指针就是C的做指针。
同时将原来A 的兄弟节点做以上同样的操作得到节点2(acd)。同样将1、2和他们的父节点k做以上的两个步骤。
以下是代码实现:
假设二叉树对象是:
public class TestObject {
private int data;
private TestObject left;
private TestObject right;
}
实现是:
/**
* 二叉树变为双向链表
* @param last 双向链表的最后一个元素,初始为null;
* @param tree 当前解析的左中右 的中节点 初始为树的根节点
* @return
*/
/**
* 二叉树变为双向链表
* @param last 双向链表的最后一个元素,初始为null;
* @param tree 当前解析的左中右 的中节点 初始为树的根节点
* @return
*/
public static TestObject testMethod1(TestObject last, TestObject tree) {
if (tree == null) {
return null;
}
TestObject left = tree.getLeft();
TestObject right = tree.getRight();
//左边的元素已经是变化过的 左中右的 左
if (left != null) {
//子节点是叶子节点时 就是链表的最后一个节点
if(left.getLeft() == null && left.getRight() == null){
if(last != null){
last.setRight(left);
left.setLeft(last);
}
last = left;
} else {
//非叶子节点,获取节点的左边序列 leftTreeLast为左边序列的最后一个元素
TestObject leftTreeLast = testMethod1(last, left);
//最后一个节点中加入左边的节点
if (last != null) {
if (leftTreeLast != null) {
last.setRight(leftTreeLast);
leftTreeLast.setLeft(last);
leftTreeLast.setRight(null);
last = leftTreeLast;
}
} else {
if (leftTreeLast != null) {
leftTreeLast.setRight(last);
last = leftTreeLast;
last.setRight(null);
}
}
}
}
//序列的最后一个元素直线当前节点,即左中右的 中
if(last != null) {
last.setRight(tree);
//根节点的左指针指向双向链表的最后节点
tree.setLeft(last);
last = last.getRight();
last.setRight(null);
} else {
//走到这里表示,左边没有子节点
last = tree;
}
//序列的最后一个元素直线当前节点,即左中右的 右
TestObject rightTreeLast = null;
//获取右节点
if (right != null) {
//子节点是叶子节点时 就是链表的最后一个节点
if(right.getLeft() == null && right.getRight() == null){
if(last != null){
last.setRight(right);
right.setLeft(last);
}
last = right;
} else {
//非根节点,获取右边的序列
last = testMethod1(last, right);
}
}
last.setRight(null);
return last;
}
上面的代码还是太复杂,优化之后是:
/**
* 二叉树变为双向链表
* @param last 双向链表的最后一个元素,初始为null;
* @param tree 当前解析的左中右 的中节点 初始为树的根节点
* @return
*/
public static TestObject testMethod(TestObject last, TestObject tree) {
if (tree == null) {
return null;
}
TestObject left = tree.getLeft();
TestObject right = tree.getRight();
//子节点是叶子节点时,则设置双向链表最新的元素时该元素
if(left == null && right == null){
if(last != null){
last.setRight(tree);
tree.setLeft(last);
}
last = tree;
return last;
}
//左中右的 左
//获取左节点的
if (left != null) {
//非根节点,获取左边的序列
last = testMethod(last, left);
}
//即左中右的 中
//序列的最后一个元素直线当前节点,即左中右的 中
if(last != null) {
last.setRight(tree);
//根节点的左指针指向双向链表的最后节点
tree.setLeft(last);
last = last.getRight();
last.setRight(null);
} else {
//走到这里表示,左边没有子节点
last = tree;
}
即左中右的 右
if (right != null) {
//非根节点,获取右边的序列
last = testMethod(last, right);
}
last.setRight(null);
return last;
}
接下来就可以使用测试方法了:
package com.zhong.demo.usultestdemo.testdemo.demo_3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author yi xiangxuehai
* @date 2021/3/30-14:28
*/
public class TestMethod {
public static void main(String[] args) {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
System.out.println("根节点值:");
try {
String value = br.readLine();
if(!value.equals("0")) {
TestObject testObject = new TestObject();
testObject.setData(Integer.valueOf(value));
inputTree(testObject, br);
System.out.print("中序遍历 ");
centerScan(testObject);
System.out.println();
TestObject linked = testMethod(null, testObject);
System.out.print("链表 ");
linkedScan(linked);
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//输入二叉树,先中再左,再右。以0表示不需要子节点
public static void inputTree(TestObject testObject, BufferedReader br){
try {
System.out.println("请输入"+ testObject.getData() +"的左节点值(0表示无):");
String value = br.readLine();
if(!value.equals("0")) {
TestObject left = new TestObject();
testObject.setLeft(left);
left.setData(Integer.valueOf(value));
inputTree(left, br);
}
System.out.println("请输入"+ testObject.getData() +"的右节点值(0表示无):");
value = br.readLine();
if(!value.equals("0")) {
TestObject right = new TestObject();
right.setData(Integer.valueOf(value));
testObject.setRight(right);
inputTree(right, br);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//中序遍历
public static void centerScan(TestObject tree) {
if (tree.getLeft() != null) {
centerScan(tree.getLeft());
}
System.out.print(tree.getData() + " ");
if (tree.getRight() != null) {
centerScan(tree.getRight());
}
}
//遍历链表
public static void linkedScan(TestObject tree){
TestObject testObject = tree;
while(testObject.getLeft() != null){
testObject = testObject.getLeft();
}
while (testObject != null){
System.out.print(testObject.getData()+ " ");
testObject = testObject.getRight();
}
}
}
测试结果如下:
根节点值:
5
请输入5的左节点值:
4
请输入4的左节点值:
2
请输入2的左节点值:
0
请输入2的右节点值:
0
请输入4的右节点值:
3
请输入3的左节点值:
0
请输入3的右节点值:
0
请输入5的右节点值:
6
请输入6的左节点值:
7
请输入7的左节点值:
0
请输入7的右节点值:
0
请输入6的右节点值:
0
中序遍历:2 4 3 5 7 6
链表:2 4 3 5 7 6