链表代码实现(C++)
数据结构第三篇
一、几个注意点
1、同时持有头尾结点的引用
双链表一般同时持有头尾结点的引用
因为在工程应用中,通常在容器尾插入元素,双链表持有尾部节点的引用,就可以在O(1)时间复杂度的情况下在尾部添加元素。
对于单链表,持有尾节点的引用也有优化效果,比如在单链表尾部添加元素;如果没有尾部节点的引用,就需要遍历整个链表找到尾部节点,时间复杂度O(n);如果有尾部节点的引用,就可以在O(1)的时间复杂度内完成尾部添加元素的操作;如果删除一次单链表的尾节点,那么之前的尾节点的引用就会失效,还需要遍历一次才能找到尾节点?但是其实,可以更新尾结点的引用
2、虚拟头尾结点
在创建双链表时,创建一个虚拟的头尾结点,无论双链表是否为空,这两个结点都存在,这样就不会出现空指针的问题,避免边界处理的问题。
空的双链表:
dummyHead <-> dummyTail
添加两个元素:
dummyHead <-> 1 <-> 2 <-> dummyTail
之前需要在头部插入元素,需要分类讨论,现在有了虚拟头尾结点,无论是否为空,都不需要分类讨论。
注意:虚拟头尾结点是对外不可见的
3、内存泄漏
// 假设单链表头结点 head = 1 -> 2 -> 3 -> 4 -> 5
// 删除单链表头结点
head = head.next; // 此时 head = 2 -> 3 -> 4 -> 5
删除结点,最好是把删除的结点都设为null
二、代码实现
1、双链表
#include <iostream>
#include <stdexcept>
template<typename E>
class MyLinkedList {
// 虚拟头尾节点
struct Node {
E val;
Node* next;
Node* prev;
Node(E value) : val(value), next(nullptr), prev(nullptr) {}
};
Node* head;
Node* tail;
int size;
public:
// 构造函数初始化虚拟头尾节点
MyLinkedList() {
head = new Node(E());
tail = new Node(E());
head->next = tail;
tail->prev = head;
size = 0;
}
// ***** 增 *****
void addLast(E e) {
Node* x = new Node(e);
Node* temp = tail->prev;
// temp <-> x
temp->next = x;
x->prev = temp;
x->next = tail;
tail->prev = x;
// temp <-> x <-> tail
size++;
}
void addFirst(E e) {
Node* x = new Node(e);
Node* temp = head->next;
// head <-> temp
temp->prev = x;
x->next = temp;
head->next = x;
x->prev = head;
// head <-> x <-> temp
size++;
}
void add(int index, E element) {
checkPositionIndex(index);
if (index == size) {
addLast(element);
return;
}
// 找到 index 对应的 Node
Node* p = getNode(index);
Node* temp = p->prev;
// temp <-> p
// 新要插入的 Node
Node* x = new Node(element);
p->prev = x;
temp->next = x;
x->prev = temp;
x->next = p;
// temp <-> x <-> p
size++;
}
// ***** 删 *****
E removeFirst() {
if (size < 1) {
throw std::out_of_range("No elements to remove");
}
// 虚拟节点的存在是我们不用考虑空指针的问题
Node* x = head->next;
Node* temp = x->next;
// head <-> x <-> temp
head->next = temp;
temp->prev = head;
E val = x->val;
delete x;
// head <-> temp
size--;
return val;
}
E removeLast() {
if (size < 1) {
throw std::out_of_range("No elements to remove");
}
Node* x = tail->prev;
Node* temp = tail->prev->prev;
// temp <-> x <-> tail
tail->prev = temp;
temp->next = tail;
E val = x->val;
x->prev = null;
x->next = null;
delete x;
// temp <-> tail
size--;
return val;
}
E remove(int index) {
checkElementIndex(index);
// 找到 index 对应的 Node
Node* x = getNode(index);
Node* prev = x->prev;
Node* next = x->next;
// prev <-> x <-> next
prev->next = next;
next->prev = prev;
E val = x->val;
x->prev = null;
x->next = null;
delete x;
// prev <-> next
size--;
return val;
}
// ***** 查 *****
E get(int index) {
checkElementIndex(index);
// 找到 index 对应的 Node
Node* p = getNode(index);
return p->val;
}
E getFirst() {
if (size < 1) {
throw std::out_of_range("No elements in the list");
}
return head->next->val;
}
E getLast() {
if (size < 1) {
throw std::out_of_range("No elements in the list");
}
return tail->prev->val;
}
// ***** 改 *****
E set(int index, E val) {
checkElementIndex(index);
// 找到 index 对应的 Node
Node* p = getNode(index);
E oldVal = p->val;
p->val = val;
return oldVal;
}
// ***** 其他工具函数 *****
int getSize() const {
return size;
}
bool isEmpty() const {
return size == 0;
}
void display() {
std::cout << "size = " << size << std::endl;
for (Node* p = head->next; p != tail; p = p->next) {
std::cout << p->val << " <-> ";
}
std::cout << "null" << std::endl;
std::cout << std::endl;
}
private:
Node* getNode(int index) {
checkElementIndex(index);
Node* p = head->next;
// TODO: 可以优化,通过 index 判断从 head 还是 tail 开始遍历
for (int i = 0; i < index; i++) {
p = p->next;
}
return p;
}
bool isElementIndex(int index) const {
return index >= 0 && index < size;
}
bool isPositionIndex(int index) const {
return index >= 0 && index <= size;
}
// 检查 index 索引位置是否可以存在元素
void checkElementIndex(int index) const {
if (!isElementIndex(index))
throw std::out_of_range("Index: " + std::to_string(index) + ", Size: " + std::to_string(size));
}
// 检查 index 索引位置是否可以添加元素
void checkPositionIndex(int index) const {
if (!isPositionIndex(index))
throw std::out_of_range("Index: " + std::to_string(index) + ", Size: " + std::to_string(size));
}
~MyLinkedList() {
while (size > 0) {
removeFirst();
}
delete head;
delete tail;
}
};
int main() {
MyLinkedList<int> list;
list.addLast(1);
list.addLast(2);
list.addLast(3);
list.addFirst(0);
list.add(2, 100);
list.display();
// size = 5
// 0 <-> 1 <-> 100 <-> 2 <-> 3 <-> null
return 0;
}
2、单链表
#include <iostream>
#include <stdexcept>
template <typename E>
class MyLinkedList2 {
private:
// 节点结构
struct Node {
E val;
Node* next;
Node(E value) : val(value), next(nullptr) {}
};
Node* head;
// 实际的尾部节点引用
Node* tail;
int size_;
public:
MyLinkedList2() {
head = new Node(E());
tail = head;
size_ = 0;
}
void addFirst(E e) {
Node* newNode = new Node(e);
newNode->next = head->next;
head->next = newNode;
if (size_ == 0) {
tail = newNode;
}
size_++;
}
void addLast(E e) {
Node* newNode = new Node(e);
tail->next = newNode;
tail = newNode;
size_++;
}
void add(int index, E element) {
checkPositionIndex(index);
if (index == size_) {
addLast(element);
return;
}
Node* prev = head;
for (int i = 0; i < index; i++) {
prev = prev->next;
}
Node* newNode = new Node(element);
newNode->next = prev->next;
prev->next = newNode;
size_++;
}
E removeFirst() {
if (isEmpty()) {
throw std::out_of_range("No elements to remove");
}
Node* first = head->next;
head->next = first->next;
if (size_ == 1) {
tail = head;
}
size_--;
E val = first->val;
delete first;
return val;
}
E removeLast() {
if (isEmpty()) {
throw std::out_of_range("No elements to remove");
}
Node* prev = head;
while (prev->next != tail) {
prev = prev->next;
}
E val = tail->val;
delete tail;
prev->next = nullptr;
tail = prev;
size_--;
return val;
}
E remove(int index) {
checkElementIndex(index);
Node* prev = head;
for (int i = 0; i < index; i++) {
prev = prev->next;
}
Node* nodeToRemove = prev->next;
prev->next = nodeToRemove->next;
// 删除的是最后一个元素
if (index == size_ - 1) {
tail = prev;
}
size_--;
E val = nodeToRemove->val;
delete nodeToRemove;
return val;
}
// ***** 查 *****
E getFirst() {
if (isEmpty()) {
throw std::out_of_range("No elements in the list");
}
return head->next->val;
}
E getLast() {
if (isEmpty()) {
throw std::out_of_range("No elements in the list");
}
return getNode(size_ - 1)->val;
}
E get(int index) {
checkElementIndex(index);
Node* p = getNode(index);
return p->val;
}
// ***** 改 *****
E set(int index, E element) {
checkElementIndex(index);
Node* p = getNode(index);
E oldVal = p->val;
p->val = element;
return oldVal;
}
// ***** 其他工具函数 *****
int size() {
return size_;
}
bool isEmpty() {
return size_ == 0;
}
~MyLinkedList2() {
Node* current = head;
while (current != nullptr) {
Node* next = current->next;
delete current;
current = next;
}
}
private:
bool isElementIndex(int index) {
return index >= 0 && index < size_;
}
bool isPositionIndex(int index) {
return index >= 0 && index <= size_;
}
// 检查 index 索引位置是否可以存在元素
void checkElementIndex(int index) {
if (!isElementIndex(index)) {
throw std::out_of_range("Index: " + std::to_string(index) + ", size_: " + std::to_string(size_));
}
}
// 检查 index 索引位置是否可以添加元素
void checkPositionIndex(int index) {
if (!isPositionIndex(index)) {
throw std::out_of_range("Index: " + std::to_string(index) + ", size_: " + std::to_string(size_));
}
}
// 返回 index 对应的 Node
// 注意:请保证传入的 index 是合法的
Node* getNode(int index) {
Node* p = head->next;
for (int i = 0; i < index; i++) {
p = p->next;
}
return p;
}
};
int main() {
MyLinkedList2<int> list;
list.addFirst(1);
list.addFirst(2);
list.addLast(3);
list.addLast(4);
list.add(2, 5);
std::cout << list.removeFirst() << std::endl; // 2
std::cout << list.removeLast() << std::endl; // 4
std::cout << list.remove(1) << std::endl; // 5
std::cout << list.getFirst() << std::endl; // 1
std::cout << list.getLast() << std::endl; // 3
std::cout << list.get(1) << std::endl; // 3
return 0;
}