【整理】链表特征以及代码实现

本文介绍了链表的基本概念,包括单向线性、单向循环、双向线性、双向循环、数组链表、链表数组、二维链表等类型。并提供了单向线性链表的C语言实现,包括插入、删除、遍历、逆转等操作。
摘要由CSDN通过智能技术生成
1 基本概念
  由地址不连续的节点序列组成,彼此通过指针进行相互连接构成的数据结构,叫做链表

2.链表的分类
  (1) 单向线性链表
   每个节点中除了存储数据元素本身之外,还需要一个指针,用于记录下一下节点的地址,叫做后指针
   ×   其中第一个节点叫做头节点,指向头节点的指针叫做头指针;最后一个节点叫做尾节点,尾节点的后指针是空指针
  (2) 单向循环链表
   与单向线性链表相似,所不同的是最后一个节点的后指针指向头节点的地址,首位相接构成一头结构
  (3) 双向线性链表
   每个节点中除了存储数据元素本身之处,还需要两个指针,一个指针用于指向下一个节点的地址,叫做后指针,另外一个 指针用于指向前一个节点的地址,叫做前指针
   × 其中头节点的前指针和尾节点的后指针都是空指针
  (4) 双向的循环链表
   与双向线性链表相似,所不同的是头节点的前指针指向尾节点,尾节点的后指针指向头节点,从而构成环状结构
  (5) 数组链表
   链表中的每个元素都是一个数组,也就是说由数组构成的链表
  (6) 链表数组
   数组中的每一个元素都是一个链表,也就是说由链表构成的数组
  (7) 二维链表
   链表中的每一个元素都是一个链表的链表,也就是由链表构成的链表

************************************************************************************
单向线性链表的代码实现:
#include <stdio.h>
#include <stdlib.h>

//定义节点的数据类型
typedef struct Node
{
	int data; //数据内容
	struct Node *next; // 下一个节点的地址
}Node;

//定义单链表数据类型
typedef struct
{
	Node *head; // 头指针
	int cnt;	//记录链表中元素的个数
}List;

//向链表的头节点位置插入新节点
void push_head(List *pl, int data);
//遍历链表中的所有元素
void travel(List *pl);
//实现创建新节点的功能
Node *create_node(int data);
//计算链表中元素的个数
int size(List *pl);
//清空链表中所有的节点
void clear(List *pl);
//向指定的下标位置插入指定的节点,下标从0开始
void insert(List *pl, int pos, int data);
//实现向链表的尾部插入新节点的操作
void push_tail(List *pl, int data);
//实现删除指定下标位置的节点操作
void del(List *pl, int pos);
//实现删除头节点的功能
void pop_head(List *pl);
//实现删除尾节点的功能
void pop_tail(List *pl);
//实现判断链表是否为空
int empty(List *pl);
//实现判断链表是否为满
int full(List *pl);
//实现获取关节点的元素值
int get_head(List *pl);
//实现获取尾节点的元素值
int get_tail(List *pl);
//实现单链表的逆转
void reverse(List *pl);


int main(void)
{
	//创建单链表,并且进行初始化
	List list;
	list.head = 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("链表中元素的个数是:%d\n", list.cnt); // 3
	printf("链表中元素的个数是:%d\n", size(&list)); // 3
	printf("***************************\n");
	insert(&list, -2, 44);
	travel(&list);	// 33 22 11 44
	insert(&list, 0, 55);
	travel(&list);  // 55 33 22 11 44
	insert(&list, 5, 66);
	travel(&list);	// 55 33 22 11 44 66
	insert(&list, 2, 77);
	travel(&list);	// 55 33 77 22 11 44 66
	insert(&list, 20, 88);
	travel(&list);	// 55 33 77 22 11 44 66 88
	printf("链表中元素的个数是:%d\n", size(&list)); // 8
	push_tail(&list, 99);
	travel(&list); // 55 33 77 22 11 44 66 88 99
	printf("链表中元素的个数是:%d\n", size(&list)); // 9

	printf("***************************\n");
	del(&list, -2); //失败
	travel(&list);  // 55 33 77 22 11 44 66 88 99
	del(&list, 0);
	travel(&list); // 33 77 22 11 44 66 88 99
	del(&list, 8); // 失败
	travel(&list); // 33 77 22 11 44 66 88 99
	del(&list, 2);
	travel(&list); // 33 77 11 44 66 88 99
	del(&list, 6); 
	travel(&list); // 33 77 11 44 66 88
	printf("链表中元素的个数是:%d\n", size(&list)); // 6

	printf("***************************\n");
	travel(&list); // 33 77 11 44 66 88
	pop_head(&list);
	travel(&list);	// 77 11 44 66 88
	pop_tail(&list);
	travel(&list); // 77 11 44 66
	printf("链表中头节点的元素值是:%d\n", get_head(&list)); // 77
	printf("链表中尾节点的元素值是:%d\n", get_tail(&list)); // 66
	printf("%s\n", empty(&list) ? "链表为空" : "链表不为空");
	printf("%s\n", full(&list) ? "链表为满" : "链表不为满");
	printf("链表中元素的个数是:%d\n", size(&list)); // 4

	printf("***************************\n");
	travel(&list); // 77 11 44 66
	reverse(&list);
	travel(&list); // 66 44 11 77


	printf("***************************\n");
	clear(&list);
	travel(&list); // 啥也没有 
	printf("链表中元素的个数是:%d\n", size(&list)); // 0
	return 0;
}

//实现单链表的逆转
void reverse(List *pl)
{
	//判断链表为空,或者只有一个节点不需要逆转
	if (size(pl) <= 1)
	{
		printf("该链表不需要逆转\n");
		return;
	}
	//提供三个指针分别记录前三个节点的地址
	Node *p1 = pl->head;
	Node *p2 = p1->next;
	Node *p3 = p2->next;
	//将原来的头节点的Next置为Null
	p1->next = NULL;
	while(p3 != NULL)
	{
		//让第二个节点的next指向第一个节点
		p2->next = p1;
		//p1指向p2指向的节点
		p1 = p2;
		//p2指向p3指向的节点
		p2 = p3;
		//p3指向下一节点
		p3 = p3->next;
	}
	//特殊处理最后两个节点
	p2->next = p1;
	//头指针指向新的节点
	pl->head = p2;
}
//实现删除头节点的功能
void pop_head(List *pl)
{
	//调用del函数
	del(pl, 0);	
}

//实现删除尾节点的功能
void pop_tail(List *pl)
{
	//调用del函数
	del(pl, size(pl) - 1);
}

//实现判断链表是否为空
int empty(List *pl)
{
	return NULL == pl->head;
	// !pl->cnt;
	// return 0 == pl->cnt;
}

//实现判断链表是否为满
int full(List *pl)
{
	return 0;
}

//实现获取头节点的元素值
int get_head(List *pl)
{
	if (empty(pl))
	{
		return -1; //表示错误 
	}
	return pl->head->data;
}

//实现获取尾节点的元素值
int get_tail(List *pl)
{
	if (empty(pl))
	{
		return -1; //表示错误
	}
	Node *p = pl->head;
	while (p->next != NULL)
	{
		p = p->next;
	}
	return p->data;
}

//实现删除指定下标位置的节点操作
void del(List *pl, int pos)
{
	//1.判断删除的坐标是否合法以及链表是否为空
	if (pos <0 || pos >=size(pl))
	{
		printf("删除的坐标不合法或节点不存在,删除失败\n");
		return;
	}
	//2.当删除头节点时的处理方式
	if (0 == pos)
	{
		Node *p = pl->head;
		pl->head = pl->head->next;
		free(p);
		p = NULL;
		//节点元素的个数减1
		pl->cnt--;
		return;
	}

	//3.当删除其它位置节点时的处理方式
	int i = 0;
	Node *p = pl->head;
	for (i =1; i <pos; i++)
	{
		p = p->next;
	}
	//记录要删除的节点地址
	Node *q = p->next;
	//让删除节点的前一个节点连接删除节点的后一个节点
	p->next = p->next->next;
	//释放要删除节点的空间
	free(q);
	q = NULL;
	//元素的个数减1
	pl->cnt--;
}


//实现向链表的尾部插入新节点的操作
void push_tail(List *pl, int data)
{
	//调用insert函数进行插入
	insert(pl, size(pl), data);
}

//向指定的下标位置插入指定的节点,下标从0开始
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.当把新节点插入到头节点位置时的处
	if (0 == pos)
	{
		pn->next = pl->head;
		pl->head = pn;
		//元素个数加1
		pl->cnt++;
		return;
	}
	
	//4.当把新节点插入到其它位置时的处理
	Node *p = pl->head;
	int i = 0;
	//使用循环控制 当pos>1时多出来的next执行
	for (i =1; i <pos; i++)
	{
		p = p->next;
	}
	//pos = 1时,需要执行的代码
	pn->next = p->next;
	p->next = pn;
	//节点元素的个数加1
	pl->cnt++;

}

//计算链表中元素的个数
int size(List *pl)
{
	return pl->cnt;
}

//清空链表中所有的节点
void clear(List *pl)
{
	while(pl->head != NULL)
	{
		//保存要删除节点的地址
		Node *pn = pl->head;
		//让头指针指向下一个
		pl->head = pl->head->next;
		//释放要删除的节点
		free(pn);
		pn = NULL;
	}
	//链表中节点的个数置为0,头指针置为空指针
	pl->cnt = 0;
	// pl->head = NULL;
}

Node *create_node(int data)
{
	Node *pn = (Node *)malloc(sizeof(Node));
	pn->data = data;
	pn->next = NULL;
	//返回创建的新节点地址
	return pn;
}

//向链表的头节点位置插入新节点
void push_head(List *pl, int data)
{
	//1.创建新节点
	//自定义create_node 创建 新节点
	// Node *pn = create_node(data);
	// Node *pn = (Node *)malloc(sizeof(Node));
	// pn->data = data;
	// pn->next = NULL;
	//2.将新节点插入到头节点的位置上
	//pn->next = pl->head;
	//pl->head = pn;
	//3.元素个数加1
	//pl->cnt++;
	insert(pl, 0, data);

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值