💓博主个人主页:不是笨小孩👀
⏩专栏分类:数据结构与算法👀
🚚代码仓库:笨小孩的代码库👀
⏩社区:不是笨小孩👀
🌹欢迎大家三连关注,一起学习,一起进步!!💓
链表OJ
- 链表的回文结构
- 链表分割
- 两个链表的交集
- 链表循环
- 思考
- [链表周期 II](https://leetcode.cn/problems/linked-list-cycle-ii/description/)
- 方法一
- 方法二
链表的回文结构
回文节后是对称的,所以这个题目我们可以先找到中间节点,然后将中间反转,然后两个指针,一个从头开始,一个从反转后的头节点开始,一一比对,如果val不相等一定不是回文,如果相等,那么一定是回文链表。
偶数个数据时
奇数个数据时:
所以我们可以清楚的知道当rmid为空时就要结束循环。
代码如下:
// 计算中间节点
struct ListNode* midNode(struct ListNode* head)
{struct ListNode* fast = head,*slow = head;while(fast&&fast->next){slow = slow->next;fast = fast->next->next;}return slow;
}// 翻转链表
struct ListNode* reverseList(struct ListNode* head)
{struct ListNode* cur = head, *newhead = NULL,*next = NULL;while(cur){next = cur->next;cur->next = newhead;newhead = cur;cur = next;}return newhead;
}
bool isPalindrome(struct ListNode* head)
{//计算中间节点struct ListNode* mid = midNode(head);//反转链表struct ListNode* rmid =reverseList(mid);while(rmid){//不相等一定不回文,返回falseif(head->val!=rmid->val){return false;}head = head->next;rmid = rmid->next;} //循环结束一定是回文return true;
}
链表分割
这个题我们需要两个链表,然后遍历原链表,将小于x的尾插到head1,将大于等于x的尾插带head2,然后将两个链表链接一下,需要注意的是我们需要将head2的为尾节点的next置空,不然可能会出现带环链表,还有就是我们的head1有可能为空,这里我们使用带哨兵位的链表,就可以解决这个问题,但是head2也可能为空,这时我们就需要将head1的尾节点next置空,不然就会存在非法访问。
代码如下:
struct ListNode* partition(struct ListNode* head, int x)
{struct ListNode* head1,*tail1,*head2,*tail2;//开辟两个哨兵位head1 = tail1 = (struct ListNode*)malloc(sizeof(struct ListNode));head2 = tail2 = (struct ListNode*)malloc(sizeof(struct ListNode));struct ListNode* cur = head;while(cur){if(cur->val<x){//小于的尾插到tail1tail1->next = cur;cur = cur->next;tail1 = tail1->next;}else{//大于的尾插到tail2tail2->next = cur;cur = cur->next;tail2 = tail2->next;}}//链接两个链表tail1->next = head2->next;//防止出现带环链表tail2->next = NULL;if (head2->next == NULL){//第二个链表为空,这是要将这是要将tail1->next置空,不然会出现非法访问tail1->next = NULL;struct ListNode* next = head1->next;free(head2);free(head1);return next;}//保存头结点struct ListNode* next = head1->next;//释放哨兵位free(head1);free(head2);return next;
}
两个链表的交集
判断是否相交非常简单,我们只需要判断两个链表的为节点是否相同就可以了,如果相同,那么它们一定是相交的,我们就可以找相加的那个节点,我们在找尾的时候可以记录一下两个链表的长度,由于从相交的位置开始后面的节点都一样了,我们可以计算出长的那个链表比短的链表长多少,让长的链表先走多少步,然后让两个链表同时走,相等的第一个节点就是我们要求的。
代码如下:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{struct ListNode* curA = headA,*curB = headB;int s1 = 1;int s2 = 1;//找A链表的尾,顺表记录长度while(curA->next){curA= curA->next;s1++;}//找B链表的尾,顺表记录长度while(curB->next){curB= curB->next;s2++;}//尾节点不相等一定不相交if(curA!=curB){return NULL;}//计算差值int k = abs(s1-s2);//假设长的链表为Astruct ListNode* longNode = headA,*shortNode = headB;//如果B链表长,则将长链表赋值Bif(s1<s2){longNode = headB;shortNode = headA;}//长链表先走k步while(k--){longNode= longNode->next;}//寻找第一个相等的节点while(longNode!=shortNode){longNode = longNode->next;shortNode = shortNode->next;}return longNode;
}
链表循环
我们可以用快慢指针来解决这个问题,我们让慢指针走一步,快指针走两步,如果有环,那么当俩个指针都进入环中是,就是追击问题,如果快指针走到NULL,或者快指针的next为空,那么就意味着链表无环。
代码如下:
bool hasCycle(struct ListNode *head)
{struct ListNode* fast = head,*slow = head;while(fast&&fast->next){fast = fast->next->next;slow = slow->next;if(fast==slow) return true;}return false;
}
思考
1.如果slow走一步,fast走二步是不是一定可以追上?
答案是一定可以追上,为什么呢,假设slow进环时fast与它相距N步,那么每走一步,他们之间的距离减少一,而N一定小于一圈的长度,所以在一圈之内fast一定追上slow。
2.如果slow走一步,fast走三步是不是一定可以追上?
答案是不太一定,假设他们之间的距离还是N,如果N是偶数那么就追上了,如果N奇数,他们就会错过,假设圆的长度为C,他们的距离就会变成C-1,这是又要分情况了,假设C-1位奇数那么就一定追不上了,如果C-1位偶数那么在下一圈的追击中就会追上。
链表周期 II
方法一
我们需要推导一个公式,假设头到入环点的长度为L,然后假设他们在环中相遇的点为X,入环点到X的距离为N,然后整个环的长度为C,我们可以推导:
有了这个推导关系我们就可以写代码了。
代码如下:
struct ListNode *detectCycle(struct ListNode *head)
{struct ListNode* slow = head,*fast = head;while(fast&&fast->next){fast = fast->next->next;slow = slow->next;//判断是否相遇if(fast==slow){struct ListNode* meet = fast,*cur = head;//一个从相遇点开始走,一个从头开始走while(meet!=cur){meet = meet->next;cur = cur->next;}//此时相遇的点就是入环点return meet;}}//出循环一定没有环return NULL;
}
方法二
我们还是找相遇点,然后我们把相遇点的next置空,保存相遇点的next,然后就变成了相交链表。
代码如下:
//找相交链表的第一个相交点struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{struct ListNode* curA = headA,*curB = headB;int s1 = 1;int s2 = 1;//找A链表的尾,顺表记录长度while(curA->next){curA= curA->next;s1++;}//找B链表的尾,顺表记录长度while(curB->next){curB= curB->next;s2++;}//尾节点不相等一定不相交if(curA!=curB){return NULL;}//计算差值int k = abs(s1-s2);//假设长的链表为Astruct ListNode* longNode = headA,*shortNode = headB;//如果B链表长,则将长链表赋值Bif(s1<s2){longNode = headB;shortNode = headA;}//长链表先走k步while(k--){longNode= longNode->next;}//寻找第一个相等的节点while(longNode!=shortNode){longNode = longNode->next;shortNode = shortNode->next;}return longNode;
}struct ListNode *detectCycle(struct ListNode *head)
{struct ListNode* slow = head,*fast = head;while(fast&&fast->next){fast = fast->next->next;slow = slow->next;//判断是否相遇if(fast==slow){struct ListNode* meet = fast,*newhead = fast->next;//转换相交链表meet->next = NULL;//找第一个相交点,也就是入环点return getIntersectionNode(head,newhead);}}//出循环一定没有环return NULL;
}
总结一下:
方法一逻辑强,不好推导,但是推导出来代码好写
方法二逻辑没有那么强,适合我们普通人,但是代码量稍微大一点。
今天的分享就到这里结束了,感谢大家的关注和支持。