前言
在学习了C++基础以及算法复杂度分析方法(NP完整性笔者也没有理清,待到吃透再更)后,我们开始学习第一种数据结构——链表。在学习链表之前,相信熟练使用C语言的各位已经掌握了数组这一数据结构 。其有两个局限:
- 编译前就需要知道大小。
- 数组中的每个元素占据的内存单元数是相同的。
链表就不存在这些问题,所以它可以分散存储于内存的任何位置,对于双向链表,每一个节点都存有两个地址,分别存放上一个节点和下一个节点的地址。链表的实现方法很多,指针的灵活运用是最为常用的做法。
单向链表
一言以蔽之,单向链表包含两个数据成员:info,next。info用于存储用户需要的数据,next存储维持链表结构的数据成员。标准代码如下:
class IntSLLNode
{
public:
IntSLLNode()
{
next = NULL;}
IntSLLNode(int i, IntSLLNode *in = NULL)
{
info = i;
next = in;
}
int info; //数据成员-存储用户信息。
IntSLLNode *next; //数据成员-存储下一节点指针。
}
这里的构造函数分别实现了用户没有指定参数,以及指定了数据成员而缺省下一节点指针信息与用户指定全部信息的情形。
常见的声明方式为:
IntSLLNode *p = new IntSLLNode(10); //创建头节点。
p->next = new IntSLLNode(8); //创建下一节点。
大家很容易分析上面的代码,如果继续创建,就需要重复写入p->next->next
费时费力,所以需要其他方式访问链表中的节点(关于是否是尾节点,可以通过next是否为NULL来判断)。方法之一是保存头节点和尾节点的地址。(也可以通过添加指向上一节点和下一节点的指针来维护链表结构,但此处只介绍单向链表,故稍后讲解。)
class IntSLLNode {
public:
IntSLLNode() {
next = NULL;
}
IntSLLNode(int el, IntSLLNode *ptr = NULL) {
info = el; next = ptr;
}
int info;
IntSLLNode *next;
};
class IntSLList {
public:
IntSLList() {
head = tail = NULL;
}
~IntSLList();
int isEmpty() {
return head == NULL;
}
void addToHead(int); //从头部添加节点。
void addToTail(int); //从尾部添加节点。
int deleteFromHead(); // 删除头节点。
int deleteFromTail(); // 删除尾节点。
void deleteNode(int); //删除某一节点。
bool isInList(int) const; //查询是否存在节点。
void printAll() const; //打印。
private:
IntSLLNode *head, *tail; //注意:头节点和尾节点信息也由该结构维护。
}; //特地声明一个类来对链表进行维护。
相关的.h文件可以将其声明为如上形式,对于相关的函数代码可以在接下来的.c文件中实现:
//以下实现代码摘自C++数据结构与算法第四版。注意类的声明尽可能参照上面的标准,链表维护实现方式可以略有区别。
IntSLList::~IntSLList() {
for (IntSLLNode *p; !isEmpty(); ) {
p = head->next;
delete head;
head = p;
}
}
void IntSLList::addToHead(int el) {
head = new IntSLLNode(el,head);
if (tail == NULL)
tail = head;
}
void IntSLList::addToTail(int el) {
if (tail != NULL) {
// if list not empty;
tail->next = new IntSLLNode(el);
tail = tail->next;
}
else head = tail = new IntSLLNode(el);
}
int IntSLList::deleteFromHead() {
int el = head->info;
IntSLLNode *tmp = head;
if (head == tail) // if only one node on the list;
head = tail = NULL;
else head = head->next;
delete tmp;
return el;
}
int IntSLList::deleteFromTail() {
int el = tail->info;
if (head == tail) {
// if only one node on the list;
delete head;
head = tail = NULL;
}
else {
// if more than one node in the list,
IntSLLNode *tmp; // find the predecessor of tail;
for (tmp = head; tmp->next != tail; tmp = tmp->next);
delete tail;
tail