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++;
}