一、138 复制带随机指针的链表
问题描述
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
返回复制链表的头节点。
用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
- val:一个表示 Node.val 的整数。
- random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
你的代码只接受原链表的头节点 head 作为传入参数。
解题思路
1、map映射
a、第一次遍历,生成一个只具有val属性的链表;
b、第二次遍历,根据map的映射关系,将random和next指针指向对应节点或者null。
var copyRandomList=function(head){
if(head==null) return null;
let map=new Map();
let cur=head;
while(cur){
map.set(cur,new Node(cur.val));
cur=cur.next;
}
cur =head;
while(cur){
map.get(cur).next=map.get(cur.next)||null;
map.get(cur).random=map.get(cur.random)||null;
cur=cur.next;
}
return map.get(head);
};
2、复制节点
var copyRandomList=function(head){
if(head==null) return null;
let cur=head;
//在原链表后续借新节点
while(cur){
let tempNode=new Node(cur.val);
tempNode.next=cur.next;
cur.next=tempNode;
cur=tempNode.next;
}
//为当前每个链表的新节点赋随机值
cur=head;
while(cur&&cur.next){
if(cur.random){
cur.next.random=cur.random.next;
}
cur=cur.next.next;
}
//将链表按照间隔分开 将新节点串成新链表并还原原链表
cur=head.next;
let preNode=head; //原链表
let newhead=head.next; //新链表
while(cur&&cur.next){
preNode.next=preNode.next.next;
cur.next=cur.next.next;
preNode=preNode.next;
cur=cur.next;
}
preNode.next=null; //将原链表的最后一个节点的next重新指向null
return newhead;
};
二、109 有序链表转换二叉搜索树
问题描述
给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为高度平衡的二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。
解题思路
1、快慢指针+中序遍历+递归
a、BST的中序遍历是顺序是升序的,等同于根据中序遍历的序列恢复二叉搜索树;
b、升序序列中中间元素作为根节点(高度平衡),以该元素左边的升序序列构造左子树,以该元素右边的升序序列构造右子树;
c、不断递归左子树和右子树,并将中间元素作为根节点;
d、链表可以通过快慢指针的方式找到中间元素;
e、注意得到的树就是一个二叉搜索树。
var sortedListToBST=function(head){
function travese(head,tail){
if(head===tail) return null;
//利用快慢指针找到中间元素作为根节点
let slow=fast=head;
while(fast!==tail&&fast.next!==tail){
slow=slow.next;
fast=fast.next.next;
}
let root=new TreeNode(slow.val);
root.left=travese(head,slow);
root.right=travese(slow.next,tail);
return root;
}
return travese(head,null);
};
2、数组变二叉树
a、将原链表转化为数组;
b、根据二分法,将数组转化为二叉树;
var sortedListToBST=function(head){
if(head==null) return null;
let cur=head;
let arr=[];
while(head){
arr.push(head.val);
head=head.next;
}
let left=0,right=arr.length-1;
return help(arr,left,right);
};
function help(arr,left,right){
if(left>right) return null;
let mid=Math.floor((right+left)/2);
let root=new TreeNode(arr[mid]);
root.left=help(arr,left,mid-1);
root.right=help(arr,mid+1,right);
return root;
}
3、其他方法
三、114 二叉树展开为链表
问题描述
给你二叉树的根结点 root ,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
- 展开后的单链表应该与二叉树 先序遍历 顺序相同。
解题思路
1、先序遍历
可以根据题解看出生成后的链表是原二叉树先序遍历之后的结果。具体思路如下:
- 右子树转成的单链表只需要获取头结点,等待左子树生成完成后,链接到左子树的尾节点处即可;
- 左子树的生成首先需要获取头节点,连接到根节点的right;然后需要获取左子树的尾节点,需要一直查找右节点,直到找到左子树的尾节点,供连接;
- 左子树生成的链表两端都连接完成后,需要将根节点的左边设置为null,不然root还存在左子树。
var flatten=function(root){
const helper=(root)=>{//将当前子树转化为单链表
if(root==null) return null;
//转化后会直接生成连接到右侧,所以先处理右子树的单链表
if(root.right) helper(root.right);
if(root.left){ //左子树生成为单链表后连接到右子树
const leftfirst=helper(root.left); //生成单链表,并获取头结点
let leftend=leftfirst; //单链表的尾节点
while(leftend.right) // 一直寻找右节点,获取单链表的尾节点
leftend=leftend.right;
leftend.rigtht=root.right; //尾节点后面接左子树展平后的单链表
root.right=leftfirst; // 根节点的right改成leftFirst
root.left=null; root.left置为null
}
return root; //返回当前子树转化成的单链表
};
helper(root); // 原地修改,不用返回
};
2、设置变量保存
先设置变量保存根节点的左右子树,然后将其左子树转换为右子树,右子树链接到原根节点右子树。感觉这个方法有点作弊哈哈哈哈,但是思路很好懂。给出原po主的链接:参考链接。
var flatten=function(root){
if(root==null) return null;
flatten(root.left);
flatten(root.right);
//设置变量保存左右子树
let left=root.left;
let right=root.right;
//将左子树作为右子树
root.left=null;
root.right=left;
//将原先的右子树接到当前右子树的末端
while(root.right!=null){
root=root.right;
}
root.right=right;
return root;
};
3、前序遍历
将二叉树展开为单链表之后,单链表中的节点顺序即为二叉树的前序遍历访问各节点的顺序。因此,可以对二叉树进行前序遍历,获得各节点被访问到的顺序。由于将二叉树展开为链表之后会破坏二叉树的结构,因此在前序遍历结束之后更新每个节点的左右子节点的信息,将二叉树展开为单链表。
var flatten=function(root){
const list=[];
preorder(root,list);
for(let i=1;i<list.length;i++){
let preNode=list[i-1];
let cur=list[i];
preNode.left=null;
preNode.right=cur;
}
};
const preorder=(root,list)=>{
if(root!=null){
list.push(root);
preorder(root.left,list);
preorder(root.right,list);
}
}