链表题目练习及讲解

本节小编将分享链表的部分练习题,友友们可以自己去练练,更好的理解链表!

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.反转链表

206. 反转链表 - 力扣(LeetCode)

给你单链表的头节点 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;
}

希望支持小编的留下三连和评论吧!!!

  • 91
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 33
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值