线性表逻辑:唯一前驱唯一后继.
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef int Status;
/*
typedef struct{
char num[8];
char name[8];
int score;
}ElemType;
*/
#define OK 1
#define ERROR 0
typedef struct Lnode{
ElemType data;
struct Londe *next;
}Lnode,*Linklist;
//自己定义自己,嵌套定义。
void printLnode(Linklist L){
Lnode *p = L->next;
if(!p) return printf("kong");
while(p){
printf("%d ",p->data);
p = p->next;
}
}
Status InitList_L(Linklist *L)
{
*L = (Linklist)malloc(sizeof(Lnode));
(*L)->next =NULL;
return OK;
}
Status void_L(Linklist L)
{
if (L->next)
{
return ERROR;
}
else return OK;
}
Status Destroy_L(Linklist *L)
{
Lnode *p;
while ((*L))
{
p = *L;
(*L) = (*L)->next;
free(p);
}
}
//销毁整个链表的时候,需要将整个链表包括头结点全部删除,清空时需要进行保留。
Status Clear_L(Linklist *L)
{
Lnode* p,*q;
p =(*L)->next;
q = p->next;
while (q)
{
free(p);
p = q;
q = q->next;
}
(*L)->next = NULL;
return OK;
}
int ListLength(Linklist L)
{
Lnode *p;
p = L->next;
int i = 0;
while (p)
{
i++;
p = p->next;
}
return i;
}
Status GetElem_L(Linklist L,int i,ElemType *e)
{
Lnode *p;int j=1;
p = L->next;
while(p && j<i){
p = p->next;
j++;
}
if(!p||j>i) return ERROR;
*e = p->data;
return OK;
}
Lnode * LocateElem_L(Linklist L,ElemType e)
{
Lnode* p = L->next;
while(p && p->data!=e)
{
p = p->next;
}
return p;
}
int LocateElem_L2(Linklist L,ElemType e)
{
Lnode* p = L->next;int j=1;
while(p && p->data!=e){
p = p->data;
j++;
}
if(p) return j;
else return ERROR;
}
Status ListInsert_L(Linklist*L,int i,ElemType e)
{
Lnode * p;
p = (*L);
int j = 0;
while (p && j<i-1){
p = p->next;j++;
}
if(!p||j>i-1) return ERROR;
Lnode* s = (Lnode*)malloc(sizeof(Lnode));
s->data = e;
s->next = p->next;
p->next = s;
}
Status ListDelete(Linklist *L,int i,ElemType *e)
{
Lnode *p = L;
Lnode *q = L;
int j =0;
while (p->next && j<i-1)
{
p = p->next;
j++;
}
if(!p->next||j>i-1) return ERROR;
q = p->next;
p->next = q->next;
e = q->data;
free(q);
return OK;
}
//头插法:
void CreateList_H(Linklist *L,int n)
{
*L = (Lnode*)malloc(sizeof(Lnode));
(*L)->next = NULL;
for(int i=n;i>0;i--)
{
Lnode *p = (Lnode*)malloc(sizeof(Lnode));
printf("请输入第%d个元素",n-i+1);
scanf("%d",&(p->data));
p->next = (*L)->next;
(*L)->next = p;
}
}
//尾插法:
void CreateList_R(Linklist *L,int n)
{
(*L) = (Lnode*)malloc(sizeof(Lnode));
(*L)->next = NULL;
//初始化
Lnode *r = *L;
for(int i = 0; i < n; ++i)
{
Lnode *p = (Lnode*)malloc(sizeof(Lnode));
p->next = NULL;
r->next = p;
r = p;
printf("请输入第%d个元素",i+1);
scanf("%d",&(p->data));
}
}
int main()
{
Linklist L;
int i;
ElemType e=0;
//头插法初始化链表
printf("请输入需要初始化的元素数量:");
scanf("%d",&i);
CreateList_H(&L,i);
printLnode(L);
//输入长度
printf("%d",ListLength(L));
//查找需要的长度
printf("请输入需要查找的元素:");
scanf("%d",&i);
GetElem_L(L,i,&e);
printf("\ne=%d",e);
//删除所需要的元素
printf("请输入需要删除的元素位置:");
scanf("%d",&i);
ListDelete(L,i,&e);
printf("\ne=%d",e);
printLnode(L);
//清除链表
Clear_L(&L);
//实现尾插操作
printf("请输入需要初始化的元素数量:");
scanf("%d",&i);
CreateList_R(&L,i);
printf("\n");
printLnode(L);
//删除所需要的元素
printf("请输入需要插入的元素位置:");
scanf("%d",&i);
printf("请输入需要插入的元素:");
scanf("%d",&e);
ListInsert_L(&L,i,e);
printLnode(L);
return 0;
}
上面的单链表代码是按照王卓老师的一步一步实现的。以下是对其中的一些实现的解释说明。
定义
首先是对链表的结构怎么定义的:
其实就是抽象的 数据域与代码域怎么用c表达?
typedef int ElemType;
typedef int Status;
/*
typedef struct{
char num[8];
char name[8];
int score;
}ElemType;
*/
typedef struct Lnode{
ElemType data;
struct Londe *next;
}Lnode,*Linklist;
这里我们可以看到其实就是一个 结构体。结构体中的第一个元素表示数据域,可以任意定义,第二个元素表示下一个元素的指针(链条),两者相组合构成了一个链表。
初始化头结点
Status InitList_L(Linklist *L)
{
*L = (Linklist)malloc(sizeof(Lnode));
(*L)->next =NULL;
return OK;
}
初始化第一步:分配空间。没有空间资源就如巧妇无米,炊不起来。所以第一步我们分配给L一个空间。此处我们要用Linklist * 的原因在于我们在c中无引用参数,所以只能用传地址的方式进行平替。
需要注意的是(*L)这个地方要用到*L。我在之前的文章中有提到关于指针的一些知识,这里就不再赘述,其目的是为了让L的真身得到地址的分配以及真身的next指针设置为空。
初始化第二步:设置我们所构成的数据结构的参数。
因为此处我们设置的是头结点,所以其实也就是设置next指针为NULL。
判断表是否为空?
Status void_L(Linklist L)
{
if (L->next)
{
return ERROR;
}
else return OK;
}
思想:看一个链表是否为空,就看头结点的next是否为空即可。
删除一个链表(头都不剩)
Status Destroy_L(Linklist *L)
{
Lnode *p;
while ((*L))
{
p = *L;
(*L) = (*L)->next;
free(p);
}
}
思想:我们要是想将链表删除,那么我们需要从头结点开始一个一个删除。
这是一个基本思路。那我们咋删呢?只用一个头指针能不能删呢?我们可以发现,如果只用一个头指针删除,我们只能删除一项,因为我们删了一个后,没有可以指向下一个链表结构的指针了。所以我们需要定义一个指针,替我们擦屁股(或者替我们带路)。
此处就是,等L存好下一个指针的时候,p将前一个指针释放。
清空指针
//销毁整个链表的时候,需要将整个链表包括头结点全部删除,清空时需要进行保留。
Status Clear_L(Linklist *L)
{
Lnode* p,*q;
p =(*L)->next;
q = p->next;
while (q)
{
free(p);
p = q;
q = q->next;
}
free(p);
(*L)->next = NULL;
return OK;
}
此处是一个清空指针的操作。此处我们需要保留头节点。所以说之前我们头结点动,现在不能动了。那怎么办呢?我们再定义一个指针就行了嘛。
此处需要注意的是停止条件。我试过停止条件是p的时候,下面的代码会有报错,我猜的可能是因为q =NULL。此时让q = q->next查无此词所以就失败了。
返回链表长度
int ListLength(Linklist L)
{
Lnode *p;
p = L->next;
int i = 0;
while (p)
{
i++;
p = p->next;
}
return i;
}
就是数数,但要注意条件,捋一捋逻辑:首先将p指向首元结点,若首元结点存在即让j++,再将p指向下一个节点。如果说下一个节点非空再(此处循环)执行j++ 。。。。。
头插法plus尾插法
//头插法:
void CreateList_H(Linklist *L,int n)
{
*L = (Lnode*)malloc(sizeof(Lnode));
(*L)->next = NULL;
for(int i=n;i>0;i--)
{
Lnode *p = (Lnode*)malloc(sizeof(Lnode));
printf("请输入第%d个元素",n-i+1);
scanf("%d",&(p->data));
p->next = (*L)->next;
(*L)->next = p;
}
}
//尾插法:
void CreateList_R(Linklist *L,int n)
{
(*L) = (Lnode*)malloc(sizeof(Lnode));
(*L)->next = NULL;
//初始化
Lnode *r = *L;
for(int i = 0; i < n; ++i)
{
Lnode *p = (Lnode*)malloc(sizeof(Lnode));
p->next = NULL;
r->next = p;
r = p;
printf("请输入第%d个元素",i+1);
scanf("%d",&(p->data));
}
}
其实这两种方法都包含了对链表L的初始化。前两行便是对链表的初始化。
头插法就是在头结点处进行插入。反正输入新元素都先记住!!!先创造新元素,即先分给新元素空间。后面再管指针。
头插法:
先将头结点的指针给新节点的next,再将头结点的next等于最新的节点。
打个不太适合的比喻:本来是(老板)直接管理(员工),现在多了(经理),那老板先将权利下放给经理们,然后再掌控经理。
尾插法:
一直在最尾部插入节点,我们需要一个指向尾部的指针 r 。该指针一开始跟 L 一样指向头结点。
r->next 其实就等于下一个新元素的位置。我们需要将此位置信息给新的值的地址,再将r移到新的元素的位置。所以需要对指针的操作只有两个,一个是链接上,第二个是将 r 移到最新元素的地址处。