【数据结构】

双向链表

双向链表的特点

单向链表只有向后指的指针,如果向要获取前一个节点,需要从头遍历(单链表)或者通过当前位置循环遍历一圈(单向循环链表)。这样的话时间复杂度就会比较高。所以就有人提出一个节点包含两个指针,一个指针指向前一个节点,一个指针指向后一个节点,这种链表就是双向链表。

双向链表的结构

双向链表的节点格式

在这里插入图片描述
next(rear):指向后一个节点
pre(front):指向前一个节点
data:就是它的数据域

双向链表的结构体定义
#define datatype int
typedef struct node{
    datatype data;
    struct node *pre,*next;
}DPlist_t;
双向链表的结构

在这里插入图片描述

双向链表的操作

双向链表的创建

在这里插入图片描述

DPlist_t *DPListCreate(void)
{
    DPlist_t *h;
    h = (DPlist_t *)malloc(sizeof(*h));
    if(h == NULL){
        printf("malloc head node memory error\n");
        return NULL;
    }
    h->data = (datatype)0;
    h->pre = NULL;
    h->next = NULL;

    return h;
}
双向链表头插法
情况1:链表为空

在这里插入图片描述

情况2:链表不为空

在这里插入图片描述
双向链表节点插入(两上两下,从后往前操作)


int DPListInsertHead(DPlist_t* h, datatype data)
{
    DPlist_t* tmp;
    // 1.分配tmp节点,将data存放进去
    tmp = (DPlist_t*)malloc(sizeof(*tmp));
    if (tmp == NULL) {
        printf("mallocnode memory error\n");
        return -1;
    }
    tmp->data = data;

    // 2.将tmp节点进行头插
    tmp->next = h->next;
    tmp->pre = h;
    if(h->next != NULL)
       h->next->pre = tmp; 
    h->next = tmp;

    return 0;
}
双向链表遍历
void DPListShow(DPlist_t* h)
{
    while (h->next) {
        printf("-%d", h->next->data);
        h = h->next;
    }
    printf("-\n");
    while (h && h->pre) {
        printf("-%d", h->data);
        h = h->pre;
    }
    printf("-\n");
}
双向链表的尾插法

在这里插入图片描述

int DPListInsertTail(DPlist_t* h, datatype data)
{
    DPlist_t* tmp;
    // 1.分配tmp节点,将data存放进去
    tmp = (DPlist_t*)malloc(sizeof(*tmp));
    if (tmp == NULL) {
        printf("mallocnode memory error\n");
        return -1;
    }
    tmp->data = data;
    // 2.让h走到双向链表的尾节点
    while (h->next) {
        h = h->next;
    }

    // 3.尾插法
    tmp->next = h->next;
    tmp->pre = h;
    h->next = tmp;

    return 0;
}
双向链表的位置插入

在这里插入图片描述

int DPListInsertPos(DPlist_t* h, int pos, datatype data)
{
    DPlist_t* tmp;

    // 1.判断位置是否合法
    if (pos < 0) {
        printf("插入的位置不正确\n");
        return -1;
    }

    // 2通过循环找到插入的位置
    while (h) {
        if (pos > 0) {
            pos--;
            h = h->next;
        } else {
            // 3.分配tmp节点,将data存放进去
            tmp = (DPlist_t*)malloc(sizeof(*tmp));
            if (tmp == NULL) {
                printf("mallocnode memory error\n");
                return -1;
            }
            tmp->data = data;

            // 4.将tmp节点插入
            tmp->next = h->next;
            tmp->pre = h;
            if (h->next != NULL)
                h->next->pre = tmp;
            h->next = tmp;

            return 0;
        }
    }
    printf("插入的位置不正确\n");
    return -1;
}
双链表的头删法
情况1:只有一个节点

在这里插入图片描述

情况2:有多个节点

在这里插入图片描述
操作流程:

步骤1:先判断双向链表是否是空
if(DPlistIsEmpty(h)){
   return -1;
}

步骤2:让tmp记录h的下一个节点位置
tmp = h->next

步骤3:判断tmp的next是否是空
if(tmp->next != NULL){
  tmp->next->pre = h;
}

步骤4:让h的next指向tmp的next
h->next = tmp->next;

步骤5:释放tmp
free(tmp);
tmp=NULL;
int DPListIsEmpty(DPlist_t* h)
{
    return h->next == NULL ? 1 : 0;
}
datatype DPListDeleteHead(DPlist_t* h)
{
    DPlist_t* tmp;
    datatype data;
    // 1.判断链表是否是空
    if (DPListIsEmpty(h)) {
        printf("链表是空,头删失败\n");
        return (datatype)-1;
    }
    // 2.让tmp记录h的下一个节点位置
    tmp = h->next;

    // 3.判断tmp的next是否是空
    if (tmp->next != NULL) {
        tmp->next->pre = h;
    }
    // 4.让h的next指向tmp的next
    h->next = tmp->next;
    //5.释放tmp
    data = tmp->data;
    if(tmp != NULL){
        free(tmp);
        tmp = NULL;
    }

    return data;
}
位置删除
情况1:只有一个节点

在这里插入图片描述

情况2:有多个节点

在这里插入图片描述
删除的思路:

步骤1:判断双向链表是否是空,如果是空,退出
if(DPlistIsEmpty(h)){
    return -1;
}

步骤2通过循环找到删除的位置
while(h->next){
 if(pos>0){
  pos--;
  h = h->next;
 }else{
  //已经找到位置了
 }
}

步骤3:让tmp指向h->next位置
 tmp= h->next;

步骤4:开始删除
 if(tmp->next != NULL){
  tmp->next->pre = h;
 }
 
 h->next = tmp->next;
 
 free(tmp);
 tmp=NULL;
datatype DPListDeletePos(DPlist_t* h, int pos)
{
    DPlist_t* tmp;
    datatype data;
    // 1.判断pos是否合法
    if (pos < 0) {
        printf("位置不合法,无法删除\n");
        return (datatype)-1;
    }
    // 2.判断链表是否是空
    if (DPListIsEmpty(h)) {
        printf("双向链表是空,删除失败\n");
        return (datatype)-1;
    }
    // 3.通过循环找到删除的位置
    while (h->next) {
        if (pos > 0) {
            pos--;
            h = h->next;
        } else {
            // 4.让tmp指向h的后一个节点
            tmp = h->next;

            // 5.判断tmp是否是最后一个节点
            if (tmp->next != NULL) {
                tmp->next->pre = h;
            }
            // 6.让h的next指针指向tmp的next
            h->next = tmp->next;
            // 7.释放tmp内存
            data = tmp->data;
            if (tmp != NULL) {
                free(tmp);
                tmp = NULL;
            }
            return data;
        }
    }
    printf("位置不合法,无法删除\n");
    return (datatype)-1;
}
位置查询
datatype DPListCheckDataByPos(DPlist_t* h, int pos)
{
    datatype data;
    // 1.判断pos是否合法
    if (pos < 0) {
        printf("位置不合法,无法查询\n");
        return (datatype)-1;
    }

    // 2.通过循环找到查询的位置
    while (h->next) {
        if (pos > 0) {
            pos--;
            h = h->next;
        } else {
            data = h->next->data;
            return data;
        }
    }
    printf("位置不合法,无法查询\n");
    return (datatype)-1;
}
位置更新
int DPListUpdateDataByPos(DPlist_t *h,int pos,datatype data)
{
    // 1.判断pos是否合法
    if (pos < 0) {
        printf("位置不合法,无法更新\n");
        return -1;
    }

    // 2.通过循环找到更新的位置
    while (h->next) {
        if (pos > 0) {
            pos--;
            h = h->next;
        } else {
            h->next->data = data;
            return 0;
        }
    }
    printf("位置不合法,无法更新\n");
    return -1;
}

双向链表整体代码

DPlist.h

#ifndef __DPLIST_H__
#define __DPLIST_H__

#include <stdio.h>
#include <stdlib.h>

#define datatype int
typedef struct node{
    datatype data;
    struct node *pre,*next;
}DPlist_t;

DPlist_t *DPListCreate(void);
int DPListInsertHead(DPlist_t *h,datatype data);
int DPListInsertTail(DPlist_t *h,datatype data);
int DPListInsertPos(DPlist_t *h,int pos,datatype data);
void DPListShow(DPlist_t *h);
datatype DPListDeleteHead(DPlist_t *h);
datatype DPListDeletePos(DPlist_t *h,int pos);
datatype DPListCheckDataByPos(DPlist_t *h,int pos);
int DPListUpdateDataByPos(DPlist_t *h,int pos,datatype data);
#endif

DPlist.c

#include "DPlist.h"

DPlist_t* DPListCreate(void)
{
    DPlist_t* h;
    h = (DPlist_t*)malloc(sizeof(*h));
    if (h == NULL) {
        printf("malloc head node memory error\n");
        return NULL;
    }

    h->data = (datatype)0;
    h->pre = NULL;
    h->next = NULL;

    return h;
}

int DPListInsertHead(DPlist_t* h, datatype data)
{
    DPlist_t* tmp;
    // 1.分配tmp节点,将data存放进去
    tmp = (DPlist_t*)malloc(sizeof(*tmp));
    if (tmp == NULL) {
        printf("mallocnode memory error\n");
        return -1;
    }
    tmp->data = data;

    // 2.将tmp节点进行头插
    tmp->next = h->next;
    tmp->pre = h;
    if (h->next != NULL)
        h->next->pre = tmp;
    h->next = tmp;

    return 0;
}

void DPListShow(DPlist_t* h)
{
    while (h->next) {
        printf("-%d", h->next->data);
        h = h->next;
    }
    printf("-\n");
    while (h && h->pre) {
        printf("-%d", h->data);
        h = h->pre;
    }
    printf("-\n");
    puts("----------------------------------------");
}

int DPListInsertTail(DPlist_t* h, datatype data)
{
    DPlist_t* tmp;
    // 1.分配tmp节点,将data存放进去
    tmp = (DPlist_t*)malloc(sizeof(*tmp));
    if (tmp == NULL) {
        printf("mallocnode memory error\n");
        return -1;
    }
    tmp->data = data;
    // 2.让h走到双向链表的尾节点
    while (h->next) {
        h = h->next;
    }

    // 3.尾插法
    tmp->next = h->next;
    tmp->pre = h;
    h->next = tmp;

    return 0;
}

int DPListInsertPos(DPlist_t* h, int pos, datatype data)
{
    DPlist_t* tmp;

    // 1.判断位置是否合法
    if (pos < 0) {
        printf("插入的位置不正确\n");
        return -1;
    }

    // 2通过循环找到插入的位置
    while (h) {
        if (pos > 0) {
            pos--;
            h = h->next;
        } else {
            // 3.分配tmp节点,将data存放进去
            tmp = (DPlist_t*)malloc(sizeof(*tmp));
            if (tmp == NULL) {
                printf("mallocnode memory error\n");
                return -1;
            }
            tmp->data = data;

            // 4.将tmp节点插入
            tmp->next = h->next;
            tmp->pre = h;
            if (h->next != NULL)
                h->next->pre = tmp;
            h->next = tmp;

            return 0;
        }
    }
    printf("插入的位置不正确\n");
    return -1;
}
int DPListIsEmpty(DPlist_t* h)
{
    return h->next == NULL ? 1 : 0;
}
datatype DPListDeleteHead(DPlist_t* h)
{
    DPlist_t* tmp;
    datatype data;
    // 1.判断链表是否是空
    if (DPListIsEmpty(h)) {
        printf("链表是空,头删失败\n");
        return (datatype)-1;
    }
    // 2.让tmp记录h的下一个节点位置
    tmp = h->next;

    // 3.判断tmp的next是否是空
    if (tmp->next != NULL) {
        tmp->next->pre = h;
    }
    // 4.让h的next指向tmp的next
    h->next = tmp->next;
    // 5.释放tmp
    data = tmp->data;
    if (tmp != NULL) {
        free(tmp);
        tmp = NULL;
    }

    return data;
}
datatype DPListDeletePos(DPlist_t* h, int pos)
{
    DPlist_t* tmp;
    datatype data;
    // 1.判断pos是否合法
    if (pos < 0) {
        printf("位置不合法,无法删除\n");
        return (datatype)-1;
    }
    // 2.判断链表是否是空
    if (DPListIsEmpty(h)) {
        printf("双向链表是空,删除失败\n");
        return (datatype)-1;
    }
    // 3.通过循环找到删除的位置
    while (h->next) {
        if (pos > 0) {
            pos--;
            h = h->next;
        } else {
            // 4.让tmp指向h的后一个节点
            tmp = h->next;

            // 5.判断tmp是否是最后一个节点
            if (tmp->next != NULL) {
                tmp->next->pre = h;
            }
            // 6.让h的next指针指向tmp的next
            h->next = tmp->next;
            // 7.释放tmp内存
            data = tmp->data;
            if (tmp != NULL) {
                free(tmp);
                tmp = NULL;
            }

            return data;
        }
    }
    printf("位置不合法,无法删除\n");
    return (datatype)-1;
}

datatype DPListCheckDataByPos(DPlist_t* h, int pos)
{
    datatype data;
    // 1.判断pos是否合法
    if (pos < 0) {
        printf("位置不合法,无法查询\n");
        return (datatype)-1;
    }

    // 2.通过循环找到查询的位置
    while (h->next) {
        if (pos > 0) {
            pos--;
            h = h->next;
        } else {
            data = h->next->data;
            return data;
        }
    }
    printf("位置不合法,无法查询\n");
    return (datatype)-1;
}

int DPListUpdateDataByPos(DPlist_t *h,int pos,datatype data)
{
    // 1.判断pos是否合法
    if (pos < 0) {
        printf("位置不合法,无法更新\n");
        return -1;
    }

    // 2.通过循环找到查询的位置
    while (h->next) {
        if (pos > 0) {
            pos--;
            h = h->next;
        } else {
            h->next->data = data;
            return 0;
        }
    }
    printf("位置不合法,无法更新\n");
    return -1;
}

main.c

#include "DPlist.h"

int main(int argc, const char* argv[])
{
    DPlist_t* h;

    h = DPListCreate();
    if (h == NULL) {
        printf("DP list create error\n");
        return -1;
    }

    DPListInsertHead(h, 40);
    DPListInsertHead(h, 30);
    DPListInsertHead(h, 20);
    DPListInsertHead(h, 10);
    DPListShow(h);
    DPListInsertTail(h, 50);
    DPListShow(h);

    DPListInsertPos(h, 0, 222);
    DPListShow(h);

    // printf("delete head = %d\n",DPListDeleteHead(h));
    // printf("delete head = %d\n",DPListDeleteHead(h));
    // printf("delete head = %d\n",DPListDeleteHead(h));
    // printf("delete head = %d\n",DPListDeleteHead(h));
    // DPListShow(h);
    // printf("delete pos = %d\n", DPListDeletePos(h, 0));
    // printf("delete pos = %d\n", DPListDeletePos(h, 4));
    // printf("delete pos = %d\n", DPListDeletePos(h, 0));
    // printf("delete pos = %d\n", DPListDeletePos(h, 0));
    // printf("delete pos = %d\n", DPListDeletePos(h, 0));
    // printf("delete pos = %d\n", DPListDeletePos(h, 0));
    // printf("delete pos = %d\n", DPListDeletePos(h, 0));
    // DPListShow(h);

    printf("Check pos = %d\n", DPListCheckDataByPos(h, 3));
    DPListUpdateDataByPos(h,3,7777);

    printf("Check pos = %d\n", DPListCheckDataByPos(h, 3));
    DPListShow(h);
    return 0;
}

双向循环链表

双向循环链表的特点

双向循环链表和双向链表的结构都是一样的,只不过双向循环链表的尾结点不指向NULL,而是指向了头节点。
双向循环链表的头节点也不指向NULL,而是指向尾节点。

双向循环链表的结构

在这里插入图片描述

#define datatype int
typedef struct node{
    datatype data;
    struct node *pre,*next;
}DPloop_t;

双向循环链表的操作

双向循环链表的创建

在这里插入图片描述

DPloop_t* DPLoopCreate(void)
{
    DPloop_t* h;

    h = (DPloop_t*)malloc(sizeof(*h));
    if (h == NULL) {
        printf("malloc DP head node error\n");
        return NULL;
    }
    h->data = (datatype)0;
    h->next = h;
    h->pre = h;

    return h;
}
双向循环链表的头插法

在这里插入图片描述
在这里插入图片描述

int DPLoopInsertHead(DPloop_t* h, datatype data)
{
    DPloop_t* tmp;
    // 1.分配节点的内存,并将data存放进去
    tmp = (DPloop_t*)malloc(sizeof(*tmp));
    if (tmp == NULL) {
        printf("malloc node memory error\n");
        return -1;
    }
    tmp->data = data;
    // 2.进行头插法
    tmp->next = h->next;
    tmp->pre = h;
    h->next->pre = tmp;
    h->next = tmp;
    return 0;
}
双向循环链表的头删法

在这里插入图片描述
头删的思想:

步骤1:双向循环链表的判空
 if(DPLoopIsEmpty(h)){
  return -1;
 }
步骤2:让tmp指向h的下一个节点
 tmp = h->next;
步骤3:让tmp的下一个节点的pre指向h
 tmp->next->pre = h;
步骤4:让h的next指向tmp的下一个节点
 h->next = tmp->next
步骤5:释放tmp
 free(tmp);
 tmp = NULL;
datatype DPLoopDeleteHead(DPloop_t* h)
{
    datatype data;
    DPloop_t* tmp;
    // 1.判空
    if (DPLoopIsEmpty(h)) {
        printf("双向循环链表是空,头删失败\n");
        return (datatype)-1;
    }
    // 2.让tmp指向h的下一个节点
    tmp = h->next;

    // 3.让tmp的下一个节点的pre指向h
    tmp->next->pre = h;
    // 4.让h的next指向tmp的下一个节点
    h->next = tmp->next;
    // 5.释放tmp
    data = tmp->data;
    if (tmp != NULL) {
        free(tmp);
        tmp = NULL;
    }

    return data;
}
双向循环链表的遍历
void DPLoopShow(DPloop_t* h)
{
    DPloop_t* th = h;
    while (h->next != th) {
        printf("-%d", h->next->data);
        h = h->next;
    }
    printf("-\n");
    while (h->pre != th) {
        printf("-%d", h->data);
        h = h->pre;
    }
    printf("-%d-\n", h->data);
}

双向循环链表的整体代码

DPloop.h

#ifndef __DPLOOP_H__
#define __DPLOOP_H__
#include <stdio.h>
#include <stdlib.h>

#define datatype int
typedef struct node{
    datatype data;
    struct node *pre,*next;
}DPloop_t;

DPloop_t * DPLoopCreate(void);
int DPLoopInsertHead(DPloop_t *h,datatype data);
datatype DPLoopDeleteHead(DPloop_t *h);
void DPLoopShow(DPloop_t *h);
#endif

DPloop.c

#include "DPloop.h"

DPloop_t* DPLoopCreate(void)
{
    DPloop_t* h;

    h = (DPloop_t*)malloc(sizeof(*h));
    if (h == NULL) {
        printf("malloc DP head node error\n");
        return NULL;
    }

    h->data = (datatype)0;
    h->next = h;
    h->pre = h;

    return h;
}

int DPLoopInsertHead(DPloop_t* h, datatype data)
{
    DPloop_t* tmp;
    // 1.分配节点的内存,并将data存放进去
    tmp = (DPloop_t*)malloc(sizeof(*tmp));
    if (tmp == NULL) {
        printf("malloc node memory error\n");
        return -1;
    }
    tmp->data = data;
    // 2.进行头插法
    tmp->next = h->next;
    tmp->pre = h;
    h->next->pre = tmp;
    h->next = tmp;
    return 0;
}
int DPLoopIsEmpty(DPloop_t* h)
{
    return h->next == h ? 1 : 0;
}
datatype DPLoopDeleteHead(DPloop_t* h)
{
    datatype data;
    DPloop_t* tmp;
    // 1.判空
    if (DPLoopIsEmpty(h)) {
        printf("双向循环链表是空,头删失败\n");
        return (datatype)-1;
    }
    // 2.让tmp指向h的下一个节点
    tmp = h->next;

    // 3.让tmp的下一个节点的pre指向h
    tmp->next->pre = h;
    // 4.让h的next指向tmp的下一个节点
    h->next = tmp->next;
    // 5.释放tmp
    data = tmp->data;
    if (tmp != NULL) {
        free(tmp);
        tmp = NULL;
    }

    return data;
}
void DPLoopShow(DPloop_t* h)
{
    DPloop_t* th = h;
    while (h->next != th) {
        printf("-%d", h->next->data);
        h = h->next;
    }
    printf("-\n");
    while (h->pre != th) {
        printf("-%d", h->data);
        h = h->pre;
    }
    printf("-%d-\n", h->data);
}

main.c

#include "DPloop.h"

int main(int argc, char const* argv[])
{
    DPloop_t* h;
    h = DPLoopCreate();
    if (h == NULL) {
        printf("create error\n");
        return -1;
    }

    DPLoopInsertHead(h, 40);
    DPLoopInsertHead(h, 30);
    DPLoopInsertHead(h, 20);
    DPLoopInsertHead(h, 10);
    DPLoopShow(h);
    printf("delete head = %d\n", DPLoopDeleteHead(h));
    printf("delete head = %d\n", DPLoopDeleteHead(h));
    printf("delete head = %d\n", DPLoopDeleteHead(h));
    printf("delete head = %d\n", DPLoopDeleteHead(h));
    printf("delete head = %d\n", DPLoopDeleteHead(h));
    DPLoopShow(h);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值