单链表部分题目的特点和解答(链表逆置,返回链表的中心节点,倒数第n个节点,链表回文)

备注:这里的所有题目都出自于LeetCode和牛客网题库

目录

1.链表的逆置

 2.返回链表的中心节点

3.返回链表倒数第n个节点的值

3.链表的回文问题


1.链表的逆置

题目地址:. - 力扣(LeetCode)

链表的逆置是将一个单向不循环的链表反过来,如1->2->3->4->5逆置后变为5->4->3->2->1,

进行逆置有很多种办法,开一个新的链表然后头插或者通过额外创建一个指针保存下一个节点然后一个个逆置等都是可行的办法

(1)额外创建一个新链表拿原链表的节点进行头插

创建一个空节点,然后遍历链表,新建一个节点保存当前节点的下一个节点避免找不到下一个节点,在节点指向空节点后,空节点应将其改成当前节点。在更改引用之后,将当前节点更改为保存的下一个节点。以此循环直到空退出循环,最后返回新的头引用。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) 
{
    struct ListNode*newhead=NULL;        //新的头节点
    struct ListNode*pcur=head;           //代替head
    while(pcur)                          //进行头插
    {
        struct ListNode*next=pcur->next;
        pcur->next=newhead;
        newhead=pcur;
        pcur=next;
    }
    return newhead;                       
}

 (2)额外创建一个新节点然后一个个节点逆置

这个方法和上一个的方法有相似之处如额外创建一个节点,但这个方法中的节点的用处就不再是新的头节点,它的用处是保存当前节点的下一个节点,然后直接对此链表进行逆置

1.创建三个新的指针,新的头P2,原本的头P1,进行保存下一个值的P3

2.进行一个个节点的逆置,同时P2=1节点,P1=P3,然后P3保存P1的下一个节点

3.将全部得节点逆置后P2为新的头节点,返回P2便可

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
//假设链表是1->2->3->4->5->NULL
typedef struct ListNode list;
struct ListNode* reverseList(struct ListNode* head)
{
     if(head==NULL)
    {
        return head;        //判断链表是否为空
    }
    list*p1=head;           //代替head
    list*p2=NULL;           //新的头结点
    list*p3=p1->next;       //用于保存下一个节点 
    while(p1)              
    {
        p1->next=p2;        //此时1指向空
        p2=p1;              //将p2改为1 
        p1=p3;              //将p1改为保存的下一个节点
        if(p3)              //判断想要保存的地址是否为空
        {
            p3=p3->next;    //不为空则保存下一个节点
        }
    }
    return p2;              //最后返回p2,此时为5->4->3->2->1->NULL
}

 2.返回链表的中心节点

题目地址:876. 链表的中间结点 - 力扣(LeetCode)

这个在链表的题目中是十分经典的题目,它的解法也有很多种,可以新建一个count来计数,也可以使用快慢指针的方式来解决

(1)使用count来计数,然后count/2得到中间节点得到位置

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode list;
struct ListNode* middleNode(struct ListNode* head)
{
   int count=1;            //count从一计数
   list*P1=head;           //用于遍历链表
   list*P2=head;           //用于遍历一半的链表
    while(P1&&P1->next)    
    {
       P1=P1->next;
       count++;
    }
    count=count/2;
    while(count--)
    {
    P2=P2->next;
    }
    return P2;
}

(2) 使用基于相对速度理论的快慢指针

这个方法引用了数学中的相对速度,设一个x指针走一步,一个y指针走x指针的两倍,y指针始终是x的两倍得出x=y/2,以此类推在链表中设立两个节点,在比较快的节点走到尾时,较慢的节点就是中间节点:

1.x走一步,y走两步

2.

3.y走到尾,x为中心节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode list;
struct ListNode* middleNode(struct ListNode* head)
{
    list *P1=head;        //快指针
    list *P2=head;        //慢指针
    while(P1&&P1->next)
    {
        P1=P1->next->next;
        P2=P2->next;
    }
    return P2;
}

3.返回链表倒数第n个节点的值

题目地址:. - 力扣(LeetCode)

这个题目顾名思义就是返回特点的节点是返回中心节点的衍生题,单链表无法反向前进所以反向查询没有效果,这个问题很好解决也可以依靠由相对速度理论产生的快慢指针来解决,也可以将这个链表逆置之后再进行节点前进

(1)逆置后查询(不推荐)

这个方法只需要引用链表逆置的代码,然后前进n次便可,但太过麻烦极不推荐

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
//链表逆置
typedef struct ListNode LIST;
struct ListNode* reverseList(struct ListNode* head)
{
    if(head==NULL)
    {
        return head;
    }
    LIST*P1=head;
    LIST*P2=NULL;
    LIST*P3=P1->next;
    while(P1)
    {
      P1->next=P2;
      P2=P1;
      P1=P3;
      if(P3)
      {
        P3=P3->next;
      }
    }
    return P2;
}
int kthToLast(struct ListNode* head, int k)
{
    struct ListNode*newhead=reverseList(head);
    struct ListNode*pcur=newhead;
    while(--k)
    {
        pcur=pcur->next;
    }
    return pcur->val;
}

(2)使用基于相对速度理论的快慢指针

通过相对速度理论来理解。创建连个指针fast,last,可以先让一个fast指针先走n步,然后两个指针同时前进,此时两个指针始终相差n个节点,当较快的指针走到NULL时停止,刚好较慢的指针last与尾节点相差n个节点,返回last的值

1.两个指针都指向头

2.fast先走n(假设n为2)步

3.然后同时前进

4.当fast走到空时,last节点刚好是倒数第n(2)个节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int kthToLast(struct ListNode* head, int k)
{
    struct ListNode*last=head;struct ListNode*fast=head;
    while(k--)
    {
        fast=fast->next;    //fast先走k步
    }
    while(fast)             //同时前进
    {
        last=last->next;
        fast=fast->next;
    }
    return last->val;       //返回较慢指针的值
}

3.链表的回文问题

题目地址:链表的回文结构_牛客题霸_牛客网

链表的回文是指以链表的中心节点向两边比较如果相同则是回文结构,比如1->2->2->1就是一个回文结构,这道题将两个题目(返回链表中心节点,链表的逆置)的使用方法结合了起来对于链表的理解有一定要求

ps:牛客的回文题目只有c++,c++也可以使用c的写法所以直接引用c的写法

先使用寻找中心节点的代码找到该链表的中心节点,随后使用链表逆置代码进行翻转,如果链表有奇数个也不怕,它将链表翻转后会变成这样的结构:

比较好理解的例子:1->2->3->2->1 会变成 1->2->3<-2<-1

如果想深入理解可以看下图:

对中心节点后面的节点逆置,在后半逆置后两边的2节点会同时指向3节点,3节点此时要指向空,然后两边的头节点同时开始前进进行对比,如果有一个不一致就返回false,走到NULL退出循环返回true

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
//链表的逆置
struct ListNode* reverseList(struct ListNode* head)
{
     if(head==NULL)
    {
        return head;
    }
    struct ListNode*p1=head;
    struct ListNode*p2=NULL;
    struct ListNode*p3=p1->next;
    while(p1)
    {
        p1->next=p2;
        p2=p1;
        p1=p3;
        if(p3)
        {
            p3=p3->next;
        }
    }
    return p2;
}
//寻找中心节点
struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode*P1=head;
    struct ListNode*P2=head;
    while(P1&&P1->next)
    {
        P1=P1->next->next;
        P2=P2->next;
    }
    return P2;
}
class PalindromeList 
{
public:
    bool chkPalindrome(ListNode* A) 
    {
        struct ListNode*newhead=A;
        struct ListNode*meet=middleNode(A);
        struct ListNode*pcur=reverseList(meet);
        while(pcur&&newhead)
        {
            if(pcur->val!=newhead->val)
            {
                return false;
            }
            pcur=pcur->next;
            newhead=newhead->next;
        }
        return true;
    }
};

本章到这里结束了,希望能够对你在链表的理解上产生帮助

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值