20150405_链表
链表(Linked list):是一种常见的数据结构,是一种线性表,但并不会按线性的存储数据,而是每个结点里存着指向下一个结点的指针。
- 优点:由于不必按顺序存储,链表在插入式的时间复杂度为O(1)。
- 缺点:查找或访问特定结点需要遍历,不支持随机查找和访问,时间复杂度O(n)。
单链表
1.结构定义
typedef struct LNode
{
ElemType data;
// 在此定义链表的成员
struct Node *next;
}LNode, * Linklist; //LNode 为链表结点的类型,Linklist为链表的类型
2.创建链表
//带头结点
void ini_linklist(Linklist *L)
{
(*L)=(Linklist)malloc(sizeof(LNode));//这一句不可以少
(*L)->next=NULL;
}
//在这里,创建链表,要对实参进行改变,所以对于类型linklist要传地址过来。笔者刚学习的时候对这些不太了解,所以又有如此下的一种写法,可以暂时规避这个问题。
Linklist ini_linklist(void)
{
Linklist L;
L=(Linklist)malloc(sizeof(LNode));
L->next=NULL;
return L;
}
//不带头结点
Linklist ini_linklist(void)
{
Linklist head;
head=NULL; //注意这句别写错了,不然下面的都会出现问题
return head;
}
3.头插法
顾名思义,将结点查到链表的最前头,使其成为第一个结点(若是带头结点的链表,则插入在头结点后)
说明:无论插入还是删除,我们都需要知晓我们操作结点的前驱结点的位置。
关键代码
//**带头结点的头插法**
//说明:pnew为插入结点,pre为pnew的前驱,这里应为头结点L
pnew->next=L->next;
L->next=pnew;
//**不带头结点的头插法**
//说明:笔者习惯用L表示不存储数据的头结点,对于存储数据的头结点使用head表示
pnew->next=phead;
下面附上两个实现的头插法版本
//首先是带头结点的
void head_insert(Linklist *L)
{
Linklist pnew;
int val;
while(scanf("%d",&val)!=EOF)
{
pnew=(Linklist)malloc(sizeof(LNode));
pnew->next=NULL; //其实这里这句可以不写,但是建议养成习惯,创建完备的结点,防止出现野指针。
pnew->data=val;
pnew->next=(*L)->next;
(*L)->next=pnew;
}
}
//不带头结点版本
void show_link(Linklist head)
{
Linklist ptr;
ptr=head;
if(head==NULL)
{
printf("the linklist is empty\n");
return ;
}
while(ptr!=NULL)
{
printf("%d ",ptr->data);
ptr=ptr->next;
}
printf("\n");
}
4.尾插法
尾插法,插入在链表现有元素的最后一个元素(tail)之后。
关键代码
//在这里有没有头结点操作没有不同,除非使用尾插法初始化的是第一个元素,这里我们不讨论这个问题,会在下面的具体实例里处理
//说明:pnew为待插入结点,ptail为尾元素,也将成为pnew的前驱。
pnew->next=ptail->next;
ptail->next=pnew;
ptail=pnew;
下面附上两个版本的尾插法
//带头节点版本
#include "myfunc.h"
void tail_insert(Linklist *L)
{
Linklist pnew;
Linklist ptail;
int val;
ptail=(*L);
while(ptail->next!=NULL)
{
ptail=ptail->next;
}
//尾插
while(scanf("%d",&val)!=EOF)
{
pnew=(Linklist)malloc(sizeof(LNode));
pnew->next=NULL;
pnew->data=val;
pnew->next=ptail->next;
ptail->next=pnew;
ptail=pnew;
}
}
//不带头结点版本
void tail_insert(Linklist *head)
{
Linklist pnew;
Linklist ptail;
int val,ret;
ptail=(*head);
if(ptail==NULL)
{
ret=scanf("%d",&val);
if(ret!=1)
return ;
ptail=(Linklist)malloc(sizeof(LNode));//head为空,必须为ptail分配一个地址空间,让head指向它
ptail->data=val;
ptail->next=NULL;
(*head)=ptail;
}
while(ptail->next!=NULL)
{
ptail=ptail->next; //对于非空链表,遍历到正确的ptail位置
}
//尾插
while(scanf("%d",&val)!=EOF)
{
pnew=(Linklist)malloc(sizeof(LNode));
pnew->next=NULL;
pnew->data=val;
pnew->next=ptail->next;
ptail->next=pnew;
ptail=pnew;
}
}
5.删除
关键代码
//说明:ptr为操作结点指针,pre为其前驱
pre->next=ptr->next;
free(ptr);//只能释放由malloc,realloc等分配的空间,你如果不知道是否是这样的分配,最好不写。
还是惯例,附上两个删除版本
//带头结点
void delete_link(Linklist *L,int val)
{
Linklist pre;
Linklist ptr;
pre=(*L);
ptr=(*L)->next;
if(ptr==NULL)
{
printf("it's an empty linklist\n");
return ;
}
while(ptr!=NULL)
{
if(ptr->data==val)
{
pre->next=ptr->next;
free(ptr);
printf("success\n");
return ;
}
pre=ptr;
ptr=ptr->next;
}
printf("no such datum\n");
}
//不带头结点版本
#include "myfunc.h"
void delete_link(Linklist *head,int val)
{
Linklist ptr,pre;
ptr=(*head);
if(ptr==NULL)
{
printf("the linklist is empty\n");
return ;
}
if(ptr->data==val)
{
ptr=ptr->next;
(*head)=ptr;
return ;
}
pre=ptr;
ptr=ptr->next;
while(ptr!=NULL)
{
if(ptr->data==val)
{
pre->next=ptr->next;
free(ptr);
printf("success\n");
return ;
}
pre=ptr;
ptr=ptr->next;
}
printf("no such value\n");
}