推荐一款模拟面试、刷题神器 、从基础到大厂笔试题:点击跳转刷题网站进行注册学习
一、移除链表元素
二、反转链表
三、链表的中间节点
四、链表中倒数第k个节点
五、合并两个有序列表
六、链表分割
七、回文链表
八、相交链表
九、环形链表
十、环形链表 II
1、思路一
2、思路二
十一、复制带随即指针的链表
1、思路
2、代码
新建一个哨兵位,用cur指针遍历原链表,cur->val!=val,将该节点连接至哨兵链表,反之释放。迭代遍历原链表即可,最后记得将哨兵链表的尾节点的next置空!!!
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* guard=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* guardcur=guard;//用于记录哨兵链表的尾插
struct ListNode* cur=head;//用于原链表的遍历
while(cur)
{
struct ListNode* next=cur->next;
if(cur->val!=val)
{
guardcur->next=cur;
guardcur=cur;
}
else
{
free(cur);
}
cur=next;
}
guardcur->next=NULL;//最后需要把尾节点的next置空
head=guard->next;
free(guard);//记得释放哨兵位
return head;
}
遍历原链表,将每一个节点取下来,不断头插反转链表
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;
}
思路1:可以遍历计数找中间节点
思路2:快慢指针,快指针走两步,慢指针走一步,快指针走到尾节点或者空节点,慢指针就指向链表的中间节点
struct ListNode* middleNode(struct ListNode* head){
struct ListNode* fast=head,*slow=head;
while(fast!=NULL&&fast->next!=NULL)
{
fast=fast->next->next;
slow=slow->next;
}
return slow;
}
注意,while循环内两个条件的顺序不能颠倒。如果颠倒,当fast为NULL时,先判断fast->next!=NULL,这是一个对空指针解引用的问题!
定义快慢指针,不难发现,快指针需要比慢指针快k-1步,所以先让快指针向前走k-1步,判断fast==NULL是防止原链表为空,判断fast->next=NULL是因为,后面fast=fast->next,说明k大于链表长度。
struct ListNode* getKthFromEnd(struct ListNode* head, int k){
struct ListNode* fast=head,*slow=head;
int tmp=k-1;//快慢指针相差的距离
while(tmp--)
{
if(fast==NULL||fast->next==NULL)//考虑下值大于节点个数的问题
return NULL;
fast=fast->next;
}
while(fast->next)
{
fast=fast->next;
slow=slow->next;
}
return slow;
}
创建一个哨兵节点,用两个指针分别指向原链表,将val小的节点尾插即可。记得释放哨兵位。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
struct ListNode* cur1=list1,*cur2=list2;
struct ListNode* guard=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* guardcur=guard;
struct ListNode* small,*big;
while(cur1!=NULL&&cur2!=NULL)
{
if(cur1->val>cur2->val)
{
small=cur2;
big=cur1;
cur2=cur2->next;
}
else
{
small=cur1;
big=cur2;
cur1=cur1->next;
}
guardcur->next=small;
guardcur=small;
}
if(cur1==NULL)
{
guardcur->next=cur2;
}
else
{
guardcur->next=cur1;
}
struct ListNode* head=guard->next;
free(guard);
return head;
}
创建两个哨兵位,遍历原链表,将val小于x和大于等于x的节点分别尾插至两个哨兵链表,再将两个链表连接起来。注意要把大的哨兵链表的尾指针置空。
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
ListNode* guard1=(ListNode*)malloc(sizeof(ListNode));//哨兵位1
ListNode* guard2=(ListNode*)malloc(sizeof(ListNode));//哨兵位2
ListNode* small=guard1,*big=guard2;
ListNode* cur=pHead;//用于遍历链表
while(cur)
{
if(cur->valnext=cur;
small=small->next;
cur=cur->next;
}
else
{
big->next=cur;
big=big->next;
cur=cur->next;
}
}
big->next=NULL;
small->next=guard2->next;//链接两个链表
free(guard2);
pHead=guard1->next;
free(guard1);
return pHead;
}
};
1、找出中间节点(本文题三)
2、对后半段进行逆置(本文题二)
3、比对val是否相等,当逆置指针为空,比对结束
bool isPalindrome(struct ListNode* head){
//先用快慢指针找到中间节点
struct ListNode* fast=head,*slow=head;
while(fast!=NULL&&fast->next!=NULL)
{
slow=slow->next;
fast=fast->next->next;
}
//现在slow是中间节点
//反转后半部分链表
struct ListNode* newnode=NULL;
struct ListNode* cur=slow;
while(cur)
{
struct ListNode* next=cur->next;
cur->next=newnode;
newnode=cur;
cur=next;
}
//比较
fast=head;
slow=newnode;
while(slow)//fast和slow会同时为空或slow先空
{
if(fast->val!=slow->val)
return false;
fast=fast->next;
slow=slow->next;
}
return true;
}
1、分别遍历链表,算出两个链表的长度。
2、让长链表先往前走,走到和短链表一样长
3、两个链表再同时走,走到两个指针一样即为相交点,如果走到空还没有找到,则不相交
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* curA=headA,*curB=headB;//用于遍历链表
int lenA=1,lenB=1;//统计A和B的长度
while(curA)
{
curA=curA->next;
++lenA;
}
while(curB)
{
curB=curB->next;
++lenB;
}
int a=abs(lenA-lenB);//A和B链表长度的差值
struct ListNode* longlist=headA,*shortlist=headB;//设两个节点
if(lenAnext;
}
while(longlist)
{
if(longlist==shortlist)
return longlist;
longlist=longlist->next;
shortlist=shortlist->next;
}
return NULL;
}
定义快慢指针,快指针一次走两步,慢指针一次走一步,如果相遇,说明有环,当快指针等于NULL,说明没环
bool hasCycle(struct ListNode *head) {
//快指针一次走两步,慢指针一次走一步,快指针先进环,慢指针后进环,相对静止
//能追上说明有环,有一个指针为空说明没环
struct ListNode* fast=head,*slow=head;
while(fast&&fast->next)//这里不用判断slow是否为空,不管带不带环,slow没有机会为空
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
return true;
}
return false;
}
1、定义快慢指针,快指针一次走两步,慢指针一次走一步,找到相遇点(本文题九)
2、将相遇点的后一个节点当成另一个链表的头结点,问题转化为本文题八
struct ListNode *detectCycle(struct ListNode *head) {
//先找到相遇点
struct ListNode* fast=head,*slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
break;
}
if(fast==NULL||fast->next==NULL)
return NULL;
struct ListNode* headA=fast->next;//两个链表的头结点
struct ListNode* curA=headA;//cur用于遍历统计链表长度
struct ListNode* headB=head;
struct ListNode* curB=headB;
int lenA=1,lenB=1;
while(curA!=fast)
{
curA=curA->next;
++lenA;
}
while(curB!=fast)
{
curB=curB->next;
++lenB;
}
struct ListNode* longlist=headA,*shortlist=headB;
if(lenAnext;
}
while(1)
{
if(longlist==shortlist)
return longlist;
longlist=longlist->next;
shortlist=shortlist->next;
}
}
1、设进环前的长度为L,入环点到相遇点的距离为X,环的长度为R
2、两指针相遇时,slow走的距离是L+X,fast走的距离是L+N*R+X,N为fast指针在环里绕的圈数(注意这里N一定大于等于1,因为快慢指针在环内相遇,说明快指针和慢指针相遇前,快指针起码路过了一次相遇点)(而且当慢指针进环后,快指针肯定会一圈追上慢指针,因为快指针比慢指针每次多走一步,相对静止,走一圈的距离必定追上)
3、那么2*(L+X)=L+N*R+X,化简得L=N*R-X,再化简得L=(N-1)*R+R-X
4、可得结论:一个指针从头结点开始走,另一个指针从相遇点开始走,那么他们会在入口处相遇
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast=head,*slow=head;//找相遇点
while(fast!=NULL&&fast->next!=NULL)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
break;
}
if(fast==NULL||fast->next==NULL)
return NULL;
slow=head;
while(fast!=slow)
{
fast=fast->next;
slow=slow->next;
}
return fast;
}
题目要求是复制一个一模一样的链表,难点在于random指针的控制
1、在每个节点后边插入一个节点,复制对应的val
2、再复制random指针,复制时要分空和非空情况,对于空,复制节点random为NNULL;非空,
则为cur->next->random=cur->random->next
3、再创建两个哨兵节点,将原链表与复制链表节点分开。
struct Node* copyRandomList(struct Node* head) {
struct Node* cur=head;
while(cur)//插入复制节点
{
struct Node* next=cur->next;
struct Node* newnode=(struct Node*)malloc(sizeof(struct Node));
cur->next=newnode;
newnode->val=cur->val;
cur=next;
newnode->next=cur;
}
cur=head;
while(cur)//复制random指针
{
if(cur->random==NULL)
cur->next->random=NULL;
else
{
cur->next->random=cur->random->next;
}
cur=cur->next->next;
}
//将链表复原
cur=head;
struct Node* guard=(struct Node*)malloc(sizeof(struct Node));
struct Node* guardcopy=(struct Node*)malloc(sizeof(struct Node));
guardcopy->next=NULL;
struct Node* curA=guard,*curB=guardcopy;
int count=1;
while(cur)
{
struct Node* next=cur->next;
if(count%2==1)
{
curA->next=cur;
curA=cur;
}
else
{
curB->next=cur;
curB=cur;
}
cur=next;
++count;
}
curA->next=NULL;
struct Node* newhead=guardcopy->next;
free(guardcopy);
free(guard);
return newhead;
}