24. 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
思路:
- 当链表为空或者链表只有头节点这一个节点时,不用进行交换。
- 当链表长度
>=2时,- 新链表的头节点一定是原链表的第二个节点,因此,在交换前需要将这个节点进行保存。
- 如果链表长度为偶数,则链表中所有节点都可以进行两两交换;如果链表长度为奇数,则链表的最后一个元素不用进行交换。
- 在一次两两交换中,主要涉及到的节点有3个,即要进行交换的两个节点和第一个节点的前驱节点。一次两两交换的过程如图:
- 对于链表的头节点,可以使用一个虚拟头节点,令其指向头结点,使得头两个节点的处理方法与其他节点的处理方法相同。
/**
* 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;
}
// head有两个或以上的节点,返回必然是第二个节点,在交换前先保存起来
ListNode res=head.next;
//两两交换
ListNode dummy=new ListNode(-1);
dummy.next=head;
ListNode first=head,second=head.next,nextFirst=null;
while(first!=null&&second!=null){
nextFirst=second.next;
second.next=first;
first.next=nextFirst;
dummy.next=second;
dummy=first;
first=nextFirst;
second=(first==null)?null:first.next;
}
return res;
}
}
19.删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第
n个结点,并且返回链表的头结点。
19. 删除链表的倒数第 N 个结点 - 力扣(Leetcode)
思路:
令A和B从同一位置出发,其中A呆在原地不动,B向前走n步,那么以B为原点,A相对于B的距离为-n步。此后,A和B同步调前进,当B到达终点时,A就达到距离终点n步的位置。
将这种方法类比到链表中,让两个指针都从头节点或虚拟头节点出发,其中一个指针先向前走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 pre=head,end=head;
for(int i=0;i<n;i++){
end=end.next;
}
if(end==null){
//删除的是头节点
return head.next;
}
while(end.next!=null){
pre=pre.next;
end=end.next;
}
// 要删除的是pre的后继结点,且此节点必然存在
// pre.next不是空指针
// 使用end保存pre的新的后继结点
end=pre.next.next;
pre.next=end;
return head;
}
}
面试题 02.07. 链表相交
给你两个单链表的头节点
headA和headB,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null。
思路:
两个单链表如果相交,那么这两链表从相交的节点到最后一个节点为止都是相同的节点。将两条链表从末尾对齐,如果一条链表比较长,相交的部分不可能出现在这条链表前部多出来的一部分。
因此,可以先求出两条链表的长度差distance,将两个指针分别指向两条链表的头节点,让指向长链表的指针率先移动distance个节点,这样,两条链表就相当于同一起点和同一终点。
然后使用这两个指针分别对两条链表进行遍历,并比较它们指向的节点是否是同一个节点。
/**
* 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=getLength(headA);
int lenB=getLength(headB);
//为避免讨论A长还是B长
//让head指向较长的链表
//让head指向较短的链表
ListNode headL=lenA>lenB?headA:headB;
ListNode headS=lenA>lenB?headB:headA;
for(int i=0;i<Math.abs(lenA-lenB);i++){
headL=headL.next;
}
while(headL!=null){
if(headL==headS){
return headL;
}
headL=headL.next;
headS=headS.next;
}
return null;
}
private int getLength(ListNode head){
int len=0;
while(head!=null){
len++;
head=head.next;
}
return len;
}
}
142.环形链表II
给定一个链表的头节点
head,返回链表开始入环的第一个节点。 如果链表无环,则返回null。如果链表中有某个节点,可以通过连续跟踪next指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos是-1,则在该链表中没有环。注意:pos不作为参数进行传递,仅仅是为了标识链表的实际情况。 不允许修改 链表。
参考:programmercarl.com/0142.%E7%8E…
思路:设置slow和fast指针,slow每次走一个节点,fast每次走两个结点,如果链表中存在环,因为两个指针的速度不同,无法保持相对静止,必然会在环中某一个节点相遇。如果fast在过程中指向了null,这意味着fast遍历完了整个链表,链表没有形成环。
本题的难点在于如何计算出环的入口。结论1是两个指针第一次相遇时slow在环内未走完一圈(证明看参考链接)。
设头节点到入口处的距离是,入口到相遇点的距离是,相遇点到入口点的距离是,
当两个指针相遇时,slow的路程是,fast的路程是,
因为 ,
所以 ,
要求,移项得:。
因此当时,。
这意味着同时从头节点和相遇点出发,以相同速度移动,两指针再次相遇时,相遇点就是环的入口节点。
/**
* 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) {
//边界条件,空链表或只有头节点的链表,不会形成环
if(head==null||head.next==null){
return null;
}
ListNode slow=head,fast=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
//到达相遇点
// fast从相遇出发
// slow从头节点出发
slow=head;
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}
return fast;
}
}
return null;
}
}