C/C++知识点笔记:链表

        随着学习的不断深入,我新接触到了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;
}
    

本篇学习笔记基于《关于链表,看这一篇就够了!(新手入门)》(博主:小吕编码),如有侵权,请联系删改。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没啥基础的小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值