【数据结构与算法】数据结构初阶:详解顺序表和链表(一)

🔥个人主页:艾莉丝努力练剑

❄专栏传送门:《C语言》、《数据结构与算法》

🍉学习方向:C/C++方向

⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平 


前言:上篇文章我们介绍了复杂度的概念,我们通过一个个经典的例子 ,对时间复杂度和空间复杂度的概念进行了剖析,结合图像,让大家更加直观地理解知识点,我们对比了常见的复杂度,展示了各种各样的排序算法的复杂度表格,通过轮转数组这一道力扣题向大家展示了“算法思路有很多种”名不虚传,我们通过三种不同的思路(超出时间限制、通过、时间复杂度O(n)空间复杂度O(1)的情况下通过)详解了算法和复杂度的关系。

本篇文章,我们就要开始介绍顺序表和链表相关的知识点了,在初阶的数据结构与算法阶段,我们把知识点分成三部分,复杂度作为第一部分,顺序表和链表、栈和队列、二叉树为第二部分,排序为第二部分,我们已经介绍完了第一部分:算法复杂度,从本文开始,我们就正式进入第二部分中的顺序表和链表部分内容的学习啦。


目录

正文

一、线性表

二、顺序表

(一)顺序表的概念

(二)顺序表的分类

1、静态顺序表

2、动态顺序表

(三)顺序表的结构

(四)动态顺序表的实现

动态顺序表的实现(包含尾插)

(1)SeqList.h

 (2)SeqList.c

 (3)test.c

(五)详解顺序表书写

1、静态顺序表 

2、动态顺序表

(1)尾插

结尾 


 


正文

一、线性表

线性表(Linear List)是指n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串……

线性表在逻辑上是线性结构,也就是说是连续的一条直线。但是在物理上不一定,线性表在物理上的存储形式通常是数组和链式结构。

由此可见线性表还可分为两种结构:线性结构和逻辑结构。

所谓逻辑结构就是人为想象出来的。

二、顺序表

(一)顺序表的概念

概念:顺序表是一段用物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。那么我们可不可以说,数组就是顺序表?或者说,顺序表就是数组?可以这么说吗?如果不能这么说,那么数组和顺序表又有什么区别呢?

顺序表的底层结构是数组,对数组的封装,实现了常见的增删查改等接口

换句话说,数据结构完成的就是顺序表的增删查改等接口函数的实现。

举个例子,数组和顺序表的关系可以拿苍蝇小馆和米其林五星级餐厅类比一下——

(二)顺序表的分类

顺序表分为静态顺序表动态顺序表

1、静态顺序表

静态顺序表就是使用定长数组存储元素

静态顺序表缺陷:空间给少了不够用,给多了造成空间浪费。

2、动态顺序表

动态顺序表就是按需申请空间存储进行存储。

(三)顺序表的结构

我们分别介绍静态顺序表和动态顺序表的结构——

(四)动态顺序表的实现

静态顺序表和动态顺序表,我们这里就实现一下动态顺序表,但并不是说静态顺序表就没有学习的必要了。这一点要明确。

我们要分成三个文件来实现,因为是顺序表,我们就取个名字叫"SeqList",用三个文件实现一个功能我们在扫雷游戏那里就接触过了,我们简单分析一下三个文件各自的分工:

我们分别创建三个文件:SeqList.h头文件、SeqList.c文件、test.c测试文件——

下面我们会分别介绍三个文件的书写,每个小标题下面的第一个代码是动态顺序表的代码实现。在代码下面则是抽丝剥茧展开来的详解。 

动态顺序表的实现(包含尾插)
(1)SeqList.h
#pragma once
#include<stdio.h>//定义动态顺序表的结构
typedef int SLDataType;
typedef struct SeqList
{SLDataType* arr;//存储数据int size;//有效数据个数int capacity;//空间大小
}SL;//typedef struct SeqList SL;void SLInit(SL s);
 (2)SeqList.c
#define  _CRT_SECURE_NO_WARNINGS  1#include"SeqList.h"
#include"stdio.h"
#include"test.c"
#include<stdlib.h>void SLInit(SL* ps)
{ps->arr = NULL;ps->size = ps->capacity = 0;
}//尾插
void SLPushBack(SL* ps, SLDataType x)
{//空间不够if (ps->size == ps->capacity){int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//增容SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity);if (tmp == NULL);{perror("realloc fail!");exit(1);}ps->arr = tmp;ps->capacity = newCapacity;}//空间足够ps->arr[ps->size++] = x;
}
 (3)test.c
#define  _CRT_SECURE_NO_WARNINGS  1#include"SeqList.h"void test01()
{SL sl;SLInit(&sl);//具备了一个空的顺序表SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushBack(&sl, 4);
}int main()
{test01();return 0;
}

(五)详解顺序表书写

这里涉及到了结构体的知识,我们学习数据结构要对结构体有足够的认识。

#pragema once#define  N  100
typedef int SQDataType;struct SeqList
{SQDataType a[N];int size;
}//增删查改等接口函数

这里.h文件的第一行我们看到的代码的作用,博主在C语言专栏的这篇文章里面介绍过——预处理深入详解:预定义符号、宏、命名约定、命令行定义、条件编译、头文件的包含

我给大家截过来看一下—— 

#pragma once

功能就是可以避免头文件被重复包含。

如果你不想这样定义,也可以写成这样,作用是等价的——

#ifdef __TEST_H__
#define __TEST_H__#define  N  100
typedef int SQDataType;struct SeqList
{SQDataType a[N];int size;
}//增删查改等接口函数
#endif  //__TEST_H__

我们定义的时候要注意,为什么这里数据类型不用define来定义?类型我们一般用typedef来定义的,定义一个普通的常量,我们是用#define,比如这里我们定义N是一个常量100。

#define  N  100

我们这样定义的好处就是不需要再在代码中一个一个去修改了——

#pragema oncestruct SeqList
{int a[10];int size;
}

如果我们想顺序表存储的不是10,而是100,或者1000,我们一般这样定义:

#pragema once#define  N  100struct SeqList
{SQDataType a[N];int size;
}

a[ ]里面放N,当我们想改的时候,直接在定义里面把N改一下就可以了,不用在代码里面一个一个去改。像顺序表存储的数据类型,我们这样定义——

typedef int SLDataType;

我们这样定义有什么好处?很简单,比如我们上面的头文件里面定义了一个数据类型叫SLDATAType,现在在代码里面我们定义它是int类型,如果我要存的是char类型,我把这里改成char就行了,如果我要存的是double,我把这里改成double就可以了。

typedef char SLDataType;
typedef float SLDataType;
typedef double SLDataType;

是不是这个道理?这样我们就不需要考虑别的,直接在定义里面修改就可以了。这就是我们这样定义的好处。

定义完了,我们就要进行一下初始化。

初始化我们可以考虑用一个循环来初始化,也可以像下面这样用一个memset函数来初始化,memset函数是按字节对其进行初始化。

#pragema once#define  N  100
typedef int SQDataType;struct SeqList
{SQDataType a[N];int size;
}//增删查改等接口函数
void SeqListInit(struct SeqList sl);

我们C语言里面结构体不能直接写结构体名称,uu们是不是觉得这样太长了,我们可以简写:

#ifdef __TEST_H__
#define __TEST_H__#define  N  100
typedef int SQDataType;struct SeqList
{SQDataType a[N];int size;
}
typedef struct SeqList SL;
void SeqListInit(struct SeqList sl);
//增删查改等接口函数
#endif  //__TEST_H__

当然我们还可以这样写(有很多书上包括很多老师是这样写的) :

#ifdef __TEST_H__
#define __TEST_H__#define  N  100
typedef int SQDataType;typedef struct SeqList
{SQDataType a[N];int size;
}SL;
void SeqListInit(struct SeqList sl);
//增删查改等接口函数
#endif  //__TEST_H__

那我们就可以不写那么长一截了,直接写简写“SL”——

#ifdef __TEST_H__
#define __TEST_H__#define  N  100
typedef int SQDataType;typedef struct SeqList
{SQDataType a[N];int size;
}SL;
void SeqListInit(SL sl);
//增删查改等接口函数
#endif  //__TEST_H__

是不是简单多了?这里再提醒一下uu们,我们敲代码不要一股脑敲完了再测试,我们可以敲一个测试一个,代码如果很长,一股脑写完可能出问题了一时半会儿找不到bug,这是一个很好的代码书写习惯,是前辈们总结出来的经验教训,大家也应当学习一下这种书写方式。 

写程序的时候,奋笔疾书、一顿操作,写完一运行几十个错误,再反复调试了一两个小时,再一运行,又报了几十个错误,自己都快崩溃了。 

那我们写一个测试一个,有问题也只在这几行代码里面,方便我们查找,犯不上自己吓自己~

顺序表就是数组嘛,uu们可能觉得这个N还不好理解,我们可以在定义的时候写成这样——

#ifdef __TEST_H__
#define __TEST_H__#define  MAX_SIZE  100
typedef int SQDataType;typedef struct SeqList
{SQDataType a[MAX_SIZE];int size;
}SL;
void SeqListInit(SL s);
//增删查改等接口函数
#endif  //__TEST_H__

这样是不是更容易理解,我们一看便知,这是数组最大的大小。

#include"SeqList.h"void SeqListInit(SL s)
{memset(sl.a,0,sizeof(SQDataType)*MAX_SIZE);sl.size = 0;
//sl.a说明是按字节初始化
//初始化多少个字节呢?MAX_SIZE个
//每个有多大呢?sizeof(SQDataType)——SQDataType这个类型每一个的大小
}

这里我们就能更明显地感觉到我们定义数组大小为MAX_SIZE的好处了,我们想把数组大小改一下是不是只要在.h文件的定义这里改一下就把所有包含了.h文件的数组大小都改掉了?很方便。

定义数据类型和定义数组大小同理,本质上都是增强了程序可维护性(不用所有地方都去改)。

那我们现在开始写一点测试一点,我们程序要输入输出,包含一个头文件,我们不知道的话再查一下memset的头文件,<cstring>或者<string.h>这两个头文件是一样的——

#ifdef __TEST_H__
#define __TEST_H__#include<stdio.h>
#include<string.h>
//增强程序可维护性
#define  MAX_SIZE  100
typedef int SQDataType;typedef struct SeqList
{SQDataType a[MAX_SIZE];int size;
}SL;
void SeqListInit(SL s);
//增删查改等接口函数
#endif  //__TEST_H__

然后我们来到test.c文件,先写一个简单的——

#include"SeqList.h"void TestSeqList1()
{SL sl;SeqListInit(SL s1);
//调接口进行初始化
}int main()
{return 0;
}

定义完了,我们先来试着跑一跑, 调试的时候我们可以结合VS调试技巧中介绍过的技巧,F9打断点,按F5调试,运行到断点处。

所以这里我们就不能这么写,要把前面的改一改:

1、静态顺序表 

我们C语言里面学过一个通讯录的东西,就是这个结构。定义一个宏就写死了。

(1)SeqList.h

#ifdef __TEST_H__
#define __TEST_H__#include<stdio.h>
#include<string.h>
//增强程序可维护性
#define  MAX_SIZE  10//100个、500个有点太大了,先来10个
typedef int SQDataType;typedef struct SeqList
{SQDataType a[MAX_SIZE];int size;
}SL;
//增删查改等接口函数
void SeqListInit(SL*ps);
void SeqListPushBack(SL*ps,SQDataType x);//尾插
void SeqListPushFront(SL*ps,SQDataType x);//头插
void SeqListPopBack(SL*ps);//头删
void SeqListPopFront(SL*ps);//尾删
#endif  

这里uu们如果有其他明明也是可以的,博主这里是跟cpp的STL库——标准库——的命名走的。这是一种比较规范的命名形式——

void SeqListPushBack(SL*ps,SQDataType x);//尾插
void SeqListPushFront(SL*ps,SQDataType x);//头插
void SeqListPopBack(SL*ps);//头删
void SeqListPopFront(SL*ps);//尾删

当然这里还有其他的接口,我们还是老原则,写一个测试一个。 

(2)SeqList.c(静态顺序表)

#include"SeqList.h"void SeqListInit(SL*p s)
{memset(ps->a,0,sizeof(SQDataType)*MAX_SIZE);ps->size = 0;
}
//头插  尾插  头删  尾删
void SeqListPushBack(SL*ps,SQDataType x);
{if(ps->size >= MAX_SIZE){printf("SeqList is Full\n");return;}ps->a[ps->size] = x;ps->size++;
}
//下面的大致也是上面这样,就不介绍了
void SeqListPushFront(SL*ps,SQDataType x);
void SeqListPopBack(SL*ps);
void SeqListPopFront(SL*ps);

从这里我们可以看出静态顺序表结构不是一个好的顺序表,存储1001个,你定义MAX_SIZE为10000,是不是浪费了空间,静态顺序表存在的问题总结一下就是:

给少了不够用,给多了用不完浪费空间,不能很好地控制。

正因如此,我们选择更加灵活的动态顺序表,静态顺序表我们就介绍到这里。

(3)test.c

#include"SeqList.h"void TestSeqList1()
{SL sl;SeqListInit(&sl);
//调接口进行初始化
}int main()
{TestSeqListl();return 0;
}

为什么要写指针呢,因为形参是实参的一份临时拷贝,形参和实参空间相互独立,我们要传地址,这样形参改变不会影响实参,传地址为什么可以影响呢?

2、动态顺序表
(1)尾插

(1)SeqList.h

#ifdef __TEST_H__
#define __TEST_H__#include<stdio.h>
#include<string.h>
//增强程序可维护性
typedef int SQDataType;typedef struct SeqList
{SQDataType*a;int size;     //有效数据的个数int capacity;//容量
}SL;
//增删查改等接口函数
void SeqListInit(SL*ps);
void SeqListPrint(SL*ps);
void SeqListPushBack(SL*ps,SQDataType x);//头插
void SeqListPushFront(SL*ps,SQDataType x);//尾插
void SeqListPopBack(SL*ps);//头删
void SeqListPopFront(SL*ps);//尾删
#endif  

(2)SeqList.c(动态顺序表)

#include"SeqList.h"void SeqListInit(SL*ps)
{
//粗暴一点,先置为空,再改成0,然后capacity也改为0ps->a = NULL;ps->size = 0;ps->capacity = 0;
//当然我们也可以直接malloc
}
//头插  尾插  头删  尾删
void SeqListPushBack(SL*ps,SQDataType x);
{//满了就要扩容if(ps->size == ps->capacity){//扩容一般扩两倍,一个一个太慢了,三倍又太多了,cpp标准库一般是扩两倍//我们有两种思路,一种是新开辟一个两倍的空间,然后把原来的内容复制过去,另一种是reallocint newcapacity = ps->capacity == 0 ? 4 : ps->capacity*2;//第一次给多少没什么讲究,给个10也可以,只是说一开始不要给太大,给太大就浪费了//动态管理,就是开始给小一点,不够了慢慢增加//最小给个1SQDataType* tmp = realloc(ps->a,ps->capacity*2*sizeof(SQDataType));//realloc有可能失败if(tmp == NULL){printf("realloc fail\n");exit(-1);}else{ps->a = tmp;//ps->capacity *= 2;//这里就不是乘两倍了,直接是等于newcapacityps->capacity = newcapacity;}}ps->a[ps->size] = x;ps->size++;  
}
void SeqListPushFront(SL*ps,SQDataType x);
void SeqListPopBack(SL*ps);
void SeqListPopFront(SL*ps);void SeqListPrint(SL*ps)
{for(int i = 0;i < ps->size;++i){printf("%d ",ps->a[i]);}printf("\n");
}

(3)test.c

#include"SeqList.h"void TestSeqList1()
{SL sl;SeqListInit(&sl);SeqListPushBack(&sl,1);SeqListPushBack(&sl,2);SeqListPushBack(&sl,3);SeqListPushBack(&sl,4);SeqListPushBack(&sl,5);SeqListPushBack(&sl,6);SeqListPushBack(&sl,7);SeqListPushBack(&sl,8);SeqListPushBack(&sl,9);SeqListPushBack(&sl,10);SeqListPushBack(&sl,11);SeqListPrint(&sl);
}int main()
{TestSeqListl();return 0;
}

这种就是尾插,只要内存够,我们可以继续插。不会像静态顺序表那样,它不需要定义多少个,不够了就增容,可以一直尾插下去,这就是动态顺序表相较于静态顺序表的优越性。

那么uu们可能要问了,realloc不是在原来空间的基础上扩容吗?如果原来空间是0怎么办呢?

realloc这里有一句说明,原来空间可以为空,只不过这样一来realloc扩容就和malloc一样,就是说如果原来空间为空,它就申请一个新的空间,等于malloc。 

像头插、指定插入、指定删除这些知识点博主会专门整理一篇博客出来,当然一篇肯定讲不完,给主包一些时间整理一下。或者铸币主包在顺序表和链表的博客里面融入这些知识点,也是可行的。


结尾 

往期回顾:

【数据结构】详解算法复杂度:时间复杂度和空间复杂度

本期内容需要回顾的C语言知识放在下面了(指针博主写了6篇,列出来有水字数嫌疑了,就只放指针第六篇的网址,博主在指针(六)把指针部分的前五篇的网址都放在【往期回顾】了,点击【传送门】就可以去看了),大家如果对前面部分的知识点印象不深,可以去看看:

【动态内存管理】深入详解:malloc和free、calloc和realloc、常见的动态内存的错误、柔性数组、总结C/C++中程序内存区域划分

【自定义类型:结构体】:类型声明、结构体变量的创建与初始化、内存对齐、传参、位段

C语言指针深入详解(六):sizeof和strlen的对比,【题解】数组和指针笔试题解析、指针运算笔试题解析 【深入详解】函数栈帧的创建与销毁:寄存器、压栈、出栈、调用、回收空间

结语:本篇文章到这里就结束了,对数据结构的顺序表知识感兴趣的友友们可以在评论区留言,博主创作时可能存在笔误,或者知识点不严谨的地方,大家多担待,有什么错误欢迎在评论区批评指出,感谢友友们的关注和支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/910402.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/910402.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Arrays.asList() 的不可变陷阱:问题、原理与解决方案

&#x1f6a8; Arrays.asList() 的不可变陷阱&#xff1a;问题、原理与解决方案 #Java集合 #开发陷阱 #源码解析 #编程技巧 一、问题现象&#xff1a;无法修改的集合 当开发者使用 Arrays.asList() 转换数组为集合时&#xff0c;尝试添加/删除元素会抛出异常&#xff1a; St…

uniapp对接融云IM即时通讯,语音消息无法播放

uniapp对接融云IM即时通讯&#xff0c;语音消息无法播放 问题背景解决方案1.本地音频播放2.远程音频播放 问题背景 最近使用uniapp对接融云的即时通讯sdk&#xff0c;发送语音消息后&#xff0c;本地音频&#xff08;local&#xff09;和远程音频&#xff08;remote&#xff0…

【C++开发】CMake构建工具

目录 1&#xff0c;CMake介绍 2&#xff0c;配置文件CMakeLists.txt 1&#xff0c;CMake介绍 CMake 是一个开源的、跨平台的自动化构建系统生成工具&#xff0c;广泛用于 C 和 C 项目的构建管理。它使用一个名为 CMakeLists.txt 的配置文件来定义如何构建项目&#xff0c;并能…

大模型MetaGPT面试题汇总及参考答案

目录 MetaGPT 的核心目标与设计理念是什么? 它如何实现多角色协同(如 Planner、Coder、Reviewer、Tester)? 不同 agent 之间的通信机制是怎样的? MetaGPT 是如何进行任务拆分与任务分配的? 它如何实现可执行的反馈循环(self-correcting)? 在实际项目中如何监控各…

深入理解 HTTP 状态码 —— 前端后端必备知识

&#x1f4da;深入理解 HTTP 状态码 —— 前端后端必备知识 作者&#xff1a;lvzi 日期&#xff1a;2025 年 6 月 22 日 标签&#xff1a;HTTP、前端、后端、状态码、Web基础 &#x1f4a1;引言 在 Web 开发过程中&#xff0c;我们经常会遇到形如 200 OK、404 Not Found、500…

Python商务数据分析——Python 入门基础知识学习笔记

一、简介 1.1 Python 特性 解释型语言&#xff1a;代码无需编译可直接运行&#xff0c;适合快速开发。 动态类型&#xff1a;变量类型在运行时确定&#xff08;如x1后x"str"仍合法&#xff09;。 面向对象&#xff1a;支持类、对象、继承等特性&#xff0c;代码可…

IT小白到高手:HCIA、HCIP、HCIE认证攻略

大家好&#xff0c;这里是G-LAB IT实验室。6月&#xff12;&#xff12;日&#xff0c;周日&#xff01;HCIA&#xff0b;CCNA开新班啦&#xff01; 01 华为HCIA、HCIP、HCIE有必要考证吗 在如今竞争激烈的IT行业&#xff0c;华为的认证体系已成为众多网络工程师的重要参考。…

【IndexDB】前端IndexedDB终极指南

前端 IndexedDB 详细教程 IndexedDB 是一个浏览器内置的 NoSQL 数据库系统&#xff0c;允许在客户端存储大量结构化数据&#xff0c;并支持高性能搜索。相比 localStorage&#xff0c;IndexedDB 更适合存储大量数据并提供更复杂的查询功能。 基本概念 数据库&#xff1a;每个…

扩散模型与强化学习(1):字节Seedance中的人类偏好优化实践

扩散模型与强化学习(0)&#xff1a;专栏汇总与导航 前言&#xff1a;最近强化学习在Diffusion Models得到了越来越多广泛的应用&#xff0c;本专栏将系统性地介绍当前Diffusion Models中实用且前沿的技术进展。这篇博客介绍字节最新的视频生成模型Seedance 1.0: Exploring the …

【内存】Linux 内核优化实战 - vm.max_map_count

目录 vm.max_map_count参数全面解析一、参数定义与核心作用二、默认值与关键调整场景1. 默认限制与不足场景2. 典型报错案例 三、操作指南&#xff1a;查看与修改方法四、场景化建议值与配置示例五、关键注意事项六、延伸知识&#xff1a;内存映射的底层逻辑 vm.max_map_count参…

组件之间的双向绑定:v-model

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

GetX 实现 MVVM 架构, 高效 路由管理 和 状态管理

GetX是Flutter中的一个高效的状态管理与路由管理框架&#xff0c;结合MVVM架构能简化代码逻辑。以下是使用GetX实现MVVM架构&#xff0c;并完成路由和状态管理的核心思路与实践&#xff1a; 一、MVVM架构在GetX中的映射 MVVM&#xff08;Model-View-ViewModel&#xff09;与G…

Qt项目,记事本

一、项目说明 项目功能&#xff1a; &#xff08;1&#xff09;打开文件&#xff1a;点击打开文件按钮弹出对话框&#xff0c;选择文本文件后&#xff0c;在主窗口编辑界面显示内容。 &#xff08;2&#xff09;关闭文件&#xff1a;关闭打开的文件&#xff0c;并询问是否保存…

【全开源】填表问卷统计预约打卡表单系统+uniapp前端

一.系统介绍 填表问卷统计预约打卡表单系统是ThinkPHPUniApp开发的一款集信息填表、预约报名&#xff0c;签到打卡、活动通知、报名投票、班级统计等功能的自定义表单统计小程序。 二.搭建环境 系统环境&#xff1a;CentOS、 运行环境&#xff1a;宝塔 Linux 网站环境&…

开源 python 应用 开发(一)python、pip、pyAutogui、python opencv安装

最近有个项目需要做视觉自动化处理的工具&#xff0c;最后选用的软件为python&#xff0c;刚好这个机会进行系统学习。短时间学习&#xff0c;需要快速开发&#xff0c;所以记录要点步骤&#xff0c;防止忘记。 链接&#xff1a; 开源 python 应用 开发&#xff08;一&#x…

SpringCloud + Zookeeper + Feign整合及Feign原理

知其然 SpringCloud Zookeeper Spring Cloud 与 Zookeeper的整合只需要添加相关的starter依赖和增加相关注解即可完成。 pom.xml 如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.…

深入探索 OpenCV 图像识别:从基础到深度学习

在当今数字化时代&#xff0c;图像识别技术已经渗透到我们生活的方方面面&#xff0c;从智能手机中的拍照翻译功能到自动驾驶汽车的目标检测系统&#xff0c;图像识别的应用无处不在。作为一名算法工程师&#xff0c;我有幸深入研究并实践了 OpenCV 在图像识别领域的强大功能。…

Hadoop部署(HA)高可用集群

一、准备工作 1.把集群全部停掉 在三台节点上都做&#xff08;在xshell通过右键----> 发送输入到--->所有会话&#xff09; 2..在/export/servers下创建HA目录 sudo mkdir -p /export/servers/HA 3.创建用户和设置所属主和所属组 #创建用户 sudo adduser ygre #设置…

STM32 CAN位同步、错误处理

一、接收方数据采样 CAN总线没有时钟线&#xff0c;总线上的所有设备通过约定波特率的方式确定每一个数据位的时长发送方以约定的位时长每隔固定时间输出一个数据位接收方以约定的位时长每隔固定时间采样总线的电平&#xff0c;输入一个数据位理想状态下&#xff0c;接收方能依…

django serializer __all__中 额外添加外键里的某一个属性

在Django中使用序列化器&#xff08;Serializer&#xff09;时&#xff0c;你可能会遇到需要将模型&#xff08;Model&#xff09;中的外键字段转换成其关联对象的一部分属性的情况。默认情况下&#xff0c;序列化器会自动序列化外键字段&#xff0c;但如果你想要在序列化结果中…