前言:
最近在学习数据结构中的链表,可能是刚刚接触的吧,感觉有点难度,它结合了指针及结构体,所以,当你在看这篇博客时,你应该对C语言的指针及其结构体要有了解,不然的话,你会在看这篇博客时,会说,这个博主在写什么乱七八糟的东西啊,不知道怎么敢发。哈哈,我可不想听到大家对我说这样的话。鉴于此,我会将每一步讲的十分仔细的。
先来了解一下基础的知识吧。
好了,接下来实战。
1.1.定义相关的结构体
1.1.1.代码:
#define OK 1
#define ERROR 0
#define TURE 1
#define FALSE 0
#define MAXSIZE 20
typedef int Status; //Status为函数的类型
typedef int ElemType;
typedef struct Node{
ElemType data;
struct Node *next;
};
typedef struct Node *LinkList; //定义LinkList
1.2.单链表的读取
在单链表中,由于第i个元素没办法一开始就知道,必须得从头开始找。因此,对于单链表实现第i个元素的数据的操作GetElem。
在读取第i个数据的算法思路:
- 申明一个结点指向链表的第一个结点。
- 定义一个j,当j<i时,让p的指针向后移动,不断指向下一结点。
- 定义一个数e,并将找到的第i个数据返回e。
- 如果查找不成功,则表明第i个数据不存在。
1.2.1.代码算法:
Status GetElem(LinkList L,int i,ElemType *e){
int j=1;
LinkList p //申明一个结点
p=L->next; //让p指向第一个结点
while(p&&j<i){
p=p->next; //每次循环都将指向下一个结点,直到直到第i个结点
++j;
}
if(!p||j>i){
return ERROR;
}
*e=p->data; //用e的返回查找的数
return OK;
}
上述代码的算法就是遍历每一个元素,基本上和刚开始学的数组是差不多的,然而不同的是这里是用指针的指向去遍历元素,而数组是用索引去遍历元素。
1.3.单链表的插入
在单链表里,我们就不必像数组那样将一些数组中的元素后移或者是前移,而是,将指针的指向要插入的元素,而插入的元素指针指向它前面元素在它未插入前指向的元素即可,所以,在这一方面链表显然更为灵活。
1.3.1.代码算法:
//单链表L存在,i表示在在第i个数前面插入数为e的元素
Status ListInsert(LinkList *L,int i,ElemType e){
int j=1;
ListLink p,s;
p=*L;
while(p&&j<i){
p=p->next;
++j;
}
if(!p||j>i){
return ORRER;
}
s=(LinkList)malloc(sizeof(Node)); //申请新的结点;
s->data=e;
s->next=p->next;
p->next=s;
return OK;
}
1.4.单链表的删除
当然,对于单链表的删除,我们现在应该有一定的意识了吧,在这里,我们可以将要删除的元素的前一元素指向其的后一位元素,简单的说就是,指针在指向下一元素时,跳过要删除的元素。在这里,是不是为链表的灵活连连叫好。
1.4.1代码算法:
Status ListDelete(LinkList *L,int i,ElemType *e){
int j=1;
LinkList p,q;
p=*L;
while(j<i&&p->next){ //这里判断的是p->next是因为要指向被删除元素的后一位元素,所以要判断它是否存在
p=p->next;
++j;
}
if(j>i||!(p->next)){
return ORRER;
}
q=p-next;
p->next=q->next;
*e=q->data;
free(q); //回收此结点
return OK;
}
1.5.单链表的整表创建
创建单链表的过程,其实就是动态创建一个链表的过程依次建立结点,并插入元素,在这里,我介绍两个方法插入元素,一个是头插法,一个是尾插法。对于,头插法,就是将每个插入的元素放在第一个结点,然后依次类推,当然,尾插法,就是将每个要插入的元素放在最后一个结点,以此类推。
头插法:
尾插法:
1.5.1.代码算法:
//头插法
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;
(*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=(LinkList)malloc(sizeof(Node));
p->data=rand()%100+1;
r->next=p;
r=p;
}
r->next=NULL; //当前链表结束
}
想到整表的创建,那么肯定有整表的删除吧。
1.6.单链表的整表删除
删除单链表是十分有用的,它可以腾出空间留给其他程序或软件的使用。
其算法思路是蛮简单的,就是一个一个的释放结点。
1.6.1.代码算法:
Status ClearList(LinkList *L){
LinkList p.q;
p=(*L)->next;
while(p){
q=p->next;
free(p); //释放结点
p=q;
}
(*L)->next=NULL;
return OK;
}
上面我是分步给大家伪代码的,以方便大家能够理解,当然啦,接下来,我给出一些其在main()函数被调用时的用法,不过在调用其时,还要去定义一些函数,以便输出元素。
Status vis(ElemType e){
printf("%d ",e);
return OK;
}
// 定义单链表长度的函数
int ListLength(LinkList L){
int i=0;
LinkList p=L->next;
while(p){
p=p->next;
i++;
}
return i;
}
//定义一个将单链表中每个元素输出的函数
Status ListPrint(LinkList L){
LinkList p=L->next;
while(p){
vis(p->data);
p=p->next;
}
printf("\n");
return OK;
}
2.1.主函数代码:
int main(){
LinkList L; //申明一个单链表
ElemType e; //元素的值
Status i; //相当于int i;
int k,j;
for(k=1;k<=10;k++){
ListInsert(&L,1,k);
}
printf("插入后的链表为:");
ListPrint(L);
printf("单链表的长度为:%d\n",ListLenght(L));
ClearList(&L);
printf("清空后,其单链表的长度为:%d",ListLenght(L));
for(j=1;j<=10;j++){
ListInsert(&L,j,j);
}
printf("插入数据后,单链表为:");
ListPrint(L);
printf("单链表的长度为:%d\n",ListLength(L));
GetElem(L,6,&e);
printf("第6个元素为;%d\n");
ListDelete(&L,3,&e);
ListPrint(L);
ClearList(&L);
Create(ListHead(&L,20);
printf("头插法创建单链表的元素为:");
ListPrint(L);
ClearList(&L);
CreateListTail(&L,20);
printf("尾插法创建单链表的元素为:");
ListPrint(L);
return 0;
}
好了,今天的博客就要在这里结束了,不过在下一个博客我还会介绍静态链表,循环链表的相关知识。不过呢,我也十分感谢CSDN上有些大牛的博客让我学会了单链表。最后,希望大家能够多多支持。😄😄😄
参考文献:
《大话数据结构》