线性表的链式存储结构——单链表,手把手注释代码讲解

    我通过C语言代码实现了单链表的初始化、判断是否为空、清空线性表、查询、查找、插入、删除、返回长度等操作,并写了主函数测试编译运行通过。

    我对代码进行了手把手的单行注释,及其适合单链表各项操作的理解和代码记忆,现将代码分享如下.


#include <stdio.h>
#include <stdlib.h>
#define bool int
#define true 1
#define false 0
#define ok 1
#define error 0

typedef int ElemType;
typedef int Status;

typedef struct Node {//线性表的单链表存储结构 
	ElemType data;//数据域 
	struct Node *next;//指针域 
} Node;

typedef struct Node* LinkList;//定义LinkList:struct Node* 

//函数声明 
void InitList(LinkList* L);
bool ListEmpty(LinkList L);
void ClearList(LinkList* L);
Status GetElem(LinkList L, int i, ElemType *e);
int LocateElem(LinkList L, ElemType e);
Status ListInsert(LinkList *L, int i, ElemType e);
Status ListDelete(LinkList *L, int i, ElemType *e);
int ListLength(LinkList L);
void PrintList(LinkList L);

int main(int argc, char *argv[]) {//在主函数里进行测试 
	ElemType e;//定义变量e用来保存被删除的数据 
	LinkList list;//定义一个链表list 
	InitList(&list);//初始化它 
	printf("链表是否为空? %d\n", ListEmpty(list));//使用ListEmpty判断链表是否为空,此时因为链表没有元素,所以得到1表示为空 
	ListInsert(&list, 1, 1);//使用ListInsert插入3个节点,数据分别为1,2,3;因为都是从第一个位置插入元素,相当于头插法 
	ListInsert(&list, 1, 2);
	ListInsert(&list, 1, 3);
	PrintList(list);//打印一下当前链表,输出3,2,1 
	printf("数据2的位置是 %d\n", LocateElem(list, 2)); //使用LocateElem查找数据为2的元素,并打印其位置 
	printf("数据4的位置是 %d\n", LocateElem(list, 4)); //使用LocateElem查找数据为4的元素,并打印其位置,由于此时链表中没有数据4,所以打印的值为0 
	printf("链表的长度: %d\n", ListLength(list));//使用ListLength打印链表的长度,此时为3 
	ListInsert(&list, 1, 4);//使用ListInsert继续插入两个元素 
	ListInsert(&list, 1, 5);
	PrintList(list);//此时打印的值为5,4,3,2,1 
	printf("数据4的位置是 %d\n", LocateElem(list, 4));//此时可以使用LocateElem查找到数据4的位置 
	printf("链表是否为空? %d\n", ListEmpty(list));//使用ListEmpty判断链表是否为空,因为已经有5个元素,所以打印0 
	printf("链表的长度: %d\n", ListLength(list));//此时链表的长度为5
	ListInsert(&list, 3, 100);//在中间插入一个元素100 
	PrintList(list);//打印显示5,4,100,3,2,1 
	ListDelete(&list, 3, &e);//使用 ListDelete删除第三个元素即100 
	PrintList(list);//再次打印发现100已经被删除,显示5,4,3,2,1 
	printf("被删除的元素为:%d\n", e);//通过保存刚刚删除的元素的变量e打印删除的元素的数据 
	ClearList(&list);//清空链表 
	printf("链表是否为空? %d\n", ListEmpty(list));//此时链表为空,打印1 
	printf("链表的长度: %d\n", ListLength(list));//被清空后链表的长度为0 
	return 0;
}

void InitList(LinkList* L) {//初始化链表 
	*L = (LinkList)malloc(sizeof(Node));//为单链表分配头结点 
	(*L) -> next = NULL;//指针域设置为空 
} 

bool ListEmpty(LinkList L) {//判断链表是否为空 
	return L->next == NULL;//如果头结点的指针域为空,则表示是空链表 
} 

void ClearList(LinkList* L) {//清空链表 
	LinkList p,q;//定义两个辅助指针 
	p = (*L)-> next;//p指向第一个结点,从第一个结点开始删除 
	while (p) {//没到表尾就继续循环 
		q = p->next;//q来记录下一个结点 
		free(p);//删除p指向的结点 
		p = q;//把q指向的下一个结点赋值给p 
	}
	(*L)->next = NULL;//指针域设置为空 
} 

Status GetElem(LinkList L, int i, ElemType *e) {//将线性表L中的第i个元素值返回给e 
	int j;//定义计数器j 
	LinkList p;//定义辅助指针p 
	p = L->next;//p指向单链表的第一个结点,从第一个结点开始遍历 
	j = 1;//计数器初始化为1 
	while (p && j < i) {//如果没到表尾或者计数器不等于i就继续循环 
		p = p->next;//p指向下一个结点 
		++j;//计数器自加 
	}
	if (!p || j > i) {//到表尾或非法值,j > i 用于非法值处理,比如传入i = 0 
		return error;//返回错误值 
	}
	*e = p->data;//把得到的值给参数指针e指向的空间 
	return ok;//返回成功 
}
 
int LocateElem(LinkList L, ElemType e) {//在线性表L 中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则,返回0表示失败. 
	int j;//定义计数器j 
	LinkList p;//定义辅助指针p 
	p = L->next;//p指向单链表的第一个结点,从第一个结点开始查找 
	j = 1;//计数器初始化为1 
	while (p && p->data != e) {//如果没到表尾或者还没有查询到给定元素继续循环 
		p = p->next;//p指向下一个结点 
		++j;//计数器自加 
	}
	if (!p) {//如果到表尾 
		return 0;//返回0表示查找失败 
	}
	return j;//返回查找到的元素的序号,等于计数器j的值 
}
 
Status ListInsert(LinkList *L, int i, ElemType e) {//在链表L的第i个位置插入元素e 
	int j;//定义计数器j 
	LinkList p, s;//定义两个辅助指针p,s 
	p = *L;//p指向链表的头结点,插入操作需要找到指定位置的前一个位置,头结点是第一个位置的前驱 
	j = 1;//计数器初始化为1 
	while (p && j < i) {//如果没到表尾或者j<i就继续循环,因为p指向头结点,所以当j=i时p指向第i个结点的前一个结点 
		p = p->next;//p指向后一个结点 
		++j;//计数器自加1 
	}
	if (!p || j > i) {//如果到达表尾或者输入非法,j>i 用于非法值处理,比如传入i=0 
		return error;//返回错误 
	} 
	s = (LinkList)malloc(sizeof(Node));//给s分配空间 
	s->data = e;//s数据域保存元素e, 
	s->next = p->next;//s指向p的下一个结点或者NULL,即原来第i个位置的结点或者尾部 
	p->next = s;//原来第i个位置的前一个结点p指向新结点s 
	return ok; //返回成功 
}
 
Status ListDelete(LinkList *L, int i, ElemType *e) {//删除链表L的第i个结点,并用e返回其值 
	int j;//定义计数器 
	LinkList p, q;//定义两个辅助指针p,q 
	p = *L;//p指向头结点,因为删除操作需要找到需要删除的结点的前一个结点,头结点是第一个结点的前一个结点 
	j = 1;// 初始化计数器为1 
	while (p->next && j < i) {//当到达最后一个元素,或者找到第i个结点的前一个结点时结束循环 
		p = p->next;//p指向下一个节点 
		++j;//计数器自加1 
	}
	if (!(p->next) || j > i) {//如果到达最后一个元素或者非法输入,j>i 用于非法值处理,比如传入i=0 
		return error;//返回错误 
	}
	q = p->next;//q指向要删除的结点,即p的下一个结点 
	p->next = q->next;//前一个结点p越过要删除的结点q指向q的下一个结点 
	*e = q->data;//把要删除的结点p的数据给参数e 
	free(q);//删除结点q 
	return ok;//返回成功 
}
 
int ListLength(LinkList L) {//返回链表L的长度 
	int j;//定义计数器j 
	LinkList p;//定义辅助指针P 
	p = L->next;//p指向第一个结点 
	j = 0;//计数器初始化为0 
	while (p) {//当到表尾时退出循环 
		p = p->next;//p指向下一个结点 
		++j;//计数器自加1 
	}
	return j;//返回链表元素的个数,即计数器j的值 
} 

void PrintList(LinkList L) {//遍历打印链表 
	int i;//定义循环变量i 
	ElemType e;//定义一个数据变量用来保存查找到的值 
	for (i = 1; i < ListLength(L) + 1; i++) {//循环从1开始,到最后一个元素结束,即链表的长度值相同位置的元素 
		if (GetElem(L, i, &e)) {//如果查找第i个元素成功 
			printf("%d ",e);//打印被保存在e中的元素数据 
		}
	}
	printf("\n"); 
}

 

发布了10 篇原创文章 · 获赞 15 · 访问量 3万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览