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

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

例如:

struct Student {
int num;
float score;
struct Student* next;
};

typedef int ElemType;

struct tnode {
ElemType data;
struct tnode* left;
struct tnode* right;
};

struct tnode2 {
ElemType data;
struct tnode2* prev;
struct tnode2* next;
};

单链表的基本概念:

头指针:链表中第一个结点的存储位置(内存地址)叫做头指针
头结点:在链表的第一个结点前附设的结点称为头结点 ### 一个链表可以【没有】头结点 ### 头结点的数据域一般无意义,可以不存储任何信息,也可以存放链表的长度等附加信息
尾指针:链表中最后一个结点的存储位置叫做尾指针
结点: 结点由存放数据元素的数据域和存放后继结点地址的指针域组成

如果一个链表没有头结点,则头指针直接指向 “结点1” ,结点1 变成此时的第一个结点
如果一个链表有头结点,则头指针指向头结点,头结点的 next 指针指向 “结点1” ### 若链表为空表,则头结点的 next 指针为 NULL

头指针和头结点的异同:
头指针是指向链表的第一个结点的首地址的指针,若链表有头结点,则头指针指向头结点
头指针具有标识作用,所以常用头指针冠以链表的名字
头指针是链表的必要元素,无论链表是否为空表,头指针均不为空! ### 如果链表是空表,并且没有头指针呢?

头结点是为了操作的统一和方便而设立的,放在第一个结点之前
有了头结点,对在第一个结点之前插入结点、删除第一个结点,其操作方式与对其它结点的操作方式就统一了
头结点不一定是链表的必要元素,可以没有头结点

双向链表:在单链表的每个结点中增加一个指向前驱结点的指针域

【鉴于头结点有上述好处,今后一律在链表中添加头结点】

单链表:head指针 → 头结点(第一个结点) → 结点1 → 结点2 → … → 结点i → … → 尾结点 → NULL

双向链表:
在这里插入图片描述

循环链表:将单链表中终端结点的指针由空指针改为指向头结点,使整个单链表形成一个环,这种首尾相接的单链表称为单循环链表,简称循环链表 ### 在编程中判断循环结束的条件,原来判断 p->next 是否为空,现在则是 p->next 是否等于头结点

双向循环链表:将尾结点的next指针指向头结点,将头结点的prev指针指向尾结点,这就形成了一个环 ### 在编程中判断循环结束的条件是 p->next 是否等于头结点

循环链表和双向循环链表中的每个结点的指针域不再是NULL

对于循环链表和双向循环链表,还可以从链表的任意中间位置开始访问全部的结点,在编程中判断循环结束的条件,p->next 是否等于循环开始前的自己 ### p->next != p0,至于是否要跳过头结点,取决于编程者

向链表中插入一个结点:

插入到链表的头部,作为首元结点,即结点1;
插入到链表中间的某个位置;
插入到链表的最末端,作为链表的最后一个结点;

因此,插入结点的时候注意测试边界情况,即在 结点1 的位置插入、在 尾结点 的位置插入,当然在中间插入的情况也要测试
同样的,删除结点也要测试边界删除、中间删除是否都能成功

头插法:始终让新结点在第一的位置
尾插法:每次新结点都添加在最后

单链表插入一个结点:### 在纸上画图以帮助理解
在这里插入图片描述

假设:
结点 p 存储数据 ai (i为下标)
结点 p->next 存储数据 ai+1 (i+1为下标) ### p->next 紧跟在 p 之后
结点 s 存储数据 e
p, s 为相应结点的指针

现在要将 结点s 插入到 结点p 和 结点p->next 之间,代码上如何实现?

s->next = p->next;
p->next = s;

这两句可不可以换位呢?不能!
如果先执行 p->next = s;
那么后执行 s->next = p->next; 则相当于 s->next = s; ### 这导致 s->next 的后继断了,也就是 p->next 结点与它的前一个结点之间断开了连接

循环链表插入一个结点,同样是这两句

从单链表中删除一个结点:

单链表删除一个结点:### 在纸上画图以帮助理解
在这里插入图片描述

假设:
结点 p 存储数据 ai-1 (i-1为下标)
结点 q 存储数据 ai (i为下标) ### q 紧跟在 p 后面,即 q = p->next
结点 q->next 存储数据 ai+1 (i+1为下标) ### q->next 紧跟在 q 后面
p, q 为相应结点的指针

现在要将 结点q 删除,代码上如何实现?

q = p->next;
p->next = q->next;
free(q);

循环链表删除一个结点,同样是这三句

双向链表插入一个结点:### 在纸上画图以帮助理解
在这里插入图片描述

同样的假设:
结点 p 存储数据 ai (i为下标)
结点 p->next 存储数据 ai+1 (i+1为下标) ### p->next 紧跟在 p 之后
结点 s 存储数据 e
p, s 为相应结点的指针

现在要将 结点s 插入到 结点p 和 结点p->next 之间,代码上如何实现?

s->prev = p;
s->next = p->next;
p->next->prev = s;// p->next != NULL 时才有这一句
p->next = s;

这四句同样不能换位
顺序是:先搞定s 的前驱和后继,再搞定后结点 p->next 的前驱,最后解决前结点 p 的后继

双向循环链表插入一个结点,同样是这四句,而且:
s->prev = p;
s->next = p->next;
p->next->prev = s;// p->next != 头结点 这一句判断也不需要!
p->next = s;

双向链表删除一个结点:### 在纸上画图以帮助理解
在这里插入图片描述

假设:
结点 p->prev 存储数据 ai-1 (i-1为下标)
结点 p 存储数据 ai (i为下标)
结点 p->next 存储数据 ai+1 (i+1为下标)

现在要将 结点p 删除,代码上如何实现?

p->prev->next = p->next;
p->next->prev = p->prev;// p->next != NULL 时才有这一句
free§;

双向循环链表删除一个结点,同样是这三句,而且:
p->prev->next = p->next;
p->next->prev = p->prev;// p->next != 头结点 这一句判断也不需要!
free§;

list.h

#pragma once

#define OK    1
#define ERROR 0
#define TRUE  1
#define FALSE 0

typedef int Status;
typedef int ElemType;

typedef struct ListNodeData {
   
    int a;
    float b;
    double c;
    int KeyID;
    char name[128];
    char description[256];
    char* details;
}ElemType2;

typedef struct Node {
   
    ElemType data;//数据可以是其它的复杂情况,比如结构体,这里为了讲述问题方便故意让 ElemType 非常简单
    struct Node* next;
}NodeL;//单链表

typedef struct Node2 {
   
    ElemType data;
    struct Node2* prev;
    struct Node2* next;
}NodeD;//双向链表

typedef struct Node3 {
   
    ElemType data;
    struct Node3* next;
}NodeLL;//循环链表

typedef struct Node4 {
   
    ElemType data;
    struct Node4* prev;
    struct Node4* next;
}NodeLD;//双向循环链表

typedef struct Node5 {
   
    ElemType2 data;
    struct Node5* next;
}NodeL2;

typedef struct Node* LinkList;
typedef struct Node2* DLinkList;
typedef struct Node3* LLinkList;
typedef struct Node4* LDLinkList;
typedef struct Node5* LinkList2;

//单链表
extern LinkList list_init(void);
extern Status list_append(LinkList* L, ElemType e);
extern Status list_insert(LinkList* L, int i, ElemType e);
extern Status list_delete(LinkList* L, int i, ElemType* e);
extern int list_length(LinkList L);
extern Status list_get_elem(LinkList L, int i, ElemType* e);
extern Status list_modify(LinkList* L, int i, ElemType newData);
extern void list_create_head(LinkList* L, int n);
extern void list_create_tail(LinkList* L, int n);
extern Status list_clear(LinkList* L);
extern void list_access(LinkList L);
extern void list_access2(LinkList L);
extern Status list_access_ext(LinkList L, NodeL* node);//待实现
extern Status list_head_insert(LinkList* L, ElemType e);
extern Status list_tail_insert(LinkList* L, ElemType e);
extern Status list_head_delete(LinkList* L, ElemType* e);
extern Status list_tail_delete(LinkList* L, ElemType* e);
extern Status list_delete_by_node_address(LinkList* L, NodeL* node);
extern Status list_delete_by_data(LinkList* L, ElemType e);
extern Status list_delete_by_name(LinkList* L, const char* name, ElemType2* e);//待实现
extern Status list_get_elem_by_name(LinkList L, const char* name, ElemType2* e);//待实现
extern NodeL* list_get_node(LinkList L, int i);
extern NodeL* list_copy_node(NodeL* node);
extern NodeL* list_get_node_by_name(LinkList L, const char* name);//待实现
extern NodeL* list_get_node_by_KeyID(LinkList L, int KeyID);//待实现
extern int list_node_location(LinkList L, NodeL* node);
extern int list_elem_location(LinkList L, ElemType e);
extern int list_name_location(LinkList L, const char* name);//待实现
extern int list_KeyID_location(LinkList L, int KeyID);//待实现
extern Status list_node_compare(LinkList L, NodeL* node1, NodeL* node2);
extern Status list_reverse(LinkList* L);
extern Status list_remove_duplication(LinkList* L);
extern Status list_remove_duplication_ext(LinkList* L);
extern LinkList list_joint(LinkList L1, LinkList L2);
extern LinkList list_merge(LinkList L1, LinkList L2);
extern LinkList list_merge2(LinkList L1, LinkList L2);
extern LinkList list_merge_ext(LinkList L1, LinkList L2);
static LinkList list_slice(LinkList L, int i, int j);
extern LinkList list_slice_ext(LinkList L, int i, int j);
extern LinkList list_create_subset(LinkList L, ElemType data[], int n);
extern Status list_sort_by_name(LinkList* L);//待实现

//双向链表
extern DLinkList doubly_list_init(void);
extern Status doubly_list_append(DLinkList* L, ElemType e);
extern Status doubly_list_insert(DLinkList* L, int i, ElemType e);
extern Status doubly_list_delete(DLinkList* L, int i, ElemType* e);
extern int doubly_list_length(DLinkList L);
extern Status doubly_list_get_elem(DLinkList L, int i, ElemType* e);
extern Status doubly_list_modify(DLinkList* L, int i, ElemType newData);
extern void doubly_list_create_head(DLinkList* L, int n);
extern void doubly_list_create_tail(DLinkList* L, int n);
extern Status doubly_list_clear(DLinkList* L);
extern void doubly_list_access(DLinkList L);
extern void doubly_list_access_back(DLinkList L);
extern Status doubly_list_head_insert(DLinkList* L, ElemType e);
extern Status doubly_list_tail_insert(DLinkList* L, ElemType e);
extern Status doubly_list_head_delete(DLinkList* L, ElemType* e);
extern Status doubly_list_tail_delete(DLinkList* L, ElemType* e);
extern Status doubly_list_delete_by_node_address(DLinkList* L, NodeD* node);
extern Status doubly_list_delete_by_data(DLinkList* L, ElemType e);
extern NodeD* doubly_list_get_node(DLinkList L, int i);
extern NodeD* doubly_list_copy_node(NodeD* node);
extern int doubly_list_node_location(DLinkList L, NodeD* node);
extern int doubly_list_elem_location(DLinkList L, ElemType e);
extern Status doubly_list_node_compare(DLinkList L, NodeD* node1, NodeD* node2);
extern Status doubly_list_reverse(DLinkList* L);
extern Status doubly_list_remove_duplication(DLinkList* L);
extern Status doubly_list_remove_duplication_ext(DLinkList* L);
extern DLinkList doubly_list_joint(DLinkList L1, DLinkList L2);
extern DLinkList doubly_list_merge(DLinkList L1, DLinkList L2);
extern DLinkList doubly_list_merge2(DLinkList L1, DLinkList L2);
extern DLinkList doubly_list_merge_ext(DLinkList L1, DLinkList L2);
static DLinkList doubly_list_slice(DLinkList L, int i, int j);
extern DLinkList doubly_list_slice_ext(DLinkList L, int i, int j);
extern DLinkList doubly_list_create_subset(DLinkList L, ElemType data[], int n);
extern Status doubly_list_sort_by_name(DLinkList* L);//待实现

//循环链表
extern LLinkList loop_list_init(void);
extern Status loop_list_append(LLinkList* L, ElemType e);
extern Status loop_list_insert(LLinkList* L, int i, ElemType e);
extern Status loop_list_delete(LLinkList* L, int i, ElemType* e);
extern int loop_list_length(LLinkList L);
extern Status loop_list_get_elem(LLinkList L, int i, ElemType* e);
extern Status loop_list_modify(LLinkList* L, int i, ElemType newData);
extern void loop_list_create_head(LLinkList* L, int n);
extern void loop_list_create_tail(LLinkList* L, int n);
extern Status loop_list_clear(LLinkList* L);
extern void loop_list_access(LLinkList L);
extern Status loop_list_head_insert(LLinkList* L, ElemType e);
extern Status loop_list_tail_insert(LLinkList* L, ElemType e);
extern Status loop_list_head_delete(LLinkList* L, ElemType* e);
extern Status loop_list_tail_delete(LLinkList* L, ElemType* e);
extern Status loop_list_delete_by_node_address(LLinkList* L, NodeLL* node);
extern Status loop_list_delete_by_data(LLinkList* L, ElemType e);
extern NodeLL* loop_list_get_node(LLinkList L, int i);
extern NodeLL* loop_list_copy_node(NodeLL* node);
extern int loop_list_node_location(LLinkList L, NodeLL* node);
extern int loop_list_elem_location(LLinkList L, ElemType e);
extern Status loop_list_node_compare(LLinkList L, NodeLL* node1, NodeLL* node2);
extern Status loop_list_reverse(LLinkList* L);
extern Status loop_list_remove_duplication(LLinkList* L);
extern Status loop_list_remove_duplication_ext(LLinkList* L);
extern LLinkList loop_list_joint(LLinkList L1, LLinkList L2);
extern LLinkList loop_list_merge(LLinkList L1, LLinkList L2);
extern LLinkList loop_list_merge2(LLinkList L1, LLinkList L2);
extern LLinkList loop_list_merge_ext(LLinkList L1, LLinkList L2);
static LLinkList loop_list_slice(LLinkList L, int i, int j);
extern LLinkList loop_list_slice_ext(LLinkList L, int i, int j);
extern LLinkList loop_list_create_subset(LLinkList L, ElemType data[], int n);
extern Status loop_list_sort_by_name(LLinkList* L);//待实现

//双向循环链表
extern LDLinkList loop_doubly_list_init(void);
extern Status loop_doubly_list_append(LDLinkList* L, ElemType e);
extern Status loop_doubly_list_insert(LDLinkList* L, int i, ElemType e);
extern Status loop_doubly_list_delete(LDLinkList* L, int i, ElemType* e);
extern int loop_doubly_list_length(LDLinkList L);
extern Status loop_doubly_list_get_elem(LDLinkList L, int i, ElemType* e);
extern Status loop_doubly_list_modify(LDLinkList* L, int i, ElemType newData);
extern void loop_doubly_list_create_head(LDLinkList* L, int n);
extern void loop_doubly_list_create_tail(LDLinkList* L, int n);
extern Status loop_doubly_list_clear(LDLinkList* L);
extern void loop_doubly_list_access(LDLinkList L);
extern void loop_doubly_list_access_back(LDLinkList L);
extern Status loop_doubly_list_head_insert(LDLinkList* L, ElemType e);
extern Status loop_doubly_list_tail_insert(LDLinkList* L, ElemType e);
extern Status loop_doubly_list_head_delete(LDLinkList* L, ElemType* e);
extern Status loop_doubly_list_tail_delete(LDLinkList* L, ElemType* e);
extern Status loop_doubly_list_delete_by_node_address(LDLinkList* L, NodeLD* node);
extern Status loop_doubly_list_delete_by_data(LDLinkList* L, ElemType e);
extern NodeLD* loop_doubly_list_get_node(LDLinkList L, int i);
extern NodeLD* loop_doubly_list_copy_node(NodeLD* node);
extern int loop_doubly_list_node_location(LDLinkList L, NodeLD* node);
extern int loop_doubly_list_elem_location(LDLinkList L, ElemType e);
extern Status loop_doubly_list_node_compare(LDLinkList L, NodeLD* node1, NodeLD* node2);
extern Status loop_doubly_list_reverse(LDLinkList* L);
extern Status loop_doubly_list_remove_duplication(LDLinkList* L);
extern Status loop_doubly_list_remove_duplication_ext(LDLinkList* L);
extern LDLinkList loop_doubly_list_joint(LDLinkList L1, LDLinkList L2);
extern LDLinkList loop_doubly_list_merge(LDLinkList L1, LDLinkList L2);
extern LDLinkList loop_doubly_list_merge2(LDLinkList L1, LDLinkList L2);
extern LDLinkList loop_doubly_list_merge_ext(LDLinkList L1, LDLinkList L2);
static LDLinkList loop_doubly_list_slice(LDLinkList L, int i, int j);
extern LDLinkList loop_doubly_list_slice_ext(LDLinkList L, int i, int j);
extern LDLinkList loop_doubly_list_create_subset(LDLinkList L, ElemType data[], int n);
extern Status loop_doubly_list_sort_by_name(LDLinkList* L);//待实现


//两把枪的 KeyID 之间相隔100,意思是允许玩家最多能同时拥有100把这种型号的武器
#define SINGLE_WEAPON_MAX     100
#define WEAPON_BASE           100000
#define KeyID_M4A1            (WEAPON_BASE + 0)
#define KeyID_AK47            (WEAPON_BASE + 100)
#define KeyID_AWP             (WEAPON_BASE + 200)
#define KeyID_SG552           (WEAPON_BASE + 300)

list.c

注意:只能有一个 main() 函数
如果需要调试,请打开相关的注释 #if 0 …… #endif
编写平台:Windows11 + Visual Studio 2022

谭浩强

//C include
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <windows.h>
#include <time.h>

//my include
#include "list.h"


//先看谭浩强的例子 ### 没有头结点 ### 尾插法
#if 0
struct Student {
   
    int num;
    float score;
    struct Student* next;
};

int n = 0;//记录链表中结点的个数

struct Student *create(void)//尾插法
{
   
    int aaa;
    float bbb;
    int count = sizeof(struct Student);
    struct Student* head, * p1, * p2;

    n = 0;
    head = NULL;
    p1 = p2 = (struct Student*)malloc(count);
    //scanf("%ld, %f", &p1->num, &p1->score);
    scanf_s("%ld, %f", &p1->num, &p1->score);/*引号内可以有逗号,也可以有空格*/
#if 0//谭浩强的原版代码
    while (p1->num) {
   
        n++;
        if (n == 1)
            head = p1;
        else
            p2->next = p1;
        p2 = p1;
        p1 = (struct Student*)malloc(count);
        scanf_s("%ld, %f", &p1->num, &p1->score);
    }
/*
怎么理解谭浩强的这段代码? ### 2022.12.18
尾插法 ### 没有头结点
p2 始终指向链表尾
新添加的结点用 p1 表示,并将 p1 加到链表尾
如果 scanf 输入的不是 0, 0 --- p2 的 next 指针域就是 p1
如果 scanf 输入的正是 0, 0 --- p2 的 next 指针域就是 NULL
*/
#else//我修改后的样子
    while (p1->num) {
   //终端输入 0 或者 0, 0 结束输入
        n++;//记录链表中结点的个数
        if (n == 1)
            head = p1;
        //假定让 p2 始终指向链表尾
        //一开始,head = p1; 链表中只有p1一个结点 ### 没有头结点
        //新添加的结点用 p1 表示,并将 p1 加到链表尾
        //如此一来 p2 变成了倒数第二,所以:p2->next = p1;
        //为了让 p2 再次变成尾结点,则 p2 = p1;
        //结点添加完毕,最后,让尾结点的 next 指针为 NULL,p2->next = NULL;
        scanf_s("%ld, %f", &aaa, &bbb);
        if (aaa == 0)//不是有效的新结点就退出循环
            break;
        p1 = (struct Student*)malloc(count);
        p2->next = p1;
        p2 = p1;
        //给新结点赋值
        p1->num = aaa;
        p1->score = bbb;    
    }
#endif
    p2->next = NULL;
    return head;
}

void print(struct Student *head)
{
   
    struct Student* p = head;

    printf("\nNow, these %d records are: \n", n);
    if (head != NULL) {
   
        do {
   
            printf("%ld, %5.1f\n", p->num, p->score);
            p = p->next;
        } while (p != NULL);
    }
}

int main(void)
{
   
    struct Student* head;

    head = create();
    print(head);
    return 0;
}
#endif

单链表

//单链表

//创建一个带有头结点的空链表
LinkList list_init(void)
{
   
    LinkList pHead;

    pHead = (LinkList)malloc(sizeof(NodeL));
    assert(pHead);
    memset(pHead, 0, sizeof(NodeL));
    pHead->next = NULL;
    return pHead;
}

//初始条件:链表 L 已经存在
//操作结果:在 L 的尾部添加一个新结点,其数据值为 e,链表 L 的长度加 1
Status list_append(LinkList* L, ElemType e)
{
   
    LinkList p, r;

    r = *L;
    while (r->next) {
   
        r = r->next;//将 r 变成尾指针
    }

    p = (LinkList)malloc(sizeof(NodeL));
    assert(p);
    memset(p, 0, sizeof(NodeL));
    r->next = p;
    p->next = NULL;
    p->data = e;
    return OK;
}

//初始条件:链表 L 已经存在,并且 1 <= i <= list_length(L)
//操作结果:在 L 中的第 i 个结点【之前】插入新结点,其数据值为 e,链表 L 的长度加 1
Status list_insert(LinkList *L, int i, ElemType e)// 这里:L 是一个二重指针
{
   
    int j = 1;
    LinkList p, s;
    int count = list_length(*L);
    
    if (count == 0) {
   
        return list_append(L, e);
    }

    if (i >= 1 && i <= count)
    {
   
        p = *L;
        while (p && j < i) {
   //寻找第 i-1 个结点
            p = p->next;
            ++j;
        }
        if (!p || j > i)
            return ERROR;
        s = (LinkList)malloc(sizeof(NodeL));
        assert(s);
        memset(s, 0, sizeof(NodeL));
        s->next = p->next;
        p->next = s;
        s->data = e;
        return OK;
    }
    return ERROR;
}

//初始条件:链表 L 已经存在,并且 1 <= i <= list_length(L)
//操作结果:删除 L 的第 i 个结点,用 e 返回其值,链表 L 的长度减 1
Status list_delete(LinkList *L, int i, ElemType *e)
{
   
    int j = 1;
    LinkList p, q;
    int count = list_length(*L);

    if (i >= 1 && i <= count)
    {
   
        p = *L;
        while (p->next && j < i) {
   //寻找第 i-1 个结点
            p = p->next;
            ++j;
        }
        if (!(p->next) || j > i)
            return ERROR;//第i个结点不存在就报错
        q = p->next;//q指向第i个结点
        p->next = q->next;
        *e = q->data;
        free(q);
        return OK;
    }
    return ERROR;
}

//初始条件:链表 L 已经存在
//操作结果:返回链表的长度 ### 不包括头结点
int list_length(LinkList L)
{
   
    int i = 0;
    LinkList p = L->next;
    while (p) {
   
        p = p->next;
        i++;
    }
    return i;
}

//初始条件:链表 L 已经存在,并且 1 <= i <= list_length(L)
//操作结果:用 e 返回 L 中第 i 个元素的值
Status list_get_elem(LinkList L, int i, ElemType* e)
{
   
    int j = 1;
    LinkList p = L->next;
    int count = list_length(L);

    if (i >= 1 && i <= count)
    {
   
        while (p && j < i) {
   
            p = p->next;
            ++j;
        }
        if (!p || j > i)
            return ERROR;
        *e = p->data;
        return OK;
    }
    return ERROR;
}

//初始条件:链表 L 已经存在,并且 1 <= i <= list_length(L)
//操作结果:修改 L 中第 i 个元素的值为指定的 newData
Status list_modify(LinkList *L, int i, ElemType newData)
{
   
    int j = 1;
    LinkList p = (*L)->next;
    int count = list_length(*L);

    if (i >= 1 && i <= count)
    {
   
        while (p && j < i) {
   
            p = p->next;
            ++j;
        }
        if (!p || j > i)
            return ERROR;
        p->data = newData;
        return OK;
    }
    return ERROR;
}

//创建带头结点的单链表 L,L 有 n 的结点,每个结点的值随机产生 ### 每次都在 结点1 的位置插入新结点
void list_create_head(LinkList *L, int n)//头插法
{
   
    LinkList p;
    int i;

    srand(time(0));
    *L = (LinkList)malloc(sizeof(NodeL));
    assert(*L);
    memset(*L, 0, sizeof(NodeL));
    (*L)->next = NULL;//保证尾结点的 next 指针域为空
    for (i = 0; i < n; i++) {
   
        p = (LinkList)malloc(sizeof(NodeL));
        assert(p);
        memset(p, 0, sizeof(NodeL));
        p->data = rand() % 100 + 1;
        p->next = (*L)->next;//起初,因为 L 是空链表,头结点即是尾结点,n==1 或者 i==0 时,第一个加进来的结点就是新的尾结点,L 是头结点,所以 (*L)->next == NULL,这就保证了最终的尾结点指针域是 NULL
        (*L)->next = p;
    }
}


//创建带头结点的单链表 L,L 有 n 的结点,每个结点的值随机产生 ### 每次都在链表的最后位置加上新结点
/*
假设:r 始终是链表的尾结点
在尾部添加一个新结点p后
p变成尾结点,r 倒数第二
因此:r->next = p;
为了让 r 再次变成尾结点,将 p 赋值给 r
因此:r = p;
所有结点添加完成,将尾结点的指针域指空:r->next = NULL;
*/
void list_create_tail(LinkList* L, int n)//尾插法
{
   
    LinkList p, r;
    int i;

    srand(time(0));
    *L = (LinkList)malloc(sizeof(NodeL));
    assert(*L);
    memset(*L, 0, sizeof(NodeL));
    (*L)->next = NULL;
    r = *L;
    while (r->next) {
   
        r = r->next;//r 始终是链表的尾结点
    }
    for (i = 0; i < n; i++) {
   
        p = (LinkList)malloc(sizeof(NodeL));
        assert(p);
        memset(p, 0, sizeof(NodeL));
        p->data = rand() % 100 + 1;
        r->next = p;
        r = p;
    }
    r->next = NULL;
}

//初始条件:链表 L 已经存在
//操作结果:将链表清空 ### 每次都删除结点1 ### 但仍然保留着头结点
Status list_clear(LinkList *L)
{
   
    LinkList p, q;

    p = (*L)->next;
    while (p) {
   
        q = p->next;//先保存 p->next 的值(内存地址),即结点2(如果存在的话),防止在 free(p) 时  p->next 的值丢失
        free(p);
        p = q;//再将保存的  p->next 的值赋值给 p 进行下一轮的循环判断
    }
    (*L)->next = NULL;//将头结点的后续指针域置空
    return OK;
}

//#include <windows.h>
//void Sleep(DWORD dwMilliseconds); 参数为毫秒
//访问包含头结点的链表
void list_access(LinkList L)
{
   
    int i = 0;
    LinkList p = L->next;

    while (p) {
   
        i++;
        printf("第%d个数据:%d\n", i, p->data);
        p = p->next;
        Sleep(5);//休眠5ms
    }
}

//访问不包含头结点的链表
void list_access2(LinkList L)
{
   
    int i = 0;
    LinkList p = L;

    while (p) {
   
        i++;
        printf("第%d个数据:%d\n", i, p->data);
        p = p->next;
        Sleep(5);//休眠5ms
    }
}

//初始条件:链表 L 已经存在 ### 结点 node 位于 L 中
//以 node 为出发点,向前向后访问结点
Status list_access_ext(LinkList L, NodeL* node)
{
   
    return OK;
}

//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
Status list_head_insert(LinkList* L, ElemType e)
{
   
    LinkList s;

    s = (LinkList)malloc(sizeof(NodeL));
    assert(s);
    memset(s, 0, sizeof(NodeL));
    s->next = (*L)->next;
    (*L)->next = s;
    s->data = e;
    return OK;
}

//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置插入一个新结点,其数据值为 e,链表 L 的长度加 1
//#define list_tail_insert list_append
Status list_tail_insert(LinkList* L, ElemType e)
{
   
    return list_append(L, e);
}

//初始条件:链表 L 已经存在
//操作结果:在 结点1 的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status list_head_delete(LinkList* L, ElemType* e)
{
   
    LinkList p;

    p = (*L)->next;
    if (!p) {
   
        return ERROR;
    }
    (*L)->next = p->next;
    *e = p->data;
    free(p);
    return OK;
}

//初始条件:链表 L 已经存在
//操作结果:在尾结点的位置删除一个结点,用 e 返回其值,链表 L 的长度减 1
Status list_tail_delete(LinkList* L, ElemType* e)
{
   
    LinkList p, r;
    int count = list_length(*L);

    if (count == 1) {
   
        p = (*L)->next;
        *e = p->data;
        free(p);
        (*L)->next = NULL;
        return OK;
    }
    else if (count > 1) {
   
        p = r = *L;
        while (p->next->next) {
   
            p = p->next;//p最终变成倒数第二个结点
            r = p->next;//r最终变成尾结点
        }
        p->next = NULL;
        *e = r->data;
        free(r);
        return OK;
    }
    else {
   
        return ERROR;
    }
}

//初始条件:链表 L 已经存在
//操作结果:直接删除结点 node
Status list_delete_by_node_address(LinkList* L, NodeL* node)
{
   
    LinkList prevL, curL, nextL;

    prevL = nextL = *L;
    curL = (*L)->next;
    while (curL) {
   
        if (node == curL) {
   
            nextL = curL->next;
            prevL->next = nextL;
            free(curL);
            return OK;
        }
        prevL = prevL->next;
        curL = curL->next;
    }
    return ERROR;
}

//初始条件:链表 L 已经存在
//操作结果:删除包含着数据 e 的第一个结点
Status list_delete_by_data(LinkList* L, ElemType e)
{
   
    LinkList prevL, curL, nextL;

    prevL = nextL = *L;
    curL = (*L)->next;
    while (curL) {
   
        if (e == curL->data) {
   
            nextL = curL->next;
            prevL->next = nextL;
            free(curL);
            return OK;
        }
        prevL = prevL->next;
        curL = curL->next;
    }
    return ERROR;
}

//如果链表 L 的数据不再是简单的 ElemType,而是接近真实项目中的数据,比如像 ElemType2 这样,其中包含着一个 name 数组
Status list_delete_by_name(LinkList* L, const char* name, ElemType2* e)
{
   
    return OK;
}

Status list_get_elem_by_name(LinkList L, const char* name, ElemType2* e)
{
   
    return OK;
}

//初始条件:链表 L 已经存在
//操作结果:返回第 i 个位置的结点的地址
NodeL* list_get_node(LinkList L, int i)
{
   
    int j = 0;
    int count = list_length(L);
    NodeL* p = L;

    if (!(1 <= i <= count)) {
   
        return NULL;
    }
    while (j < i)
    {
   
        j++;
        p = p->next;
    }

    return (p!=L) ? p : NULL;
}

//复制一个结点
NodeL* list_copy_node(NodeL* node)
{
   
    NodeL* p = (NodeL*)malloc(sizeof(NodeL));
    assert(p);
    memset(p, 0, sizeof(NodeL));
    p->data = node->data;
    return p;
}

//初始条件:链表 L 已经存在
//如果链表 L 的数据不再是简单的 ElemType,而是接近真实项目中的数据,比如像 ElemType2 这样,其中包含着一个 name 数组
//操作结果:返回 name 对应的第一个结点的地址
NodeL* list_get_node_by_name(LinkList L, const char* name)
{
   
    NodeL* p = L;

    return p;
}

//初始条件:链表 L 已经存在
//如果链表 L 的数据不再是简单的 ElemType,而是接近真实项目中的数据,比如像 ElemType2 这样,其中包含着一个 KeyID
//操作结果:返回 KeyID 对应的第一个结点的地址
NodeL* list_get_node_by_KeyID(LinkList L, int KeyID)
{
   
    NodeL* p = L;

    return p;
}

//初始条件:链表 L 已经存在 ### 返回 node 的位序
//如果 node 的地址与 链表 L 中任何一个结点的地址相同,则认为 node 存在于 L 中,返回值 >0; 否则返回值为 0
int list_node_location(LinkList L, NodeL* node)
{
   
    int i = 0;
    Status find = FALSE;
    LinkList p = L;

     while (p->next) {
   
        i++;
        p = p->next;
        if (p == node) {
   
            find = TRUE;
            break;
        }
    }
    return (find == TRUE) ? i : 0;
}

//初始条件:链表 L 已经存在 ### 返回 e 的位序
//如果数据 e 与链表 L 中某个结点的数据完全相同,则认为 e 存在于 L 中,返回值 >0; 否则返回值为 0
int list_elem_location(LinkList L, ElemType e)
{
   
    int i = 0;
    Status find = FALSE;
    LinkList p = L;

    while (p->next) {
   
        i++;
        p = p->next;
        if (p->data == e) {
   
            find = TRUE;
            break;
        }
    }
    return (find == TRUE) ? i : 0;
}

int list_name_location(LinkList L, const char* name)
{
   
    int i = 0;
    return i;
}

int list_KeyID_location(LinkList L, int KeyID)
{
   
    int i = 0;
    return i;
}

//初始条件:链表 L 已经存在 ### node1 和 node2 属于链表 L
//比较 node1 与 node2 的数据是否完全相同
//完全相同,返回 TRUE;否则,返回 FALSE
Status list_node_compare(LinkList L, NodeL* node1, NodeL* node2)
{
   
    int i = list_node_location(L, node1);
    int j = list_node_location(L, node2);

    if (i == 0 || j == 0) {
   
        return FALSE;
    }
    if (node1->data == node2->data) {
   
        return TRUE;
    }
    return FALSE;
}

//初始条件:链表 L 已经存在
//操作结果:将链表逆序
//链表逆序 = 遍历 + 头插入
Status list_reverse(LinkList* L)
{
   
    LinkList p, q, r;

    r = p = (*L)->next;//最开始让p指向结点1
    while (p)//始终将结点p插入在结点1的位置,p从结点1挨个遍历到尾结点 ### while 循环结束时,结点1 就变成了尾结点,r 即是尾结点
    {
   
        q = p->next;
        p->next = (*L)->next;
        (*L)->next = p;
        p = q;
    }
    r->next = NULL;
    return OK;
}

//多个结点可以包含相同的数据,因此有必要去除重复的数据
Status list_remove_duplication(LinkList* L)
{
   
    LinkList p, q;
    ElemType data1, data2;

    p = *L;
    q = p->next;
    while (p->next) {
   
        p = p->next;
        data1 = p->data;
        while (q->next) {
   
            q = q->next;
            data2 = q->data;
            if (data1 == data2) {
   
                if (list_delete_by_data(L, data1) == ERROR) {
   
                    return ERROR;
                }
                p = *L;//删除一个重复的结点后要重新设置循环条件,从第一个结点开始重新查找重复的结点
                break;
            }
        }
        q = p->next;
    }
    return OK;
}

Status list_remove_duplication_ext(LinkList* L)
{
   
    int i = 0;
    int j = 0;
    LinkList p, q;
    ElemType data1, data2;

    p = *L;
    q = p->next;
    while (p->next) {
   
        i++;
        p = p->next;
        data1 = p->data;
        while (q->next) {
   
            q = q->next;
            data2 = q->data;
            if (data1 == data2) {
   
                if (list_delete_by_data(L, data1) == ERROR) {
   
                    return ERROR;
                }
                p = *L;//删除一个重复的结点后要重新设置循环条件
                for (j = 1; j < i; j++) {
   //跳过前面 i-1 个已经确认没有重复数据的结点
                    p = p->next;
                }
                --i;//因为已经删除了一个结点,所以 i 要减一
                break;
            }
        }
        q = p->next;
    }
    return OK;
}

//初始条件:链表 L1 和 L2 已经存在
//操作结果:将两个链表直接拼接成一个
//最后销毁 L2 的头结点
LinkList list_joint(LinkList L1, LinkList L2)
{
   
    LinkList h1, h2, p, q;

    h1 = L1;
    h2 = L2;
    p = L1->next;
    q = L2->next;
    while (p->next) {
   
        p = p->next;
    }
    p->next = q;
    free(h2);
    return h1;
}

//通常,一个结点在添加进某个链表中的时候,它的前驱指针和后继指针就固定了,因此,一个结点不太可能同时存在于两个及更多的链表中
//由于 L1 中的结点和 L2 中的结点通常不可能拥有相同的内存起始地址 p
//而在 list_merge_NOK_1() 和 list_merge_NOK_2() 两个函数中都使用了 i = list_node_location(L1, p)
//因此,两个函数的 i 值将一直是 0 ### 删除重复结点的动作就不能执行
//但凡事都不绝对 ### 如果把链表的某个片段取出来放到其它链表中,再比较包含公共片段的两个链表,那么在这段公共的链表片段中就会出现 "数据、前驱指针、后继指针" 完全相同的结点了
//只有在这种特殊的情况下,list_merge_NOK_1() 和 list_merge_NOK_2() 两个函数才能起作用
//但这会有一个明显的缺点:一旦这个公共的链表片段的第一个或最后一个结点被删除,所有包含该公共片段的链表都会受到影响,甚至导致链表断裂,产生灾难
LinkList list_merge_NOK_1(LinkList L1, LinkList L2)//使用严重受限 --- 不推荐
{
   
    int n = 0;
    ElemType e = 0;
    LinkList p = L1;
    LinkList h = L1;

    while (p->next) {
   
        p = p->next;
        n = list_node_location(L2, p);
        if (n > 0) {
   
            list_delete(&L2, n, &e);//对于 L1 中的每一个结点p,如果p在 L2 中有相同的副本,就将该副本从 L2 中删除
            n = 0;
        }
    }
    if (list_length(L2) > 0) {
   
        h = list_joint(L1, L2);
    }
    else {
   
        free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间
    }
    return h;
}

LinkList list_merge_NOK_2(LinkList L1, LinkList L2)//使用严重受限 --- 不推荐
{
   
    int n = 0;
    LinkList p = L2;
    LinkList h = L1;

    while (p->next) {
   
        p = p->next;
        n = list_node_location(L1, p);
        if (n > 0) {
   
            list_delete_by_node_address(&L2, p);//对于 L2 中的每一个结点p,如果p在 L1 中有相同的副本,就将该副本从 L2 中删除
            p = L2;//因为是对 L2 的每一个结点在做循环,所以当 L2 被删除一个结点后,必须更新循环条件
            n = 0;
        }
    }
    if (list_length(L2) > 0) {
   
        h = list_joint(L1, L2);
    }
    else {
   
        free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间
    }
    return h;
}

//list_merge() & list_merge2()
//初始条件:链表 L1 和 L2 已经存在
//操作结果:将 L2 中的结点依次添加到 L1 中,前提:即将添加的结点的数据部分与 L1 中任何一个结点的数据都不同
//最后销毁 L2 的头结点
//如果 L2 的所有结点所包含的数据在 L1 中都存在,则完全销毁 L2

//修改 list_merge_NOK_1() ,不使用 list_node_location(),换成 list_elem_location() ### 应该就可以了
LinkList list_merge(LinkList L1, LinkList L2)
{
   
    int n = 0;
    ElemType e = 0;
    LinkList p = L1;
    LinkList h = L1;

    while (p->next) {
   
        p = p->next;
        n = list_elem_location(L2, p->data);
        if (n > 0) {
   
            list_delete(&L2, n, &e);//对于 L1 中的每一个结点p,如果p在 L2 中有相同的副本,就将该副本从 L2 中删除
            n = 0;
        }
    }
    if (list_length(L2) > 0) {
   
        h = list_joint(L1, L2);
    }
    else {
   
        free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间
    }
    return h;
}

//修改 list_merge_NOK_2(),不使用 list_node_location(),换成 list_elem_location()
//不使用 list_delete_by_node_address(),换成 list_delete_by_data() ### 应该就可以了
LinkList list_merge2(LinkList L1, LinkList L2)
{
   
    int n = 0;
    int i = 0;
    int j = 0;
    ElemType e = 0;
    LinkList p = L2;
    LinkList h = L1;

    while (p->next) {
   
        i++;
        p = p->next;
        n = list_elem_location(L1, p->data);
        if (n > 0) {
   
            list_delete_by_data(&L2, p->data);//对于 L2 中的每一个结点p,如果p在 L1 中有相同的副本,就将该副本从 L2 中删除
            p = L2;//因为是对 L2 的每一个结点在做循环,所以当 L2 被删除一个结点后,必须更新循环条件
            for (j = 1; j < i; j++) {
   //跳过前面 i-1 个已经确认没有重复数据的结点
                p = p->next;
            }
            --i;//因为已经删除了一个结点,所以 i 要减一
            n = 0;
        }
    }
    if (list_length(L2) > 0) {
   
        h = list_joint(L1, L2);
    }
    else {
   
        free(L2);//L2 为空表,仅剩头结点,释放头结点的内存空间
    }
    return h;
}

//初始条件:链表 L1 和 L2 已经存在
//将 L1 和 L2 拼接在一起,保证每一个结点的数据部分都是独一无二的
LinkList list_merge_ext(LinkList L1, LinkList L2)
{
   
    LinkList h = list_joint(L1, L2);
    return (list_remove_duplication_ext(&h) == OK) ? h : NULL;
}

//初始条件:链表 L 已经存在 ### 截取 L 的一个连续的子片段
// 1 <= i <= list_length(L) && 1 <= j <= list_length(L)
//操作结果:对链表进行切片,返回包含 结点i 和 结点j 以及 它们中间的所有结点组成的链表片段 ### 没有头结点
//注意:如果你截取链表 L 的子片段 L0 是为了把它拼接到另外一个链表中去 ### 这将会产生极大的隐患
//当链表 L 被清空时,子片段 L0 也就被清空了,这将导致与 L0 连接的链表在 L0 两端的连接处断开,产生灾难
//一定要小心使用 list_slice() !!!
//list_slice() 不提供给外部使用!!!
static LinkList list_slice(LinkList L, int i, int j)
{
   
    int k = 0;
    int len = 0;
    int start = 0;
    int count = 0;
    LinkList p = L;
    LinkList r = L;

    count = list_length(L);
    assert(1 <= i <= count);
    assert(1 <= j <= count);
    start = (i <= j) ? i : j;
    len =
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值