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

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

一、单向循环链表

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

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;
}




相关文章
|
7月前
|
存储 Python
Python 实现单向链表,和单向链表的反转
链表是一种数据结构,每个节点存储相邻节点的位置信息。单链表中的节点仅存储下一节点的位置。通过Python实现单链表,定义`ListNode`类并关联节点可创建链表。例如,创建A-&gt;B-&gt;C的链表后,可通过反转函数`reverse`将链表反转为CBA。代码展示了如何实现和操作单链表。
139 6
Python 实现单向链表,和单向链表的反转
|
11月前
|
存储
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)(一)
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)
128 1
|
11月前
|
算法 Java
数据结构与算法学习六:单向环形链表应用实例的约瑟夫环问题
这篇文章通过单向环形链表的应用实例,详细讲解了约瑟夫环问题的解决方案,并提供了Java代码实现。
153 0
|
11月前
|
存储 缓存
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)(二)
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)
103 0
|
10月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
856 9
|
10月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
215 59
|
3月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
44 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
8月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
331 77
|
7月前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
152 11