侵入式与非侵入式链表
非侵入式链表
非侵入式链表中数据保存在节点中,结点链接域存的是下一个结点的内存首地址
template<typename T>
struct ListNode {
ListNode* prev;
ListNode* next;
T value;
}
侵入式链表
“侵入式”这个术语的来源可以追溯到数据结构和算法中的一个概念,即“对象的内部状态由外部对象管理”。在侵入式链表中,每个节点都包含指向下一个节点的指针,并且链接关系由节点本身维护。因此,在访问链表时,客户端代码必须直接侵入到节点结构体中来获取指向下一个节点的指针。
相比之下,在非侵入式链表中,每个节点只是一个独立的对象,并且链接关系由链表本身维护。因此,客户端代码不需要了解节点的内部结构,而只需调用链表提供的方法来插入、删除或遍历节点。这就是为什么它被称为“非侵入式”链表。
因此,“侵入式”一词指的是客户端代码需要直接侵入到节点结构体中才能访问链表的特定部分。虽然这种做法可能会使代码更复杂和难以维护,但它通常比非侵入式链表提供更高的性能和灵活性。
struct ListNode {
ListNode* prev;
ListNode* next;
}
template<typename T>
struct Data {
ListNode list;
T value;
}
侵入式链表和非侵入式链表的主要区别在于它们如何表示节点之间的链接关系。
在非侵入式链表中,每个节点通常是一个单独的对象,并且包含指向下一个节点的指针。这意味着节点本身并不知道它们在链表中的位置。相反,链表本身维护着所有节点的顺序和链接关系。
在另一方面,侵入式链表中,除了节点本身的数据,每个节点都包含一个指向下一个节点的指针。这个指针可以是节点成员变量,也可以通过指针计算得出。因此,节点本身就知道它们在链表中的位置,并且可以使用指针直接访问相邻的节点。
由于每个节点都包含指向下一个节点的指针,侵入式链表通常比非侵入式链表更紧凑,因此具有更好的空间效率。此外,由于节点本身知道它们在链表中的位置,因此插入和删除节点时不需要遍历整个链表来查找节点,从而提高了插入和删除操作的效率。
然而,使用侵入式链表需要设计良好的数据结构,以在节点中存储所有必要的信息。这需要更多的设计和开发工作,但在某些情况下,侵入式链表可以提供更高的性能和灵活性。
侵入式链表的特点:
- 非侵入式链表中需要解引用才能访问到数据,而侵入式链表不需要额外的解引用操作
- 侵入式链表删除指定节点覅咋读为O(1),无需迭代遍历
- 可以将同一份数据加入到多个链表中
样例
template<class T>
class IntrusiveList {
private:
struct Node;
public:
class iterator {
public:
iterator(typename IntrusiveList<T>::Node *node) : node_(node) {}
~iterator() {
delete node_;
}
iterator &operator++() {
if (node_) {
node_ = node_->next;
}
return *this;
}
iterator &operator--() {
if (node_) {
node_ = node_->prev;
}
return *this;
}
bool operator==(const iterator &iter) const {
return node_ == iter.node_;
}
bool operator!=(const iterator &iter) const {
return !operator==(iter);
}
T &operator*() {
return *node_;
}
T *operator->() {
return node_;
}
private:
typename IntrusiveList<T>::Node *node_;
};
IntrusiveList() : head_(nullptr), tail_(nullptr) {}
~IntrusiveList() {
auto node = head_;
while (node != nullptr) {
auto temp = node;
node = node->next;
delete temp;
}
}
T *append(T t) {
Node *node = new Node(t);
if (head_ == nullptr && tail_ == nullptr) {
head_ = node;
tail_ = node;
} else {
tail_->next = node;
node->prev = tail_;
tail_ = node;
}
return node;
}
void remove(T *t) {
Node *node = (Node *) t;
assert(typeid(*node).name() == typeid(Node).name());
if (head_ == node) {
head_ = head_->next;
}
if (tail_ == node) {
tail_ = tail_->prev;
}
if (node->prev) {
node->prev->next = node->next;
}
if (node->next) {
node->next->prev = node->prev;
}
delete node;
}
iterator begin() {
return iterator(head_);
}
iterator end() {
return iterator(nullptr);
}
private:
struct Node : public T {
Node *prev;
Node *next;
Node(T t) : T(t), prev(nullptr), next(nullptr) {}
};
Node *head_;
Node *tail_;
};