In computer science, a linked list is a data structure consisting of a group of nodes which together represent a sequence. Under the simplest form, each node is composed of a data and areference (in other words, a link) to the next node in the sequence; more complex variants add additional links. This structure allows for efficient insertion or removal of elements from any position in the sequence.
摘自——维基百科Linked list.除此之外,维基还给了我们这样一张图:
不同于数组开辟连续的内存空间,链表是一种非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。所以我们得出链表很重要的两个组成部分:
- 指针
- 节点
typedef struct Node
{
int data;
struct Node *pNext;
}NODE,*PNODE; //NODE等价于struct Node, PNODE等价于struct Node *
data可以理解为图中的12,99,37这些存储的数据。
pNext可以理解为图中的箭头,作用是指向下一个节点。
同理,我还是只用c++实现,c语言实现的方法网上一大堆(咦?什么网上不是一大堆?)。
需要说明的是,wiki上说的链表是LinkedList,而代码确实在我wiki之前写的,那时候对于链表的名称我用的是Chain,现在为了更好的理解看了一下wiki,就决定用他的表示方法比较好,不过代码慢慢改也不容易。
还有一点,代码中的迭代器并没有加入友类中,所以实际上是不能调用的,但是写在那里更一种新的思路,即当Output成员并不存在时,可以用迭代器来输出链表,效率更高。为什么?
#ifndef CHAIN_H
#define CHAIN_H
#include "xcept.h"
template<class T> class Chain;
template<class T>
class ChainNode{
friend class Chain<T>;//Chain<T>是ChainNode的友类,以便可以访问ChainNode<T>的所有成员
private:
T data;
ChainNode<T> *link;
};
template<class T>
class Chain{
public:
Chain(){first=0;}
~Chain();
bool IsEmpty() const {return first==0;}
int Length() const;
bool Find(int k,T& x) const;
int Search(const T& x) const;
Chain<T>& Delete(int k,T& x);
Chain<T>& Insert(int k,const T& x);
void Erase();
Chain<T>& Append(const T& x);
void Output(ostream& out) const;
private:
ChainNode<T> *first;//指向第一个节点的指针
};
template<class T>
Chain<T>::~Chain(){
//链表的析构函数,用于删除链表中的所有节点
ChainNode<T> *next;//指向下一个节点
while (first){
next = first->link;
delete first;
first = next;
}
}
template<class T>
int Chain<T>::Length() const{
//确定链表长度
ChainNode<T> *current = first;
int len = 0;
while (current){
len++;
current = current->link;
}
return len;
}
template<class T>
bool Chain<T>::Find(int k, T& x) const{
//寻找链表中的第k个元素,并将其传送至x
//如果不存在第k个元素,则返回false,否则返回true
if (k < 1) return false;
ChainNode<T> *current = first;
int index = 1;///current的索引
while (index < k && current){
current = current->link;
index++;
}
if (current){
x = current->data;
return true;
}
return false;
}
template<class T>
int Chain<T>::Search(const T& x) const{
//寻找x,如果发现x,则返回x的地址
//如果x不存在,则返回0
ChainNode<T> *current = first;
int index = 1;
while (current&¤t->data != x){
current = current->link;
index++;
}
if (current) return index;
return 0;
}
template<class T>
void Chain<T>::Output(ostream& out) const{
//将链表元素送至输出流
ChainNode<T> *current;
for (current = first; current; current = current->link)
out << current->data << " ";
}
//重载<<操作符
template<class T>
ostream& operator<<(ostream& out, const Chain<T>& x){
x.Output(out);
return out;
}
template<class T>
Chain<T>& Chain<T>::Delete(int k, T& x){
//把第k个元素取至x,然后从链表中删除第k个元素
//如果不存在第k个元素,则引发异常OutOfBounds
if (k < 1 || !first)
throw OutOfBounds();//k<1或链表为空,抛出异常
//定义指针p,p最终将指向第k个节点
ChainNode<T> *p = first;
if (k == 1)// 删除的是首元素
first = first->link;
else{
ChainNode<T> *q = first;//用q指向第k-1个元素
for (int index = 1; index < k - 1 && q; index++)
q = q->link;
if (!q||!q->link)
throw OutOfBounds();//如果不存在第k个元素
p = q->link;//存在第k个元素
q->link = p->link;//从链表中删除该元素
}
x = p->data;//保存第k个元素并释放节点p
delete p;
return *this;
}
template<class T>
Chain<T>& Chain<T>::Insert(int k, const T& x){
//在第k个元素之后插入x
//如果不存在第k个元素,这引发异常OutOfBounds
//如果没有足够的空间,这传递NoMem异常
if (k < 0) throw OutOfBounds();
//定义指针p,p最终将指向第k个节点
ChainNode<T> *p = first;
//将p移动至第k个元素
for (int index = 1; index < k&&p; index++)
p = p->link;
if (k>0 && !p) throw OutOfBounds();//不存在第k个元素
//插入x
ChainNode<T> *y = new ChainNode<T>;
y->data = x;
if (k){
y->link = p->link;
p->link = y;
}
else{
y->link = first;//作为第一个元素插入
first = y;
}
return *this;
}
//扩充类Chain
template<class T>
void Chain<T>::Erase(){
//删除链表中的所有节点
ChainNode<T> *next;
while (first){
next = first->next;
delete first;
first = next;
}
}
template<class T>
Chain<T>& Chain<T>::Append(const T& x){
//在链表尾部添加元素x
ChainNode<T> *y=new ChainNode<T>;
ChainNode<T> *temp=first;
y->data = x;
y->link = 0;
if (first==0)
{
first=y;
}
else
{
while(temp->link!=0){
temp=temp->link;
}
temp->link=y;
}
return *this;
}
template<class T>
class ChainIterator{
//链表遍历器
public:
T* Initialize(const Chain<T>& c){
location = c.first;
if (location) return &location->data;
return 0;
}
T *Next(){
if (!location) return 0;
location = location->link;
if (location) return &location->data;
return 0;
}
private:
ChainNode<T> *location;
};
//采用链表遍历器输出整数链表X
/*int *x;
ChainIterator<int> c;
x = c.Initialize(X);
while (x){
cout << *x << " ";
x = c.Next();
}
cout << endl;*/
#endif
详细的就不说了,小小链表总共也搞了差不多三个小时,收获颇丰,不要小瞧。最后是对链表的测试。
#include <conio.h>
#include <iostream>
#include "Chain.h"
#include "xcept.h"
#include <string>
using namespace std;
void main(void)
{
try
{
string str[5]={"I","am","wade","tang","!"};
Chain<string> strChain;
//调用Append函数插入数据
for (int i=0;i<5;i++)
{
strChain.Append(str[i]);
}
//调用Output函数输出链表
cout<<"输出链表: "<<strChain<<endl;
//调用Length函数输出链表长度
cout<<"链表长度为: "<<strChain.Length()<<endl;
//调用Find函数查找第4个元素
string x;
if(strChain.Find(4,x))
cout<<"第四个元素:"<<str[3]<<"= "<<x<<endl;
//调用Search查找wade在几个元素
cout<<str[2]<<"在第"<<strChain.Search(str[2])<<"个位置"<<endl;
string y;
//调用Delete删除最后一个元素
cout<<"删除最后一个元素后:"<<strChain.Delete(5,y)<<endl;
system("pause");
}
catch (NoMem)
{
cerr<<"An exception has occurred"<<endl;
}
}
希望有兴趣的朋友测试一下迭代器,并交流思路。
附录,还有xcept.h中的两个异常的定义:
#ifndef XCEPT_H
#define XCEPT_H
#include <iostream>//异常处理
using namespace std;
class OutOfBounds{
public:
OutOfBounds() {}
};
class NoMem{
public:
NoMem(){}
};
//使new引发NoMem异常而不是xalloc异常
void my_new_handler(){
throw NoMem();
}
std::new_handler Old_Handler_ = std::set_new_handler(my_new_handler);
#endif