【數據結構與算法】數據結構初階:詳解順序表和鏈表(一)

🔥個人主頁:艾莉絲努力練劍

?專欄傳送門:《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
英文地址,請注明出處:http://en.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;但如果你想要在序列化結果中…