二叉树转成双向链表(只动指针)

/**
 * 二叉树后续遍历转双向链表
 */
public class TreeNodeToNode {

    public static void main(String[] args) {

        /**
         *       10
         *     /   \
         *   8       16
         *          / \
         *         13  18
         */
        TreeNode treeNode5 = new TreeNode();
        treeNode5.val = 18;

        TreeNode treeNode4 = new TreeNode();
        treeNode4.val = 13;

        TreeNode treeNode3 = new TreeNode();
        treeNode3.val = 16;
        treeNode3.left = treeNode4;
        treeNode3.right = treeNode5;

        TreeNode treeNode2 = new TreeNode();
        treeNode2.val = 8;

        TreeNode treeNode1 = new TreeNode();
        treeNode1.val = 10;
        treeNode1.left = treeNode2;
        treeNode1.right = treeNode3;

        TreeOperation.show(treeNode1);

        TreeNode newTree = convertTreeToNode(treeNode1,null);


        TreeNode headNode = newTree;

        while (headNode.left != null) {

            headNode = headNode.left;
        }


        while (headNode != null) {

            System.out.println(headNode.val);

            headNode=headNode.right;
        }


    }

    public static TreeNode convertTreeToNode(TreeNode head,TreeNode lastNode){

        if(head == null){
            return lastNode;
        }

        TreeNode curNode = head;

        if(curNode.left != null) {
            lastNode = convertTreeToNode(curNode.left, lastNode);
        }

        curNode.left = lastNode;

        if(lastNode != null){
            lastNode.right = curNode;
        }

        lastNode = curNode;

        if(curNode.right != null) {
            lastNode = convertTreeToNode(curNode.right, lastNode);
        }


        return lastNode;
    }


    static class Node{
        int data;
        Node pre;
        Node next;
    }
}
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
}


/**
 * 按照树形结构直观地打印出一棵二叉树(Java)
 */
public class TreeOperation {
    /*
    树的结构示例:
              1
            /   \
          2       3
         / \     / \
        4   5   6   7
    */
 
    // 用于获得树的层数
    public static int getTreeDepth(TreeNode root) {
        return root == null ? 0 : (1 + Math.max(getTreeDepth(root.left), getTreeDepth(root.right)));
    }
 
 
    private static void writeArray(TreeNode currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) {
        // 保证输入的树不为空
        if (currNode == null) return;
        // 先将当前节点保存到二维数组中
        res[rowIndex][columnIndex] = String.valueOf(currNode.val);
 
        // 计算当前位于树的第几层
        int currLevel = ((rowIndex + 1) / 2);
        // 若到了最后一层,则返回
        if (currLevel == treeDepth) return;
        // 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔)
        int gap = treeDepth - currLevel - 1;
 
        // 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
        if (currNode.left != null) {
            res[rowIndex + 1][columnIndex - gap] = "/";
            writeArray(currNode.left, rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
        }
 
        // 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值
        if (currNode.right != null) {
            res[rowIndex + 1][columnIndex + gap] = "\\";
            writeArray(currNode.right, rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
        }
    }
 
 
    public static void show(TreeNode root) {
        if (root == null) System.out.println("EMPTY!");
        // 得到树的深度
        int treeDepth = getTreeDepth(root);
 
        // 最后一行的宽度为2的(n - 1)次方乘3,再加1
        // 作为整个二维数组的宽度
        int arrayHeight = treeDepth * 2 - 1;
        int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
        // 用一个字符串数组来存储每个位置应显示的元素
        String[][] res = new String[arrayHeight][arrayWidth];
        // 对数组进行初始化,默认为一个空格
        for (int i = 0; i < arrayHeight; i ++) {
            for (int j = 0; j < arrayWidth; j ++) {
                res[i][j] = " ";
            }
        }
 
        // 从根节点开始,递归处理整个树
        // res[0][(arrayWidth + 1)/ 2] = (char)(root.val + '0');
        writeArray(root, 0, arrayWidth/ 2, res, treeDepth);
 
        // 此时,已经将所有需要显示的元素储存到了二维数组中,将其拼接并打印即可
        for (String[] line: res) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < line.length; i ++) {
                sb.append(line[i]);
                if (line[i].length() > 1 && i <= line.length - 1) {
                    i += line[i].length() > 4 ? 2: line[i].length() - 1;
                }
            }
            System.out.println(sb.toString());
        }
    }
}
 

 

      10     
    /   \    
  8       16 
         / \ 
        13  18
8
10
13
16
18

剑指offer解题报告(Java版)——二叉搜索树转换为双向链表 27

引言

   

自己觉得对二叉树了解的不是很多,所以想专门练习一下这方面的问题,剑指Offer中有一道题是将二叉搜索树转换为双向链表,开始的时候照着书上的思路去做,最后发现问题很多,看来这本书也有很多问题啊,原因是Java和C++还是有很多不同的,特别是对对象的引用方面。自己在红色部分标注出了问题的所在,希望能给之后遇到同样问题的人一点帮助

   

分析问题

   

首先需要明白二叉搜索树也是一种排序的数据结构,它的中序遍历就是一个不递减的顺序排列

所以如果要转换成一个排序好的双向链表,那么仅需要改变原来指向左子节点和右子节点的指针,让他们分别指向前节点和后节点即可,如图所示

调整指针

原先指向左子节点的指针调整为链表中指向前一个节点的指针

原先指向右子节点的指针调整为链表中指向后一个节点的指针

如何调整

考虑根节点和左右子树的根本情况,因为如果用递归,这种根本情况考虑就可以去将同样的方法用到左右子树上

对于这种基本情况,可以分成三个部分来看,根节点10,左子树,右子树,需要做的就是将10与左子树中的最大值连起来,然后把10与右子树中的最小值连起来

   

现在有个问题就是我们并不知道左子树中的最大值和右子树中的最小值,如果我们知道就好了。但是想到递归,递归到左子树中,如果左子树已转换为双向链表,那么双向链表的最后一个节点就是我们想要的,而右子树中的第一个节点也是我们想要的

转换代码:

public static BinaryTreeNode baseconvert(BinaryTreeNode root, BinaryTreeNode lastNode) {

if (root == null)

return lastNode;

BinaryTreeNode current = root;

if (current.leftNode != null)

lastNode=baseconvert(current.leftNode, lastNode);

current.leftNode = lastNode;

if (lastNode != null)

lastNode.rightNode = current;

lastNode = current;

if (current.rightNode != null)

lastNode=baseconvert(current.rightNode, lastNode);

return lastNode;

}

要说明的一点是,剑指Offer书上是用C++写的,Java写的时候有点不同,就是baseconvert也要指定返回值,不然会报nullpointer的错误,估计是因为java中只是对象的引用,不然每次返回的lastNode都是空

   

上面的代码中有两个参数,一个是根节点,一个是已经转换好的链表的最后一个节点,因为二叉搜索树中序遍历的特性,当遍历到根节点的时候,左子树已经排好序了,所以会有一个左子树已经转换好的链表,而这个链表的最后一个节点即是我们需要和根节点左连的节点

最开始的时候lastNode为null

current为当前子树的根节点

如果左子树存在,那么转换左子树,递归下去,递归返回之后,说明找到了链表的第一个节点,也就是4那个节点,将4的前面节点置为null,此时current为4那个节点,这个时候由于6的4这个左子树已遍历完了,所以需要往上层返回,返回之前需要将current赋值给lastNode,说明4这个左子树的最后一个节点就是4

   

由于往上返回了一层,此时的current已经是6了,将6的左节点赋值为之前返回的4,判断之前返回的lastNode是否为null,不为空说明需要把根节点和lastNode连起来,其实lastNode为null的情况就只有第一个节点会出现,其他的时候都不会出现。现在已排好序的包括6的左子树以及6本身了,所以此时的lastNode为6

   

6和4的双向连接就完成了,由于6的右子树存在,又会递归到右边子树去,由于8不存在左右子树,递归下去一层之后current就是8这个节点,但它的左孩子为空,所以不会左边递归下去,将8的左连接与之前的lastNode连接起来,建立双向连接的一条连接,然后由于lastNode不为空,所以又把lastNode的右连接与8连接起来,至此双向连接建立,此时lastNode为8

所以468都已排好序,此时lastNode为8,返回到上一层,也就是根节点10了,在这一层current为10,将current的左连接与lastNode连接起来,如果lastNode存在,将lastNode的右连接与10连接一起,以此建立双向连接。至此就将根节点和左子树都连接起来了,然后就是去转换右子树,现在的lastNode为10,current为14,14有左孩子,所以需要递归到下一层,下一层的current为12,12没有左孩子,所以不用在坐递归,所以12是12这棵子树转换成双向链表的最左边的节点,将lastNode与12连接,也就是10与12连接,此时的lastNode就变成了12,再将12的右子树递归,由于没有右子树,所以直接返回到上一层,上一层的current是14,14与已排好序的lastNode连接,也就是12与14连接,然后lastNode变为14,递归到14的右子树,也就current变为16,16再递归左子树,无左子树,将16与14连接,此时的lastNode变为16,递归右子树,无右子树,所以整个递归工作完成 

找到头节点 

之前的转换只是把引用乾坤大挪移了一下,其实可以发现引用的个数并没有变化,而且头尾节点只有一个出度的引用,而现在我们如果要使用这个双向链表,需要找到头节点

public static BinaryTreeNode convert(BinaryTreeNode root) {

BinaryTreeNode lastNode = null;

lastNode=baseconvert(root, lastNode);

BinaryTreeNode headNode = lastNode;

while (headNode.leftNode != null)

headNode = headNode.leftNode;

return headNode;

}

测试代码

public static void main(String[] args) {

// TODO Auto-generated method stub

BinaryTreeNode root = new BinaryTreeNode(10);

BinaryTreeNode six=new BinaryTreeNode(6);

BinaryTreeNode four=new BinaryTreeNode(4);

BinaryTreeNode eight=new BinaryTreeNode(8);

BinaryTreeNode fourteen=new BinaryTreeNode(14);

BinaryTreeNode twelve=new BinaryTreeNode(12);

BinaryTreeNode sixteen=new BinaryTreeNode(16);

root.leftNode=six;

root.rightNode=fourteen;

six.leftNode=four;

six.rightNode=eight;

four.leftNode=null;

four.rightNode=null;

eight.leftNode=null;

eight.rightNode=null;

fourteen.leftNode=twelve;

fourteen.rightNode=sixteen;

twelve.leftNode=null;

twelve.rightNode=null;

sixteen.rightNode=null;

sixteen.leftNode=null;

BinaryTreeNode result=convert(root);

//                BinaryTreeNode result=baseconvert(root, null);

while (result!=null) {

System.out.println(result.data);

result=result.rightNode;

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值