1、删除链表中等于给定值 val 的所有节点OJ链接
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode*perv=NULL;
struct ListNode*cur=head;
while(cur)
{
if(cur->val==val)
{
if(cur==head)
{
head=cur->next;
free(cur);
cur=head;
}
else
{
perv->next=cur->next;
free(cur);
cur=perv->next;
}
}
else
{
perv=cur;
cur=cur->next;
}
}
return head;
}
2、反转一个单链表OJ链接
//方法一
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode*n1,*n2,*n3;
n1=NULL;
n2=head;
if(head==NULL)
{
return NULL;
}
n3=head->next;
while(n2)
{
n2->next=n1;
n1=n2;
n2=n3;
if(n3)
{
n3=n3->next;
}
}
return n1;
}
这是方法一的核心思想,运用3个指针,迭代往后走。
//方法二
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode*cur=head;
struct ListNode*newhead=NULL;
while(cur)
{
struct ListNode*next = cur->next;
cur->next=newhead;
newhead=cur;
cur=next;
}
return newhead;
}
方法二是运用头插的方法。
3、给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点OJ链接
struct ListNode* middleNode(struct ListNode* head){
struct ListNode*slow,*fast;
slow=head;
fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
运用快慢指针,慢指针走一步,快指针走两步,那么快指针走完的时候,慢指针正好走了一半。
4、输入一个链表,输出该链表中倒数第k个结点OJ链接
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k )
{
if(pListHead==NULL)
return NULL;
struct ListNode*slow,*fast;
slow=fast=pListHead;
for(int i=0;i<k;i++)
{
if(fast==NULL)
return NULL;
fast=fast->next;
}
while(fast)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
这道题也是使用了快慢指针,让快指针先走k步,然后再让slow和fast一起走,直到fast为NULL,此时slow就是倒数第k的位置。
5、将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的OJ链接
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
if(l1==NULL)
return l2;
if(l2==NULL)
return l1;
struct ListNode*head=NULL;
struct ListNode*tail=NULL;
if(l1->val<l2->val)
{
head=tail=l1;
l1=l1->next;
}
else
{
head=tail=l2;
l2=l2->next;
}
while(l1&&l2)
{
if(l1->val<l2->val)
{
tail->next=l1;
tail=l1;
l1=l1->next;
}
else
{
tail->next=l2;
tail=l2;
l2=l2->next;
}
}
if(l1==NULL)
{
tail->next=l2;
}
else
{
tail->next=l1;
}
return head;
}
6、编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前OJ链接
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
struct ListNode*lesshead,*lesstail,*greaterhead,*greatertail;
lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
lesstail->next=NULL;
greaterhead=greatertail=(struct ListNode*)malloc(sizeof(struct ListNode));
greatertail->next=NULL;
struct ListNode*cur=pHead;
while(cur)
{
if(cur->val<x)
{
lesstail->next=cur;
lesstail=lesstail->next;
}
else
{
greatertail->next=cur;
greatertail=greatertail->next;
}
cur=cur->next;
}
greatertail->next=NULL;//注意一下防止死循环比如2,4,6,7,3 k=5
lesstail->next=greaterhead->next;
struct ListNode*head=lesshead->next;
free(lesshead);
free(greaterhead);
return head;
}
};
7、链表的回文结构OJ链接
struct ListNode*reverseList(struct ListNode*head)
{
struct ListNode*cur=head;
struct ListNode*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;
}
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
struct ListNode*mid=middleNode(A);
struct ListNode*rhead=reverseList(mid);
struct ListNode*curA=A;
struct ListNode*curR=rhead;
while(curA&&curR)
{
if(curA->val!=curR->val)
{
return false;
}
else
{
curA=curA->next;
curR=curR->next;
}
}
return true;
}
};
这个需要上面的取中间结点,和链表的反转,之后比较就行了
8、输入两个链表,找出它们的第一个公共结点OJ链接
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
assert(headA&&headB);
struct ListNode*tailA=headA;
struct ListNode*tailB=headB;
int numA=1;
while(tailA->next)
{
numA++;
tailA=tailA->next;
}
int numB=1;
while(tailB->next)
{
numB++;
tailB=tailB->next;
}
if(tailA!=tailB)
return NULL;
else
{
int gap=abs(numA-numB);
struct ListNode*longList=headA;
struct ListNode*shortList=headB;
if(numA<numB)
{
longList=headB;
shortList=headA;
}
while(gap--)
{
longList=longList->next;
}
while(longList!=shortList)
{
longList=longList->next;
shortList=shortList->next;
}
return longList;
}
}
这个题分为两步:
1、判断是否相交,如果不相交何谈交点是哪,那怎么看是否相交,只需看最后一个结点的地址是否相同即可。
2、找到交点。
如果长度相同就直接一起走,走到地址相同的地方就是交点了。
如果长度不相同,就让长的先走两链表长度之差的步数(目的就是让它俩同步),然后再一起走。
9、给定一个链表,判断链表中是否有环OJ链接
bool hasCycle(struct ListNode *head)
{
struct ListNode*slow=head,*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(fast==slow)
{
return true;
}
}
return false;
}
运用快慢指针
快指针走两步,慢指针走一步,快指针先进环,之后慢指针进环,如果快指针追上了慢指针说明存在环。
环的定义:
如果链表中有某个结点,可以通过连续跟踪next指针再次到达,则链表中存在环
如图都可以算环不管你环大环小
1、slow走一步,fast走两步,带环,一定会相遇
分析:
第一步:fast先进环,当fast刚进环的瞬间,这时slow走了环前的一般路程
第二步:随着slow进环,fast已经在环里走了一段了,走了多少由环的大小决定
假设slow进环时,slow与fast距离为N,fast开始追slow,slow每往前走一步,fast往前走两步,每追一次,fast和slow的距离会减少1,变成N-1,N-2,N-3…1,0最终会减到0相遇。
2、slow走一步,fast走三步,带环,不一定会相遇
分析:
同样还是fast先进环,当slow进环的时候,fast已经走了一段了
假设两者距离为N,fast开始追,每追一次两者距离减少2,所以分两种情况讨论
当N是偶数,那么两者距离变化为N-2,N-4,N-6…2,0当减到0相遇,这种情况可以追上;当N为奇数,两者距离变化为N-2,N-4,N-6…1,-1这一次没有追上,这时他们之间的距离变为C-1(C为环长),如果C-1是偶数就可以追上,如果是奇数就追不上。
3、slow走一步,fast走四步,带环,不一定会相遇
分析:
和第二种一样,每追一次,两者距离减少3,分类讨论
当N为3的倍数时,两者距离变化为N-3,N-6…3,0最后追上;
当N不是3的倍数,两者距离变化为N-3,N-6…1,-2或者N-3,N-6…2,-1这两种就要看C-2,C-1是不是3的倍数,是的话就能追上,否则不能。
10、
带环链表,slow走一步,fast走两步,我们已经知道肯定可以相遇,那如何求环的入口结点呢?
先直接说结论:
一个指针从相遇点开始走,一个指针从链表头开始走,每次都是走一步,他们会在环的入口点相遇。
证明:
追上相遇的过程中:
慢指针走的距离为:L+X
快指针走的距离为:L+X+N*C (N是相遇之前快指针走的圈数,N>=1)
已知快指针走的路程是慢指针的2倍
2(L+X)=L+X+N×C
L+X=N×C
L=N×C-X
L=(N-1)C+C-X
所以可以证明结论是正确的
做一个题:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULLOJ链接
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode*slow=head,*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(fast==slow)
{
struct ListNode*meetnode=slow;
while(meetnode!=head)
{
head=head->next;
meetnode=meetnode->next;
}
return meetnode;
}
}
return NULL;
}
11、复制带随机指针的链表,给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。要求返回这个链表的深度拷贝。OJ链接
struct Node* copyRandomList(struct Node* head) {
struct Node*cur=head;
while(cur)
{
struct Node*copy=(struct Node*)malloc(sizeof(struct Node));
copy->val=cur->val;
copy->next=cur->next;
cur->next=copy;
cur=copy->next;
}
cur=head;
while(cur)
{
struct Node*copy=cur->next;
if(cur->random==NULL)
copy->random=NULL;
else
copy->random=cur->random->next;
cur=copy->next;
}
struct Node*copyhead=NULL,*copytail=NULL;
cur=head;
while(cur)
{
struct Node*copy=cur->next;
struct Node*next=copy->next;
if(copytail==NULL)
{
copyhead=copytail=copy;
}
else
{
copytail->next=copy;
copytail=copy;
}
cur->next=next;
cur=next;
}
return copyhead;
}
本题解题思路是将拷贝结点插入到原结点后面,根据原结点,处理copy结点的random,最后拆解下来copy链表,恢复原链表。
感谢阅读,我们下期再见
如有错 欢迎提出一起交流
关注周周汪哦
关注三连么么么哒