随着学习的不断深入,我新接触到了C语言的一些较为复杂的领域。不得不说,对现阶段的我而言想要较好的使用它们是一项挑战。
但知识的掌握、难点的突破,是可以建立在不断记录并且巩固的基础之上的,所以我产生了写下笔记的想法。此后我想要从 1.定时分享课程作业中的较难题目,2.及时记录知识难点的笔记,3.定期发表新写的对我而言较难的项目. 三方面分享博客,以此记录我编程历程的进步。
以下是我了解链表后的一些思考。或许有部分错误或不足,敬请指正。
#include<stdio.h>
#include<stdlib.h>//播种随机函数
#include <time.h>//查用时间
#define MAXSIZE 20
#define ERROR 0//用于自定义Status函数的表达
#define OK 1
typedef int Status;
typedef int ElemType;
struct Node{
//数据域
ElemType data;
//结构体内含一个指向同类型的指针,作为链表的链
//指针域
struct Node * next;
};
//把链点重命名,提高了编写时的容错率
typedef struct Node *LinkList;
//头指针:存放头结点(若无,则为首元结点)地址,头指针的名字即为链表名字
//头结点:非必要 有利于判断链表性质和操作 头结点指针域为空,则链表为空
//首元结点:链表真正意义上的首个结点 存放着链表第一个数据
//初始化链表 为头指针指向 即头结点分配内存 其指针域为空
void InitList(LinkList *L){
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
}
//检验链表是否为空 即头结点指向是否为空(是否有首元结点)
bool ListEmpty(LinkList L){
if(L->next)
return false;
else
return true;
}
//清空链表
void ClearList(LinkList *L){
LinkList p,q;
//将头结点指向空间(首元结点)赋给p
p = (*L)->next;
while(p){
//用p指向的结点(初次即第二结点)赋给q
q = p->next;
//释放p的内存
free(p);
//q指向的结点赋给p 整个操作即将p不断深移,同时清除此前结点
p = q;
}
//检验头结点指向空间即首元结点是否存在,若为空,则整个链表为空
(*L)->next = NULL;
}
//计算链表长度
int ListLength(LinkList L){
int i = 0;
//定义一个工作指针,用来遍历链表 从头结点指向的首元结点开始
LinkList p = L->next;
//通过循环使工作指针不断深移,直到其指向空
while(p){
//上一个结点判断不为空,链表长加一
i++;
p = p->next;
}
return i;
}
//查找指定结点位置的元素(给位置,找数据)
Status GetElem(LinkList L,int i,ElemType *e){
//表示位置的变量cnt 此时已经与链表结点相对应
int cnt = 1;
//设置工作指针p用来遍历链表 从头结点指向的首元结点开始
LinkList p = L->next;
//p沿着链表移动同时cnt++,数据和位置改变相统一
//当cnt==i,循环结束
while(p && cnt < i){
p = p->next;
cnt++;
}
//若在cnt==i的位置工作指针指向空,即不存在该结点
if(!p)
return ERROR;
//若指向非空,将数据存入同类型指针e指向的空间
*e = p->data;
return OK;
}
//查找元素 寻得结点位置(给数据,查位置)
int LocateElem(LinkList L,ElemType e){
int cnt = 0;
//定义工作指针p遍历链表 从头结点指向的首元结点开始
LinkList p = L->next;
while(p){
//位置与结点相统一
cnt++;
//判断当前结点数据是否与给定的相同,若同则返回位置,否则继续移动
if(p->data == e)
return cnt;
//移动工作指针
p = p->next;
}
//若遍历结束,仍然没有找到与给定数据相同的结点,返回无
return 0;
}
//插入元素
Status ListInsert(LinkList *L,int i,ElemType e){
//定义工作指针 p,中间指针s
LinkList p,s;
//p为头指针指向的头结点的指针域
p = (*L);
int cnt = 1;
//不断移动工作指针p p实际上是比cnt前一个位置的结点
while(p && cnt < i){
cnt++;
p = p->next;
}
//若p不存在,则链表遍历结束,无法插入指针
if(!p)
return ERROR;
//为指针s分配内存空间 s指向数据为需要插入的e
s = (LinkList)malloc(sizeof(Node));
s->data = e;
//s指向的下一结点代替p指向的下一结点
s->next = p->next;
//p指针域指向s,此时即保持p不变 s插入了p之后,p原本下一结点之前
p->next = s;
return OK;
}
//删除元素
Status ListDelete(LinkList *L,int i,ElemType *e){
int cnt = 1;
//设置工作指针p,p从头指针指向的头结点开始
//设置中间结点q
LinkList q,p;
p = (*L);
//此时 p 为头结点,p->next为首元结点
//判断p的下一结点不为空且未达目标位置,执行循环
while( p->next && cnt < i){
p = p->next;
cnt++;
}
//循环结束时,cnt==i 实际上是p的下一结点
//若p的下一结点为空,返回错误
if(!(p->next))
return ERROR;
//若非空,则将p指向的结点赋给q
q = p->next;
//将q的下一结点赋给p的下一结点,代替了p原本的下一结点
p->next = q->next;
//此时q结点实际上被废弃 q的原上一结点p已经绕过q指向了q的下一结点
*e = q->data;
//释放结点q的内存
free(q);
return OK;
}
//遍历链表 输出元素
Status ListTraverse(LinkList L){
//设置工作指针p为头结点指向的首元结点
LinkList p = L->next;
//若p当前结点非空,输出数据并向后移动
while(p){
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return OK;
}
//头插法 建立链表
void CreateListHead(LinkList *L,int n){
LinkList p;
srand(time(0));
//为头指针指向的头结点分配空间 并指向空(即无首元结点)
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
//开始生成新节点建立链表 不断生成新的首元结点代替原首元结点
for(int i = 0 ; i < n ; i++){
//建立一个结点并给其数据域赋值
p = (LinkList)malloc(sizeof(Node));
p->data = rand()%100 + 1;
//将头结点指向的首元结点赋给p的下一结点
p->next = (*L)->next;
//将头结点指向p 此时p变成了新的首元结点
(*L)->next = p;
}
}
//尾插法 建立链表
void CreateListTail(LinkList *L,int n){
//设置工作指针p,r p用于不断生成新结点,r用于保存结点并使p弃置
LinkList p,r;
srand(time(0));
//为头指针指向的头结点分配空间 并赋给r
*L = (LinkList)malloc(sizeof(Node));
r = *L;
//开始循环 分配新的空间给结点p 并给该空间赋数据
for(int i = 0 ; i < n ; i++){
p = (LinkList)malloc(sizeof(Node));
p->data = rand()%100 + 1;
//r的下一结点指向p
r->next = p;
//原本的p赋给r,r代替p,将p弃置
r = p;
}
//循环结束 p处于弃置状态,r位于最后一结点,需要让其指针域指向空
r->next = NULL;
}
//主函数 此处对之前的函数进行检验
int main(){
ElemType e;
Status r;
LinkList L;
InitList(&L);
printf("初始化L后:ListLength(L) = %d \n",ListLength(L));
for(int i = 1;i <= 10;i++){
r = ListInsert(&L,1,i);
}
printf("在表头依次插入1-10后:L.data = ");
ListTraverse(L);
printf("ListLength(L) = %d \n",ListLength(L));
r = ListEmpty(L);
printf("L是否为空:r = %d (1:是 0:否)\n",r);
ClearList(&L);
printf("清空L后,ListLength(L) = %d \n",ListLength(L));
r = ListEmpty(L);
printf("L是否为空:r = %d (1:是 0:否) \n",r);
for(int i = 1; i <= 10 ; i++){
ListInsert(&L,i,i);
}
printf("在L的表尾依次插入1-10后,L.data = ");
ListTraverse(L);
printf("ListLength(L) = %d\n",ListLength(L));
ListInsert(&L,1,0);
printf("在表头插入0后:L.data = ");
ListTraverse(L);
printf("ListLength(L) = %d\n",ListLength(L));
GetElem(L,5,&e);
printf("第5个元素的值为%d\n",e);
for(int i = 3 ; i <= 4 ;i++){
r = LocateElem(L,i);
if(r)
printf("第%d位元素的值为%d\n",r,i);
else
printf("不存在值为%d的元素\n",i);
}
int l = ListLength(L);
for(int i = l + 1; i >= l ; i--){
r = ListDelete(&L,i,&e);
if(r == ERROR)
printf("删除第%d个元素失败\n",i);
else
printf("删除第%d个元素值为: %d\n",i,e);
}
printf("依次输出L的元素: ");
ListTraverse(L);
r = 5;
ListDelete(&L,r,&e);
printf("删除的第%d个元素的值为:%d\n",r,e);
printf("依次输出L的元素: ");
ListTraverse(L);
ClearList(&L);
printf("\n清空L后,ListLength = %d\n",ListLength(L));
CreateListHead(&L,20);
printf("整体创建L的元素(头插法): ");
ListTraverse(L);
ClearList(&L);
printf("\n清空L后,ListLength = %d\n",ListLength(L));
CreateListTail(&L,20);
printf("整体创建的元素(尾插法):");
ListTraverse(L);
return 0;
}
本篇学习笔记基于《关于链表,看这一篇就够了!(新手入门)》(博主:小吕编码),如有侵权,请联系删改。