单链表OJ题(文字解读 + 图解)
- 1. 移除链表元素
- 2. 反转链表
- 3. 链表的中间结点
- 4. 返回倒数第 k 个节点
- 5. 合并两个有序链表
1. 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
OJ链接=>
struct ListNode* removeElements(struct ListNode* head, int val);
这题当然可以暴力遍历,将值一一对比,相等的删除,并重新链接。但还有更优的思路:
- 定义两个指针(slow,fast);
- fast先走,如果fast指向的val和其相等,则让slow指向fast的下一个后fast再走;如果不相等,则让slow指向fast指向的位置后发射台再走;
- 最后再分一些特殊情况。
struct ListNode* removeElements(struct ListNode* head, int val) {struct ListNode* slow = head;struct ListNode* fast = head;//头为空,直接返回空if(head == NULL)return NULL;while(fast){if(fast->val == val){slow->next = fast->next;fast = fast->next;}else{slow=fast;fast=fast->next;}} //当头结点就是val时并且头结点下一个结点为空,直接返回空if(head->val == val&&head->next==NULL)return NULL;//当头结点就是val时并且头结点下一个结点不去为空,直接返回头节点的下一个节点else if(head->val == val&&head->next!=NULL)return head->next;else return head;
}
最后一定要分情况,不然OJ有些测试用例通过不了。
2. 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
OJ链接=>
struct ListNode* reverseList(struct ListNode* head);
这题的最优解:
- 创建3个指针,第一个指针n1指向空,第二个指针n2指向头节点,第三个指针n3指向头节点的下一个节点;
- 先将n2指向n1,再让n1指向n2,n2指向n3,再循环往后走;
struct ListNode* reverseList(struct ListNode* head) {struct ListNode* n1 = NULL;struct ListNode* n2 = head;while(n2){struct ListNode* n3 = n2->next;n2->next = n1;n1 = n2;n2 = n3;}return n1;
}
最后一次,当n3赋值给n2时,n2指向空,所以循环结束条件n2!=NULL。如果将n3放在循环外创建可能会造成空指针,因为数组可能只有一个元素或者就没有元素,这样n3就没有用了。
3. 链表的中间结点
给你单链表的头结点 head ,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
OJ链接=>
struct ListNode* middleNode(struct ListNode* head) ;
这题还是快慢指针问题:
让一个指针从头节点开始一次走一步,让第二个指针从头节点开始一次走两步,走到尾时,慢指针刚好走到中间节点。
struct ListNode* middleNode(struct ListNode* head) {struct ListNode* slow = head;struct ListNode* fast = head;while(fast && fast->next){fast = fast->next->next;slow = slow->next;}return slow;
}
当节点个数为奇数个时,循环终止条件为fast->next != NULL;当节点个数为偶时,循环终止条件为fast != NULL,将两种情况合并。
4. 返回倒数第 k 个节点
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
OJ链接=>
int kthToLast(struct ListNode* head, int k);
这是一道经典的面试题,最优思路为:先让一个指针从头节点走k步,再让一个指针从头节点开始和前一个指针同时走,当走到尾,慢指针指向的就是我们要的。
int kthToLast(struct ListNode* head, int k){struct ListNode* slow = head;struct ListNode* fast = head;while(k--){fast = fast->next;}while(fast){fast = fast->next;slow = slow->next;}return slow->val;
}
循环结束条件为当fast == NULL.
5. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
OJ链接=>
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2);
解题思路:
此题可以先创建一个空链表,然后依次从两个有序链表中选取最小的进行尾插操作进行合并。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {struct ListNode* head = NULL,*tail = NULL;//特殊情况先出if(list1 == NULL){return list2;}if(list2 == NULL){return list1;}while(list1 && list2){if(list1->val < list2->val){//第一次插入if(tail == NULL){head = tail = list1;}else{//后续取较小的尾插tail->next = list1;tail = tail->next; }list1 = list1->next;}else{if(tail == NULL){head = tail = list2;}else{tail->next = list2;tail = tail->next; }list2 = list2->next;}}//当list2走完,list1还有节点时直接尾插if(list1){tail->next = list1;}//当list1走完,list2还有节点时直接尾插if(list2){tail->next = list2;}return head;
}
单链表没有这么简单,目前程度我做不来难题,后续有机会再更新.