【问题标题】:pop_back() in Singly Linked List in C++C++ 单链表中的 pop_back()
【发布时间】:2020-07-08 02:27:05
【问题描述】:

我编写了单链表的以下实现。我面临的一个问题是我希望pop_back() 删除最后一个节点,然后将tail 设置为 O(1) 中的倒数第二个节点。现在,问题是它不是一个双向链表,所以如果不运行循环,我就无法访问倒数第二个节点。我在 O(1) 中完成了push_back()。如何在此代码中在 O(1) 中生成 pop_back()

#include <iostream>
using namespace std;

template <class T>
class LinkedList {
private:
    T data;
    LinkedList *next, *head, *tail;
public:
    LinkedList() : next(nullptr), head(nullptr), tail(nullptr){}
    LinkedList* get_node(T);
    void push_back(T);
    void insert(int, T);
    void pop_back();
    void erase(int);
    void print();
};

template <typename T>
LinkedList<T>* LinkedList<T>:: get_node(T element) {
    auto* new_node = new LinkedList;
    new_node -> data = element;
    new_node ->next = nullptr;
    return new_node;
}

template <typename T>
void LinkedList<T>:: push_back(T element) {
    if(head == nullptr) {
        head = get_node(element);
        tail = head;
    } else {
        LinkedList *node = get_node(element);
        tail->next = node;
        tail = node;
    }
}

template <typename T>
void LinkedList<T>:: insert(int position, T element) {
    LinkedList *node = get_node(element);
    if(position == 1){
        node->next = head;
        head = node;
    } else {
        LinkedList *start = head;
        int it = 1;
        while (it < position - 1) {
            start = start->next;
            ++it;
        }
        node->next = start->next;
        start->next = node;
    }
}
template <typename T>
void LinkedList<T>:: pop_back() {

}


template <typename T>
void LinkedList<T>:: erase(int position) {
    LinkedList *temp;
    if(position == 1){
        temp = head;
        head = head ->next;
        delete temp;
    } else {
        LinkedList *start = head;
        int it = 1;
        while (it < position - 1) {
            start = start->next;
            ++it;
        }
        temp = start -> next;
        start ->next = start ->next->next;
        delete temp;
    }
}

template <typename T>
void LinkedList<T>:: print() {
    LinkedList *start = head;
    while(start != nullptr) {
        cout << start->data << " ";
        start = start->next;
    }
}

int main() {
    LinkedList<int> lt;
    lt.push_back(2);
    lt.push_back(3);
    lt.push_back(4);
    lt.push_back(5);
    lt.insert(1, 34);
    lt.print();
}

【问题讨论】:

  • 遗憾的是,单链表是不可能的。由于您的LinkedList alreday 的大小至少为 4 个字,因此将其进行双重链接应该不会使它变得更加臃肿。

标签: c++ data-structures linked-list singly-linked-list


【解决方案1】:

这是不可能的。

说明

如果您真的想在 O(1) 中执行此操作并希望保持列表单链接,您可以考虑存储除最后一个节点之外的倒数第二个节点。

但是,除了成为组织上的恐怖之外,它仍然行不通。当然,在pop_back 中,您可以简单地使用delete tail; 并将尾部设置为倒数第二个元素。但是现在您仍然需要一个循环来确定新的倒数第二个元素。

这还很糟糕吗?

我不这么认为。每个数据结构都有它的优点和缺点。在我看来,在某些事情上确实做得很好,但在其他事情上表现不佳的数据结构比在中等速度上完成所有事情的数据结构要好。最好让你的班级保持苗条,针对特定场景对其进行优化并接受它的缺点。

【讨论】:

    【解决方案2】:

    你不能使用单个链表,让我们实现你的

    template <typename T>
    void LinkedList<T>:: pop_back() {
       tail = find(pos); 
       erase_next(tail); // erase tail's next.
    }
    

    您需要一个函数来查找位置,这意味着您还需要知道其中有多少个,在这种情况下 pos 的大小为 1(如果使用零偏移,则为 -2)或搜索具有 pos 的节点作为它的下一个节点。你可以使用erase中的大部分代码来实现它。

    现在这个新发现将有一个 O(n),这是单链表的诅咒。

    【讨论】:

      【解决方案3】:

      遗憾的是,你不能在 O(1) 时间内做到这一点,因为没有办法做到这一点,你必须为性能补偿内存并使用双链表。 如果您可以选择使用其他数据结构,我建议您使用 Deque

      【讨论】:

        【解决方案4】:

        正如其他答案中提到的,添加到单链表的末尾是一个 O(N) 操作。出于这个原因,我的 API 中根本没有包含 pop_back。如果用户需要像双端队列一样从同一个链表的两端弹出,他们应该使用双向链表。

        然而,这并不是一个完全失败的原因,因为您仍然可以反向使用单链表来实现 O(1) FIFO 和 LIFO 行为:

        • 对于堆栈,不要使用push_backpop_back,而是使用 push_frontpop_front,这是 O(1) 操作。
        • 对于队列,不要使用push_frontpop_back,而是使用 push_backpop_front,它们是 O(1) 操作。

        总而言之,push_frontpop_frontpush_back 是 O(1) 操作,但 pop_back 不是 - 它是 O(N)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-08-20
          • 2011-01-19
          • 2014-04-04
          • 1970-01-01
          • 2014-06-10
          • 2016-06-09
          相关资源
          最近更新 更多