c++ · 2024年9月6日 0

双链表

链表 –
单链表 –
内存空间
操作函数
1.头插函数
新的结点插入到头结点和第一个有效结点之间
void list_add_head(list_t* list, int data){}
2.尾插函数
新的结点插入到最后一个有效结点和尾结点之间
void list_add_tail(list_t* list, int data){}

head—->10—>20—->30—->tail


双链表:

问题 – 可以通过tail找到30数据所在节点 – 不可以
通过前一个节点找到后一个节点
无法通过后一个节点找到前一个节点

为了克服这个问题 – 在设计链表的时候, 可以在每个节点中再设置一个指向前面结点的指针
每个结点中都包含两个指针
一个指向后面的元素
一个指向前面的元素
双向链表

声明描述一个节点的结构体
struct node{
int data;
struct node* next;//指向下一个节点的首地址
struct node* prev;//指向上一个节点的首地址
};
声明描述一个双链表的结构体
struct list{
struct node head;
struct node tail;
};

mkdir list2
vim list.h

#ifndef _LIST_H
#define _LIST_H
//包含公共的头文件
#include <stdio.h>
#include <stdlib.h>
//声明描述结点的结构体
typedef struct node{
    int data;
    struct node* next;//保存下一个结点首地址
    struct node* prev;//保存上一个结点首地址
}node_t;
//声明描述双链表的结构体
typedef struct list{
    struct node head;
    struct node tail;
}list_t;
extern void list_init(list_t* list);//初始化函数
extern int list_empty(list_t* list);//判断链表是否为空的函数

#endif


vim list.c

#include "list.h"

//初始化函数
void list_init(list_t* list){
    //初始化头结点
    list->head.next = &list->tail;
    list->head.prev = NULL;
    list->head.data = 0;

    //初始化尾结点
    list->tail.next = NULL;
    list->tail.prev = &list->head;
    list->tail.data = 0;
}
//判断空函数
int list_empty(list_t* list){
   //如果链表中只有头结点和尾结点 
   return list->head.next == &list->tail;//满足, 空; 不满足, 非空
}
//判断结点个数函数
int list_size(list_t* list){
    int count = 0;//记录结点个数
    for(node_t* pnode = &list->head; pnode != &list->tail; pnode = pnode->next){
        //制造三个游标
        node_t* pfirst = pnode;
        node_t* pmid = pfirst->next;
        node_t* plast = pmid->next;
        if(pmid != &list->tail)
            count++;
    }
    return count;
}
//定义构造新节点的函数
static node_t* create_node(int data){
    node_t* pnew = malloc(sizeof(node_t));
    pnew->data = data;
    pnew->next = NULL;
    pnew->prev = NULL;
    return pnew;
}
static void insert_node(node_t* pfirst, node_t* pmid, node_t* pnew){
    pfirst->next = pnew;
    pnew->prev = pfirst;

    pnew->next = pmid;
    pmid->prev = pnew;
}
//按照顺序插入函数
void list_add(list_t* list, int data){
    node_t* pnew = create_node(data);
    //head<------>10<---------->20<----->30<---->tail
    //                pnew(15)
    //          pfrist         pmid
    for(node_t* pnode = &list->head; pnode != &list->tail; pnode = pnode->next){
        //制造三个游标
        node_t* pfirst = pnode;
        node_t* pmid = pfirst->next;
        node_t* plast = pmid->next;
        if(pmid->data >= pnew->data || pmid == &list->tail){
            insert_node(pfirst, pmid, pnew);
            break;
        }
    }
}

//头插函数
void list_add_head(list_t* list, int data){
    node_t* pnew = create_node(data);
    //head<------------>10<---->20<----->30<---->tail
    //         pnew
    //pfirst          pmid     plast
    //制造游标
    node_t* pfirst = &list->head;
    node_t* pmid = pfirst->next;
    node_t* plast = pmid->next;
    //将pnew结点插入到pfirst和pmid之间
    insert_node(pfirst, pmid, pnew);
}
//尾插函数
void list_add_tail(list_t* list, int data){
    node_t* pnew = create_node(data);
    //head<---->10<---->20<----->30<--------------->tail
    //                                   pnew
    //                         pfirst               pmid   plast
    //制造游标
    node_t* pfirst = list->tail.prev;
    node_t* pmid = pfirst->next;
    node_t* plast = pmid->next;
    //将pnew插入到pfirst和pmid之间
    insert_node(pfirst, pmid, pnew);
}
//定义删除pmid指向的结点
static void del_node(node_t* pfirst, node_t* pmid, node_t* plast){
    pfirst->next = plast;
    plast->prev = pfirst;
    free(pmid);
}
//删除指定的结点
void list_del(list_t* list, int data){
    //head<------>10<---------->20<----->30<---->tail
    for(node_t* pnode = &list->head; pnode != &list->tail; pnode = pnode->next){
        //制造三个游标
        node_t* pfirst = pnode;
        node_t* pmid = pfirst->next;
        node_t* plast = pmid->next;
        if(pmid->data == data && pmid != &list->tail){
            del_node(pfirst, pmid, plast);
        }
    } 
}
//删除第一个有效结点
void list_del_head(list_t* list){
    //判断链表是否为空
    if(list_empty(list)){
        printf("链表为空, 无法删除\n");
        return;
    }
    //head<------>10<---------->20<----->30<---->tail
    //pfirst     pmid         plast
    //定义游标
    node_t* pfirst = &list->head;
    node_t* pmid = pfirst->next;
    node_t* plast = pmid->next;
    //删除pmid指向的结点
    del_node(pfirst, pmid, plast);
    
}
//删除最后一个有效结点
//让pmid指向要删除的结点 - 指向最后一个有效结点
void list_del_tail(list_t* list){
    //判断链表是否为空
    if(list_empty(list)){
        printf("链表为空, 无法删除\n");
        return;
    }
    //head<------>10<------>20<----->30<---->tail
    //                     pfirst   pmid     plast
    //定义游标
    node_t* plast = &list->tail;//plast指向了tail结点
    node_t* pmid = plast->prev;//plast的上一个结点 - 最后一个有效结点 - pmid 
    node_t* pfirst = pmid->prev;//指向pmid的上一个结点
    //删除pmid指向的结点 
    del_node(pfirst, pmid, plast);
}

//根据结点编号来获取数据的函数
//数组:
// 10 20 30 40  数据
// 0  1  2  3   数组下标
//双链表:
// 10 20 30 40  双链表数据 
// 0  1  2  3   结点编号 
// 根据结点编号来获取编号对应的数据
int list_get(list_t* list, int index){
    int count = 0;//表示循环的次数
    // head    10     20     30     40   tail 双链表数据 
    //         0      1      2      3         结点编号 
    //              pfirst  pmid   plast
    //tips:根据循环的次数, 判断pmid指向的内容
    for(node_t* pnode = &list->head; pnode != &list->tail; pnode = pnode->next){
        //制造三个游标
        node_t* pfirst = pnode;
        node_t* pmid = pfirst->next;
        node_t* plast = pmid->next;
        if(count == index && pmid != &list->tail)
            return pmid->data;
        count++;
    }
}

//清除链表中的所有内容 
void list_deinit(list_t* list){
   //head----10----20-----30----tail
   //head-----tail
   //head----tail
   //pfirst  pmid  plast
   while(list->head.next != &list->tail){
        node_t* pfirst = &list->head;
        node_t* pmid = pfirst->next;
        node_t* plast = pmid->next;
        del_node(pfirst, pmid, plast);//删除pmid指向的结点
   }
}


vim main.c

//双链表测试
#include "list.h"

int main(void){
    list_t list;
    list_init(&list);//初始化 
    
    //各种插入
    list_add_head(&list, 100);
    list_add_head(&list, 30);
    list_add_tail(&list, 70);
    list_add_tail(&list, 50);
    list_add(&list, 80);
    list_add(&list, 20);
    list_add(&list, 40);
    list_add(&list, 90);
    list_add(&list, 60);
    list_add(&list, 10);
    list_add(&list, 110);

    //获取链表内容
    int size = list_size(&list);
    for(int i = 0; i < size; i++)
        printf("%d ", list_get(&list, i));
    printf("\n");

    //各种删除结点
    list_del(&list, 90);
    list_del_head(&list);
    list_del_tail(&list);

    //获取链表内容
    size = list_size(&list);
    for(int i = 0; i < size; i++)
        printf("%d ", list_get(&list, i));
    printf("\n");


    list_deinit(&list);//释放
    return 0;
}

pmid指针
循环的次数 – 有效数据的个数 + 1
循环 – pmid != &list->tail
循环次数 == 有效数据个数

解决列表里数据动态的新增和删除

typedef struct birth{
int year;
int month;
int day;
}birth_t;

typedef struct info{
int num;
char name[32];
int num2;
char sex;
birth_t birth;
char add[40];
}info_t;

typedef struct node{
info_t info;
struct node* next;
struct node* prev;
}node_t;