链表题写起来有种纯看数值的美,没什么高深的算法思路,全看过硬的coding能力。(捂脸)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==NULL||headB==NULL)
{
return NULL;
}
ListNode* a=headA,* b=headB;
int diff=0;//记录两链表长度差值
while(a!=NULL)
{
a=a->next;
diff++;
}
while(b!=NULL)
{
b=b->next;
diff--;
}
if(a!=b)
{
return NULL;
}
if(diff>=0)
{
a=headA;
b=headB;
}
else
{
a=headB;
b=headA;
}
diff=diff<0?-diff:diff;
while(diff!=0)
{
a=a->next;
diff--;
}
while(a!=b)
{
a=a->next;
b=b->next;
}
return a;
}
};
看到这个题的第一思路就是两个指针分别往中间跳,跳到相同的节点就是相交节点。
考虑到两链表长度不一,所以设置diff变量记录两链表长度差值。思路是遍历第一个链表让diff++,然后遍历第二个链表让diff--,此时若a和b不相等,说明根本没有相交节点。之后取a为长链表,然后diff取绝对值。然后先让长链表走差值个距离,再两个指针一起跳即可。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* start=head;
ListNode* end=head;
end=teamEnd(start,k);
if(end==NULL)
{
return head;
}
head=end;//第一组要先处理
reverse(start,end);
ListNode* lastTeamEnd=start;
while(lastTeamEnd->next!=NULL)
{
start=lastTeamEnd->next;
end=teamEnd(start,k);
if(end==NULL)
{
return head;
}
reverse(start,end);
lastTeamEnd->next=end;
lastTeamEnd=start;
}
return head;
}
ListNode* teamEnd(ListNode* start,int k)
{
// for(int i=1;inext;
// }
while(--k>0&&start!=NULL)
{
start=start->next;
}
return start;
}
void reverse(ListNode* start,ListNode* end)
{
end=end->next;
ListNode* last=NULL;
ListNode* cur=start;
ListNode* nextNode=NULL;
while(cur!=end)
{
nextNode=cur->next;
cur->next=last;
last=cur;
cur=nextNode;
}
start->next=end;
}
};
这个题就有点难度了,主要不是思路上的,而是纯写代码上的,总是莫名其妙就报错了。TvT
就像上文提的,思路不难,就是K个一组反转,然后原本的头节点去连下一组的尾节点。反转链表的操作可以看我的这篇文章。大一计算机的自学总结:单双链表的反转
略有不同的是,这里要先记end的下一个节点,最后记得让start节点和end的下一个节点相连。
为了划分K个一组,设置teamEnd函数,返回从头节点开始的K个节点。
注意,这里为了要传回更新后的head节点,所以要对第一组数据进行特殊处理,还要记得特殊情况,若链表长度不足一组就直接返回head节点。(心累)
接着设置lastTeamEnd变量记上一组更新后的尾节点,为了找下一组的开头以及最后和下一组的更新前的尾节点相连。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head==NULL)
{
return NULL;
}
Node* cur=head;
Node* next=NULL;
while(cur!=NULL)
{
next=cur->next;
cur->next=new Node(cur->val);
cur->next->next=next;
cur=next;
}
cur=head;
Node* copy=NULL;
while(cur!=NULL)
{
next=cur->next->next;
copy=cur->next;
copy->random=cur->random!=NULL?cur->random->next:NULL;
cur=next;
}
Node* ans=head->next;
cur=head;
while(cur!=NULL)
{
next=cur->next->next;
copy=cur->next;
cur->next=next;
copy->next=next!=NULL?next->next:NULL;
cur=next;
}
return ans;
}
};
这个题主要是题目比较难懂,思路就是让复制节点跟在原节点后,这样就能通过原节点的random指针找到复制节点的random指针的位置。
第一个循环,在每个节点的后面建立一个复制的节点,并让它们相连。
之后让cur回到head,进行第二个循环,让复制节点的random指针等于cur节点的random指针。
最后让ans等于复制的头节点,进行第三个循环,让复制的节点和原节点断连,形成复制的链表。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
if(head==NULL||head->next==NULL)
{
return true;
}
ListNode* slow=head,* fast=head;
while(fast->next!=NULL&&fast->next->next!=NULL)
{
slow=slow->next;
fast=fast->next->next;
}
ListNode* last=slow;
ListNode* cur=last->next;
ListNode* next=NULL;
last->next=NULL;
while(cur!=NULL)//逆序
{
next=cur->next;
cur->next=last;
last=cur;
cur=next;
}
ListNode* left=head;
ListNode* right=last;
while(left!=NULL&&right!=NULL)
{
if(left->val!=right->val)
{
return false;
}
left=left->next;
right=right->next;
}
// 还原
// cur=last->next;
// last->next=NULL;
// next=NULL;
// while(cur!=NULL)
// {
// next=cur->next;
// cur->next=last;
// last=cur;
// cur=next;
// }
return true;
}
};
思路很好想,就是让链表的后半部分反转,然后一个一个节点对比就行。
这里,重点是找一个链表中点的方法:快慢指针。快慢指针就是在头节点放置一个快指针一个慢指针,快指针每次跳两步,慢指针每次跳一步,这样当快指针走到尾节点时慢指针的位置就是中点。
之后反转后半部分链表,然后一个一个节点比较即可。
(最后也可以加上还原后半部分的代码。)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL||head->next==NULL||head->next->next==NULL)
{
return NULL;
}
ListNode* slow=head->next;
ListNode* fast=head->next->next;
while(slow!=fast)
{
if(fast->next==NULL||fast->next->next==NULL)
{
return NULL;
}
slow=slow->next;
fast=fast->next->next;
}
fast=head;
while(slow!=fast)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
};
这个题也用到了快慢指针,原理是,快慢指针还是都从头节点出发,快指针一次走两步,慢指针一次走一步,若有环,则快慢指针必相遇;相遇后让快指针回到头节点,改为一次走一步,当快慢指针再次相遇时就是入环节点。
除此之外还需要注意特殊情况的判断,若快指针能走到NULL说明链表没有环。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* start;
ListNode* end;
void merge(ListNode* l1,ListNode* r1,ListNode* l2,ListNode* r2)
{
ListNode* last;
if(l1->val<=l2->val)
{
start=l1;
last=l1;
l1=l1->next;
}
else
{
start=l2;
last=l2;
l2=l2->next;
}
while(l1!=NULL&&l2!=NULL)
{
if(l1->val<=l2->val)
{
last->next=l1;
last=l1;
l1=l1->next;
}
else
{
last->next=l2;
last=l2;
l2=l2->next;
}
}
if(l1!=NULL)
{
last->next=l1;
end=r1;
}
else
{
last->next=l2;
end=r2;
}
}
ListNode* findEnd(ListNode* s,int step)
{
for(int i=1;inext!=NULL;i++)
{
s=s->next;
}
return s;
}
ListNode* sortList(ListNode* head) {
if(head==NULL||head->next==NULL)
{
return head;
}
end=head;
int n=0;
while(end!=NULL)
{
n++;
end=end->next;
}
ListNode* l1,* r1,*l2,*r2,* next,* lastTeamEnd;
for(int step=1;stepnext;
if(l2==NULL)
{
break;
}
r2=findEnd(l2,step);
next=r2->next;
r1->next=r2->next=NULL;
merge(l1,r1,l2,r2);
head=start;
lastTeamEnd=end;
while(next!=NULL)
{
l1=next;
r1=findEnd(l1,step);
l2=r1->next;
if(l2==NULL)
{
lastTeamEnd->next=l1;
break;
}
r2=findEnd(l2,step);
next=r2->next;
r1->next=r2->next=NULL;
merge(l1,r1,l2,r2);
lastTeamEnd->next=start;
lastTeamEnd=end;
}
}
return head;
}
};
在我的文章大一计算机的自学总结:基数排序中有写排序算法的稳定性。在数组中,不存在时间复杂度为O(n*logn),空间复杂度为O(1)而且还有稳定性的算法,但链表中存在,就是链表的归并排序,归并排序的内容可见我的文章大一计算机的自学总结:归并排序及归并分治。
因为要求空间复杂度为O(1),所以连递归都不能调用。(憔悴)
首先要找到链表的尾节点,还要借助findEnd函数找step长度的尾节点。因为要返回头节点,所以要对第一组step先进行特殊处理。这里还要设置四个变量分别表示两段step的头尾。
一定!一定!一定不要忘了讨论边界情况!!!(大声)
之后的操作大同小异,重点还是merge函数。使用尾插法,在确定头节点后进行尾插,最后记得设置尾节点end。
这个题真是思路好想代码难写,一道题写完感觉心力憔悴……
链表的题目就是菜就多练,没啥高难的思路算法,就是纯看代码能力。(瘫)