链链不忘:双向链表哪里跑

 一 . 概念与结构

      1.原名:带头双向循环链表
             注意:这⾥的“带头”跟前面说的“头结点”是两个概念,实际前⾯的在单链表阶段称呼不严谨,但是为了更好的理解就直接称为单链表的头结点。
      2.带头链表⾥的头结点,实际为“哨兵位”,哨兵位结点不存储任何有效元素,只是站在这⾥“放哨的”,是占位的。
     3.双向链表的每一个结点中三种“元素”,一个是存放的数据,一个是指向下一个结点的指针,一个是指向上一个结点的指针。双向链表中头结点指向上一个结点的指针指向尾结点,尾结点指向下一个结点的指针指向头结点,因此是循环的。


     4.定义双向链表:

 typedef int LTDataType;
         typedef struct ListNode
         {
            struct ListNode* next; //指针保存下⼀个结点的地址
            struct ListNode* prev; //指针保存前⼀个结点的地址
            LTDataType data;
        }LTNode;

二. 双向链表的实现

SList.h ----头文件

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


//链表由一个一个的结点组成,因此定义链表就是定义结点,而一个结点有两个部分组成:存储的数据和指针(指向下一个结点的地址)


//定义链表(结点)的结构
typedef int SLTDataType;
typedef struct SListNode
{
    SLTDataType  data;
    struct SListNode* next;
}SLTNode;

//链表的打印
void SLTPrint(SLTNode* phead);  //phead指向node1


//插入数据
                      //二级指针
void SLPushBack(SLTNode** pphead,SLTDataType x);//尾插
void SLPushFront(SLTNode** pphead,SLTDataType x);//头插
void SLTInsert(SLTNode** pphead, SLTNode pos, SLTDataType x);//在指定位置之前插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//在指定位置之后插入数据

//删除数据
void SLPopBack(SLTNode** pphead); //尾删
void SLPopFront(SLTNode** pphead); //头删
void SLTErase(SLTNode** pphead, SLTNode* pos);//删除pos结点
void SLTEraseAfter(SLTNode* pos);//删除pos之后的结点

//销毁链表
void SListDestroy(SLTNode** pphead);


SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

SList.c-----源文件

#include"SList.h"

void SLTPrint(SLTNode* phead) //phear指向链表的第一个结点的地址
{
    SLTNode* pcur = phead;  //pcur指向的是第一个结点的指针
    while (pcur)
    {
        printf("%d->", pcur->data);
        pcur = pcur->next;
    }
    printf("NULL\n");
}



//定义一个申请新结点的函数
SLTNode* SLTBuyNode(SLTDataType x)
{
    SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
    if (node == NULL)
    {
        perror("malloc fail");
        exit(1);
    }
    node->data = x;
    node->next = NULL;

    return node;
}



//尾部插入函数         //二级指针
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);


    //pphead接收的是&plist(plist就是指向第一个节点的指针)
    // *pphead=plist(解引用pphead)
    //申请新结点
    SLTNode* newnode = SLTBuyNode(x);
    //用链表尾结点指向定义的新结点从而来插入数据
    //找尾结点
    SLTNode* pcur = *pphead; //此时pcur和phear都为NULL
    if (*pphead == NULL)
    {
        *pphead = newnode;
    }
    else
    {
        while (pcur->next)
        {
            pcur = pcur->next;//pcur下一个指向地址给当前pcur指针;
        }
        //连接pucr和newnode
        pcur->next = newnode;
    }
}



//头部插入函数
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);
    SLTNode* newnode = SLTBuyNode(x);

    //让新结点newnode的next指针 指向头结点*pphead
    newnode->next = *pphead;
    *pphead = newnode;
}




//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) 
{
    assert(pphead);
    assert(pos);

    if (pos == *pphead)
    {
        SLPushFront(pphead, x);
    }
    else
    {
        SLTNode* newnode = SLTBuyNode(x);
        SLTNode* prev = *pphead; //找pos的前一个结点prev(需要从头开始遍历)
        while (prev->next != pos)
        {
            prev = prev->next;//这里是继续循环的操作,prev要一个一个往下找
        }
        //以上操作已经找到了prev和pos,现在需要在二者中间插入newnode
        newnode->next = pos->next;
        prev->next = newnode;

    }
}




//在指定位置之后插入数据 
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
     //要先让新插入的结点newnode指向指定位置的下一个结点pos->next,再让pos->next指针指向newnode
    assert(pos);
    SLTNode* newnode = SLTBuyNode(x);
    newnode = pos->next;
    pos->next = newnode;
}




//删除数据



//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
    assert(pphead && *pphead);
    assert(pos);
    //pos为头结点时,要头删
    if (pos == *pphead)
    {
        SLPopFront(pphead);
    }
    else
    {
        SLTNode* prev = *pphead;
        while (prev->next != pos) 
        {
            prev = prev->next;
        }
        prev->next = pos->next;
        free(pos);
        pos = NULL;
    }

}



//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{
    assert(pos && pos->next);
    SLTNode* del = pos->next;//这里为什么要保存pos->next结点?记得看回放
    pos->next = pos->next->next;
    free(del);
    del = NULL;

}




//尾部删除数据
void SLPopBack(SLTNode** pphead)
{
    assert(pphead && *pphead); //pphead意味着传的参数不为空,*pphead表示链表不为空
    //处理只有一个结点的情况:要删除的就是头结点
    if ((*pphead)->next == NULL)
    {
        free(*pphead);
        *pphead = NULL;
    }
    else
    {
        //找尾结点的前一个prev和尾结点ptail  
        SLTNode* prev = NULL;
        SLTNode* ptail = *pphead;
        while (ptail->next)
        {
            prev = ptail;
            ptail = ptail->next;
        }
        prev->next = NULL;
        free(ptail);
        ptail = NULL;
    }
}



//头部删除数据
void SLPopFront(SLTNode** pphead)
{
    assert(pphead && *pphead);
    SLTNode* next = (*pphead)->next;
    free(*pphead);
    *pphead = next;
}


//销毁链表
void SListDestroy(SLTNode** pphead)
{
    assert(pphead && *pphead);
    SLTNode* pcur = *pphead;
    while (pcur)
    {
        SLTNode* next = pcur->next;
        free(pcur);
        pcur = next;
    }
    *pphead = NULL;

}



//查找数据
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
    assert(phead);
    SLTNode* pcur = phead;
    while (pcur)
    {
        if (pcur->data == x)
        {
            return pcur;
        }
        pcur = pcur->next;
    }
    //没有找到
    return NULL;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值