24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
思路:
按照上述顺序,去记录防止存在某个节点意外失踪。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null) return head;
ListNode dummy = new ListNode(-1, head);
ListNode cur = dummy;
while(cur.next != null && cur.next.next != null){
ListNode tmp = cur.next;
ListNode tmp1 = cur.next.next.next;
cur.next = cur.next.next;
cur.next.next = tmp;
cur.next.next.next = tmp1;
cur = cur.next.next;
}
return dummy.next;
}
}
19.删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5] 示例 2:
输入:head = [1], n = 1 输出:[] 示例 3:
输入:head = [1,2], n = 1 输出:[1]
思路:
双指针方法,最后删除的节点,一定要知道它的前一个节点才能删除。而n可以转化为两个指针之前的距离。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1,head);
ListNode pre = dummy;
ListNode cur = head;
for(int i = 0; i < n; i++){
cur = cur.next;
}
while(cur != null){
pre = pre.next;
cur = cur.next;
}
pre.next = pre.next.next;
return dummy.next;
}
}
面试题 02.07. 链表相交
同:160.链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
示例 1:
示例 2:
示例 3:
思路:
一开始的思路真不太好想,想了十分钟看了眼题解的开头。要想知道是否有相交的节点,需要先将两个链表的长度差给抹去,较长的指针与较短的首指针保持同样的位置。如果依然节点不一致,就去往下找寻headA == headB的情况,需要注意的是,如果找到最后,headA == null和headB == null都没能找到headA == headB的情况,就说明两者没有相交的节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenA = 0;
int lenB = 0;
ListNode cura = headA;
ListNode curb = headB;
while(cura != null){
lenA++;
cura = cura.next;
}
while(curb != null){
lenB++;
curb = curb.next;
}
if(lenA > lenB){
int n = lenA-lenB;
while(n-- > 0){
headA = headA.next;
}
while(headA != headB && headA != null && headB != null){
headA = headA.next;
headB = headB.next;
}
if(headA != null){
return headA;
}
return null;
}else{
int n = lenB-lenA;
while(n-- > 0){
headB = headB.next;
}
while(headA != headB && headA != null && headB != null){
headA = headA.next;
headB = headB.next;
}
if(headA != null){
return headA;
}
return null;
}
}
}
142.环形链表II
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
思路:
这题一刷做到的时候,想了半天,需要运用到一定的数学思维,不能理解或者理解不了。二刷只想到了要用快慢指针判断是否有环,慢指针一次移动一步,快指针一次移动两步,如果快指针不存在null,并且最终slow和fast相等,则说明会存在环。
找到判断环之后,我去计算入口节点的时候出现问题,误以为快慢指针相遇的节点就是环的入口,导致计算超时。看了题解才发现,计算环入口节点才是重头戏。
如图,x表示从开头到入口的距离,z表示入口到快慢指针相遇的节点,y表示相遇节点到入口的距离。那么存在以下关系:
slow移动 (x+z), fast 移动 x+z+n(y+z),以及 2(x+z) = x+z+n(y+z) => x = (n-1)(y+z)+y , 其中n表示fast指针在环内绕的圈数,把n-1的环内距离抵消掉的话,就可以推导出x = y,放两个指针同时出发,相遇的地方就是入口
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
//快慢指针,快指针比慢指针快两步,这样如果存在环,快慢指针一定会相遇
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){ //确定有环
ListNode index1 = head;
ListNode index2 = fast;
while(index1 != index2){
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}
总结:
双指针和虚拟头节点是最主要方法,理清楚链表之间联系,可以解决很多问题,相比一刷能直接AC的题变多了,还是很欣慰的,这是个明显的进步。