数据结构学习笔记(一)—— 链表

本文是数据结构学习笔记的第一部分,主要介绍链表的基本结构和思想,包括链表如何解决数组的局限性,以及C语言中实现链表的结构体、宏定义和相关方法,如初始化、添加、查找、修改、删除元素等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构学习笔记(一)—— 链表

(先在这里悄悄打个小广告 !^_^)
我看的是青岛大学王卓老师的数据结构视频,真的讲的非常好,每个点都讲得很仔细
B站链接:数据结构与算法基础(青岛大学-王卓)

链表

链表是一种很基础的数据结构,也可以利用链表构造其他的高级数据结构,掌握其中的一些思想也会方便学习之后的高级数据结构

一、基本结构与思想

传统的存储方式很容易想到用数组来实现。
用数组存储数据优点有很多比如:

  1. 创建起来比较方便
  2. 因为每个元素存储在连续的一片内存空间上,这样读取某个特定位置的元素就可以通过基地址+偏移量的方式很快的取出来

但是相应的缺点也是存在的

  1. 有时内存中没有大片的连续内存空间,就存不了较大规模的元素
  2. 要从数组中删除一个元素,就需要将其后面的元素一个一个向前搬移一格,会浪费大量的时间

因此就有了链表这样的一种思路,即每个元素不仅存储自身的信息,还存储一个指针,指向它的下一个元素,这样只要拿到了这个元素,就可以根据它的下一个元素指针找到下一个元素存放的内存位置。
最后一个元素之后没有元素了,所以将这个元素的下一个元素指针指向一个空值NULL
在这里插入图片描述

就像C语言声明数组一样,数组名表示了数组的首地址,链表也可以声明一个指针,指向链表的第一个元素,这样拿到这个指针就可以依次访问到数组中的各个元素了

在这里插入图片描述

拓展

在创建一个链表时可以把第一个节点作为头节点,在其中存储链表相关的信息。
在这里插入图片描述

二、C语言实现

1.链表结构体

每个链表元素我们称之为节点,每个节点包含两个成员,存储自身信息的数据域,存储下一个元素位置的指针域

typedef struct Lnode{
	int val;
	struct Lnode *next;
}Lnode, Linklist;
2.错误宏定义
宏定义内容代表含义
INDEX_OVERFLOW所给定位置超出链表范围-1
LINKLIST_EMPTY链表为空400
#define		INDEX_OVERFLOW   	-1
#define	 	LINKLIST_EMPTY		-400
3.链表相关方法

初始化链表
判断链表是否为空
在链表末尾添加元素
查找链表指定位置的值
查找指定值在链表中出现的位置, 返回第一次出现的下标
修改指定位置元素的值
在指定位置之后插入元素
删除指定位置元素
打印链表

1.初始化链表
将新建立的链表下一个元素指向NULL 我是使用将第一个元素节点作为头节点,在其中存储链表的长度这一信息
/*
 * 初始化链表
 * @param  {Linklist *L} 
 * @rerurn {void}
*/
void initLinkList(Linklist *L){
	L->val = 0;
	L->next = NULL;
	return; 
}
2.判断链表是否为空
/*
 * 判断链表是否为空 
 * @param  {Linklist *L} 
 * @rerurn {int (1-空 0-非空)}
*/
int isLinkListEmpty(Linklist *L){
	if(L->next) return 0;
	return 1;
}
3.在链表末尾添加元素
/*
 * 在链表末尾添加元素 
 * @param  {Linklist *L,
 *			int num} 
 * @rerurn {void}
*/
void addLinkListTail(Linklist *L,int num){
	Lnode *newLnode = (Linklist *)malloc(sizeof(Lnode));
	newLnode->val = num;
	newLnode->next = NULL;
	Linklist *p = L;
	while(p->next){
		p = p->next;
	}
	p->next = newLnode;
	L->val++;
}
4.查找链表指定位置的值
/*
 * 查找链表指定位置的值 
 * @param  {Linklist *L,
 *			int pos,
 *          int *e} 
 * @rerurn {void}
*/
void searchLinkListIndexOf(Linklist *L,int pos,int *e){
	int i=0;
	Linklist *p = L;
	if(!L->next) return;
	while(p->next){
		p = p->next;
		if(i==pos){
			*e = p->val;
			return;
		}
		i++;
	}
	exit(INDEX_OVERFLOW);
}
5.查找指定值在链表中出现的位置, 返回第一次出现的下标
/*
 * 查找指定值在链表中出现的位置,返回第一次出现的下标 
 * @param  {Linklist *L,
 *			int num,
 *          int *pos} 
 * @rerurn {void}
*/
void searchLinkList(Linklist *L,int num,int *pos){
	Linklist *p = L->next;
	int i = 0;
	while(p->next){
		if(p->val==num){
			*pos = i;
			return;
		}
		i++;
		p = p->next;
	}
	if(p->val==num){
		*pos = i;
		return;
	}
	*pos = -1;
}
6.修改指定位置元素的值
/*
 * 修改指定位置元素的值 
 * @param  {Linklist *L,
 *			int pos, 
 *			int num} 
 * @rerurn {void}
*/
void modifyLinkListVal(Linklist *L,int pos,int num){
	int i=0;
	Linklist *p = L;
	while(p->next){
		p = p->next;
		if(i==pos){
			p->val = num;
			break;
		}
		i++;
	}
	if(i==pos){
		p->val = num;
	}
	return;
}
7.在指定位置之后插入元素
/*
 * 在指定位置之后插入元素 
 * @param  {Linklist *L,
 *          int pos,
 *			int num} 
 * @rerurn {void}
*/
void insertLinkLisAtIndex(Linklist *L,int pos,int num){
	if(pos==L->val-1){
		addLinkListTail(L,num);
		return;
	}
	Lnode *newNode = (Lnode *)malloc(sizeof(Lnode));
	Linklist *p = L->next,*q = NULL;
	newNode->val = num;
	int i = 0;
	while(p->next){
		if(i==pos){
			q = p->next;
			newNode->next = q;
			p->next = newNode;
			L->val++;
			return;
		}
		p = p->next;
		i++;
	}
	exit(INDEX_OVERFLOW);
}
8.删除指定位置元素
/*
 * 删除指定位置元素 
 * @param  {Linklist *L,
 *          int pos} 
 * @rerurn {void}
*/
void removeLinkListAtIndex(Linklist *L,int pos){
	if(isLinkListEmpty(L)) exit(LINKLIST_EMPTY);
	if(pos<0 || pos>L->val-1) exit(INDEX_OVERFLOW);
	Linklist *p = L->next,*q = NULL;
	if(pos==0){
		L->next = L->next->next;
		L->val--;
		free(p);
		return;
	}
	if(pos==L->val-1){
		while(p->next){
			q = p;
			p = p->next; 
		}
		q->next = NULL;
		free(p);
		L->val--;
		return;
	}
	int i = 0;
	while(p->next){
		if(i==pos){
			q->next = p->next;
			L->val--;
			free(p);
			return;	
		}
		i++;
		q = p;
		p = p->next;
	}
	exit(INDEX_OVERFLOW);
}
9.打印链表

其实这是一个遍历链表的方法,可以传入一个回调函数,用于对每个遍历到的元素执行一定的操作,但是我这里没有写,有机会再完善吧!

/*
 * 打印链表  eg:  val1->val2->val3->NULL
 * @param  {Linklist *L} 
 * @rerurn {void}
*/
void printLinkList(Linklist *L){
	Linklist *p = L;
	if(!p->next){
		printf("NULL\n");
		return;
	}
	while(p->next){
		p = p->next;
		printf("%d->",p->val);
	}
	printf("NULL\n");
	return;
}

三.测试

int main(void){
	Linklist L;
	int a;
	initLinkList(&L);
	
	addLinkListTail(&L,1);
	addLinkListTail(&L,2);
	addLinkListTail(&L,3);
	addLinkListTail(&L,4);
	addLinkListTail(&L,5);
	addLinkListTail(&L,6);
	addLinkListTail(&L,7);
	addLinkListTail(&L,8);
	addLinkListTail(&L,9);
	printLinkList(&L);
	printf("length: %d\n",L.val);
	
	modifyLinkListVal(&L,4,69);
	printLinkList(&L);
	
	searchLinkListIndexOf(&L,1,&a);
	insertLinkLisAtIndex(&L,2,38);
	printLinkList(&L);
	printf("length: %d\n",L.val);
	
	removeLinkListAtIndex(&L,8);
	printLinkList(&L);
	removeLinkListAtIndex(&L,0);
	printLinkList(&L);
	printf("length: %d\n",L.val);
	searchLinkList(&L,3,&a);
	
	printf("Index is:%d\n",a);
	printLinkList(&L);
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值