目录
[leetcode]21.合并两个有序链表
思路:依次比较,尾插较小的结点
要尾插我们一般需要一个head用于返回,一个tail用于方便找尾。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
if(list1==NULL)
return list2;
if(list2==NULL)
return list1;
struct ListNode* cur1=list1,*cur2=list2;
struct ListNode* head=NULL,*tail=NULL;
while(cur1&&cur2)
{
if(cur1->val < cur2->val)
{
if(head==NULL)
{
head=tail=cur1;
}
else
{
tail->next=cur1;
tail=tail->next;
}
cur1=cur1->next;
}
else
{
if(head==NULL)
{
head=tail=cur2;
}
else
{
tail->next=cur2;
tail=tail->next;
}
cur2=cur2->next;
}
}
if(cur1)
tail->next=cur1;
if(cur2)
tail->next=cur2;
return head;
}
那么这段代码还可以怎么简化呢?我们引入一个新的概念就是带哨兵位的头结点,意思就是在头结点之前有一个结点,这个结点不存储内容,这样的好处就是可以不用上述代码最开始的非空判断,那么我们这里就再用这个思路优化一下代码。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
struct ListNode* cur1=list1,*cur2=list2;
struct ListNode* guard=NULL,*tail=NULL;
guard=tail=(struct ListNode*)malloc(sizeof(struct ListNode));
tail->next=NULL;
while(cur1&&cur2)
{
if(cur1->val < cur2->val)
{
tail->next=cur1;
tail=tail->next;
cur1=cur1->next;
}
else
{
tail->next=cur2;
tail=tail->next;
cur2=cur2->next;
}
}
if(cur1)
tail->next=cur1;
if(cur2)
tail->next=cur2;
struct ListNode* head=guard->next;
free(guard);
return head;
}
[牛客]链表分割
思路:用两个链表,小于x的尾插到一个链表,大于等于x的尾插到另一个链表,然后再链接起来
(最好使用哨兵位,因为可以避免很多非空处理)
ListNode* partition(ListNode* pHead, int x) {
struct ListNode* gGuard,*gTail,*lGuard,*lTail;
gGuard=gTail=(struct ListNode*)malloc(sizeof(struct ListNode));
lGuard=lTail=(struct ListNode*)malloc(sizeof(struct ListNode));
gTail->next=lTail->next=NULL;
struct ListNode* cur=pHead;
while(cur)
{
if(cur->val<x)
{
lTail->next=cur;
lTail=lTail->next;
}
else
{
gTail->next=cur;
gTail=gTail->next;
}
cur=cur->next;
}
lTail->next=gGuard->next;
pHead=lGuard->next;
free(lGuard);
free(gGuard);
return pHead;
}
按照思路我们把代码写好后提交,我们会得到下面的提示信息:内存超限
哪里出问题了呢,我们自测一组数据找一下问题:
我们发现很多数据重复输出,出现死循环,说明什么?链表带环。根据自测数据仔细分析上边代码 ,我们分开两个链表尾插时,就拿{1,5,2,7,3,4}举例,两个链表尾插结束后,其实我们的7的next仍然指向3,因为我们在尾插时,是让前一个结点的next指向要尾插的结点,并没有改变要尾插的这个结点的next(反复体会这句话,转折点),所以我们分析出原因后,只需要简单修改就可以了,就是将我们的gTail->next=NULL就可以了。
ListNode* partition(ListNode* pHead, int x) {
struct ListNode* gGuard,*gTail,*lGuard,*lTail;
gGuard=gTail=(struct ListNode*)malloc(sizeof(struct ListNode));
lGuard=lTail=(struct ListNode*)malloc(sizeof(struct ListNode));
gTail->next=lTail->next=NULL;
struct ListNode* cur=pHead;
while(cur)
{
if(cur->val<x)
{
lTail->next=cur;
lTail=lTail->next;
}
else
{
gTail->next=cur;
gTail=gTail->next;
}
cur=cur->next;
}
lTail->next=gGuard->next;
gTail->next=NULL;
pHead=lGuard->next;
free(lGuard);
free(gGuard);
return pHead;
}
[牛客]链表的回文结构
思路:找到中间结点,从中间结点开始,对后半段逆置,然后比较前半段和后半段。
你可能会说需要分奇数个和偶数个进行判断,但其实,这里很妙的地方就是你把后半段逆置之后,前半段最后一个结点还是指向逆置到最后的那个结点(如下图),所以这个思路十分简单。
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* cur = head,*newhead=NULL;
while(cur)
{
struct ListNode*next=cur->next;
cur->next=newhead;
newhead=cur;
cur=next;
}
return newhead;
}
struct ListNode* middleNode(struct ListNode* head){
struct ListNode* slow ,*fast;
slow=fast=head;
while(fast && fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
bool chkPalindrome(ListNode* head) {
struct ListNode* mid=middleNode(head);
struct ListNode* rhead=reverseList(mid);
while(head&&rhead)
{
if(head->val!=rhead->val)
return false;
head=head->next;
rhead=rhead->next;
}
return true;
}
这里的代码复用了上一篇文章中的找中间结点和逆置对应的代码,所以实现起来很快,想了解的也可以再看看上一篇文章。
[leetcode]160.相交链表
思路:判断两个链表是否相交,只需要判断尾结点地址是否相同,但由于两个链表的长度不一定长,所以需要先求出两个链表的长度,然后让两个指针分别指向两个链表的头,长的链表对应的指针先走长度之差步,然后两个指针再同时走,第一个地址相同的就是交点。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* tailA=headA,*tailB=headB;
int lenA=1,lenB=1;
while(tailA->next)
{
tailA=tailA->next;
lenA++;
}
while(tailB->next)
{
tailB=tailB->next;
lenB++;
}
if(tailA!=tailB)
return NULL;
int gap=abs(lenA-lenB);
struct ListNode* longList=headA,*shortList=headB;
if(lenA<lenB)
{
longList=headB;
shortList=headA;
}
while(gap--)
{
longList=longList->next;
}
while(longList!=shortList)
{
longList=longList->next;
shortList=shortList->next;
}
return longList;
}