数据结构之双链表

2.双链表

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

格式:

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

2.1 定义链表节点结构:

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

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

2.2初始化链表头指针

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

c++ 复制代码
//初始化链表头指针
ListNode* head = NULL; // 初始化链表为空链表

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

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

  • 如果头节点为空,新创建的节点即为头节点
c++ 复制代码
if (head == NULL)
    {
      // 如果链表为空,新节点即为头节点
      head = createNode(value);
    }
  • 头部插入,让新节点的 p_next 指向头节点,头节点的 p_prev 指向新节点,那么新节点就变 成了新的头节点

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

c++ 复制代码
//头部添加节点 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)

c++ 复制代码
/尾插
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)
c++ 复制代码
//中间插入
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删除链表中的节点

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

c++ 复制代码
//删除节点
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 清空链表

c++ 复制代码
//清空链表
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 完整代码

c++ 复制代码
#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;
}
相关推荐
明月看潮生5 分钟前
青少年编程与数学 02-018 C++数据结构与算法 24课题、密码学算法
c++·算法·青少年编程·密码学·编程与数学
小小白?15 分钟前
64.搜索二维矩阵
数据结构·线性代数·算法·矩阵
wuqingshun31415922 分钟前
蓝桥杯 17. 通电
c++·算法·职场和发展·蓝桥杯·深度优先·动态规划
烦躁的大鼻嘎33 分钟前
【Linux】深入理解Linux基础IO:从文件描述符到缓冲区设计
linux·运维·服务器·c++·ubuntu
卷卷的小趴菜学编程40 分钟前
算法篇-----滑动窗口
数据结构·算法·双指针·滑动窗口·哈希表·数组相关
李匠20241 小时前
C++负载均衡远程调用学习之HOOK注册机制
java·c++·学习·负载均衡
zxctsclrjjjcph1 小时前
【动态规划】子序列问题
开发语言·c++·算法·动态规划·力扣
forth touch1 小时前
C和指针——预处理
c语言·开发语言
字节旅行者1 小时前
如何使用VSCode编写C、C++和Python程序
开发语言·c++·ide·vscode·python·编辑器
Sheep Shaun1 小时前
C++ STL简介:构建高效程序的基石
开发语言·数据结构·c++·算法