带头双向循环链表的实现

简介: 带头双向循环链表的实现

认识带头双向循环链表

双向链表

我们之前认学习的单链表,是包含一个next指针指向下一个结点,而双向链表既有next指针,又有一个前指针指向前一个结点

循环链表

循环链表就是最后一个结点的next不指向NULL,指向第一个结点

带头链表

带头链表就是带哨兵位的头结点head,头结点不存数据

带头双向循环链表

  1. 无头单向非循环链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  2. 带头双向循环链表结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单

双向链表的优势和不足:

双向链表的优势

  • 任意位置插入删除都是O(1)
  • 按需申请释放,合理利用空间,不存在浪费

问题

  • 下标的随机访问不方便O(N)

顺序表的优势和不足:

顺序表的优势

  • 支持下标的随机访问O(1)

问题

  • 头插或中间插入的效率低O(N)
  • 空间不够需要扩容,有一定的消耗,且可能存在一定的空间浪费
  • 只适合尾插尾删

实现带头双向循环链表

同样我们创建三个文件来实现:

创建带头双向循环链表

我们在结构体中定义val存数据,prev指向前一个结点,next指向下一个结点

初始化

让phead->next和phead->prev都指向phead,给phead->val赋值为-1,最后返回phead

创建返回链表的头结点

打印链表

尾插

尾删

头插

头删

头结点不能删!!!

所以我们要assert(phead->next != phead)

查找

在pos位置前插入

特殊情况:

  • LTInsert(phead->next,x)就是头插
  • LTInsert(phead,x)就是尾插

删除pos位置

特殊情况:

  • LTErase(phead->next)就是头删
  • LTErase(phead->prev)就是尾删

销毁

总代码

ListNode.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{
  LTDataType val;
  struct ListNode* prev;
  struct ListNode* next;
}LTNode;
//初始化
LTNode* LTInit();
//创建返回链表的头结点.
LTNode* CreateLTNode(LTDataType x);
//打印
void LTPrint(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//尾删
void LTPopBack(LTNode* phead);
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//头删
void LTPopFront(LTNode* phead);
//查找
LTNode* LTFind(LTNode* phead, LTDataType x);
//在pos位置前插入
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置
void LTErase(LTNode* pos);
//销毁
void LTDestroy(LTNode* phead);

ListNode.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"ListNode.h"
//初始化
LTNode* LTInit()
{
  LTNode* phead = CreateLTNode(-1);
  phead->next = phead;
  phead->prev = phead;
  return phead;
}
//创建返回链表的头结点.
LTNode* CreateLTNode(LTDataType x)
{
  LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
  if (newnode == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  newnode->val = x;
  newnode->next = NULL;
  newnode->prev = NULL;
  return newnode;
}
//打印
void LTPrint(LTNode* phead)
{
  assert(phead);
  LTNode* cur = phead->next;
  printf("哨兵位");
  while (cur != phead)
  {
    printf("%d<=>", cur->val);
    cur = cur->next;
  }
  printf("哨兵位\n");
}
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
  assert(phead);
  LTNode* tail = phead->prev;
  LTNode* newnode = CreateLTNode(x);
  tail->next = newnode;
  newnode->prev = tail;
  phead->prev = newnode;
  newnode->next = phead;
}
//尾删
void LTPopBack(LTNode* phead)
{
  assert(phead);
  assert(phead->next != phead);
  LTNode* tail = phead->prev;
  LTNode* tailPrev = tail->prev;
  free(tail);
  tailPrev->next = phead;
  phead->prev = tailPrev;
}
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
  assert(phead);
  LTNode* newnode = CreateLTNode(x);
  LTNode* first = phead->next;
  newnode->next = first;
  first->prev = newnode;
  phead->next = newnode;
  newnode->prev = phead;
}
//头删
void LTPopFront(LTNode* phead)
{
  assert(phead);
  assert(phead->next != phead);
  LTNode* first = phead->next;
  LTNode* second = first->next;
  phead->next = second;
  second->prev = phead;
  free(first);
  first = NULL;
}
//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
  assert(phead);
  LTNode* cur = phead->next;
  while (cur != phead)
  {
    if (cur->val == x)
    {
      return cur;
    }
    cur = cur->next;
  }
  return NULL;
}
//在pos位置前插入
void LTInsert(LTNode* pos, LTDataType x)
{
  assert(pos);
  LTNode* newnode = CreateLTNode(x);
  LTNode* posprev = pos->prev;
  posprev->next = newnode;
  newnode->prev = posprev;
  newnode->next = pos;
  pos->prev = newnode;
}
//删除pos位置
void LTErase(LTNode* pos)
{
  assert(pos);
  LTNode* posprev = pos->prev;
  LTNode* posnext = pos->next;
  posprev->next = posnext;
  posnext->prev = posprev;
  free(pos);
  pos = NULL;
}
//销毁
void LTDestroy(LTNode* phead)
{
  assert(phead);
  LTNode* cur = phead->next;
  while (cur != phead)
  {
    LTNode* next = cur->next;
    free(cur);
    cur = next;
  }
  free(phead);
  phead = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"ListNode.h"
int main()
{
  LTNode* plist = LTInit();
  //尾插
  LTPushBack(plist, 1);
  LTPushBack(plist, 2);
  LTPushBack(plist, 3);
  LTPushBack(plist, 4);
  LTPrint(plist);
  //尾删
  LTPopBack(plist);
  LTPrint(plist);
  //头插
  LTPushFront(plist, 0);
  LTPrint(plist);
  //头删
  LTPopFront(plist);
  LTPrint(plist);
  //查找 pos前插
  LTNode* pos = LTFind(plist, 3);
  LTInsert(pos, 3);
  LTPrint(plist);
  //删除pos位置
  LTErase(pos);
  LTPrint(plist);
  //销毁
  LTDestroy(plist);
  return 0;
}


相关文章
|
8月前
|
存储 算法
数据结构与算法之《带头双向循环链表》详解
数据结构与算法之《带头双向循环链表》详解
41 0
|
12天前
|
存储 算法 C语言
链表带头和不带头的区别及其应用
链表带头和不带头的区别及其应用
|
14天前
双向循环链表
双向循环链表
20 0
|
19天前
|
C语言 C++
【c++】用c++实现带头双向循环链表
【c++】用c++实现带头双向循环链表
|
19天前
|
存储
带头双向循环链表
带头双向循环链表
40 0
|
7月前
|
Python
循环链表
循环链表是一种链表的变种,它的最后一个节点的指针指向第一个节点,形成了一个环状结构。循环链表的特点包括:可以高效地实现正向和反向遍历,但是插入和删除操作相对较为复杂。
55 6
|
10月前
无头单向不循环链表和带头双向循环链表的创建
接下来我们将会了解最基础的链表--->单链表 以及最方便也是最爽的链表--->带头双向循环链表。
30 0
|
12月前
|
存储
数据结构之链表(带头双向循环链表)
数据结构之链表(带头双向循环链表)
78 0
|
12月前
双向带头循环链表(增删查改)
双向带头循环链表(增删查改)
|
12月前
|
存储
【链表】双向循环链表的实现
【链表】双向循环链表的实现
61 0