【初阶数据结构】单链表OJ题较难题讲解

 前言

📚作者简介:爱编程的小马,正在学习C/C++,Linux及MySQL。

📚本文收录与初阶数据结构系列,本专栏主要是针对时间、空间复杂度,顺序表和链表、栈和队列、二叉树以及各类排序算法,持续更新!

📚相关专栏C++及Linux正在发展,敬请期待!


1.链表OJ题

1.1 第一题

第一题链接:链表分割 

题目描述: 

现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。 

 题目分析:

这道题呢解法很多,大部分同学想到的呢就是把小于x结点的放前面,大于X的放后面,但是注意, 题目有一个要求是不能改变原数据的顺序,所以呢,需要考虑其他方法。

我在这个地方给大家提供一个很快速的方法:

为什么用带哨兵位(带头的链表),主要是因为可以不用判断链表是不是为空,在后面链接greater链表的时候也更加方便。

代码实现:

ListNode* partition(ListNode* pHead, int x)
    {
        struct ListNode* lesshead , *lesstail,*greaterhead,*greatertail; 
        lesshead = lesstail = (struct ListNode *)malloc(sizeof(struct ListNode));
        greaterhead = greatertail = (struct ListNode *)malloc(sizeof(struct ListNode));
        struct ListNode* cur = pHead;
        while(cur)
        {
            if(cur->val < x)
            {
                lesstail->next = cur;
                lesstail = lesstail->next;
            }   
            else
            {
                greatertail->next = cur;
                greatertail = greatertail->next;
            }
            cur = cur->next;
        }
        lesstail->next = greaterhead->next;
        greatertail->next = NULL;
        pHead = lesshead->next;
        free(lesshead);
        free(greaterhead);
        return pHead;
    }

大家记住,就是哨兵位知识一个站位的,不存储有效数据。是否使用带头的大家也不要死记硬背,而是根据实际情况来决定。

1.2 第二题

 第二题链接:链表的回文结构

题目描述: 

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

回文是什么意思呢?就是是否对称,中间结点前面的链表的数值和后面的是否相同,相同就返回Ture,否则就返回False。这题应该怎么做?有些同学想到将数值取出来放在数列中,然后左右两边同时走最后,走完了全部相同就判断为真,这样可不可以,其实是可以的,但是这道题有要求空间复杂度为O(1),那怎么办?

题目分析:  

其实核心就是,要想到逆置字符串,一起来看下代码

代码实现: 

bool chkPalindrome(ListNode* head) {
        //找到中间点然后逆置
        //找中间点
        struct ListNode * rmid = head;
        struct ListNode * fast = head;
        struct ListNode * slow = head;
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
       rmid = slow;
       struct ListNode * n1 = NULL;
       struct ListNode * n2 = rmid;
       struct ListNode * n3 = rmid->next;
       while(n3)
       {
            n2->next = n1;
            n1 = n2;
            n2 = n3;
            if(n3)
                n3=n3->next;
       }
       rmid = n2;
       while(rmid)
       {
            if(head->val != rmid->val)
                return false;
            else
             {
                head = head->next;
                rmid = rmid->next;
             }
       }
       return true;
    }

1.3 第三题

第三题链接:相交链表

题目描述: 

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

题目分析:

 这道题比较复杂,要仔细听。

代码实现:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    struct ListNode * tailA = headA;
    struct ListNode * tailB = headB;
    int lenA = 1;
    int lenB = 1;
    while(tailA->next)
    {
        tailA = tailA->next;
        lenA++;
    }
    while(tailB->next)
    {
        tailB = tailB->next;
        lenB++;
    }
    if(tailA != tailB)
    {
        return NULL;
    }
    struct ListNode* longlist = headA;
    struct ListNode* shortlist = headB;
    if(lenA < lenB)
    {
        longlist = headB;
        shortlist = headA;
    }
    int dif = abs(lenA-lenB);
    while(dif--)
    {
        longlist = longlist->next;
    }
    while(longlist)
    {
        if(longlist != shortlist)
        {
            longlist = longlist->next;
            shortlist = shortlist->next;
        } 
        else
        {
            break;
        }
    }
    return longlist; 
}

1.4 第四题

第四题链接:环形链表 

题目描述: 

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

题目分析: 

很多同学认为,带环链表循环无法结束,不带环链表可以结束,那用循环判断就可以了呀,不需要其他方法,但是带环链表的循环是死循环,程序无法结束。那么我们一起来分析一下, 

上图是什么意思呢,是这样,首先定义快慢指针,快指针一次走两步,慢指针一次走一步,等到慢进链表时候快指针就开始追赶慢指针,直到最后追上慢指针结束。只要是能追上就是带环,不能追上就不带环。

 代码实现:

bool hasCycle(struct ListNode *head) 
{
    struct ListNode * slow = head;
    struct ListNode * fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)
            return true;
    }
    return false;
}

1.5 第五题

第五题链接:环形链表Ⅱ

 题目描述:

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表

题目分析: 

实现代码: 

struct ListNode *detectCycle(struct ListNode *head)
{
    struct ListNode * slow = head;
    struct ListNode * fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)
        {
            struct ListNode* meet = fast;
            while(head != meet)
            {
                head = head->next;
                meet = meet->next;
            }
            return meet;
        }
    }
    return NULL;
}

1.6 为什么第四题和第五题可以这么玩,深度剖析

对于第四题来说,我想问问两个问题

1、fast和slow一定会相遇吗? 答案:一定能相遇

2、fast必须比slow,多走n步可以吗?(n=2,3,4,5,...) 答案:不一定可以

解答:

1、如果fast比slow多走一步,就一定会相遇,为什么?看图

 

 2.多走n步可以吗?

还有一个问题呢是为什么第五题可以这么玩?相遇点凭什么一定能遇到的头结点?凭什么能返回相遇点的地址,他和链表入口处是一样的吗? 

 

总结

1、其实OJ题并不难,难的就是一些思路以及算法这方面可能见得少了没想到,大家一定要多联系多思考

2、需要把链表学的比较好一些,因为这个是后面的基础,下一讲给大家更新带头双向循环链表。

 如果这份博客对大家有帮助,希望各位给小马一个大大的点赞鼓励一下,如果喜欢,请收藏一下,谢谢大家!!!
制作不易,如果大家有什么疑问或给小马的意见,欢迎评论区留言

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值