顺序表
静态定义
#include <iostream>
#include <stdexcept>
const int MAX_SIZE = 100; // 假设顺序表的最大容量是100
template <typename T>
class ArrayList {
private:
T elements[MAX_SIZE]; // 用于存储元素的数组
int size; // 当前顺序表中的元素个数
public:
// 构造函数
ArrayList() : size(0) {}
// 获取顺序表的当前大小
int getSize() const {
return size;
}
// 检查顺序表是否为空
bool isEmpty() const {
return size == 0;
}
// 在顺序表末尾添加元素
void append(const T& element) {
if (size < MAX_SIZE) {
elements[size] = element;
size++;
} else {
throw std::overflow_error("顺序表已满,无法添加元素");
}
}
// 获取指定位置的元素
T get(int index) const {
if (index >= 0 && index < size) {
return elements[index];
} else {
throw std::out_of_range("索引越界");
}
}
// 在指定位置插入元素
void insert(int index, const T& element) {
if (size < MAX_SIZE && index >= 0 && index <= size) {
for (int i = size - 1; i >= index; --i) {
elements[i + 1] = elements[i];
}
elements[index] = element;
size++;
} else {
throw std::out_of_range("索引越界或顺序表已满,无法插入元素");
}
}
// 在指定位置删除元素
void remove(int index) {
if (index >= 0 && index < size) {
for (int i = index; i < size - 1; ++i) {
elements[i] = elements[i + 1];
}
size--;
} else {
throw std::out_of_range("索引越界");
}
}
// 打印顺序表中的所有元素
void display() const {
for (int i = 0; i < size; ++i) {
std::cout << elements[i] << " ";
}
std::cout << std::endl;
}
};
动态定义
要求掌握插入、删除、搜索以及其性能分析
#i。nclude <iostream>
#include <cstdlib>
const int defaultSize = 100;
template <class T>
class SeqList {
protected:
T* data; // 存放数组
int maxSize; // 最大可容纳表项的项数
int last; // 当前已存表项的最后位置(从0开始)
void reSize(int newSize); // 改变data数组空间大小
//public 成员可以被类的外部访问,也可以被派生类访问。
//protected 成员不能被类的外部直接访问,但可以被派生类访问。
//private 成员只能被类的内部访问,连派生类都不能直接访问。
public:
SeqList(int size = defaultSize); // 构造函数
~SeqList(); // 析构函数
int getSize() const; // 获取顺序表的当前大小
bool isEmpty() const; // 检查顺序表是否为空
void insert(int index, const T& element); // 在指定位置插入元素
void remove(int index); // 在指定位置删除元素
T get(int index) const; // 获取指定位置的元素
void display() const; // 打印顺序表中的所有元素
};
// 实现构造函数
template <class T>
SeqList<T>::SeqList(int size) {
if (size <= 0) {
std::cerr << "Error: Invalid size for SeqList." << std::endl;
exit(EXIT_FAILURE);
}
maxSize = size;
data = new T[maxSize];
last = -1;
}
// 实现析构函数
template <class T>
SeqList<T>::~SeqList() {
delete[] data;
}
// 实现获取顺序表大小的函数
template <class T>
int SeqList<T>::getSize() const {
return last + 1;
}
// 实现检查顺序表是否为空的函数
template <class T>
bool SeqList<T>::isEmpty() const {
return last == -1;
}
// 实现插入元素的函数
template <class T>
void SeqList<T>::insert(int index, const T& element) {
if (index < 0 || index > last + 1 || last == maxSize - 1) {
std::cerr << "Error: Invalid index for insertion or SeqList is full." << std::endl;
exit(EXIT_FAILURE);
}
for (int i = last; i >= index; --i) {
data[i + 1] = data[i];
}
data[index] = element;
last++;
}
// 实现删除元素的函数
template <class T>
void SeqList<T>::remove(int index) {
if (index < 0 || index > last) {
std::cerr << "Error: Invalid index for removal." << std::endl;
exit(EXIT_FAILURE);
}
for (int i = index; i < last; ++i) {
data[i] = data[i + 1];
}
last--;
}
template <class T>
int SeqList<T>::search(const T& target) const {
for (int i = 0; i <= last; ++i) {
if (data[i] == target) {
return i; // 找到目标元素,返回索引
}
}
return -1; // 未找到目标元素
}
// 实现获取指定位置元素的函数
template <class T>
T SeqList<T>::get(int index) const {
if (index < 0 || index > last) {
std::cerr << "Error: Invalid index for retrieval." << std::endl;
exit(EXIT_FAILURE);
}
return data[index];
}
// 实现改变data数组空间大小的函数
template <class T>
void SeqList<T>::reSize(int newSize) {
if (newSize <= 0) {
std::cerr << "Error: Invalid size for resizing." << std::endl;
exit(EXIT_FAILURE);
}
T* newData = new T[newSize];
for (int i = 0; i <= last; ++i) {
newData[i] = data[i];
}
delete[] data;
data = newData;
maxSize = newSize;
}
// 实现打印顺序表中所有元素的函数
template <class T>
void SeqList<T>::display() const {
for (int i = 0; i <= last; ++i) {
std::cout << data[i] << " ";
}
std::cout << std::endl;
}
![image-20231111132544296](https://i-blog.csdnimg.cn/blog_migrate/d17dcb5183e36d39182fe1b93abbd829.png)
![image-20231111132709974](https://i-blog.csdnimg.cn/blog_migrate/63636eed6df0ec44e544e92f62e143af.png)
应用
template <class T>
//集合的并
void Union(SeqList<T>& LA, SeqList<T>& LB) {
int n = LA.getSize();
int m = LB.getSize();
int i, k, x;
for (i = 1; i <= m; ++i) {
LB.get(i, x);
k = LA.search(x);
if (k == -1) {
LA.insert(n, x);
++n;
}
}
}
template <class T>
//集合的交
void Intersection(SeqList<T>& LA, SeqList<T>& LB) {
int n = LA.getSize();
int i = 1, k, x;
while (i <= n) {
LA.get(i, x);
k = LB.search(x);
if (k == -1) {
LA.remove(i);
--n;
} else {
++i;
}
}
}
单链表
定义
单链表是一种基本的线性数据结构,它由节点(Node)构成,每个节点包含数据元素和一个指向下一个节点的指针。
template <class T>
struct Node {
T data; // 数据元素
Node<T>* next; // 指向下一个节点的指针
// 构造函数
Node(const T& newData, Node<T>* nextNode = nullptr)
: data(newData), next(nextNode) {}
};
template <class T>
class LinkedList {
private:
Node<T>* head; // 头指针,指向链表的第一个节点
int size; // 链表中的节点数量
public:
// 构造函数
LinkedList() : head(nullptr), size(0) {}
// 析构函数
~LinkedList();
// 获取链表的大小
int getSize() const;
// 检查链表是否为空
bool isEmpty() const;
// 在链表头部插入元素
void insertAtHead(const T& element);
// 在链表尾部插入元素
void insertAtTail(const T& element);
// 在指定位置插入元素
void insertAt(int index, const T& element);
// 删除链表头部的元素
void removeFromHead();
// 删除链表尾部的元素
void removeFromTail();
// 删除指定位置的元素
void removeFrom(int index);
// 获取指定位置的元素
T getAt(int index) const;
// 打印链表中的所有元素
void display() const;
};
单链表的主要特点包括:
单链表的特点是长度可以很方便地进行扩充。例如有一个连续的可用存储空间,指针free指示当前可用空间的开始地址。当链表要增加一个新的结点时,只要可用存储空间允许,就可以为链表分配一个结点空间,供链表使用。因此,线性表中数据元素的顺序与其链表表示中结点的物理顺序可能不一致,一般通过单链表的指针将各个数据元素按照线性表的逻辑顺序链接起来
![image-20231111144758114](https://i-blog.csdnimg.cn/blog_migrate/755f21d706995f5befa5672df2eafa3c.png)
- 动态内存分配: 单链表使用动态内存分配,节点在运行时动态创建,不需要预先分配固定的空间大小。
- 灵活插入和删除: 由于每个节点都包含指向下一个节点的指针,插入和删除元素变得非常灵活。在链表中间插入或删除一个元素的操作只涉及指针的修改,不需要移动大量元素。
- 不需要预先分配空间: 相比于数组,单链表不需要预先分配固定的存储空间,可以根据需要动态地分配和释放内存。
- 不连续的内存空间: 节点在内存中分布是不连续的,这与数组等连续存储结构不同。
- 插入和删除效率高: 在链表头部插入和删除操作的时间复杂度是 O(1),而在链表中间插入和删除的效率也相对较高。
- 顺序访问效率低: 与数组相比,链表的顺序访问效率较低。要访问链表的第
i
个元素,需要从头节点开始遍历直到第i
个节点。
单链表的插入以及删除算法
插入算法(Insert)
bool List::Insert(int i, int& x) {
// 将新元素x插入第i个结点之后。i从1开始,i=0表示插入第一个结点之前
if (first == NULL) {
// 插入空表或非空表第一个结点之前
LinkNode* newNode = new LinkNode(x);
// 建立一个新结点
if (newNode == NULL) {
cerr << "存储分配错误!\n";
exit(1);
}
newNode->link = first;
first = newNode;
} else {
LinkNode* current = first;
// 从第一个结点开始检测
for (int k = 1; k < i; ++k) {
// 遵循链指针所指链接方向(简称循链)找第i个结点
if (current == NULL) {
break;
} else {
current = current->link;
}
}
if (current == NULL) {
// 非空表且链太短
cerr << "无效的插入位置!\n";
return false;
} else {
// 插入链表的中间
LinkNode* newNode = new LinkNode(x);
// 建立一个新结点
if (newNode == NULL) {
cerr << "存储分配错误!\n";
exit(1);
}
newNode->link = current->link;
current->link = newNode;
}
}
return true;
}
删除算法(Remove)
bool List::Remove(int i, int& x) {
// 将链表中的第i个元素删除,i从1开始
LinkNode* del;
LinkNode* current;
if (i <= 1) {
del = first;
first = first->link;
} else {
// 删除第一个结点时重新拉链
current = first;
for (int k = 1; k < i - 1; ++k) {
// 循链找第i-1个结点
if (current == NULL) {
break;
} else {
current = current->link;
}
}
if (current == NULL || current->link == NULL) {
// 空表或者链太短
cerr << "无效的删除位置!\n";
return false;
}
del = current->link;
// 删除中间结点或尾结点时拉链
current->link = del->link;
}
x = del->data;
delete del;
// 取出被删结点中的数据值
return true;
}
这两个算法实现了单链表中元素的插入和删除操作。需要注意的是,这里使用了 LinkNode
结构来表示节点,其中包含了数据 data
和指向下一个节点的指针 link
。在插入和删除时,需要考虑头结点的特殊处理。
附加头节点的单链表
为了实现更方便的单链表操作,我们为每个单链表加入了一个附加头结点。该附加头结点位于链表第一个结点之前,且在链表存在的情况下必须至少有一个附加头结点。附加头结点的 data
域可以不存储实际信息,也可以存放特殊标志或表长等信息。
由于存在附加头结点,我们可以将其视为含有数据 data
的结点。在算法中查找第 i-1
个结点时,我们从 i=0
开始。如果 i
不超过表的长度加1,总能找到含 i-1
的结点,并让指针 current
指向它。这样,在非空表或空表的第一个结点之前插入一个新结点可以不作为特殊情况专门处理,与一般情况一样统一进行处理。
具体地,如果我们事先用一个指针 current
指向附加头结点,那么无论链表是否为空,插入新结点的操作都可以一致地进行。在非空表中,我们仍然从 i=0
开始查找到第 i-1
个结点,而在空表中,由于附加头结点的存在,我们同样可以将新结点插入到第 i-1
个位置,使得链表得到正确的更新。
图2.10(a)展示了在非空表中第一个结点之前插入一个新结点 newNode
的情形,而图2.10(b)展示了在空表中插入一个新结点 newNode
的情形。这种处理方式使得代码更加统一且易于理解。
![image-20231111145119043](https://i-blog.csdnimg.cn/blog_migrate/b1ddb58d07c3859a9c006c10cc0ef6aa.png)
这段代码实现了带附加头结点的单链表的类定义,包括结点的定义、链表的搜索、定位、插入和删除操作。以下是对代码的简要说明:
结点的定义
template <class T>
struct LinkNode {
T data; // 数据域
LinkNode<T>* link; // 指针域
// 仅初始化指针成员的构造函数
LinkNode(LinkNode<T>* ptr = NULL) : link(ptr) {}
// 初始化数据与指针成员的构造函数
LinkNode(const T& item, LinkNode<T>* ptr = NULL) : data(item), link(ptr) {}
};
带附加头结点的单链表的类定义
template <class T>
class List {
private:
LinkNode<T>* first; // 头指针,指向链表的附加头结点
public:
// 构造函数
List() : first(new LinkNode<T>) {}
// 析构函数
~List();
// 在表中搜索含数据x的结点,搜索成功时返回该结点地址;否则返回NULL值
LinkNode<T>* Search(T x);
// 定位函数,返回表中第i个元素的地址。若i<0或i超出表中结点个数,则返回NULL
LinkNode<T>* Locate(int i);
// 将新元素x插入链表中第i个结点之后
bool Insert(int i, T& x);
// 将链表中的第i个元素删除,通过引用型参数x返回该元素的值
bool Remove(int i, T& x);
};
在表中搜索含数据x的结点
template <class T>
LinkNode<T>* List<T>::Search(T x) {
LinkNode<T>* current = first->link;
while (current != NULL) {
if (current->data == x) break;
else current = current->link;
}
return current;
}
定位函数,返回表中第i个元素的地址
template <class T>
LinkNode<T>* List<T>::Locate(int i) {
if (i < 0) return NULL; // i不合理
LinkNode<T>* current = first;
int k = 0;
while (current != NULL && k < i) {
current = current->link;
++k;
}
return current; // 返回第i个结点地址,若返回NULL,表示i值太大
}
将新元素x插入链表中第i个结点之后
template <class T>
bool List<T>::Insert(int i, T& x) {
LinkNode<T>* current = Locate(i);
if (current == NULL) return false; // 插入不成功
LinkNode<T>* newNode = new LinkNode<T>(x);
if (newNode == NULL) {
cerr << "存储分配错误!" << endl;
exit(1);
}
newNode->link = current->link; // 链接在current之后
current->link = newNode;
return true; // 插入成功
}
将链表中的第i个元素删除,通过引用型参数x返回该元素的值
template <class T>
bool List<T>::Remove(int i, T& x) {
LinkNode<T>* current = Locate(i - 1);
if (current == NULL || current->link == NULL) return false; // 删除不成功
LinkNode<T>* del = current->link; // 重新拉链,将被删结点从链中摘下
current->link = del->link;
x = del->data; // 取出被删结点中的数据值
delete del;
return true;
}
这段代码实现了带附加头结点的单链表的基本操作。附加头结点的存在使得对于头结点之前的插入操作变得更为统一。
//不适用额外的数组或者节点,点到链表
#include <iostream>
template <class T>
struct Node {
T data;
Node<T>* next;
Node(const T& newData, Node<T>* nextNode = nullptr)
: data(newData), next(nextNode) {}
};
template <class T>
class LinkedList {
private:
Node<T>* head;
public:
LinkedList() : head(nullptr) {}
// 在链表头部插入元素
void insertAtHead(const T& element) {
Node<T>* newNode = new Node<T>(element, head);
head = newNode;
}
// 输出链表元素
void display() const {
Node<T>* current = head;
while (current != nullptr) {
std::cout << current->data << " ";
current = current->next;
}
std::cout << std::endl;
}
// 颠倒链表
void reverse() {
Node<T>* prev = nullptr;
Node<T>* current = head;
Node<T>* next = nullptr;
while (current != nullptr) {
next = current->next; // 保存下一个节点
current->next = prev; // 当前节点指向前一个节点,颠倒方向
prev = current; // 移动prev指针
current = next; // 移动current指针
}
head = prev; // 更新头指针
}
};
循环单链表
表为指针的link域是表头指针,只要知道任何一个节点的地址就可以遍历表中任何一个节点
循环链表为不指针的判断条件不是current->link == NULL;而是current->link == first
这是一个循环单链表的类定义,包括结点的定义和链表类的基本操作。以下是对代码的简要说明:
结点的定义
template <class T>
struct CircLinkNode {
T data; // 数据域
CircLinkNode<T>* link; // 指针域
// 构造函数,初始化指针成员
CircLinkNode(CircLinkNode<T>* next = NULL) : link(next) {}
// 初始化数据与指针成员的构造函数
CircLinkNode(T d, CircLinkNode<T>* next = NULL) : data(d), link(next) {}
};
循环单链表的类定义
template <class T>
class CircList {
public:
// 构造函数
CircList(const T& x);
// 复制构造函数
CircList(CircList<T>& L);
// 析构函数
~CircList();
// 计算循环单链表长度
int Length() const;
// 判断表空否
bool IsEmpty() { return first->link == first; }
// 判断p是否为头结点
bool IsHead(CircLinkNode<T> p) { return p == first; }
// 返回附加头结点地址
CircLinkNode<T>* getHead() { return first; }
// 返回p的下一结点地址
CircLinkNode<T>* getNext(CircLinkNode<T>* p) { return p->link; }
// 设置附加头结点地址
void setHead(CircLinkNode<T>* p) { first = p; }
// 搜索含数据x的元素
CircLinkNode<T>* Search(T x);
// 搜索第i个元素的地址
CircLinkNode<T>* Locate(int i);
// 在第i个元素后插入x
bool Insert(int i, T& x);
// 删除第i个元素,x返回该元素的值
bool Remove(int i, T& x);
// 按尾插法输入并创建表
void input();
// 输出
void output();
private:
CircLinkNode<T>* first, *rear; // 头指针,尾指针
};
这个类包括了循环单链表的基本操作,例如搜索、定位、插入和删除元素,以及输入和输出功能。请注意,CircList
类中使用了头指针 first
和尾指针 rear
,构成了循环单链表。
#include <iostream>
template <class T>
struct CircLinkNode {
T data;
CircLinkNode<T>* link;
CircLinkNode(const T& newData, CircLinkNode<T>* next = nullptr)
: data(newData), link(next) {}
};
template <class T>
class CircList {
private:
CircLinkNode<T>* first;
public:
CircList() : first(nullptr) {}
// 在链表中从头搜索其数据值为x的结点
CircLinkNode<T>* Search(T x) {
CircLinkNode<T>* current = first->link;
while (current != first && current->data != x) {
current = current->link;
}
return (current != first && current->data == x) ? current : nullptr;
}
};
int main() {
CircList<int> myList;
// 假设有一个带头节点的循环单链表
CircLinkNode<int>* node1 = new CircLinkNode<int>(1);
CircLinkNode<int>* node2 = new CircLinkNode<int>(2);
CircLinkNode<int>* node3 = new CircLinkNode<int>(3);
myList.first = new CircLinkNode<int>(); // 头节点
mylist.first->link = node1;
node1->link = node2;
node2->link = node3;
node3->link = nmy.list.first;
// 搜索数据值为2的结点
CircLinkNode<int>* result = myList.Search(2);
if (result != nullptr) {
std::cout << "Found: " << result->data << std::endl;
} else {
std::cout << "Not Found." << std::endl;
}
// 释放内存
delete myList.first;
delete node1;
delete node2;
delete node3;
return 0;
}
约瑟夫问题是一个经典的数学和计算机科学问题,通常描述为:N个人(编号从1到N)围坐在一起,从第一个人开始报数,报到M的人出列,剩下的人继续从1开始报数。循环进行,直到所有人都出列。现在,您可以使用循环单链表来模拟这个过程。
下面是使用循环单链表解决约瑟夫问题的简单C++实现:
#include <iostream>
template <class T>
struct CircLinkNode {
T data;
CircLinkNode<T>* link;
CircLinkNode(const T& newData, CircLinkNode<T>* next = nullptr)
: data(newData), link(next) {}
};
template <class T>
class CircList {
private:
CircLinkNode<T>* first;
public:
CircList() : first(nullptr) {}
// 在链表中从头搜索其数据值为x的结点
CircLinkNode<T>* Search(T x) {
CircLinkNode<T>* current = first->link;
while (current != first && current->data != x) {
current = current->link;
}
return (current != first && current->data == x) ? current : nullptr;
}
// 在链表尾部插入新节点
void insertAtTail(const T& element) {
CircLinkNode<T>* newNode = new CircLinkNode<T>(element);
if (first == nullptr) {
first = newNode;
first->link = first; // 链接到自身形成循环
} else {
CircLinkNode<T>* last = first;
while (last->link != first) {
last = last->link;
}
last->link = newNode;
newNode->link = first; // 链接到头节点形成循环
}
}
// 解决约瑟夫问题
void josephus(int M) {
if (first == nullptr) {
std::cout << "List is empty." << std::endl;
return;
}
CircLinkNode<T>* current = first;
CircLinkNode<T>* prev = nullptr;
while (current->link != current) {
// 移动到第M个节点
for (int i = 1; i < M; ++i) {
prev = current;
current = current->link;
}
// 删除当前节点
if (prev != nullptr) {
prev->link = current->link;
} else {
first = current->link;
}
std::cout << "Person " << current->data << " is eliminated." << std::endl;
delete current;
current = prev->link; // 移动到下一个节点
}
// 最后剩下的节点即为胜利者
std::cout << "Person " << first->data << " is the winner!" << std::endl;
// 释放头节点内存
delete first;
first = nullptr;
}
};
int main() {
CircList<int> myList;
// 假设有5个人围坐在一起
for (int i = 1; i <= 5; ++i) {
myList.insertAtTail(i);
}
// 解决约瑟夫问题,报数到3
myList.josephus(3);
return 0;
}
这个示例中,insertAtTail
方法用于在链表尾部插入新节点,josephus
方法用于解决约瑟夫问题。在这个例子中,有5个人,报数到3。程序模拟了约瑟夫问题的解决过程,并输出每次出列的人。最后,输出获胜者。
双向链表
双向链表又称双链表。使用双向链表(doubly linked list)的目的是解决在链表中访问直接前驱和直接后继的问题。因为在双向链表中每个结点都有两个链指针,一个指向结点的直接前驱,另一个指向结点的直接后继,这样不论是向前驱方向搜索还是向后继方向搜
索,其时间开销都只有O(1)。
#include <iostream>
template <class T>
struct DblNode {
T data; // 链表结点数据
DblNode<T>* lLink; // 链表前驱(左链)指针
DblNode<T>* rLink; // 链表后继(右链)指针
// 构造函数
DblNode(DblNode<T>* left = nullptr, DblNode<T>* right = nullptr)
: lLink(left), rLink(right) {}
// 构造函数
DblNode(T value, DblNode<T>* left = nullptr, DblNode<T>* right = nullptr)
: data(value), lLink(left), rLink(right) {}
};
template <class T>
class DblList {
public:
// 构造函数:建立附加头结点
DblList() {
first = new DblNode<T>();
first->lLink = first->rLink = first;
}
// 析构函数:释放所用存储
~DblList() {
while (!IsEmpty()) {
T x;
Remove(0, x, 0); // 删除头结点后继方向的结点,直到链表为空
}
delete first; // 释放头结点
}
// 计算双链表的长度
int Length() {
int len = 0;
DblNode<T>* current = first->rLink;
while (current != first) {
len++;
current = current->rLink;
}
return len;
}
// 判断双链表空否
bool IsEmpty() {
return first->rLink == first;
}
// 取附加头结点地址
DblNode<T>* getHead() {
return first;
}
// 取p的后继方向下一结点地址
DblNode<T>* getNext(DblNode<T>* p) {
return p->rLink;
}
// 取p的前驱方向下一结点地址
DblNode<T>* getPrior(DblNode<T>* p) {
return p->lLink;
}
// 在链表中沿后继方向寻找等于给定值x的结点
DblNode<T>* Search(T& x) {
DblNode<T>* current = first->rLink;
while (current != first && current->data != x) {
current = current->rLink;
}
return (current != first && current->data == x) ? current : nullptr;
}
// 在链表中定位序号为i(≥0)的结点,d=0按前驱方向,d≠0按后继方向
DblNode<T>* Locate(int i, int d) {
if (i < 0) {
return nullptr; // i不合理
}
DblNode<T>* current = first;
int count = 0;
if (d == 0) {
while (count <= i && current != first) {
current = current->lLink;
count++;
}
} else {
while (count <= i && current != first) {
current = current->rLink;
count++;
}
}
return (count > i && current != first) ? current : nullptr;
}
// 在第i个结点后插入一个含值x的新结点,d=0按前驱方向,d≠0按后继方向
bool Insert(int i, T x, int d) {
DblNode<T>* current = Locate(i, d);
if (current == nullptr) {
return false; // 插入不成功
}
DblNode<T>* newNode = new DblNode<T>(x);
newNode->lLink = current;
newNode->rLink = current->rLink;
current->rLink->lLink = newNode;
current->rLink = newNode;
return true; // 插入成功
}
// 删除第i个结点,x返回其值,d=0按前驱方向,d≠0按后继方向
bool Remove(int i, T& x, int d) {
DblNode<T>* current = Locate(i, d);
if (current == nullptr
静态链表
静态链表(Static Linked List)是一种使用数组实现的链表。以下是一个简单的静态链表的类定义,其中使用数组存储结点:
#include <iostream>
const int MAX_SIZE = 100; // 定义静态链表的最大容量
template <class T>
struct StaticNode {
T data;
int next; // 下一个结点的索引
StaticNode() : next(-1) {} // 初始值表示空结点
};
template <class T>
class StaticLinkedList {
public:
StaticLinkedList();
~StaticLinkedList();
bool IsEmpty() const;
bool IsFull() const;
int Length() const;
bool Insert(int i, const T& x);
bool Remove(int i, T& x);
void Display() const;
private:
StaticNode<T> list[MAX_SIZE]; // 静态链表的数组
int head; // 头结点索引
int free; // 空闲结点索引
int GetFreeNode(); // 获取一个空闲结点
};
template <class T>
StaticLinkedList<T>::StaticLinkedList() {
head = -1;
free = 0;
// 初始化空闲结点链表
for (int i = 0; i < MAX_SIZE - 1; ++i) {
list[i].next = i + 1;
}
list[MAX_SIZE - 1].next = -1; // 最后一个结点表示链表结束
}
template <class T>
StaticLinkedList<T>::~StaticLinkedList() {
// 在析构函数中并没有实际释放内存,因为静态链表使用的是静态数组,而不是动态分配的内存
}
template <class T>
bool StaticLinkedList<T>::IsEmpty() const {
return head == -1;
}
template <class T>
bool StaticLinkedList<T>::IsFull() const {
return free == -1;
}
template <class T>
int StaticLinkedList<T>::Length() const {
int count = 0;
int current = head;
while (current != -1) {
++count;
current = list[current].next;
}
return count;
}
template <class T>
bool StaticLinkedList<T>::Insert(int i, const T& x) {
if (i < 0 || i > Length() || IsFull()) {
return false; // 插入位置不合法或链表已满
}
int newNodeIndex = GetFreeNode();
list[newNodeIndex].data = x;
if (i == 0) {
list[newNodeIndex].next = head;
head = newNodeIndex;
} else {
int current = head;
for (int j = 0; j < i - 1; ++j) {
current = list[current].next;
}
list[newNodeIndex].next = list[current].next;
list[current].next = newNodeIndex;
}
return true; // 插入成功
}
template <class T>
bool StaticLinkedList<T>::Remove(int i, T& x) {
if (i < 0 || i >= Length() || IsEmpty()) {
return false; // 删除位置不合法或链表为空
}
int removedNodeIndex;
if (i == 0) {
removedNodeIndex = head;
head = list[head].next;
} else {
int current = head;
for (int j = 0; j < i - 1; ++j) {
current = list[current].next;
}
removedNodeIndex = list[current].next;
list[current].next = list[removedNodeIndex].next;
}
x = list[removedNodeIndex].data;
// 将删除的结点添加到空闲结点链表头部
list[removedNodeIndex].next = free;
free = removedNodeIndex;
return true; // 删除成功
}
template <class T>
void StaticLinkedList<T>::Display() const {
int current = head;
while (current != -1) {
std::cout << list[current].data << " ";
current = list[current].next;
}
std::cout << std::endl;
}
template <class T>
int StaticLinkedList<T>::GetFreeNode() {
if (free == -1) {
return -1; // 没有空闲结点可用
}
int freeNodeIndex = free;
free = list[free].next; // 更新空闲结点链表头部
return freeNodeIndex;
}
int main() {
StaticLinkedList<int> myStaticList;
// 插入元素
myStaticList.Insert(0, 1);
myStaticList.Insert(1, 2);
myStaticList.Insert(2, 3);
myStaticList.Insert(0, 0);
// 输出链表
std::cout << "Static Linked List: ";
myStaticList.Display();
// 删除元素
int removedElement;
myStaticList.Remove(2, removedElement);
// 输出删除后的链表
std::cout <<
"List after removal: ";
myStaticList.Display();
return 0;
}
这是一个简单的静态链表的实现,使用数组作为存储结点的数据结构。请注意,这里的 GetFreeNode
函数负责从空闲结点链表中获取一个可用的空闲结点。