CPP实现双链表

双链表

今天我们来实现一个双链表

首先,理清结构体成员,关于结构体的命名,在当前数据结构中视为局部变量,但是为了混合编程时候的区分,我更倾向于见名知义

遵循简单明了的原则,我们叫节点为DLNode,即Double-linked List Node.

数据组成

  • 前驱节点指针
  • 数据成员
  • 后继节点指针
typedef int ElemType;
typedef struct DLNode
{
    DLNode *precursor;
    ElemType data;
    DLNode *successor;
} DLNode;

初始化

自此我们规定,在数据结构的初始化中,尽量单独定义方法去申请内存和初始化数据

使用引用形参,开辟空间然后设置指针指向为空即可

在这里,我们学习一个新的方法,可以省去我们手动置空的操作,void *calloc(size_t nitems, size_t size)会设置分配的内存为零

  • nitems – 要被分配的元素个数。
  • size – 元素的大小。
void Init(DLNode *&DL)
{
    DL = (DLNode *)calloc(1, sizeof(DLNode));
}

创建

首先是头插法,插入节点的操作顺序一般为(将待插入节点称为t,待插入位置的节点称为DL):

  1. t.data写入数据
  2. t后继指针指向DL的后继指针
  3. t前驱指针指向DL
  4. DL的后继指针指向t

忽然间想到了需要修改尾结点的前驱指针,然后就想到了需要判断尾结点是否为空,但是这里是创建,第一个尾结点是我们的临时节点,不可能为空,所以就不用考虑空节点的前驱问题了,这个问题是在指定位置插入时需要考虑的

void HeadInsertionCteate(DLNode *&DL)
{
    DLNode *tempNode;

    ElemType data;
    scanf("%d", &data);
    while (data != 0)
    {
        tempNode = (DLNode *)calloc(1, sizeof(DLNode));
        tempNode->data = data;
        tempNode->successor = DL->successor;
        tempNode->precursor = DL;

        DL->successor = tempNode;
        // 头插法头结点的后继已经被初始化为空,所以省去插入的判断
        // if (tempNode->successor != NULL)
        // {
        //     //或许这里的逻辑用在插入方法里面更合适
        //     tempNode->successor->precursor = tempNode;
        // }
        scanf("%d", &data);
    }
}

然后是尾插法,需要一个保持指向尾结点的游标节点

void TailInsertionCreate(DLNode *&DL)
{
    DLNode *tempNode;
    DLNode *tailCursor = DL; // 始终指向尾结点
    int data;
    scanf("%d", &data);
    while (data != 0)
    {
        tempNode = (DLNode *)calloc(1, sizeof(DLNode));

        tempNode->data = data;

        tailCursor->successor = tempNode;
        tempNode->precursor = tailCursor;
        tailCursor = tempNode;

        scanf("%d", &data);
    }
    // 由于是calloc方法申请内存,所以不用在此置空尾游标后继指针了
}

增加

向指定位置添加节点,那么我们不妨先去写根据位置获取节点的方法

插入的思路很简单,找到节点位置,然后有顺序的变更指向

  • 使用影子节点操作内存
  • 单链表不用判断尾结点是因为没有访问前驱,如果对空地址访问前驱则会出错
bool InsertByPosition(DLNode *DL, int position, ElemType data)
{
    DLNode *shadowNode = GetByPosition(DL, position);
    if (shadowNode == NULL) // 我还是没能实现追加,也许本来就不需要?
    {
        return false;
    }
    DLNode *tempNode = (DLNode *)calloc(1, sizeof(DLNode));
    tempNode->data = data;

    tempNode->successor = shadowNode;
    tempNode->precursor = shadowNode->precursor;
    tempNode->precursor->successor = tempNode;
    if (shadowNode->successor != NULL) // 需要这个判断,如果捕捉到的是尾部节点的话
    {
        tempNode->successor->precursor = tempNode;
        // 单链表不需要做这个判断,是因为不用获取前驱
    }
    return true;
}

删除

根据位置删除节点,通过我们定义查找方法,省去了做位置和合法性的判断

删除逻辑也是变更指向,需要做尾结点的判断,防止访问出错

bool DeleteByPosition(DLNode *DL, int position)
{
    DLNode *designatedNode = GetByPosition(DL, position);
    if (designatedNode == NULL)
    {
        return false;
    }
    designatedNode->precursor->successor = designatedNode->successor; // 尾结点也行
    if (designatedNode->successor != NULL)                            // 需要尾结点判断,尾结点之后的节点并不存在,也不存在前驱指针
    {
        designatedNode->successor->precursor = designatedNode->precursor;
    }
    return true;
}

修改

获取指定位置的节点,修改其中数据即可

bool UpdateByPosition(DLNode *DL, int position, ElemType data)
{
    DLNode *toBeUpdate = GetByPosition(DL, position);

    if (toBeUpdate == NULL)
    {
        return false;
    }
    toBeUpdate->data = data;
    return true;
}

查找

为了方便其他方法的使用,我们先来实现根据位置查找节点

需要判断位置的合法性

在循环里,使用严格不等号与自增运算相配合,小于的情况时,数据偏移,那么不满足时刚好为等于的情况,此时跳出循环即可

DLNode *GetByPosition(DLNode *DL, int position)
{
    if (position < 0)
    {
        return NULL;
    }
    if (position == 0)
    {
        return DL;
    }
    int cursor = 1;     // 以上已经省去0索引
    DL = DL->successor; // 忽略头结点
    while (DL != NULL)
    {
        if (cursor < position)
        {
            DL = DL->successor;
            cursor += 1;
        }
        else
        {
            // 现在是cursor==position的时候,返回即可
            break;
        }
    }
    return DL;
}

这里的根据值查找也会有好几种,在这里我们只设计从前往后遍历

bool IsExist(DLNode *DL, ElemType data)
{

    DL = DL->successor; // 忽略头结点
    while (DL != NULL)
    {
        if (DL->data == data)
        {
            return true;
        }
        DL = DL->successor;
    }
    return false;
}

代码

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

/*定义结构体*/
typedef int ElemType;
typedef struct DLNode
{
    DLNode *precursor;
    ElemType data;
    DLNode *successor;
} DLNode;

void Init(DLNode *&DL)
{
    DL = (DLNode *)calloc(1, sizeof(DLNode));
}

void HeadInsertionCteate(DLNode *&DL)
{
    DLNode *tempNode;

    ElemType data;
    scanf("%d", &data);
    while (data != 0)
    {
        tempNode = (DLNode *)calloc(1, sizeof(DLNode));
        tempNode->data = data;
        tempNode->successor = DL->successor;
        tempNode->precursor = DL;

        DL->successor = tempNode;
        // 头插法头结点的后继已经被初始化为空,所以省去插入的判断
        // if (tempNode->successor != NULL)
        // {
        //     //或许这里的逻辑用在插入方法里面更合适
        //     tempNode->successor->precursor = tempNode;
        // }
        scanf("%d", &data);
    }
}

void TailInsertionCreate(DLNode *&DL)
{
    DLNode *tempNode;
    DLNode *tailCursor = DL; // 始终指向尾结点
    int data;
    scanf("%d", &data);
    while (data != 0)
    {
        tempNode = (DLNode *)calloc(1, sizeof(DLNode));

        tempNode->data = data;

        tailCursor->successor = tempNode;
        tempNode->precursor = tailCursor;
        tailCursor = tempNode;

        scanf("%d", &data);
    }
    // 由于是calloc方法申请内存,所以不用在此置空尾游标后继指针了
}

DLNode *GetByPosition(DLNode *DL, int position)
{
    if (position < 0)
    {
        return NULL;
    }
    if (position == 0)
    {
        return DL;
    }
    int cursor = 1;     // 以上已经省去0索引
    DL = DL->successor; // 忽略头结点
    while (DL != NULL)
    {
        if (cursor < position)
        {
            DL = DL->successor;
            cursor += 1;
        }
        else
        {
            // 现在是cursor==position的时候,返回即可
            break;
        }
    }
    return DL;
}

bool InsertByPosition(DLNode *DL, int position, ElemType data)
{
    DLNode *shadowNode = GetByPosition(DL, position);
    if (shadowNode == NULL) // 我还是没能实现追加,也许本来就不需要?
    {
        return false;
    }
    DLNode *tempNode = (DLNode *)calloc(1, sizeof(DLNode));
    tempNode->data = data;

    tempNode->successor = shadowNode;
    tempNode->precursor = shadowNode->precursor;
    tempNode->precursor->successor = tempNode;
    if (shadowNode->successor != NULL) // 需要这个判断,如果捕捉到的是尾部节点的话
    {
        tempNode->successor->precursor = tempNode;
        // 单链表不需要做这个判断,是因为不用获取前驱
    }
    return true;
}

bool DeleteByPosition(DLNode *DL, int position)
{
    DLNode *designatedNode = GetByPosition(DL, position);
    if (designatedNode == NULL)
    {
        return false;
    }
    designatedNode->precursor->successor = designatedNode->successor; // 尾结点也行
    if (designatedNode->successor != NULL)                            // 需要尾结点判断,尾结点之后的节点并不存在,也不存在前驱指针
    {
        designatedNode->successor->precursor = designatedNode->precursor;
    }
    return true;
}

bool UpdateByPosition(DLNode *DL, int position, ElemType data)
{
    DLNode *toBeUpdate = GetByPosition(DL, position);

    if (toBeUpdate == NULL)
    {
        return false;
    }
    toBeUpdate->data = data;
    return true;
}

bool IsExist(DLNode *DL, ElemType data)
{

    DL = DL->successor; // 忽略头结点
    while (DL != NULL)
    {
        if (DL->data == data)
        {
            return true;
        }
        DL = DL->successor;
    }
    return false;
}

void Print(DLNode *DL)
{
    DL = DL->successor; // 忽略头结点
    while (DL != NULL)
    {
        printf("%2d\t", DL->data);
        DL = DL->successor;
    }
    printf("\n");
}
int main()
{
    DLNode *DL;
    Init(DL);
    // HeadInsertionCteate(DL);
    TailInsertionCreate(DL);
    Print(DL);
    /*查找*/
    DLNode *temp = GetByPosition(DL, 2);
    if (temp != NULL)
    {
        printf("get position 2 success data is %d\n", temp->data);
    }
    else
    {
        printf("get error\n");
    }
    /*查找2*/
    if (IsExist(DL, 888))
    {
        printf("data = 888 is exist\n");
        Print(DL);
    }
    else
    {
        printf("not exist\n");
    }
    /*插入*/
    if (InsertByPosition(DL, 2, 999))
    {
        printf("insert into position 2 data 999 success\n");
        Print(DL);
    }
    else
    {
        printf("insert fault\n");
    }
    /*删除*/
    if (DeleteByPosition(DL, 3))
    {
        printf("delete from position 3 success\n");
        Print(DL);
    }
    else
    {
        printf("delete fault\n");
    }
    /*修改*/
    if (UpdateByPosition(DL, 3, 888))
    {
        printf("update data = 888 on position 3 success\n");
        Print(DL);
    }
    else
    {
        printf("update fault\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值