带头结点的双向链表的基本操作

带头结点的双向链表的基本操作_第1张图片
准备工作

//带头结点的双向链表
#pragma once

typedef char DataType;

typedef struct Node
{
      DataType data;         //数据区
      struct Node *next;    //指向下一个节点
      struct Node *pre;     //指向前一个节点
}DLinklistNode;

//创建一个新节点
DLinklistNode* DLinklistCreatNode(DataType value)
{

    DLinklistNode* newnode=(DLinklistNode *)malloc(sizeof(DLinklistNode));

    newnode->next=newnode;
    newnode->pre=newnode;
    newnode->data=value;
    return newnode;

}

初始化

void DLinklistInit(DLinklistNode** phead)   
{
    if(  phead==NULL)
    {
        printf(  "  非法输入\n");
        return ;

    }
    *phead=DLinklistCreatNode('0');

}

带头结点的双向链表的基本操作_第2张图片

//打印链表
void DLinklistPrint(DLinklistNode *phead,char* s)
{

    if(  phead==NULL)
    {
        printf(  "  非法输入\n");
        return ;

    }
    printf("\n%s\n",s);
    //打印头结点
    //printf("[%c][%p]\n",phead->data,phead);
    //从头到尾打印
    DLinklistNode *cur1=phead->next;
    while(cur1!=phead)
    {
        printf("[%c][%p]    ",cur1->data,cur1);
        cur1=cur1->next;
    }
    printf("\n");
    //从尾到头打印
    DLinklistNode *cur2=phead->pre;
    while(cur2!=phead)
    {
        printf( "[%c][%p]   ",cur2->data,cur2);
        cur2=cur2->pre;
    }

    //打印头结点
    // printf("[%c][%p] ", cur2 -> data, cur2 -> pre);
}



带头结点的双向链表的基本操作_第3张图片

//尾插
void DLinklistPushBack(DLinklistNode* phead,DataType value)
{

    if(  phead==NULL)
    {
        printf(  "  非法输入\n");
        return ;

    }
    //创建一个新节点
    DLinklistNode *newnode=DLinklistCreatNode(value);

    //定义一个尾节点
    DLinklistNode* tail=phead->pre;
    //phead  vs  newnode
    newnode->next=phead;
    phead->pre=newnode;
    //tail vs newnode
    newnode->pre=tail;
    tail->next=newnode;
}

带头结点的双向链表的基本操作_第4张图片

//销毁一个节点
void DLinklistDestory(DLinklistNode* to_delete)
{
    if(to_delete==NULL)
    {
        printf(  "非法操作\n");
        return;
    }
    free(to_delete);
}
//尾删
void DLinklistPopBack(DLinklistNode* phead)
{

    if(  phead==NULL)
    {
        printf(  "  非法输入\n");
        return ;
    }  
    if(  phead->next==NULL)
    {
        printf( "空链表\n");
        return;
    }
    //找到删除节点和删除节点的前一个节点
    DLinklistNode* to_delete=phead->pre;
    DLinklistNode* pre_delete=to_delete->pre;
    //phead vs pre_delete
    pre_delete->next=phead;
    phead->pre=pre_delete;
    //释放to_delete
    DLinklistDestory(to_delete);
}

带头结点的双向链表的基本操作_第5张图片

//头插

带头结点的双向链表的基本操作_第6张图片


void DLinklistPushFront(DLinklistNode* phead,DataType value)
{

    if(phead==NULL)
    {
        printf("非法输入\n");
        return ;
    }
    //创建新节点
    DLinklistNode* newnode=DLinklistCreatNode(value);
    //找到头结点之后的第一个节点
    DLinklistNode* after=phead->next;
    //newnode vs head
    newnode->pre=phead;
    phead->next=newnode;
    //newnode vs after
    newnode->next=after;
    after->pre=newnode;

}

带头结点的双向链表的基本操作_第7张图片

//头删
void DLinklistPopFront( DLinklistNode* phead)
{

    if(  phead==NULL)
    {
        printf(  "  非法输入\n");
        return ;
    }  
    if(  phead->next==NULL)
    {
        printf( "空链表\n");
        return;
    }
    //找到删除节点和删除节点的后一个节点
    DLinklistNode* to_delete=phead->pre;
    DLinklistNode* to_delete_next=to_delete->next;
    //修改指向
    phead->next=to_delete_next;
    to_delete_next->pre=phead;
    //释放节点
    DLinklistDestory(to_delete);
}

带头结点的双向链表的基本操作_第8张图片

//查找指定值的位置
DLinklistNode*  DLinklistFind(DLinklistNode* phead,DataType to_find)
{

    if(  phead==NULL)
    {
        printf(  "  非法输入\n");
        return ;
    }
    //空链表,返回NULL
    if(  phead->next==NULL)
    {
          printf(  "空链表\n");
          return NULL;

    }
    //定义一个指针遍历链表,找到返回cur指针
    DLinklistNode* cur=phead->next;
    while(cur!=phead)
    {
          if( cur->data==to_find)
          {
                return cur;
          }
          cur=cur->next;
    }
    //找不到,返回NULL
    return NULL;
}

带头结点的双向链表的基本操作_第9张图片

在任意位置之后插入

带头结点的双向链表的基本操作_第10张图片


void DLinklistInsert(DLinklistNode* phead,DLinklistNode*pos,DataType value)
{
      if(phead==NULL||pos==NULL)
      {
            printf( " 非法输入\n");
            return ;

      }
      //创建一个节点
      DLinklistNode* newnode=DLinklistCreatNode(value);
      //找到pos之前的节点
      DLinklistNode* cur=pos->pre;
      //newnode vs cur
      cur->next=newnode;
      newnode->pre=cur;
      //newnode vs pos
      newnode->next=pos;
      pos->pre=newnode;

}  

带头结点的双向链表的基本操作_第11张图片

在任意位置之后插入

带头结点的双向链表的基本操作_第12张图片


void DLinklistInsert_Front(DLinklistNode* phead,DLinklistNode*pos,DataType value)
{

      if(phead==NULL||pos==NULL)
      {
            printf( " 非法输入\n");
            return ;

      }
      //创建一个节点
      DLinklistNode* newnode=DLinklistCreatNode(value);
      //找到pos之前的节点
      DLinklistNode* cur=pos->next;
      //newnode vs pos
      pos->pre=newnode;
      newnode->next=pos;
      //newnode vs cur
      newnode->pre=cur;
      cur->next=newnode;

}

带头结点的双向链表的基本操作_第13张图片

//删除指定位置的值
void DLinkListErase(DLinklistNode* phead,DLinklistNode* pos)  
{

      if(phead==NULL||pos==NULL)
      {
            printf( " 非法输入\n");
            return ;


      }
      if( phead->next==NULL)
      {
          printf(  "空链表\n");
          return NULL;
      }  



    //找到pos之前和之后的节点
      DLinklistNode* before=pos->pre;
      DLinklistNode* after=pos->next;

     //before vs after
      before->next=after;
      after->pre=before;



}

//删除指定值
void DLinkListRemove(DLinklistNode* phead,DataType value)
{

      if(phead==NULL||pos==NULL)
      {
            printf( " 非法输入\n");
            return ;
      }  
      //定一个节点,遍历链表
      DLinklistNode* cur=phead->next;
      for(;cur!=phead;cur=cur->next)
      {
            //找到链表中的第一个value值,
            if(  cur->data==value)
            {
                  //保存存放value值的节点
                  DLinklistNode* tmp=cur;
                  //修改tmp节点前节点和后节点指向关系
                  cur->pre->next=cur->next;
                  cur->next->pre=cur->pre;
                  //销毁tmp节点
                  DLinklistDestory(tmp);
                  返回
                  return ;
            }
      }
}

//删除指定值的所有元素
void DLinkListRemoveAll(DLinklistNode* phead,DataType value)
{

      if(phead==NULL||pos==NULL)
      {
            printf( " 非法输入\n");
            return ;
      }  
      DLinklistNode* cur=phead->next;
      //遍历链表,找到所有value值,并删除
      for(;cur!=phead;cur=cur->next)
      {
            if(  cur->data==value)
            {
                  DLinklistNode* tmp=cur;
                  cur->pre->next=cur->next;
                  cur->next->pre=cur->pre;
                  DLinklistDestory(tmp);

            }
      }
}

//求双向链表的长度
size_t DLinkListSize(DLinklistNode* phead)
{

      if(phead==NULL||pos==NULL)
      {
            printf( " 非法输入\n");
            return ;
      } 
      //定义一个指针遍历链表,一个计数器计数
      DLinklistNode* cur=phead->next;
      size_t count=0;
      for(  ;cur!=phead;cur=cue->next)
      {
            ++count;
      }
      return count;
}

//判断链表是否为空
int DLinkListEmpty(DLinklistNode* phead)
{
      //链表为空,返回0,不为空返回1,非法操作返回-1
      if(phead==NULL||pos==NULL)
      {
            printf( " 非法输入\n");
            return -1;
      } 
     if(phead->next==NULL)
     {
           return 0;
     }
     return 1;
}

总结:

  • 在对链表进行插入删除等操作之前一定要对链表进行初始化,否则就会出现段错误,或者重复插入的情况。

  • 在任意位置插入之前,首先的找到插入位置,这个时候可以将未知问题化为已知问题。先调用查找函数,然后进行插入操作。

  • 各个节点的关系要确认清楚,尤其在任意位置之前插入和任意位置之后插入。

你可能感兴趣的:(数据结构)