第一题:24. 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
方法一:各种指针
*注意考虑边界情况:最后剩一个结点 也要连上 && 刚好全部交换完
struct ListNode* swapPairs(struct ListNode* head) {
struct ListNode *dummyhead=(struct ListNode *)malloc(sizeof(struct ListNode )),*p1,*p2,*tem,*nextp;
dummyhead->next=head;
p1=dummyhead;
p2=head;
while( p2!=NULL && p2->next!=NULL){
tem=p2->next;
nextp=tem->next;
p1->next=tem;
tem->next=p2;
p2->next=nextp;
p1=p2;
p2=nextp;
}
return dummyhead->next;
}
方法二:递归
函数返回的结果为 下一对结点中交换后的前一个结点
struct ListNode* swapPairs(struct ListNode* head) {
if(head==NULL || head ->next==NULL){//递归终点,只剩下一个结点or零个结点
return head;
}
struct ListNode* p1,*p2,*p3;
p1=head;
p2=head->next;
p3=p2->next;
p2->next=p1;
p1->next=swapPairs(p3);
return p2;
}
第二题:19.删除链表的倒数第N个节点
使用双指针
*注意return dummyhead->next,而非head(因为有可能head标记的结点已经被删除了)
*注意删除结点、插入结点 时:需要获取操作位置的前一个结点,故而引入dummyhead统一第一个结点的删除插入操作
*注意快慢指针循环的条件
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
struct ListNode* dummyhead=(struct ListNode*)malloc(sizeof(struct ListNode));
dummyhead->next=head;
dummyhead->val=0;
struct ListNode* fast=head,*slow=dummyhead,*tem;
n=n-1;
for(int i=0;i<n;i++){
fast=fast->next;
}
while(fast->next!=NULL){
fast=fast->next;
slow=slow->next;
}
tem=slow->next;
slow->next=tem->next;
free(tem);
return dummyhead->next;
}
第三题:面试题 02.07. 链表相交
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
思路:使两个链表尾部对齐,从对齐的第一个结点开始向后比较(即上图a1,b2)
相等则找到相交结点
分为两步
1、获得两个链表的长度,得到两个链表的长度差
2、较长的链表,前进长度差个结点
3、逐一比较
- 时间复杂度:O(n + m)
- 空间复杂度:O(1)
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *a=headA,*b=headB,*tem;
int alength=0,blength=0,t;
while(a!=NULL){
a=a->next;
alength++;
}
while(b!=NULL){
b=b->next;
blength++;
}
//保证a长b短
int d;
if(alength>blength){
a=headA;
b=headB;
d=alength-blength;
}
else{
a=headB;
b=headA;
d=blength-alength;
}
for(int i=0;i<d;i++){
a=a->next;
}
while(a!=NULL){
if(a==b){
return a;
}
a=a->next;
b=b->next;
}
return NULL;
}
第四题:142 环形链表
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
思路:分为两步
1、判断是否有环
使用双指针的方式,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点。
如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
2、寻找环入口的位置
判断有环后,从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点。
两个指针相遇的时候,即为环形入口的节点。
具体原理参见:代码随想录 (programmercarl.com)
*为什么不需要dummyhead:暂未涉及插入删除操作,不需要操作位置的前一个结点
struct ListNode *detectCycle(struct ListNode *head) {
int pos=-1;
struct ListNode *fast,*slow;
slow=head;
fast=head;
while(fast!=NULL && fast->next !=NULL){
slow=slow->next;
fast=fast->next->next;
if(fast==slow){
struct ListNode *p=head;
pos=0;
while(p!=slow){
pos++;
p=p->next;
slow=slow->next;
}
return slow;
}
}
return NULL;
}