详解带头双向循环列表

简介: 详解带头双向循环列表

前言


上篇文章我们讲述了链表,链表有带头、不带头,循环、不循环,单向、双向,三种大的形式这三种形式可以组合出8中结构,今天我们学习最常用的其中一种——带头双向循环链表。


一、带头双向循环链表的结构


6bf3493ac86a4221bdabc6349d4430e9.png


我们可以看出这个结构和上篇文章的无头单向非循环链表最大的区别是含有一个头部和每个结构内都含有两个指针,一个指向下个结构,一个指向前一个结构。


二、 带头双向循环链表的实现


2.1链表的创建


typedef int SLTDatatype;
typedef struct SListNode
{
  SLTDatatype data;
  struct SListNode* next;
  struct SListNode* prev;
}SLTNode;


从这个结构体我们就可以看到里面含有两个指针一个next指向下一个,一个prev指向前一个。


2.2开辟新的结点


SLTNode* BuySLT( SLTDatatype x)
{
  SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  if (newnode==NULL)
  {
    perror("malloc failed");
    exit(-1);
  }
  newnode->data = x;
  newnode->next = NULL;
  newnode->prev = NULL;
  return newnode;
}


2.3初始化

SLTNode* SLTInit()
{
  SLTNode* phead = BuySLT(0);
  phead->next = phead;
  phead->prev = phead;
  return phead;
}


这个初始化会创建一个头,我们这个头是循环的,要进行的增删查改的操作也是以这个头为根基进行操作的。


2.4释放销毁


void SLTDer(SLTNode* phead)
{
  SLTNode* cur = phead->next;
  while (cur != phead)
  {
    SLTNode* next = cur->next;
    free(cur);
    cur = next;
  }
  free(phead);
}


我们要使用malloc函数开辟空间,在程序结束时也要依次释放,否则会造成内存泄漏。


2.5链表的打印


void SLprint(SLTNode*phead)
{
  SLTNode* tail = phead->next;
  printf("phead<=>");
  while (tail!= phead)
  {
    printf("%d<=>", tail->data);
    tail = tail->next;
  }
  printf("\n");
}


2.6查找链表中的数据

SLTNode* SLFind(SLTNode* phead, SLTDatatype x)
{
  assert(phead);
  SLTNode* cur = phead->next;
  while (cur!= phead)
  {
    if (cur->data == x)
    {
      return cur;
    }
    else
    {
      cur = cur->next;
    }
  }
  return NULL;
}

由于我们的链表是循环的不能以结尾指向空的指针结尾,要根据这个结构以不指向头的指针为判断条件创建循环,依次打印每个结构中的有效数据。


2.7尾插


void SLPushBack(SLTNode* phead, SLTDatatype x)
{
  assert(phead);
  SLTNode* newnode = BuySLT(x);
  SLTNode* tail = phead->prev;
  newnode->prev = tail;
  tail->next = newnode;
  newnode->next = phead;
  phead->prev = newnode;
}

根据这个链表的结构特点,我们可以通过头指针里的指向后一个也就是末尾的结构体,然后将末尾的结构和新结点里的两个指针连接起来,再将新节点和头结点链接起来,尾插就完成了,整个结构也就串联起来了。


2.8尾删


void SLPopBack(SLTNode* phead)
{
  assert(phead);
  if (phead->next != phead)
  {
    SLTNode* tail = phead->prev;
    SLTNode* first = tail->prev;
    first->next = phead;
    phead->prev = first;
    free(tail);
  }
}

通过头指针里的指向后一个指针就可以找到链表的尾,再根据链表的尾找到前一个保存前一个释放掉尾,然后再将其和头串联起来。


2.9头插


void SLPushFront(SLTNode* phead,SLTDatatype x)
{
  assert(phead);
  SLTNode* newnode = BuySLT(x);
  SLTNode* first = phead->next;
  newnode->next = first;
  first->prev = newnode;
  phead->next = newnode;
  newnode->prev = phead;
}

还是根据这个链表的结构特点就很容易完成头插的操作


2.10头删


void SLPopFront(SLTNode* phead)
{
  assert(phead);
  SLTNode* first = phead->next;
  SLTNode* cur = first->next;
  cur->prev = phead;
  phead->next = cur;
  free(first);
}

和前面的操作大差不差根据这个链表的特点完成这个操作


三、带头双向循环链表中间随机值的插入和删除


这个附加内容就不详解了,通过前面的文章大家应该能有自己的思路了吧!给大家个参考。


3.1在pos位置插入x


void SLTInsert( SLTNode* pos, SLTDatatype x)
{
  assert(pos);
  SLTNode* posprev = pos->prev;
  SLTNode* newnode = BuySLT(x);
  posprev->next = newnode;
  newnode->prev = posprev;
  newnode->next = pos;
  pos->prev = newnode;
}


3.2删除pos位置的值


void SLTErase(SLTNode* pos)
{
  assert(pos);
  SLTNode* first = pos->prev;
  SLTNode* cur = pos->next;
  first->next = cur;
  cur->prev = first;
  free(pos);
}


四、完整代码


#define _CRT_SECURE_NO_WARNINGS 67
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDatatype;
typedef struct SListNode
{
  SLTDatatype data;
  struct SListNode* next;
  struct SListNode* prev;
}SLTNode;
SLTNode* BuySLT( SLTDatatype x)
{
  SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  if (newnode==NULL)
  {
    perror("malloc failed");
    exit(-1);
  }
  newnode->data = x;
  newnode->next = NULL;
  newnode->prev = NULL;
  return newnode;
}
void SLTDer(SLTNode* phead)
{
  SLTNode* cur = phead->next;
  while (cur != phead)
  {
    SLTNode* next = cur->next;
    free(cur);
    cur = next;
  }
  free(phead);
}
SLTNode* SLTInit()
{
  SLTNode* phead = BuySLT(0);
  phead->next = phead;
  phead->prev = phead;
  return phead;
}
void SLprint(SLTNode*phead)
{
  SLTNode* tail = phead->next;
  printf("phead<=>");
  while (tail!= phead)
  {
    printf("%d<=>", tail->data);
    tail = tail->next;
  }
  printf("\n");
}
SLTNode* SLFind(SLTNode* phead, SLTDatatype x)
{
  assert(phead);
  SLTNode* cur = phead->next;
  while (cur!= phead)
  {
    if (cur->data == x)
    {
      return cur;
    }
    else
    {
      cur = cur->next;
    }
  }
  return NULL;
}
//尾插
void SLPushBack(SLTNode* phead, SLTDatatype x)
{
  assert(phead);
  SLTNode* newnode = BuySLT(x);
  SLTNode* tail = phead->prev;
  newnode->prev = tail;
  tail->next = newnode;
  newnode->next = phead;
  phead->prev = newnode;
}
//尾删
void SLPopBack(SLTNode* phead)
{
  assert(phead);
  if (phead->next != phead)
  {
    SLTNode* tail = phead->prev;
    SLTNode* first = tail->prev;
    first->next = phead;
    phead->prev = first;
    free(tail);
  }
}
//头插
void SLPushFront(SLTNode* phead,SLTDatatype x)
{
  assert(phead);
  SLTNode* newnode = BuySLT(x);
  SLTNode* first = phead->next;
  newnode->next = first;
  first->prev = newnode;
  phead->next = newnode;
  newnode->prev = phead;
}
//头删
void SLPopFront(SLTNode* phead)
{
  assert(phead);
  SLTNode* first = phead->next;
  SLTNode* cur = first->next;
  cur->prev = phead;
  phead->next = cur;
  free(first);
}
//
void SLTInsert( SLTNode* pos, SLTDatatype x)
{
  assert(pos);
  SLTNode* posprev = pos->prev;
  SLTNode* newnode = BuySLT(x);
  posprev->next = newnode;
  newnode->prev = posprev;
  newnode->next = pos;
  pos->prev = newnode;
}
//
void SLTErase(SLTNode* pos)
{
  assert(pos);
  SLTNode* first = pos->prev;
  SLTNode* cur = pos->next;
  first->next = cur;
  cur->prev = first;
  free(pos);
}
int main()
{
  SLTNode* plist = SLTInit();
  //尾插
  SLPushBack(plist, 1);
  SLPushBack(plist, 2);
  SLPushBack(plist, 3);
  SLPushBack(plist, 4);
  SLPushBack(plist, 5);
  //尾插测试
  printf("尾插:\n");
  SLprint(plist);
  //尾删
  SLPopBack(plist);
  printf("尾删:\n");
  SLprint(plist);
  //头插
  SLPushFront(plist,10);
  printf("头插:\n");
  SLprint(plist);
  //头删
  SLPopFront(plist);
  printf("头删:\n");
  SLprint(plist);
  //在pos位置后插入x
  SLTNode* pos1 = SLFind(plist, 4);
  SLTInsert(pos1, 5);
  printf("在pos位置插入x:\n");
  SLprint(plist);
  //删除pos位置的值
  SLTNode* pos2 = SLFind(plist, 5);
  SLTErase(pos2);
  printf("删除pos位置的值\n");
  SLprint(plist);
  SLTDer(plist);
  plist = NULL;
  return 0;
}


cf4dd0a1b6dd44e7a33be8685c33e2e7.png

这个就是本篇文章的所有内容了,通读全文我们可以发现带头双向循环链表这个结构确实实用,使用起来也是非常的方便简洁,数据结构链表的内容到这就基本结束了,欢迎大家在评论区留言,交流和探讨,说出自己的想法。

相关文章
|
6月前
|
编译器 C++
C++ 反向迭代器的设计与实现
C++ 反向迭代器的设计与实现
|
7月前
|
存储
【数据结构】【版本1.2】【线性时代】——链表之王(双向带头循环)
【数据结构】【版本1.2】【线性时代】——链表之王(双向带头循环)
|
7月前
|
消息中间件 算法 调度
数据结构与算法——单向循环列表、栈和队列(附代码)
数据结构与算法——单向循环列表、栈和队列(附代码)
45 2
|
7月前
链表中涉及“快慢指针”的编程题—“返回中间节点”
链表中涉及“快慢指针”的编程题—“返回中间节点”
61 0
|
存储 算法
数据结构单链表之检测和删除链表中的循环 | 第十三套
数据结构单链表之检测和删除链表中的循环 | 第十三套
57 0
|
算法
数据结构单链表之以给定大小的组反转链表 | 第十二套
数据结构单链表之以给定大小的组反转链表 | 第十二套
39 0
|
存储 C++
【双向链表】带头双向循环(1)
【双向链表】带头双向循环(1)
64 0
|
存储 移动开发 算法
【数据结构和算法】使用数组的结构实现链表(单向或双向)
【数据结构和算法】使用数组的结构实现链表(单向或双向)
|
缓存 算法 大数据
双向 链表
双向 链表
下一篇
DataWorks