C语言提高篇-数据结构~链表

C语言提高篇-数据结构~链表
一、链表(list)
1、基本概念
由若干个地址不连续的节点序列组成,不同的节点之间彼此通过指针连接组成的数据结构,叫做链表

2、基本分类
(1)单向线性链表(重点)
每个节点除了存储数据元素本身之外,还需要保存下一个节点地址的指针,叫做后指针
其中链表中的第一个节点,叫做头节点;
把指向头节点的指针,叫做头指针
链表中最后一个节点,叫做尾结点;
指向尾结点的指针,叫做尾指针;
尾结点中的后指针是一个空指针

(2)单向循环链表
与单向线性链表类似,所不同的是尾结点的后指针指向头节点,首尾相接构成环状结构

(3)双向线性链表
每个节点中除了存储数据元素本身之外,还需要两个指针,其中一个用于记录下一个节点的地址,叫做后指针;另外一个用于记录前一个节点的地址,叫做前指针;
头节点的前指针和尾结点的后指针都是空指针

(4)双向循环链表
与双向线性链表类似,所不同的是让头节点的前指针指向尾结点;尾结点的后指针指向头节点

(5)数组链表
链表中的每一个元素都是一个数组,也就是由数组构成的链表叫做数组链表

(6)链表数组
字符数组 - 数组中的每一个元素都是一个字符
整型数组 - 数组中的每一个元素都是整型变量
结构体数组-数组中的每一个元素都是结构体变量
指针数组 - 数组中的每一个元素都是指针变量
链表数组 - 数组中的每一个元素都是一个链表

(7)二维链表
二维数组 - 数组中的每一个元素都是一个一维数组 的 一维数组
二维链表 - 链表中的每一个元素都是一个链表的 链表,也就是由链表构成的链表

二、编程实现单链表的各种操作

//编程实现单链表的各种操作
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

//定义节点的数据类型
typedef struct Node
{
	int data;//存放数据元素本身
	struct Node* next;//记录下一个节点地址
}Node;

//定义链表的数据类型
typedef struct
{
	Node* head;//记录头节点的地址
	Node* tail;//记录尾结点的地址
	int cnt;//记录节点的个数
}List;

//实现向头节点位置插入元素的功能
void push_head(List* pl,int data);
//实现遍历链表中所有的节点元素值
void travel(List* pl);
//实现创建新结点的函数
Node* create_node(int data);
//实现向链表的尾部追加新结点
void append_tail(List* pl,int data);
//实现判断链表是否为空
bool empty(List* pl);
//实现判断链表是否为满
bool full(List* pl);
//计算链表中元素的个数
int size(List* pl);
//实现向指定的链表中指定的下标位置插入元素
void insert(List* pl,int pos,int data);
//获取头节点的元素值
int get_head(List* pl);
//获取尾结点的元素值
int get_tail(List* pl);
//实现删除头节点的功能
int pop_head(List* pl);


int main(void)
{
	//创建链表,并且进行初始化
	List list;
	list.head = NULL;
	list.tail = NULL;
	list.cnt = 0;

	push_head(&list,11);
	travel(&list);// 11
	push_head(&list,22);
	travel(&list);// 22 11
	push_head(&list,33);
	travel(&list);// 33 22 11

	printf("---------------------------\n");
	append_tail(&list,44);
	travel(&list);// 33 22 11 44
	append_tail(&list,55);
	travel(&list);// 33 22 11 44 55
	
	printf("%s\n",empty(&list)?"链表已经空了":"链表没有空"); //链表没有空
	printf("%s\n",full(&list)?"链表已经满了":"链表没有满"); //链表没有满
	printf("链表中节点的个数是:%d\n",size(&list)); // 5

	printf("--------------------------\n");
	travel(&list);//33 22 11 44 55
	insert(&list,-4,66);
	travel(&list);//33 22 11 44 55 66
	insert(&list,0,77);
	travel(&list);//77 33 22 11 44 55 66
	insert(&list,4,88);
	travel(&list);//77 33 22 11 88 44 55 66
	insert(&list,8,99);
	travel(&list);//77 33 22 11 88 44 55 66 99
	
	printf("--------------------------\n");
	printf("头节点的元素是:%d\n",get_head(&list)); //77
	printf("尾结点的元素是:%d\n",get_tail(&list)); //99
	printf("链表中元素的个数是:%d\n",size(&list)); //9
	
	printf("删除的头节点元素是:%d\n",pop_head(&list));//77
	printf("头节点的元素是:%d\n",get_head(&list));//33
	printf("尾结点的元素是:%d\n",get_tail(&list));//99
	printf("链表中元素的个数是:%d\n",size(&list));//8

	return 0;
}

//获取头节点的元素值
int get_head(List* pl)
{
	// 使用-1表示失败
	return empty(pl)?-1:pl->head->data;
}
//获取尾结点的元素值
int get_tail(List* pl)
{
	return empty(pl)?-1:pl->tail->data;
}
//实现删除头节点的功能
int pop_head(List* pl)
{
	if(empty(pl))
	{
		return -1;//删除失败
	}
	Node* p = pl->head;
	pl->head = p->next;
	int temp = p->data;
	free(p);
	p = NULL;
	//节点的个数减 1
	--pl->cnt;
	return temp;
}

//实现向指定的链表中指定的下标位置插入元素
void insert(List* pl,int pos,int data)
{
	//1.判断坐标的合法性
	if(pos < 0 || pos > size(pl))
	{
		//printf("下标不合法,插入失败\n");
		//return;//结束当前函数
		//当坐标不合法时,默认插入到头节点位置
		//pos = 0;
		//默认追加到链表的尾部
		pos = size(pl);
	}
	//2.创建新结点
	Node* pn = create_node(data);
	//3.根据坐标的位置插入新结点
	//3.1 当pos = 0时,插入到头节点位置
	if(0 == pos)
	{
		push_head(pl,data);
	}
	//3.2 当pos = size(pl),追加到尾部
	else if(pos == size(pl))
	{
		append_tail(pl,data);
	}
	//3.3 当pos为其他值,寻找合适位置插入 
	else
	{
		Node* p = pl->head;
		int i = 0;
		for(i = 1; i < pos; i++)
		{
			//相对于pos=1时多出来的next执行完毕
			p = p->next;
		}
		//下面写pos=1时的功能代码即可
		pn->next = p->next;
		p->next = pn;
		//节点个数加 1
		++pl->cnt;
	}
}

//实现判断链表是否为空
bool empty(List* pl)
{
	return NULL == pl->head;
}
//实现判断链表是否为满
bool full(List* pl)
{
	return false;
}
//计算链表中元素的个数
int size(List* pl)
{
	return pl->cnt;
}

//实现向链表的尾部追加新结点
void append_tail(List* pl,int data)
{
	//1.创建新结点
	Node* pn = create_node(data);
	//2.当链表为空时,插入头节点的位置
	//if(NULL == pl->head)
	if(empty(pl))
	{
		pl->head = pl->tail = pn;
	}
	//3.当链表不为空时,追加到最后
	else
	{
		pl->tail->next = pn;
		pl->tail = pn;
	}
	//4.节点个数 加1
	pl->cnt++;
}

//实现创建新结点的函数
Node* create_node(int data)
{
	Node* pn = (Node*)malloc(sizeof(Node));
	if(NULL == pn)
	{
		printf("创建节点失败\n");
		return;//结束当前函数
	}
	pn->data = data;
	pn->next = NULL;
	return pn;
}

//实现遍历链表中所有的节点元素值
void travel(List* pl)
{
	Node* p = pl->head;
	printf("链表中的元素有:");
	while(p != NULL)
	{
		printf("%d ",p->data);
		//指向下一个节点
		p = p->next;
	}
	printf("\n");
}

//实现向头节点位置插入元素的功能
void push_head(List* pl,int data)
{
	//1.创建新结点,并且进行初始化
	// create_node
	/*
	Node* pn = (Node*)malloc(sizeof(Node));
	if(NULL == pn)
	{
		printf("创建节点失败\n");
		return;//结束当前函数
	}
	pn->data = data;
	pn->next = NULL;
	*/
	Node* pn = create_node(data);
	//2.将新节点插入到头节点位置
	/*
	pn->next = pl->head;
	pl->head = pn;
	//3.调整链表中其他成员的值
	pl->cnt++;
	//当插入的节点是链表中第一个节点时
	//让尾指针也指向该节点,否则尾指针不变
	if(1 == pl->cnt)
	{
		pl->tail = pn;
	}
	*/
	if(empty(pl))
	{
		//链表为空时,头尾指针都指向新结点
		pl->head = pl->tail = pn;
	}
	else
	{
		//链表不为空,原来的头节点连在新节点后边
		pn->next = pl->head;
		//头指针指向新节点,作为最新的头节点
		pl->head = pn;
	}
	pl->cnt++;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值