1 链表概述
所谓的链表,指的是线性表的链式存储结构,数据存储的时候可以存储当前数据记录,同时存储下一条数据记录的存储空间的地址,在访问的时候,可以通过地址访问到下一条数据记录。
链表的分类
1.
- 按照链表结点数据的访问方向:
a.
- 单向链表:结点在存储数据的时候,同时存储下一个结点存储空间的地址,只能从当前结点访问到下一个结点,不能从下一个结点访问到当前结点,整个访问过程是单向的。
b.
- 双向链表:结点在存储数据的时候,同时存储后继结点存储空间地址和前驱结点存储空间地址,可以从当前访问到后继结点,后继结点也可以访问到当前结点。整个访问是双向的。
2.
- 按照是否存在头结点:
数据域:指的是数据记录的存储空间。
头结点:作为整个链表的起始结点,
1) 如果数据域为有效数据域,可以参与数据的操作,起始结点会随着数据操作而改变。此时称为数据结点而非头结点;
2) 如果数据域为无效数据域,不参与数据的运算,此时链表中的起始结点不会改变。称为链表的头结点。
a.
- 带头结点链表
起始结点数据域为无效数据;
b.
- 不带头结点链表
起始结点数据域为有效数据域。
3.
- 按照链表是否带环
a.
- 环形链表:
b.
- 非环形链表
1.带头结点链表初始化
/* 结点数据域类型:可以根据实际应用需要存储的数据记录进行抽象 */
#if 0
typedef struct Stu {
int num;
char name[32];
int score;
} data_t;
#else
typedef int data_t;
#endif
/* 整个结点数据类型: */
typedef struct node {
data_t data; /* 当前结点数据域:存储当前结点的数据 */
struct node *next; /* 当前结点指针域:存储下一个结点空间的起始地址 */
} node_t;
node_t *CreateLinkList(void)
{
node_t *head;
/* 开辟头结点存储空间 */
head = malloc(sizeof(node_t));
if (head == NULL)
return head;
memset(head, 0, sizeof(node_t));
head->next = NULL; /* 设置头结点指针域为NULL,表示没有后继结点 */
return head;
}
2.链表插入数据元素
int InsertLinkList(node_t *head, int local, data_t mydata)
{
int i;
node_t *p = head;
node_t *q;
/* 判断插入位置是否满足条件:local >= 0 */
if (local < 0)
return -1;
/* 找到需要插入结点的前一个结点 */
for (i = 0; i < local && p != NULL; i++) {
p = p->next;
}
if (p == NULL)
return -1;
/* 创建需要插入数据结点空间 */
q = malloc(sizeof(node_t));
if (q == NULL)
return -1;
memset(q, 0, sizeof(node_t));
q->data = mydata;
/* 插入结点 */
q->next = p->next;
p->next = q;
return 0;
}
3.遍历链表
void DisplayLinkList(node_t *head)
{
node_t *p = head->next;
while(p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
4.删除链表中的元素
int DeleteLinkList(node_t *head, int local)
{
int i;
node_t *p = head;
node_t *q;
/* 判断链表是否为空表:空表删除失败 */
if (head->next == NULL)
return -1;
/* 找要删除结点的前一个结点 */
for (i = 0; i < local && p->next != NULL; i++)
p = p->next;
if (p->next == NULL)
return -1;
/* 删除结点 */
q = p->next;
p->next = q->next;
free(q);
return 0;
}
5.链表查询元素
int SelectLinkList(node_t *head, int local, data_t *mydata)
{
int i;
node_t *p;
/* 判断链表是否为空表 */
if (head->next == NULL)
return -1;
/* 查找到指定序号的结点 */
p = head->next;
for (i = 0; i < local && p != NULL; i++)
p = p->next;
if (p == NULL)
return -1;
/* 返回结点的数据信息 */
*mydata = p->data;
return 0;
}
6.链表修改元素
int UpdateLinkList(node_t *head, int local, data_t newdata)
{
int i;
node_t *p;
/* 判断链表是否为空表 */
if (head->next == NULL)
return -1;
/* 查找到指定序号的结点 */
p = head->next;
for (i = 0; i < local && p != NULL; i++)
p = p->next;
if (p == NULL)
return -1;
/* 返回结点的数据信息 */
p->data = newdata;
return 0;
}
7.其他运算
/* 判断链表是否为空表 */
int isEmptyLinkList(node_t *head)
{
return (head->next == NULL);
}
/* 判断链表是否为满表 */
int isFullLinkList(node_t *head)
{
return 0; /* 链表不可能为满表,返回0表示false */
}
/* 计算链表中有效数据结点个数,链表的长度 */
int LengthLinkList(node_t *head)
{
int len = 0;
node_t *p = head->next;
while(p != NULL) {
len++;
p = p->next;
}
return len;
}
/* 清空链表的数据结点,头结点保留 */
void ClearLinkList(node_t *head)
{
node_t *q;
node_t *p = head->next;
head->next = NULL;
while(p != NULL) {
q = p;
p = p->next;
free(q);
}
}
/* 销毁整个链表 */
void DestoryLinkList(node_t **head)
{
node_t *q;
node_t *p = *head;
*head = NULL;
while(p != NULL){
q = p;
p = p->next;
free(q);
}
}
8.链表的排序
void SortLinkList(node_t *head)
{
node_t *p;
node_t *q;
node_t *r;
/* 判断链表是否为空表:空表直接结束 */
if (head->next == NULL)
return;
p = head->next->next; /* 第0个有效数据结点作为有效序列,从第1个有效数据结点开始做顺序插入排序 */
head->next->next = NULL;
/* 从第1个结点开始循环遍历,并做顺序插入 */
while(p != NULL) {
q = p; /* 遍历需要排序的结点p赋值给运算结点q */
p = p->next; /* 遍历到下一个结点 */
/* 循环找到需要插入结点位置的前一个结点 */
r = head;
while(r->next != NULL) { /* r的下一个结点是否存在,r->next == NULL说明r为有效链表的最后一个结点 */
if (r->next->data > q->data) /* 下一个结点数据 > 大于插入结点的数据,说明当前结点为插入结点大的前一个结点 */
break;
r = r->next;
}
/* 在r结点后插入待排序结点q */
q->next = r->next;
r->next = q;
}
}