前言
本文选取了一些经典的来自力扣和牛客的链表OJ题,并提供优质,值得借鉴的思路供读者积累与掌握。均配有步骤图解。
一、注意事项
我们在学习数据结构时刷题很重要,刷题的时候画图很重要,学会作图才能理解其中的核心思想,代码的实现反而是其次的,要画图,然后看图写代码。本文在讲解时也遵循先解析思路,配图,再进行代码实现的先后顺序。
二、OJ精讲
1.移除链表元素
- 题目描述
- 思路讲解
整体:从头开始遍历链表,把不是val值的节点取下来尾插进新链表。
拆分:
- 准备工作,初始化指针
- 分是val和不是val两种情况,其中是val时要格外讨论是否为第一次插入新表。
- 代码实现
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* tail = NULL;
struct ListNode* newhead = NULL;
struct ListNode* cur = head;
while (cur)
{
if (cur->val != val)
{
if (newhead == NULL)
{
newhead = cur;
tail = newhead;
}
else
{
tail->next = cur;
tail = tail->next;
}
cur = cur->next;
}
else
{
struct ListNode* tmp = cur;
cur = cur->next;
free(tmp);
}
}
if(tail)
tail->next = NULL;
return newhead;
}
2. 反转单链表
- 题目描述
- 思路讲解
从头开始依次反转节点指向,并更新头结点
- 代码实现
struct ListNode* reverseList(struct ListNode* head) {
if(head == NULL)
return NULL;
struct ListNode* cur = head;
struct ListNode* next = cur->next;
struct ListNode* newhead = NULL;
while(cur)
{
cur->next = newhead;
newhead = cur;
cur = next;
if(next)
next = next->next;
}
return newhead;
}
3. 链表的中间节点
- 题目描述
- 思路讲解
快慢指针法:
让fast指针一次走两步,slow一次走一步,当fast走到尾节点或NULL时,slow位置即为中间节点。对于本题,节点个数为奇数和偶数在代码实现上是一样的,不必分开讨论。
- 代码实现
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
4. 链表中倒数第K个节点
- 题目描述
- 思路讲解
快慢指针法:
fast 先走K步,再同时走,当fast为空时,slow位置为倒数第K个节点。
类似于上题,读者可以尝试自行使用快慢指针法画图求解
- 代码实现
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
struct ListNode* fast = pListHead;
struct ListNode* slow = pListHead;
while(k--)
{
if(fast == NULL)
return NULL;
fast = fast->next;
}
while(fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
5.合并两个有序链表
- 题目描述
- 思路讲解
分别从头遍历给定的两个链表,进行节点值的比较,将较小的先尾插到一个新链表。
- 代码实现
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if(list1 == NULL)
return list2;
if(list2 == NULL)
return list1;
if(list1==NULL && list2==NULL)
return NULL;
struct ListNode* cur1 = list1;
struct ListNode* cur2 = list2;
struct ListNode* newhead = NULL;
struct ListNode* tail;
while(cur1 && cur2)
{
if(cur1->val <= cur2->val)
{
if(newhead == NULL)
tail = newhead = cur1;
else
{
tail->next = cur1;
tail = tail->next;
}
cur1 = cur1->next;
}
else
{
if(newhead == NULL)
tail = newhead = cur2;
else
{
tail->next = cur2;
tail = tail->next;
}
cur2 = cur2->next;
}
}
if(cur1)
{
tail->next = cur1;
}
if(cur2)
{
tail->next = cur2;
}
return newhead;
}
6.链表的回文结构
- 题目描述
- 思路讲解
1.找到中间节点
2.反转链表后半部,使原链表拆分为两个链表
3.两个新链表从头开始依次比较节点的val值,均相同则为回文结构,否则不为回文结构。
- 代码实现
class PalindromeList {
struct ListNode* reverseList(struct ListNode* head) {
if (head == NULL)
return NULL;
struct ListNode* cur = head;
struct ListNode* next = cur->next;
struct ListNode* newhead = NULL;
while (cur) {
cur->next = newhead;
newhead = cur;
cur = next;
if (next)
next = next->next;
}
return newhead;
}
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
public:
bool chkPalindrome(ListNode* head) {
ListNode* mid = middleNode(head);
ListNode* newhead = reverseList(mid);
while (head && newhead)
{
if(head->val != newhead->val)
return false;
head = head->next;
newhead = newhead->next;
}
return true;
}
};
7. 相交链表
- 题目描述
- 思路讲解
1.判断是否相交
分别找尾,尾节点相同则相交。
2.找交点
快慢指针法。计算出两表长度,长表先从头走差距步,再同时走,直到节点地址相同时停下,即为交点。
- 代码实现
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
//1.判断是否相交,顺便计算A,B长度
struct ListNode * tailA = headA;
struct ListNode * tailB = headB;
int lenA = 1;
int lenB = 1;
while(tailA->next)
{
tailA = tailA->next;
lenA++;
}
while(tailB->next)
{
tailB = tailB->next;
lenB++;
}
if(tailA != tailB)
return NULL;
//2.找交点
//假设法精简代码
struct ListNode * longlist = headA;
struct ListNode * shortlist = headB;
//lenA与lenB差值的绝对值
int n = abs(lenA-lenB);
if(lenA < lenB)
{
longlist = headB;
shortlist = headA;
}
while(n--)
{
longlist = longlist->next;
}
while(longlist != shortlist)
{
longlist = longlist->next;
shortlist = shortlist->next;
}
return longlist;
}
总结
本文精选了链表经典OJ题,有些思路对初学者来说可能是第一次见,我们要学会站在巨人的肩膀上,将它们消化吸收,在往后的实践中运用能达到很好的效果。如果对你有所帮助,还望点赞收藏支持博主。
文章中有什么不对的丶可改正的丶可优化的地方,欢迎各位来评论区指点交流,博主看到后会一一回复。