一、对于结点的理解
1.结点的定义
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
2.对该定义进行分解,首先得到:
struct
{
ElemType data;
struct Node *next;
}Node;
分析:Node是一个结构体变量,内含两个成员变量;但这样定义有个缺点,就是当定义的结构体变量过多时,程序的可读性就下 降,为了提升可读性,对结构体加上标签;
3.对上述结构体加上标签得到:
struct Node
{
ElemType data;
struct Node *next;
}Node;
分析:struct Node成为自定义的数据类型,用它可以定义像Node这样的结构体变量,前者Node表示标签,后者表示结构体变 量,但是还可以更加简便;
4.对上述结构体简化:
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
分析:typedef是给struct Node这种数据类型起了一个别名叫Node,这时候就可以用Node来定义结构体变量;
5.对成员函数的理解:
对于第二个成员函数:struct Node *next,表示的是结点指针,它指向的是下一个结点
二、对链表中函数参数的理解
1.为什么有的函数用ElemType *e,有的函数用ElemType e,还有LinkList *L和LinkList L?
分析:关于*e和e,问题在于我们要不要通过函数改变变量e的值,如果函数的参数是ElemType e, 那么在函数外部的e值将不会 因函数的操作而改变;如果函数参数为ElemType *e,则需要把e的地址传递给函数,实际上是改变了指针指向的内容;
关于LinkList *L,LinkList是用来定义结构体指针的数据类型,因此 *L 是结构体指针,*L指向结构体,
那么L就是指向结构体指针的指针,是二级指针,当LinkList *L作形参的时候,传入的是结构体指针的地
址,也就L指向的地址,对L解引用之后,相当于直接引用定义的结构体指针,从而实现对结构体指针修改;
当使用LinkList L为形参时,传入的是结构体指针,可以使用它,但是没法修改它;
三、代码实现
/*
项目名称:单链表的建立与基本操作
编译环境:VC++6.0
作者相关:。。。
最后修改:2019.5.18
学习目标:1.建立单链表
2.熟悉各项基本操作的算法原理
注意事项:1.初始化随机数种子:
(1)srand(time(0)),意思是随着时间的变化产生不同的随机数种子
配合rand()使用,产生真正的随机数
(2)若只使用rang(),系统先调用预设srand()函数产生种子,然后产
生伪随机序列,假设产生了10个不同的数,这10个数会一直循环
常见错误:1.混淆define和typedef
2.在define后加分号
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define OK 1
#define ERROR 0
#define TRUE 1//多分号出现:后面程序if-else match error;
#define FALSE 0
typedef int ElemType;
typedef bool Status;
//定义链表结点结构体
typedef struct Node
{
ElemType data;
struct Node *next;
}Node,*LinkList;
//函数声明
Status InitList( LinkList *L );
Status ListEmpty( LinkList L );
Status ClearList( LinkList *L );
int LengthList( LinkList L );
int LocateElem( LinkList L, ElemType e );//查找链表里是不是有和e相等的元素,有就返回这个元素的位置
Status GetELem( LinkList L, int i, ElemType *e );//用e返回链表中第i个数据元素的值
Status ListInsert( LinkList *L, int i, ElemType e );//操作对象是指针,要改变指针的值,那就要用指针的指针
void CreateListHead( LinkList *L, int n );//头插法
void CreateListTail( LinkList *L, int n );//尾插法
Status ListDelete( LinkList *L, int i, ElemType *e );
Status OutList( LinkList L );
int main()
{
LinkList L1,L2,L3;
ElemType e;
int k;
InitList(&L1);//把指针的地址传递给InitList
for(int i=1;i<=10;i++)
ListInsert(&L1,1,i);
OutList(L1);
GetELem(L1,3,&e);
printf("第3个元素的值为:%d \n",e);
k = LocateElem(L1,e);
printf("%d 在链表中所处的位置为:%d \n",e,k);
ListDelete(&L1,5,&e);
OutList(L1);
ClearList(&L1);
ListEmpty(L1);
CreateListHead(&L2,10);
OutList(L2);
CreateListTail(&L3,10);
OutList(L3);
return 0;
}
//初始化,建立头结点,L指向此头结点,该结点是结构体指针类型
Status InitList( LinkList *L )
{
*L = (LinkList)malloc(sizeof(Node));//结点类型是LinkList
if(!L)
return ERROR;
(*L)->next = NULL;
return OK;
}
Status ListEmpty( LinkList L )
{
if(L->next)
return FALSE;
else
{
printf("the list is empty!\n");
return TRUE;
}
}
//清空链表:只保留头结点
Status ClearList( LinkList *L )
{
LinkList p,q;
p = (*L)->next;//p指向第一个结点
while(p)//未到表尾
{
q = p->next;
free(p);//释放p所指向结点的内存
p = q;//p再指向下一个结点
}
(*L)->next = NULL;
return OK;
}
int LengthList( LinkList L )
{
LinkList p;
int count = 0;
p=L->next;//p指向第一个结点
while(p)
{
++count;
p = p->next;
}
return count;
}
//返回链表中与e相等元素的位置,没有则返回0
int LocateElem( LinkList L, ElemType e )
{
if(ListEmpty(L))
{
printf("the linklist is empty!\n");
return ERROR;
}
LinkList p;
int i=1;
p = L->next;//p指向第一个结点
while(p)
{
if(p->data == e)
return i;
else
{
p=p->next;
++i;
}
}
if(p == NULL)
{
printf("与e相等的元素不存在!\n");
return 0;
}
}
//用e返回链表中第i个数据元素的值
Status GetELem( LinkList L, int i, ElemType *e )
{
LinkList p;
int j = 1;
p = L->next;
while(p&&j<i)//算法思想:工作指针后移
{
p=p->next;
++j;
}
if(!p||j>i)//1.链表为空;2.i的值小于1
return ERROR;
*e = p->data;
return OK;
}
//在第i个元素之前插入数据e
Status ListInsert( LinkList *L, int i, ElemType e )
{
LinkList p,s;
int j=1;
p = *L;
while(p&&j<i)
{
p=p->next;
++j;
}
if(!p||j>i)//i的值不存在
{
return ERROR;
}
s=(LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
//随机产生n个数,利用头插法完成链表的''整表创建''
void CreateListHead( LinkList *L, int n )
{
LinkList p;
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
srand(time(0));//随时间产生不同的随机数种子
for(int i=0;i<n;i++)
{
p = (LinkList)malloc(sizeof(Node));
p->data = rand()%100+1;//任何数对100取余是0~99,进而取值范围是1~100
p->next = (*L)->next;
(*L)->next = p;
}
}
//随机产生n个数,利用尾插法完成链表的''整表创建''
void CreateListTail( LinkList *L, int n )
{
LinkList p,s;
*L = (LinkList)malloc(sizeof(Node));
srand(time(0));
p = *L;//p永远指向尾结点
for(int i=0;i<n;i++)
{
s = (LinkList)malloc(sizeof(Node));
s->data = rand()%100+1;
p->next = s;
p = s;//更新p的位置到尾结点
}
p->next = NULL;
}
//删除链表中第i个数据元素
Status ListDelete( LinkList *L, int i, ElemType *e )
{
LinkList p,q;
int j=1;
p = *L;
while(p->next&&j<i)//p有后继结点并且j<i
{
p=p->next;
++j;
}
if(!p||j>i)//i的值不存在
{
return ERROR;
}
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return OK;
}
Status OutList( LinkList L )
{
LinkList p;
p=L->next;
printf("The list is: \n");
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
return OK;
}