一、两两交换链表中的节点
力扣24
创建一个虚拟头结点dummyNode指向头结点,设置三个指针cur、left、right如下图所示:
首先令left.next = right.next
然后令cur.next = right
最后令right.next = left完成交换
随后调整三个指针的指向,cur = left、left = left.next、right = left.next,交换下两个节点位置
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public ListNode swapPairs(ListNode head) {
//如果是只有头结点,则返回头结点
if(head == null || head.next == null){
return head;
}
ListNode dummyNode = new ListNode(0);
dummyNode.next = head;
ListNode cur = dummyNode;
ListNode left = head;
ListNode right = left.next;
//当链表长度为2或者3时
if(right.next == null || right.next.next == null){
left.next = right.next;
cur.next = right;
right.next = left;
right = right.next;
left = cur.next;
}
while(right.next != null && right.next.next != null){
left.next = right.next;
cur.next = right;
right.next = left;
cur = left;
left = left.next;
right = left.next;
if(right.next == null || right.next.next == null){
left.next = right.next;
cur.next = right;
right.next = left;
right = right.next;
left = cur.next;
}
}
return dummyNode.next;
}
}
二、删除链表的倒数第N个节点
力扣19
设置两个指针left和right,二者初始时都指向头结点。让right向右移动n+1个身位,然后让right和left同时移动,直到right为空,此时left所指节点的下一个结点即为要删除的节点。
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyNode = new ListNode(0);
dummyNode.next = head;
ListNode right = dummyNode;
ListNode left = dummyNode;
//right指针比left快n+1个身位,这样当right = null时,left.next即为要删除的节点
for(int i = 0; i <= n; i++){
right = right.next;
}
while(right != null){
right = right.next;
left = left.next;
}
left.next = left.next.next;
return dummyNode.next;
}
}
三、链表相交
面试题02.07
设置两个指针curA和curB分别指向headA和headB。
首先遍历得到两个链表A和B的长度sizeA和sizeB。随后计算两个链表长度差值step,然后更长的那个链表的cur指针向右移step个单位,这样curA和curB指向的部分在长度上就对齐了。
随后同时遍历A和B剩余的部分,当curA=curB时,说明此节点为相交结点
时间复杂度:O(n)
空间复杂度:O(1)
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int sizeA = 0;
int sizeB = 0;
//求链表A和B的长度
while(curA != null){
sizeA++;
curA = curA.next;
}
while(curB != null){
sizeB++;
curB = curB.next;
}
curA = headA;
curB = headB;
//计算两个链表长度差值step,然后更长的那个链表的指针右移step,这样curA和curB在长度上就对齐了。
int step;
if(sizeA >= sizeB){
step = sizeA - sizeB;
for(int i = 0; i < step; i++){
curA = curA.next;
}
while(curA != null){
if(curA == curB){
return curA;
}
curA = curA.next;
curB = curB.next;
}
}else{
step = sizeB - sizeA;
for(int i = 0; i < step; i++){
curB = curB.next;
}
while(curB != null){
if(curB == curA){
return curB;
}
curB = curB.next;
curA = curA.next;
}
}
return null;
}
}
四、环形链表II
力扣142
判断链表是否有环
可以使用快慢指针法,分别定义fast和slow指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点(因此如果有环则fast一定先进环),如果fast和slow指针在途中相遇 ,说明这个链表有环。
找到环的入口
假设从头结点到环形入口节点的节点数为x。 环形入口节点到fast指针与slow指针相遇节点的节点数为y。 从相遇节点再到环形入口节点的节点数为 z。
那么在相遇时slow指针走过的节点数为x+y,fast指针走过的节点数x+y+n(y+z),n为fast指针在环内走了n圈才遇到slow指针,(y+z)为 一圈内节点的个数。因为fast走两步slow才走一步,则fast走过的节点数目是slow的两倍。因此(x+y)*2=x+y+n(y+z),由此可得x = (n-1)(y+z)+z。
这意味着slow再从头结点出发走到入环结点的时候,fast结点从在环内之前与slow相交的位置出发也走到了入环节点。因此可以令indexA = head,indexB = fast,令二者右移,indexA和indexB相交的位置即为入环节点。
时间复杂度:O(n)
空间复杂度:O(1)
public class Solution {
public ListNode detectCycle(ListNode head) {
//快慢指针法判断是否有环
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
//fast走两步,slow走一步,如果有环fast肯定比slow先进环。
fast = fast.next.next;
slow = slow.next;
//当fast和slow相遇的时候,说明有环
if(fast == slow){
ListNode index1 = fast;
ListNode index2 = head;
while(index1 != index2){
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}