线性表的链表存储,今天主要对其中重要的操作进行分析,其他的操作均可由基本操作拓展得到。
学数据结构必须得吃透每一个句子,只有真正理解了,数据结构才能了然于心。
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
typedef int ElemType; //用户自定义类型
typedef struct LNode
{
ElemType num;
struct LNode *next;
}LNode,*LinkList;
int InitLinkList(LinkList &T) /
{
T=(LinkList)malloc(sizeof(LNode));
if(!T) return ERROR;
T->next=NULL;
return OK;
}
int InsertElement(LinkList T,ElemType elem)
{
LinkList temp;
temp=(LinkList)malloc(sizeof(LNode));
if(!temp) return ERROR;
temp->num=elem;
while(T->next)
T=T->next;
temp->next=T->next;
T->next=temp;
return OK;
}
int DeleteElement(LinkList T,ElemType elem)
{
LinkList pre;
while(T->next)
{
pre=T;
T=T->next;
if(T->num==elem)
{
pre->next=T->next;
free(T);
return OK;
}
}
printf("删除数据不存在\n");;
return ERROR;
}
int ListLength(LinkList T)
{
int j=0;
while(T->next)
{
T=T->next;
j++;
}
return j;
}
void visitLinkList(LinkList T)
{
LinkList h=T;
h=h->next;
while(h)
{
printf("%d\t",h->num);
h=h->next;
}
}
int main()
{
int i;
LinkList a;
i=InitLinkList(a);
for(i=1;i<=50;i++)
InsertElement(a,i);
i=ListLength(a);
printf("线性链表的长度为%d\n",i);
visitLinkList(a);
}
注意:
一.typedef struct 和struct区别
typedef struct LNode
{
ElemType num;
struct LNode *next;
}LNode,*LinkList;
1.typedef的作用是什么?简单地讲,为现有类型创建一个新的名字,或称为类型别名。
typedef struct
{
ElemType num;
struct LNode *next; //编译器会报错
}LNode,*LinkList;
那么这个与前一个又有什么区别呢?显然,这个struct并没有名字,因此当定义struct LNode *next时,编译器根本不晓得存在一个结构体叫LNode。尽管我们用typedef为struct创建一个新名字LNode.
我们现在来看一看实际上第一段代码是分为两个部分的。
第一部分:
struct LNode
{
ElemType num;
struct LNode *next;
};
第二部分
typedef struct LNode LNode,*LinkList
也就是说先对结构体定义,然后再起别名。
二.LNode和LinkList区别
结构体类型LNode是单链表中的结点类型,它包括两个成员项,其一是数据域elem,用于存放某个数据元素本身的信息,其类型为通用类型标示符ElemType,由用户在使用时自行定义;其二是指针域next,用于存放某结点的直接后继结点的存储地址,即它指向某结点的直接后继结点,显然类型为struct LNode *类型。
LinkList是指向LNode结构体类型的指针类型,实际上就是和LNode*类型一样,只不过名字不一样而已。
两者的大小区别:
(1)LNode因为是结构体大小,所以所占内存大小由结构体内的变量大小所决定。
(2)LinkList是指针,而指针的内存空间无论指向什么类型的变量,都只跟cpu寻址字长有关,而在我的系统中长度为4.
三.指针和指针引用
int InitLinkList(LinkList &T) /
{
T=(LinkList)malloc(sizeof(LNode));
if(!T) return ERROR;
T->next=NULL;
return OK;
}
一开始我很奇怪,为什么需要对LinkList加上引用.
指针其实类似于int、char等类型,只不过其存放的是地址,而不是一个具体的元素信息。
由此,当我没加上&的时候
int InitLinkList(LinkList T) /
{
T=(LinkList)malloc(sizeof(LNode));
if(!T) return ERROR;
T->next=NULL;
return OK;
}
这个函数为T申请一个头结点,其分配的空间会在函数结束后消失,并不能改变实参。
而当加上&后,在函数里申请到的头结点会影响到实参,从而达到了修改实参的效果。
四.为什么在其他函数里却不用加&
int InsertElement(LinkList T,ElemType elem)
{
LinkList temp;
temp=(LinkList)malloc(sizeof(LNode));
if(!temp) return ERROR;
temp->num=elem;
while(T->next)
T=T->next;
temp->next=T->next;
T->next=temp;
return OK;
}
(1)我在这里没有加&的原因很简单,实际上我并不需要对T所指向的内容进行修改。而我需要修改的是T->next的内容
由于next是struct LNode*类型,所以我在函数里可以直接对next所指向的内容直接进行修改。
(2)是否有必要每次都要重新定义一个LinkList p指向头结点
int InsertElement(LinkList &T,ElemType elem)
{
LinkList p=T,temp;
temp=(LinkList)malloc(sizeof(LNode));
if(!temp) return ERROR;
temp->num=elem;
while(p->next)
p=p->next;
temp->next=p->next;
p->next=temp;
return OK;
}
我在上面已经讲过,如果在
int InsertElement(LinkList &T,ElemType elem) 里对LinkList加上引用后,会对实参T进行修改
所以当你用T=T->next时,T所指向的内容一直在改变,而我们其实并不需要它改变,由此我们将T的所指向的内容赋给p,p进行p=p->next,
当函数结束时p所指向的内容会消失,而不会对实参造成任何影响。