链表与二叉树的转化

 

目录

(leetcode109) 有序链表转换二叉搜索树(中等)

方法一:集合操作链表

方法二:直接操作链表

(leetcode109) 二叉树展开为链表(中等)

方法一:前序遍历展开二叉树(递归法)

方法二:前序遍历展开二叉树(迭代法)

方法三:二叉树的展开


(leetcode109) 有序链表转换二叉搜索树(中等)

方法一:集合操作链表

对于链表的题目,由于链表不像数组,不能直接操作,所以我们应该想到将链表存入到集合中,利用集合操作链表,完成创建二叉搜索树。观察题目可以发现,链表的中间节点就是根节点。链表根节点的左边构成左子树,根节点的右边构成右子树。

思路分析(此时的left,right,mid指的是集合中元素下标) :

1:判断头结点是否为空,判断链表中是否只有一个节点(即头结点的下一个节点为空),如果链表中只有一个节点,直接将链表的头结点的值设置为树的根节点。

2:创建一个集合,指定泛型类型为Integer,遍历链表,将链表中的所有节点依次添加到集合中

3:定义一个创建二叉搜索树的方法buildTree(),链表的头结点的索引下标为left,链表的最后一个节点的索引下标为right。中间节点的索引下标为mid。mid=left+(right-left)/2。

4:创建树的根节点,根节点的值就是链表中间节点的值。用递归的方法创建二叉搜索树。 

5:在题目给定的方法中调用buildTree(),这里注意传参时,因为下标从0开始,所以list.size()-1

 

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        List<Integer> list=new ArrayList<>();
       if(head==null){
           return null;
       }
        if(head.next==null){
            TreeNode root=new TreeNode(head.val);
            return root;
        }
        while(head!=null){
            list.add(head.val);
            head=head.next;
        }
        System.out.println(list);
        return buildTree(list,0,list.size()-1);
    }
    public TreeNode buildTree(List<Integer> list,int left,int right){
        if(left>right){
            return null;
        }
        int mid=left+(right-left)/2;
       //int mid=(right+left)/2;
        TreeNode root=new TreeNode(list.get(mid));
        root.left=buildTree(list,left,mid-1);
        root.right=buildTree(list,mid+1,right);
        return root;
    }
}

方法二:直接操作链表

 对链表进行操作,那么就需要找到链表的中间节点,利用快慢双指针,把中间节点设置为二叉搜索树的根节点

思路分析:(双指针解法)(此处的left,right,mid指的是链表中的头结点,中间节点和尾节点)

1:先定义快慢双指针,fast和slow,快指针移动速度是慢指针的两倍,当快指针的下一个节点为空或者快指针已经为空时,此时慢指针刚好指向链表的中间节点。

2:定义一个创建二叉搜索树的方法,传入两个参数leftNode和rightNode。

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        if(head==null){
            return null;
        }
        return buildTree(head,null);
    }
         public TreeNode buildTree(ListNode leftNode,ListNode rightNode){
             if(leftNode==rightNode){
                 return null;
             }
             ListNode midNode=getMid(leftNode,rightNode);
             TreeNode root=new TreeNode(midNode.val);
             root.left=buildTree(leftNode,midNode);
             root.right=buildTree(midNode.next,rightNode);
             return root;

        }
        public ListNode getMid(ListNode leftNode,ListNode rightNode){
            ListNode fast=leftNode;
            ListNode slow=leftNode;
            while(fast!=rightNode&&fast.next!=rightNode){
                fast=fast.next.next;
                slow=slow.next;
            }
            return slow;
        }
}

 

(leetcode109) 二叉树展开为链表(中等)

 

方法一:前序遍历展开二叉树(递归法)

由题意可知,链表的节点顺序就是二叉树前序遍历的顺序,因为我们可以想到定义一个集合,将二叉树前序遍历的节点顺序存入到集合中,再遍历集合。因为要构成一个具有TreeNode的单链表,因此集合的泛型定义为TreeNode。取出集合中的元素构成一个只有右子节点的单链表。前序遍历的思路分析可以看我的另一篇博客二叉树的遍历https://blog.csdn.net/StevenBian/article/details/124608658?spm=1001.2014.3001.5501

 

class Solution {
    public void flatten(TreeNode root) {
        //定义一个集合,用于存储二叉树前序遍历所有节点
       List<TreeNode> list=new ArrayList<>();
       //定义指向当前节点的指针curNode
       TreeNode curNode=null;
       //定义指向当前节点的前驱节点的指针preNode
       TreeNode preNode=null;
       //前序遍历二叉树
       preOrder(list,root);
       //注意这里要从1开始,如果从0开始,当前驱节点取i-1时会抛出异常:非法索引
       for(int i=1;i<list.size();i++){
           //当前节点
          curNode = list.get(i);
          //前驱节点
          preNode = list.get(i-1);
          //前驱节点的右子节点为当前节点
          preNode.right=curNode;
          //前驱节点的左子节点为空
          preNode.left=null;
       }
    }
    //定义一个对二叉树前序遍历的方法,无返回值,直接存入到list集合中
    public void preOrder(List<TreeNode> list,TreeNode root){
        if(root==null){
            return ;
        }
        list.add(root);
        preOrder(list,root.left);
        preOrder(list,root.right);
    }
}

方法二:前序遍历展开二叉树(迭代法)

迭代法思路分析可以参照方法一中给出的二叉树的遍历的博客链接,这里就不多赘述

class Solution {
    public void flatten(TreeNode root) {
        //定义一个集合用于存储二叉树前序遍历的所有节点
        List<TreeNode> list=new ArrayList<>();
        //定义一个栈,利用栈先进后出的原则,前序遍历出二叉树
        Stack<TreeNode> stack=new Stack<>();
        while(root!=null || !stack.isEmpty()){
        while(root!=null){
            list.add(root);
            stack.push(root);
            root=root.left;
        }
        //当二叉树的左子树遍历完时,此时所有根节点已经全部存入集合中
        //取出栈中的元素
        root=stack.pop();
        //如果其右子节点不为空,并且栈不为空就把右子节点添加到集合中
        root=root.right;
        //否则继续从栈中pop出节点
       }
       //循环结束后,前序遍历完成
       for(int i=1;i<list.size();i++){
           TreeNode preNode=list.get(i-1);
           TreeNode curNode=list.get(i);
           preNode.left=null;
           preNode.right=curNode;
       }
    }
}

 

方法三:二叉树的展开

有一种最直接的思路,就是将二叉树直接展开成right子指针指向链表中下一个结点,而左子指针始终为null的单链表。大致思路如下:因为单链表的左子树为空,因此我们要将二叉树根节点的左子树挂载到右子树上,可以用到迭代

思路分析

1:因为二叉树满足左子节点的值<根节点的值<右子节点的值,想要将左子树整体挂载到右子树上,并且还要满足二叉树的节点顺序,就必须先将根节点的右子树挂载到根节点的左子树的最右子节点上。

2:判断左子树是否为空,若为空则将当前节点curNode右移。

3:遍历根节点的左子树,定义一个指针temp,将temp不断的向右遍历,直到根节点的左子树的右子节点为叶子结点,将根节点的右子树挂载到temp的右边

第一步                                                                                  第二步 

 

 

 第三步                                                                          第四步

 

 

 第五步

 

 

 

class Solution {
    public void flatten(TreeNode root) {
    if(root==null){
        return;
    }
    //定义指向根节点的指针curNode
    TreeNode curNode=root;
     while(curNode!=null){
         if(curNode.left!=null){
             //定义指向根节点的左子节点的指针leftNode;
             TreeNode leftNode=curNode.left;
             TreeNode temp=leftNode;
             while(temp.right!=null){
                 //遍历找到根节点左子树的最右子节点
                 temp=temp.right;
             }
             //将根节点的左子树挂载到最右子节点的右子节点
             temp.right=curNode.right;
             //根节点的左子树移到根节点的右边
             curNode.right=leftNode;
             curNode.left=null;

         }else{
             curNode=curNode.right;
         }
     }
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值