数据结构链表oj讲解(上)

简介: 数据结构链表oj讲解

反转单链表


1、题目描述


leetcode:206、反转单链表


给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。


示例:


1669250586054.jpg


输入:head = [1,2,3,4,5]

输出:[5,4,3,2,1]


这道题目比较暴力的算法是新创建一个链表,把旧链表的数据一个一个挪下来,进行头插。


这样就可以解答出来,不过这种方式的时间复杂度是0(N),空间复杂度O(N),那么我们是否可以简化一下复杂度呢?很显然我们要想反转链表,遍历一遍是必须的,所以我们是否可以节省空间,对原链表下手,而不开辟新的空间呢?答案是肯定的。


2、解:三指针反转单链表


1669250601743.jpg


只需要注意一下最后需要返回的头是什么就可以了。


struct ListNode* reverseList(struct ListNode* head)
{
   struct ListNode* prev=NULL;
   struct ListNode* cur=head;
   struct ListNode* next=NULL;
   if(head==NULL)
   {
       return NULL;
   }
   while(cur)
   {
       next=cur->next;
       cur->next=prev;
       prev=cur;
       cur=next;
   }
   return prev;
}


合并两个有序链表


1、题目描述


leetcode:21、合并两个有序链表


将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。


1669250630816.jpg


两个指针对两个链表进行遍历,小的尾插,即可。需要注意的是当一个链表结束的时候另一个链表还没结束的处理。


2、解:


带哨兵位的链表进行解答在找头节点的时候比较方便。


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    struct ListNode* guard=(struct ListNode*)malloc(sizeof(struct ListNode));
    guard->next=NULL;
    struct ListNode* cur1=list1;
    struct ListNode* cur2=list2;
    struct ListNode* cur=guard;
    while(cur1&&cur2)
    {
        if(cur1->val<cur2->val)
        {
            cur->next=cur1;
            cur1=cur1->next;
        }
        else
        {
            cur->next=cur2;
            cur2=cur2->next;
        }
        cur=cur->next;
    }
    while(cur1)
    {
        cur->next=cur1;
        cur1=cur1->next;
        cur=cur->next;
    }
     while(cur2)
    {
        cur->next=cur2;
        cur2=cur2->next;
        cur=cur->next;
    }
    struct ListNode* newhead=guard->next;
    free(guard);
    guard=NULL;
    return newhead;
}


链表的回文结构


1、题目描述


牛客:(OR36 链表的回文结构)


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


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


测试样例:


1->2->1->2   返回: true


1->2->3->2->1 返回: true


1->2->3->4->2 返回: false


对于回文类的题目,比较快捷的方式就是找到链表的中间节点,对后半段链表进行翻转,然后和前半段链表进行比较,这种方式是比较便捷的。


这里还有一个问题,如何查找链表的中间节点,可能有的方式是遍历计数,然后找中间节点,这种方式是比较低效的,博主想给大家介绍的方式是采用快慢指针的方式查找中间节点。


演示:


1669250666522.jpg


通过观察可以发现,快慢指针对于奇数个和偶数个的处理不一致,只需添加限定条件即可。


找到中间节点后,反转链表我放在上面了,可以直接拿来用,所以,看代码:


class PalindromeList {
public:
     struct ListNode* middleNode(struct ListNode* head)
        {
            struct ListNode* fast,*slow;
            fast=slow=head;
            while(fast&&fast->next)
           {
            fast=fast->next->next;
            slow=slow->next;
           }
            return slow;
        }
        struct ListNode* reverseList(struct ListNode* head)
        {
            struct ListNode* cur=head;
            struct ListNode* prev=NULL;
            struct ListNode* next=NULL;
            while(cur)
            {
                next=cur->next;
                cur->next=prev;
                prev=cur;
                cur=next;
            }
            return prev;
        }
    bool chkPalindrome(ListNode* A) {
        // write code here
        //快慢指针找到一半,然后前后比较
       struct ListNode* mid=middleNode(A);
       struct ListNode* rmid=reverseList(mid);
        struct ListNode* cur=A;
        while(cur&&rmid)//奇数个和偶数个都要考虑到
        {
            if(cur->val!=rmid->val)
                return false;
            cur=cur->next;
            rmid=rmid->next;
        }
        return true;
    }
};


链表中倒数第k个节点


1、题目描述


牛客:(链表中倒数第k个节点)


输入一个链表,输出该链表中倒数第k个结点。


输入:1,{1,2,3,4,5}


返回值:{5}


如果掌握了快慢指针的精髓,那么这道题目对于大家来说是非常轻而易举的,因为这道题目让找到倒数第k个节点,这个节点和最后一个节点相差的不就是k,所以慢指针和快指针相差k,而快指针走到尾不就行了?不过这道题目还有很多细节的地方需要讲述。


struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{
    struct ListNode* fast=pListHead;
    struct ListNode* slow=pListHead;
    if(fast==NULL)
        return NULL;
    while(k--)
    {
        fast=fast->next;
        if(k>0&&fast==NULL)
            return NULL;
    }
    while(fast)
    {
        slow=slow->next;
        fast=fast->next;
    }
    return slow;
}

这段代码是可以跑过去的,不过可读性比较差,还可以优化。


主要问题是在这两处:

1669250716427.jpg

这处代码主要考虑有可能为NULL的问题。


1669250726183.jpg


这段代码为什么说它可读性差呢?主要就是k>0这部分让人摸不着头脑。为什么会这样写呢?这也是为了解决一些特殊情况:

如果k是恰好为这个链表的总长度,那么倒数第k个节点势必是第一个节点,但是这时fast已经指向NULL,为了则会返回NULL,这显然是错误的,但是这句代码是为了解决k大于整个链表长度时倒数第k个节点为NULL的情况,显然不能删,只好更改为这样,有没有更好的解决办法?答案肯定是有的:

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{
    // write code here
    //快慢指针
    //找倒数第四个
    struct ListNode* fast=pListHead;
    struct ListNode* slow=pListHead;
    if(pListHead)
    {
             while(k--)
        {
                 if(fast==NULL)
                 return NULL;
                 fast=fast->next;
        }
        while(fast)
        {
            fast=fast->next;
            slow=slow->next;
        }
    }
    return slow;
}

这样处理对代码进行了进一步的优化。

相关文章
|
10月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
161 4
|
7月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
184 30
|
7月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
285 25
|
7月前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
8月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
314 5
|
9月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
10月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
262 5
|
10月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
673 4
|
10月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
10月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
226 0