赞
踩
链表是一种常见的数据结构,它允许动态地分配内存空间,并通过指针(或引用)将数据元素连接在一起。双向链表作为链表的一种,除了拥有普通链表的特性外,每个节点还包含两个指针:一个指向前一个节点(prev),另一个指向后一个节点(next)。这种结构使得双向链表在插入、删除以及遍历等操作上都具有独特的优势。
双向链表的基本结构通常包括一个节点(Node)类和一个双向链表(DoublyLinkedList)类。节点类用于存储数据元素以及指向前后节点的指针,而双向链表类则用于管理这些节点,提供插入、删除、遍历等操作的方法。
节点类通常包含两个指针(prev和next)和一个数据元素(data)。在C++中,节点类可以定义如下:
template <typename T>
class Node {
public:
T data;
Node* prev;
Node* next;
Node(T data) : data(data), prev(nullptr), next(nullptr) {}
};
这里使用了模板类(template class),使得节点类可以存储任意类型的数据。
双向链表类需要包含一些基本操作,如插入节点、删除节点、遍历链表等。以下是一个简单的双向链表类的实现:
template <typename T> class DoublyLinkedList { private: Node<T>* head; // 指向头节点的指针 Node<T>* tail; // 指向尾节点的指针 int size; // 链表的大小 // 辅助函数,用于在指定位置插入节点 void insertAt(int position, T data) { // ... 实现细节将在后文给出 ... } // 辅助函数,用于删除指定位置的节点 void deleteAt(int position) { // ... 实现细节将在后文给出 ... } public: DoublyLinkedList() : head(nullptr), tail(nullptr), size(0) {} // 插入节点到链表末尾 void append(T data) { insertAt(size, data); } // 插入节点到链表开头 void prepend(T data) { insertAt(0, data); } // 删除指定位置的节点 void removeAt(int position) { if (position < 0 || position >= size) { throw std::out_of_range("Invalid position"); } deleteAt(position); } // 删除指定值的节点(如果存在多个,只删除第一个) void remove(T data) { Node<T>* current = head; while (current != nullptr) { if (current->data == data) { removeAt(currentIndex(current)); return; } current = current->next; } } // 遍历链表并打印节点数据 void printList() { Node<T>* current = head; while (current != nullptr) { std::cout << current->data << " "; current = current->next; } std::cout << std::endl; } // 获取链表大小 int getSize() const { return size; } // 检查链表是否为空 bool isEmpty() const { return size == 0; } // ... 其他可能的方法,如查找、反转等 ... };
接下来,我们将详细实现双向链表类中的一些关键操作。
在指定位置插入节点时,我们需要考虑几种情况:插入到链表开头、插入到链表末尾以及插入到链表中间。以下是insertAt方法的实现:
template <typename T> void DoublyLinkedList<T>::insertAt(int position, T data) { if (position < 0 || position > size) { throw std::out_of_range("Invalid position"); } Node<T>* newNode = new Node<T>(data); if (size == 0) { // 链表为空,新节点既是头节点也是尾节点 head = tail = newNode; } else if (position == 0) { // 插入到链表开头 newNode->next = head; head->prev = newNode; head = newNode; } else if (position == size) { // 插入到链表末尾 newNode->prev = tail; tail->next = newNode; tail = newNode; } else { // 插入到链表中间 Node<T>* current = head; for (int i = 0; i < position - 1; ++i) { current = current->next; } newNode->prev = current; newNode->next = current->next; current->next->prev = newNode; current->next = newNode; } ++size; }
删除指定位置的节点时,我们需要考虑删除头节点、删除尾节点以及删除中间节点的情况。以下是deleteAt方法的实现:
template <typename T> void DoublyLinkedList<T>::deleteAt(int position) { if (position < 0 || position >= size) { throw std::out_of_range("Invalid position"); } Node<T>* target = head; if (position == 0) { // 删除头节点 head = head->next; if (head != nullptr) { head->prev = nullptr; } else { tail = nullptr; // 如果链表为空,则尾节点也为空 } } else if (position == size - 1) { // 删除尾节点 target = tail; tail = tail->prev; if (tail != nullptr) { tail->next = nullptr; } else { head = nullptr; // 如果链表为空,则头节点也为空 } } else { // 删除中间节点 for (int i = 0; i < position; ++i) { target = target->next; } target->prev->next = target->next; target->next->prev = target->prev; } delete target; --size; }
下面是一个简单的测试程序,用于演示双向链表的用法:
#include <iostream> int main() { DoublyLinkedList<int> list; list.append(1); list.append(2); list.prepend(0); list.insertAt(1, 100); list.printList(); // 输出: 0 100 1 2 std::cout << "Size: " << list.getSize() << std::endl; // 输出: 4 std::cout << "Is empty? " << (list.isEmpty() ? "Yes" : "No") << std::endl; // 输出: No list.removeAt(1); // 删除索引为1的节点(值为100) list.printList(); // 输出: 0 1 2 list.remove(1); // 删除值为1的节点 list.printList(); // 输出: 0 2 return 0; }
本文详细介绍了双向链表的基本结构、操作实现以及C++代码示例。双向链表通过引入两个指针(prev和next)来实现对前后节点的访问,从而在插入、删除和遍历等操作上提供了更高的灵活性。在实际应用中,双向链表常用于需要频繁进行节点插入和删除的场景,如LRU(最近最少使用)缓存淘汰算法等。通过掌握双向链表的基本原理和实现方法,读者可以更好地理解和应用这种数据结构。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。