当前位置: 首页 > news >正文

【探索数据结构与算法】——深入了解双向链表(图文详解)

目录

一、双向链表的基本概念 ​​​

二、双向链表的结构

三、双向链表的基本操作实现方法 

1.双向链表的初始化

2.双向链表的头插

3.双向链表的尾插

6.查找节点

7.在指定位置之前插入节点

8.删除指定位置节点

9.打印链表数据 

 10.双向链表销毁

四、完整代码实现 

LIst.h

List.c


💓 博客主页:C-SDN花园GGbond

⏩ 文章专栏:探索数据结构与算法

一、双向链表的基本概念 ​​​

双向链表是一种常见的数据结构,与单向链表类似,但它允许我们从两个方向遍历链表:向前和向后。每个节点包含三个部分:一个数据元素和两个指针,一个指向链表中的前一个节点,另一个指向链表中的下一个节点。

一般情况下,我们所说的双向链表指的是带头节点,双向,循环链表,以下若无特殊说明,均代表此含义。

二、双向链表的结构

双向链表中的每个节点通常包含以下部分:

  1. 数据元素:可以是任何类型的数据,如整数、浮点数、字符串或对象。
  2. prev 指针:指向前一个节点的指针。
  3. next 指针:指向下一个节点的指针。相比单链表只有独立存在的每个节点,双向链表多了哨兵位节点,该节点作为头结点,不存储有效数据,只有指向第一个有效节点的next指针和指向尾节点的prev指针。
    只要链表存在,哨兵位节点就存在

 相比单链表只有独立存在的每个节点,双向链表多了哨兵位节点,该节点作为头结点,不存储有效数据,只有指向第一个有效节点的next指针和指向尾节点的prev指针。
只要链表存在,哨兵位节点就存在

三、双向链表的基本操作实现方法 

  1. 初始化顺序表中的数据。
  2. 对顺序表进行头插(开头插入数据)。
  3. 对顺序表进行尾插插(末尾插入数据)。
  4. 对顺序表进行头删(开头删除数据)。
  5. 对顺序表进行尾删(末尾删除数据)。
  6. 对顺序表进行查找。
  7. 7.在指定位置之前插入节点
  8. 删除指定位置节点。
  9. 打印顺序表中的数据。
  10. .双向链表销毁
1.双向链表的初始化

双向链表的初始化也就是创建一个哨兵位节点,所以实现初始化之前需要先封装一个节点申请函数

单独封装的申请节点函数

  • 动态申请节点大小的空间
  • 判空
  • 通过形参接受的数据初始化节点数据部分
  • next指针和prev指针都指向自身(因为双向链表是循环链表)
  • 返回节点地址

LTNode* NewNode(LTDataType x)//申请节点
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("NewNode\n");exit(1);}newnode->data = x;//根据参数初始化数据newnode->next = newnode->prev = newnode;//两个指针初始都指向自身return newnode;
}

双向链表初始化

  • 调用申请节点函数创建一个哨兵位节点,作为链表的头结点
  • 返回链表头结点地址

注意:

双向链表因为有哨兵位的存在,链表始终不为空,哨兵位节点不会被改变,所以不再需要传递地址,以及用二级指针接收。这一点区别于单链表实现

LTNode* LTInit()            //链表初始化
{LTNode* newnode = NewNode(-1);return newnode;
}
2.双向链表的头插

双向链表头插

  • 头插就是将新节点插入到哨兵位节点和第一个有效节点之间
  • 形参接收一个哨兵位地址和要插入的数据
  • 首先对哨兵位地址判空,判断链表结构是否正常
  • 申请新节点,存入要插入的数据

  • 接下来是移动指针

  • 一般情况下应当先调整新节点的指针,因为新节点指针调整不会影响到原链表:

  • 新节点prev指向哨兵位,next指向哨兵位的next所指向节点

  • 然后,调整第一个有效节点的prev指针,不再指向哨兵位而是指向新节点;调整哨兵位

  • next指针,不再指向原来的第一个有效节点,而是指向新节点

  • 新节点插入完成

void LTPushFront(LTNode* phead, LTDataType x)//头插
{assert(phead);//判空LTNode* newnode = NewNode(x);//申请节点newnode->next = phead->next;//改变新节点指针指向newnode->prev = phead;phead->next->prev = newnode;//改变原链表相关节点指针指向phead->next = newnode;}

3.双向链表的尾插

双向链表尾删

  • 尾删就是将双向链表的最后一个有效节点删除
  • (尾删的前提是至少存在一个有效节点)
  • 首先创建一个指针del指向要删除的节点(哨兵位prev指向节点)
  • 然后将倒数第二个节点的next指针指向哨兵位
  • 将哨兵位的prev指针指向倒数第二个节点
  • 指针修改完成,此时倒数第二个节点成为最后一个节点,释放del指向的节点
  • (如果删除之前链表只有一个节点,删除完之后只剩下一个哨兵位节点,两个指针都指向自己)

void LTPopBack(LTNode* phead)		//尾删
{assert(phead && phead->next != phead);//判空LTNode* del = phead->prev;//暂时存储要删除的节点del->prev->next = phead;//移动指针phead->prev = del->prev;free(del);//释放节点空间del = NULL;
}
4.双向链表的头删 

双向链表头删

  • 头删就是将双向链表第一个有效节点删除
  • (头删的前提是链表至少存在一个有效节点)
  • 首先创建一个指针del指向要删除的节点(哨兵位节点next指向的节点)
  • 然后将第二个有效节点的prev指针指向哨兵位节点
  • 哨兵位节点的next指针指向第二个有效节点
  • 指针修改完成,此时第二个有效节点成为链表的第一个有效节点,释放del指向的节点
  • (如果删除之前链表只有一个节点,删除完之后只剩下一个哨兵位节点,两个指针都指向自己)

void LTPopFront(LTNode* phead)		//头删
{assert(phead && phead->next != phead);//判空LTNode* del = phead->next;//暂时存储要删除的节点del->next->prev = phead;//移动指针phead->next = del->next;free(del);//释放节点空间del = NULL;
}

5.双向链表的尾删

双向链表尾删

  • 尾删就是将双向链表的最后一个有效节点删除
  • (尾删的前提是至少存在一个有效节点)
  • 首先创建一个指针del指向要删除的节点(哨兵位prev指向节点)
  • 然后将倒数第二个节点的next指针指向哨兵位
  • 将哨兵位的prev指针指向倒数第二个节点
  • 指针修改完成,此时倒数第二个节点成为最后一个节点,释放del指向的节点
  • (如果删除之前链表只有一个节点,删除完之后只剩下一个哨兵位节点,两个指针都指向自己)

void LTPopBack(LTNode* phead)		//尾删
{assert(phead && phead->next != phead);//判空LTNode* del = phead->prev;//暂时存储要删除的节点del->prev->next = phead;//移动指针phead->prev = del->prev;free(del);//释放节点空间del = NULL;
}

6.查找节点

双向链表查找(根据数据查找节点)

  • 首先对哨兵位地址判空,否则可能对空地址解引用
  • 创建一个遍历链表的指针,从第一个有效节点开始,将节点数据与要查找的数据进行比对,如果相同返回节点地址
  • 出循环,说明未找到,返回NULL
LTNode* LTFind(LTNode* phead, LTDataType x)//查找节点
{assert(phead);//判空LTNode* pcur = phead->next;//遍历链表的指针while (pcur!= phead){if (pcur->data == x)return pcur;pcur = pcur->next;}return NULL;
}
7.在指定位置之前插入节点

双向链表在指定位置之前插入节点:

插入的前提是指定位置存在
申请新节点,存入要插入的数据
调整指针:
临时创建一个指针prev指向指定位置节点的perv指向节点
新节点next指针指向指定位置节点,prev指针指向prev节点
然后再修改原链表指针:
指定位置节点的prev指针指向新节点,prev节点的next指针指向新节点
新节点插入完成

void LTInsert(LTNode* pos, LTDataType x)//在pos位置之前插入数据
{assert(pos);//判空LTNode* newnode = NewNode(x);//申请新节点newnode->next = pos;//改变新节点指针指向newnode->prev = pos->prev;pos->prev->next = newnode;//改变原链表相关节点指针指向pos->prev = newnode;}

8.删除指定位置节点

双向链表删除指定位置节点

  • 删除指定节点的前提是该节点必须存在
  • 首先创建一个指针del指向要删除的节点
  • 然后将该节点的前一个结点的next指针指向它的后一个节点,后一个节点的prev指针指向他的前一个节点
  • 释放该节点
  • (如果删除之前链表只有一个节点,删除完之后只剩下一个哨兵位节点,两个指针都指向自己)

void LTErase(LTNode* pos)//删除指定位置节点
{assert(pos);//判空pos->next->prev = pos->prev;//改变要删除节点的前后节点指针指向pos->prev->next = pos->next;free(pos);pos = NULL;}

9.打印链表数据 

双向链表数据打印

  • 首先对地址判空,防止对空地址解引用
  • 创建一个遍历链表的指针,从第一个有效节点打印数据,然后向后移动,直到指针遍历到哨兵位
void LTPrint(LTNode* phead)  //链表数据打印
{assert(phead);//判空LTNode* pcur = phead->next;//遍历链表的指针while (pcur != phead)//打印数据{printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}
 10.双向链表销毁

双向链表的销毁

从第一个有效节点开始,创建指针循环遍历链表:
创建next指针临时保存下一个节点,释放本节点,遍历指针指向next节点
循环至有效节点全部被释放
然后将哨兵位节点释放,指针置空,销毁完成
注意:

为了与其他配套函数的参数保持一致,这里的参数本该用二级指针接收,却用一级指针接收。出函数后,需要手动将哨兵位指针置空

oid LTDestroy(LTNode* phead)//链表销毁
{assert(phead);//判空LTNode* pcur = phead->next;//遍历链表的指针while (pcur != phead)//从第一个有效节点开始,逐个释放节点{LTNode* next = pcur->next;free(pcur);pcur = next;}free(phead);//释放哨兵位节点phead = NULL;}

四、完整代码实现 

LIst.h
//List.h 双链表头文件
#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;//数据类型重定义,可以是任意数据类型
typedef struct ListNode//双向链表节点结构
{LTDataType data;//数据struct ListNode* prev;//指向前一个节点的指针struct ListNode* next;//指向下一个节点的指针
}LTNode;LTNode* NewNode(LTDataType x);//申请节点
LTNode* LTInit();             //链表初始化void LTPushFront(LTNode* phead, LTDataType x);//头插
void LTPushBack(LTNode* phead, LTDataType x);//尾插void LTPopBack(LTNode* phead);		//尾删
void LTPopFront(LTNode* phead);		//头删LTNode* LTFind(LTNode* phead, LTDataType x);//查找节点void LTInsert(LTNode* pos, LTDataType x);//在pos位置之后插入数据
void LTErase(LTNode* pos);//删除指定位置节点void LTPrint(LTNode* phead);  //链表数据打印
void LTDestroy(LTNode* phead);//链表销毁
List.c
//DouSList.c  双链表源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"DouSList.h"LTNode* NewNode(LTDataType x)//申请节点
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("NewNode\n");exit(1);}newnode->data = x;//根据参数初始化数据newnode->next = newnode->prev = newnode;//两个指针初始都指向自身return newnode;
}
LTNode* LTInit()            //链表初始化
{LTNode* newnode = NewNode(-1);return newnode;
}
void LTPushFront(LTNode* phead, LTDataType x)//头插
{assert(phead);//判空LTNode* newnode = NewNode(x);//申请节点newnode->next = phead->next;//改变新节点指针指向newnode->prev = phead;phead->next->prev = newnode;//改变原链表相关节点指针指向phead->next = newnode;}
void LTPushBack(LTNode* phead, LTDataType x)//尾插
{assert(phead);//判空LTNode* newnode = NewNode(x);//申请节点newnode->next = phead;//改变新节点指针指向newnode->prev = phead->prev;phead->prev->next = newnode;//改变原链表相关节点指针指向phead->prev = newnode;
}void LTPopFront(LTNode* phead)		//头删
{assert(phead && phead->next != phead);//判空LTNode* del = phead->next;//暂时存储要删除的节点del->next->prev = phead;//移动指针phead->next = del->next;free(del);//释放节点空间del = NULL;
}void LTPopBack(LTNode* phead)		//尾删
{assert(phead && phead->next != phead);//判空LTNode* del = phead->prev;//暂时存储要删除的节点del->prev->next = phead;//移动指针phead->prev = del->prev;free(del);//释放节点空间del = NULL;
}LTNode* LTFind(LTNode* phead, LTDataType x)//查找节点
{assert(phead);//判空LTNode* pcur = phead->next;//遍历链表的指针while (pcur!= phead){if (pcur->data == x)return pcur;pcur = pcur->next;}return NULL;
}void LTInsert(LTNode* pos, LTDataType x)//在pos位置之前插入数据
{assert(pos);//判空LTNode* newnode = NewNode(x);//申请新节点newnode->next = pos;//改变新节点指针指向newnode->prev = pos->prev;pos->prev->next = newnode;//改变原链表相关节点指针指向pos->prev = newnode;}void LTErase(LTNode* pos)//删除指定位置节点
{assert(pos);//判空pos->next->prev = pos->prev;//改变要删除节点的前后节点指针指向pos->prev->next = pos->next;free(pos);pos = NULL;}void LTPrint(LTNode* phead)  //链表数据打印
{assert(phead);//判空LTNode* pcur = phead->next;//遍历链表的指针while (pcur != phead)//打印数据{printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}void LTDestroy(LTNode* phead)//链表销毁
{assert(phead);//判空LTNode* pcur = phead->next;//遍历链表的指针while (pcur != phead)//从第一个有效节点开始,逐个释放节点{LTNode* next = pcur->next;free(pcur);pcur = next;}free(phead);//释放哨兵位节点phead = NULL;}

相关文章:

【探索数据结构与算法】——深入了解双向链表(图文详解)

目录 一、双向链表的基本概念 ​​​ 二、双向链表的结构 三、双向链表的基本操作实现方法 1.双向链表的初始化 2.双向链表的头插 3.双向链表的尾插 6.查找节点 7.在指定位置之前插入节点 8.删除指定位置节点 9.打印链表数据 10.双向链表销毁 四、完整代码实现 …...

linux常用命令备忘录

一、常用命令 查看被占用进程&#xff1a;ps ef|grep 11612 查看当前目录&#xff1a;pwd 查看文件的md5&#xff1a; &#xff08;linux&#xff09;md5sum 文件名 &#xff08;windows&#xff09;certutil -hashfile some_file MD5 查看当前目录的文件大小&#xff1a…...

【C++进阶学习】第十二弹——C++ 异常处理:深入解析与实践应用

前言&#xff1a; 在C编程语言中&#xff0c;异常处理是一种重要的机制&#xff0c;它允许程序员在运行时捕获和处理错误或异常情况。本文将详细介绍C异常处理的相关知识点&#xff0c;包括异常的定义、抛出与捕获、异常处理的原则、以及在实际编程中的应用。 目录 1. 异常处理…...

《算法竞赛进阶指南》0x23剪枝

剪枝&#xff0c;就是减少搜索树的规模、尽可能排除搜索书中不必要的分支的一种手段。形象地看&#xff0c;就好像剪掉了搜索树的枝条&#xff0c;故被称为“剪枝”。在深度优先搜索中&#xff0c;有以下常见的剪枝方法。 1.优化搜索顺序 在一些搜索问题中&#xff0c;搜索树的…...

同态加密和SEAL库的介绍(三)BFV - Batch Encoder

写在前面&#xff1a; 在上一篇中展示了如何使用 BFV 方案执行一个非常简单的计算。该计算在 plain_modulus 参数下进行&#xff0c;并且仅使用了 BFV 明文多项式中的一个系数。这种方法有两个显著的问题&#xff1a; 实际应用通常使用整数或实数运算&#xff0c;而不是模运算…...

Docker 环境下使用 Traefik v3 和 MinIO 快速搭建私有化对象存储服务

上一篇文章中&#xff0c;我们使用 Traefik 新版本完成了本地服务网关的搭建。接下来&#xff0c;来使用 Traefik 的能力&#xff0c;进行一系列相关的基础设施搭建吧。 本篇文章&#xff0c;聊聊 MinIO 的单独使用&#xff0c;以及结合 Traefik 完成私有化 S3 服务的基础搭建…...

玛雅房产系统源码开发与技术功能解析

引言 随着房地产市场的蓬勃发展&#xff0c;房产管理系统&#xff08;Real Estate Management System, REMS&#xff09;作为提升行业效率、优化资源配置的关键工具&#xff0c;其重要性日益凸显。房产系统源码开发不仅涉及复杂的业务逻辑处理&#xff0c;还融合了先进的软件开…...

c++----初识模板

大家好&#xff0c;这篇博客想与大家分享一些我们c中比较好用的知识点。模板。首先咧&#xff0c;我们都知道模板嘛&#xff0c;就是以前人的经验总结出来的知识。方便我们使用。这里的模板也是一样的。当我们学习过后&#xff0c;对于一些在c中的自定义函数&#xff0c;我们在…...

SpringBoot3热部署

引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional> </dependency> 默认就是,无需配置 可以了…...

J. 二进制与、平方和

https://codeforces.com/gym/104095/problem/J 分析操作一 1&00 ,0&10&#xff0c;ai<qmi(2,24),说明每个数最多操作25次 维护区间或和&#xff0c;orsum & x orsum 就不用递归下去了 势能线段树code // Problem: J. 二进制与、平方和 // Contest: Codeforc…...

LVS中NAT模式和DR模式实战讲解

1DR模式 DR&#xff1a;Direct Routing&#xff0c;直接路由&#xff0c;LVS默认模式,应用最广泛,通过为请求报文重新封装一个MAC首部进行 转发&#xff0c;源MAC是DIP所在的接口的MAC&#xff0c;目标MAC是某挑选出的RS的RIP所在接口的MAC地址&#xff1b;源 IP/PORT&#xf…...

写给小白程序员的一封信

文章目录 1.编程小白如何成为大神&#xff1f;大学新生的最佳入门攻略2.程序员的练级攻略3.编程语言的选择4.熟悉Linux5.学会git6.知道在哪寻求帮助7.多结交朋友8.参加开源项目9.坚持下去 1.编程小白如何成为大神&#xff1f;大学新生的最佳入门攻略 编程已成为当代大学生的必…...

Leaf分布式ID

文章目录 系统对Id号的要求UUIDsnowflakeLeafLeaf-snowflakeLeaf-segmentMySQL自增主键segment双buffer 系统对Id号的要求 1、业务 1&#xff09;全局唯一性&#xff1a;不能出现重复的ID号&#xff0c;既然是唯一标识&#xff0c;这是最基本的要求 2&#xff09;趋势递增&a…...

Starrocks解析json数组

json数据 [{"spec": "70g/支","unit": "支","skuId": "1707823848651276346","amount": 6,"weight": 70,"spuName": "伊利 甄稀 苦咖啡味雪糕 流心冰淇淋 70g/支",&quo…...

安卓基本布局(下)

TableLayout 常用属性描述collapseColumns设置需要被隐藏的列的列号。shrinkColumns设置允许被伸缩的列的列号。stretchColumns设置允许被拉伸的列的列号。 <TableLayout xmlns:android"http://schemas.android.com/apk/res/android"android:id"id/TableL…...

Python中使用正则表达式

摘要&#xff1a; 正则表达式&#xff0c;又称为规则表达式&#xff0c;它不是某种编程语言所特有的&#xff0c;而是计算机科学的一个概念&#xff0c;通常被用来检索和替换某些规则的文本。 一.正则表达式的语法 ①行定位符 行定位符就是用来描述字符串的边界。"^&qu…...

三大口诀不一样的代码,小小的制表符和换行符玩的溜呀

# 小案例&#xff0c;打印输出加法口诀 for i in range(1,10):for j in range(1,10):if j>i:breakprint(f"{j}{i}{ji}".strip(),end\t)print() print(\n) for i in range(1,10):for j in range(1,10):if j>i:breakprint(f"{j}x{i}{j*i}",end\t)print…...

[qt] 线程等待与唤醒

对于生产者与消费者的数据处理的另一种好的解决方法是使用QWaitCondition类,允许线程在一定的条件下唤醒其他多个线程来共同处理。 一 定义公共变量 DataSize: 生产者生产数据的大小BufferSize: 也就是这个缓冲区的大小,每个单元是一个int&#xff0c;也有可能是一个链表,结构…...

Springboot 实现 Modbus Rtu 协议接入物联网设备

Modbus RTU 技术教程 引言 Modbus是一种开放标准的通信协议,它最初由Modicon(现施耐德电气)在1979年发布,旨在让可编程逻辑控制器(PLC)之间能够进行通信。随着时间的发展,Modbus已经成为工业自动化领域中最常用的通信协议之一,尤其适用于连接工业电子设备。本文将详细…...

鸿蒙笔记--装饰器

这一节主要了解一下鸿蒙里的装饰器,装饰器是一种特殊的语法结构&#xff0c;用于装饰类、结构体、方法以及变量; 1 Component在鸿蒙&#xff08;HarmonyOS&#xff09;开发中扮演着重要角色&#xff0c;主要用于定义可重用的UI组件,主要作用:1)组件化&#xff1a;Component装饰…...

【反无人机检测】C2FDrone:基于视觉Transformer网络的无人机间由粗到细检测

C2FDrone&#xff1a;基于视觉Transformer网络的无人机间由粗到细检测 C2FDrone: Coarse-to-Fine Drone-to-Drone Detection using Vision Transformer Networks 论文链接 摘要 摘要——基于视觉的无人机间检测系统在碰撞规避、反制敌对无人机和搜救行动等应用中至关重要。然…...

从理论崩塌到新路径:捷克科学院APL Photonics论文重构涡旋光技术边界

理论预言 vs 实验挑战 光子轨道角动量&#xff08;Orbital Angular Momentum, OAM&#xff09;作为光场调控的新维度&#xff0c;曾被理论预言可突破传统拉曼散射的对称性限制——尤其是通过涡旋光&#xff08;如拉盖尔高斯光束&#xff09;激发晶体中常规手段无法探测的"…...

DAY43打卡

浙大疏锦行 kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 进阶&#xff1a;并拆分成多个文件 fruit_cnn_project/ ├─ data/ # 存放数据集&#xff08;需手动创建&#xff0c;后续放入图片&#xff09; │ ├─ train/ …...

ComfyUI 对图片进行放大的不同方法

本篇里 ComfyUI Wiki将讲解 ComfyUI 中几种基础的放大图片的办法,我们时常会因为设备性能问题,不能一次性生成大尺寸的图片,通常会先生成小尺寸的图像然后再进行放大。 不同的放大图片方法有不同的特点,以下是本篇教程将会涉及的方法: 像素重新采样SD 二次采样放大使用放…...

SQL进阶之旅 Day 19:统计信息与优化器提示

【SQL进阶之旅 Day 19】统计信息与优化器提示 文章简述 在数据库性能调优中&#xff0c;统计信息和优化器提示是两个至关重要的工具。统计信息帮助数据库优化器评估查询成本并选择最佳执行计划&#xff0c;而优化器提示则允许开发人员对优化器的行为进行微调。本文深入探讨了…...

java教程笔记(十一)-泛型

Java 泛型&#xff08;Generics&#xff09;是 Java 5 引入的重要特性之一&#xff0c;它允许在定义类、接口和方法时使用类型参数。泛型的核心思想是将类型由具体的数据类型推迟到使用时再确定&#xff0c;从而提升代码的复用性和类型安全性。 1.泛型的基本概念 1. 什么是泛…...

当丰收季遇上超导磁测量:粮食产业的科技新征程

麦浪藏光阴&#xff0c;心田种丰年&#xff01;又到了一年中最令人心潮澎湃的粮食丰收季。金色的麦浪随风翻滚&#xff0c;沉甸甸的稻穗谦逊地低垂着&#xff0c;处处洋溢着丰收的喜悦。粮食产业&#xff0c;无疑是国家发展的根基与命脉&#xff0c;是民生稳定的压舱石。在现代…...

Vue.js 组件:深入理解与实践

Vue.js 组件:深入理解与实践 引言 随着前端技术的不断发展,Vue.js 作为一种流行的前端框架,因其简洁、易学、高效的特点受到越来越多开发者的青睐。在Vue.js中,组件是构建用户界面的基石。本文将深入探讨Vue.js组件的概念、特性、创建方式以及在实际开发中的应用,帮助读…...

Django CMS 的 Demo

以下是关于 Django CMS 的 Demo 示例及相关资源的整理 安装与运行 Django CMS 示例 使用 djangocms-installer 快速创建 Django CMS 项目&#xff1a; pip install django_cms djangocms -p . mysite安装记录 pip install django-cms Looking in indexes: https://pypi.tun…...

Android协程学习

目录 Android上的Kotlin协程介绍基本概念与简单使用示例协程的高级用法 结构化并发线程调度器(Dispatchers)自定义调度器并发:同步 vs 异步 异步并发(async 并行执行)同步顺序执行协程取消与超时 取消机制超时控制异步数据流 Flow协程间通信 使用 Channel使用 StateFlow /…...