1.从实现的角度看,链表可分为动态链表和静态链表;从连接方式的角度看,链表可分为单向链表、循环链表和双向链表。
2.单向链表的结点结构
(1)数据域data
(2)指针域link
3.我们通常用头指针来标识一个单向链表。头指针为NULL则表示一个空链表。
4.一个链表包含了零个、一个或多个结点,因此一个LinkedList的对象包含有零个、一个或多个ListNode的对象。这种关系在面向对象中叫聚合关系。可以用复合类来表达这种关系:利用在ListNode类中声明友元类的方法,让ListNode类和LinkedList类的成员都能访问ListNode类的私有成员。
5.单向链表的类定义
//文件node.h,结点类ListNode
#ifndef ListNode_
#define ListNode_
template <class T> class LinkedList; //前视定义,否则友元无法定义
template <class T> //模板类型为T
class ListNode
{
friend class LinkedList<T>; //定义类LinkedList<T>为友元
private:
ListNode<T> *link; //指向下一结点的指针
T data; //定义为私有成员
public:
//构造函数1,用于构造头结点
ListNode(ListNode<T> *ptrlink = NULL)
{ link = ptrlink; }
//构造函数2,用于构造其他结点
ListNode(const T& item, ListNode<T> *ptrlink = NULL)
{ data = item; link = ptrlink; }
~ListNode(void){} //析构函数
//以item和next建立一个新结点
ListNode<T> * getNode(const T& item, ListNode<T> *next = NULL)
ListNode<T> * getLink() { return link; } //取得结点的下一结点地址
T getData() { return data; } //取得结点中的数据
void setLink(ListNode<T> * next) { link = next; } //修改结点的link域
void setData(T value) { data = value; } //修改结点的data值
};
#endif
//文件LinkedList.h,链表的类定义
#ifndef LinkedList_
#define LinkedList_
#include <iostream>
#include "node.h"
template<class T>
class LinkedList
{
public:
LinkedList() { first = 0; }
~LinkedList();
bool IsEmpty() const { return first = = 0; }
int Length() const;
bool Find(int k, T& x) const;
int Search(const T& x) const;
ListNode<T> *First() { return first; }
LinkedList<T>& Delete(int k, T& x);
LinkedList<T>& Insert(int k, const T& x);
void Output(ostream& out) const;
private:
ListNode<T> *first; //头指针
};
...
#endif
单向链表上基本操作的实现
1.建立单向链表
(1)头插法
//创建一个单向链表, finished是停止建表输入标志, 是所有输入值中不可能出现的数值
template <class Type>
LinkedList <Type> :: LinkedList()
{
ListNode<Type> *p;
first = NULL; //创建空表
Type value;
cin >> value;
while (value != finished)
{
p= new ListNode<Type>(value, first);
first = p;
cin >> value;
}
}
//创建一个单向链表, finished是停止建表输入标志, 是所有输入值中不可能出现的数值
template <class Type>
LinkedList <Type>::LinkedList()
{
ListNode<Type> *p, *rear;
Type value;
first = NULL;
rear = NULL;
cin >> value;
while (value != finished)
{
p= new ListNode<Type>(value, NULL);
if (first == NULL) first = p; //第一个结点的处理
else rear->link = p; //其他结点的处理
rear = p; //r指向新的尾结点
cin >> value;
}
if (rear != NULL) rear->link = NULL; //对非空表, 最后结点的指针域放空指针
}
为解决第一个结点的问题,增加一个头结点。
2.求表长
//返回链表中元素的个数
//移动指针current和计数器len
template<class T>
int LinkedList<T>::Length() const
{
ListNode<T> *current = first;
int len = 0;
while (current)
{
len++;
current = current->link;
}
return len;
}
时间复杂度为O(n)
3.查找
(1)按序号查找
//查找第k个元素用x返回, 查找成功返回true, 否则返回false
template<class T>
bool LinkedList<T>::Find(int k, T& x) const
{
if (k < 1) return false;
ListNode<T> *current = first;
int index = 1; //当前索引
while (index < k && current)
{
current = current->link;
index++;
}
if (current)
{
x = current->data;
return true;
}
return false; //没有第k个元素
}
(2)按值查找,即定位
//如果找到 x, 返回其位序, 否则返回0
template<class T>
int LinkedList<T>::Search(const T& x) const
{
ListNode<T> *current = first;
int index = 1; //当前索引
while (current && current->data != x)
{
current = current->link;
index++;
}
if (current) return index;
return 0;
}
4.插入
(1)后插结点
将*s插入到*current的后面
s->link=current->link;
current->link=s;
(2)前插结点
将*s插入到*current的前面
q=first;
while(q->link!=current)
q=q->link; //找*current的直接前驱
s->link=q->link;
q->link=s;
后插操作的时间复杂度是O(1),前插操作的复杂度是O(n)。也可将*s插入到*current的后面,然后将current->data与s->data交换,时间复杂度降为O(1)。
//在第k个元素后插入一个元素值为x的结点
template<class T>
LinkedList<T>& LinkedList<T>::Insert(int k, const T& x)
{
if (k < 0) throw OutOfBounds();
//查找第k个结点, 用p指向它
ListNode<T> *p = first;
for (int index = 1; index < k && p; index++)
p = p->link;
if (k > 0 && !p) throw OutOfBounds(); //没有第k个结点
//插入
ListNode<T> *s = new ListNode<T>;
s->data = x;
if (k)
{ //插在*p后面
s->link = p->link;
p->link = s;
}
else
{ //如果k==0, 插入作为第一个元素
s->link = first;
first = s;
}
return *this;
}
时间复杂度是O(n)
5.删除
(1)删除*current
找到前驱*current的前驱结点*q
q->link=current->link;
delete current;
//时间复杂度O(n)
(2)删除*current的后继结点(假设存在)
s=current->link;
current->link=s->link;
delete s;
//时间复杂度O(1)
删除操作的具体算法
//删除第k个元素, 值用x返回
template<class T>
LinkedList<T>& LinkedList<T>::Delete(int k, T& x)
{
if (k < 1 || !first) throw OutOfBounds(); //没有第k个元素
//查找第k个结点, 用p指向它
ListNode<T> *p = first;
if (k == 1) //p已经是第k个元素
first = first->link; //从链中删除
else
{ //用q记录第k-1元素
ListNode<T> *q = first;
for (int index = 1; index < k - 1 && q; index++)
q = q->link;
if (!q || !q->link) throw OutOfBounds(); //没有第k个元素
p = q->link; //记录被删结点
q->link = p->link;
} //从链中删除
//保存第k个元素并且回收结点
x = p->data;
delete p;
return *this;
}
时间复杂度为O(n)