3.1链表基本概念+经典OJ题

本篇博客来探讨数据结构当中的链表,并且来手撕一些经典算法OJ题,OJ题已插入超链接,点击可直接跳转~

一、链表的概念与结构

1.概念

链表是物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
概念

2.链表的分类

(1)单向或者双向

单向或者双向

(2)带头或者不带头

带头或者不带头
带头:指的是带有哨兵位head,head不存储具体的信息,只是作为一个站岗的位置,方便链表的操作

(3)循环或者非循环

循环或者非循环

二、链表经典算法OJ题

1.返回倒数第k个节点

(1)题目描述

实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
示例

(2)题目分析

核心思路:快慢指针
请添加图片描述

  • 第一步:fast指针先走k步
  • 第二步:fast和slow指针同时++,往后一步步走,直到fast=NULL
    具体代码实现如下
int kthToLast(struct ListNode* head, int k)
{
    struct ListNode*fast=head;
    struct ListNode*slow=head;
    k=k-1;
    while(k--)
    {
        fast=fast->next;
    }

    while(fast->next!=NULL)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return slow->val;
}

2.链表的回文结构

(1)题目描述

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
示例
对回文结构的理解:链表从中间砍成两半,如果对称即为回文

(2)题目分析

  • 第一步:先找中间节点
    思路:快慢指针——fast的速度为slow的二倍,则fast指针指向链表末尾的时候,slow指向链表中间节点,此处需要理清什么是中间节点?
    先找中间节点
    红色箭头所指就是中间节点
    具体代码实现如下
  ListNode* findMid(ListNode*A)
    {
        ListNode*fast=A;
        ListNode*slow=A;
        while(fast&&fast->next)
        {
            fast=fast->next->next;
            slow=slow->next;
        }
        return slow;
    }
  • 第二步:分成两半,将后半段逆置
    本题算法可以关联题目“反转链表
    思路:创建三个指针
    思路
    具体代码实现如下
  ListNode*reverse(ListNode*mid)
    {
        ListNode*n1=NULL;
        ListNode*n2=mid;
        ListNode*n3=mid->next;
        while(n2)
        {
            n2->next=n1;
            n1=n2;
            n2=n3;
            if(n3)
            {
                n3=n3->next;
            }
        }
        return n1;
    }
  • 第三步:将前半段的链表和后半段的链表进行比较,用两个指针进行遍历,直到其中一个指针为NULL,根据遍历情况确定返回true还是false
    具体代码实现如下
  bool chkPalindrome(ListNode* A) 
    {
        ListNode*mid=findMid(A);
        ListNode*remid=reverse(mid);
        ListNode*p1=A;
        ListNode*p2=remid;
        while(p1&&p2)
        {
            if(p1->val!=p2->val)
            {
                return false;
            }
            p1=p1->next;
            p2=p2->next;
        }
        return true;
    }

完整代码呈现如下(C++兼容C,网页内虽然没提供C语言环境,但也可以直接用C语言写)

class PalindromeList 
{
public:
    ListNode* findMid(ListNode*A)
    {
        ListNode*fast=A;
        ListNode*slow=A;
        while(fast&&fast->next)
        {
            fast=fast->next->next;
            slow=slow->next;
        }
        return slow;
    }
    ListNode*reverse(ListNode*mid)
    {
        ListNode*n1=NULL;
        ListNode*n2=mid;
        ListNode*n3=mid->next;
        while(n2)
        {
            n2->next=n1;
            n1=n2;
            n2=n3;
            if(n3)
            {
                n3=n3->next;
            }
        }
        return n1;
    }
    bool chkPalindrome(ListNode* A) 
    {
        ListNode*mid=findMid(A);
        ListNode*remid=reverse(mid);
        ListNode*p1=A;
        ListNode*p2=remid;
        while(p1&&p2)
        {
            if(p1->val!=p2->val)
            {
                return false;
            }
            p1=p1->next;
            p2=p2->next;
        }
        return true;
    }
};

3.相交链表

(1)题目描述

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
相交链表图示

(2)题目分析

  • 第一步:判断两个链表是否相交(遍历两个链表,看尾指针是否指向同一个地方,是的话就相交)
  • 第二步:若相交,找出第一个交点
    思路分析
    具体代码实现如下
typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    //1.判断是否相交
    ListNode*ptail1=headA;
    ListNode*ptail2=headB;
    int lenA=1;
    int lenB=1;
    while(ptail1)
    {
        ptail1=ptail1->next;
        lenA++;
    }

    while(ptail2)
    {
        ptail2=ptail2->next;
        lenB++;
    }
    if(ptail1!=ptail2)
    {
        return NULL;
    }

    //2.找相交的节点
    ListNode* _long=headA;
    ListNode* _short=headB;
    if(lenA>=lenB)//假设法,经过此处处理之后就可以不用关心到底是A链表长还是B链表长
    {
        _long=headA;//此处保证了long指向了较长的链表
        _short=headB;
    }
    else
    {
        _long=headB;
        _short=headA;
    }

    int gap=abs(lenA-lenB);
    ListNode* p1=_long;
    ListNode* p2=_short;
    while(gap--)
    {
        p1=p1->next;
    }
    while(p1&&p2)
    {
        if(p1==p2)
        {
            return p1;
        }
        else
        {
            p1=p1->next;
            p2=p2->next;
        }
    }
    return NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值