在跟着 代码随想录 刷链表篇的过程中,发现链表相关的题目绕不开两种解法:
其中 虚拟头结点 主要是在调整链表元素的位置时会用到,使用了这种方式可以省去对头结点单独处理的逻辑。相关题目包括:[203.移除链表元素]、[707.设计链表]、[206.反转链表]、[24.两两交换链表中的节点]、[19.删除链表的倒数第N个节点]
双指针 则是用在 寻找链表中指定节点的位置 相关题目中,比如普通双指针相关的题目:[160.链表相交],快慢指针的题目:[19.删除链表的倒数第N个节点]、[8.环形链表II]
移除链表元素
相关题目:
这题有两种解法,取决于是否需要对头结点进行额外的处理,而两种解法都需要定义一个前置节点 pre
和当前节点 cur
,之后再对节点进行遍历。
第一种解法,对头结点进行额外的处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public ListNode removeElements(ListNode head, int val) { while (head != null && head.val == val) { head = head.next; } if (head == null) { return head; } ListNode pre = head; ListNode cur = head.next; while (cur != null) { if (cur.val == val) { pre.next = cur.next; } else { pre = cur; } cur = cur.next; } return head; }
|
第二种解法,创建一个虚拟的头结点,这样头结点可以像普通节点一样处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public ListNode removeElements(ListNode head, int val) { ListNode tmpHead = new ListNode(0, head); ListNode pre = tmpHead; ListNode cur = head; while (cur != null) { if (cur.val == val) { pre.next = cur.next; } else { pre = cur; } cur = cur.next; } return tmpHead.next; }
|
在做了几道关于删除链表元素的题目后,发现虚拟头节点很好用,不用再对头节点的删除进行额外的判断。
翻转链表
相关题目:
此题解法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public ListNode reverseList(ListNode head) { ListNode pre = null; ListNode cur = head; ListNode next; while (cur != null) { next = cur.next; cur.next = pre; pre = cur; cur = next; } return pre; }
|
两两交换链表中的节点
相关题目:
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public ListNode swapPairs(ListNode head) { ListNode tmpHead = new ListNode(0, head); ListNode pre = tmpHead; ListNode cur = head; ListNode next; while (cur != null && cur.next != null) { next = cur.next; pre.next = next; cur.next = next.next; next.next = cur; pre = cur; cur = pre.next; } return tmpHead.next; }
|
删除链表的倒数第N个节点
相关题目:
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public ListNode removeNthFromEnd(ListNode head, int n) { ListNode tmpHead = new ListNode(0, head); ListNode fastNode = tmpHead; ListNode slowNode = tmpHead; for (int i = 0; i <= n; i++) { fastNode = fastNode.next; } while (fastNode != null) { fastNode = fastNode.next; slowNode = slowNode.next; } slowNode.next = slowNode.next.next; return tmpHead.next; }
|
链表相交
相关题目:
此题的关键在于求出两个链表长度差值x,然后定义两个指针,分别指向两个链表,其中较长的那个链表的指针指向的位置提前移动差值x,与较短链表的指针对齐,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public ListNode getIntersectionNode(ListNode headA, ListNode headB) { int sizeA = 0; ListNode curA = headA; while (curA != null) { curA = curA.next; sizeA++; }
int sizeB = 0; ListNode curB = headB; while (curB != null) { curB = curB.next; sizeB++; }
int diff = sizeA - sizeB; if (diff < 0) { diff = -diff; curA = headB; curB = headA; } else { curA = headA; curB = headB; }
while (diff-- > 0) { curA = curA.next; }
while (curA != null) { if (curA == curB) { return curA; } curA = curA.next; curB = curB.next; } return null; }
|
环形链表II
相关题目:
解题关键点都在下面的注释中写出来了,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public ListNode detectCycle(ListNode head) { ListNode fastNode = head; ListNode slowNode = head; while (fastNode != null && fastNode.next != null) { fastNode = fastNode.next.next; slowNode = slowNode.next; if (fastNode == slowNode) {
ListNode index1 = fastNode; ListNode index2 = head; while (index1 != index2) { index1 = index1.next; index2 = index2.next; } return index2; } } return null; }
|