目录
(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;
}
}
}
}