关于单链表数据结构的理解

先上代码:

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#include<stdlib.h>

typedef int Status
typedef int ElemType

typedef struct Node
{
ElemType data;
struct Node *next;
}Node,*LinkList;

Status GetElem(LinkList L,int i,ElemType *e)
{
int j;
LinkList p;
p=L->next;
j=1;
while(p && j<i) //检查p是否为空指针以及p是否到达i结点
{
p=p->next;
++j;
}
if(!p || j>i) //包含了p为空和非法的i输入
{
return ERROR;
}
*e=p->data;
return OK;
}

Status ListInsert(LinkList *L,int i,ElemType e) //注意这里L并不直接指向Node,是*L直接指向Node
{
int j;
LinkList p,s;
p->*L; //p代表了头结点的指针域
j=1;
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;
}

Status ListDelete(LinkList *L,int i,ElemType *e)
{
int j;
LinkList p,q;
p=*L; //p是头结点的指针域,指向第一个结点
j=1;
while(p->next && j<i) //循环结束后p指向第i-1个结点,这里要保证第i个结点不是NULL
{
p=p->next;
++j;
}
if(!(p->next) || j>i)
return ERROR;
q=p->next;
p->next=q->next; //这里考虑进了q是最后一个结点的情况,那么p->next会被赋NULL
*e=q->data;
free(q);
return OK;
}

void CreateListHead(LinkList *L,int n) //在链表头部插入新元素
{
LinkList p;
int i;
srand(time(0)); //初始化随机数种子
*L=(LinkList)malloc(sizeof(Node)); //这个叫作头结点
(*L)->next=NULL;
for(i=0;i<n;i++)
{
p=(LinkList)malloc(sizeof(Node));
p->data=rand()%100+1; //随机生成100以内的数字
p->next=(*L)->next; //在头结点和尾结点中间插入新结点,一共有n个有效结点
(*L)->next=p;
}
}

void CreateListTail(LinkList *L,int n)
{
LinkList p,r;
int i;
srand(time(0));
*L=(LinkList)malloc(sizeof(Node));
r=*L;
for(i=0;i<n;i++)
{
p=(Node*)malloc(sizeof(Node));
p->data=rand()%100+1;
r->next=p;
r=p;
}
r->next=NULL;
}

Status ClearList(LinkList *L)
{
LinkList p,q;
p=(*L)->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
(*L)->next=NULL;
return OK;
}

 

理解单链表的两个重要问题:

1、LinkList *L这种定义的本质是什么?

答:L是指向结构体Node的指针的指针,结构体Node可以用**L表示,也就是说(**L).data等价于(*L)->data。这里头结点是(**L),(*L)->next是指向第一个非头结点的指针。

2、头结点的意义是什么?

答:

1、防止单链表是空的而设的.当链表为空的时候,带头结点的头指针就指向头结点.如果当链表为空的时候,单链表没有带头结点,那么它的头指针就为NULL.
2、是为了方便单链表的特殊操作,插入在表头或者删除第一个结点.这样就保持了单链表操作的统一性!
3、单链表加上头结点之后,无论单链表是否为空,头指针始终指向头结点,因此空表和非空表的处理也统一了,方便了单链表的操作,也减少了程序的复杂性和出现bug的机会。
4、对单链表的多数操作应明确对哪个结点以及该结点的前驱。不带头结点的链表对首元结点、中间结点分别处理等;而带头结点的链表因为有头结点,首元结点、中间结点的操作相同 ,从而减少分支,使算法变得简单 ,流程清晰。对单链表进行插入、删除操作时,如果在首元结点之前插入或删除的是首元结点,不带头结点的单链表需改变头指针的值,在C 算法的函数形参表中头指针一般使用指针的指针(在C+ +中使用引用 &); 而带头结点的单链表不需改变头指针的值,函数参数表中头结点使用指针变量即可。

转载于:https://www.cnblogs.com/seanchang93/p/6743900.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值