LeetCode 0707. 设计链表:C++小详解,万字小长文:分别借助和不借助STL解决

Posted Tisfy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 0707. 设计链表:C++小详解,万字小长文:分别借助和不借助STL解决相关的知识,希望对你有一定的参考价值。

【LetMeFly】707.设计链表:C++小详解,万字小长文:分别借助和不借助STL解决

力扣题目链接:https://leetcode.cn/problems/design-linked-list/

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 nextval 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

 

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2);   //链表变为1-> 2-> 3
linkedList.get(1);            //返回2
linkedList.deleteAtIndex(1);  //现在链表是1-> 3
linkedList.get(1);            //返回3

 

提示:

  • 所有val值都在 [1, 1000] 之内。
  • 操作次数将在  [1, 1000] 之内。
  • 请不要使用内置的 LinkedList 库。

方法一:借助STL解决

如果借助STL解决,那么就会比较简单,因为STL中的list已经实现了大部分功能,所需要的就是特判一下index,按照题意对输入的index进行相应的操作。

额外功能:

为了方便实现,我们自定义一个“获取链表第index个元素的迭代器”的函数(因为STL的list不支持随机访问,因此需要从头节点开始逐渐往后遍历)

inline list<int>::iterator getIndexIterator(int index) 
    list<int>::iterator it = li.begin();
    while (index--) 
        it++;
    
    return it;

这个函数不判断输入是否合法

初始化:

无需进行任何操作

取元素:

题目中说,“如果索引无效,则返回-1”

因此首先判断index是否合法,不合法则返回-1,合法则直接返回迭代器“getIndexIterator(index)”所指的元素

int get(int index) 
    if (index < 0 || index >= li.size())
        return -1;
    return *getIndexIterator(index);

往头部插入元素:

直接调用现有函数“push_front(int val)”

void addAtHead(int val) 
    li.push_front(val);

往尾部插入元素:

直接调用现有函数“push_back(int val)”

void addAtTail(int val) 
    li.push_back(val);

往中间位置插入元素:

按照题目要求:

  • 如果index小于零,则插入到头部(index=0)
  • 如果index大于链表长度,则不进行任何操作(return)
  • 否则插入到对应位置(迭代器,可由getIndexIterator获取)
void addAtIndex(int index, int val) 
    if (index < 0)
        index = 0;
    if (index > li.size())
        return;
    li.insert(getIndexIterator(index), val);

删除元素:

按照题目要求:

  • 如果index有效,则删除(直接调用erase即可)
  • index无效,则不操作
void deleteAtIndex(int index) 
    if (index >= li.size())
        return;
    li.erase(getIndexIterator(index));

  • 时间复杂度:初始化 O ( 1 ) O(1) O(1),插入/删除/获取元素 O ( i n d e x ) O(index) O(index)
  • 空间复杂度 O ( n ) O(n) O(n),其中 n n n取决于某时刻链表中最多的节点个数

AC代码

C++

class MyLinkedList 
private:
    list<int> li;

    inline list<int>::iterator getIndexIterator(int index) 
        list<int>::iterator it = li.begin();
        while (index--) 
            it++;
        
        return it;
    

    inline void debugList() 
        for (auto t : li) 
            cout << t << ", ";
        
        puts("");
    
public:
    MyLinkedList() 

    
    
    int get(int index) 
        if (index < 0 || index >= li.size())
            return -1;
        return *getIndexIterator(index);
    
    
    void addAtHead(int val) 
        li.push_front(val);
    
    
    void addAtTail(int val) 
        li.push_back(val);
    
    
    void addAtIndex(int index, int val) 
        if (index < 0)
            index = 0;
        if (index > li.size())
            return;
        li.insert(getIndexIterator(index), val);
    
    
    void deleteAtIndex(int index) 
        if (index >= li.size())
            return;
        li.erase(getIndexIterator(index));
    
;

方法二:不借助STL解决

如果不借助STL解决,那么可以自定义一个结构体,来存放某个节点。

之后手动实现各种操作。

额外功能:

定义结构体,来存放某个节点。

我们需要存放:

  1. 节点的值
  2. 节点的下一个节点的指针
struct __LetMeFly_ListNode 
    int val;
    __LetMeFly_ListNode* next;

    __LetMeFly_ListNode() : next(nullptr)   // 默认下一个节点为NULL
    __LetMeFly_ListNode(int val) : val(val), next(nullptr) 
;

为了方便实现,我们自定义一个“获取链表第index个元素的指针”的函数(从头节点开始逐渐往后遍历)

__LetMeFly_ListNode* getIndexIterator(int index)   // 无脑计算,不验证合法性
    __LetMeFly_ListNode* p = head->next;
    while (index--) 
        p = p->next;
    
    return p;

初始化:

初始化的时候,我们需要初始化头节点(为了方便,我们额外开辟一个不存放任何值的头节点)

同时,初始化节点长度为0

MyLinkedList() 
    head = new __LetMeFly_ListNode;
    size = 0;

取元素:

题目中说,“如果索引无效,则返回-1”

因此首先判断index是否合法,不合法则返回-1,合法则直接返回节点指针“getIndexIterator(index)”所指的节点的元素

int get(int index) 
    if (index < 0 || index >= size) 
        return -1;
    
    return getIndexIterator(index)->val;

往头部插入元素:

首先获取原始的第一个节点:head->next(是因为头部多开辟了一个不存放任何值的节点)

然后将头部节点的next指向新new出来的节点

并将新节点的next指向原始的第一个节点

最后更新size,将size加一

void addAtHead(int val) 
    __LetMeFly_ListNode* secondNode = head->next;
    head->next = new __LetMeFly_ListNode(val);
    head->next->next = secondNode;
    size++;

往尾部插入元素:

往尾部插入元素包括:

  1. 获取原始尾部元素
  2. 将原始尾部元素的next指向新节点
  3. 链表节点个数size加一

需要注意的是,尾部元素的下标是“节点个数 - 1”

因此需要特判,如果原始链表为空,节点个数减一就变成了负数。

其实,原始链表为空的时候,“往链表尾部插入一个元素” 等价于 “往链表头部插入一个元素”

void addAtTail(int val) 
    if (size == 0) 
        return addAtHead(val);
    
    getIndexIterator(size - 1)->next = new __LetMeFly_ListNode(val);
    size++;

往中间位置插入元素:

按照题目要求:

  • 如果index小于零,则插入到头部(index=0时也是插入到头部),直接调用刚刚写好的“addAtHead”即可
  • 如果index大于链表长度,则不进行任何操作(return)
  • 否则插入到对应位置。对应位置又分为两种:
    1. index等于链表长度,就插入到链表尾部(直接调用刚刚写好的“addAtTail”即可)
    2. 否则,通过“getIndexIterator”获取要插入元素上一个元素的位置,记录上一个元素的下一个元素,将上一个元素的指向新new出来的节点,并将新节点的next指向原本的“上一个元素的下一个元素”,更新链表中节点个数size
void addAtIndex(int index, int val) 
    if (index <= 0) 
        index = 0;
        addAtHead(val);
        return;
    
    if (index > size)
        return;
    if (index == size) 
        addAtTail(val);
        return;
    
    __LetMeFly_ListNode* lastNode = getIndexIterator(index - 1);
    __LetMeFly_ListNode* nextNode = lastNode->next;
    lastNode->next = new __LetMeFly_ListNode(val);
    lastNode->next->next = nextNode;
    size++;

删除元素:

按照题目要求,如果index有效,才进行删除操作:

  • 如果index为0,则是删除头部元素:
    • 记录原始的第二个元素(可能是空指针,但是没关系)
    • 删除第一个元素
    • 将链表中的头节点的next指向第二个元素
  • 否则:
    • 通过“getIndexIterator”获取待删除元素的前一个节点
    • 记录前一个节点的下一个节点的下一个节点
    • 删除前一个节点的下一个节点
    • 前一个节点的next指向原始的“下一个节点的下一个节点”

上述两种操作无需担心越界问题,因为已经判断了index有效。

无论怎样,只要index有效,就要更新size(size–)

void deleteAtIndex(int index) 
    if (index >= 0 && index < size) 
        if (index == 0) 
            __LetMeFly_ListNode* nextNode = head->next->next;
            delete head->next;
            head->next = nextNode;
        
        else 
            __LetMeFly_ListNode* lastNode = getIndexIterator(index - 1);
            __LetMeFly_ListNode* nextNode = lastNode->next->next;
            delete lastNode->next;
            lastNode->next = nextNode;
        
        size--;
    

  • 时间复杂度:初始化 O ( 1 ) O(1) O(1),插入/删除/获取元素 O ( i n d e x ) O(index) O(index)
  • 空间复杂度 O ( n ) O(n) O(n),其中 n n n取决于某时刻链表中最多的节点个数

AC代码

C++

struct __LetMeFly_ListNode 
    int val;
    __LetMeFly_ListNode* next;

    __LetMeFly_ListNode() : next(nullptr) 
    __LetMeFly_ListNode(int val) : val(val), next(nullptr) 
;


class MyLinkedList 
private:
    __LetMeFly_ListNode* head;
    size_t size;

    __LetMeFly_ListNode* getIndexIterator(int index)   // 无脑计算,不验证合法性
        __LetMeFly_ListNode* p = head->next;
        while (index--) 
            p = p->next;
        
        return p;
    
public:
    MyLinkedList() 
        head = new __LetMeFly_ListNode;
        size = 0;
    
    
    int get(int index) 
        if (index < 0 || index >= size) 
            return -1;
        
        return getIndexIterator(index)->val;
    
    
    void addAtHead(int val) 
        __LetMeFly_ListNode* secondNode = head->next;
        head->next = new __LetMeFly_ListNode(val);
        head->next->next = secondNode;
        size++;
    
    
    void addAtTail(int val) 
        if (size == 0) 
            return addAtHead(val);
        
        getIndexIterator(size - 1)->next = new __LetMeFly_ListNode(val);
        size++;
    
    
    void addAtIndex(int index, int val) 
        if (index <= 0) 
            index = 0;
            addAtHead(val);
            return;
        
        if (index > size)
            return;
        if (index == size) 
            addAtTail(val);
            return;
        
        __LetMeFly_ListNode* lastNode = getIndexIterator(index - 1);
        __LetMeFly_ListNode* nextNode = lastNode->next;
        lastNode->next = new __LetMeFly_ListNode(val);
        lastNode->next->next = nextNode;
        size++;
    
    
    void deleteAtIndex(int index) 
        if (index >= 0 && index < size) 
            if (index == 0) 
                __LetMeFly_ListNode* nextNode = head->next->next;
                delete head->next;
                head->next = nextNode;
            
            else 
                __LetMeFly_ListNode* lastNode = getIndexIterator(index - 1);
                __LetMeFly_ListNode* nextNode = lastNode->next->next;
                delete lastNode->next;
                lastNode->next = nextNode;
            
            size--;
        
    
;

同步发文于CSDN,原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/127009642

以上是关于LeetCode 0707. 设计链表:C++小详解,万字小长文:分别借助和不借助STL解决的主要内容,如果未能解决你的问题,请参考以下文章

leetcode 148. 排序链表(c++)

leetcode21 C++ 8ms 合并两个有序链表

精选力扣500题 第11题 LeetCode 141. 环形链表 c++详细题解

Leetcode No.21 Merge Two Sorted Lists合并两个有序链表(c++实现)

精选力扣500题 第18题 LeetCode 23. 合并K个升序链表c++详细题解

LeetCode 21. Merge Two Sorted Lists合并两个有序链表 (C++)