数据结构之双链表

2.双链表

在C++中,双链表(Doubly Linked List)是一种常见的数据结构,它允许我们在链表的任何位置高效地 插入和删除节点。与单链表不同,双链表的每个节点都包含两个指针:一个指向下一个节点

格式:

 struct 类型名
{
类型1 变量1; // 数据1
 ...
类型n 变量n; // 数据n
 struct 类型名 *前向指针变量; // 存储数据元素的地址。在双链表结构里面,存储上一个数据元素
的内存地址
struct 类型名 *后向指针变量; // 存储数据元素的地址。在双链表结构里面,存储下一个数据元素
的内存地址
}; 

2.1 定义链表节点结构:

首先,需要定义一个链表节点的结构体,该结构体至少包含三个成员:存储数据的部分(数据域)和指 向上一个节点的指针、指向下一个节点的指针。(指针域)。

//定义链表节点结构
typedef struct listNode
{
    int val;//整形数据
    struct listNode *p_prev;//指向下一个节点的指针
    struct listNode *p_next;//指向上一个节点的指针
}listNode;

2.2初始化链表头指针

在创建链表之前,需要初始化链表的头指针。头指针是一个指向链表第一个节点的指针。如果链表为 空,则头指针通常设置为 NULL 。

//初始化链表头指针
ListNode* head = NULL; // 初始化链表为空链表

在这里插入图片描述

*2.3 创建并添加节点到链表

**接下来,可以通过创建新的节点并将其插入到链表中来构建链表。插入操作可以根据需要在链表的不同 位置进行(如头部、尾部或特定位置)。

  • 如果头节点为空,新创建的节点即为头节点
if (head == NULL)
    {
      // 如果链表为空,新节点即为头节点
      head = createNode(value);
    }

在这里插入图片描述

  • 头部插入,让新节点的 p_next 指向头节点,头节点的 p_prev 指向新节点,那么新节点就变 成了新的头节点

头节点变化就返回返回新的头节点

//头部添加节点 head:头节点 value:插入的值
//listNode*返回头节点
listNode* insertNodeAtHead(listNode*&head,int value)
{
    //创建新节点
    listNode*newHead=createNode(value);
    //新节点指向头节点
    newHead->p_next=head;    //让新节点的p_next指向头节点
    head->p_prev= newHead; //让头节点的p_prev指向新节点
    return  newHead;   //返回新的头节点

}

在这里插入图片描述

  • 尾部插入:如果要在链表尾部添加节点,需要遍历链表直到最后一个节点,然后将最后一个节 点的 p_next 指针指向新节点,将新节点的 p_prev 指向尾节点。

头节点没变返回返回原来的头节点(head)

/尾插
listNode* insertNodeATail(listNode*&head,int value)
{
    //为空,新节点就是头节点
    if(head==nullptr)
    {
       head= createNode(value);
       
    }
    else
    {
        //定义中间变量遍历
        listNode *current=head;
        while(current->p_next)
        {
            current=current->p_next;
        }
        listNode *newNode=createNode(value);
        newNode->p_prev=current;
        current->p_next=newNode;
   
    }
    return head;
}

在这里插入图片描述

  • 中间插入:按照一定规则在某个位置插入元素,头节点没变返回返回原来的头节点(head)
//中间插入
listNode* insertNodeAMiddle(listNode*&head,int value)
{
    if(head==nullptr)
    {
       head=createNode(value);

       return  head;
    }
    else
    {
        //比头节点小
        if(value<head->val)
        {
           head= insertNodeAtHead(head,value);
           return head;
        }
        //在中间
        listNode* first=head;
        listNode* second=first->p_next;
        while(second)
        {
            if(value>=first->val&&value<=second->val)
            {
                //不用太在意顺序,但是最好对称
                listNode*newNode=createNode(value);
                newNode->p_next=second;
                second->p_prev=newNode;
                first->p_next=newNode;
                newNode->p_prev=first;
                return head;

            }
            first=second;
            second=second->p_next;
        }
        //循环结束也没有插入,就是尾插
        head=insertNodeATail(head,value);

       
    }

  return head;
  
}

在这里插入图片描述

2.4删除链表中的节点

将链表中某个节点删除,删除时要注意,创建节点时申请了内存,删除时要释放内存以防止内存泄露, 同时将指针置为空

//删除节点
listNode*deleteNode(listNode* head, int value)
 {
    if (head == NULL)
    {
        // 如果链表为空,结束
        return NULL;
    }
    //删除的是头节点
    else if (head->val == value)
    {
        //记录新的头节点
        listNode*newHead = head->p_next;
        //新的头节点前面为空
        newHead->p_prev = NULL;
        //删除头节点。防止内存泄露
        delete head;
        //更新头节点
        head = newHead;
    }
    //非头节点
    else
    {
        //从前往后遍历
        listNode* current = head;
        //遍历链表 判断current即可,因为要删除的是current
        while (current)
        {
            //使用一个指针的方式
            //找到要删除的元素 current->next->val而不是curren->val是因为要删除节点,如果
// 是curren->val则无法定位到前面的节点信息
            //所以要删除当前节点的下一个节点
            if (current->val == value)
            {
                //要删除的是当前节点
                //ListNode* needDelNode = current;
                //不是尾节点 例如:1-2-3删除2 变成1-3
                if (current->p_next)
                {
                    //更新节点指向 1的p_next指向3  current是2
                    current->p_prev->p_next = current->p_next;
                    //3的p_prev指向1  current是2
                    current->p_next/*->p_next*/->p_prev = current->p_prev;
                }
                //尾节点 例如:1-2删除2 变成1
                else
                {
                    //1的p_next指向空 current是2
                    current->p_prev->p_next = NULL;
                }
                //删除当前节点
                delete current;
                current = NULL;
                //提前结束
                return head;
            }
            //向后遍历
            current = current->p_next;
}

}
 //执行到此处说明没找到删除的这个节点信息
return head;
  • 删除头节点

在这里插入图片描述

  • 删除尾节点

在这里插入图片描述

  • 删除中间节点

在这里插入图片描述

2.5 清空链表

//清空链表
void  deinitNode(listNode*&head)
{
     //判断链表是否为空
     if(head==NULL)
     {
        cout<<"链表为空"<<endl;

     }
     else
     {
        listNode*current=head;
        while(current)
        {
            listNode*newNode=current;
   
            current=current->p_next;
            delete newNode;
            newNode=nullptr;
        }
        
     }
     head=nullptr;

}

2.6 完整代码

#include <iostream>
using namespace std;

//定义链表节点结构
typedef struct listNode
{
    int val;//整形数据
    struct listNode *p_prev;//指向下一个节点的指针
    struct listNode *p_next;//指向上一个节点的指针
}listNode;
//创建节点 
listNode *createNode(int value)
{ 
    
    listNode*newNode =new listNode{value};
    if(newNode!=nullptr)
    {
        newNode->p_prev=nullptr; // 指向上一个节点的指针 
        newNode->p_next=nullptr; // 指向上一个节点的指针 
        
    }
    return newNode;//此处可以返回局部变量的地址是因为newNode在堆区,
    // 生命周期在delete后才结
}

//头部添加节点 head:头节点 value:插入的值
//listNode*返回头节点
listNode* insertNodeAtHead(listNode*&head,int value)
{
    if (head == NULL)
    {
      // 如果链表为空,新节点即为头节点
      head = createNode(value);
    }
 
    //创建新节点
    listNode*newHead=createNode(value);
    //新节点指向头节点
    newHead->p_next=head;    //让新节点的p_next指向头节点
    head->p_prev= newHead; //让头节点的p_prev指向新节点
    return  newHead;   //返回新的头节点

}

//尾插
listNode* insertNodeATail(listNode*&head,int value)
{
    //为空,新节点就是头节点
    if(head==nullptr)
    {
       head= createNode(value);
       
    }
    else
    {
        //定义中间变量遍历
        listNode *current=head;
        while(current->p_next)
        {
            current=current->p_next;
        }
        listNode *newNode=createNode(value);
        newNode->p_prev=current;
        current->p_next=newNode;
   
    }
    return head;
}
//中间插入
listNode* insertNodeAMiddle(listNode*&head,int value)
{
    if(head==nullptr)
    {
       head=createNode(value);

       return  head;
    }
    else
    {
        //比头节点小
        if(value<head->val)
        {
           head= insertNodeAtHead(head,value);
           return head;
        }
        //在中间
        listNode* first=head;
        listNode* second=first->p_next;
        while(second)
        {
            if(value>=first->val&&value<=second->val)
            {
                //不用太在意顺序,但是最好对称
                listNode*newNode=createNode(value);
                newNode->p_next=second;
                second->p_prev=newNode;
                first->p_next=newNode;
                newNode->p_prev=first;
                return head;

            }
            first=second;
            second=second->p_next;
        }
        //循环结束也没有插入,就是尾插
        head=insertNodeATail(head,value);
    }
  return head;
  
}

//删除节点
listNode*deleteNode(listNode* head, int value)
 {
    if (head == NULL)
    {
        // 如果链表为空,结束
        return NULL;
    }
    //删除的是头节点
    else if (head->val == value)
    {
        //记录新的头节点
        listNode*newHead = head->p_next;
        //新的头节点前面为空
        newHead->p_prev = NULL;
        //删除头节点。防止内存泄露
        delete head;
        //更新头节点
        head = newHead;
    }
    //非头节点
    else
    {
        //从前往后遍历
        listNode* current = head;
        //遍历链表 判断current即可,因为要删除的是current
        while (current)
        {
            //使用一个指针的方式
            //找到要删除的元素 current->next->val而不是curren->val是因为要删除节点,如果
// 是curren->val则无法定位到前面的节点信息
            //所以要删除当前节点的下一个节点
            if (current->val == value)
            {
                //要删除的是当前节点
                //ListNode* needDelNode = current;
                //不是尾节点 例如:1-2-3删除2 变成1-3
                if (current->p_next)
                {
                    //更新节点指向 1的p_next指向3  current是2
                    current->p_prev->p_next = current->p_next;
                    //3的p_prev指向1  current是2
                    current->p_next/*->p_next*/->p_prev = current->p_prev;
                }
                //尾节点 例如:1-2删除2 变成1
                else
                {
                    //1的p_next指向空 current是2
                    current->p_prev->p_next = NULL;
                }
                //删除当前节点
                delete current;
                current = NULL;
                //提前结束
                return head;
            }
            //向后遍历
            current = current->p_next;
}

}
 //执行到此处说明没找到删除的这个节点信息
return head;
 }
//清空链表
void  deinitNode(listNode*&head)
{
     //判断链表是否为空
     if(head==NULL)
     {
        cout<<"链表为空"<<endl;

     }
     else
     {
        listNode*current=head;
        while(current)
        {
            listNode*newNode=current;
   
            current=current->p_next;
            delete newNode;
            newNode=nullptr;
        }
        
     }
     head=nullptr;

}
//打印链表
void printNode(listNode *head)
{
    //如果是空链表
    if(head==nullptr)
    {
        cout<<"链表为空"<<endl;
        return;
    }
    //前向遍历指针
    listNode*current=head;
     //后向遍历指针
    listNode*current_prev=head->p_prev;
   //正向
    while(current)
    {
        //输出内容
        
       cout<< current->val<<" ";

       current_prev= current;
       current=current->p_next;
       

    }
    cout<<endl;
       //反向 1-2-3
    while(current_prev)
    {
           //输出内容
          cout<< current_prev->val<<" ";
          //向前遍历
          current_prev= current_prev->p_prev;
   
       }
       cout<<endl;
}
int main()
{
    //初始化链表
    listNode*head=nullptr;
    //插入
    head=insertNodeAMiddle(head,1);//头
    head=insertNodeAMiddle(head,5);//尾
    head=insertNodeAMiddle(head,3);//中间
    head=insertNodeAMiddle(head,4);
    head=insertNodeAMiddle(head,2);
    
    //打印
    printNode(head);

    // 删除
    head=deleteNode(head,1);//头
    head=deleteNode(head,5);//尾
    head=deleteNode(head,3);//中间
     //打印
     printNode(head);

   deinitNode(head);
   printNode(head);
     return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值