链表是一种可以动态的进行内存分配的数据结构 ### 相当于长度不固定的结构体数组
链表中的元素在内存中的地址可以是不连续的
链表这种数据结构必须使用指针才能实现
用结构体建立链表是最合适的

例如:

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 0typedef 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;elsep2->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;    }
#endifp2->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指向结点1while (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;len = abs(i - j);for (k = 0; k < start; k++) {p = p->next;//p是最终链表片段的第一个结点}for (k = 0, r = p; k < len; k++) {r = r->next;//r是最终链表片段的最后一个结点}//r->next = NULL;//注意:这会导致被截取片段的链表意外断裂!!!但是没有这一句,子链表又不能正常的结束!!!return p;
}//为 list_slice() 产生的链表子片段的每个结点分配新的存储空间,并附加一个头结点
LinkList list_slice_ext(LinkList L, int i, int j)
{int k = abs(i - j);LinkList r = NULL;LinkList s = NULL;LinkList pHead = NULL;LinkList tmp = list_slice(L, i, j);pHead = s = (NodeL*)malloc(sizeof(NodeL));assert(s);memset(s, 0, sizeof(NodeL));for (; k >= 0; k--) {r = (NodeL*)malloc(sizeof(NodeL));assert(r);memset(r, 0, sizeof(NodeL));memcpy(r, tmp, sizeof(NodeL));s->next = r;s = s->next;tmp = tmp->next;}r->next = NULL;return pHead;
}//初始条件:链表 L 已经存在
//获取 L 的一个子集,不一定是 L 的连续子片段,组装成一个新的链表
LinkList list_create_subset(LinkList L, ElemType data[], int n)
{int i = 0;int j = 0;LinkList s = L->next;LinkList t = NULL;LinkList q = NULL;LinkList r = NULL;//r是尾结点LinkList pHead = NULL;pHead = q = (NodeL*)malloc(sizeof(NodeL));assert(q);memset(q, 0, sizeof(NodeL));for (i = 0; i < n; i++) {while (s) {j++;if (s->data == data[i]) {//找到一个目标结点t = list_get_node(L, j);r = (NodeL*)malloc(sizeof(NodeL));assert(r);memset(r, 0, sizeof(NodeL));r->data = t->data;q->next = r;q = q->next;break;}s = s->next;}s = L->next;//重新开始从头查找j = 0;}r->next = NULL;return pHead;
}/*
LinkList2 list_create_subset2(LinkList2 L, char* name[], int n)
{}*///初始条件:链表 L 已经存在
//操作结果:将链表按照某种规则排序
//如果链表 L 的数据不再是简单的 ElemType,而是接近真实项目中的数据,比如像 ElemType2 这样,其中包含着一个 name 数组
//说得更具体一点,假设一款游戏中用链表存放着某个具体玩家的武器,显然,每件武器都会有一个独一无二的名字,还会有一个独一无二的关键字KeyID(当某件武器同时拥有多把时可以区分它们)
//怎么给这些武器排序呢?
Status list_sort_by_name(LinkList* L)
{return OK;
}#if 0
void test1(void)
{ElemType aaa, bbb;LinkList pHead;list_create_head(&pHead, 10);list_access(pHead);printf("\n\n");list_insert(&pHead, 3, 10000);list_access(pHead);printf("\n\n");list_delete(&pHead, 6, &aaa);printf("aaa:%d\n", (int)aaa);list_access(pHead);list_clear(&pHead);free(pHead);printf("\n\n");list_create_tail(&pHead, 20);list_access(pHead);list_get_elem(pHead, 15, &bbb);printf("bbb:%d\n", (int)bbb);printf("\n\n");list_clear(&pHead);free(pHead);pHead = NULL;
}//真实项目中,每个链表结点是各自独立添加的,通常不是一次就加入多个结点
//因此,真实项目是:一开始初始化一个空链表,然后一个一个的添加结点
//先 list_init() 然后 list_append() 、 list_insert()、 list_delete() 等
//实际上 list_create_head(&pHead, 0) 和 list_create_tail(&pHead, 0) 也能实现 list_init() 的功能void test2(void)
{LinkList pHead;ElemType aaa = 100;ElemType bbb = 200;pHead = list_init();list_append(&pHead, aaa);list_append(&pHead, bbb);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test3(void)
{LinkList pHead;ElemType aaa = 300;ElemType bbb = 400;ElemType ccc = 500;pHead = list_init();list_insert(&pHead, 1, aaa);list_insert(&pHead, 1, bbb);list_insert(&pHead, 1, ccc);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test4(void)
{LinkList pHead;ElemType aaa = 600;ElemType bbb = 700;ElemType ccc = 800;ElemType ddd = 900;ElemType eee = 1000;ElemType fff = 2000;ElemType ggg;ElemType hhh;pHead = list_init();list_insert(&pHead, 1, aaa);list_append(&pHead, bbb);list_insert(&pHead, 1, ccc);list_append(&pHead, ddd);list_access(pHead);printf("\n\n");list_insert(&pHead, 2, eee);list_access(pHead);printf("\n\n");list_insert(&pHead, 4, fff);list_access(pHead);printf("\n\n");list_delete(&pHead, 1, &ggg);printf("ggg:%d\n", (int)ggg);list_access(pHead);printf("\n\n");list_delete(&pHead, 2, &hhh);printf("hhh:%d\n", (int)hhh);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test5(void)
{LinkList pHead;list_create_head(&pHead, 0);list_access(pHead);list_clear(&pHead);free(pHead);list_create_tail(&pHead, 0);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test5_2(void)//链表长度为1,删除链表的一个结点会出现什么情况?
{LinkList pHead;ElemType aaa = 100;ElemType bbb;pHead = list_init();list_append(&pHead, aaa);list_access(pHead);list_delete(&pHead, 1, &bbb);list_delete(&pHead, 1, &bbb);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test5_3(void)
{LinkList pHead;ElemType aaa = 12345;ElemType bbb = 23456;ElemType ccc = 34567;list_create_head(&pHead, 10);list_access(pHead);printf("\n\n");list_modify(&pHead, 5, aaa);printf("\n");list_access(pHead);printf("\n");list_modify(&pHead, 1, bbb);list_modify(&pHead, 10, ccc);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test5_4(void)//专门测试链表为空表,使用 list_insert() 在 结点1 的位置插入结点的情况 ### 是 OK 的!!!
{LinkList pHead;ElemType aaa = 100;pHead = list_init();list_insert(&pHead, 1, aaa);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test5_5(void)
{LinkList pHead;list_create_head(&pHead, 5);list_access(pHead);printf("\n");list_reverse(&pHead);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test5_6(void)
{LinkList ph1, ph2, ph3;int count = 0;list_create_head(&ph1, 5);list_create_tail(&ph2, 8);list_access(ph1);printf("\n");list_access(ph2);printf("\n");ph3 = list_joint(ph1, ph2);count = list_length(ph3);printf("合并后的新链表长度:%d\n", count);printf("ph3: %p\n", ph3);printf("ph1: %p\n", ph1);printf("ph2: %p\n", ph2);list_access(ph3);printf("\n");list_clear(&ph3);free(ph3);ph3 = NULL;//有没有内存泄漏? ### 没有,下面的释放 ph1 和 ph2 不需要!!!/*if (ph1) {free(ph1);//ph1 和 ph3 实际上指向的是同一块堆内存,而这块堆内存已经被 ph3 释放了,如果重复释放将会导致错误!!!ph1 = NULL;}if (ph2) {free(ph2);//ph2 指向的堆内存在 list_joint() 函数中已经释放了头指针,ph2 链表的身段部分在 ph3 中被释放,如果重复释放将会导致错误!!!ph2 = NULL;}*/
}void test5_7(void)
{LinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee;pHead = list_init();list_tail_insert(&pHead, aaa);list_head_insert(&pHead, bbb);list_insert(&pHead, 1, ccc);list_append(&pHead, ddd);list_access(pHead);printf("\n");list_head_delete(&pHead, &eee);list_access(pHead);printf("\n");list_head_delete(&pHead, &eee);list_access(pHead);printf("\n");list_tail_delete(&pHead, &eee);list_access(pHead);printf("\n");list_tail_delete(&pHead, &eee);list_access(pHead);printf("\n");list_clear(&pHead);free(pHead);pHead = NULL;
}void test5_8(void)
{LinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = list_init();list_head_insert(&pHead, aaa);list_tail_insert(&pHead, bbb);list_insert(&pHead, 1, ccc);list_insert(&pHead, 3, ddd);list_insert(&pHead, 4, eee);list_insert(&pHead, 2, bbb);list_insert(&pHead, 3, bbb);list_access(pHead);printf("\n");list_delete_by_data(&pHead, bbb);list_delete_by_data(&pHead, bbb);list_delete_by_data(&pHead, bbb);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test5_9(void)
{int loc = 0;Status res = FALSE;LinkList pHead;NodeL* node1, *node2, *node3;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = list_init();list_head_insert(&pHead, aaa);list_tail_insert(&pHead, bbb);list_insert(&pHead, 1, ccc);list_insert(&pHead, 3, ddd);list_insert(&pHead, 4, eee);list_insert(&pHead, 2, bbb);list_insert(&pHead, 3, bbb);list_access(pHead);printf("\n");node1 = list_get_node(pHead, 2);node2 = list_get_node(pHead, 3);node3 = list_get_node(pHead, 7);loc = list_node_location(pHead, node1);loc = list_node_location(pHead, node2);loc = list_node_location(pHead, node3);loc = list_elem_location(pHead, aaa);loc = list_elem_location(pHead, ccc);loc = list_elem_location(pHead, ddd);loc = list_elem_location(pHead, bbb);loc = list_elem_location(pHead, eee);res = list_node_compare(pHead, node1, node2);list_delete_by_node_address(&pHead, node1);list_delete_by_node_address(&pHead, node2);list_delete_by_node_address(&pHead, node3);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test5_10(void)
{LinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = list_init();list_head_insert(&pHead, aaa);list_tail_insert(&pHead, bbb);list_insert(&pHead, 1, ccc);list_insert(&pHead, 3, ddd);list_insert(&pHead, 4, eee);list_insert(&pHead, 2, bbb);list_insert(&pHead, 3, bbb);list_access(pHead);printf("\n");//list_remove_duplication(&pHead);list_remove_duplication_ext(&pHead);list_access(pHead);list_clear(&pHead);free(pHead);pHead = NULL;
}void test5_11(void)
{LinkList ph1, ph2, ph3;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;ph1 = list_init();list_head_insert(&ph1, aaa);list_tail_insert(&ph1, bbb);list_insert(&ph1, 1, ccc);list_insert(&ph1, 3, ddd);list_insert(&ph1, 4, eee);list_insert(&ph1, 2, bbb);list_insert(&ph1, 3, bbb);list_access(ph1);printf("\n");ph2 = list_init();list_head_insert(&ph2, 600);list_head_insert(&ph2, 700);list_head_insert(&ph2, 800);list_head_insert(&ph2, 900);list_head_insert(&ph2, 1000);list_insert(&ph2, 3, bbb);list_insert(&ph2, 5, bbb);list_access(ph2);printf("\n");//ph3 = list_merge_NOK_1(ph1, ph2);//失败ph3 = list_merge_NOK_2(ph1, ph2);//失败list_access(ph3);list_clear(&ph3);free(ph3);ph3 = NULL;//有没有内存泄漏? ### 没有,下面的释放 ph1 和 ph2 不需要!!!/*if (ph1) {free(ph1);//ph1 和 ph3 实际上指向的是同一块堆内存,而这块堆内存已经被 ph3 释放了,如果重复释放将会导致错误!!!ph1 = NULL;}if (ph2) {free(ph2);//ph2 指向的堆内存在 list_merge_NOK_2() 函数中已经释放了头指针,ph2 链表的身段部分在 ph3 中被释放,如果重复释放将会导致错误!!!ph2 = NULL;}*/
}void test5_12(void)
{LinkList ph1, ph2, ph3;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;ph1 = list_init();list_head_insert(&ph1, aaa);list_tail_insert(&ph1, bbb);list_insert(&ph1, 1, ccc);list_insert(&ph1, 3, ddd);list_insert(&ph1, 4, eee);list_insert(&ph1, 2, bbb);list_insert(&ph1, 3, bbb);list_access(ph1);printf("\n");ph2 = list_init();list_head_insert(&ph2, 600);list_head_insert(&ph2, 700);list_head_insert(&ph2, 800);list_head_insert(&ph2, 900);list_head_insert(&ph2, 1000);list_insert(&ph2, 3, bbb);list_insert(&ph2, 5, bbb);list_access(ph2);printf("\n");//ph3 = list_merge(ph1, ph2);//ph3 = list_merge2(ph1, ph2);ph3 = list_merge_ext(ph1, ph2);list_access(ph3);list_clear(&ph3);free(ph3);ph3 = NULL;
}void test5_13(void)
{LinkList pHead, p;list_create_tail(&pHead, 20);list_access(pHead);printf("\n");p = list_slice(pHead, 5, 15);list_access2(p);//这里有一个隐蔽的错误:将访问5~20这些结点,而不是5~15printf("\n");list_clear(&pHead);free(pHead);pHead = NULL;//list_clear(&p);// p = list_slice() 并没有产生任何新的结点或链表,它只是截取了其它链表的一部分,在被截取的那个链表被清空的时候,p 指代的链表片段也就被删除了 ### 因此这里重复清空链表将会导致错误!//free(p);//p = NULL;
}void test5_14(void)
{LinkList ph1, ph2, ph3, p;list_create_tail(&ph1, 20);list_access(ph1);printf("\n");p = list_slice_ext(ph1, 5, 15);list_clear(&ph1);free(ph1);ph1 = NULL;list_access(p);printf("\n");list_create_head(&ph2, 5);list_access(ph2);printf("\n");ph3 = list_joint(ph2, p);list_access(ph3);printf("\n");list_clear(&ph3);free(ph3);ph3 = NULL;//有没有内存泄漏? ### 没有,下面的释放 ph2 和 p 不需要!!!/*if (ph2) {free(ph2);//ph2 和 ph3 实际上指向的是同一块堆内存,而这块堆内存已经被 ph3 释放了,如果重复释放将会导致错误!!!ph2 = NULL;}if (p) {free(p);//p 指向的堆内存在 list_joint() 函数中已经释放了头结点,p链表的身段部分在 ph3 中被释放,如果重复释放将会导致错误!!!p = NULL;}*/
}void test5_15(void)
{LinkList ph1, ph2;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;ElemType data[] = {bbb, ccc, eee, 600, 800};int n = sizeof(data) / sizeof(data[0]);ph1 = list_init();list_head_insert(&ph1, aaa);list_tail_insert(&ph1, bbb);list_insert(&ph1, 1, ccc);list_insert(&ph1, 3, ddd);list_insert(&ph1, 4, eee);list_insert(&ph1, 2, bbb);list_insert(&ph1, 3, bbb);list_append(&ph1, 600);list_append(&ph1, 700);list_append(&ph1, 800);list_append(&ph1, 900);list_append(&ph1, 1000);list_access(ph1);printf("\n");for (int i = 0; i < n; i++) {printf("%d\t", data[i]);}printf("\n");ph2 = list_create_subset(ph1, data, n);list_access(ph2);printf("\n");list_clear(&ph1);list_clear(&ph2);ph1 = NULL;ph2 = NULL;
}int main(void)
{test1();test2();test3();test4();test5();test5_2();test5_3();test5_4();test5_5();test5_6();test5_7();test5_8();test5_9();test5_10();test5_11();test5_12();test5_13();test5_14();test5_15();return 0;
}
#endif

双向链表

//双向链表//创建一个带有头结点的双向空链表
DLinkList doubly_list_init(void)
{DLinkList pHead;pHead = (DLinkList)malloc(sizeof(NodeD));assert(pHead);memset(pHead, 0, sizeof(NodeD));pHead->prev = NULL;pHead->next = NULL;return pHead;
}//初始条件:链表 L 已经存在
//操作结果:在 L 的尾部添加一个新结点,其数据值为 e,链表 L 的长度加 1
Status doubly_list_append(DLinkList* L, ElemType e)
{DLinkList p, r;r = *L;while (r->next) {r = r->next;//将 r 变成尾指针}p = (DLinkList)malloc(sizeof(NodeD));assert(p);memset(p, 0, sizeof(NodeD));r->next = p;p->prev = r;p->next = NULL;p->data = e;return OK;
}//初始条件:链表 L 已经存在,并且 1 <= i <= doubly_list_length(L)
//操作结果:在 L 中的第 i 个结点【之前】插入新结点,其数据值为 e,链表 L 的长度加 1
Status doubly_list_insert(DLinkList* L, int i, ElemType e)// 这里:L 是一个二重指针
{int j = 1;DLinkList p, s;int count = doubly_list_length(*L);if (count == 0) {return doubly_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 = (DLinkList)malloc(sizeof(NodeD));assert(s);memset(s, 0, sizeof(NodeD));if (p->next) {s->prev = p;s->next = p->next;p->next->prev = s;//如果 p->next 为空,p->next->prev 就是错的!p->next = s;}else {s->prev = p;s->next = NULL;p->next = s;}s->data = e;return OK;}return ERROR;
}//初始条件:链表 L 已经存在,并且 1 <= i <= doubly_list_length(L)
//操作结果:删除 L 的第 i 个结点,用 e 返回其值,链表 L 的长度减 1
Status doubly_list_delete(DLinkList* L, int i, ElemType* e)
{int j = 1;DLinkList p, q;int count = doubly_list_length(*L);if (i >= 1 && i <= count){q = *L;while (q->next && j < i) {//寻找第 i-1 个结点q = q->next;++j;}if (!(q->next) || j > i)return ERROR;//第i个结点不存在就报错p = q->next;//p指向第i个结点if (p->next) {p->prev->next = p->next;p->next->prev = p->prev;//如果 p->next 为空,p->next->prev 就是错的!}else {p->prev->next = NULL;}*e = p->data;free(p);return OK;}return ERROR;
}//初始条件:链表 L 已经存在
//操作结果:返回链表的长度 ### 不包括头结点
int doubly_list_length(DLinkList L)
{int i = 0;DLinkList p = L->next;while (p) {p = p->next;i++;}return i;
}//初始条件:链表 L 已经存在,并且 1 <= i <= doubly_list_length(L)
//操作结果:用 e 返回 L 中第 i 个元素的值
Status doubly_list_get_elem(DLinkList L, int i, ElemType* e)
{int j = 1;DLinkList p = L->next;int count = doubly_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 <= doubly_list_length(L)
//操作结果:修改 L 中第 i 个元素的值为指定的 newData
Status doubly_list_modify(DLinkList* L, int i, ElemType newData)
{int j = 1;DLinkList p = (*L)->next;int count = doubly_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 doubly_list_create_head(DLinkList* L, int n)//头插法
{DLinkList p;int i;srand(time(0));*L = (DLinkList)malloc(sizeof(NodeD));assert(*L);memset(*L, 0, sizeof(NodeD));(*L)->prev = NULL;(*L)->next = NULL;//保证尾结点的 next 指针域为空for (i = 0; i < n; i++) {p = (DLinkList)malloc(sizeof(NodeD));assert(p);memset(p, 0, sizeof(NodeD));p->data = rand() % 100 + 1;/*要领:new->prev = header;new->next = header->next;//新节点的next指针指向原来的 节点1 的地址if(NULL != header->next)//判断当前结点的下一个结点的地址是否为空{header->next->prev = new;//原来的结点1 的prev指针指向新节点的地址}header->next = new;*/p->prev = *L;p->next = (*L)->next;//起初,因为 L 是空链表,头结点即是尾结点,n==1 或者 i==0 时,第一个加进来的结点就是新的尾结点,L 是头结点,所以 (*L)->next == NULL,这就保证了最终的尾结点指针域是 NULLif ((*L)->next) {(*L)->next->prev = p;}(*L)->next = p;}
}//创建带头结点的双向链表 L,L 有 n 的结点,每个结点的值随机产生 ### 每次都在链表的最后位置加上新结点
/*
假设:r 始终是链表的尾结点
在尾部添加一个新结点p后
p变成尾结点,r 倒数第二
因此:r->next = p;
为了让 r 再次变成尾结点,将 p 赋值给 r
因此:r = p;
所有结点添加完成,将尾结点的指针域指空:r->next = NULL;
*/
void doubly_list_create_tail(DLinkList* L, int n)//尾插法
{DLinkList p, r;int i;srand(time(0));*L = (DLinkList)malloc(sizeof(NodeD));assert(*L);memset(*L, 0, sizeof(NodeD));(*L)->prev = NULL;(*L)->next = NULL;r = *L;while (r->next) {r = r->next;//r 始终是链表的尾结点}for (i = 0; i < n; i++) {p = (DLinkList)malloc(sizeof(NodeD));assert(p);memset(p, 0, sizeof(NodeD));p->data = rand() % 100 + 1;//要领:1.原来尾节点的next指针指向新节点的首地址;2.新节点的prev指针指向原来的尾节点的首地址r->next = p;p->prev = r;p->next = NULL;r = p;}//r->next = NULL;//这一句可以不要
}//初始条件:链表 L 已经存在
//操作结果:将链表清空 ### 每次都删除结点1 ### 但仍然保留着头结点
Status doubly_list_clear(DLinkList* L)
{DLinkList p, q;p = (*L)->next;while (p) {q = p->next;//先保存 p->next 的值(内存地址),即结点2(如果存在的话),防止在 free(p) 时  p->next 的值丢失/*//这里只要后继指针保持正常就行if (q) {q->prev = *L;//如果结点2存在,将结点2的前驱指针指向头结点(*L)->next = q;}*/free(p);p = q;//再将保存的  p->next 的值赋值给 p 进行下一轮的循环判断}(*L)->next = NULL;//将头结点的后续指针域置空return OK;
}//#include <windows.h>
//void Sleep(DWORD dwMilliseconds); 参数为毫秒
void doubly_list_access(DLinkList L)
{int i = 0;DLinkList p = L->next;while (p) {i++;printf("第%d个数据:%d\n", i, p->data);p = p->next;Sleep(5);//休眠5ms}
}void doubly_list_access_back(DLinkList L)
{int i = 0;DLinkList p = L;while (p->next) {p = p->next;//找到尾结点}while (p->prev) {i++;printf("第%d个数据:%d\n", i, p->data);p = p->prev;Sleep(5);}
}//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
Status doubly_list_head_insert(DLinkList* L, ElemType e)
{DLinkList s;s = (DLinkList)malloc(sizeof(NodeD));assert(s);memset(s, 0, sizeof(NodeD));s->prev = *L;s->next = (*L)->next;if ((*L)->next) {(*L)->next->prev = s;}(*L)->next = s;s->data = e;return OK;
}//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
Status doubly_list_tail_insert(DLinkList* L, ElemType e)
{return doubly_list_append(L, e);
}//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status doubly_list_head_delete(DLinkList* L, ElemType* e)
{DLinkList p;p = (*L)->next;if (!p) {return ERROR;}(*L)->next = p->next;if (p->next) {p->next->prev = *L;}*e = p->data;free(p);return OK;
}//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status doubly_list_tail_delete(DLinkList* L, ElemType* e)
{DLinkList p, r;int count = doubly_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 doubly_list_delete_by_node_address(DLinkList* L, NodeD* node)
{DLinkList prevL, curL, nextL;prevL = nextL = *L;curL = (*L)->next;while (curL) {if (node == curL) {nextL = curL->next;prevL->next = nextL;if (nextL) {nextL->prev = prevL;}free(curL);return OK;}prevL = prevL->next;curL = curL->next;}return ERROR;
}//初始条件:链表 L 已经存在
//操作结果:删除包含着数据 e 的第一个结点
Status doubly_list_delete_by_data(DLinkList* L, ElemType e)
{DLinkList prevL, curL, nextL;prevL = nextL = *L;curL = (*L)->next;while (curL) {if (e == curL->data) {nextL = curL->next;prevL->next = nextL;if (nextL) {nextL->prev = prevL;}free(curL);return OK;}prevL = prevL->next;curL = curL->next;}return ERROR;
}//初始条件:链表 L 已经存在
//操作结果:返回第 i 个位置的结点的地址
NodeD* doubly_list_get_node(DLinkList L, int i)
{int j = 0;int count = doubly_list_length(L);NodeD* p = L;if (!(1 <= i <= count)) {return NULL;}while (j < i){j++;p = p->next;}return (p != L) ? p : NULL;
}//复制一个结点
NodeD* doubly_list_copy_node(NodeD* node)
{NodeD* p = (NodeD*)malloc(sizeof(NodeD));assert(p);memset(p, 0, sizeof(NodeD));p->data = node->data;return p;
}//初始条件:链表 L 已经存在 ### 返回 node 的位序
//如果 node 的地址与 链表 L 中任何一个结点的地址相同,则认为 node 存在于 L 中,返回值 >0; 否则返回值为 0
int doubly_list_node_location(DLinkList L, NodeD* node)
{int i = 0;Status find = FALSE;DLinkList 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 doubly_list_elem_location(DLinkList L, ElemType e)
{int i = 0;Status find = FALSE;DLinkList p = L;while (p->next) {i++;p = p->next;if (p->data == e) {find = TRUE;break;}}return (find == TRUE) ? i : 0;
}//初始条件:链表 L 已经存在 ### node1 和 node2 属于链表 L
//比较 node1 与 node2 的数据是否完全相同
//完全相同,返回 TRUE;否则,返回 FALSE
Status doubly_list_node_compare(DLinkList L, NodeD* node1, NodeD* node2)
{int i = doubly_list_node_location(L, node1);int j = doubly_list_node_location(L, node2);if (i == 0 || j == 0) {return FALSE;}if (node1->data == node2->data) {return TRUE;}return FALSE;
}//初始条件:链表 L 已经存在
//操作结果:将链表逆序
//链表逆序 = 遍历 + 头插入
Status doubly_list_reverse(DLinkList* L)
{DLinkList p, q, r;r = p = (*L)->next;//最开始让p指向结点1while (p)//始终将结点p插入在结点1的位置,p从结点1挨个遍历到尾结点 ### while 循环结束时,结点1 就变成了尾结点,r 即是尾结点{q = p->next;p->prev = *L;p->next = (*L)->next;(*L)->next->prev = p;(*L)->next = p;p = q;}r->next = NULL;return OK;
}//多个结点可以包含相同的数据,因此有必要去除重复的数据
Status doubly_list_remove_duplication(DLinkList* L)
{DLinkList 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 (doubly_list_delete_by_data(L, data1) == ERROR) {return ERROR;}p = *L;//删除一个重复的结点后要重新设置循环条件,从第一个结点开始重新查找重复的结点break;}}q = p->next;}return OK;
}Status doubly_list_remove_duplication_ext(DLinkList* L)
{int i = 0;int j = 0;DLinkList 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 (doubly_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 已经存在
//操作结果:将两个链表直接拼接成一个
DLinkList doubly_list_joint(DLinkList L1, DLinkList L2)
{DLinkList h1, h2, p, q;h1 = L1;h2 = L2;p = L1->next;q = L2->next;while (p->next) {p = p->next;}p->next = q;if (q) {q->prev = p;}free(h2);return h1;
}//初始条件:链表 L1 和 L2 已经存在
//操作结果:将 L2 中的结点依次添加到 L1 中,前提:即将添加的结点的数据部分与 L1 中任何一个结点的数据都不同
//最后销毁 L2 的头结点
//如果 L2 的所有结点所包含的数据在 L1 中都存在,则完全销毁 L2
DLinkList doubly_list_merge(DLinkList L1, DLinkList L2)
{int n = 0;ElemType e = 0;DLinkList p = L1;DLinkList h = L1;while (p->next) {p = p->next;n = doubly_list_elem_location(L2, p->data);if (n > 0) {doubly_list_delete(&L2, n, &e);//对于 L1 中的每一个结点p,如果p在 L2 中有相同的副本,就将该副本从 L2 中删除n = 0;}}if (doubly_list_length(L2) > 0) {h = doubly_list_joint(L1, L2);}else {free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间}return h;
}DLinkList doubly_list_merge2(DLinkList L1, DLinkList L2)
{int n = 0;int i = 0;int j = 0;ElemType e = 0;DLinkList p = L2;DLinkList h = L1;while (p->next) {i++;p = p->next;n = doubly_list_elem_location(L1, p->data);if (n > 0) {doubly_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 (doubly_list_length(L2) > 0) {h = doubly_list_joint(L1, L2);}else {free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间}return h;
}//初始条件:链表 L1 和 L2 已经存在
//将 L1 和 L2 拼接在一起,保证每一个结点的数据部分都是独一无二的
DLinkList doubly_list_merge_ext(DLinkList L1, DLinkList L2)
{DLinkList h = doubly_list_joint(L1, L2);return (doubly_list_remove_duplication_ext(&h) == OK) ? h : NULL;
}//初始条件:链表 L 已经存在 ### 截取 L 的一个连续的子片段
// 1 <= i <= doubly_list_length(L) && 1 <= j <= doubly_list_length(L)
//操作结果:对链表进行切片,返回包含 结点i 和 结点j 以及 它们中间的所有结点组成的链表片段 ### 没有头结点
//注意:如果你截取链表 L 的子片段 L0 是为了把它拼接到另外一个链表中去 ### 这将会产生极大的隐患
//当链表 L 被清空时,子片段 L0 也就被清空了,这将导致与 L0 连接的链表在 L0 两端的连接处断开,产生灾难
//一定要小心使用 doubly_list_slice() !!!
//doubly_list_slice() 不提供给外部使用!!!
static DLinkList doubly_list_slice(DLinkList L, int i, int j)
{int k = 0;int len = 0;int start = 0;int count = 0;DLinkList p = L;DLinkList r = L;count = doubly_list_length(L);assert(1 <= i <= count);assert(1 <= j <= count);start = (i <= j) ? i : j;len = abs(i - j);for (k = 0; k < start; k++) {p = p->next;//p是最终链表片段的第一个结点}for (k = 0, r = p; k < len; k++) {r = r->next;//r是最终链表片段的最后一个结点}//r->next = NULL;//注意:这会导致被截取片段的链表意外断裂!!!但是没有这一句,子链表又不能正常的结束!!!return p;
}//为 doubly_list_slice() 产生的链表子片段的每个结点分配新的存储空间,并附加一个头结点
DLinkList doubly_list_slice_ext(DLinkList L, int i, int j)
{int k = abs(i - j);DLinkList r = NULL;DLinkList s = NULL;DLinkList pHead = NULL;DLinkList tmp = doubly_list_slice(L, i, j);pHead = s = (NodeD*)malloc(sizeof(NodeD));assert(s);memset(s, 0, sizeof(NodeD));for (; k >= 0; k--) {r = (NodeD*)malloc(sizeof(NodeD));assert(r);memset(r, 0, sizeof(NodeD));memcpy(r, tmp, sizeof(NodeD));s->next = r;s = s->next;tmp = tmp->next;}r->next = NULL;return pHead;
}//初始条件:链表 L 已经存在
//获取 L 的一个子集,不一定是 L 的连续子片段,组装成一个新的链表
DLinkList doubly_list_create_subset(DLinkList L, ElemType data[], int n)
{int i = 0;int j = 0;DLinkList s = L->next;DLinkList t = NULL;DLinkList q = NULL;DLinkList r = NULL;//r是尾结点DLinkList pHead = NULL;pHead = q = (NodeD*)malloc(sizeof(NodeD));assert(q);memset(q, 0, sizeof(NodeD));for (i = 0; i < n; i++) {while (s) {j++;if (s->data == data[i]) {//找到一个目标结点t = doubly_list_get_node(L, j);r = (NodeD*)malloc(sizeof(NodeD));assert(r);memset(r, 0, sizeof(NodeD));r->data = t->data;q->next = r;q = q->next;break;}s = s->next;}s = L->next;//重新开始从头查找j = 0;}r->next = NULL;return pHead;
}//初始条件:链表 L 已经存在
//操作结果:将链表按照某种规则排序
Status doubly_list_sort_by_name(DLinkList* L)
{return OK;
}#if 0
void test6(void)
{ElemType aaa, bbb;DLinkList pHead;int len = 0;doubly_list_create_head(&pHead, 10);doubly_list_access(pHead);printf("\n\n");doubly_list_insert(&pHead, 3, 10000);doubly_list_access(pHead);printf("\n\n");doubly_list_delete(&pHead, 6, &aaa);printf("aaa:%d\n", (int)aaa);doubly_list_access(pHead);printf("\n");doubly_list_insert(&pHead, 10, 20000);//边界插入:此时链表长度刚好为10 ### 在链表的最后插入一个结点会怎么样? ### OKdoubly_list_access(pHead);printf("\n");doubly_list_insert(&pHead, 1, 30000);//边界插入:在1 的位置插入一个结点会怎么样? ### OKdoubly_list_access(pHead);//doubly_list_insert(&pHead, 13, 40000);//边界插入:此时链表长度为12,我想在 13 的位置插入会怎么样? ### 出错了!//doubly_list_insert(&pHead, 0, 50000);//边界插入:在 0 的位置插入呢? ### 虽然没有出错,但是没有意义!//doubly_list_insert(&pHead, 0, 60000);//doubly_list_access(pHead);//printf("出错了吗?\n");len = doubly_list_length(pHead);printf("最终链表长度为:%d\n", len);doubly_list_clear(&pHead);free(pHead);printf("\n\n");doubly_list_create_tail(&pHead, 20);doubly_list_access(pHead);doubly_list_get_elem(pHead, 15, &bbb);printf("bbb:%d\n", (int)bbb);doubly_list_delete(&pHead, 15, &aaa);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test7(void)
{DLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;pHead = doubly_list_init();doubly_list_append(&pHead, aaa);doubly_list_append(&pHead, bbb);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test8(void)
{DLinkList pHead;ElemType aaa = 300;ElemType bbb = 400;ElemType ccc = 500;pHead = doubly_list_init();doubly_list_insert(&pHead, 1, aaa);doubly_list_insert(&pHead, 1, bbb);doubly_list_insert(&pHead, 1, ccc);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test9(void)
{DLinkList pHead;ElemType aaa = 600;ElemType bbb = 700;ElemType ccc = 800;ElemType ddd = 900;ElemType eee = 1000;ElemType fff = 2000;ElemType ggg;ElemType hhh;pHead = doubly_list_init();doubly_list_insert(&pHead, 1, aaa);doubly_list_append(&pHead, bbb);doubly_list_insert(&pHead, 1, ccc);doubly_list_append(&pHead, ddd);doubly_list_access(pHead);printf("\n\n");doubly_list_insert(&pHead, 2, eee);doubly_list_access(pHead);printf("\n\n");doubly_list_insert(&pHead, 4, fff);doubly_list_access(pHead);printf("\n\n");doubly_list_delete(&pHead, 1, &ggg);printf("ggg:%d\n", (int)ggg);doubly_list_access(pHead);printf("\n\n");doubly_list_delete(&pHead, 2, &hhh);printf("hhh:%d\n", (int)hhh);doubly_list_access(pHead);doubly_list_delete(&pHead, 2, &hhh);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10(void)
{DLinkList pHead;doubly_list_create_head(&pHead, 0);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);doubly_list_create_tail(&pHead, 0);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_2(void)//链表长度为1,删除链表的一个结点会出现什么情况?
{DLinkList pHead;ElemType aaa = 100;ElemType bbb;pHead = doubly_list_init();doubly_list_append(&pHead, aaa);doubly_list_access(pHead);doubly_list_delete(&pHead, 1, &bbb);doubly_list_delete(&pHead, 1, &bbb);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_3(void)
{DLinkList pHead;ElemType aaa = 12345;ElemType bbb = 23456;ElemType ccc = 34567;doubly_list_create_head(&pHead, 10);doubly_list_access(pHead);printf("\n\n");doubly_list_modify(&pHead, 5, aaa);printf("\n");doubly_list_access(pHead);printf("\n");doubly_list_modify(&pHead, 1, bbb);doubly_list_modify(&pHead, 10, ccc);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_4(void)//专门测试链表为空表,使用 doubly_list_insert() 在 结点1 的位置插入结点的情况 ### 是 OK 的!!!
{DLinkList pHead;ElemType aaa = 100;pHead = doubly_list_init();doubly_list_insert(&pHead, 1, aaa);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_5(void)
{DLinkList pHead;doubly_list_create_head(&pHead, 5);doubly_list_access(pHead);printf("\n");doubly_list_access_back(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_6(void)
{DLinkList pHead;doubly_list_create_head(&pHead, 5);doubly_list_access(pHead);printf("\n");doubly_list_reverse(&pHead);doubly_list_access(pHead);printf("\n");doubly_list_access_back(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_7(void)
{DLinkList ph1, ph2, ph3;int count = 0;doubly_list_create_head(&ph1, 5);doubly_list_create_tail(&ph2, 8);doubly_list_access(ph1);printf("\n");doubly_list_access(ph2);printf("\n");ph3 = doubly_list_joint(ph1, ph2);count = doubly_list_length(ph3);printf("合并后的新链表长度:%d\n", count);printf("ph3: %p\n", ph3);printf("ph1: %p\n", ph1);printf("ph2: %p\n", ph2);doubly_list_access(ph3);printf("\n");doubly_list_access_back(ph3);doubly_list_clear(&ph3);free(ph3);ph3 = NULL;//有没有内存泄漏? ### 没有,下面的释放 ph1 和 ph2 不需要!!!/*if (ph1) {free(ph1);//ph1 和 ph3 实际上指向的是同一块堆内存,而这块堆内存已经被 ph3 释放了,如果重复释放将会导致错误!!!ph1 = NULL;}if (ph2) {free(ph2);//ph2 指向的堆内存在 doubly_list_joint() 函数中已经释放了头指针,ph2 链表的身段部分在 ph3 中被释放,如果重复释放将会导致错误!!!ph2 = NULL;}*/
}void test10_8(void)
{DLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee;pHead = doubly_list_init();doubly_list_tail_insert(&pHead, aaa);doubly_list_head_insert(&pHead, bbb);doubly_list_insert(&pHead, 1, ccc);doubly_list_append(&pHead, ddd);doubly_list_access(pHead);printf("\n");doubly_list_head_delete(&pHead, &eee);doubly_list_access(pHead);printf("\n");doubly_list_head_delete(&pHead, &eee);doubly_list_access(pHead);printf("\n");doubly_list_tail_delete(&pHead, &eee);doubly_list_access(pHead);printf("\n");doubly_list_tail_delete(&pHead, &eee);doubly_list_access(pHead);printf("\n");doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_9(void)
{DLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = doubly_list_init();doubly_list_head_insert(&pHead, aaa);doubly_list_tail_insert(&pHead, bbb);doubly_list_insert(&pHead, 1, ccc);doubly_list_insert(&pHead, 3, ddd);doubly_list_insert(&pHead, 4, eee);doubly_list_insert(&pHead, 2, bbb);doubly_list_insert(&pHead, 3, bbb);doubly_list_access(pHead);printf("\n");doubly_list_delete_by_data(&pHead, bbb);doubly_list_delete_by_data(&pHead, bbb);doubly_list_delete_by_data(&pHead, bbb);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_10(void)
{int loc = 0;Status res = FALSE;DLinkList pHead;NodeD* node1, * node2, * node3;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = doubly_list_init();doubly_list_head_insert(&pHead, aaa);doubly_list_tail_insert(&pHead, bbb);doubly_list_insert(&pHead, 1, ccc);doubly_list_insert(&pHead, 3, ddd);doubly_list_insert(&pHead, 4, eee);doubly_list_insert(&pHead, 2, bbb);doubly_list_insert(&pHead, 3, bbb);doubly_list_access(pHead);printf("\n");node1 = doubly_list_get_node(pHead, 2);node2 = doubly_list_get_node(pHead, 3);node3 = doubly_list_get_node(pHead, 7);loc = doubly_list_node_location(pHead, node1);loc = doubly_list_node_location(pHead, node2);loc = doubly_list_node_location(pHead, node3);loc = doubly_list_elem_location(pHead, aaa);loc = doubly_list_elem_location(pHead, ccc);loc = doubly_list_elem_location(pHead, ddd);loc = doubly_list_elem_location(pHead, bbb);loc = doubly_list_elem_location(pHead, eee);res = doubly_list_node_compare(pHead, node1, node2);doubly_list_delete_by_node_address(&pHead, node1);doubly_list_delete_by_node_address(&pHead, node2);doubly_list_delete_by_node_address(&pHead, node3);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_11(void)
{DLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = doubly_list_init();doubly_list_head_insert(&pHead, aaa);doubly_list_tail_insert(&pHead, bbb);doubly_list_insert(&pHead, 1, ccc);doubly_list_insert(&pHead, 3, ddd);doubly_list_insert(&pHead, 4, eee);doubly_list_insert(&pHead, 2, bbb);doubly_list_insert(&pHead, 3, bbb);doubly_list_access(pHead);printf("\n");//doubly_list_remove_duplication(&pHead);doubly_list_remove_duplication_ext(&pHead);doubly_list_access(pHead);doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test10_12(void)
{DLinkList ph1, ph2, ph3;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;ph1 = doubly_list_init();doubly_list_head_insert(&ph1, aaa);doubly_list_tail_insert(&ph1, bbb);doubly_list_insert(&ph1, 1, ccc);doubly_list_insert(&ph1, 3, ddd);doubly_list_insert(&ph1, 4, eee);doubly_list_insert(&ph1, 2, bbb);doubly_list_insert(&ph1, 3, bbb);doubly_list_access(ph1);printf("\n");ph2 = doubly_list_init();doubly_list_head_insert(&ph2, 600);doubly_list_head_insert(&ph2, 700);doubly_list_head_insert(&ph2, 800);doubly_list_head_insert(&ph2, 900);doubly_list_head_insert(&ph2, 1000);doubly_list_insert(&ph2, 3, bbb);doubly_list_insert(&ph2, 5, bbb);doubly_list_access(ph2);printf("\n");//ph3 = doubly_list_merge(ph1, ph2);//ph3 = doubly_list_merge2(ph1, ph2);ph3 = doubly_list_merge_ext(ph1, ph2);doubly_list_access(ph3);doubly_list_clear(&ph3);free(ph3);ph3 = NULL;
}void test10_13(void)
{DLinkList ph1, ph2, ph3, p;doubly_list_create_tail(&ph1, 20);doubly_list_access(ph1);printf("\n");p = doubly_list_slice_ext(ph1, 5, 15);doubly_list_clear(&ph1);free(ph1);ph1 = NULL;doubly_list_access(p);printf("\n");doubly_list_create_head(&ph2, 5);doubly_list_access(ph2);printf("\n");ph3 = doubly_list_joint(ph2, p);doubly_list_access(ph3);printf("\n");doubly_list_clear(&ph3);free(ph3);ph3 = NULL;//有没有内存泄漏? ### 没有,下面的释放 ph2 和 p 不需要!!!/*if (ph2) {free(ph2);//ph2 和 ph3 实际上指向的是同一块堆内存,而这块堆内存已经被 ph3 释放了,如果重复释放将会导致错误!!!ph2 = NULL;}if (p) {free(p);//p 指向的堆内存在 list_joint() 函数中已经释放了头结点,p链表的身段部分在 ph3 中被释放,如果重复释放将会导致错误!!!p = NULL;}*/
}void test10_14(void)
{DLinkList ph1, ph2;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;ElemType data[] = { bbb, ccc, eee, 600, 800 };int n = sizeof(data) / sizeof(data[0]);ph1 = doubly_list_init();doubly_list_head_insert(&ph1, aaa);doubly_list_tail_insert(&ph1, bbb);doubly_list_insert(&ph1, 1, ccc);doubly_list_insert(&ph1, 3, ddd);doubly_list_insert(&ph1, 4, eee);doubly_list_insert(&ph1, 2, bbb);doubly_list_insert(&ph1, 3, bbb);doubly_list_append(&ph1, 600);doubly_list_append(&ph1, 700);doubly_list_append(&ph1, 800);doubly_list_append(&ph1, 900);doubly_list_append(&ph1, 1000);doubly_list_access(ph1);printf("\n");for (int i = 0; i < n; i++) {printf("%d\t", data[i]);}printf("\n");ph2 = doubly_list_create_subset(ph1, data, n);doubly_list_access(ph2);printf("\n");doubly_list_clear(&ph1);doubly_list_clear(&ph2);ph1 = NULL;ph2 = NULL;
}int main(void)
{test6();test7();test8();test9();test10();test10_2();test10_3();test10_4();test10_5();test10_6();test10_7();test10_8();test10_9();test10_10();test10_11();test10_12();test10_13();test10_14();return 0;
}
#endif

循环链表

//循环链表//创建一个带有头结点的空链表
LLinkList loop_list_init(void)
{LLinkList pHead;pHead = (LLinkList)malloc(sizeof(NodeLL));assert(pHead);memset(pHead, 0, sizeof(NodeLL));pHead->next = pHead;return pHead;
}//初始条件:链表 L 已经存在
//操作结果:在 L 的尾部添加一个新结点,其数据值为 e,链表 L 的长度加 1
Status loop_list_append(LLinkList* L, ElemType e)
{LLinkList p, r;r = *L;while (r->next != *L) {r = r->next;//将 r 变成尾指针}p = (LLinkList)malloc(sizeof(NodeLL));assert(p);memset(p, 0, sizeof(NodeLL));r->next = p;p->next = *L;p->data = e;return OK;
}//初始条件:链表 L 已经存在,并且 1 <= i <= loop_list_length(L)
//操作结果:在 L 中的第 i 个结点【之前】插入新结点,其数据值为 e,链表 L 的长度加 1
Status loop_list_insert(LLinkList* L, int i, ElemType e)// 这里:L 是一个二重指针
{int j = 1;LLinkList p, s;int count = loop_list_length(*L);if (count == 0) {return loop_list_append(L, e);}if (i >= 1 && i <= count){p = *L;while ((p->next != *L) && j < i) {//寻找第 i-1 个结点p = p->next;++j;}if ((p->next == *L) || j > i)return ERROR;s = (LLinkList)malloc(sizeof(NodeLL));assert(s);memset(s, 0, sizeof(NodeLL));s->next = p->next;p->next = s;s->data = e;return OK;}return ERROR;
}//初始条件:链表 L 已经存在,并且 1 <= i <= loop_list_length(L)
//操作结果:删除 L 的第 i 个结点,用 e 返回其值,链表 L 的长度减 1
Status loop_list_delete(LLinkList* L, int i, ElemType* e)
{int j = 1;LLinkList p, q;int count = loop_list_length(*L);if (i >= 1 && i <= count){p = *L;while ((p->next != *L) && j < i) {//寻找第 i-1 个结点p = p->next;++j;}if ((p->next == *L) || 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 loop_list_length(LLinkList L)
{int i = 0;LLinkList p = L->next;while (p != L) {p = p->next;i++;}return i;
}//初始条件:链表 L 已经存在,并且 1 <= i <= loop_list_length(L)
//操作结果:用 e 返回 L 中第 i 个元素的值
Status loop_list_get_elem(LLinkList L, int i, ElemType* e)
{int j = 1;LLinkList p = L->next;int count = loop_list_length(L);if (i >= 1 && i <= count){while ((p != L) && j < i) {p = p->next;++j;}if ((p == L) || j > i)return ERROR;*e = p->data;return OK;}return ERROR;
}//初始条件:链表 L 已经存在,并且 1 <= i <= loop_list_length(L)
//操作结果:修改 L 中第 i 个元素的值为指定的 newData
Status loop_list_modify(LLinkList* L, int i, ElemType newData)
{int j = 1;LLinkList p = (*L)->next;int count = loop_list_length(*L);if (i >= 1 && i <= count){while ((p != *L) && j < i) {p = p->next;++j;}if ((p == *L) || j > i)return ERROR;p->data = newData;return OK;}return ERROR;
}//创建带头结点的单链表 L,L 有 n 的结点,每个结点的值随机产生 ### 每次都在 结点1 的位置插入新结点
void loop_list_create_head(LLinkList* L, int n)//头插法
{LLinkList p;int i;srand(time(0));*L = (LLinkList)malloc(sizeof(NodeLL));assert(*L);memset(*L, 0, sizeof(NodeLL));(*L)->next = *L;for (i = 0; i < n; i++) {p = (LLinkList)malloc(sizeof(NodeLL));assert(p);memset(p, 0, sizeof(NodeLL));p->data = rand() % 100 + 1;p->next = (*L)->next;(*L)->next = p;}
}//创建带头结点的单链表 L,L 有 n 的结点,每个结点的值随机产生 ### 每次都在链表的最后位置加上新结点
/*
假设:r 始终是链表的尾结点
在尾部添加一个新结点p后
p变成尾结点,r 倒数第二
因此:r->next = p;
为了让 r 再次变成尾结点,将 p 赋值给 r
因此:r = p;
所有结点添加完成,将尾结点的指针域指空:r->next = NULL;
*/
void loop_list_create_tail(LLinkList* L, int n)//尾插法
{LLinkList p, r;int i;srand(time(0));*L = (LLinkList)malloc(sizeof(NodeLL));assert(*L);memset(*L, 0, sizeof(NodeLL));(*L)->next = *L;r = *L;while (r->next != *L) {r = r->next;//r 始终是链表的尾结点}for (i = 0; i < n; i++) {p = (LLinkList)malloc(sizeof(NodeLL));assert(p);memset(p, 0, sizeof(NodeLL));p->data = rand() % 100 + 1;r->next = p;r = p;}r->next = *L;
}//初始条件:链表 L 已经存在
//操作结果:将链表清空 ### 每次都删除结点1 ### 但仍然保留着头结点
Status loop_list_clear(LLinkList* L)
{LLinkList p, q;p = (*L)->next;while (p != *L) {q = p->next;//先保存 p->next 的值(内存地址),即结点2(如果存在的话),防止在 free(p) 时  p->next 的值丢失free(p);p = q;//再将保存的  p->next 的值赋值给 p 进行下一轮的循环判断}(*L)->next = *L;return OK;
}//#include <windows.h>
//void Sleep(DWORD dwMilliseconds); 参数为毫秒
void loop_list_access(LLinkList L)
{int i = 0;LLinkList p = L->next;while (p != L) {i++;printf("第%d个数据:%d\n", i, p->data);p = p->next;Sleep(5);//休眠5ms}
}//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
Status loop_list_head_insert(LLinkList* L, ElemType e)
{LLinkList s;s = (LLinkList)malloc(sizeof(NodeLL));assert(s);memset(s, 0, sizeof(NodeLL));s->next = (*L)->next;(*L)->next = s;s->data = e;return OK;
}//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
Status loop_list_tail_insert(LLinkList* L, ElemType e)
{return loop_list_append(L, e);
}//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status loop_list_head_delete(LLinkList* L, ElemType* e)
{LLinkList p;p = (*L)->next;if (p == *L) {return ERROR;}(*L)->next = p->next;*e = p->data;free(p);return OK;
}//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status loop_list_tail_delete(LLinkList* L, ElemType* e)
{LLinkList p, r;int count = loop_list_length(*L);if (count == 1) {p = (*L)->next;*e = p->data;free(p);(*L)->next = *L;return OK;}else if (count > 1) {p = r = *L;while (p->next->next != *L) {p = p->next;//p最终变成倒数第二个结点r = p->next;//r最终变成尾结点}p->next = *L;*e = r->data;free(r);return OK;}else {return ERROR;}
}//初始条件:链表 L 已经存在
//操作结果:直接删除结点 node
Status loop_list_delete_by_node_address(LLinkList* L, NodeLL* node)
{LLinkList prevL, curL, nextL;prevL = nextL = *L;curL = (*L)->next;while (curL != *L) {if (node == curL) {nextL = curL->next;prevL->next = nextL;free(curL);return OK;}prevL = prevL->next;curL = curL->next;}return ERROR;
}//初始条件:链表 L 已经存在
//操作结果:删除包含着数据 e 的第一个结点
Status loop_list_delete_by_data(LLinkList* L, ElemType e)
{LLinkList prevL, curL, nextL;prevL = nextL = *L;curL = (*L)->next;while (curL != *L) {if (e == curL->data) {nextL = curL->next;prevL->next = nextL;free(curL);return OK;}prevL = prevL->next;curL = curL->next;}return ERROR;
}//初始条件:链表 L 已经存在
//操作结果:返回第 i 个位置的结点的地址
NodeLL* loop_list_get_node(LLinkList L, int i)
{int j = 0;int count = loop_list_length(L);NodeLL* p = L;if (!(1 <= i <= count)) {return NULL;}while (j < i){j++;p = p->next;}return (p != L) ? p : NULL;
}//复制一个结点
NodeLL* loop_list_copy_node(NodeLL* node)
{NodeLL* p = (NodeLL*)malloc(sizeof(NodeLL));assert(p);memset(p, 0, sizeof(NodeLL));p->data = node->data;return p;
}//初始条件:链表 L 已经存在 ### 返回 node 的位序
//如果 node 的地址与 链表 L 中任何一个结点的地址相同,则认为 node 存在于 L 中,返回值 >0; 否则返回值为 0
int loop_list_node_location(LLinkList L, NodeLL* node)
{int i = 0;Status find = FALSE;LLinkList p = L;while (p->next != L) {i++;p = p->next;if (p == node) {find = TRUE;break;}}return (find == TRUE) ? i : 0;
}//初始条件:链表 L 已经存在 ### 返回 e 的位序
//如果数据 e 与链表 L 中某个结点的数据完全相同,则认为 e 存在于 L 中,返回值 >0; 否则返回值为 0
int loop_list_elem_location(LLinkList L, ElemType e)
{int i = 0;Status find = FALSE;LLinkList p = L;while (p->next != L) {i++;p = p->next;if (p->data == e) {find = TRUE;break;}}return (find == TRUE) ? i : 0;
}//初始条件:链表 L 已经存在 ### node1 和 node2 属于链表 L
//比较 node1 与 node2 的数据是否完全相同
//完全相同,返回 TRUE;否则,返回 FALSE
Status loop_list_node_compare(LLinkList L, NodeLL* node1, NodeLL* node2)
{int i = loop_list_node_location(L, node1);int j = loop_list_node_location(L, node2);if (i == 0 || j == 0) {return FALSE;}if (node1->data == node2->data) {return TRUE;}return FALSE;
}//初始条件:链表 L 已经存在
//操作结果:将链表逆序
//链表逆序 = 遍历 + 头插入
Status loop_list_reverse(LLinkList* L)
{LLinkList p, q, r;r = p = (*L)->next;//最开始让p指向结点1while (p != *L)//始终将结点p插入在结点1的位置,p从结点1挨个遍历到尾结点 ### while 循环结束时,结点1 就变成了尾结点,r 即是尾结点{q = p->next;p->next = (*L)->next;(*L)->next = p;p = q;}r->next = *L;return OK;
}//多个结点可以包含相同的数据,因此有必要去除重复的数据
Status loop_list_remove_duplication(LLinkList* L)
{LLinkList p, q;ElemType data1, data2;p = *L;q = p->next;while (p->next != *L) {p = p->next;data1 = p->data;while (q->next != *L) {q = q->next;data2 = q->data;if (data1 == data2) {if (loop_list_delete_by_data(L, data1) == ERROR) {return ERROR;}p = *L;//删除一个重复的结点后要重新设置循环条件,从第一个结点开始重新查找重复的结点break;}}q = p->next;}return OK;
}Status loop_list_remove_duplication_ext(LLinkList* L)
{int i = 0;int j = 0;LLinkList p, q;ElemType data1, data2;p = *L;q = p->next;while (p->next != *L) {i++;p = p->next;data1 = p->data;while (q->next != *L) {q = q->next;data2 = q->data;if (data1 == data2) {if (loop_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 已经存在
//操作结果:将两个链表直接拼接成一个
LLinkList loop_list_joint(LLinkList L1, LLinkList L2)
{LLinkList h1, h2, r1, r2, p;h1 = L1;h2 = L2;r1 = L1->next;p = r2 = L2->next;while (r1->next != h1) {r1 = r1->next;//r1 是 L1 的尾结点}while (r2->next != h2) {r2 = r2->next;//r2 是 L2 的尾结点}if (p != h2) {//L2 链表不是空表r1->next = p;r2->next = h1;}free(h2);return h1;
}//初始条件:链表 L1 和 L2 已经存在
//操作结果:将 L2 中的结点依次添加到 L1 中,前提:即将添加的结点的数据部分与 L1 中任何一个结点的数据都不同
//最后销毁 L2 的头结点
//如果 L2 的所有结点所包含的数据在 L1 中都存在,则完全销毁 L2
LLinkList loop_list_merge(LLinkList L1, LLinkList L2)
{int n = 0;ElemType e = 0;LLinkList p = L1;LLinkList h = L1;while (p->next != L1) {p = p->next;n = loop_list_elem_location(L2, p->data);if (n > 0) {loop_list_delete(&L2, n, &e);//对于 L1 中的每一个结点p,如果p在 L2 中有相同的副本,就将该副本从 L2 中删除n = 0;}}if (loop_list_length(L2) > 0) {h = loop_list_joint(L1, L2);}else {free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间}return h;
}LLinkList loop_list_merge2(LLinkList L1, LLinkList L2)
{int n = 0;int i = 0;int j = 0;ElemType e = 0;LLinkList p = L2;LLinkList h = L1;while (p->next != L2) {i++;p = p->next;n = loop_list_elem_location(L1, p->data);if (n > 0) {loop_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 (loop_list_length(L2) > 0) {h = loop_list_joint(L1, L2);}else {free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间}return h;
}//初始条件:链表 L1 和 L2 已经存在
//将 L1 和 L2 拼接在一起,保证每一个结点的数据部分都是独一无二的
LLinkList loop_list_merge_ext(LLinkList L1, LLinkList L2)
{LLinkList h = loop_list_joint(L1, L2);return (loop_list_remove_duplication_ext(&h) == OK) ? h : NULL;
}//初始条件:链表 L 已经存在 ### 截取 L 的一个连续的子片段
// 1 <= i <= loop_list_length(L) && 1 <= j <= loop_list_length(L)
//操作结果:对链表进行切片,返回包含 结点i 和 结点j 以及 它们中间的所有结点组成的链表片段 ### 没有头结点
//注意:如果你截取链表 L 的子片段 L0 是为了把它拼接到另外一个链表中去 ### 这将会产生极大的隐患
//当链表 L 被清空时,子片段 L0 也就被清空了,这将导致与 L0 连接的链表在 L0 两端的连接处断开,产生灾难
//一定要小心使用 loop_list_slice() !!!
//loop_list_slice() 不提供给外部使用!!!
static LLinkList loop_list_slice(LLinkList L, int i, int j)
{int k = 0;int len = 0;int start = 0;int count = 0;LLinkList p = L;LLinkList r = L;count = loop_list_length(L);assert(1 <= i <= count);assert(1 <= j <= count);start = (i <= j) ? i : j;len = abs(i - j);for (k = 0; k < start; k++) {p = p->next;//p是最终链表片段的第一个结点}for (k = 0, r = p; k < len; k++) {r = r->next;//r是最终链表片段的最后一个结点}//r->next = NULL;//注意:这会导致被截取片段的链表意外断裂!!!但是没有这一句,子链表又不能正常的结束!!!return p;
}//为 loop_list_slice() 产生的链表子片段的每个结点分配新的存储空间,并附加一个头结点
LLinkList loop_list_slice_ext(LLinkList L, int i, int j)
{int k = abs(i - j);LLinkList r = NULL;LLinkList s = NULL;LLinkList pHead = NULL;LLinkList tmp = loop_list_slice(L, i, j);pHead = s = (NodeLL*)malloc(sizeof(NodeLL));assert(s);memset(s, 0, sizeof(NodeLL));for (; k >= 0; k--) {r = (NodeLL*)malloc(sizeof(NodeLL));assert(r);memset(r, 0, sizeof(NodeLL));memcpy(r, tmp, sizeof(NodeLL));s->next = r;s = s->next;tmp = tmp->next;}r->next = pHead;return pHead;
}//初始条件:链表 L 已经存在
//获取 L 的一个子集,不一定是 L 的连续子片段,组装成一个新的链表
LLinkList loop_list_create_subset(LLinkList L, ElemType data[], int n)
{int i = 0;int j = 0;LLinkList s = L->next;LLinkList t = NULL;LLinkList q = NULL;LLinkList r = NULL;//r是尾结点LLinkList pHead = NULL;pHead = q = (NodeLL*)malloc(sizeof(NodeLL));assert(q);memset(q, 0, sizeof(NodeLL));for (i = 0; i < n; i++) {while (s != L) {j++;if (s->data == data[i]) {//找到一个目标结点t = loop_list_get_node(L, j);r = (NodeLL*)malloc(sizeof(NodeLL));assert(r);memset(r, 0, sizeof(NodeLL));r->data = t->data;q->next = r;q = q->next;break;}s = s->next;}s = L->next;//重新开始从头查找j = 0;}r->next = pHead;return pHead;
}//初始条件:链表 L 已经存在
//操作结果:将链表按照某种规则排序
Status loop_list_sort_by_name(LLinkList* L)
{return OK;
}#if 0
void test11(void)
{ElemType aaa, bbb;LLinkList pHead;loop_list_create_head(&pHead, 10);loop_list_access(pHead);printf("\n\n");loop_list_insert(&pHead, 3, 10000);loop_list_access(pHead);printf("\n\n");loop_list_delete(&pHead, 6, &aaa);printf("aaa:%d\n", (int)aaa);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);printf("\n\n");loop_list_create_tail(&pHead, 20);loop_list_access(pHead);loop_list_get_elem(pHead, 15, &bbb);printf("bbb:%d\n", (int)bbb);printf("\n\n");loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test12(void)
{LLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;pHead = loop_list_init();loop_list_append(&pHead, aaa);loop_list_append(&pHead, bbb);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test13(void)
{LLinkList pHead;ElemType aaa = 300;ElemType bbb = 400;ElemType ccc = 500;pHead = loop_list_init();loop_list_insert(&pHead, 1, aaa);loop_list_insert(&pHead, 1, bbb);loop_list_insert(&pHead, 1, ccc);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test14(void)
{LLinkList pHead;ElemType aaa = 600;ElemType bbb = 700;ElemType ccc = 800;ElemType ddd = 900;ElemType eee = 1000;ElemType fff = 2000;ElemType ggg;ElemType hhh;pHead = loop_list_init();loop_list_insert(&pHead, 1, aaa);loop_list_append(&pHead, bbb);loop_list_insert(&pHead, 1, ccc);loop_list_append(&pHead, ddd);loop_list_access(pHead);printf("\n\n");loop_list_insert(&pHead, 2, eee);loop_list_access(pHead);printf("\n\n");loop_list_insert(&pHead, 4, fff);loop_list_access(pHead);printf("\n\n");loop_list_delete(&pHead, 1, &ggg);printf("ggg:%d\n", (int)ggg);loop_list_access(pHead);printf("\n\n");loop_list_delete(&pHead, 2, &hhh);printf("hhh:%d\n", (int)hhh);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15(void)
{LLinkList pHead;loop_list_create_head(&pHead, 0);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);loop_list_create_tail(&pHead, 0);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15_2(void)//链表长度为1,删除链表的一个结点会出现什么情况?
{LLinkList pHead;ElemType aaa = 100;ElemType bbb;pHead = loop_list_init();loop_list_append(&pHead, aaa);loop_list_access(pHead);loop_list_delete(&pHead, 1, &bbb);loop_list_delete(&pHead, 1, &bbb);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15_3(void)
{LLinkList pHead;ElemType aaa = 12345;ElemType bbb = 23456;ElemType ccc = 34567;loop_list_create_head(&pHead, 10);loop_list_access(pHead);printf("\n\n");loop_list_modify(&pHead, 5, aaa);printf("\n");loop_list_access(pHead);printf("\n");loop_list_modify(&pHead, 1, bbb);loop_list_modify(&pHead, 10, ccc);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15_4(void)//专门测试链表为空表,使用 loop_list_insert() 在 结点1 的位置插入结点的情况 ### 是 OK 的!!!
{LLinkList pHead;ElemType aaa = 100;pHead = loop_list_init();loop_list_insert(&pHead, 1, aaa);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15_5(void)
{LLinkList pHead;loop_list_create_head(&pHead, 5);loop_list_access(pHead);printf("\n");loop_list_reverse(&pHead);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15_6(void)
{LLinkList ph1, ph2, ph3;int count = 0;loop_list_create_head(&ph1, 5);loop_list_create_tail(&ph2, 8);loop_list_access(ph1);printf("\n");loop_list_access(ph2);printf("\n");ph3 = loop_list_joint(ph1, ph2);count = loop_list_length(ph3);printf("合并后的新链表长度:%d\n", count);printf("ph3: %p\n", ph3);printf("ph1: %p\n", ph1);printf("ph2: %p\n", ph2);loop_list_access(ph3);printf("\n");loop_list_clear(&ph3);free(ph3);ph3 = NULL;//有没有内存泄漏? ### 没有,下面的释放 ph1 和 ph2 不需要!!!/*if (ph1) {free(ph1);//ph1 和 ph3 实际上指向的是同一块堆内存,而这块堆内存已经被 ph3 释放了,如果重复释放将会导致错误!!!ph1 = NULL;}if (ph2) {free(ph2);//ph2 指向的堆内存在 loop_list_joint() 函数中已经释放了头指针,ph2 链表的身段部分在 ph3 中被释放,如果重复释放将会导致错误!!!ph2 = NULL;}*/
}void test15_7(void)
{LLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee;pHead = loop_list_init();loop_list_tail_insert(&pHead, aaa);loop_list_head_insert(&pHead, bbb);loop_list_insert(&pHead, 1, ccc);loop_list_append(&pHead, ddd);loop_list_access(pHead);printf("\n");loop_list_head_delete(&pHead, &eee);loop_list_access(pHead);printf("\n");loop_list_head_delete(&pHead, &eee);loop_list_access(pHead);printf("\n");loop_list_tail_delete(&pHead, &eee);loop_list_access(pHead);printf("\n");loop_list_tail_delete(&pHead, &eee);loop_list_access(pHead);printf("\n");loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15_8(void)
{LLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = loop_list_init();loop_list_head_insert(&pHead, aaa);loop_list_tail_insert(&pHead, bbb);loop_list_insert(&pHead, 1, ccc);loop_list_insert(&pHead, 3, ddd);loop_list_insert(&pHead, 4, eee);loop_list_insert(&pHead, 2, bbb);loop_list_insert(&pHead, 3, bbb);loop_list_access(pHead);printf("\n");loop_list_delete_by_data(&pHead, bbb);loop_list_delete_by_data(&pHead, bbb);loop_list_delete_by_data(&pHead, bbb);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15_9(void)
{int loc = 0;Status res = FALSE;LLinkList pHead;NodeLL* node1, * node2, * node3;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = loop_list_init();loop_list_head_insert(&pHead, aaa);loop_list_tail_insert(&pHead, bbb);loop_list_insert(&pHead, 1, ccc);loop_list_insert(&pHead, 3, ddd);loop_list_insert(&pHead, 4, eee);loop_list_insert(&pHead, 2, bbb);loop_list_insert(&pHead, 3, bbb);loop_list_access(pHead);printf("\n");node1 = loop_list_get_node(pHead, 2);node2 = loop_list_get_node(pHead, 3);node3 = loop_list_get_node(pHead, 7);loc = loop_list_node_location(pHead, node1);loc = loop_list_node_location(pHead, node2);loc = loop_list_node_location(pHead, node3);loc = loop_list_elem_location(pHead, aaa);loc = loop_list_elem_location(pHead, ccc);loc = loop_list_elem_location(pHead, ddd);loc = loop_list_elem_location(pHead, bbb);loc = loop_list_elem_location(pHead, eee);res = loop_list_node_compare(pHead, node1, node2);loop_list_delete_by_node_address(&pHead, node1);loop_list_delete_by_node_address(&pHead, node2);loop_list_delete_by_node_address(&pHead, node3);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15_10(void)
{LLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = loop_list_init();loop_list_head_insert(&pHead, aaa);loop_list_tail_insert(&pHead, bbb);loop_list_insert(&pHead, 1, ccc);loop_list_insert(&pHead, 3, ddd);loop_list_insert(&pHead, 4, eee);loop_list_insert(&pHead, 2, bbb);loop_list_insert(&pHead, 3, bbb);loop_list_access(pHead);printf("\n");//loop_list_remove_duplication(&pHead);loop_list_remove_duplication_ext(&pHead);loop_list_access(pHead);loop_list_clear(&pHead);free(pHead);pHead = NULL;
}void test15_11(void)
{LLinkList ph1, ph2, ph3;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;ph1 = loop_list_init();loop_list_head_insert(&ph1, aaa);loop_list_tail_insert(&ph1, bbb);loop_list_insert(&ph1, 1, ccc);loop_list_insert(&ph1, 3, ddd);loop_list_insert(&ph1, 4, eee);loop_list_insert(&ph1, 2, bbb);loop_list_insert(&ph1, 3, bbb);loop_list_access(ph1);printf("\n");ph2 = loop_list_init();loop_list_head_insert(&ph2, 600);loop_list_head_insert(&ph2, 700);loop_list_head_insert(&ph2, 800);loop_list_head_insert(&ph2, 900);loop_list_head_insert(&ph2, 1000);loop_list_insert(&ph2, 3, bbb);loop_list_insert(&ph2, 5, bbb);loop_list_access(ph2);printf("\n");//ph3 = loop_list_merge(ph1, ph2);//ph3 = loop_list_merge2(ph1, ph2);ph3 = loop_list_merge_ext(ph1, ph2);loop_list_access(ph3);loop_list_clear(&ph3);free(ph3);ph3 = NULL;
}void test15_12(void)
{LLinkList ph1, ph2, ph3, p;loop_list_create_tail(&ph1, 20);loop_list_access(ph1);printf("\n");p = loop_list_slice_ext(ph1, 5, 15);loop_list_clear(&ph1);free(ph1);ph1 = NULL;loop_list_access(p);printf("\n");loop_list_create_head(&ph2, 5);loop_list_access(ph2);printf("\n");ph3 = loop_list_joint(ph2, p);loop_list_access(ph3);printf("\n");loop_list_clear(&ph3);free(ph3);ph3 = NULL;//有没有内存泄漏? ### 没有,下面的释放 ph2 和 p 不需要!!!/*if (ph2) {free(ph2);//ph2 和 ph3 实际上指向的是同一块堆内存,而这块堆内存已经被 ph3 释放了,如果重复释放将会导致错误!!!ph2 = NULL;}if (p) {free(p);//p 指向的堆内存在 list_joint() 函数中已经释放了头结点,p链表的身段部分在 ph3 中被释放,如果重复释放将会导致错误!!!p = NULL;}*/
}void test15_13(void)
{LLinkList ph1, ph2;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;ElemType data[] = { bbb, ccc, eee, 600, 800 };int n = sizeof(data) / sizeof(data[0]);ph1 = loop_list_init();loop_list_head_insert(&ph1, aaa);loop_list_tail_insert(&ph1, bbb);loop_list_insert(&ph1, 1, ccc);loop_list_insert(&ph1, 3, ddd);loop_list_insert(&ph1, 4, eee);loop_list_insert(&ph1, 2, bbb);loop_list_insert(&ph1, 3, bbb);loop_list_append(&ph1, 600);loop_list_append(&ph1, 700);loop_list_append(&ph1, 800);loop_list_append(&ph1, 900);loop_list_append(&ph1, 1000);loop_list_access(ph1);printf("\n");for (int i = 0; i < n; i++) {printf("%d\t", data[i]);}printf("\n");ph2 = loop_list_create_subset(ph1, data, n);loop_list_access(ph2);printf("\n");loop_list_clear(&ph1);loop_list_clear(&ph2);ph1 = NULL;ph2 = NULL;
}int main(void)
{test11();test12();test13();test14();test15();test15_2();test15_3();test15_4();test15_5();test15_6();test15_7();test15_8();test15_9();test15_10();test15_11();test15_12();test15_13();return 0;
}
#endif

双向循环链表

//双向循环链表//创建一个带有头结点的双向空链表
LDLinkList loop_doubly_list_init(void)
{LDLinkList pHead;pHead = (LDLinkList)malloc(sizeof(NodeLD));assert(pHead);memset(pHead, 0, sizeof(NodeLD));pHead->prev = pHead;pHead->next = pHead;return pHead;
}//初始条件:链表 L 已经存在
//操作结果:在 L 的尾部添加一个新结点,其数据值为 e,链表 L 的长度加 1
Status loop_doubly_list_append(LDLinkList* L, ElemType e)
{LDLinkList p, r;r = *L;while (r->next != *L) {r = r->next;//将 r 变成尾指针}p = (LDLinkList)malloc(sizeof(NodeLD));assert(p);memset(p, 0, sizeof(NodeLD));r->next = p;p->prev = r;p->next = *L;(*L)->prev = p;p->data = e;return OK;
}//初始条件:链表 L 已经存在,并且 1 <= i <= loop_doubly_list_length(L)
//操作结果:在 L 中的第 i 个结点【之前】插入新结点,其数据值为 e,链表 L 的长度加 1
Status loop_doubly_list_insert(LDLinkList* L, int i, ElemType e)// 这里:L 是一个二重指针
{int j = 1;LDLinkList p, s;int count = loop_doubly_list_length(*L);if (count == 0) {return loop_doubly_list_append(L, e);}if (i >= 1 && i <= count){p = *L;while ((p->next != *L) && j < i) {//寻找第 i-1 个结点p = p->next;++j;}if ((p->next == *L) || j > i)return ERROR;s = (LDLinkList)malloc(sizeof(NodeLD));assert(s);memset(s, 0, sizeof(NodeLD));//if (p->next != *L)//这个判断没有必要!{s->prev = p;s->next = p->next;p->next->prev = s;p->next = s;}//else {//    s->prev = p;//    s->next = *L;//    (*L)->prev = s;//    p->next = s;//}s->data = e;return OK;}return ERROR;
}//初始条件:链表 L 已经存在,并且 1 <= i <= loop_doubly_list_length(L)
//操作结果:删除 L 的第 i 个结点,用 e 返回其值,链表 L 的长度减 1
Status loop_doubly_list_delete(LDLinkList* L, int i, ElemType* e)
{int j = 1;LDLinkList p, q;int count = loop_doubly_list_length(*L);if (i >= 1 && i <= count){q = *L;while ((q->next != *L) && j < i) {//寻找第 i-1 个结点q = q->next;++j;}if ((q->next == *L) || j > i)return ERROR;//第i个结点不存在就报错p = q->next;//p指向第i个结点//if (p->next != *L)//p->next 不能是头结点 ### 这个判断没有必要!{p->prev->next = p->next;p->next->prev = p->prev;}//else {//    p->prev->next = *L;//    (*L)->prev = p->prev;//}*e = p->data;free(p);return OK;}return ERROR;
}//初始条件:链表 L 已经存在
//操作结果:返回链表的长度 ### 不包括头结点
int loop_doubly_list_length(LDLinkList L)
{int i = 0;LDLinkList p = L->next;while (p != L) {p = p->next;i++;}return i;
}//初始条件:链表 L 已经存在,并且 1 <= i <= loop_doubly_list_length(L)
//操作结果:用 e 返回 L 中第 i 个元素的值
Status loop_doubly_list_get_elem(LDLinkList L, int i, ElemType* e)
{int j = 1;LDLinkList p = L->next;int count = loop_doubly_list_length(L);if (i >= 1 && i <= count){while ((p != L) && j < i) {p = p->next;++j;}if ((p == L) || j > i)return ERROR;*e = p->data;return OK;}return ERROR;
}//初始条件:链表 L 已经存在,并且 1 <= i <= loop_doubly_list_length(L)
//操作结果:修改 L 中第 i 个元素的值为指定的 newData
Status loop_doubly_list_modify(LDLinkList* L, int i, ElemType newData)
{int j = 1;LDLinkList p = (*L)->next;int count = loop_doubly_list_length(*L);if (i >= 1 && i <= count){while ((p != *L) && j < i) {p = p->next;++j;}if ((p == *L) || j > i)return ERROR;p->data = newData;return OK;}return ERROR;
}//创建带头结点的双向链表 L,L 有 n 的结点,每个结点的值随机产生 ### 每次都在 结点1 的位置插入新结点
void loop_doubly_list_create_head(LDLinkList* L, int n)//头插法
{LDLinkList p;int i;srand(time(0));*L = (LDLinkList)malloc(sizeof(NodeLD));assert(*L);memset(*L, 0, sizeof(NodeLD));(*L)->prev = *L;(*L)->next = *L;for (i = 0; i < n; i++) {p = (LDLinkList)malloc(sizeof(NodeLD));assert(p);memset(p, 0, sizeof(NodeLD));if (i == 0) {(*L)->prev = p;//保证最终头结点的 prev 是尾结点}p->data = rand() % 100 + 1;/*要领:new->prev = header;new->next = header->next;//新节点的next指针指向原来的 节点1 的地址if(header != header->next)//判断当前结点的下一个结点的地址是否为头结点{header->next->prev = new;//原来的结点1 的prev指针指向新节点的地址}header->next = new;*/p->prev = *L;p->next = (*L)->next;//起初,因为 L 是空链表,头结点即是尾结点,n==1 或者 i==0 时,第一个加进来的结点就是新的尾结点,L 是头结点,(*L)->next 也是头结点,这就保证了最终尾结点的 next 是头结点if ((*L)->next != *L) {(*L)->next->prev = p;}(*L)->next = p;}
}//创建带头结点的双向链表 L,L 有 n 的结点,每个结点的值随机产生 ### 每次都在链表的最后位置加上新结点
/*
假设:r 始终是链表的尾结点
在尾部添加一个新结点p后
p变成尾结点,r 倒数第二
因此:r->next = p;
为了让 r 再次变成尾结点,将 p 赋值给 r
因此:r = p;
所有结点添加完成,将尾结点的指针域指空:r->next = NULL;
*/
void loop_doubly_list_create_tail(LDLinkList* L, int n)//尾插法
{LDLinkList p, r;int i;srand(time(0));*L = (LDLinkList)malloc(sizeof(NodeLD));assert(*L);memset(*L, 0, sizeof(NodeLD));(*L)->prev = *L;(*L)->next = *L;r = *L;while (r->next != *L) {r = r->next;//r 始终是链表的尾结点}for (i = 0; i < n; i++) {p = (LDLinkList)malloc(sizeof(NodeLD));assert(p);memset(p, 0, sizeof(NodeLD));p->data = rand() % 100 + 1;//要领:1.原来尾节点的next指针指向新节点的首地址;2.新节点的prev指针指向原来的尾节点的首地址r->next = p;p->prev = r;p->next = *L;r = p;}//r->next = *L;//这一句可以不要(*L)->prev = r;
}//初始条件:链表 L 已经存在
//操作结果:将链表清空 ### 每次都删除结点1 ### 但仍然保留着头结点
Status loop_doubly_list_clear(LDLinkList* L)
{LDLinkList p, q;p = (*L)->next;while (p != *L) {q = p->next;//先保存 p->next 的值(内存地址),即结点2(如果存在的话),防止在 free(p) 时  p->next 的值丢失/*//这里只要后继指针保持正常就行if (q != *L) {q->prev = *L;//如果结点2存在,将结点2的前驱指针指向头结点(*L)->next = q;}*/free(p);p = q;//再将保存的  p->next 的值赋值给 p 进行下一轮的循环判断}(*L)->next = *L;(*L)->prev = *L;return OK;
}//#include <windows.h>
//void Sleep(DWORD dwMilliseconds); 参数为毫秒
void loop_doubly_list_access(LDLinkList L)
{int i = 0;LDLinkList p = L->next;while (p != L) {i++;printf("第%d个数据:%d\n", i, p->data);p = p->next;Sleep(5);//休眠5ms}
}void loop_doubly_list_access_back(LDLinkList L)
{int i = 0;LDLinkList p = L;while (p->next != L) {p = p->next;//找到尾结点}while (p != L) {i++;printf("第%d个数据:%d\n", i, p->data);p = p->prev;Sleep(5);}
}//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
Status loop_doubly_list_head_insert(LDLinkList* L, ElemType e)
{LDLinkList s;s = (LDLinkList)malloc(sizeof(NodeLD));assert(s);memset(s, 0, sizeof(NodeLD));s->prev = *L;s->next = (*L)->next;if ((*L)->next != *L) {(*L)->next->prev = s;}(*L)->next = s;s->data = e;return OK;
}//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
Status loop_doubly_list_tail_insert(LDLinkList* L, ElemType e)
{return loop_doubly_list_append(L, e);
}//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status loop_doubly_list_head_delete(LDLinkList* L, ElemType* e)
{LDLinkList p;p = (*L)->next;if (p == *L) {return ERROR;}(*L)->next = p->next;if (p->next != *L) {p->next->prev = *L;}*e = p->data;free(p);return OK;
}//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status loop_doubly_list_tail_delete(LDLinkList* L, ElemType* e)
{LDLinkList p, r;int count = loop_doubly_list_length(*L);if (count == 1) {p = (*L)->next;*e = p->data;free(p);(*L)->next = *L;return OK;}else if (count > 1) {p = r = *L;while (p->next->next != *L) {p = p->next;//p最终变成倒数第二个结点r = p->next;//r最终变成尾结点}p->next = *L;*e = r->data;free(r);return OK;}else {return ERROR;}
}//初始条件:链表 L 已经存在
//操作结果:直接删除结点 node
Status loop_doubly_list_delete_by_node_address(LDLinkList* L, NodeLD* node)
{LDLinkList prevL, curL, nextL;prevL = nextL = *L;curL = (*L)->next;while (curL != *L) {if (node == curL) {nextL = curL->next;prevL->next = nextL;nextL->prev = prevL;free(curL);return OK;}prevL = prevL->next;curL = curL->next;}return ERROR;
}//初始条件:链表 L 已经存在
//操作结果:删除包含着数据 e 的第一个结点
Status loop_doubly_list_delete_by_data(LDLinkList* L, ElemType e)
{LDLinkList prevL, curL, nextL;prevL = nextL = *L;curL = (*L)->next;while (curL != *L) {if (e == curL->data) {nextL = curL->next;prevL->next = nextL;nextL->prev = prevL;free(curL);return OK;}prevL = prevL->next;curL = curL->next;}return ERROR;
}//初始条件:链表 L 已经存在
//操作结果:返回第 i 个位置的结点的地址
NodeLD* loop_doubly_list_get_node(LDLinkList L, int i)
{int j = 0;int count = loop_doubly_list_length(L);NodeLD* p = L;if (!(1 <= i <= count)) {return NULL;}while (j < i){j++;p = p->next;}return (p != L) ? p : NULL;
}//复制一个结点
NodeLD* loop_doubly_list_copy_node(NodeLD* node)
{NodeLD* p = (NodeLD*)malloc(sizeof(NodeLD));assert(p);memset(p, 0, sizeof(NodeLD));p->data = node->data;return p;
}//初始条件:链表 L 已经存在 ### 返回 node 的位序
//如果 node 的地址与 链表 L 中任何一个结点的地址相同,则认为 node 存在于 L 中,返回值 >0; 否则返回值为 0
int loop_doubly_list_node_location(LDLinkList L, NodeLD* node)
{int i = 0;Status find = FALSE;LDLinkList p = L;while (p->next != L) {i++;p = p->next;if (p == node) {find = TRUE;break;}}return (find == TRUE) ? i : 0;
}//初始条件:链表 L 已经存在 ### 返回 e 的位序
//如果数据 e 与链表 L 中某个结点的数据完全相同,则认为 e 存在于 L 中,返回值 >0; 否则返回值为 0
int loop_doubly_list_elem_location(LDLinkList L, ElemType e)
{int i = 0;Status find = FALSE;LDLinkList p = L;while (p->next != L) {i++;p = p->next;if (p->data == e) {find = TRUE;break;}}return (find == TRUE) ? i : 0;
}//初始条件:链表 L 已经存在 ### node1 和 node2 属于链表 L
//比较 node1 与 node2 的数据是否完全相同
//完全相同,返回 TRUE;否则,返回 FALSE
Status loop_doubly_list_node_compare(LDLinkList L, NodeLD* node1, NodeLD* node2)
{int i = loop_doubly_list_node_location(L, node1);int j = loop_doubly_list_node_location(L, node2);if (i == 0 || j == 0) {return FALSE;}if (node1->data == node2->data) {return TRUE;}return FALSE;
}//初始条件:链表 L 已经存在
//操作结果:将链表逆序
//链表逆序 = 遍历 + 头插入
Status loop_doubly_list_reverse(LDLinkList* L)
{LDLinkList p, q, r;r = p = (*L)->next;//最开始让p指向结点1while (p != *L)//始终将结点p插入在结点1的位置,p从结点1挨个遍历到尾结点 ### while 循环结束时,结点1 就变成了尾结点,r 即是尾结点{q = p->next;p->prev = *L;p->next = (*L)->next;(*L)->next->prev = p;(*L)->next = p;p = q;}r->next = *L;return OK;
}//多个结点可以包含相同的数据,因此有必要去除重复的数据
Status loop_doubly_list_remove_duplication(LDLinkList* L)
{LDLinkList p, q;ElemType data1, data2;p = *L;q = p->next;while (p->next != *L) {p = p->next;data1 = p->data;while (q->next != *L) {q = q->next;data2 = q->data;if (data1 == data2) {if (loop_doubly_list_delete_by_data(L, data1) == ERROR) {return ERROR;}p = *L;//删除一个重复的结点后要重新设置循环条件,从第一个结点开始重新查找重复的结点break;}}q = p->next;}return OK;
}Status loop_doubly_list_remove_duplication_ext(LDLinkList* L)
{int i = 0;int j = 0;LDLinkList p, q;ElemType data1, data2;p = *L;q = p->next;while (p->next != *L) {i++;p = p->next;data1 = p->data;while (q->next != *L) {q = q->next;data2 = q->data;if (data1 == data2) {if (loop_doubly_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 已经存在
//操作结果:将两个链表直接拼接成一个
LDLinkList loop_doubly_list_joint(LDLinkList L1, LDLinkList L2)
{LDLinkList h1, h2, r1, r2, p;h1 = L1;h2 = L2;r1 = L1->next;p = r2 = L2->next;while (r1->next != h1) {r1 = r1->next;//r1 是 L1 的尾结点}while (r2->next != h2) {r2 = r2->next;//r2 是 L2 的尾结点}if (p != h2) {//L2 链表不是空表r1->next = p;p->prev = r1;r2->next = h1;h1->prev = r2;}free(h2);return h1;
}//初始条件:链表 L1 和 L2 已经存在
//操作结果:将 L2 中的结点依次添加到 L1 中,前提:即将添加的结点的数据部分与 L1 中任何一个结点的数据都不同
//最后销毁 L2 的头结点
//如果 L2 的所有结点所包含的数据在 L1 中都存在,则完全销毁 L2
LDLinkList loop_doubly_list_merge(LDLinkList L1, LDLinkList L2)
{int n = 0;ElemType e = 0;LDLinkList p = L1;LDLinkList h = L1;while (p->next != L1) {p = p->next;n = loop_doubly_list_elem_location(L2, p->data);if (n > 0) {loop_doubly_list_delete(&L2, n, &e);//对于 L1 中的每一个结点p,如果p在 L2 中有相同的副本,就将该副本从 L2 中删除n = 0;}}if (loop_doubly_list_length(L2) > 0) {h = loop_doubly_list_joint(L1, L2);}else {free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间}return h;
}LDLinkList loop_doubly_list_merge2(LDLinkList L1, LDLinkList L2)
{int n = 0;int i = 0;int j = 0;ElemType e = 0;LDLinkList p = L2;LDLinkList h = L1;while (p->next != L2) {i++;p = p->next;n = loop_doubly_list_elem_location(L1, p->data);if (n > 0) {loop_doubly_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 (loop_doubly_list_length(L2) > 0) {h = loop_doubly_list_joint(L1, L2);}else {free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间}return h;
}//初始条件:链表 L1 和 L2 已经存在
//将 L1 和 L2 拼接在一起,保证每一个结点的数据部分都是独一无二的
LDLinkList loop_doubly_list_merge_ext(LDLinkList L1, LDLinkList L2)
{LDLinkList h = loop_doubly_list_joint(L1, L2);return (loop_doubly_list_remove_duplication_ext(&h) == OK) ? h : NULL;
}//初始条件:链表 L 已经存在 ### 截取 L 的一个连续的子片段
// 1 <= i <= loop_doubly_list_length(L) && 1 <= j <= loop_doubly_list_length(L)
//操作结果:对链表进行切片,返回包含 结点i 和 结点j 以及 它们中间的所有结点组成的链表片段 ### 没有头结点
//注意:如果你截取链表 L 的子片段 L0 是为了把它拼接到另外一个链表中去 ### 这将会产生极大的隐患
//当链表 L 被清空时,子片段 L0 也就被清空了,这将导致与 L0 连接的链表在 L0 两端的连接处断开,产生灾难
//一定要小心使用 loop_doubly_list_slice() !!!
//loop_doubly_list_slice() 不提供给外部使用!!!
static LDLinkList loop_doubly_list_slice(LDLinkList L, int i, int j)
{int k = 0;int len = 0;int start = 0;int count = 0;LDLinkList p = L;LDLinkList r = L;count = loop_doubly_list_length(L);assert(1 <= i <= count);assert(1 <= j <= count);start = (i <= j) ? i : j;len = abs(i - j);for (k = 0; k < start; k++) {p = p->next;//p是最终链表片段的第一个结点}for (k = 0, r = p; k < len; k++) {r = r->next;//r是最终链表片段的最后一个结点}//r->next = NULL;//注意:这会导致被截取片段的链表意外断裂!!!但是没有这一句,子链表又不能正常的结束!!!return p;
}//为 loop_doubly_list_slice() 产生的链表子片段的每个结点分配新的存储空间,并附加一个头结点
LDLinkList loop_doubly_list_slice_ext(LDLinkList L, int i, int j)
{int k = abs(i - j);LDLinkList r = NULL;LDLinkList s = NULL;LDLinkList pHead = NULL;LDLinkList tmp = loop_doubly_list_slice(L, i, j);pHead = s = (NodeLD*)malloc(sizeof(NodeLD));assert(s);memset(s, 0, sizeof(NodeLD));for (; k >= 0; k--) {r = (NodeLD*)malloc(sizeof(NodeLD));assert(r);memset(r, 0, sizeof(NodeLD));memcpy(r, tmp, sizeof(NodeLD));s->next = r;s = s->next;tmp = tmp->next;}r->next = pHead;return pHead;
}//初始条件:链表 L 已经存在
//获取 L 的一个子集,不一定是 L 的连续子片段,组装成一个新的链表
LDLinkList loop_doubly_list_create_subset(LDLinkList L, ElemType data[], int n)
{int i = 0;int j = 0;LDLinkList s = L->next;LDLinkList t = NULL;LDLinkList q = NULL;LDLinkList r = NULL;//r是尾结点LDLinkList pHead = NULL;pHead = q = (NodeLD*)malloc(sizeof(NodeLD));assert(q);memset(q, 0, sizeof(NodeLD));for (i = 0; i < n; i++) {while (s != L) {j++;if (s->data == data[i]) {//找到一个目标结点t = loop_doubly_list_get_node(L, j);r = (NodeLD*)malloc(sizeof(NodeLD));assert(r);memset(r, 0, sizeof(NodeLD));r->data = t->data;q->next = r;q = q->next;break;}s = s->next;}s = L->next;//重新开始从头查找j = 0;}r->next = pHead;return pHead;
}//初始条件:链表 L 已经存在
//操作结果:将链表按照某种规则排序
Status loop_doubly_list_sort_by_name(LDLinkList* L)
{return OK;
}#if 1
void test16(void)
{ElemType aaa, bbb;LDLinkList pHead;int len = 0;loop_doubly_list_create_head(&pHead, 10);loop_doubly_list_access(pHead);printf("\n\n");loop_doubly_list_insert(&pHead, 3, 10000);loop_doubly_list_access(pHead);printf("\n\n");loop_doubly_list_delete(&pHead, 6, &aaa);printf("aaa:%d\n", (int)aaa);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_insert(&pHead, 10, 20000);//边界插入:此时链表长度刚好为10 ### 在链表的最后插入一个结点会怎么样? ### OKloop_doubly_list_access(pHead);printf("\n");loop_doubly_list_insert(&pHead, 1, 30000);//边界插入:在1 的位置插入一个结点会怎么样? ### OKloop_doubly_list_access(pHead);//loop_doubly_list_insert(&pHead, 13, 40000);//边界插入:此时链表长度为12,我想在 13 的位置插入会怎么样? ### 出错了!//loop_doubly_list_insert(&pHead, 0, 50000);//边界插入:在 0 的位置插入呢? ### 虽然没有出错,但是没有意义!//loop_doubly_list_insert(&pHead, 0, 60000);//loop_doubly_list_access(pHead);//printf("出错了吗?\n");len = loop_doubly_list_length(pHead);printf("最终链表长度为:%d\n", len);loop_doubly_list_clear(&pHead);free(pHead);printf("\n\n");loop_doubly_list_create_tail(&pHead, 20);loop_doubly_list_access(pHead);loop_doubly_list_get_elem(pHead, 15, &bbb);printf("bbb:%d\n", (int)bbb);loop_doubly_list_delete(&pHead, 15, &aaa);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test17(void)
{LDLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;pHead = loop_doubly_list_init();loop_doubly_list_append(&pHead, aaa);loop_doubly_list_append(&pHead, bbb);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test18(void)
{LDLinkList pHead;ElemType aaa = 300;ElemType bbb = 400;ElemType ccc = 500;pHead = loop_doubly_list_init();loop_doubly_list_insert(&pHead, 1, aaa);loop_doubly_list_insert(&pHead, 1, bbb);loop_doubly_list_insert(&pHead, 1, ccc);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test19(void)
{LDLinkList pHead;ElemType aaa = 600;ElemType bbb = 700;ElemType ccc = 800;ElemType ddd = 900;ElemType eee = 1000;ElemType fff = 2000;ElemType ggg;ElemType hhh;pHead = loop_doubly_list_init();loop_doubly_list_insert(&pHead, 1, aaa);loop_doubly_list_append(&pHead, bbb);loop_doubly_list_insert(&pHead, 1, ccc);loop_doubly_list_append(&pHead, ddd);loop_doubly_list_access(pHead);printf("\n\n");loop_doubly_list_insert(&pHead, 2, eee);loop_doubly_list_access(pHead);printf("\n\n");loop_doubly_list_insert(&pHead, 4, fff);loop_doubly_list_access(pHead);printf("\n\n");loop_doubly_list_delete(&pHead, 1, &ggg);printf("ggg:%d\n", (int)ggg);loop_doubly_list_access(pHead);printf("\n\n");loop_doubly_list_delete(&pHead, 2, &hhh);printf("hhh:%d\n", (int)hhh);loop_doubly_list_access(pHead);loop_doubly_list_delete(&pHead, 2, &hhh);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20(void)
{LDLinkList pHead;loop_doubly_list_create_head(&pHead, 0);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);loop_doubly_list_create_tail(&pHead, 0);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_2(void)//链表长度为1,删除链表的一个结点会出现什么情况?
{LDLinkList pHead;ElemType aaa = 100;ElemType bbb;pHead = loop_doubly_list_init();loop_doubly_list_append(&pHead, aaa);loop_doubly_list_access(pHead);loop_doubly_list_delete(&pHead, 1, &bbb);loop_doubly_list_delete(&pHead, 1, &bbb);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_3(void)
{LDLinkList pHead;ElemType aaa = 12345;ElemType bbb = 23456;ElemType ccc = 34567;loop_doubly_list_create_head(&pHead, 10);loop_doubly_list_access(pHead);printf("\n\n");loop_doubly_list_modify(&pHead, 5, aaa);printf("\n");loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_modify(&pHead, 1, bbb);loop_doubly_list_modify(&pHead, 10, ccc);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_4(void)//专门测试链表为空表,使用 loop_doubly_list_insert() 在 结点1 的位置插入结点的情况 ### 是 OK 的!!!
{LDLinkList pHead;ElemType aaa = 100;pHead = loop_doubly_list_init();loop_doubly_list_insert(&pHead, 1, aaa);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_5(void)
{LDLinkList pHead;loop_doubly_list_create_head(&pHead, 5);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_access_back(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_6(void)
{LDLinkList pHead;loop_doubly_list_create_head(&pHead, 5);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_reverse(&pHead);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_access_back(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_7(void)
{LDLinkList ph1, ph2, ph3;int count = 0;loop_doubly_list_create_head(&ph1, 5);loop_doubly_list_create_tail(&ph2, 8);loop_doubly_list_access(ph1);printf("\n");loop_doubly_list_access(ph2);printf("\n");ph3 = loop_doubly_list_joint(ph1, ph2);count = loop_doubly_list_length(ph3);printf("合并后的新链表长度:%d\n", count);printf("ph3: %p\n", ph3);printf("ph1: %p\n", ph1);printf("ph2: %p\n", ph2);loop_doubly_list_access(ph3);printf("\n");loop_doubly_list_access_back(ph3);loop_doubly_list_clear(&ph3);free(ph3);ph3 = NULL;//有没有内存泄漏? ### 没有,下面的释放 ph1 和 ph2 不需要!!!/*if (ph1) {free(ph1);//ph1 和 ph3 实际上指向的是同一块堆内存,而这块堆内存已经被 ph3 释放了,如果重复释放将会导致错误!!!ph1 = NULL;}if (ph2) {free(ph2);//ph2 指向的堆内存在 loop_doubly_list_joint() 函数中已经释放了头指针,ph2 链表的身段部分在 ph3 中被释放,如果重复释放将会导致错误!!!ph2 = NULL;}*/
}void test20_8(void)
{LDLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee;pHead = loop_doubly_list_init();loop_doubly_list_tail_insert(&pHead, aaa);loop_doubly_list_head_insert(&pHead, bbb);loop_doubly_list_insert(&pHead, 1, ccc);loop_doubly_list_append(&pHead, ddd);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_head_delete(&pHead, &eee);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_head_delete(&pHead, &eee);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_tail_delete(&pHead, &eee);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_tail_delete(&pHead, &eee);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_9(void)
{LDLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = loop_doubly_list_init();loop_doubly_list_head_insert(&pHead, aaa);loop_doubly_list_tail_insert(&pHead, bbb);loop_doubly_list_insert(&pHead, 1, ccc);loop_doubly_list_insert(&pHead, 3, ddd);loop_doubly_list_insert(&pHead, 4, eee);loop_doubly_list_insert(&pHead, 2, bbb);loop_doubly_list_insert(&pHead, 3, bbb);loop_doubly_list_access(pHead);printf("\n");loop_doubly_list_delete_by_data(&pHead, bbb);loop_doubly_list_delete_by_data(&pHead, bbb);loop_doubly_list_delete_by_data(&pHead, bbb);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_10(void)
{int loc = 0;Status res = FALSE;LDLinkList pHead;NodeLD* node1, * node2, * node3;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = loop_doubly_list_init();loop_doubly_list_head_insert(&pHead, aaa);loop_doubly_list_tail_insert(&pHead, bbb);loop_doubly_list_insert(&pHead, 1, ccc);loop_doubly_list_insert(&pHead, 3, ddd);loop_doubly_list_insert(&pHead, 4, eee);loop_doubly_list_insert(&pHead, 2, bbb);loop_doubly_list_insert(&pHead, 3, bbb);loop_doubly_list_access(pHead);printf("\n");node1 = loop_doubly_list_get_node(pHead, 2);node2 = loop_doubly_list_get_node(pHead, 3);node3 = loop_doubly_list_get_node(pHead, 7);loc = loop_doubly_list_node_location(pHead, node1);loc = loop_doubly_list_node_location(pHead, node2);loc = loop_doubly_list_node_location(pHead, node3);loc = loop_doubly_list_elem_location(pHead, aaa);loc = loop_doubly_list_elem_location(pHead, ccc);loc = loop_doubly_list_elem_location(pHead, ddd);loc = loop_doubly_list_elem_location(pHead, bbb);loc = loop_doubly_list_elem_location(pHead, eee);res = loop_doubly_list_node_compare(pHead, node1, node2);loop_doubly_list_delete_by_node_address(&pHead, node1);loop_doubly_list_delete_by_node_address(&pHead, node2);loop_doubly_list_delete_by_node_address(&pHead, node3);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_11(void)
{LDLinkList pHead;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;pHead = loop_doubly_list_init();loop_doubly_list_head_insert(&pHead, aaa);loop_doubly_list_tail_insert(&pHead, bbb);loop_doubly_list_insert(&pHead, 1, ccc);loop_doubly_list_insert(&pHead, 3, ddd);loop_doubly_list_insert(&pHead, 4, eee);loop_doubly_list_insert(&pHead, 2, bbb);loop_doubly_list_insert(&pHead, 3, bbb);loop_doubly_list_access(pHead);printf("\n");//loop_doubly_list_remove_duplication(&pHead);loop_doubly_list_remove_duplication_ext(&pHead);loop_doubly_list_access(pHead);loop_doubly_list_clear(&pHead);free(pHead);pHead = NULL;
}void test20_12(void)
{LDLinkList ph1, ph2, ph3;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;ph1 = loop_doubly_list_init();loop_doubly_list_head_insert(&ph1, aaa);loop_doubly_list_tail_insert(&ph1, bbb);loop_doubly_list_insert(&ph1, 1, ccc);loop_doubly_list_insert(&ph1, 3, ddd);loop_doubly_list_insert(&ph1, 4, eee);loop_doubly_list_insert(&ph1, 2, bbb);loop_doubly_list_insert(&ph1, 3, bbb);loop_doubly_list_access(ph1);printf("\n");ph2 = loop_doubly_list_init();loop_doubly_list_head_insert(&ph2, 600);loop_doubly_list_head_insert(&ph2, 700);loop_doubly_list_head_insert(&ph2, 800);loop_doubly_list_head_insert(&ph2, 900);loop_doubly_list_head_insert(&ph2, 1000);loop_doubly_list_insert(&ph2, 3, bbb);loop_doubly_list_insert(&ph2, 5, bbb);loop_doubly_list_access(ph2);printf("\n");//ph3 = loop_doubly_list_merge(ph1, ph2);//ph3 = loop_doubly_list_merge2(ph1, ph2);ph3 = loop_doubly_list_merge_ext(ph1, ph2);loop_doubly_list_access(ph3);loop_doubly_list_clear(&ph3);free(ph3);ph3 = NULL;
}void test20_13(void)
{LDLinkList ph1, ph2, ph3, p;loop_doubly_list_create_tail(&ph1, 20);loop_doubly_list_access(ph1);printf("\n");p = loop_doubly_list_slice_ext(ph1, 5, 15);loop_doubly_list_clear(&ph1);free(ph1);ph1 = NULL;loop_doubly_list_access(p);printf("\n");loop_doubly_list_create_head(&ph2, 5);loop_doubly_list_access(ph2);printf("\n");ph3 = loop_doubly_list_joint(ph2, p);loop_doubly_list_access(ph3);printf("\n");loop_doubly_list_clear(&ph3);free(ph3);ph3 = NULL;//有没有内存泄漏? ### 没有,下面的释放 ph2 和 p 不需要!!!/*if (ph2) {free(ph2);//ph2 和 ph3 实际上指向的是同一块堆内存,而这块堆内存已经被 ph3 释放了,如果重复释放将会导致错误!!!ph2 = NULL;}if (p) {free(p);//p 指向的堆内存在 list_joint() 函数中已经释放了头结点,p链表的身段部分在 ph3 中被释放,如果重复释放将会导致错误!!!p = NULL;}*/
}void test20_14(void)
{LDLinkList ph1, ph2;ElemType aaa = 100;ElemType bbb = 200;ElemType ccc = 300;ElemType ddd = 400;ElemType eee = 500;ElemType data[] = { bbb, ccc, eee, 600, 800 };int n = sizeof(data) / sizeof(data[0]);ph1 = loop_doubly_list_init();loop_doubly_list_head_insert(&ph1, aaa);loop_doubly_list_tail_insert(&ph1, bbb);loop_doubly_list_insert(&ph1, 1, ccc);loop_doubly_list_insert(&ph1, 3, ddd);loop_doubly_list_insert(&ph1, 4, eee);loop_doubly_list_insert(&ph1, 2, bbb);loop_doubly_list_insert(&ph1, 3, bbb);loop_doubly_list_append(&ph1, 600);loop_doubly_list_append(&ph1, 700);loop_doubly_list_append(&ph1, 800);loop_doubly_list_append(&ph1, 900);loop_doubly_list_append(&ph1, 1000);loop_doubly_list_access(ph1);printf("\n");for (int i = 0; i < n; i++) {printf("%d\t", data[i]);}printf("\n");ph2 = loop_doubly_list_create_subset(ph1, data, n);loop_doubly_list_access(ph2);printf("\n");loop_doubly_list_clear(&ph1);loop_doubly_list_clear(&ph2);ph1 = NULL;ph2 = NULL;
}int main(void)
{test16();test17();test18();test19();test20();test20_2();test20_3();test20_4();test20_5();test20_6();test20_7();test20_8();test20_9();test20_10();test20_11();test20_12();test20_13();test20_14();return 0;
}
#endif

在我自己的程序猿生涯中受到了CSDN的莫大支持
谨以此篇作为对大陆程序猿的最后感谢

一文看懂C语言链表(原创) --- 包含完整代码相关推荐

  1. c语言异或运算作用,一文看懂C语言异或运算

    描述 一.异或运算简介 异或运算一般指异或.异或(xor)是一个数学运算符.它应用于逻辑运算.异或的数学符号为"⊕",计算机符号为"xor".其运算法则为: a ...

  2. Go 言 Go 语,一文看懂 Go 语言文件操作

    文章目录 ⛳️ 实战场景 打开关闭文件 读取文件 bufio 读取文件 写文件 ⛳️ 实战场景 本篇博客为大家再次带来 Go 语言的基础知识,这次要学习的内容是 Go 中的文件操作. 打开关闭文件 在 ...

  3. c语音异或运算符_一文看懂C语言异或运算

    一.异或运算简介 异或运算一般指异或.异或(xor)是一个数学运算符.它应用于逻辑运算.异或的数学符号为"⊕",计算机符号为"xor".其运算法则为: a⊕b= ...

  4. angular 字符串转换成数字_一文看懂Python列表、元组和字符串操作

    好文推荐,转自CSDN,原作星辰StarDust,感觉写的比自己清晰-大江狗荐语. 序列 序列是具有索引和切片能力的集合. 列表.元组和字符串具有通过索引访问某个具体的值,或通过切片返回一段切片的能力 ...

  5. 「最有用」的特殊大数据:一文看懂文本信息系统的概念框架及功能

    导读:作为一种特殊的大数据,文本数据泛指各种以自然语言形式存在的数据. 目前,我们正处在一个以大数据与人工智能技术为核心的新的工业革命时代,其主要特征是大量各种可利用的数据可以视为一种特殊的生产资料, ...

  6. 一文看懂JUC之AQS机制

     作者:VectorJin juejin.cn/post/6844904041760161806 为了解决原子性的问题,Java加入了锁机制,同时保证了可见性和顺序性.JDK1.5的并发包中新增了Lo ...

  7. 决策树 随机森林 xgboost_一文看懂随机森林-RandomForest(附4个构造步骤+4种实现方式评测+10个优缺点)...

    随机森林是一种由决策树构成的集成算法,他在很多情况下都能有不错的表现.本文将介绍随机森林的基本概念.4 个构造步骤.4 种方式的对比评测.10 个优缺点和 4 个应用方向. 什么是随机森林? 随机森林 ...

  8. 华为p40pro手机计算机在哪里,一文看懂华为P40/P40 Pro差别在哪

    中关村在线消息:北京时间2020年3月26日,华为在线上举办新款旗舰产品发布会,会上发布了三款重量级手机新品:华为P40.华为P40 Pro和华为P40 Pro+. 在正式发布新品前,华为总裁余承东发 ...

  9. 一文看懂Android APK安装的原理

    一文看懂Android APK安装的原理 前言 APK包的构成 安装APK 总结 前言 大家有没有想过一个应用的APK是怎么被安装到安卓手机上的,安装的本质是什么?我们知道,Windows应用程序的安 ...

最新文章

  1. Linux MySQl 5.7.17 MySQL ERROR 1366(HY000):Incorrect string value 解决方法
  2. Linux Tomcat 安装
  3. Windows Server 2012正式版RDS系列②
  4. netty 高低位转码_Netty解决粘包和拆包问题的四种方案
  5. Android 进程间通信——Service、Messenger
  6. Spring Boot学习总结(14)——Spring Boot常见面试题汇总
  7. Sympy符号计算库
  8. Linux统计文件夹下文件数量
  9. H5页面,华为手机打开不加载JS的问题
  10. 2021年高教杯数学建模国赛C题思路详解
  11. 集线器,交换机与路由器
  12. 微信公众号服务器端脑图,微信公众号中隐藏的思维导图工具,帮你随时随地高效思考...
  13. java还原混淆代码,android混淆 android如何将混淆代码还原?
  14. java代理模式解析
  15. scram-sha1
  16. 软件使用说明网站+IT新闻评论
  17. 美妆电商跌宕十年,跨境模式能否让其重新崛起?
  18. Fragment的基本用法
  19. HQChart使用教程30-K线图如何对接第3方数据35-固定范围/可视范围成交量分布图数据
  20. ASP.NET MVC4 PRG模式

热门文章

  1. 入错行,悔断肠子也回不了头吗?未必,专家给你支招!
  2. uniapp云开发uniCloud.uploadFile上传图片文件后缀名丢失
  3. 歼-10战机正式列装我军航空兵
  4. 实习的Day1(熟悉环境,安装“环境”)
  5. 同学信誓旦旦地说,我司的系统从来不做性能调优!
  6. vue,springboot项目部署到window服务器
  7. 元宇宙中的情绪与情感探索在军事上的应用
  8. 通达信牛股攻击背离指标公式
  9. java map 多个key_java ListMap使用多个或者任意个数的key进行排序
  10. Logit模型和Logistic模型的区别