链表是一种可以动态的进行内存分配的数据结构 ### 相当于长度不固定的结构体数组
链表中的元素在内存中的地址可以是不连续的
链表这种数据结构必须使用指针才能实现
用结构体建立链表是最合适的
例如:
struct Student {
int num;
float score;
struct Student* next;
};
typedef int ElemType;
struct tnode {
ElemType data;
struct tnode* left;
struct tnode* right;
};
struct tnode2 {
ElemType data;
struct tnode2* prev;
struct tnode2* next;
};
单链表的基本概念:
头指针:链表中第一个结点的存储位置(内存地址)叫做头指针
头结点:在链表的第一个结点前附设的结点称为头结点 ### 一个链表可以【没有】头结点 ### 头结点的数据域一般无意义,可以不存储任何信息,也可以存放链表的长度等附加信息
尾指针:链表中最后一个结点的存储位置叫做尾指针
结点: 结点由存放数据元素的数据域和存放后继结点地址的指针域组成
如果一个链表没有头结点,则头指针直接指向 “结点1” ,结点1 变成此时的第一个结点
如果一个链表有头结点,则头指针指向头结点,头结点的 next 指针指向 “结点1” ### 若链表为空表,则头结点的 next 指针为 NULL
头指针和头结点的异同:
头指针是指向链表的第一个结点的首地址的指针,若链表有头结点,则头指针指向头结点
头指针具有标识作用,所以常用头指针冠以链表的名字
头指针是链表的必要元素,无论链表是否为空表,头指针均不为空! ### 如果链表是空表,并且没有头指针呢?
头结点是为了操作的统一和方便而设立的,放在第一个结点之前
有了头结点,对在第一个结点之前插入结点、删除第一个结点,其操作方式与对其它结点的操作方式就统一了
头结点不一定是链表的必要元素,可以没有头结点
双向链表:在单链表的每个结点中增加一个指向前驱结点的指针域
【鉴于头结点有上述好处,今后一律在链表中添加头结点】
单链表:head指针 → 头结点(第一个结点) → 结点1 → 结点2 → … → 结点i → … → 尾结点 → NULL
双向链表:
循环链表:将单链表中终端结点的指针由空指针改为指向头结点,使整个单链表形成一个环,这种首尾相接的单链表称为单循环链表,简称循环链表 ### 在编程中判断循环结束的条件,原来判断 p->next 是否为空,现在则是 p->next 是否等于头结点
双向循环链表:将尾结点的next指针指向头结点,将头结点的prev指针指向尾结点,这就形成了一个环 ### 在编程中判断循环结束的条件是 p->next 是否等于头结点
循环链表和双向循环链表中的每个结点的指针域不再是NULL
对于循环链表和双向循环链表,还可以从链表的任意中间位置开始访问全部的结点,在编程中判断循环结束的条件,p->next 是否等于循环开始前的自己 ### p->next != p0,至于是否要跳过头结点,取决于编程者
向链表中插入一个结点:
插入到链表的头部,作为首元结点,即结点1;
插入到链表中间的某个位置;
插入到链表的最末端,作为链表的最后一个结点;
因此,插入结点的时候注意测试边界情况,即在 结点1 的位置插入、在 尾结点 的位置插入,当然在中间插入的情况也要测试
同样的,删除结点也要测试边界删除、中间删除是否都能成功
头插法:始终让新结点在第一的位置
尾插法:每次新结点都添加在最后
单链表插入一个结点:### 在纸上画图以帮助理解
假设:
结点 p 存储数据 ai (i为下标)
结点 p->next 存储数据 ai+1 (i+1为下标) ### p->next 紧跟在 p 之后
结点 s 存储数据 e
p, s 为相应结点的指针
现在要将 结点s 插入到 结点p 和 结点p->next 之间,代码上如何实现?
s->next = p->next;
p->next = s;
这两句可不可以换位呢?不能!
如果先执行 p->next = s;
那么后执行 s->next = p->next; 则相当于 s->next = s; ### 这导致 s->next 的后继断了,也就是 p->next 结点与它的前一个结点之间断开了连接
循环链表插入一个结点,同样是这两句
从单链表中删除一个结点:
单链表删除一个结点:### 在纸上画图以帮助理解
假设:
结点 p 存储数据 ai-1 (i-1为下标)
结点 q 存储数据 ai (i为下标) ### q 紧跟在 p 后面,即 q = p->next
结点 q->next 存储数据 ai+1 (i+1为下标) ### q->next 紧跟在 q 后面
p, q 为相应结点的指针
现在要将 结点q 删除,代码上如何实现?
q = p->next;
p->next = q->next;
free(q);
循环链表删除一个结点,同样是这三句
双向链表插入一个结点:### 在纸上画图以帮助理解
同样的假设:
结点 p 存储数据 ai (i为下标)
结点 p->next 存储数据 ai+1 (i+1为下标) ### p->next 紧跟在 p 之后
结点 s 存储数据 e
p, s 为相应结点的指针
现在要将 结点s 插入到 结点p 和 结点p->next 之间,代码上如何实现?
s->prev = p;
s->next = p->next;
p->next->prev = s;// p->next != NULL 时才有这一句
p->next = s;
这四句同样不能换位
顺序是:先搞定s 的前驱和后继,再搞定后结点 p->next 的前驱,最后解决前结点 p 的后继
双向循环链表插入一个结点,同样是这四句,而且:
s->prev = p;
s->next = p->next;
p->next->prev = s;// p->next != 头结点 这一句判断也不需要!
p->next = s;
双向链表删除一个结点:### 在纸上画图以帮助理解
假设:
结点 p->prev 存储数据 ai-1 (i-1为下标)
结点 p 存储数据 ai (i为下标)
结点 p->next 存储数据 ai+1 (i+1为下标)
现在要将 结点p 删除,代码上如何实现?
p->prev->next = p->next;
p->next->prev = p->prev;// p->next != NULL 时才有这一句
free§;
双向循环链表删除一个结点,同样是这三句,而且:
p->prev->next = p->next;
p->next->prev = p->prev;// p->next != 头结点 这一句判断也不需要!
free§;
list.h
#pragma once
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;
typedef struct ListNodeData {
int a;
float b;
double c;
int KeyID;
char name[128];
char description[256];
char* details;
}ElemType2;
typedef struct Node {
ElemType data;//数据可以是其它的复杂情况,比如结构体,这里为了讲述问题方便故意让 ElemType 非常简单
struct Node* next;
}NodeL;//单链表
typedef struct Node2 {
ElemType data;
struct Node2* prev;
struct Node2* next;
}NodeD;//双向链表
typedef struct Node3 {
ElemType data;
struct Node3* next;
}NodeLL;//循环链表
typedef struct Node4 {
ElemType data;
struct Node4* prev;
struct Node4* next;
}NodeLD;//双向循环链表
typedef struct Node5 {
ElemType2 data;
struct Node5* next;
}NodeL2;
typedef struct Node* LinkList;
typedef struct Node2* DLinkList;
typedef struct Node3* LLinkList;
typedef struct Node4* LDLinkList;
typedef struct Node5* LinkList2;
//单链表
extern LinkList list_init(void);
extern Status list_append(LinkList* L, ElemType e);
extern Status list_insert(LinkList* L, int i, ElemType e);
extern Status list_delete(LinkList* L, int i, ElemType* e);
extern int list_length(LinkList L);
extern Status list_get_elem(LinkList L, int i, ElemType* e);
extern Status list_modify(LinkList* L, int i, ElemType newData);
extern void list_create_head(LinkList* L, int n);
extern void list_create_tail(LinkList* L, int n);
extern Status list_clear(LinkList* L);
extern void list_access(LinkList L);
extern void list_access2(LinkList L);
extern Status list_access_ext(LinkList L, NodeL* node);//待实现
extern Status list_head_insert(LinkList* L, ElemType e);
extern Status list_tail_insert(LinkList* L, ElemType e);
extern Status list_head_delete(LinkList* L, ElemType* e);
extern Status list_tail_delete(LinkList* L, ElemType* e);
extern Status list_delete_by_node_address(LinkList* L, NodeL* node);
extern Status list_delete_by_data(LinkList* L, ElemType e);
extern Status list_delete_by_name(LinkList* L, const char* name, ElemType2* e);//待实现
extern Status list_get_elem_by_name(LinkList L, const char* name, ElemType2* e);//待实现
extern NodeL* list_get_node(LinkList L, int i);
extern NodeL* list_copy_node(NodeL* node);
extern NodeL* list_get_node_by_name(LinkList L, const char* name);//待实现
extern NodeL* list_get_node_by_KeyID(LinkList L, int KeyID);//待实现
extern int list_node_location(LinkList L, NodeL* node);
extern int list_elem_location(LinkList L, ElemType e);
extern int list_name_location(LinkList L, const char* name);//待实现
extern int list_KeyID_location(LinkList L, int KeyID);//待实现
extern Status list_node_compare(LinkList L, NodeL* node1, NodeL* node2);
extern Status list_reverse(LinkList* L);
extern Status list_remove_duplication(LinkList* L);
extern Status list_remove_duplication_ext(LinkList* L);
extern LinkList list_joint(LinkList L1, LinkList L2);
extern LinkList list_merge(LinkList L1, LinkList L2);
extern LinkList list_merge2(LinkList L1, LinkList L2);
extern LinkList list_merge_ext(LinkList L1, LinkList L2);
static LinkList list_slice(LinkList L, int i, int j);
extern LinkList list_slice_ext(LinkList L, int i, int j);
extern LinkList list_create_subset(LinkList L, ElemType data[], int n);
extern Status list_sort_by_name(LinkList* L);//待实现
//双向链表
extern DLinkList doubly_list_init(void);
extern Status doubly_list_append(DLinkList* L, ElemType e);
extern Status doubly_list_insert(DLinkList* L, int i, ElemType e);
extern Status doubly_list_delete(DLinkList* L, int i, ElemType* e);
extern int doubly_list_length(DLinkList L);
extern Status doubly_list_get_elem(DLinkList L, int i, ElemType* e);
extern Status doubly_list_modify(DLinkList* L, int i, ElemType newData);
extern void doubly_list_create_head(DLinkList* L, int n);
extern void doubly_list_create_tail(DLinkList* L, int n);
extern Status doubly_list_clear(DLinkList* L);
extern void doubly_list_access(DLinkList L);
extern void doubly_list_access_back(DLinkList L);
extern Status doubly_list_head_insert(DLinkList* L, ElemType e);
extern Status doubly_list_tail_insert(DLinkList* L, ElemType e);
extern Status doubly_list_head_delete(DLinkList* L, ElemType* e);
extern Status doubly_list_tail_delete(DLinkList* L, ElemType* e);
extern Status doubly_list_delete_by_node_address(DLinkList* L, NodeD* node);
extern Status doubly_list_delete_by_data(DLinkList* L, ElemType e);
extern NodeD* doubly_list_get_node(DLinkList L, int i);
extern NodeD* doubly_list_copy_node(NodeD* node);
extern int doubly_list_node_location(DLinkList L, NodeD* node);
extern int doubly_list_elem_location(DLinkList L, ElemType e);
extern Status doubly_list_node_compare(DLinkList L, NodeD* node1, NodeD* node2);
extern Status doubly_list_reverse(DLinkList* L);
extern Status doubly_list_remove_duplication(DLinkList* L);
extern Status doubly_list_remove_duplication_ext(DLinkList* L);
extern DLinkList doubly_list_joint(DLinkList L1, DLinkList L2);
extern DLinkList doubly_list_merge(DLinkList L1, DLinkList L2);
extern DLinkList doubly_list_merge2(DLinkList L1, DLinkList L2);
extern DLinkList doubly_list_merge_ext(DLinkList L1, DLinkList L2);
static DLinkList doubly_list_slice(DLinkList L, int i, int j);
extern DLinkList doubly_list_slice_ext(DLinkList L, int i, int j);
extern DLinkList doubly_list_create_subset(DLinkList L, ElemType data[], int n);
extern Status doubly_list_sort_by_name(DLinkList* L);//待实现
//循环链表
extern LLinkList loop_list_init(void);
extern Status loop_list_append(LLinkList* L, ElemType e);
extern Status loop_list_insert(LLinkList* L, int i, ElemType e);
extern Status loop_list_delete(LLinkList* L, int i, ElemType* e);
extern int loop_list_length(LLinkList L);
extern Status loop_list_get_elem(LLinkList L, int i, ElemType* e);
extern Status loop_list_modify(LLinkList* L, int i, ElemType newData);
extern void loop_list_create_head(LLinkList* L, int n);
extern void loop_list_create_tail(LLinkList* L, int n);
extern Status loop_list_clear(LLinkList* L);
extern void loop_list_access(LLinkList L);
extern Status loop_list_head_insert(LLinkList* L, ElemType e);
extern Status loop_list_tail_insert(LLinkList* L, ElemType e);
extern Status loop_list_head_delete(LLinkList* L, ElemType* e);
extern Status loop_list_tail_delete(LLinkList* L, ElemType* e);
extern Status loop_list_delete_by_node_address(LLinkList* L, NodeLL* node);
extern Status loop_list_delete_by_data(LLinkList* L, ElemType e);
extern NodeLL* loop_list_get_node(LLinkList L, int i);
extern NodeLL* loop_list_copy_node(NodeLL* node);
extern int loop_list_node_location(LLinkList L, NodeLL* node);
extern int loop_list_elem_location(LLinkList L, ElemType e);
extern Status loop_list_node_compare(LLinkList L, NodeLL* node1, NodeLL* node2);
extern Status loop_list_reverse(LLinkList* L);
extern Status loop_list_remove_duplication(LLinkList* L);
extern Status loop_list_remove_duplication_ext(LLinkList* L);
extern LLinkList loop_list_joint(LLinkList L1, LLinkList L2);
extern LLinkList loop_list_merge(LLinkList L1, LLinkList L2);
extern LLinkList loop_list_merge2(LLinkList L1, LLinkList L2);
extern LLinkList loop_list_merge_ext(LLinkList L1, LLinkList L2);
static LLinkList loop_list_slice(LLinkList L, int i, int j);
extern LLinkList loop_list_slice_ext(LLinkList L, int i, int j);
extern LLinkList loop_list_create_subset(LLinkList L, ElemType data[], int n);
extern Status loop_list_sort_by_name(LLinkList* L);//待实现
//双向循环链表
extern LDLinkList loop_doubly_list_init(void);
extern Status loop_doubly_list_append(LDLinkList* L, ElemType e);
extern Status loop_doubly_list_insert(LDLinkList* L, int i, ElemType e);
extern Status loop_doubly_list_delete(LDLinkList* L, int i, ElemType* e);
extern int loop_doubly_list_length(LDLinkList L);
extern Status loop_doubly_list_get_elem(LDLinkList L, int i, ElemType* e);
extern Status loop_doubly_list_modify(LDLinkList* L, int i, ElemType newData);
extern void loop_doubly_list_create_head(LDLinkList* L, int n);
extern void loop_doubly_list_create_tail(LDLinkList* L, int n);
extern Status loop_doubly_list_clear(LDLinkList* L);
extern void loop_doubly_list_access(LDLinkList L);
extern void loop_doubly_list_access_back(LDLinkList L);
extern Status loop_doubly_list_head_insert(LDLinkList* L, ElemType e);
extern Status loop_doubly_list_tail_insert(LDLinkList* L, ElemType e);
extern Status loop_doubly_list_head_delete(LDLinkList* L, ElemType* e);
extern Status loop_doubly_list_tail_delete(LDLinkList* L, ElemType* e);
extern Status loop_doubly_list_delete_by_node_address(LDLinkList* L, NodeLD* node);
extern Status loop_doubly_list_delete_by_data(LDLinkList* L, ElemType e);
extern NodeLD* loop_doubly_list_get_node(LDLinkList L, int i);
extern NodeLD* loop_doubly_list_copy_node(NodeLD* node);
extern int loop_doubly_list_node_location(LDLinkList L, NodeLD* node);
extern int loop_doubly_list_elem_location(LDLinkList L, ElemType e);
extern Status loop_doubly_list_node_compare(LDLinkList L, NodeLD* node1, NodeLD* node2);
extern Status loop_doubly_list_reverse(LDLinkList* L);
extern Status loop_doubly_list_remove_duplication(LDLinkList* L);
extern Status loop_doubly_list_remove_duplication_ext(LDLinkList* L);
extern LDLinkList loop_doubly_list_joint(LDLinkList L1, LDLinkList L2);
extern LDLinkList loop_doubly_list_merge(LDLinkList L1, LDLinkList L2);
extern LDLinkList loop_doubly_list_merge2(LDLinkList L1, LDLinkList L2);
extern LDLinkList loop_doubly_list_merge_ext(LDLinkList L1, LDLinkList L2);
static LDLinkList loop_doubly_list_slice(LDLinkList L, int i, int j);
extern LDLinkList loop_doubly_list_slice_ext(LDLinkList L, int i, int j);
extern LDLinkList loop_doubly_list_create_subset(LDLinkList L, ElemType data[], int n);
extern Status loop_doubly_list_sort_by_name(LDLinkList* L);//待实现
//两把枪的 KeyID 之间相隔100,意思是允许玩家最多能同时拥有100把这种型号的武器
#define SINGLE_WEAPON_MAX 100
#define WEAPON_BASE 100000
#define KeyID_M4A1 (WEAPON_BASE + 0)
#define KeyID_AK47 (WEAPON_BASE + 100)
#define KeyID_AWP (WEAPON_BASE + 200)
#define KeyID_SG552 (WEAPON_BASE + 300)
list.c
注意:只能有一个 main() 函数
如果需要调试,请打开相关的注释 #if 0 …… #endif
编写平台:Windows11 + Visual Studio 2022
谭浩强
//C include
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <windows.h>
#include <time.h>
//my include
#include "list.h"
//先看谭浩强的例子 ### 没有头结点 ### 尾插法
#if 0
struct Student {
int num;
float score;
struct Student* next;
};
int n = 0;//记录链表中结点的个数
struct Student *create(void)//尾插法
{
int aaa;
float bbb;
int count = sizeof(struct Student);
struct Student* head, * p1, * p2;
n = 0;
head = NULL;
p1 = p2 = (struct Student*)malloc(count);
//scanf("%ld, %f", &p1->num, &p1->score);
scanf_s("%ld, %f", &p1->num, &p1->score);/*引号内可以有逗号,也可以有空格*/
#if 0//谭浩强的原版代码
while (p1->num) {
n++;
if (n == 1)
head = p1;
else
p2->next = p1;
p2 = p1;
p1 = (struct Student*)malloc(count);
scanf_s("%ld, %f", &p1->num, &p1->score);
}
/*
怎么理解谭浩强的这段代码? ### 2022.12.18
尾插法 ### 没有头结点
p2 始终指向链表尾
新添加的结点用 p1 表示,并将 p1 加到链表尾
如果 scanf 输入的不是 0, 0 --- p2 的 next 指针域就是 p1
如果 scanf 输入的正是 0, 0 --- p2 的 next 指针域就是 NULL
*/
#else//我修改后的样子
while (p1->num) {
//终端输入 0 或者 0, 0 结束输入
n++;//记录链表中结点的个数
if (n == 1)
head = p1;
//假定让 p2 始终指向链表尾
//一开始,head = p1; 链表中只有p1一个结点 ### 没有头结点
//新添加的结点用 p1 表示,并将 p1 加到链表尾
//如此一来 p2 变成了倒数第二,所以:p2->next = p1;
//为了让 p2 再次变成尾结点,则 p2 = p1;
//结点添加完毕,最后,让尾结点的 next 指针为 NULL,p2->next = NULL;
scanf_s("%ld, %f", &aaa, &bbb);
if (aaa == 0)//不是有效的新结点就退出循环
break;
p1 = (struct Student*)malloc(count);
p2->next = p1;
p2 = p1;
//给新结点赋值
p1->num = aaa;
p1->score = bbb;
}
#endif
p2->next = NULL;
return head;
}
void print(struct Student *head)
{
struct Student* p = head;
printf("\nNow, these %d records are: \n", n);
if (head != NULL) {
do {
printf("%ld, %5.1f\n", p->num, p->score);
p = p->next;
} while (p != NULL);
}
}
int main(void)
{
struct Student* head;
head = create();
print(head);
return 0;
}
#endif
单链表
//单链表
//创建一个带有头结点的空链表
LinkList list_init(void)
{
LinkList pHead;
pHead = (LinkList)malloc(sizeof(NodeL));
assert(pHead);
memset(pHead, 0, sizeof(NodeL));
pHead->next = NULL;
return pHead;
}
//初始条件:链表 L 已经存在
//操作结果:在 L 的尾部添加一个新结点,其数据值为 e,链表 L 的长度加 1
Status list_append(LinkList* L, ElemType e)
{
LinkList p, r;
r = *L;
while (r->next) {
r = r->next;//将 r 变成尾指针
}
p = (LinkList)malloc(sizeof(NodeL));
assert(p);
memset(p, 0, sizeof(NodeL));
r->next = p;
p->next = NULL;
p->data = e;
return OK;
}
//初始条件:链表 L 已经存在,并且 1 <= i <= list_length(L)
//操作结果:在 L 中的第 i 个结点【之前】插入新结点,其数据值为 e,链表 L 的长度加 1
Status list_insert(LinkList *L, int i, ElemType e)// 这里:L 是一个二重指针
{
int j = 1;
LinkList p, s;
int count = list_length(*L);
if (count == 0) {
return list_append(L, e);
}
if (i >= 1 && i <= count)
{
p = *L;
while (p && j < i) {
//寻找第 i-1 个结点
p = p->next;
++j;
}
if (!p || j > i)
return ERROR;
s = (LinkList)malloc(sizeof(NodeL));
assert(s);
memset(s, 0, sizeof(NodeL));
s->next = p->next;
p->next = s;
s->data = e;
return OK;
}
return ERROR;
}
//初始条件:链表 L 已经存在,并且 1 <= i <= list_length(L)
//操作结果:删除 L 的第 i 个结点,用 e 返回其值,链表 L 的长度减 1
Status list_delete(LinkList *L, int i, ElemType *e)
{
int j = 1;
LinkList p, q;
int count = list_length(*L);
if (i >= 1 && i <= count)
{
p = *L;
while (p->next && j < i) {
//寻找第 i-1 个结点
p = p->next;
++j;
}
if (!(p->next) || j > i)
return ERROR;//第i个结点不存在就报错
q = p->next;//q指向第i个结点
p->next = q->next;
*e = q->data;
free(q);
return OK;
}
return ERROR;
}
//初始条件:链表 L 已经存在
//操作结果:返回链表的长度 ### 不包括头结点
int list_length(LinkList L)
{
int i = 0;
LinkList p = L->next;
while (p) {
p = p->next;
i++;
}
return i;
}
//初始条件:链表 L 已经存在,并且 1 <= i <= list_length(L)
//操作结果:用 e 返回 L 中第 i 个元素的值
Status list_get_elem(LinkList L, int i, ElemType* e)
{
int j = 1;
LinkList p = L->next;
int count = list_length(L);
if (i >= 1 && i <= count)
{
while (p && j < i) {
p = p->next;
++j;
}
if (!p || j > i)
return ERROR;
*e = p->data;
return OK;
}
return ERROR;
}
//初始条件:链表 L 已经存在,并且 1 <= i <= list_length(L)
//操作结果:修改 L 中第 i 个元素的值为指定的 newData
Status list_modify(LinkList *L, int i, ElemType newData)
{
int j = 1;
LinkList p = (*L)->next;
int count = list_length(*L);
if (i >= 1 && i <= count)
{
while (p && j < i) {
p = p->next;
++j;
}
if (!p || j > i)
return ERROR;
p->data = newData;
return OK;
}
return ERROR;
}
//创建带头结点的单链表 L,L 有 n 的结点,每个结点的值随机产生 ### 每次都在 结点1 的位置插入新结点
void list_create_head(LinkList *L, int n)//头插法
{
LinkList p;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(NodeL));
assert(*L);
memset(*L, 0, sizeof(NodeL));
(*L)->next = NULL;//保证尾结点的 next 指针域为空
for (i = 0; i < n; i++) {
p = (LinkList)malloc(sizeof(NodeL));
assert(p);
memset(p, 0, sizeof(NodeL));
p->data = rand() % 100 + 1;
p->next = (*L)->next;//起初,因为 L 是空链表,头结点即是尾结点,n==1 或者 i==0 时,第一个加进来的结点就是新的尾结点,L 是头结点,所以 (*L)->next == NULL,这就保证了最终的尾结点指针域是 NULL
(*L)->next = p;
}
}
//创建带头结点的单链表 L,L 有 n 的结点,每个结点的值随机产生 ### 每次都在链表的最后位置加上新结点
/*
假设:r 始终是链表的尾结点
在尾部添加一个新结点p后
p变成尾结点,r 倒数第二
因此:r->next = p;
为了让 r 再次变成尾结点,将 p 赋值给 r
因此:r = p;
所有结点添加完成,将尾结点的指针域指空:r->next = NULL;
*/
void list_create_tail(LinkList* L, int n)//尾插法
{
LinkList p, r;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(NodeL));
assert(*L);
memset(*L, 0, sizeof(NodeL));
(*L)->next = NULL;
r = *L;
while (r->next) {
r = r->next;//r 始终是链表的尾结点
}
for (i = 0; i < n; i++) {
p = (LinkList)malloc(sizeof(NodeL));
assert(p);
memset(p, 0, sizeof(NodeL));
p->data = rand() % 100 + 1;
r->next = p;
r = p;
}
r->next = NULL;
}
//初始条件:链表 L 已经存在
//操作结果:将链表清空 ### 每次都删除结点1 ### 但仍然保留着头结点
Status list_clear(LinkList *L)
{
LinkList p, q;
p = (*L)->next;
while (p) {
q = p->next;//先保存 p->next 的值(内存地址),即结点2(如果存在的话),防止在 free(p) 时 p->next 的值丢失
free(p);
p = q;//再将保存的 p->next 的值赋值给 p 进行下一轮的循环判断
}
(*L)->next = NULL;//将头结点的后续指针域置空
return OK;
}
//#include <windows.h>
//void Sleep(DWORD dwMilliseconds); 参数为毫秒
//访问包含头结点的链表
void list_access(LinkList L)
{
int i = 0;
LinkList p = L->next;
while (p) {
i++;
printf("第%d个数据:%d\n", i, p->data);
p = p->next;
Sleep(5);//休眠5ms
}
}
//访问不包含头结点的链表
void list_access2(LinkList L)
{
int i = 0;
LinkList p = L;
while (p) {
i++;
printf("第%d个数据:%d\n", i, p->data);
p = p->next;
Sleep(5);//休眠5ms
}
}
//初始条件:链表 L 已经存在 ### 结点 node 位于 L 中
//以 node 为出发点,向前向后访问结点
Status list_access_ext(LinkList L, NodeL* node)
{
return OK;
}
//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
Status list_head_insert(LinkList* L, ElemType e)
{
LinkList s;
s = (LinkList)malloc(sizeof(NodeL));
assert(s);
memset(s, 0, sizeof(NodeL));
s->next = (*L)->next;
(*L)->next = s;
s->data = e;
return OK;
}
//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
//#define list_tail_insert list_append
Status list_tail_insert(LinkList* L, ElemType e)
{
return list_append(L, e);
}
//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status list_head_delete(LinkList* L, ElemType* e)
{
LinkList p;
p = (*L)->next;
if (!p) {
return ERROR;
}
(*L)->next = p->next;
*e = p->data;
free(p);
return OK;
}
//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status list_tail_delete(LinkList* L, ElemType* e)
{
LinkList p, r;
int count = list_length(*L);
if (count == 1) {
p = (*L)->next;
*e = p->data;
free(p);
(*L)->next = NULL;
return OK;
}
else if (count > 1) {
p = r = *L;
while (p->next->next) {
p = p->next;//p最终变成倒数第二个结点
r = p->next;//r最终变成尾结点
}
p->next = NULL;
*e = r->data;
free(r);
return OK;
}
else {
return ERROR;
}
}
//初始条件:链表 L 已经存在
//操作结果:直接删除结点 node
Status list_delete_by_node_address(LinkList* L, NodeL* node)
{
LinkList prevL, curL, nextL;
prevL = nextL = *L;
curL = (*L)->next;
while (curL) {
if (node == curL) {
nextL = curL->next;
prevL->next = nextL;
free(curL);
return OK;
}
prevL = prevL->next;
curL = curL->next;
}
return ERROR;
}
//初始条件:链表 L 已经存在
//操作结果:删除包含着数据 e 的第一个结点
Status list_delete_by_data(LinkList* L, ElemType e)
{
LinkList prevL, curL, nextL;
prevL = nextL = *L;
curL = (*L)->next;
while (curL) {
if (e == curL->data) {
nextL = curL->next;
prevL->next = nextL;
free(curL);
return OK;
}
prevL = prevL->next;
curL = curL->next;
}
return ERROR;
}
//如果链表 L 的数据不再是简单的 ElemType,而是接近真实项目中的数据,比如像 ElemType2 这样,其中包含着一个 name 数组
Status list_delete_by_name(LinkList* L, const char* name, ElemType2* e)
{
return OK;
}
Status list_get_elem_by_name(LinkList L, const char* name, ElemType2* e)
{
return OK;
}
//初始条件:链表 L 已经存在
//操作结果:返回第 i 个位置的结点的地址
NodeL* list_get_node(LinkList L, int i)
{
int j = 0;
int count = list_length(L);
NodeL* p = L;
if (!(1 <= i <= count)) {
return NULL;
}
while (j < i)
{
j++;
p = p->next;
}
return (p!=L) ? p : NULL;
}
//复制一个结点
NodeL* list_copy_node(NodeL* node)
{
NodeL* p = (NodeL*)malloc(sizeof(NodeL));
assert(p);
memset(p, 0, sizeof(NodeL));
p->data = node->data;
return p;
}
//初始条件:链表 L 已经存在
//如果链表 L 的数据不再是简单的 ElemType,而是接近真实项目中的数据,比如像 ElemType2 这样,其中包含着一个 name 数组
//操作结果:返回 name 对应的第一个结点的地址
NodeL* list_get_node_by_name(LinkList L, const char* name)
{
NodeL* p = L;
return p;
}
//初始条件:链表 L 已经存在
//如果链表 L 的数据不再是简单的 ElemType,而是接近真实项目中的数据,比如像 ElemType2 这样,其中包含着一个 KeyID
//操作结果:返回 KeyID 对应的第一个结点的地址
NodeL* list_get_node_by_KeyID(LinkList L, int KeyID)
{
NodeL* p = L;
return p;
}
//初始条件:链表 L 已经存在 ### 返回 node 的位序
//如果 node 的地址与 链表 L 中任何一个结点的地址相同,则认为 node 存在于 L 中,返回值 >0; 否则返回值为 0
int list_node_location(LinkList L, NodeL* node)
{
int i = 0;
Status find = FALSE;
LinkList p = L;
while (p->next) {
i++;
p = p->next;
if (p == node) {
find = TRUE;
break;
}
}
return (find == TRUE) ? i : 0;
}
//初始条件:链表 L 已经存在 ### 返回 e 的位序
//如果数据 e 与链表 L 中某个结点的数据完全相同,则认为 e 存在于 L 中,返回值 >0; 否则返回值为 0
int list_elem_location(LinkList L, ElemType e)
{
int i = 0;
Status find = FALSE;
LinkList p = L;
while (p->next) {
i++;
p = p->next;
if (p->data == e) {
find = TRUE;
break;
}
}
return (find == TRUE) ? i : 0;
}
int list_name_location(LinkList L, const char* name)
{
int i = 0;
return i;
}
int list_KeyID_location(LinkList L, int KeyID)
{
int i = 0;
return i;
}
//初始条件:链表 L 已经存在 ### node1 和 node2 属于链表 L
//比较 node1 与 node2 的数据是否完全相同
//完全相同,返回 TRUE;否则,返回 FALSE
Status list_node_compare(LinkList L, NodeL* node1, NodeL* node2)
{
int i = list_node_location(L, node1);
int j = list_node_location(L, node2);
if (i == 0 || j == 0) {
return FALSE;
}
if (node1->data == node2->data) {
return TRUE;
}
return FALSE;
}
//初始条件:链表 L 已经存在
//操作结果:将链表逆序
//链表逆序 = 遍历 + 头插入
Status list_reverse(LinkList* L)
{
LinkList p, q, r;
r = p = (*L)->next;//最开始让p指向结点1
while (p)//始终将结点p插入在结点1的位置,p从结点1挨个遍历到尾结点 ### while 循环结束时,结点1 就变成了尾结点,r 即是尾结点
{
q = p->next;
p->next = (*L)->next;
(*L)->next = p;
p = q;
}
r->next = NULL;
return OK;
}
//多个结点可以包含相同的数据,因此有必要去除重复的数据
Status list_remove_duplication(LinkList* L)
{
LinkList p, q;
ElemType data1, data2;
p = *L;
q = p->next;
while (p->next) {
p = p->next;
data1 = p->data;
while (q->next) {
q = q->next;
data2 = q->data;
if (data1 == data2) {
if (list_delete_by_data(L, data1) == ERROR) {
return ERROR;
}
p = *L;//删除一个重复的结点后要重新设置循环条件,从第一个结点开始重新查找重复的结点
break;
}
}
q = p->next;
}
return OK;
}
Status list_remove_duplication_ext(LinkList* L)
{
int i = 0;
int j = 0;
LinkList p, q;
ElemType data1, data2;
p = *L;
q = p->next;
while (p->next) {
i++;
p = p->next;
data1 = p->data;
while (q->next) {
q = q->next;
data2 = q->data;
if (data1 == data2) {
if (list_delete_by_data(L, data1) == ERROR) {
return ERROR;
}
p = *L;//删除一个重复的结点后要重新设置循环条件
for (j = 1; j < i; j++) {
//跳过前面 i-1 个已经确认没有重复数据的结点
p = p->next;
}
--i;//因为已经删除了一个结点,所以 i 要减一
break;
}
}
q = p->next;
}
return OK;
}
//初始条件:链表 L1 和 L2 已经存在
//操作结果:将两个链表直接拼接成一个
//最后销毁 L2 的头结点
LinkList list_joint(LinkList L1, LinkList L2)
{
LinkList h1, h2, p, q;
h1 = L1;
h2 = L2;
p = L1->next;
q = L2->next;
while (p->next) {
p = p->next;
}
p->next = q;
free(h2);
return h1;
}
//通常,一个结点在添加进某个链表中的时候,它的前驱指针和后继指针就固定了,因此,一个结点不太可能同时存在于两个及更多的链表中
//由于 L1 中的结点和 L2 中的结点通常不可能拥有相同的内存起始地址 p
//而在 list_merge_NOK_1() 和 list_merge_NOK_2() 两个函数中都使用了 i = list_node_location(L1, p)
//因此,两个函数的 i 值将一直是 0 ### 删除重复结点的动作就不能执行
//但凡事都不绝对 ### 如果把链表的某个片段取出来放到其它链表中,再比较包含公共片段的两个链表,那么在这段公共的链表片段中就会出现 "数据、前驱指针、后继指针" 完全相同的结点了
//只有在这种特殊的情况下,list_merge_NOK_1() 和 list_merge_NOK_2() 两个函数才能起作用
//但这会有一个明显的缺点:一旦这个公共的链表片段的第一个或最后一个结点被删除,所有包含该公共片段的链表都会受到影响,甚至导致链表断裂,产生灾难
LinkList list_merge_NOK_1(LinkList L1, LinkList L2)//使用严重受限 --- 不推荐
{
int n = 0;
ElemType e = 0;
LinkList p = L1;
LinkList h = L1;
while (p->next) {
p = p->next;
n = list_node_location(L2, p);
if (n > 0) {
list_delete(&L2, n, &e);//对于 L1 中的每一个结点p,如果p在 L2 中有相同的副本,就将该副本从 L2 中删除
n = 0;
}
}
if (list_length(L2) > 0) {
h = list_joint(L1, L2);
}
else {
free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间
}
return h;
}
LinkList list_merge_NOK_2(LinkList L1, LinkList L2)//使用严重受限 --- 不推荐
{
int n = 0;
LinkList p = L2;
LinkList h = L1;
while (p->next) {
p = p->next;
n = list_node_location(L1, p);
if (n > 0) {
list_delete_by_node_address(&L2, p);//对于 L2 中的每一个结点p,如果p在 L1 中有相同的副本,就将该副本从 L2 中删除
p = L2;//因为是对 L2 的每一个结点在做循环,所以当 L2 被删除一个结点后,必须更新循环条件
n = 0;
}
}
if (list_length(L2) > 0) {
h = list_joint(L1, L2);
}
else {
free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间
}
return h;
}
//list_merge() & list_merge2()
//初始条件:链表 L1 和 L2 已经存在
//操作结果:将 L2 中的结点依次添加到 L1 中,前提:即将添加的结点的数据部分与 L1 中任何一个结点的数据都不同
//最后销毁 L2 的头结点
//如果 L2 的所有结点所包含的数据在 L1 中都存在,则完全销毁 L2
//修改 list_merge_NOK_1() ,不使用 list_node_location(),换成 list_elem_location() ### 应该就可以了
LinkList list_merge(LinkList L1, LinkList L2)
{
int n = 0;
ElemType e = 0;
LinkList p = L1;
LinkList h = L1;
while (p->next) {
p = p->next;
n = list_elem_location(L2, p->data);
if (n > 0) {
list_delete(&L2, n, &e);//对于 L1 中的每一个结点p,如果p在 L2 中有相同的副本,就将该副本从 L2 中删除
n = 0;
}
}
if (list_length(L2) > 0) {
h = list_joint(L1, L2);
}
else {
free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间
}
return h;
}
//修改 list_merge_NOK_2(),不使用 list_node_location(),换成 list_elem_location()
//不使用 list_delete_by_node_address(),换成 list_delete_by_data() ### 应该就可以了
LinkList list_merge2(LinkList L1, LinkList L2)
{
int n = 0;
int i = 0;
int j = 0;
ElemType e = 0;
LinkList p = L2;
LinkList h = L1;
while (p->next) {
i++;
p = p->next;
n = list_elem_location(L1, p->data);
if (n > 0) {
list_delete_by_data(&L2, p->data);//对于 L2 中的每一个结点p,如果p在 L1 中有相同的副本,就将该副本从 L2 中删除
p = L2;//因为是对 L2 的每一个结点在做循环,所以当 L2 被删除一个结点后,必须更新循环条件
for (j = 1; j < i; j++) {
//跳过前面 i-1 个已经确认没有重复数据的结点
p = p->next;
}
--i;//因为已经删除了一个结点,所以 i 要减一
n = 0;
}
}
if (list_length(L2) > 0) {
h = list_joint(L1, L2);
}
else {
free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间
}
return h;
}
//初始条件:链表 L1 和 L2 已经存在
//将 L1 和 L2 拼接在一起,保证每一个结点的数据部分都是独一无二的
LinkList list_merge_ext(LinkList L1, LinkList L2)
{
LinkList h = list_joint(L1, L2);
return (list_remove_duplication_ext(&h) == OK) ? h : NULL;
}
//初始条件:链表 L 已经存在 ### 截取 L 的一个连续的子片段
// 1 <= i <= list_length(L) && 1 <= j <= list_length(L)
//操作结果:对链表进行切片,返回包含 结点i 和 结点j 以及 它们中间的所有结点组成的链表片段 ### 没有头结点
//注意:如果你截取链表 L 的子片段 L0 是为了把它拼接到另外一个链表中去 ### 这将会产生极大的隐患
//当链表 L 被清空时,子片段 L0 也就被清空了,这将导致与 L0 连接的链表在 L0 两端的连接处断开,产生灾难
//一定要小心使用 list_slice() !!!
//list_slice() 不提供给外部使用!!!
static LinkList list_slice(LinkList L, int i, int j)
{
int k = 0;
int len = 0;
int start = 0;
int count = 0;
LinkList p = L;
LinkList r = L;
count = list_length(L);
assert(1 <= i <= count);
assert(1 <= j <= count);
start = (i <= j) ? i : j;