什么是链表?
链表,顾名思义,是一条相互链接的数据节点表。每个节点由两部分组成:数据和指向下一个节点的指针。链表的基本结构如下图所示:
一般来说,链表的头结点不存放具体的数据,所以也被称为哑节点(dummy node)。原因在于这样可以比较好地区分链表的头结点,而且可以大大简化链表的各种操作,避免很多不必要的边界讨论。
链表的种类
单向链表 双向链表 循环链表 多向表(网状表)
链表的优缺点
这里的优缺点主要是和链表对应的另一个数据结构:数组相比得出的。
链表的优点在于:
1.物理存储单元上非连续,而且采用动态内存分配,能够有效的分配和利用内存资源;
2.节点删除和插入简单,不需要内存空间的重组。
作为一种数据结构,链表必然也有让人诟病的地方。链表的缺点在于:
1.不能进行索引访问,只能从头结点开始顺序查找;
2.数据结构较为复杂,需要大量的指针操作,容易出错。
单向链表的模板类实现
笔者实现了一个单向链表模板类,支持了绝大部分链表操作。
(笔者坚信好的代码不必写过多的注释,代码本身即具有良好的可阅读性)
首先需要定义链表节点类:
template <class T>
class Node
{
public:
Node();
~Node();
T val;
Node *next;
};
链表类的定义如下:
template <class T>
class List
{
public:
List();
~List();
void isEmpty();
void isHead(Node<T> *node);
void isTail(Node<T> *node);
Node<T> *getHead();
Node<T> *getTail();
Node<T> *find(T val);
Node<T> *findPrevious(T val);
void insert(Node<T> *node, T val);
void append(T val);
void remove(T val);
void reverse();
int size();
void clear();
void print();
private:
Node<T> *_head;
};
下面是具体的链表操作实现:
1.链表是否为空
template <class T>
void List<T>::isEmpty()
{
return _head->next == NULL;
}
2.判断节点是否为头结点
template <class T>
void List<T>::isHead(Node<T> *node)
{
return node == _head;
}
3.判断节点是否为尾节点
template <class T>
void List<T>::isTail(Node<T> *node)
{
return node->next == NULL;
}
4.获取链表的头结点
template <class T>
Node<T> *List<T>::getHead()
{
return _head;
}
5.获取节点的尾节点
template <class T>
Node<T> *List<T>::getTail()
{
Node<T> *tmpNode = _head;
while(tmpNode->next)
{
tmpNode = tmpNode->next;
}
return tmpNode;
}
6.查找节点
template <class T>
Node<T> *List<T>::find(T val)
{
Node<T> *tmpNode = _head->next;
while(tmpNode)
{
if (tmpNode->val == val)
{
return tmpNode;
}
tmpNode = tmpNode->next;
}
return NULL;
}
7.查找上一个节点
template <class T>
Node<T> *List<T>::findPrevious(T val)
{
Node<T> *tmpNode = _head;
while(tmpNode->next)
{
if (tmpNode->next->val == val)
{
return tmpNode;
}
tmpNode = tmpNode->next;
}
return NULL;
}
8.在指定节点之后插入新节点
template <class T>
void List<T>::insert(Node<T> *node, T val)
{
Node<T> *tmpNode = _head->next;
while(tmpNode)
{
if (tmpNode == node)
{
Node<T> *newNode = new Node<T>();
newNode->val = val;
newNode->next = tmpNode->next;
tmpNode->next = newNode;
return;
}
tmpNode = tmpNode->next;
}
}
9.在链表尾部插入节点
template <class T>
void List<T>::append(T val)
{
Node<T> *newNode = new Node<T>();
newNode->val = val;
Node<T> *tail = getTail();
tail->next = newNode;
}
10.删除节点
template <class T>
void List<T>::remove(T val)
{
Node<T> *prevNode = findPrevious(val);
if (prevNode)
{
Node<T> *tmpNode = prevNode->next;
prevNode->next = prevNode->next->next;
delete tmpNode;
}
}
11.反转链表
template <class T>
void List<T>::reverse()
{
Node<T> *tmpNode = _head->next;
Node<T> *prevNode = NULL;
while(tmpNode)
{
Node<T> *nextNode = tmpNode->next;
tmpNode->next = prevNode;
prevNode = tmpNode;
tmpNode = nextNode;
}
_head->next = prevNode;
}
12.获取链表的节点数
template <class T>
int List<T>::size()
{
Node<T> *tmpNode = _head->next;
int n = 0;
while(tmpNode)
{
tmpNode = tmpNode->next;
n++;
}
return n;
}
13.删除整个链表
template <class T>
void List<T>::clear()
{
Node<T> *tmpNode = _head->next;
while(tmpNode)
{
Node<T> *node = tmpNode;
tmpNode = tmpNode->next;
delete node;
}
_head->next = NULL;
}
14.打印链表的所有节点
template <class T>
void List<T>::print()
{
Node<T> *tmpNode = _head->next;
while(tmpNode)
{
std::cout << tmpNode->val;//类型T需要重载输出流操作符
<<
tmpNode = tmpNode->next;
if (tmpNode)
{
std::cout << " ";
}
}
std::cout << std::endl;
}