文章目录
一、线性表的概念
线性关系
- 当1<i<n时,ai的直接前驱为ai-1, ai的直接后继为ai+1。
- 除了第一个元素与最后一个元素,序列中任何一个元素有且仅有一个直接前驱元素, 有且仅有一个直接后继元素。
- 数据元素之间的先后顺序为“一对一”的关系。
线性表
- 数据元素之间逻辑关系为线性关系的数据元素集合称为线性表。
-
线性表的顺序存储(顺序表):用一组地址连续的存储单元依次存储线性表的数据元素,数据元素之间的逻辑关系通过数据元素的存储位置直接反映。一般情况下采用数组存储。
-
线性表的链式存储(链表):用一组地址任意的存储单元(连续的或不连续的)依次存储表中各个数据元素, 数据元素之间的逻辑关系通过指针间接地反映出来。
顺序表与链表的比较
-
存储分配方式
- 顺序存储用一段连续的存储单元依次存储线性表的数据元素
- 链表采用链式储存结构,用一组不连续的存储单元存放线性表的元素
-
时间性能
-
查找
- 顺序存储:无序O(n),有序O(log2 n)
- 链表O(n)
-
插入和删除
- 顺序存储需要平均移动表长一半的元素,时间为O(n)
- 链表在给出结点位置后,插入和删除时间仅为O(1)
-
-
空间性能
- 顺序存储需要事先分配存储空间,分大了浪费,分小了易发生溢出
- 链表不需要事先分配存储空间,需要时分配结点,元素个数不受限制
二、链表的概念
定义:链表是一种物理存储上非连续、非顺序的存储结构,数据元素的逻辑顺序通过链表中的指针链接次序实现。
链表的结构:自引用结构
自引用结构分为两部分:
- 数据域:存储实际的数据
- 指针域:一个或多个指向自身结构类型的指针,用于存储数据的关系(如:下一个元素的位置)
//ElemType为数据类型,使用前需定义。
//如 #define ElemType int 或 typedef int ElemType
typedef struct node
{
ElemType val;
struct node *next;
}LNode,*LinkList;
链表的分类
单链表
双向链表
- 双向链表是指链表的每一个结点中除了数据域以外设置两个指针域,其中之一指向结点的直接后继结点,另外一个指向结点的直接前驱结点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ucfwRP93-1683127239732)(图片/链表/双链表.png)]
循环链表
- 循环链表是指链表中最后那个链结点的指针域存放指向链表最前面那个结点的指针,整个链表形成一个环
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZQthHx5K-1683127239733)(图片/链表/循环链表.png)]
哨兵节点
-
定义一个空结点(不存储任何数据),链表初始化时直接创建。
-
该结点创建后,地址不做任何修改;链表的头指针指向哨兵节点
-
第一个数据结点为头结点后面的第一个结点,后续操作只可能在
头结点后面进行操作,不涉及任何头指针的修改
-
设置头结点的最大好处是对链表结点的插入及删除操作统一了,不用单独考虑是否需要修改头指针。其数据域一般无意义(也可存放链表的长度)
三、单链表的基本操作
链表的创建
链表的创建可分为头插法和尾插法。
头插法
-
在头节点后面插入新的节点,每个新产生的节点都会被接在头节点后面。
LinkList create_head(int n)
{
LinkList head =(LinkList)malloc(sizeof(LNode));
head->val = n;//哨兵节点,方便插入与删除操作。如果ElemType类型为int可以存储链表的长度
head->next = NULL;
for (int i = 0; i < n;i++)
{
LinkList p=(LinkList)malloc(sizeof(LNode));
p->next = NULL;
scanf("%d", &p->val);//ElemType类型默认int
p->next = head->next;
head->next = p;
}
return head;
}
尾插法
- 每个新节点都插在表尾
LinkList create_tail(int n)
{
LinkList head =(LinkList)malloc(sizeof(LNode));
head->val = n;//哨兵节点,方便插入与删除操作。如果ElemType类型为int可以存储链表的长度
head->next = NULL;
struct node *p=head, *q;
for (int i = 0; i < n;i++)
{
q=(LinkList)malloc(sizeof(LNode));
q->next = 0;
scanf("%d", &q->val);//ElemType类型默认int
p->next = q;
p = p->next;
}
return head;
}
求线性链表的长度
int length(LinkList head)
{
int num = 0;
LinkList p = head->next;
while(p)
{
num++;
p = p->next;
}
return num;
/*
如果哨兵节点维护链表长度,直接输出哨兵节点数据
return head->val;
*/
}
判断链表是否为空
int isempty(LinkList head)
{
if(head->next)
return 0;
else
return 1;
}
查找
按值查找
//查找成功,返回被查找节点地址,否则返回NULL
LinkList locate(LinkList head,ElemType item)
{
LinkList p = head->next;
while(p!=NULL&&p->val!=item)
p = p->next;
return p;
}
按序号查找
//查找成功,返回被查找节点地址,否则返回NULL
LinkList search(LinkList head,int i)
{
int j = 0;
LinkList p = head;
while(p->next!=NULL&&j<i)
{
p = p->next;
j++;
}
if(j==i)
return p;
else
return NULL;
}
插入
尾结点插入
void add_node(LinkList head,int item)
{
/*链表并非自己创造,手动添加哨兵节点
LinkList dummy=(LinkList)malloc(sizeof(LNode));
dummy->next=head;
LinkList p = dummy;
*/
LinkList p = head;
LinkList q=(LinkList)malloc(sizeof(LNode));
q->val = item;
q->next = NULL;
while(p->next!=NULL)
{
p = p->next;
}
p->next = q;
}
节点后插入
void insert_tail(LinkList p,ElemType item)
{
LinkList q=(LinkList)malloc(sizeof(LNode));
q->val = item;
q->next = p->next;
p->next = q;
}
节点前插入
void insert_head(LinkList head,LinkList p,ElemType item)
{
LinkList pre = head;
LinkList q=(LinkList)malloc(sizeof(LNode));
q->val = item;
while(pre->next!=p&&pre->next!=NULL)
pre = pre->next;//找到p的前驱节点
if(pre->next!=NULL)
{
q->next = pre->next;
pre->next = q;
}
/*
也可以将节点插入p节点后,然后交换两个节点数据,时间复杂度降低
LinkList q=(LinkList)malloc(sizeof(LNode));
q->val = item;
q->next = p->next;
p->next = q;
ElemType tem=p->val;
p->val=q->val;
q->val=tem;
*/
}
按位置插入
//返回1则插入成功,返回0则插入失败,第i-1个节点不存在
int insert_pos(LinkList head,int i,ElemType item)
{
LinkList p,q=(LinkList)malloc(sizeof(LNode));
q->next = item;
p = search(head, i - 1);
if(p==NULL)
return 0;
else
{
q->next = p->next;
p->next = q;
return 1;
}
}
有序链表插入
void insert_seq(LinkList head,ElemType item)
{
LinkList p, q, r;
r=(LinkList)malloc(sizeof(LNode));
r->val = item;
p = head;
q = head->next;
while (q != NULL&&item>=q->val)
{
p = q;
q = q->next;
}
r->next = q;
p->next = r;
}
删除
删除指定节点
void delete_node(LinkList head,LinkList p)
{
LinkList pre = head;
while(pre->next!=p&&pre->next!=NULL)
pre = pre->next;//找到p的前驱节点
if(pre->next!=NULL)
{
pre->next = p->next;
free(p);
}
}
按值删除节点
void delete_data(LinkList head,ElemType item)
{
LinkList p = head,q;
while(p->next!=NULL)
{
if(p->next->val==item)
{
q = p->next;
p->next = p->next->next;
free(q);
}
p = p->next;
}
}
按位置删除节点
int delete_pos(LinkList head,int i)
{
LinkList p, q;
p = search(head, i - 1);
if(p==NULL)
return -1;
else if (p->next == NULL)
return 0;
else
{
q = p->next;
p->next = q->next;
free(q);
return 1;
}
}
删除链表
void delete_list(LinkList head)
{
LinkList p = head->next,q;
while(p!=NULL)
{
q = p->next;
free(p);
p = q;
}
}
反转链表
void reverse_list(LinkList head)
{
LinkList p = head->next,q;
head->next = NULL;
while(p)
{
q = p;
p = p->next;
q->next = head->next;
head->next = q;
}
}
循环链表查找
LinkList search_circle(LinkList head,ElemType item)
{
LinkList p = head->next;
while(p!=head)
{
if(p->val==item)
return p;
p = p->next;
}
return NULL;
}