#include <stdio.h>
#include <stdlib.h>
#include<time.h>
#define ERROR 0;
#define OK 1;
//typedef
typedef int Status;
typedef int ElemType;
typedef struct Node//定义带头结点的单链表的节点类型
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList;
//操作函数声明
Status CreateListHead(LinkList*L,int n);
Status CreateListTail(LinkList *L,int n);
Status GetElem(LinkList L,int i,ElemType *e);
Status ListInsert(LinkList *L,int i,ElemType e);
Status ListDelete(LinkList*L,int i,ElemType *e);
Status DestroyList(LinkList *L);
Status ListTraverse(LinkList L);
int main()
{
LinkList L;
int n=5;
int e=0;
int i=0;
printf("请输入需要产生的单链表长度(>=1):");
scanf("%d",&n);
CreateListTail(&L,n);
printf("产生的单链表是:");
ListTraverse(L);
printf("请输入你想要插入的位置和数:");
scanf("%d %d",&i,&e);
ListInsert(&L,i,e);
printf("插入后得到的新链表是:");
ListTraverse(L);
printf("你想要查看哪个位置的数:");
scanf("%d",&i);
printf("该数是:");
GetElem(L,i,&e);
printf("\n");
printf("你想要删除哪个位置的数:");
scanf("%d",&i);
ListDelete(&L,i,&e);
printf("删除以后的新链表是:");
ListTraverse(L);
printf("现在要销毁链表释放空间啦!\n");
DestroyList(&L);
}
//单链表的整表创建,头插法
Status CreateListHead(LinkList*L,int n)
{
LinkList p;
int i;
srand(time(0));
*L=(LinkList)malloc(sizeof(Node));
( *L)->next=NULL;
if(n<1)
return ERROR;
for(i=0;i<n;i++)
{
p=(LinkList)malloc(sizeof(Node));
p->data=rand()%100+1;
p->next=(*L)->next;
(*L)->next=p;
}
return OK;
}
//单链表的整表创建,尾插法
Status CreateListTail(LinkList *L,int n)
{
LinkList p,r;
int i;
*L=(LinkList)malloc(sizeof(Node));
( *L)->next=NULL;
r=*L;
srand(time(0));
for (i=0;i<n;i++)
{
p=(LinkList)malloc(sizeof(Node));
p->data=rand()%100+1;
r->next=p;
r=p;
}
r->next=NULL;
return OK;
}
//读取单链表中的第i个元素
Status GetElem(LinkList L,int i,ElemType *e)
{
int j=1;
LinkList p;
p=L->next;
while(p&&j<i)
{
p=p->next;
++j;
}
if(j>i||!p)
return ERROR;
*e=p->data;
printf("%d",*e);
return OK;
}
//在单链表中第i个位置之前插入一个新节点e
Status ListInsert(LinkList *L,int i,ElemType e)
{
int j=1;
LinkList p,s;
p=*L;
while(p&&j<i)
{
p=p->next;
++j;
}
if (!p||j>i)
return ERROR;
s=(LinkList)malloc(sizeof(Node));
s->data=e;
s->next=p->next;
p->next=s;
return OK;
}
//删除单链表的第i个元素,并用e返回其值
Status ListDelete(LinkList*L,int i,ElemType *e)
{
int j=1;
LinkList p,q;
p=*L;
while(p->next&&j<i)
{
p=p->next;
++j;
}
if (!(p->next)||j>i)
return ERROR;
q=p->next;
p->next=q->next;
* e=q->data;
free(q);
return OK;
}
//销毁单链表
Status DestroyList(LinkList *L)
{
LinkList p,q;
p=(*L)->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
free(*L);
*L=NULL;
printf("链表销毁成功!");
return OK;
}
//打印单链表
Status ListTraverse(LinkList L)
{
LinkList p;
p=L->next;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
return OK;
}
程序测试结果:
关于二级指针使用的必要性,我是这样理解的:
很多操作其实使用一级指针也可以实现,但是在需要改变一级指针的值是就必须使用二级指针,例如,需要删除头结点或者生成链表时,L的值就需要变化,那么此时只有传入二级指针才能达到修改L的值的目的,这种情况下传入&L也是可行的。
下面举两个例子说明一下:
(1)
在链表已经生成的情况下,修改ListInsert函数为:
Status ListInsert(LinkList L,int i,ElemType e)
{
int j=1;
LinkList p,s;
p=L;
while(p&&j<i)
{
p=p->next;
++j;
}
if (!p||j>i)
return ERROR;
s=(LinkList)malloc(sizeof(Node));
s->data=e;
s->next=p->next;
p->next=s;
return OK;
}
程序仍然可以得到正确的结果。
(2)
但是在生成链表时,若只传入一级指针,即修改CreateListTail为:
Status CreateListTail(LinkList L,int n)
{
LinkList p,r;
int i;
L=(LinkList)malloc(sizeof(Node));
L->next=NULL;
r=L;
srand(time(0));
for (i=0;i<n;i++)
{
p=(LinkList)malloc(sizeof(Node));
p->data=rand()%100+1;
r->next=p;
r=p;
}
r->next=NULL;
return OK;
}
则运行程序时,CodeBlocks会停止工作,这是因为在生成链表阶段,需要将L的值传出到主函数,供后面的链表打印函数ListTraverse调用。若此处只传入一级指针,则CreateListTail函数调用结束以后,L的值并没有改变,相当于L仍指向主函数中定义时所指向的位置(野指针或者指向NULL,暂时还不是很清楚如果不初始化指向哪里),则后续调用打印函数会出错。
总结:需要修改L的值时,必须传入二级指针即func(LinkList *L),其他情况下传一级指针即func(LinkList L)就ok。