数据结构之双向带头节点带环链表的实现

之前我们曾实现过单向不带头结点不带环的单链表,但是发现,在运用中并不是那么的好用,因为单向链表每个元素只能找到他后面的元素,而无法对其前面进行查找,这样在实际的使用中并不好用,而双向带头结点带环链表却是非常好用,因为他有两个指针,一个可以查找到后面的元素,同样另一个也可以查到前面的元素。

这里写图片描述

其实所谓双向链表,即两个指针next与prev一个指针可以向后进行遍历,一个指针可以向前,而头结点就相当于一个傀儡节点,它里面的数据并不具有什么意义,而它的作用就是一个链表的头部,所以说当一个双链表创建完毕以后,它的头结点是不能再改变的。而且双向链表的各个操作都是利用了两个指针之间的关系直接建立并不需要太多的套路等,所以后面不再赘述,直接实现。


接着在我们实现基本操作之前,同样我们应该先定义一个双链表的节点,此节点中应包括数据、前指针、后指针三个元素。

typedef struct DbLinkNode{
  DbLinkType data;//数据
  struct DbLinkNode* prev;//前指针
  struct DbLinkNode* next;//后指针
}DbLinkNode;

接着,我们在这里罗列一下我们将要实现的操作:

DbLinkNode* DbCreat(DbLinkType value);//创建新的双链表节点

void DbLinkInit(DbLinkNode** phead);//初始化双链表

void DbLinkPrint(DbLinkNode* head);//打印双链表

void DbLinkPushBack(DbLinkNode* head, DbLinkType value);//尾部插入

void DbLinkPushFront(DbLinkNode* head, DbLinkType value);//头部插入

void DbLinkPopBack(DbLinkNode* head);//尾部删除

void DbLinkPopFront(DbLinkNode* head);//头部删除

DbLinkNode* DbLinkFind(DbLinkNode* head, DbLinkType to_find);//查找某个元素

void DbLinkInsert(DbLinkNode* pos, DbLinkType value);//在pos位置之前插入一个元素

void DbLinkInsertAfter(DbLinkNode* pos,DbLinkType value);//在pos位置之后插入一个元素

void DbLinkErase(DbLinkNode* head,DbLinkNode* pos);//删除pos位置元素

void DbLinkRemove(DbLinkNode* head,DbLinkType to_delete);//删除双链表元素,只删除第一次出现的

void DbLinkRemoveAll(DbLinkNode* head,DbLinkType to_delete);//删除链表中所有to_delete

size_t DbLinkSize(DbLinkNode* head);//计算链表大小

int DbLinkEmpty(DbLinkNode* head);//判断链表是否为空

打印、初始化、创建新节点

要对链表进行操作,最基本的初始化、创建新节点、打印这三个步骤必须要有。所以从这个开始。

void DbLinkPrint(DbLinkNode* head)//打印双链表
{
  if(head == NULL)
  {
    return;//非法输入
  }
  DbLinkNode* cur = head->next;

  while(cur != head)
  {
    printf("[%c|%p]",cur->data,cur);
    cur = cur->next;
  }
  printf("\n");

  cur = head->prev;
  while(cur != head)
  {
    printf("[%c|%p]",cur->data,cur);
    cur = cur->prev;
  }
  printf("\n");
  printf("\n");
  printf("\n");

  return;
}

DbLinkNode* DbCreat(DbLinkType value)//创建新的双链表节点
{
  DbLinkNode* new_node = (DbLinkNode*)malloc(sizeof(DbLinkNode));
  new_node->next = new_node;
  new_node->prev = new_node;
  new_node->data = value;
  return new_node;
}

void DbLinkInit(DbLinkNode** phead)//初始化双链表
{
  if(phead == NULL)
  {
    return;//非法输入
  }

  *phead = DbCreat(0);
}

尾插、头插、尾删、头删

void DbLinkPushBack(DbLinkNode* head, DbLinkType value)//尾部插入
{
  if(head == NULL)
  {
    return;//非法输入
  }

  DbLinkNode* new_node = DbCreat(value);
  DbLinkNode* pre = head->prev;

  pre->next = new_node;
  new_node->prev = pre;
  head->prev = new_node;
  new_node->next = head;

  return;
}

void DbLinkPushFront(DbLinkNode* head, DbLinkType value)//头部插入
{
  if(head == NULL)
  {
    return;//非法输入
  }

  DbLinkNode* new_node = DbCreat(value);
  DbLinkNode* tmp = head->next;

  new_node->next = tmp;
  tmp->prev = new_node;
  head->next = new_node;
  new_node->prev = head;
  return;
}

void DbLinkPopBack(DbLinkNode* head)//尾部删除
{
  if(head == NULL)
  {
    return;
  }
  if(head->next == head)
  {
    return;
  }

  DbLinkNode* to_delete = head->prev;
  to_delete->prev->next = head;
  head->prev = to_delete->prev;
  free(to_delete);
  return;
}

void DbLinkPopFront(DbLinkNode* head)//头部删除
{
  if(head == NULL)
  {
    return;
  }
  if(head->next == head)
  {
    return;
  }

  DbLinkNode* to_delete = head->next;
  DbLinkNode* pre = to_delete->next;
  head->next = pre;
  pre->prev = head;
  free(to_delete);
  return;
}

查找元素


DbLinkNode* DbLinkFind(DbLinkNode* head, DbLinkType to_find)//查找某个元素
{
  if(head == NULL)
  {
    return NULL;//非法输入
  }
  DbLinkNode* cur = head->next;

  while(cur != head)
  {
    if(cur->data == to_find)
    {
      return cur;
    }
    cur = cur->next;
  }
  return NULL;
}

在pos位置之前插入,在pos位置之后插入

void DbLinkInsert(DbLinkNode* pos, DbLinkType value)//在pos位置之前插入一个元素
{
  if(pos == NULL)
  {
    return;
  }

  DbLinkNode* new_node = DbCreat(value);
  DbLinkNode* tmp = pos->prev;

  tmp->next = new_node;
  new_node->prev = tmp;
  new_node->next = pos;
  pos->prev = new_node;

  return;
}

void DbLinkInsertAfter(DbLinkNode* pos,DbLinkType value)//在pos位置之后插入一个元素
{
  if(pos == NULL)
  {
    return;//非法输入
  }

  DbLinkNode* new_node = DbCreat(value);
  DbLinkNode* tmp = pos->next;

  tmp->prev = new_node;
  new_node->next = tmp;
  pos->next = new_node;
  new_node->prev = pos;

  return;
}

删除pos位置元素


void DbLinkErase(DbLinkNode* head,DbLinkNode* pos)//删除pos位置元素
{
  if(head == NULL || pos == NULL)
  {
    return;//非法输入
  }
  if(head->next == head)
  {
    return;//空链表
  }

  DbLinkNode* pos_next = pos->next;
  DbLinkNode* pos_prev = pos->prev;

  pos_next->prev = pos_prev;
  pos_prev->next = pos_next;
  free(pos);

  return;
}

删除双链表中value元素只删除第一个/删除所有

void DbLinkRemove(DbLinkNode* head,DbLinkType to_delete)//删除双链表元素,只删除第一次出现的
{
  if(head == NULL)
  {
    return;
  }
  if(head->next == head)
  {
    return;
  }

  DbLinkNode* cur = head->next;

  while(cur != head)
  {
    if(cur->data == to_delete)
    {
      DbLinkErase(head,cur);
      return;
    }
    cur = cur->next;
  }

  return;
}

void DbLinkRemoveAll(DbLinkNode* head,DbLinkType to_delete)//删除链表中所有to_delete
{
  if(head == NULL)
  {
    return;
  }
  if(head->next == head)
  {
    return;
  }

  while(1)
  {
    DbLinkNode* pos_delete =  DbLinkFind(head,to_delete);

    if(pos_delete == NULL)
    {
      return;
    }
    DbLinkErase(head,pos_delete);
  }
}

计算链表大小、判断链表是否为空(为空返回0,不为空返回1)

size_t DbLinkSize(DbLinkNode* head)//计算链表大小
{
  if(head == NULL)
  {
    return -1;
  }

  size_t size = 0;
  DbLinkNode* cur = head->next;

  while(cur != head)
  {
    size++;
    cur = cur->next;
  }
  return size;
}

int DbLinkEmpty(DbLinkNode* head)//判断链表是否为空
{
  if(head == NULL)
  {
    return -1;
  }

  return head->next==head?0:1;
}

以上就是基本的实现操作,接下来是对代码的测试。


void TestPushBack()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushBack(head,'a');
  DbLinkPushBack(head,'b');
  DbLinkPushBack(head,'c');
  DbLinkPushBack(head,'d');
  DbLinkPrint(head);
}

void TestPushFront()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'d');
  DbLinkPrint(head);
}

void TestPopBack()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'d');
  DbLinkPrint(head);
  DbLinkPopBack(head);
  DbLinkPrint(head);
}

void TestPopFront()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'d');
  DbLinkPrint(head);
  DbLinkPopFront(head);
  DbLinkPrint(head);
}

void TestFind()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'d');

  DbLinkNode* ret = DbLinkFind(head,'b');
  printf("[%c|%p]\n",ret->data,ret);
  ret = DbLinkFind(head,'z');
  printf("%p\n",ret);
}

void TestInsert()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'d');
  DbLinkPrint(head);
  DbLinkNode* pos_b = DbLinkFind(head,'b');
  DbLinkInsert(pos_b,'z');
  DbLinkPrint(head);
}

void TestInsertAfter()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'d');
  DbLinkPrint(head);
  DbLinkNode* pos_b = DbLinkFind(head,'b');
  DbLinkInsertAfter(pos_b,'z');
  DbLinkPrint(head);
}

void TestErase()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'d');
  DbLinkPrint(head);
  DbLinkNode* pos_b = DbLinkFind(head,'b');
  DbLinkErase(head,pos_b);
  DbLinkPrint(head);
}

void TestRemove()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'d');
  DbLinkPrint(head);
  DbLinkRemove(head,'c');
  DbLinkPrint(head);
}

void TestRemoveAll()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'d');
  DbLinkPrint(head);
  DbLinkRemoveAll(head,'c');
  DbLinkPrint(head);
}

void TestSize()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPrint(head);
  size_t sz = DbLinkSize(head);
  printf("%lu\n",sz);
}

void TestEmpty()
{
  HEAD;
  DbLinkNode* head;
  DbLinkInit(&head);
  int n = DbLinkEmpty(head);
  printf("%d\n",n);
  DbLinkPushFront(head,'a');
  DbLinkPushFront(head,'b');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPushFront(head,'c');
  DbLinkPrint(head);
  n = DbLinkEmpty(head);
  printf("%d\n",n);
}

int main()
{
  TestPushBack();
  TestPushFront();
  TestPopBack();
  TestPopFront();
  TestFind();
  TestInsert();
  TestInsertAfter();
  TestErase();
  TestRemove();
  TestRemoveAll();
  TestSize();
  TestEmpty();

  printf("\n");
  printf("\n");
  printf("\n");
  printf("\n");
  return 0;
}

欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值