标题：单链表
单链表简介
  用一组地址任意的存储单元存放线性表中的数据元素。
以元素(数据元素的映象) + 指针(指示后继元素存储位置)
= 结点(表示数据元素 或 数据元素的映象)
以“结点的序列”表示线性表称作线性链表（单链表）
单链表是一种链式存取的结构，为找第 i 个数据元素，必须先找到第 i-1 个数据元素。
因此，查找第 i 个数据元素的基本操作为：移动指针，比较 j 和 i 
单链表
1、链接存储方法
链接方式存储的线性表简称为链表（Linked List）。
链表的具体存储表示为：
① 用一组任意的存储单元来存放线性表的结点（这组存储单元既可以是连续的，也可以是不连续的）
② 链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系，在存储每个结点值的同时，还必须存储指示其后继结点的地址（或位置）信息（称为指针（pointer）或链(link)）
注意：
链式存储是最常用的存储方式之一，它不仅可用来表示线性表，而且可用来表示各种非线性的数据结构。
2、链表的结点结构
┌───┬───┐
│data │next │
└───┴───┘ 
data域--存放结点值的数据域
next域--存放结点的直接后继的地址（位置）的指针域（链域）
注意：
①链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的。
②每个结点只有一个链域的链表称为单链表（Single Linked List）。
【例】线性表（bat，cat，eat，fat，hat，jat，lat，mat）的单链表示如示意图
3、头指针head和终端结点指针域的表示
单链表中每个结点的存储地址是存放在其前趋结点next域中，而开始结点无前趋，故应设头指针head指向开始结点。
注意：
链表由头指针唯一确定，单链表可以用头指针的名字来命名。
【例】头指针名是head的链表可称为表head。
终端结点无后继，故终端结点的指针域为空，即NULL。
4、单链表的一般图示法
由于我们常常只注重结点间的逻辑顺序，不关心每个结点的实际位置，可以用箭头来表示链域中的指针，线性表（bat，cat，fat，hat，jat，lat，mat）的单链表就可以表示为下图形式。
5、单链表类型描述
typedef char DataType; //假设结点的数据域类型为字符
typedef struct node{ //结点类型定义
DataType data; //结点的数据域
struct node *next;//结点的指针域
}ListNode;
typedef ListNode *LinkList;
ListNode *p;
LinkList head;
注意：
①*LinkList和ListNode是不同名字的同一个指针类型（命名的不同是为了概念上更明确）
②*LinkList类型的指针变量head表示它是单链表的头指针
③ListNode类型的指针变量p表示它是指向某一结点的指针
6、指针变量和结点变量
指针变量
结点变量
定义
在变量说明部分显式定义
在程序执行时，通过标准函数malloc生成
取值
非空时，存放某类型结点
实际存放结点各域内容的地址 
操作方式
通过指针变量名访问 
通过指针生成、访问和释放 
①生成结点变量的标准函数
p=( ListNode *)malloc(sizeof(ListNode))；
//函数malloc分配一个类型为ListNode的结点变量的空间,并将其首地址放入指针变量p中
②释放结点变量空间的标准函数 
free(p)；//释放p所指的结点变量空间
③结点分量的访问 
利用结点变量的名字*p访问结点分量
方法一：(*p).data和(*p).next
方法二：p-﹥data和p-﹥next
④指针变量p和结点变量*p的关系 
指针变量p的值——结点地址
结点变量*p的值——结点内容
(*p).data的值——p指针所指结点的data域的值
(*p).next的值——*p后继结点的地址
*((*p).next)——*p后继结点
注意：
① 若指针变量p的值为空（NULL），则它不指向任何结点。此时，若通过*p来访问结点就意味着访问一个不存在的变量，从而引起程序的错误。
② 有关指针类型的意义和说明方式的详细解释
可见，在链表中插入结点只需要修改指针。但同时，若要在第 i 个结点之前插入元素，修改的是第 i-1 个结点的指针。
因此，在单链表中第 i 个结点之前进行插入的基本操作为:
找到线性表中第i-1个结点，然后修改其指向后继的指针。
单链表的建立
链表操作中动态存储分配要使用标准函数，先介绍一下这些函数。 
(1)malloc(size) 
在内存的动态存储区申请一个长度为size字节的连续空间。 
(2)calloc(n，size) 
在内存的动态存储区申请n个长度为size字节的连续空间，函数返回值为分配空间的首地址。若此函数未被成功执行，函数返回值为0。 
(3)free(p) 
释放由指针p所指向的存储单元，而存储单元的大小是最近一次调用malloc()或calloc()函数时所申请的存储空间。 
在头文件\"stdlib．h”中包含了这些函数的信息，使用这些函数时需在程序开头用文件包含指令#include“stdlib．h”指明。 
另请读者注意，调用动态存储分配函数返回的指针是指向void类型或char类型的指针，在具体使用时，要根据所指向的数据进行强制类型转换。 
单链表的建立有头插法、尾插法两种方法。 
1．头插法 
单链表是用户不断申请存储单元和改变链接关系而得到的一种特殊数据结构，将链表的左边称为链头，右边称为链尾。头插法建单链表是将链表右端看成固定的，链表不断向左延伸而得到的。头插法最先得到的是尾结点。 
由于链表的长度是随机的，故用一个while循环来控制链表中结点个数。假设每个结点的值都大于O，则循环条件为输入的值大于o。申请存储空间可使用malloc()函数实现，需设立一申请单元指针，但malloc()函数得到的指针并不是指向结构体的指针，需使用强制类型转换，将其转换成结构体型指针。刚开始时，链表还没建立，是一空链表，head指针为NULL。 
链表建立的过程是申请空间、得到数据、建立链接的循环处理过程。
2．尾插法
若将链表的左端固定，链表不断向右延伸，这种建立链表的方法称为尾插法。尾插法建立链表时，头指针固定不动，故必须设立一个搜索指针，向链表右边延伸，则整个算法中应设立三个链表指针，即头指针head、搜索指针p2、申请单元指针pl。尾插法最先得到的是头结点。
单链表c语言表示：
#include<stdio.h>
#include<stdlib.h>
struct linknode //建立链表节点
{
int data; //需要更通用的数据类型
struct linknode *next;
};
struct link
{
struct linknode *head;
struct linknode *tail;
};
struct linknode *create() //创建链表，接受INT型值
{
int datas;
struct linknode *head,*temp,*tail;
head=tail=NULL;
while(scanf("%d",&datas)==1) //输入方式有待改进
{
temp=(struct linknode *)malloc(sizeof(struct linknode));
if(temp==NULL)
printf("allocate erro!");
else
{
temp->data=datas;
temp->next=NULL;
if(head==NULL)
head=tail=temp;
else
{
tail->next=temp;
tail=tail->next；//Modify feng
}
}
}
return head;
}
void print(struct linknode *head) //打印链表
{
struct linknode *p;
p=head;
while(p)
{
printf("%d\t",p->data);
p=p->next;
}
}
struct linknode *find(struct linknode *head,int datas) //查找特定的值的节点
{
struct linknode *p;
p=head;
while(p->data!=datas&&p->next!=NULL)
{
p=p->next;
}
if(p->data==datas)
return p;
else
return NULL;
}
struct linknode *findAhead(struct linknode *head,int datas) //查找特定值得前一个节点
{
struct linknode *p,*q;
q=NULL;
p=head;
while(p->data!=datas&&p->next!=NULL)
{
q=p;
p=p->next;
}
if(p->data==datas)
return q;
else
return NULL;
}
struct linknode *enterTohead(struct linknode *head,int datas) //在头部添加节点
{ //改变了头节点指针，需重新赋值
struct linknode *enter;
enter=(struct linknode *)malloc(sizeof(struct linknode));
if(enter==NULL)
printf("allocate erro!");
enter->data=datas;
enter->next=NULL;
if(head==NULL)
head=enter;
else
{
enter->next=head;
head=enter;
}
return head;
}
struct linknode *enterTotail(struct linknode *head,int datas) //在尾部添加节点
{
struct linknode *enter,*p;
p=head;
enter=(struct linknode *)malloc(sizeof(struct linknode));
if(enter==NULL)
printf("allocate erro!");
enter->data=datas;
enter->next=NULL;
if(head==NULL)
{
head=enter;
}
else
{
while(p->next)
{
p=p->next;
}
p->next=enter;
}
return head;
}
struct linknode *enterTovalue(struct linknode *head,int value,int datas) //在特定的位置添加节点
{
struct linknode *enter,*fd,*p;
p=findAhead(head,value);
fd=find(head,value);
if(fd==NULL)
printf("find no member is value.");
enter=(struct linknode *)malloc(sizeof(struct linknode));
enter->data=datas;
enter->next=NULL;
if(enter==NULL)
printf("allocate erro!");
if(fd==head)
{
enter->next=head;
head=enter;
}
else
{
p->next=enter;
enter->next=fd;
}
return head;
}
struct linknode *delet(struct linknode *head,int datas) //删除节点
{
struct linknode *delet,*p;
p=findAhead(head,datas);
delet=find(head,datas);
if(!delet)
return NULL;
else
{
p->next=delet->next;
free(delet);
return head;
}
}
void main() //链表建立测试
{
struct linknode *head,*fd;
head=create();
print(head);
fd=findAhead(head,4);
if(fd==NULL)
{
printf("Ahead of found is NULL.");
printf("\n");
}
else
{
printf("\n");
printf("%d\n",fd->data);
}
print(head=enterTohead(head,88));
printf("\n");
print(enterTotail(head,77));
printf("\n");
print(head=enterTovalue(head,5,413));
printf("\n");
head=delet(head,6);
if(head)
print(delet(head,6));
else
printf("have no this number.");
printf("\n");
}