24. 两两交换链表中的节点
昨天把链表的性质以及几个常见问题弄清楚后做题明显顺了很多,每次做题前画一张图,谁指到谁弄清楚,再结合一些如双指针法这样的技巧,这些问题确实难度不大。这里特别提醒一下我在debug中出现的问题,报错提示是这样的heap-use-after-free,而通过return输出大法找到出错语句分析之后,我发现问题出在调整了链表关系后,我的最后一个结点并没有明确给出指向(其实是保留了原先指向),在学习了一些博客的说法后,最后一个结点必须明确给出指向什么地方,不然系统会认定这个单向的链表是不完整的,最后我补了一句尾结点指向NULL的语句后顺利AC。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* left=new ListNode(0);
ListNode* right=new ListNode(0);
ListNode* temp=new ListNode(0);
ListNode* temp1=new ListNode(0);
ListNode* temp2=new ListNode(0);
ListNode* dummy=new ListNode(0);
dummy->next=head;
left->next=dummy;
right=head;
if (head==nullptr)
{
return head;
}
if (head->next==NULL)
{
return head;
}
temp2=head->next;
while (right!=NULL)
{
if (right->next!=NULL)
{
temp=right->next;
temp1=right->next->next;
left->next=temp;
temp->next=right;
right->next=NULL;
left=right;
right=temp1;
}
else
{
left->next=right;
return temp2;
}
}
return temp2;
}
};
19. 删除链表的倒数第 N 个结点
这道题目也比较简单,用双指针法控制前后指针中间一共是N+1个结点,那么当右侧指针搜索至最后一个时,左侧指针的位置刚好就是倒数第N+1个结点,那么将倒数第N+1个结点的next指到下下个结点处即可,下面是代码
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *right=new ListNode(0);
ListNode *left=new ListNode(0);
int i;
if (head->next==NULL)
{
return nullptr;
}
left=head;
right=head;
for (i=1;i<=n;i++)
{
right=right->next;
}
if (right==NULL)
{
head=head->next;
return head;
}
while (right->next!=NULL)
{
left=left->next;
right=right->next;
}
left->next=left->next->next;
return head;
}
};
面试题 02.07. 链表相交
这道题目就正常思路来说没有什么难度,我一开始执着于不去计算链表长度,尝试一遍搜索后找出,最后发现有点困难哈,最终还是计算链表长度后对齐,一个个比较,这道题可以明确一点是,链表结点相同指的是地址相同,而不是数值和指针均相同,完全可以存在并行结构,这点需要特别注意。
以下是代码
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int numA,numB,gap,i;
ListNode *tempA=new ListNode(0);
ListNode *tempB=new ListNode(0);
numA=0;
numB=0;
tempA=headA;
tempB=headB;
while (tempA!=NULL)
{
numA++;
tempA=tempA->next;
}
while (tempB!=NULL)
{
numB++;
tempB=tempB->next;
}
gap=abs(numA-numB);
tempA=headA;
tempB=headB;
if (numA>numB)
{
for (i=1;i<=gap;i++)
{
tempA=tempA->next;
}
}
else
{
for (i=1;i<=gap;i++)
{
tempB=tempB->next;
}
}
while (tempA!=NULL && tempB!=NULL)
{
if (tempA==tempB)
{
return tempA;
}
tempA=tempA->next;
tempB=tempB->next;
}
return NULL;
}
};
142. 环形链表 II
这道题一开始没仔细想哈哈,直接用了最暴力的方法,让快慢指针在环中相遇,得到环中结点,求出环长,然后根据环长一一判断,当然最后时间就很慢啦。先贴一个最初版本的代码
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow=new ListNode(0);
ListNode *fast=new ListNode(0);
ListNode *temp=new ListNode(0);
ListNode *temp2=new ListNode(0);
int i,len,test;
slow=head;
fast=head;
test=1;
len=1;
if (head==NULL) return NULL;
while (test==1)
{
if (fast->next==NULL)
{
return NULL;
}
if ( fast->next->next==NULL)
{
return NULL;
}
slow=slow->next;
fast=fast->next->next;
if (slow==fast)
{
temp=slow->next;
break;
}
}
while (temp!=slow)
{
temp=temp->next;
len++;
}
temp=head;
while (test==1)
{
temp2=temp;
for (i=1;i<=len;i++)
{
temp2=temp2->next;
}
if (temp2==temp) return temp;
temp=temp->next;
}
return head;
}
};
后来结合了卡哥的题解思考了一下,我感觉可以把分析过程更加精简一些,可以不拿代数表达式,把整个过程讲成一个故事,首先快慢指针速度设置成1和2同时前进,按照这个速度两个指针相遇在环中一点后,我们让两个指针以速度1同时倒退,自然会同时退到入口处,而之后二人分道扬镳,当一个指针退回原点时,另一个指针相当于走了两段,倒了一段,就和慢指针走一段到达了同一个地方,也就是相遇的结点处,那么我们就知道了,从相遇结点处两个指针同时以速度1出发,两人也会在入口处再次相遇。(感觉会更好理解一点),再放下我修改后的代码,用时明显改善:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow=new ListNode(0);
ListNode *fast=new ListNode(0);
ListNode *temp=new ListNode(0);
ListNode *temp2=new ListNode(0);
int i,len,test;
slow=head;
fast=head;
test=1;
len=1;
if (head==NULL) return NULL;
while (test==1)
{
if (fast->next==NULL)
{
return NULL;
}
if ( fast->next->next==NULL)
{
return NULL;
}
slow=slow->next;
fast=fast->next->next;
if (slow==fast)
{
temp=slow;
break;
}
}
while (head!=temp)
{
head=head->next;
temp=temp->next;
}
return head;
}
};