链表是常见的动态存储方法,
链表定义:用一组任意的存储单元来存放线性表的节点,这组存储单元可以是连续的,也可以是非连续的。
结点包括数据域和指针域。
数据域是用来存放结点的值,指针域存放数据元素直接后继的地址。
由于每一个线性链表中只有一个next指针域,所以线性链表是单链表。
单链表的存储结构
Typedef struct Node /*结点类型定义*/
{
ElemType data;
Struct Node * next;
}Node ,*LinkList; /* LinkList为结构指针类型*/
说明:
1) LinkList与Node *同为结构指针类型。LinkList主要强调是单链表的头指针变量,指向的是头指针。Node *来定义单链表中结点的指针。
2) L是单链表的头指针,则L是指向单链表的头指针,若单链表是带头结点的指针,则L是指向头结点的指针。
3) 判断是空表:
① 一种是带头结点的空表,L->next==NULL;
② 一种是不带头结点的空表,L==NULL;
4) 链表的元素,若是带头结点的链表,则P=L->next,p是指向第一个元素的结点,p->data就是第一个元素的数据域.
单链表的基本运算
1.初始化单链表
InitList(LinkList *L ){
*L =(LinkList )malloc (sizeof (Node));
(*L)->next=NULL;
}
注意
① L是指向头结点的指针,用来接收主程序中待初始化单链表的头指针变量的地址。
② *L相当于主程序中待初始化单链表的头指针变量。
2.建立单链表
① 头插法建表
算法思路
//用头插法建立单链表
void CreateFromHead(LinkList L)
{
Node *s;
char c;
int flag=1;
printf("用头插法建立线性链表,请输入字符,以$结尾\n");
while(flag) // flag初值为1,当输入"$"时,置flag为0,建表结束
{
c=getchar();
if(c!='$')
{
s=(Node*)malloc(sizeof(Node)); //建立新结点s
s->data=c;
s->next=L->next;//将s结点插入表头,将新结点接入到第一个结点的前面
L->next=s;//将头结点连接新建结点
}
else
flag=0;
}
}
② 用尾插法建表
算法思路
① 建空表,同时用H,r指向空表
② 申请新结点并赋值,s指向结点,s->data=c
③ 对新结点操作,r->next=s, r=s;
//用尾插法建立单链表
void CreateFromTail(LinkList L)
{
printf("用尾插法建立线性链表,请输入字符,以$结尾\n");
Node *r, *s;
char c;
int flag =1; /*设置一个标志,初值为1,当输入"$"时,flag为0,建表结束*/
r=L; /*r指针动态指向链表的当前表尾,以便于做尾插入,其初值指向头结点*/
while(flag) /*循环输入表中元素值,将建立新结点s插入表尾*/
{
c=getchar();
if(c!='$')
{
s=(Node*)malloc(sizeof(Node));
s->data=c;
r->next=s; //新建结点接入到表尾
r=s; //r始终指向单链表的表尾
}
else
{
flag=0;
r->next=NULL; /*将最后一个结点的next链域置为空,表示链表的结束*/
}
}
}
3.查找
① 按序号查找
算法思路
① 定义一个指针指向头结点,累加器,
② 没有找到就让指针依次往后移,累加器加一,
③ 找到后,输出结点的值
//单链表的查找,按序号查找
Node * Get (LinkList L, int i)
/*在带头结点的单链表L中查找第i个结点,若找到(1≤i≤n),则返回该结点的存储位置; 否则返回NULL*/
{
int j;
Node *p;
p=L;
j=0; /*从头结点开始扫描*/
while ((p->next!=NULL)&&(j<i))
{
p=p->next; /* 扫描下一结点*/
j++; /* 已扫描结点计数器 */
}
if(i == j)
return p; /* 找到了第i个结点 */
else
return NULL; /* 找不到,i≤0或i>n */
}
② 按值查找
算法思路
用一个指针指向第一个结点,让指针所指向的数据域与值相比较,如相同就输出其位置p
//单链表按值查找
Node *Locate( LinkList L,ElemType key)
/*在带头结点的单链表L中查找其结点值等于key的结点,若找到则返回该结点的位置p,否则返回NULL*/
{
Node *p;
p=L->next; /*从表中第一个结点开始 */
while (p!=NULL)
{
if (p->data!=key)
p=p->next;
else
break; /*找到结点值=key时退出循环 */
}
return p;
}
4.求单链表的长度
算法思路
用j做累加器,初始值为0,
//求单链表长度
int ListLength(LinkList L)
/*求带头结点的单链表L的长度*/
{
Node *p;
int j;
p=L->next;
j=0; /*用来存放单链表的长度*/
while(p!=NULL)
{
p=p->next;
j++;
}
return j; /*j为求得的单链表长度*/
}
5.单链表插入操作
在线性表的第i(1<= i <=N+1)个位置之前插入一个新元素e.
算法思路:
① 查找,在单链表中找到第i-1个结点并由指针pre指示;
② 申请,申请新结点s,将其数据域的值置为e,s->data=e;
③ 插入挂链,通过修改指针域将新结点s挂入单链表L, s->next=pre->next;
Pre->next=s;
//单链表的元素插入
int InsList(LinkList L,int i,ElemType e)
/*在带头结点的单链表L中第i个位置插入值为e的新结点*/
{
Node *pre,*s;
int k;
pre=L;
k=0; /*从"头"开始,查找第i-1个结点*/
while(pre!=NULL&&k<i-1) /*表未查完且未查到第i-1个时重复,找到pre指向第i-1个*/
{
pre=pre->next;
k=k+1;
} /*查找第i-1结点*/
if(!pre) /*如当前位置pre为空表已找完还未数到第i个,说明插入位置不合理*/
{
printf("插入位置不合理!");
return ERROR;
}
s=(Node*)malloc(sizeof(Node)); /*申请一个新的结点S */
s->data=e; /*值e置入s的数据域*/
s->next=pre->next; /*修改指针,完成插入操作*/
pre->next=s;
return OK;
}
6.单链表的删除操作
将线性表中第i个元素e删除
① 查找,通过计数方式找到第i-1个结点,并由指针pre指向;
② 删除,删除第i个结点并释放空间,r=pre->next;pre->next=r->next;free(r);
//单链表的删除元素
int DelList(LinkList L,int i,ElemType *e)
/*在带头结点的单链表L中删除第i个元素,并将删除的元素保存到变量*e中*/
{
printf("请输入你要删除的位置:");
scanf("%d",&i);
Node *pre,*r;
int k;
pre=L;
k=0;
while(pre->next!=NULL && k<i-1) /*寻找被删除结点i的前驱结点i-1使p指向它*/
{
pre=pre->next;
k=k+1;
} /*查找第i-1个结点*/
if(!(pre->next)) /* 即while循环是因为p->next=NULL或i<1而跳出的,而是因为没有找到合法的前驱位置,说明删除位置i不合法。*/
{
printf("删除结点的位置i不合理!");
return ERROR;
}
r=pre->next;
pre->next=pre->next->next; /*修改指针,删除结点r*/
*e = r->data;
free(r); /*释放被删除的结点所占的内存空间*/
printf("成功删除结点!");
return OK;
}
7.清空线性链表
//清空线性链表
void Destoy(LinkList L){
if(L->next!=NULL){
L->next=NULL;
}
printf("清空成功!");
}
8.显示单链表
// 显示单链表
void Display(LinkList L){
printf("线性链表中的元素有:\n");
Node *p;
p=L;
while(p->next!=NULL)
{
p=p->next;
printf("%c ",p->data);
}
printf("\n");
}
9.将单链表进行就地逆置
算法思路:
单链表逆置,是将每一个结点从原链表中摘下来,再以头插法的方式插入
//将单链表进行就地逆置
void ReverseList(LinkList L)
{
Node *p,*q;
p=L->next;
L->next=NULL;
while(p!=NULL)
{
q=p->next;
p->next=L->next;
L->next=p;
p=q;
}
}
/*
目的:掌握单链表建立、清空、删除、插入、遍历等操作。
要求:用头插法或尾插法建立单链表、将这个链表的内容进行遍历输出,删除某指定位置节点、在某个指定位置插入节点
*/
//单链表的头文件
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef char ElemType;//重新定义char类型的为ElemType
typedef struct Node /*结点类型定义*/
{
ElemType data;
struct Node * next;
}Node, *LinkList; /* LinkList为结构指针类型*/
//初始化单链表
void init_linklist(LinkList *L)/*对单链表进行初始化*/
{
*L=(LinkList)malloc(sizeof(Node)); /*申请结点空间*/
(*L)->next=NULL; /*置为空表*/
}
//用头插法建立单链表
void CreateFromHead(LinkList L)
{
// printf("用头插法建立线性链表,请输入字符,以$结尾\n");
Node *s;
char c;
int flag=1;
printf("用头插法建立线性链表,请输入字符,以$结尾\n");
while(flag) // flag初值为1,当输入"$"时,置flag为0,建表结束
{
c=getchar();
if(c!='$')
{
s=(Node*)malloc(sizeof(Node)); //建立新结点s
s->data=c;
s->next=L->next;//将s结点插入表头
L->next=s;
}
else
flag=0;
}
}
//用尾插法建立单链表
void CreateFromTail(LinkList L)
{
printf("用尾插法建立线性链表,请输入字符,以$结尾\n");
Node *r, *s;
char c;
int flag =1; /*设置一个标志,初值为1,当输入"$"时,flag为0,建表结束*/
r=L; /*r指针动态指向链表的当前表尾,以便于做尾插入,其初值指向头结点*/
while(flag) /*循环输入表中元素值,将建立新结点s插入表尾*/
{
c=getchar();
if(c!='$')
{
s=(Node*)malloc(sizeof(Node));
s->data=c;
r->next=s;
r=s;
}
else
{
flag=0;
r->next=NULL; /*将最后一个结点的next链域置为空,表示链表的结束*/
}
}
}
// 显示单链表
void Display(LinkList L){
printf("线性链表中的元素有:\n");
Node *p;
p=L;
while(p->next!=NULL)
{
p=p->next;
printf("%c ",p->data);
}
printf("\n");
}
//单链表的元素插入
int InsList(LinkList L,int i,ElemType e)
/*在带头结点的单链表L中第i个位置插入值为e的新结点*/
{
Node *pre,*s;
int k;
pre=L;
k=0; /*从"头"开始,查找第i-1个结点*/
while(pre!=NULL&&k<i-1) /*表未查完且未查到第i-1个时重复,找到pre指向第i-1个*/
{
pre=pre->next;
k=k+1;
} /*查找第i-1结点*/
if(!pre) /*如当前位置pre为空表已找完还未数到第i个,说明插入位置不合理*/
{
printf("插入位置不合理!");
return ERROR;
}
s=(Node*)malloc(sizeof(Node)); /*申请一个新的结点S */
s->data=e; /*值e置入s的数据域*/
s->next=pre->next; /*修改指针,完成插入操作*/
pre->next=s;
return OK;
}
//单链表的查找,按序号查找
Node * Get (LinkList L, int i)
/*在带头结点的单链表L中查找第i个结点,若找到(1≤i≤n),则返回该结点的存储位置; 否则返回NULL*/
{
int j;
Node *p;
p=L;
j=0; /*从头结点开始扫描*/
while ((p->next!=NULL)&&(j<i))
{
p=p->next; /* 扫描下一结点*/
j++; /* 已扫描结点计数器 */
}
if(i == j)
return p; /* 找到了第i个结点 */
else
return NULL; /* 找不到,i≤0或i>n */
}
//单链表按值查找
Node *Locate( LinkList L,ElemType key)
/*在带头结点的单链表L中查找其结点值等于key的结点,若找到则返回该结点的位置p,否则返回NULL*/
{
Node *p;
p=L->next; /*从表中第一个结点开始 */
while (p!=NULL)
{
if (p->data!=key)
p=p->next;
else
break; /*找到结点值=key时退出循环 */
}
return p;
}
//求单链表长度
int ListLength(LinkList L)
/*求带头结点的单链表L的长度*/
{
Node *p;
int j;
p=L->next;
j=0; /*用来存放单链表的长度*/
while(p!=NULL)
{
p=p->next;
j++;
}
return j; /*j为求得的单链表长度*/
}
//单链表的删除元素
int DelList(LinkList L,int i,ElemType *e)
/*在带头结点的单链表L中删除第i个元素,并将删除的元素保存到变量*e中*/
{
printf("请输入你要删除的位置:");
scanf("%d",&i);
Node *pre,*r;
int k;
pre=L;
k=0;
while(pre->next!=NULL && k<i-1) /*寻找被删除结点i的前驱结点i-1使p指向它*/
{
pre=pre->next;
k=k+1;
} /*查找第i-1个结点*/
if(!(pre->next)) /* 即while循环是因为p->next=NULL或i<1而跳出的,而是因为没有找到合法的前驱位置,说明删除位置i不合法。*/
{
printf("删除结点的位置i不合理!");
return ERROR;
}
r=pre->next;
pre->next=pre->next->next; /*修改指针,删除结点r*/
*e = r->data;
free(r); /*释放被删除的结点所占的内存空间*/
printf("成功删除结点!");
return OK;
}
//清空线性链表
void Destoy(LinkList L){
if(L->next!=NULL){
L->next=NULL;
}
printf("清空成功!");
}
//将单链表进行就地逆置
void ReverseList(LinkList L)
{
Node *p,*q;
p=L->next;
L->next=NULL;
while(p!=NULL)
{
q=p->next;
p->next=L->next;
L->next=p;
p=q;
}
}
//显示提示信息
void Displays()
{
printf("-----------------------\n");
printf("1.显示线性表\n");
printf("2.用头插法建立单链表\n");
printf("3.用尾插法建立单链表\n");
printf("4.删除元素\n");
printf("5.清空线性链表\n");
printf("6.将单链表进行就地逆置\n");
printf("7.退出\n");
printf("-----------------------\n");
}
//主函数
int main(){
LinkList L;
ElemType *e;
int x,i=0;
e=(char*)malloc(sizeof(char));
Displays();
printf("请选择功能1-7\n");
scanf("%d",&x);
while(1){
switch(x){
case 1:Display(L);break;
case 2:init_linklist(&L);
CreateFromHead(L);break;
case 3:
init_linklist(&L);
CreateFromTail(L);break;
case 4:DelList(L, i, e);break;
case 5:Destoy(L);break;
case 6:ReverseList( L);break;
case 7:exit(0);break;
}
printf("请选择功能1-7\n");
scanf("%d",&x);
}
return 0;
}