【数据结构和算法初阶(C语言)】顺序表+单链表经典例题图文详解(题解大合集,搭配图文演示详解,一次吃饱吃好)
���录
1.移除链表元素
1.1思路1:遍历删除
1. 2 思路2:尾插法
2.反转链表
3.链表的中间节点
3.1解题思想及过程
3.2快慢指针思想解题---变式:返回链表的倒数第K个节点
4.合并两个有序链表
4.1解题思想 1取小的尾插
5.反转链表
6、CM11 链表分割 (比较难)
描述
7.OR36 链表的回文结构
8.相交链表
9.结语
1.移除链表元素
1203. 移除链表元素https://leetcode.cn/problems/remove-linked-list-elements/
1.1思路1:遍历删除
struct ListNode* removeElements(struct ListNode* head, int val) { if(head==NULL) { return NULL; } struct ListNode* cur = head; struct ListNode* pre = head; while(cur->next) { if(cur->val==val) { //删除 //如果是头结点 if(head == cur) { head = cur->next; free(cur); cur = head; pre = cur; } else { pre->next = cur->next; free(cur); cur = pre->next; } } else { pre= cur; cur = cur->next; } } //处理尾巴 if(cur->val == val) { if(head == cur) { free(cur); return NULL; } else{ pre->next = cur ->next; free(cur); } } return head; }
1. 2 思路2:尾插法
struct ListNode* removeElements(struct ListNode* head, int val) { // if(head==NULL) // { // return NULL; // } // struct ListNode* cur = head; // struct ListNode* pre = head; // while(cur->next) // { // if(cur->val==val) // { // //删除 // //如果是头结点 // if(head == cur) // { // head = cur->next; // free(cur); // cur = head; // pre = cur; // } // else // { // pre->next = cur->next; // free(cur); // cur = pre->next; // } // } // else // { // pre= cur; // cur = cur->next; // } // } // //处理尾巴 // if(cur->val == val) // { // if(head == cur) // { // free(cur); // return NULL; // } // else{ // pre->next = cur ->next; // free(cur); // } // } // return head; struct ListNode* cur = head;//用于遍历链表 struct ListNode* newhead = NULL;//创建新的头结点 struct ListNode* tail = NULL;//定义一个尾指针 //开始遍历原先的链表 while(cur) { if(cur->val == val) { struct ListNode* deal = cur;//记录一下要删除的节点 cur = cur->next; free(deal); } else { if(tail == NULL)//第一次插入 { tail = cur; newhead = cur; } else { tail ->next = cur; tail = tail ->next; } cur = cur->next; } //单独处理一下尾巴最后一个节点要删除的话,我们的这个尾节点要为空 if(tail) { tail->next = NULL; } } return newhead; }
2.反转链表
206. 反转链表https://leetcode.cn/problems/reverse-linked-list/ 放在第五讲解
3.链表的中间节点
. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。
3.1解题思想及过程
这道题最简单的方法就是两次遍历的方法,第一次遍历就可以得到链表的长度,从而求得链表的的中间位置,第二次遍历返回中间节点就好。今天重点讲解一次遍历方法使用快慢指针
偶数长度的链表遍历结束的条件是:快指针指向空
奇数链表长度的遍历结束的条件是:快指针指向的下一个位置为空
struct ListNode* middleNode(struct ListNode* head) { //快慢指针 struct ListNode* slow = head; struct ListNode* fast = head; if(head == NULL)//传入空指针 { return NULL; } while(fast&&fast->next) { slow = slow->next; fast = fast->next->next; } return slow; }
3.2快慢指针思想解题---变式:返回链表的倒数第K个节点
解题思想:快慢指针
快指针先走K步,然后和慢指针一起移动,直到快指针来到尾节点的下一个位置过后,慢指针指向的位置就是我们的倒数第k个节点。
特别注意就是:如果我们传入空链表或者我们的输入的倒数第几个的数据,但是链表没有那么长的时候,应该返回空。
struct ListNode* FindKthToTail(struct Listnode* plistHead, int k) { //首先判断一下链表为不为空,为空就返回NULL if (plistHead == NULL) { return NULL; } struct ListNode* slow, * fast; slow = fast = plistHead; //先让快指针走K步 while (k--) { //但是要注意判断如果我们的链表没有K步长,就返回空 if (fast == NULL) { return NULL; } fast = fast->next; } //然后快慢指针一起走,直到快指针走到空 while (fast) { slow = slow->next; fast = fast->next; } return slow; }
4.合并两个有序链表
21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
4.1解题思想 1取小的尾插
主要是尾插思想,由于是有序链表,我们就取小的尾差到新的头结点,当一个链表或者两个链表同时走完就结束,将另外一个链表剩下的所有元素尾插到新的链表中然后返回头结点就可以。为了不增加遍历的时间复杂度,还是定义一个尾指针。
当有一个链表先走完时:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) { struct ListNode* head = NULL;//定义一个头指针 struct ListNode* tail = NULL;//定义一个尾指针 //先判断传入的链表中1是否有空链表,有就返回另外一个 if(list1 == NULL) { return list2; } if(list2==NULL) { return list1; } while(list1 && list2) { if(list1->valval)//取小的尾插 { if(tail == NULL)//判断是否是第一次插入 { tail = head = list1; list1 = list1->next; } else { tail->next= list1; list1 = list1->next; tail = tail->next; } } else { if(tail == NULL)//判断是否是第一次插入 { tail = head = list2; list2 = list2->next; } else { tail->next= list2; tail = tail->next; list2 = list2->next; } } } //出循环有一个链表为空,就要判断一下是哪一个为空,然后将另外一个进行尾插 if(list1) { tail->next = list1; } if(list2) { tail->next = list2; } return head; }
5.反转链表
. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/reverse-linked-list/
解题思想:头插法
struct ListNode* reverseList(struct ListNode* head) { //先判断一下传入的链表是否为空 if(head == NULL) { return NULL; } struct ListNode* newhead = NULL; struct ListNode* cur = head; struct ListNode* after=NULL; while(cur) { after = cur->next; cur ->next = newhead; newhead = cur; cur = after; } return newhead; }
6、CM11 链表分割 (比较难)
描述
现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
做题链接
补充带哨兵位的链表
首先解题的整体思想:
大于等于X的放置在一个链表,使用尾插的办法为了不改变顺序
小于X的放置在一个链表,也使用尾插的办法。
最后将两个链表链接起来。
这里使用带哨兵位的链表。我们先看一下代码实现,一边走一边说明问题
那么我们应该将大于数据的链表的尾巴置空。
class Partition { public: ListNode* partition(ListNode* pHead, int x) { // write code here struct ListNode* lhead ,*ltail;//定义小于X的值存放的链表的头尾节点 struct ListNode* ghead ,*gtail;//定义大于X的值存放的链表的头尾节点 //申请哨兵位空间 lhead = ltail = ( struct ListNode*)malloc(sizeof( struct ListNode)); ghead = gtail = ( struct ListNode*)malloc(sizeof( struct ListNode)); //开始循环遍历找 struct ListNode* cur= pHead; while(cur) { if(cur->val next = cur; ltail = ltail ->next; cur = cur->next; } else { gtail->next = cur; gtail = gtail->next; cur = cur->next; } } //链接 ltail->next = ghead->next; gtail->next = NULL; free(ghead); struct ListNode* head = lhead; head = head ->next; free(lhead); return head; } };
7.OR36 链表的回文结构
题目地址
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。 给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于 测试样例: 1-2-2-1 返回:true
struct ListNode* reverseList(struct ListNode* head) { //先判断一下传入的链表是否为空 if (head == NULL) { return NULL; } struct ListNode* newhead = NULL; struct ListNode* cur = head; struct ListNode* after = NULL; while (cur) { after = cur->next; cur->next = newhead; newhead = cur; cur = after; } return newhead; } struct ListNode* middleNode(struct ListNode* head) { //快慢指针 struct ListNode* slow = head; struct ListNode* fast = head; if (head == NULL)//传入空指针 { return NULL; } while (fast && fast->next) { slow = slow->next; fast = fast->next->next; } return slow; } bool chkPAlindrome(ListNode* head) { struct ListNode* mid = middleNode(head); struct LIstNOde* rmid = reverselist(mid); while (rmid && head) { struct ListNode* scur = head; if(rmid->val != scur ->val) return false; rmid = rmid->next; scur = head->next; } }
8.相交链表
160. 相交链表
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) { struct ListNode * cura = headA,*curb = headB; int lena = 1; int lenb = 1; while(cura->next) { cura = cura->next; lena++;//计算链表a的长度 } while(curb->next) { curb = curb->next; lenb++;//计算链表b的长度 } //如果两个链表相交,那么尾结点的地址一定相等 //所以这里就可以判断一下两个链表是否相交 if(cura != curb) { return NULL; } int gap = abs(lena-lenb);//计算两个链表之间的差值,用来绝对值 struct ListNode*longst = headA,*shortlist = headB; if(lenanext;//长的先走差距步 } //同时找交点 while(longst != shortlist) { longst = longst->next; shortlist = shortlist->next; } return longst; }
9.结语
今天关于简单链表的题目解析就更新到这里,下几篇内容是比较复杂的带环链表和复杂连边的题目,大家可以先收藏或者关注一波,方便链接后续新解