目录
203. 移除链表元素
方法一:虚拟头节点
- 添加一个虚拟头结点,删除头结点就不用另做考虑
方法二:递归
代码(虚拟头节点)
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyhead = new ListNode();
dummyhead.next = head;
ListNode p = dummyhead;
ListNode q = head;
while (q != null) {
if (q.val == val) {
p.next = q.next;
q = p.next;
}
else {
p = q;
q = q.next;
}
}
return dummyhead.next;
}
}
力扣官方题解(虚拟头节点)
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode temp = dummyHead;
while (temp.next != null) {
if (temp.next.val == val) {
temp.next = temp.next.next;
} else {
temp = temp.next;
}
}
return dummyHead.next;
}
}
力扣官方题解(递归)
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
head.next = removeElements(head.next, val);
return head.val == val ? head.next : head;
}
}
707. 设计链表
代码(官方题解)
class MyLinkedList {
int size;
ListNode head;
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
public int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
ListNode cur = head;
for (int i = 0; i <= index; i++) {
cur = cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
if (index > size) {
return;
}
index = Math.max(0, index);
size++;
ListNode pred = head;
for (int i = 0; i < index; i++) {
pred = pred.next;
}
ListNode toAdd = new ListNode(val);
toAdd.next = pred.next;
pred.next = toAdd;
}
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
ListNode pred = head;
for (int i = 0; i < index; i++) {
pred = pred.next;
}
pred.next = pred.next.next;
}
}
class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
}
206. 反转链表
思路
1. 头插法
2. 双指针法
首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
代码(头插法)
class Solution {
public ListNode reverseList(ListNode head) {
ListNode dummyhead = new ListNode(0);
ListNode p = head;
while (p != null) {
ListNode temp = new ListNode(p.val);
temp.next = dummyhead.next;
dummyhead.next = temp;
p = p.next;
}
return dummyhead.next;
}
}
代码(双指针法)
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
ListNode temp = null;
while (cur != null) {
temp = cur.next;// 保存下一个节点
cur.next = prev;
prev = cur;
cur = temp;
}
return prev;
}
}
24. 两两交换链表中的节点
思路
代码
class Solution {
public ListNode swapPairs(ListNode head) {
//边界
if (head == null || head.next == null) return head;
ListNode p = head;
ListNode q = p.next;
ListNode temp = p;
boolean sign = true;
while (true) {
p.next = q.next;
q.next = p;
//如果是前两个节点
if (sign) {
head = q;
sign = false;
}
else {
temp.next = q;
}
if (p.next == null || p.next.next == null) break;
temp = p;
p = p.next;
q = p.next;
}
return head;
}
}
代码(递归)
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = head.next;
head.next = swapPairs(newHead.next);
newHead.next = head;
return newHead;
}
}
代码(迭代)
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode temp = dummyHead;
while (temp.next != null && temp.next.next != null) {
ListNode node1 = temp.next;
ListNode node2 = temp.next.next;
temp.next = node2;
node1.next = node2.next;
node2.next = node1;
temp = node1;
}
return dummyHead.next;
}
}
19. 删除链表的倒数第 N 个结点
思路:双指针
代码
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//1.判断边界
if(head.next == null) {
head = null;
return head;
}
//2.第一遍求出节点个数
// 第二遍删除第(cnt - n + 1)的节点,但只需要遍历到cnt - n;
int cnt = 0;
ListNode p = head;
while (p != null) {
p = p.next;
cnt++;
}
ListNode dummyhead = new ListNode(0);
dummyhead.next = head;
p = dummyhead;
int length = cnt - n;
while (p != null) {
if (length == 0 && p.next != null) {
p.next = p.next.next;
}
p = p.next;
length--;
}
return dummyhead.next;
}
}
代码:双指针
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//新建一个虚拟头节点指向head
ListNode dummyNode = new ListNode(0);
dummyNode.next = head;
//快慢指针指向虚拟头节点
ListNode fastIndex = dummyNode;
ListNode slowIndex = dummyNode;
// 只要快慢指针相差 n 个结点即可
for (int i = 0; i <= n; i++) {
fastIndex = fastIndex.next;
}
while (fastIndex != null) {
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
// 此时 slowIndex 的位置就是待删除元素的前一个位置。
// 具体情况可自己画一个链表长度为 3 的图来模拟代码来理解
// 检查 slowIndex.next 是否为 null,以避免空指针异常
if (slowIndex.next != null) {
slowIndex.next = slowIndex.next.next;
}
return dummyNode.next;
}
}
160. 相交链表
思路
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
否则循环退出返回空指针。
代码
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenghA = getlength(headA);
int lenghB = getlength(headB);
int n = Math.abs(lenghA - lenghB);
ListNode p = headA;
ListNode q = headB;
if (lenghA > lenghB) {
for (int i = 0;i < n;i++) {
p = p.next;
}
}
else if (lenghA < lenghB) {
for (int i = 0;i < n;i++) {
q = q.next;
}
}
while (p != q && p != null && q != null) {
p = p.next;
q = q.next;
}
if (p == q) return p;
return null;
}
public int getlength (ListNode head) {
int length= 0;
while (head != null) {
length++;
head = head.next;
}
return length;
}
}
142. 环形链表 II
思路
x = (n - 1) (y + z) + z
问题一:判断有无环—>快慢指针
问题二:找到环的入口—> 快慢指针相遇后,另solw = head,fast不变,然后同时向后移动,直到solw = fast
代码
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (slow == fast) {
ListNode p = head;
ListNode q = fast;
while (p != q) {
p = p.next;
q = q.next;
}
return p;
}
}
return null;
}
}