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