链表:快慢指针,创建虚拟头节点,考虑哈希表建立映射关系
一、链表
1.
描述
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
如输入{1,2,3}的链表如下图:
返回一个数组为[3,2,1]
0 <= 链表长度 <= 10000
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
ListNode cur = listNode;
while(cur!=null){
int value = cur.val;
//ArrayList 中有个方法是 add(index,value),可以指定 index 位置插入 value 值
//遍历 listNode 的同时将每个遇到的值插入到 list 的 0 位置,最后输出 listNode 即可得到逆序链表
list.add(0,value);
cur = cur.next;
}
return list;
}
}
2.反转链表:快慢指针
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
//反转链表:快慢双指针
ListNode fast = head;
ListNode slow = null;
while(fast!=null){
//记录fast的next
ListNode next = fast.next;
fast.next = slow;
slow = fast;
fast = next;
}
return slow;
}
}
3.合并两个有序链表(递归)
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围: 0 \le n \le 10000≤n≤1000,-1000 \le 节点值 \le 1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
//递归做题
//退出条件
ListNode head;
if(list1==null){
return list2;
}
if(list2 == null){
return list1;
}
if(list1.val>list2.val){
head =list2;
head.next = Merge(list1,list2.next);
}else{
head = list1;
head.next = Merge(list2,list1.next);
}
return head;
}
}
4.两个链表的交点
输入描述:
输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和二个链表的公共部分。 后台会将这3个参数组装为两个链表,并将这两个链表对应的头节点传入到函数FindFirstCommonNode里面,用户得到的输入只有pHead1和pHead2。
返回值描述:
返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
//到达公共节点时,走的距离相,节点的引用也相同
ListNode n1 = pHead1;
ListNode n2 = pHead2;
while(n1!=n2){
//这里判段的是n1,不是n1.next;
n1= n1 !=null? n1.next:pHead2;
n2 = n2 !=null?n2.next:pHead1;
}
return n1;
}
}
5.链表的第一个入环节点
注意点:判断是否是,有环的while循环;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead){
//快慢指针
ListNode fast = pHead;
ListNode slow = pHead;
//要判断是不是没有环
while(fast !=null && fast.next!=null){
fast =fast.next.next;
slow = slow.next;
if(fast ==slow){
ListNode n = pHead;
while(n!=slow){
n = n.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}
6.返回地倒数n个节点,当n值大于链表长度时,返回空
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
public ListNode FindKthToTail (ListNode pHead, int k) {
// write code here
//快慢指针,快指针先走k步
//虚拟指针
// ListNode n1 = new ListNode(-1);
// n1.next = pHead;
ListNode n1 =pHead;
ListNode n2 = pHead;
//快指针先走k步
int num=0;
for(int i = 0;i<k&& n2!=null;i++){
n2 = n2.next;
num++;
}
if(num<k) return null;
while(n2 != null){
n1 = n1.next;
n2 = n2.next;
}
return n1;
}
}
7.将搜索二叉树转为循环双向链表
算法流程:
dfs(cur): 递归法中序遍历;
终止条件: 当节点 cur 为空,代表越过叶节点,直接返回;
递归左子树,即 dfs(cur.left) ;
构建链表:
当 pre 为空时: 代表正在访问链表头节点,记为 head ;
当 pre 不为空时: 修改双向节点引用,即 pre.right = cur , cur.left = pre ;
保存 cur : 更新 pre = cur ,即节点 cur 是后继节点的 pre ;
递归右子树,即 dfs(cur.right) ;
treeToDoublyList(root):
特例处理: 若节点 root 为空,则直接返回;
初始化: 空节点 pre ;
转化为双向链表: 调用 dfs(root) ;
构建循环链表: 中序遍历完成后,head 指向头节点, pre 指向尾节点,因此修改 head 和 pre 的双向节点引用即可;
返回值: 返回链表的头节点 head 即可;
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val,Node _left,Node _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
Node pre,head;
public Node treeToDoublyList(Node root) {
if(root == null) return null;
dfs(root);
head.left = pre;
pre.right = head;
return head;
}
//将搜索二叉树转化为双向链表
public void dfs(Node cur){
if(cur == null) return;
dfs(cur.left);
//当递归到父节点时
//注意循环中只是判断pre的存不存在
if(pre== null) {
head = cur;
}else{
pre.right = cur;
}
cur.left = pre;
pre = cur;
dfs(cur.right);
}
}
8.复制复杂链表-哈希表中建立新的节点
复制链表是指新的链表节点全是new的,不是复制旧链表的地址
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
题目链接:力扣
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
//定义一个指针,遍历旧的的链表,在map中建立于新节点的映射关系
Node cur=head;
Map<Node,Node> map = new HashMap<>();
//创建一个新的链表
while(cur != null){
//在map中建立新的节点
map.put(cur,new Node(cur.val));
cur = cur.next;
}
//如何找到链表的头节点?找到key=head时的value;
cur = head;
while(cur != null){
//
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
}