简单链表介绍
抽象数据类型 (Abstract Data Type,ADT)
ADT就是一个抽象的数据类型,类型包括两类信息:属性和操作。 现在要定义一个新的类型
- 是要提供储存结构的方法。
- 是要提供操作数据的法。
简单来说,就是 要自己去定义一个新的类型,而且要对这个类型写一些可以操作的函数。
表ADT
整表创建
随机产生n个元素
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=(ListList)malloc(sizeof(Node));
p->data=rand()%100+1;
p->next=(*L)->next;
(*L)->next=p;
}
}
这里是头插法
顺序插入结点的步骤
随机产生n个元素
void CreateListHead(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 = (ListList)malloc(sizeof(Node));
p->data=rand()%100+1;
r->next=p;
r=p;
}
r->next=NULL;
}
删除链表中 data
为 m
的元素。
struct ListNode *deletem( struct ListNode *L, int m )
{
struct ListNode* record,*a;
a=L;
while(a)
{
if(a->data==m)
{
if(a==L)
L=a->next;
else
record->next=a->next;
}
else
{
record=a;
}
a=a->next;
}
return L;
}
删除整表
void Clear(LinkList *L)
{
LinkList p,q;
p=(*L)->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
(*L)->next=NULL;
}
链表的接口函数
无头结点
结构定义:
typedef struct LNode *PtrToLNode;
struct LNode
{
ElementType Data;
PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;
接口函数:
Position Find( List L, ElementType X )
:返回线性表中首次出现X的位置。若找不到则返回ERROR;
List Insert( List L, ElementType X, Position P )
:将X插入在位置P指向的结点之前,返回链表的表头。如果参数P指向非法位置,则打印“Wrong Position for Insertion”,返回ERROR;
List Delete( List L, Position P )
:将位置P的元素删除并返回链表的表头。若参数P指向非法位置,则打印“ Wrong Position for Deletion
”并返回ERROR。
Position Find( List L, ElementType X )
{
List p=L;
while(p&&p->Data!=X)
{
p=p->Next;
}
if(p!=NULL)
return p;
else
return ERROR;
}
List Insert(List L,ElementType X,Position P)
{
List temp,p;
if(P==L)//如果要插向第一个结点之前
{
temp=(List)malloc(sizeof(struct LNode));
temp->Data=X;
temp->Next=L;//新结点的next指向现在的第一个结点,所以第一个结点现在变成了temp
L=temp;
return L;//返回第一个
}
temp=L;
while(temp)
{
if(temp->Next==P)//如果下一个为P,即 要将新结点插入temp与P之间
{
p=(List)malloc(sizeof(struct LNode));
p->Data=X;
p->Next=temp->Next;//要插入的结点的next指向P
temp->Next=p;//要插入的结点的前一个结点的next指向插入的结点
return L;
}
temp=temp->Next;
}
printf("Wrong Position for Insertion\n");
return ERROR;
}
List Delete(List L,Position P)
{
List p=L;
if(P==L)
{
L=L->Next;
return L;
}
while(p)
{
if(p->Next==P)
{
p->Next=P->Next;
free(P);
return L;
}
p=p->Next;
}
printf("Wrong Position for Deletion\n");
return ERROR;
}
有头结点
结构定义:
typedef struct LNode *PtrToLNode;
struct LNode {
ElementType Data;
PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;
接口函数
List MakeEmpty()
:创建并返回一个空的线性表;
Position Find( List L, ElementType X )
:返回线性表中X的位置。若找不到则返回ERROR;
bool Insert( List L, ElementType X, Position P )
:将X插入在位置P指向的结点之前,返回true。如果参数P指向非法位置,则打印“Wrong Position for Insertion”,返回false;
bool Delete( List L, Position P )
:将位置P的元素删除并返回true。若参数P指向非法位置,则打印“ Wrong Position for Deletion
”并返回false。
List MakeEmpty()
{
List head=(List)malloc(sizeof(struct LNode));
head->Data=0;
head->Next=NULL;//头指针指向空
return head;
}
Position Find( List L, ElementType X )
{
List F=L;
while(F)//每个头结点判断,直到其为空
{
if(F->Data==X)
{
return F;//如果找到返回F的位置
}
F=F->Next;
}
return ERROR;//F为空也没有找到,就返回ERROR
}
bool Insert( List L, ElementType X, Position P )
{
List F=L;
while(F)
{
if(F->Next==P)//这时F是P的前一个结点
{
List p=(List)malloc(sizeof(struct LNode));
p->Data=X;
p->Next=P;//P的Next指向P
F->Next=p;//F的Next指向p
return true;
}
F=F->Next;
}
printf("Wrong Position for Insertion\n");
return false;//如果没有找到就返回
}
bool Delete( List L, Position P )
{
List F=L;
if(!P)//要删的如果是空
{
printf("Wrong Position for Deletion\n");
return false;
}
while(F)
{
if(F->Next==P)找到P,F为其前一个结点
{
F->Next=P->Next;//F的Next指针指向P的Next
free(P);//释放P
return true;
}
F=F->Next;
}
printf("Wrong Position for Deletion\n");
return false;//没找到返回
}
可以看到每一个函数都用到了 while(F)
这样的形式来判断循环,但是要注意的是,如果判断到 NLULL
会跳出,不要在循环体外再用 F->Next
如果 F
已经为空,那么 F->Next
会出现段错误,找不到这个地址。
顺序表
结构定义:
typedef int Position;
typedef struct LNode *List;
struct LNode
{
ElementType Data[MAXSIZE];
Position Last; /* 保存线性表中最后一个元素的位置 */
};
接口函数:
List MakeEmpty()
:创建并返回一个空的线性表;
Position Find( List L, ElementType X )
:返回线性表中X的位置。若找不到则返回ERROR;
bool Insert( List L, ElementType X, Position P )
:将X插入在位置P并返回true。若空间已满,则打印“FULL”并返回false;如果参数P指向非法位置,则打印“ ILLEGAL POSITION
”并返回false;
bool Delete( List L, Position P )
:将位置P的元素删除并返回true。若参数P指向非法位置,则打印“ POSITION P EMPTY
”(其中P是参数值)并返回false。
bool Insert( List L, ElementType X, Position P )
{
if(L->Last+1==MAXSIZE)
{
printf("FULL");
return false;
}
if(P>L->Last+1||P<0)
{
printf("ILLEGAL POSITION");
return false;
}
for(int i=L->Last;i>=P;i--)
{
L->Data[i+1]=L->Data[i];
}
L->Data[P]=X;
L->Last++;
return true;
}
bool Delete( List L, Position P )
{
if(P>L->Last||P<0)
{
printf("POSITION %d EMPTY",P);
return false;
}
for(int i=P;i<=L->Last;i++)
{
L->Data[i]=L->Data[i+1];
}
L->Last--;
return true;
}
List MakeEmpty()
{
List head=(List)malloc(sizeof(struct LNode));
head->Last=-1;
return head;
}
Position Find( List L, ElementType X )
{
for(int i=0;i<=L->Last;i++)
{
if(L->Data[i]==X)
return i;
}
return ERROR;
}
静态链表
原理
因为不是所有语言都有指针,所以 可以用数组的形式来模拟指针。叫游标实现。
- 数组下标为0,也就是第一个元素。这其中有一个值来记录一个空位。
- 数组的最后一个元素中的一个值记录最初的那个插入元素的位置。它就相当于头指针。
所以初始是这样
结构定义
#define MAXSIZE 1000
typedef struct
{
ElemType data;
int cur;
}Component,StaticLinkList[MAXSIZE];
接口定义
//初始化
Status InitList(StaticLinkList space)
{
int i;
for(i=0;i<MAXSIZE-1;i++)
{
space[i].cur=i+1;
}
space[i].cur=0;
reutrn OK;
}
//找到空闲区
int Malloc_SLL(StaticLinkList space)
{
int i=space[0].cur;//找到原先的空闲位置
if(space[0].cur)
space[0].cur=space[i].cur;//这个空闲要被用了,请把这个空闲的下一个给 的第0个元素的cur,以标志着它成为了第一个空闲。
return i;//返回这个空闲的位置
}
//在L中第i个元素之前插入元素e
Status ListInsert(StaticLinkList L,int i,Elemtype e)
{
int j,k,l;
k=MAXSZIE-1;
if(i<L||i>ListLength(L)+1)
return ERROR;
j=MALLOC_SLL(L);//获得空闲下标
if(j)
{
L[j].data=e;
for(l=1;l<=i-1;i++)/*找到第i个元素之前的位置,第一个元素就是0,第二个元素就是1。k本身记录1的位置。循环一次得到第二个元素,循环两次是第三个元素,以此类推。*/
k=L[k].cur;//遍历
L[j].cur=L[k].cur;//新元素的下一个是第i个元素
L[k].cur=j;//第i个元素之前的元素的下一个是新元素
return OK;
}
return ERROR;
}
将丙插入后就是这样
用上面的代码解释就是,要将丙插入第三个位置之前,先找到空闲位置赋值丙,循环两次找到乙,把乙的下一个位置给空闲的下一个位置,空闲位置给乙的下一个位置。
删除
Status ListDelete(StaticLinkList L,int i)
{
int j,k;
if(i<1||i>ListLength(L))
return ERROR;
k=MAXSIZE-1;
for(j=1;j<=i-1;j++>)
k=L[k].cur;//找到前一个
j=L[k].cur;//找到要删的
L[k].cur=L[j].cur;//将要删的下一个给前一个的下一个
Free_SSL(L,j);
return OK:
}
void Free_SSL(StaticLinkList space,int k)
{
space[k].cur=space[0].cur;//k的下一个为空闲位置
space[0].cur=k;//k作为新的空闲位置
}