目录
一 单链表的表示和实现
1.单链表的存储结构
1.1. 头指针、头结点与首元结点
1.2. 带头结点单链表和不带头结点单链表的比较
2. 单链表的初始化
3. 单链表的长度
4. 单链表的插入
5. 单链表的删除
6. 单链表的查看
7. 单链表的撤销
完整代码
二 循环单链表
三 双向链表
1. 双向链表表的存储结构、初始化
2. 双向链表的插入、删除
结点:由数据元素域和一个或若干个指针域组成的一个结构体。
链表:链式存储结构的线性表。
一 单链表的表示和实现
1.单链表的存储结构
typedef char DataType;
typedef struct Node
{ //单链表的储存结构
DataType data; //存放数据
struct Node* next; //存放指针
}SLNode;
1. 每个存储结点都包含两部分:数据域,指针域
2. 在单链表中,除了首元结点外,任一结点的存储位置由其直接前驱结点的链域的值指示。
1.1. 头指针、头结点与首元结点
头指针是指向链表中第一个结点
头结点是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息,它不计入表长度。
首元结点是指链表中存储线性表第一个数据元素a0的结点。
1.2. 带头结点单链表和不带头结点单链表的比较
1) 带头结点的单链表的第一个数据元素插入时,其插入在头结点后。
2) 不带头结点的单链表的第一个数据元素插入时,其插入在头指针后。
3) 不带头结点的单链表的其余数据元素插入时,其插入在前一节点后。
4) 删除带头结点的单链表的第一个数据时,头结点指针指向第二个数据
5) 删除不带头结点的单链表的第一个数据时,头指针指向第二个数据
结论:插入操作时,有头结点链表方便
单链表一般构造成带头结点的单链表
2. 单链表的初始化
malloc(m)函数:向系统动态申请m字节长度的地址空间,并返回这段空间的首地址;
free(p)函数:释放指针p所指变量的存储空间,即彻底删除一个变量。
sizeof(x)运算符:计算变量x的长度(字节数);
以上有库函数/算符都在<malloc.h> 中
void ListInitiate(SLNode** hear)
{ //单链表的初始化
*hear = (SLNode*)malloc(sizeof(SLNode));
(* hear)->next = NULL; //链尾标记NULL
}
3. 单链表的长度
int ListLength(SLNode* hear)
{ //单链表的长度
SLNode* p = hear; //p 指向头结点
int size;
for (size = 0; p->next != NULL; size++)
{
p = p->next;
}
return size;
}
4. 单链表的插入
实现步骤:
1. 将插入处前一节点指针指向所插入节点的指针
(即 将所插入节点的指针指向插入处的节点,防止插入处节点丢失);
2. 将插入处前一节点指针指向所插入节点。
x 节点先 生成再使用。
int ListInsert(SLNode* head, int i, DataType x)
{ //在顺序表的第i个位置之前插入数据值x
//插入成功返回1,插入失败返回0
SLNode* p, * q;
p = head;
int j = 0;
for (j; p->next != NULL && j < i - 1; j++)
{
p = p->next;
}
if (j != i - 1)
{
printf("插入地方错误!\n");
return 0;
}
q = (SLNode*)malloc(sizeof(SLNode));
q->data = x;
q->next = p->next;
p->next = q;
return 1;
}
5. 单链表的删除
实现步骤:
1. 将要删除节点前一节点的指针指向删除节点的后一节点
(即 使得要没有指针指向删除节点);
2. 释放删除节点。
int ListDelete(SLNode* head, int i, DataType* x)
{ //删除顺序表L中位置i的数据元素值并存放到参数x中
//删除成功返回1,失败返回0
SLNode* p, * s;
p = head;
int j = 0;
for (j; p->next != NULL && j < i - 1; j++)
{
p = p->next;
}
if (j != i - 1)
{
printf("删除位置错误!\n");
return 0;
}
s = p->next;
*x = s->data;
p->next = s->next;
free(s);
return 1;
}
6. 单链表的查看
int ListGet(SLNode* head, int i, DataType* x)
{ //取顺序表中第i个元素存于x中
//取出成功返回1,失败返回0
SLNode* p;
p = head;
int j = 0;
for (j; p->next != NULL && j < i; j++)
{
p = p->next;
}
if (j != i)
{
printf("参数i不合法!\n");
return 0;
}
*x = p->data;
return 1;
}
7. 单链表的撤销
void Destroy(SLNode **head)
{ //撤销单链表
SLNode* p, * q;
p = *head;
for (p; p != NULL; free(q))
{
q = p;
p = p->next;
}
*head = NULL;
}
完整代码
#include<stdio.h>
#include<malloc.h>
typedef char DataType;
typedef struct Node
{ //单链表的储存结构
DataType data; //存放数据
struct Node* next; //存放指针
}SLNode;
void ListInitiate(SLNode** hear)
{ //单链表的初始化
*hear = (SLNode*)malloc(sizeof(SLNode));
(* hear)->next = NULL; //链尾标记NULL
}
int ListLength(SLNode* hear)
{ //单链表的长度
SLNode* p = hear; //p 指向头结点
int size;
for (size = 0; p->next != NULL; size++)
{
p = p->next;
}
return size;
}
int ListInsert(SLNode* head, int i, DataType x)
{ //在顺序表的第i个位置之前插入数据值x
//插入成功返回1,插入失败返回0
SLNode* p, * q;
p = head;
int j = 0;
for (j; p->next != NULL && j < i - 1; j++)
{
p = p->next;
}
if (j != i - 1)
{
printf("插入地方错误!\n");
return 0;
}
q = (SLNode*)malloc(sizeof(SLNode));
q->data = x;
q->next = p->next;
p->next = q;
return 1;
}
int ListDelete(SLNode* head, int i, DataType* x)
{ //删除顺序表L中位置i的数据元素值并存放到参数x中
//删除成功返回1,失败返回0
SLNode* p, * s;
p = head;
int j = 0;
for (j; p->next != NULL && j < i - 1; j++)
{
p = p->next;
}
if (j != i - 1)
{
printf("删除位置错误!\n");
return 0;
}
s = p->next;
*x = s->data;
p->next = s->next;
free(s);
return 1;
}
int ListGet(SLNode* head, int i, DataType* x)
{ //查看
//取顺序表中第i个元素存于x中
//取出成功返回1,失败返回0
SLNode* p;
p = head;
int j = 0;
for (j; p->next != NULL && j < i; j++)
{
p = p->next;
}
if (j != i)
{
printf("参数i不合法!\n");
return 0;
}
*x = p->data;
return 1;
}
void Destroy(SLNode **head)
{ //撤销单链表
SLNode* p, * q;
p = *head;
for (p; p != NULL; free(q))
{
q = p;
p = p->next;
}
*head = NULL;
}
void main(void)
{
SLNode* head;
DataType x;
ListInitiate(&head);
ListInsert(head, 1, 'a');
ListInsert(head, 2, 'b');
ListInsert(head, 3, 'c');
printf("%d\n", ListLength(head));
ListGet(head, 3, &x);
printf("%c\n", x);
ListDelete(head, 2, &x);
printf("%d\n", ListLength(head));
ListGet(head, 2, &x);
printf("%c\n", x);
Destroy(&head);
}
二 循环单链表
在初始化函数中,将语句(*head)->next = NULL改为(*head)->next =head。
void ListInitiate(SLNode** hear)
{ //单链表的初始化
*hear = (SLNode*)malloc(sizeof(SLNode));
(* hear)->next = hear; //链尾标记hear
}
在删除与查看函数中,将更改其判断语句。
int ListDelete(SLNode* hear, int i, DataType* x)
{ //删除
SLNode* p, * q;
p = hear;
int j = 0;
for (j; j < i - 1; j++)
{
p = p->next;
if (p->next == hear)
{
p = p->next;
}
}
if (j != i - 1)
{
printf("删除位置错误!\n");
return 0;
}
q = p->next;
*x = q->data;
p->next = q->next;
free(q);
return 1;
}
int ListGet(SLNode* head, int i, DataType* x)
{ //查看
SLNode* p;
p = head;
int j = 0;
for (j; j < i - 1; j++)
{
p = p->next;
if (p->next == head)
{
p = p->next;
}
}
if (j != i - 1)
{
printf("参数!不合法!\n");
return 0;
}
*x = (p->next)->data;
return 1;
}
在撤销函数中, 增加一标志点以判断循环链表已清空。
void Destroy(SLNode** head)
{ //撤销
SLNode* p, * q;
p = *head;
int i = 0;
for (p; i != 0 && p->next != *head; free(q))
{
q = p;
p = p->next;
i++;
}
*head = NULL;
}
完整代码,输出卡牌游戏,卡牌第一个位置输出 1 ,第二个位置输出 2 ,以此类推。
#include<stdio.h>
#include<malloc.h>
typedef char DataType;
typedef struct Node
{
DataType data; //存放数据
struct Node* next; //存放指针
}SLNode;
void ListInitiate(SLNode** head)
{ //初始化
*head = (SLNode*)malloc(sizeof(SLNode));
(*head)->next = *head; //链尾标记hear
}
int ListLength(SLNode* hear)
{ //长度
SLNode* p = hear;
int size;
for (size = 0; p->next != hear; size++)
p = p->next;
return size;
}
int ListInsert(SLNode* head, int i, DataType x)
{ //插入
SLNode* p, * q;
p = head;
int j = 0;
for (j; p->next != head && j < i - 1; j++)
{
p = p->next;
}
if (j != i - 1)
{
printf("插入地方错误!\n");
return 0;
}
q = (SLNode*)malloc(sizeof(SLNode));
q->data = x;
q->next = p->next;
p->next = q;
return 1;
}
int ListDelete(SLNode* hear, int i, DataType* x)
{ //删除
SLNode* p, * q;
p = hear;
int j = 0;
for (j; j < i - 1; j++)
{
p = p->next;
if (p->next == hear)
{
p = p->next;
}
}
if (j != i - 1)
{
printf("删除位置错误!\n");
return 0;
}
q = p->next;
*x = q->data;
p->next = q->next;
free(q);
return 1;
}
int ListGet(SLNode* head, int i, DataType* x)
{ //查看
SLNode* p;
p = head;
int j = 0;
for (j; j < i - 1; j++)
{
p = p->next;
if (p->next == head)
{
p = p->next;
}
}
if (j != i - 1)
{
printf("参数!不合法!\n");
return 0;
}
*x = (p->next)->data;
return 1;
}
void Destroy(SLNode** head)
{ //撤销
SLNode* p, * q;
p = *head;
int i = 0;
for (p; i != 0 && p->next != *head; free(q))
{
q = p;
p = p->next;
i++;
}
*head = NULL;
}
void main(void)
{
SLNode* hear;
DataType x;
ListInitiate(&hear);
ListInsert(hear, 1, '1');
ListInsert(hear, 2, 'K');
ListInsert(hear, 3, '2');
ListInsert(hear, 4, '8');
ListInsert(hear, 5, '3');
ListInsert(hear, 6, '0');
ListInsert(hear, 7, '4');
ListInsert(hear, 8, 'j');
ListInsert(hear, 9, '5');
ListInsert(hear, 10, '9');
ListInsert(hear, 11, '6');
ListInsert(hear, 12, 'q');
ListInsert(hear, 13, '7');
ListDelete(hear, 1, &x);
printf("%c", x);
ListDelete(hear, 2, &x);
printf("%c", x);
ListDelete(hear, 3, &x);
printf("%c", x);
ListDelete(hear, 4, &x);
printf("%c", x);
ListDelete(hear, 5, &x);
printf("%c", x);
ListDelete(hear, 6, &x);
printf("%c", x);
ListDelete(hear, 7, &x);
printf("%c", x);
ListDelete(hear, 8, &x);
printf("%c", x);
ListDelete(hear, 9, &x);
printf("%c", x);
ListDelete(hear, 10, &x);
printf("%c", x);
ListDelete(hear, 11, &x);
printf("%c", x);
ListDelete(hear, 12, &x);
printf("%c", x);
ListDelete(hear, 13, &x);
printf("%c", x);
Destroy(&hear);
}
三 双向链表
双向链表:链表中每个结点除后继指针域和数据域外还有一个前驱指针域。
它有带头结点和不带头结点,循环和非循环结构。双向链表是解决查找前驱结点问题的有效途径。
1. 双向链表表的存储结构、初始化
初始化时,前驱指针和后继指针各自构成自己的循环单链表。
2. 双向链表的插入、删除