博主个人主页:不是笨小孩
⏩专栏分类:数据结构与算法
代码仓库:笨小孩的代码库
⏩社区:不是笨小孩
欢迎大家三连关注,一起学习,一起进步!!
回文节后是对称的,所以这个题目我们可以先找到中间节点,然后将中间反转,然后两个指针,一个从头开始,一个从反转后的头节点开始,一一比对,如果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)
{
//不相等一定不回文,返回false
if(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)
{
//小于的尾插到tail1
tail1->next = cur;
cur = cur->next;
tail1 = tail1->next;
}
else
{
//大于的尾插到tail2
tail2->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);
//假设长的链表为A
struct ListNode* longNode = headA,*shortNode = headB;
//如果B链表长,则将长链表赋值B
if(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。
答案是不太一定,假设他们之间的距离还是N,如果N是偶数那么就追上了,如果N奇数,他们就会错过,假设圆的长度为C,他们的距离就会变成C-1,这是又要分情况了,假设C-1位奇数那么就一定追不上了,如果C-1位偶数那么在下一圈的追击中就会追上。
我们需要推导一个公式,假设头到入环点的长度为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);
//假设长的链表为A
struct ListNode* longNode = headA,*shortNode = headB;
//如果B链表长,则将长链表赋值B
if(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;
}
总结一下:
方法一逻辑强,不好推导,但是推导出来代码好写
方法二逻辑没有那么强,适合我们普通人,但是代码量稍微大一点。
今天的分享就到这里结束了,感谢大家的关注和支持。