链表
链表
链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一个节点的指针,因此很多链表问题可以用递归来处理。LeetCode 默认的链表表示方法如下:
1 | struct ListNode { |
由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或指针出现问题。有两个小技巧可以解决这个问题:一是尽量处理当前节点的下一个节点而非当前节点本身,二是建立一个虚拟节点 (dummy node),使其指向当前链表的头节点,这样即使原链表所有节点全被删除,也会有一个 dummy 存在,返回 dummy->next 即可。
基本操作
Reverse Linked List
题目:
Given the head of a singly linked list, reverse the list, and return the reversed list.
题解:
递归写法:
1 | ListNode* reverseList(ListNode* head, ListNode* prev=nullptr) { |
非递归写法:
1 | ListNode* reverseList(ListNode* head) { |
Merge Two Sorted Lists
题目:
You are given the heads of two sorted linked lists list1 and list2.
Merge the two lists in a one sorted list. The list should be made by splicing together the nodes of the first two lists.
Return the head of the merged linked list.
Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]
题解:
递归写法:
1 | ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { |
非递归写法:
1 | ListNode* mergeTwoLists(ListNode *l1, ListNode *l2) { |
Swap Nodes in Pairs
题目:
Given a linked list, swap every two adjacent nodes and return its head. You must solve the problem without modifying the values in the list’s nodes (i.e., only nodes themselves may be changed.)
Input: head = [1,2,3,4]
Output: [2,1,4,3]
题解:
递归写法:
1 | ListNode* swapPairs(ListNode* head) { |
非递归写法:
创建虚拟头结点 dummyHead,令 temp 表示当前到达的节点,初始时 temp = dummyHead。每次需要交换 temp 后面的两个节点。
如果 temp 的后面没有节点或者只有一个节点,则没有更多的节点需要交换,因此结束交换。否则,获得 temp 后面的两个节点 node1 和 node2,通过更新节点的指针关系实现两两交换节点。
具体而言,交换之前的节点关系是 temp -> node1 -> node2,交换之后的节点关系要变成 temp -> node2 -> node1,因此需要进行如下操作。
1 | temp.next = node2 |
完成上述操作之后,节点关系即变成 temp -> node2 -> node1。再令 temp = node1,对链表中的其余节点进行两两交换,直到全部节点都被两两交换。
1 | ListNode* swapPairs(ListNode* head) { |
原则上不对当前节点进行操作,所以会有
dummyHead和temp
其他技巧
Intersection of Two Linked Lists
题目:
Given the heads of two singly linked-lists headA and headB, return the node at which the two lists intersect. If the two linked lists have no intersection at all, return null.
Note that the linked lists must retain their original structure after the function returns.
intersect: 交集
题解:
双指针法,我们使用两个指针,分别指向两个链表的头节点,并以相同的速度前进,若到达链表结尾,则移动到另一条链表的头节点继续前进。按照这种前进方法,两个指针会在 a + b + c 次前进后同时到达相交节点。
1 | ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { |
Palindrome Linked List
题目:
Given the head of a singly linked list, return true if it is a palindrome or false otherwise.
题解:
可以直接把值复制进数组,对数组操作,但这样就失去了链表的乐趣🤣
这里先使用快慢指针找到链表中点,快指针一次走两格,慢指针一次走一格,快指针走到终点时慢指针指向中点;再把链表切成两半,然后把后半段翻转;最后比较两半是否相等。
1 | // 主函数 |
练习
Remove Duplicates from Sorted List
题目:
Given the head of a sorted linked list, delete all duplicates such that each element appears only once. Return the linked list sorted as well.
题解:
直接一次遍历即可
1 | ListNode* deleteDuplicates(ListNode* head) { |
正常工程代码一定要记得 delete
Odd Even Linked List
题目:
Given the head of a singly linked list, group all the nodes with odd indices together followed by the nodes with even indices, and return the reordered list.
The first node is considered odd, and the second node is even, and so on.
Note that the relative order inside both the even and odd groups should remain as it was in the input.
You must solve the problem in O(1) extra space complexity and O(n) time complexity.
Input: head = [2,1,3,5,6,4,7]
Output: [2,3,6,7,1,5,4]
题解:
以第一个节点,第二个节点分别作为奇偶链表的头结点,然后隔一个添加一个节点。最后链接两个链表。
1 | ListNode* oddEvenList(ListNode* head) { |
Remove Nth Node From End of List
题目:
Given the head of a linked list, remove the nth node from the end of the list and return its head.
题解:
使用双指针,快指针比慢指针提前 n 个节点,那么快指针到终点时,慢指针指向位置即倒数第 n 个节点。为了便于删除,我们需要慢指针再慢一个节点,最后慢指针指向目标删除节点的前驱。
1 | ListNode* removeNthFromEnd(ListNode* head, int n) { |
Sort List
题目:
Given the head of a linked list, return the list after sorting it in ascending order.
Input: head = [-1,5,3,4,0]
Output: [-1,0,3,4,5]
题解:
快慢指针找中点,归并排序。递归实现
1 | ListNode* sortList(ListNode* head) { |
