【循环链表】数据结构——单向循环链表和双向循环链表操作&笔记

简介: 【循环链表】数据结构——单向循环链表和双向循环链表操作&笔记

一、单向循环链表

将单链表的首尾节点相连就形成了单向循环链表。

1、单向循环链表的节点

2、单向循环链表的结构

单向循环链表只有一个节点时:

二、双向循环链表

1、双向循环链表示意图

2、双向循环链表节点设计

struct d_node{
  int data; //数据域
  struct d_node *next;
  struct d_node *prev;
};

3、双向循环链表的一般性结构

1)只有头结点的情况

2)有多个节点的情况

4、双向循环链表头插法插入节点

步骤:

1)p->next = head->next

2)head->next = p;

3)p->next->prev = p;

4)p->prev = head;

若双向循环链表只有一个节点

步骤:

1)head->next = p;

2)p->prev = head;

5、双向循环链表尾插法

步骤:

1)处理前继指针

p->prev = head->prev;

head->prev = p;

2)处理后继指针

p->prev->next = p;

p->next = head;

6、双向循环链表节点的删除

删除指定节点p步骤:

1)从逻辑上在链表中把p节点删除

p->prev->next = p->next;

p->next->prev = p->prev;

2)从物理上删除节点p——free§

若p节点是链表的最后一个节点,那就:p->prev->next = NULL;

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

//0,设计双向循环链表节点
typedef struct d_node{
  int data;
  struct d_node *prev;  //前继指针
  struct d_node *next;  //后继指针
}D_NODE;

//1,创建双向循环链表
D_NODE *D_Loop_List_Create(void)
{
  //1)申请链表头结点堆空间
  D_NODE *d_list = (D_NODE *)malloc(sizeof(D_NODE));
  if (NULL == d_list)
  {
    perror("malloc failed");
    return NULL;
  }
  //2)对头结点进行赋值
  d_list->prev = d_list;
  d_list->next = d_list;

  //3)返回头结点地址
  return d_list;
}

//2,添加链表节点 -->头插法
bool D_Loop_List_Insert_Head(D_NODE *head, int data)
{
  //1,新节点申请堆空间
  D_NODE *newnode = (D_NODE *)malloc(sizeof(D_NODE));
  if (NULL == newnode)
  {
    perror("malloc newnode failed");
    return false;
  }
  //2,对新节点进行赋值
  newnode->data = data;
  newnode->prev = newnode;
  newnode->next = newnode;

  //3,头插法插入链表
  newnode->next = head->next;
  head->next = newnode;

  newnode->next->prev = newnode;
  newnode->prev = head;
  return true;
}
//尾插法
bool D_Loop_List_Insert_End(D_NODE *head, int data)
{
  //1,新节点申请堆空间
  D_NODE *newnode = (D_NODE *)malloc(sizeof(D_NODE));
  if (NULL == newnode)
  {
    perror("malloc newnode failed");
    return false;
  }
  //2,对新节点进行赋值
  newnode->data = data;
  newnode->prev = newnode;
  newnode->next = newnode;

  //尾插法插入节点
    //1)处理前继指针
    newnode->prev = head->prev;
    head->prev = newnode;
    //2)处理后继指针
    newnode->prev->next = newnode;
    newnode->next = head;
  return true;
}

//3,链表显示
void D_Loop_List_Display(D_NODE *head)
{
  D_NODE *p = head->next;
  printf("链表数据:");
  while( p != head)
  {
    printf("%d ", p->data);
    p = p->next;
  }
  printf("\n");
}
//4,链表节点查找
bool D_Lool_List_Search(D_NODE *head, int data)
{
  int i = 1;
  D_NODE *p = head->next;
  while(p != head)
  {
    if (p->data == data)
    {
      printf("找到这个节点!,节点序号[%d]\n", i);
      return true;
    }
    p = p->next;
    i++;
  }
  printf("链表中没有这个节点!\n");
}

//5,链表节点删除
bool D_Loop_List_Remove(D_NODE *head, int data)
{
  int i = 1;
  D_NODE *p = head->next;
  while(p != head)
  {
    if (p->data == data)
    {
      printf("删除这个节点!,节点序号[%d]\n", i);
      p->prev->next = p->next;
      p->next->prev = p->prev;
      free(p);
      return true;
    }
    p = p->next;
    i++;
  }
  printf("链表中没有这个节点!\n"); 

}

//6,链表的销毁
void D_Lool_List_Destroy(D_NODE *head)
{
  int i = 0;
  D_NODE *p = head;
  while(p->next != head)
  {
    p = p->next;
    free(p->prev);
    i++;
  }
  free(p);
  printf("销毁链表成功,一共释放[%d]个节点!\n", i);
}

/*双向循环链表*/
int main(int argc, char const *argv[])
{
  int i, num;

  D_NODE *dl_list = D_Loop_List_Create();

  for(i=0; i<5; i++)
  {
    scanf("%d", &num);
    D_Loop_List_Insert_End(dl_list, num);
    D_Loop_List_Display(dl_list);
  }

  printf("请输入需要查找的数据:");
  scanf("%d", &num);
  D_Lool_List_Search(dl_list, num); 

  printf("请输入需要删除的数据:");
  scanf("%d", &num);
  D_Loop_List_Remove(dl_list, num);   

  D_Lool_List_Destroy(dl_list);

  return 0;
}




相关文章
|
22天前
|
存储 Java 索引
【数据结构】链表从实现到应用,保姆级攻略
本文详细介绍了链表这一重要数据结构。链表与数组不同,其元素在内存中非连续分布,通过指针连接。Java中链表常用于需动态添加或删除元素的场景。文章首先解释了单向链表的基本概念,包括节点定义及各种操作如插入、删除等的实现方法。随后介绍了双向链表,说明了其拥有前后两个指针的特点,并展示了相关操作的代码实现。最后,对比了ArrayList与LinkedList的不同之处,包括它们底层实现、时间复杂度以及适用场景等方面。
41 10
【数据结构】链表从实现到应用,保姆级攻略
|
1月前
|
存储 Java 程序员
"揭秘HashMap底层实现:从数组到链表,再到红黑树,掌握高效数据结构的秘密武器!"
【8月更文挑战第21天】HashMap是Java中重要的数据结构,采用数组+链表/红黑树实现,确保高效查询与更新。构造方法初始化数组,默认容量16,负载因子0.75触发扩容。`put`操作通过计算`hashCode`定位元素,利用链表或红黑树处理冲突。`get`和`remove`操作类似地定位并返回或移除元素。JDK 1.8优化了链表转红黑树机制,提升性能。理解这些原理能帮助我们更高效地应用HashMap。
32 0
|
1月前
|
存储 算法
【初阶数据结构篇】顺序表和链表算法题
此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
|
1月前
|
存储 测试技术
【初阶数据结构篇】双向链表的实现(赋源码)
因为头结点的存在,plist指针始终指向头结点,不会改变。
|
1月前
|
存储 测试技术
【初阶数据结构篇】单链表的实现(附源码)
在尾插/尾删中,都需要依据链表是否为空/链表是否多于一个节点来分情况讨论,目的是避免对空指针进行解引用造成的错误。
|
1月前
|
算法
【数据结构与算法】共享双向链表
【数据结构与算法】共享双向链表
12 0
|
1天前
|
存储
|
16天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
18天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
117 3
|
19天前
|
Java
【数据结构】栈和队列的深度探索,从实现到应用详解
本文介绍了栈和队列这两种数据结构。栈是一种后进先出(LIFO)的数据结构,元素只能从栈顶进行插入和删除。栈的基本操作包括压栈、出栈、获取栈顶元素、判断是否为空及获取栈的大小。栈可以通过数组或链表实现,并可用于将递归转化为循环。队列则是一种先进先出(FIFO)的数据结构,元素只能从队尾插入,从队首移除。队列的基本操作包括入队、出队、获取队首元素、判断是否为空及获取队列大小。队列可通过双向链表或数组实现。此外,双端队列(Deque)支持两端插入和删除元素,提供了更丰富的操作。
23 0
【数据结构】栈和队列的深度探索,从实现到应用详解