142. 环形链表 II
思路
一个指针从头出发
,定义另一个指针从相遇的节点出发
。代码
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
ListNode index1 = fast;
ListNode index2 = head;
while(index1 != index2){
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}
复杂度
时间复杂度:O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n。
空间复杂度:O(1)
相似题目
141. 环形链表
★287. 寻找重复数
面试题 02.07. 链表相交
思路
2个链表相交,则说明从相交节点开始,2个链表长度就一样。
思路一
思路二 我走过你走过的路
我先走我的路,走完我的路再走你的路
你先走你的路,走完你的路再走我的路
如果我们的路相交,我们终会相遇
如果我们的路不相交,我们各自抵达终点
代码
思路一
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int countA = 0;
int countB = 0;
ListNode curA = headA;
ListNode curB = headB;
// 统计链表A的长度
while(curA != null){
countA++;
curA = curA.next;
}
// 统计链表B的长度
while(curB != null){
countB++;
curB = curB.next;
}
curA = headA;
curB = headB;
// 计算他们的长度差
int diff = countA - countB;
// 如果A链表长
if(diff > 0){
while(diff-- > 0){
curA = curA.next;
}
// 如果B链表长
}else if(diff < 0){
while(diff++ < 0){
curB = curB.next;
}
}
// 如果curA到了最后,说明2个链表不相交
while(curA != null){
if(curA == curB) return curA;
curA = curA.next;
curB = curB.next;
}
return null;
}
}
思路二
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
while(curA != curB){
// 如果curA到达null,则指向headB
if(curA == null){
curA = headB;
}else{
curA = curA.next;
}
// 如果curB到达null,则指向headA
if(curB == null){
curB = headA;
}else{
curB = curB.next;
}
}
// 有可能是2个链表不相交,最后都到达了null
// 有可能是2个链表相交
return curA;
}
}
复杂度
时间复杂度:O(a+b)
空间复杂度:O(1)
19. 删除链表的倒数第 N 个结点
思路
因为链表是离散存储在内存中的,并且题目给的是单链表,所以无法从后开始定位节点。
如果是双向链表,则可以遍历到最后一个节点后,再向前遍历N个节点,则为倒数第N个节点。
注意:
涉及到删除链表中的元素,要考虑链表第一个节点的删除情况,通常是使用虚拟头结点代码
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode slow = dummyHead;
ListNode fast = dummyHead;
//先让快指针走n + 1步
for(int i = 0; i <= n; i++){
fast = fast.next;
}
while(fast != null){
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return dummyHead.next;
}
}
复杂度
时间复杂度: O(n)
空间复杂度: O(1)
24. 两两交换链表中的节点
思路
代码
迭代版本
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode cur = dummyHead;
while(cur.next != null && cur.next.next != null){
ListNode temp = cur.next.next.next;
ListNode firstNode = cur.next;
ListNode secondNoed = cur.next.next;
cur.next = secondNoed;
secondNoed.next = firstNode;
firstNode.next = temp;
cur = cur.next.next;
}
return dummyHead.next;
}
}
递归版本
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null) return head;
// 每次2个节点交换后,前面的节点
ListNode newHead = head.next;
head.next = swapPairs(newHead.next);
newHead.next = head;
// 返回前面的节点
return newHead;
}
}
复杂度
时间复杂度:O(n)
空间复杂度:O(1)
相似题目
★25. K 个一组翻转链表
206. 反转链表
思路
pre最终指向反转这一段的末尾
cur指向反转这一段后续的下一个节点
代码
class Solution {
public ListNode reverseList(ListNode head) {
/**
cur: 代表遍历到的节点
pre: 代表cur的上一个节点
temp: 代表临时记录cur的next指针
*/
ListNode pre = null;
ListNode cur = head;
while(cur != null){
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
//反转结束后,从原来的链表上看:
//pre最终指向反转这一段的末尾
//cur指向反转这一段后续的下一个节点
return pre;
}
}
复杂度
时间复杂度:O(n)
空间复杂度:O(1)
相似题目
92. 反转链表 II
234. 回文链表
707. 设计链表
思路
代码
class ListNode{
public int val;
public ListNode next;
public ListNode(){}
ListNode(int val) {
this.val=val;
}
}
class MyLinkedList {
//size存储链表元素的个数
private int size;
//虚拟头结点
private ListNode head;
public MyLinkedList() {
//初始化 MyLinkedList 链表。
this.head = new ListNode(-1);
this.size = 0;
}
public int get(int index) {
//获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
if(index < 0 || index >= this.size){
return -1;
}
ListNode currentNode = head;
for(int i = 0; i <= index; i++){
currentNode = currentNode.next;
}
return currentNode.val;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
//将一个值为 val 的节点插入到链表中下标为 index 的节点之前。
//如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
if (index > size) {
return;
}
if (index < 0) {
index = 0;
}
size++;
ListNode currentNode = head;
for(int i = 0; i < index; i++){
currentNode = currentNode.next;
}
ListNode temp = currentNode.next;
ListNode newNode = new ListNode(val);
currentNode.next = newNode;
newNode.next = temp;
}
public void deleteAtIndex(int index) {
if(index < 0 || index >= this.size){
return;
}
size--;
ListNode pred = head;
for (int i = 0; i < index; i++) {
pred = pred.next;
}
pred.next = pred.next.next;
}
}
复杂度
时间复杂度: 涉及 index 的相关操作为 O(index), 其余为 O(1)
空间复杂度: O(n)
移除链表元素
思路
代码
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(0, head);
ListNode p0 = dummyHead;
ListNode cur = head;
while(cur != null){
if(cur.val == val){
p0.next = cur.next;
}else{
p0 = cur;
}
cur = cur.next;
}
return dummyHead.next;
}
}
复杂度
时间复杂度: O(n)
空间复杂度: O(1)
相似题目
237. 删除链表中的节点