6.双链表

数据结构之双链表

        双链表涉及的两个结构体:

        描述节点信息的结构体:

        struct node {

                int data; //节点数据

                struct node *next; //保存下一个节点的首地址

                struct node *prev; //保存上一个节点的首地址

        };

        描述整个双链表的结构体

        struct list {

               struct node head; //头节点

               struct node tail; //尾结点

        };

        这里定义的是变量,哪怕是嵌套的变量,头节点尾节点也会自动分配好内存,不需要再去malloc,但如果是指针,则需要进行malloc

         vim list.h

//list.h:双链表声明
#ifndef __LIST_H
#define __LIST_H
#include <stdio.h>
#include <stdlib.h>
//声明描述节点信息的结构体
typedef struct node {
    int data;//数据
    struct node *prev;//保存上一个节点的首地址
    struct node *next;//保存下一个节点的首地址
}node_t;
//声明链表的结构体
typedef struct list {
    struct node head;//头结点
    struct node tail;//尾节点
}list_t;
extern void list_init(list_t *list);//初始化
extern void list_deinit(list_t *list);//释放所有节点
extern int list_empty(list_t *list);//判断链表是否为空
extern int list_size(list_t *list); //获取数据个数
extern void list_add(list_t *list, int data);//顺序插
extern void list_add_first(list_t *list, int data);//前插入
extern void list_add_last(list_t *list, int data);//后插
extern void list_del(list_t *list, int data);//删除指定节点
extern void list_del_first(list_t *list);//只删除第一个有效节点
extern void list_del_last(list_t *list);//只删除最后一个有效节点
extern int list_get(list_t *list, int index);//根据节点编号获取节点数据
#endif

        vim list.c

//list.c:双链表定义
#include "list.h"
//定义初始化函数
void list_init(list_t *list) {
    //头指向尾
    list->head.next = &list->tail;
    //尾指向头
    list->tail.prev = &list->head;
    //初始化其他成员
    list->head.data = 0;
    list->tail.data = 0;
    list->head.prev = NULL;
    list->tail.next = NULL;
}
//定义判断链表空的函数
int list_empty(list_t *list) {
    return list->head.next == &list->tail;//空返回1否则返回0
}
//定义获取有效数据个数的函数
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) //pmid不是尾节点那说明它指向的就是有效节点
            count++; //更新计数
    }
    return count;
}
//定义创建新节点函数
static node_t *create_node(int data) {
    node_t *pnew = (node_t *)malloc(sizeof(node_t));
    pnew->data = data;
    pnew->prev = NULL;
    pnew->next = NULL;
    return pnew; //返回新节点首地址
}
//定义插入新点函数,新节点插入到pfirst和pmid中间即可
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) {
    //1.创建新节点
    node_t *pnew = create_node(data);
    //2.遍历找到要插入的位置,将新节点插入到pfirst和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_first(list_t *list, int data) {
    //1.创建新节点
    node_t *pnew = create_node(data);
    //2.造游标
    node_t *pfirst = &list->head; //pfirst指向头结点
    node_t *pmid = pfirst->next; //pmid指向第一个节点
    node_t *plast = pmid->next; //plast指向后一个节点
    //3.插入新节点到pfirst和pmid中间
    insert_node(pfirst, pmid, pnew);
}
//定义后插函数
void list_add_last(list_t *list, int data) {
    //1.创建新节点
    node_t *pnew = create_node(data);
    //2.造游标
    node_t *pfirst = list->tail.prev; //pfirst指向原先的最后一个节点
    node_t *pmid = pfirst->next; //pmid指向尾节点
    node_t *plast = pmid->next; //NULL
    //3.插入新节点到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) {
   //1.遍历找到要删除的节点,让pmid指向要删除的节点,最后连接pfirst和plast即可
    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_first(list_t *list) {
    //1.判断链表是否为空
    if(list_empty(list)) {
        printf("链表空了.\n");
        return;
    }
    //2.定义游标
    node_t *pfirst = &list->head;
    node_t *pmid = pfirst->next; //pmid指向要删除的第一个节点
    node_t *plast = pmid->next; 
    //3.删除pmid指向的第一个节点并且连接pfirst和plast
    del_node(pfirst, pmid, plast);
}
//定义只删除最后一个节点的函数
void list_del_last(list_t *list) {
    //1.判断链表是否为空
    if(list_empty(list)) {
        printf("链表空了.\n");
        return;
    }
    //2.定义游标
    node_t *plast = &list->tail; //plast指向尾节点
    node_t *pmid = plast->prev; //pmid指向要删除的最后一个节点
    node_t *pfirst = pmid->prev; //pfirst指向前一个节点
    //3.删除pmid指向的第一个节点并且连接pfirst和plast
    del_node(pfirst, pmid, plast);
}
//定义根据节点编号获取节点数据
int list_get(list_t *list, int index) {
    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(index == count && pmid != &list->tail) 
            return pmid->data;
        count++;
    }
}
//定义释放所节点的函数
void list_deinit(list_t *list) {
    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);
    }
}

        注意第57行的 break;如果在这里不用break退出循环,会造成死循环,pnode会永远指向插入的节点,不断插入不会结束,由于安全等级问题会产生难以预测的问题。

        vim main.c

//main.c:测试
#include "list.h"
int main(void) {
    list_t list; //定义链表
    list_init(&list);//初始化
    list_add_first(&list, 50);
    list_add_first(&list, 20);
    //*(int *)0 = 0;
    list_add_last(&list, 70);
    list_add_last(&list, 100);//20 50 70 100
    list_add(&list, 80);
    list_add(&list, 30);
    list_add(&list, 40);
    list_add(&list, 60);
    list_add(&list, 90);
    list_add(&list, 10);
    int size = list_size(&list);//获取节点个数
    for(int i = 0; i < size; i++)
        printf("%d ", list_get(&list, i));
    printf("\n");
    list_del_first(&list);
    list_del_last(&list);
    list_del(&list, 50);
    size = list_size(&list);//获取节点个数
    for(int i = 0; i < size; i++)
        printf("%d ", list_get(&list, i));
    printf("\n");
    list_deinit(&list);//清除
    return 0;
}

        vim Makefile

BIN=list
OBJ=main.o list.o
CC=gcc

$(BIN):$(OBJ)
	$(CC) -o $(BIN) $(OBJ)

%.o:%.c
	$(CC) -c -o $@ $<

clean:
	rm $(BIN) $(OBJ)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值