本节小编将分享链表的部分练习题,友友们可以自己去练练,更好的理解链表!
1.找倒数节点
采用快慢指针的方法,先让快指针走k步,然后两个指针同时运动,当快指针指向空时,慢指针刚好指向满足的倒数节点。
struct ListNode {
int val;
struct ListNode *next;
};
typedef struct ListNode LN;
int kthToLast(struct ListNode* head, int k){
LN*fast,*slow;
fast=slow=head;
//快指针先走k步
while(k--){
fast=fast->next;
}
while(fast){
slow=slow->next;
fast=fast->next;
}
return slow->val;
}
2.链表的中间节点
给单链表的头结点 head
,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
本题同样可以采取快慢指针的方法,快指针和慢指针同时从头节点出发,快指针走两步,慢指针走一步,当快指针为空或者快指针的下一节点为空时,慢指针刚好指向满足条件的中间节点。(节点为偶数,奇数时)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode LN;
struct ListNode* middleNode(struct ListNode* head) {
LN *slow,*fast;
slow=fast=head;
while(fast && fast->next){
slow=slow->next;
fast=fast->next->next;
}
//此时slow指向的是中间节点
return slow;
}
3.反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
思路1:创建一个新链表,遍历原链表,将原链表节点头插到新链表中。
思路2:创建三个指针n1,n2,n3,完成翻转。
让n2->next=n1;n1=n2;n2=n3;n3=n3->next,到n3指向空。
循环操作...循环操作...循环操作
struct ListNode {
int val;
struct ListNode *next;
};
typedef struct ListNode LN;
struct ListNode* reverseList(struct ListNode* head) {
if (head==NULL)
return head;
LN *n1,*n2,*n3;
n1=NULL;
n2=head;
n3=head->next;
while(n2){
n2->next=n1;
n1=n2;
n2=n3;
if(n3)
n3=n3->next;
}
return n1;
}
4.环形链表的约瑟夫问题
当m=2时,模拟的过程
一直循环下去...
最后3指向3
本题借助循环链表思路,先创建带环链表,创建从1到n节点的链表(通过尾插),在通过双指针的移动寻找留下的编号。
从第一个节点报数,需要知道前节点,刚开始定义prev指向尾节点,pcur=prev->next;
从count=1开始报数,到指定m之前,指针移动,prev=pcur,pcur=prev->next,count++,到m时,先让prev->next=pcur->next,在销毁当前节点pcur,再让野指针pcur=prev->next;count变为1,一直循环下去,直到pcur->next指向自己时,跳出循环,返回该节点的值。
struct ListNode{
int val;
struct ListNode*next;
};
typedef struct ListNode ListNode;
ListNode*buynode(int x){
ListNode*Node=(ListNode*)malloc(sizeof(ListNode));
if(Node==NULL){
exit(1);
}
Node->val=x;
Node->next=NULL;
return Node;
}
ListNode*creatcircle(int n){
ListNode*head=buynode(1);
ListNode*tail=head;
for(int i=2;i<=n;i++){
tail->next=buynode(i);
tail=tail->next;
}
//首尾相连
tail->next=head;
return tail;
}
int ysf(int n, int m ) {
//创建环形链表
ListNode*prev=creatcircle(n);
ListNode*pcur=prev->next;
int count=1;
while(pcur->next!=pcur){
if(count==m){
prev->next=pcur->next;
free(pcur);
pcur=prev->next;
count=1;
}
else {
prev=pcur;
pcur=pcur->next;
count++;
}
}
return pcur->val;
}
5.分割链表
思路:创建两个新的链表,一个大链表存储比特定值大的节点,小链表存储比特定值小的节点,最后将两个链表连接起来,因此我们需要定义四个节点,两头两尾,然后采用尾插的方式相应节点插入到大小链表中。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x){
ListNode*lesshead,*lesstail,*greaterhead,*greatertail;
lesshead=lesstail=(ListNode*)malloc(sizeof(ListNode));
greaterhead=greatertail=(ListNode*)malloc(sizeof(ListNode));
ListNode*pcur=head;
while(pcur){
if(pcur->val<x){
lesstail->next=pcur;
lesstail=lesstail->next;
}
else{
greatertail->next=pcur;
greatertail=greatertail->next;
}
pcur=pcur->next;
}
greatertail->next=NULL;
//小链表与大链表连接
lesstail->next=greaterhead->next;
ListNode*ret=lesshead->next;
free(lesshead);
free(greaterhead);
lesshead=greaterhead=NULL;
return ret;
}
6.相交链表
https://leetcode.cn/problems/intersection-of-two-linked-lists/
需要判断链表是否相交(通过判断尾指针,注意需要用地址判断,用值判断有缺陷,可能两个链表的节点值相等,但是节点不相交),若相交,找出第一个交点。
一般我们会想到,让A链表的节点依次与B链表节点比较, A链表的某个节点与B链表某个节点相等,这个节点就是交点,该时间复杂度为O(N^2)
/*
struct ListNode {
int val;
struct ListNode *next;
};
*/
typedef struct ListNode LN;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
LN*cur1=headA,*cur2=headB;
int lenA=1,lenB=1;
while(cur1){
cur1=cur1->next;
lenA++;
}
while(cur2){
cur2=cur2->next;
lenB++;
}
//尾节点不相同就没有相交
if(cur1!=cur2){
return NULL;
}
//假设法,先假设LIstA是长链表。
int gap=abs(lenA-lenB);
LN* longlist = headA;
LN* shortlist = headB;
if(lenA<lenB){
longlist=headB;
shortlist=headA;
}
//同时移动时,让长链表与短链表的相对起始位置相同。
while(gap--){
longlist=longlist->next;
}
while(longlist!=shortlist){
longlist=longlist->next;
shortlist=shortlist->next;
}
return longlist;
}