[數據結構——lesson3.單鏈表]

目錄

引言

學習目標:

1.什么是鏈表

2.鏈表的分類

2.1?單向鏈表和雙向鏈表

(1)單向鏈表

(2)雙向鏈表

2.2 帶頭結點鏈表和不帶頭結點鏈表

(1)帶頭結點鏈表

(2)不帶頭結點鏈表

2.3?循環鏈表和不循環鏈表

(1)循環鏈表

(2)非循環鏈表

3.鏈表的實現

3.1 單鏈表的功能

3.2單鏈表的定義

3.3單鏈表功能實現

1.打印單鏈表數據

2.單鏈表頭插

(1)創建節點

(2)頭插代碼

3.單鏈表頭刪

4.單鏈表尾插

代碼實現

5.單鏈表尾刪

6.單鏈表數據查找

7.單鏈表數據修改

代碼實現

8.指定位置數據插入

(1)指定位置之前插入數據

(2)指定位置之后插入數據

9.指定位置數據刪除

(1)刪除pos節點

(2)刪除pos之后的節點

10.銷毀單鏈表

完整代碼SList.h


引言

在上一篇文章中?[數據結構——lesson2.順序表]我們學習了數據結構中的順序表,我們知道了順序表的空間是連續存儲的,這與數組非常類似。但是我們也遺留了一些關于順序表的問題

當我們同時當插入數據時可能存在移動數據與擴容的情況,這大大增加我們的時間與空間成本。因此我們接下來學習新的數據結構——鏈表

學習目標:

  • 什么是鏈表?
  • 鏈表的分類
  • 鏈表的實現

1.什么是鏈表

鏈表(Linked List)是一種常見的數據結構,它通過一系列的節點(Node)來存儲數據元素。這些節點之間通過指針或引用相互連接,從而形成一個鏈狀結構。每個節點通常包含兩部分:一部分用于存儲數據元素,另一部分用于存儲指向下一個節點的指針或引用。

注意:

1.從上圖可以看出,鏈式結構在邏輯上是連續的,但在物理上不一定連續;

鏈表是一種物理存儲結構上非連續、非順序的存儲結構,數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。鏈表中的每個節點通過指針指向下一個節點,從第一個節點開始,依次沿著指針可以訪問到后續節點,形成一個邏輯上連續的序列。但在物理內存中,這些節點可能分布在不同的位置,并不要求存儲單元是連續的。

2.鏈表的結點一般都是從堆上申請的;

鏈表中的結點是獨立申請的空間,通常是在需要插入數據時才去申請一塊節點的空間。操作系統的內存管理中,堆是一塊可供程序動態分配的內存區域,它允許程序在運行時根據需要申請和釋放內存,適合鏈表這種動態數據結構,因為鏈表的長度不固定,需要隨時根據數據的插入和刪除來分配和釋放節點空間。

3.從堆上申請的空間,是按照一定的策略來分配的,兩次省請的空間可能連續,可能不連續。

堆內存的分配由操作系統的內存分配器負責,它會根據一定的算法和策略來管理和分配內存,這些算法會根據堆內存的當前使用情況來尋找合適的空閑塊分配給程序。由于堆內存的使用是動態變化的,每次申請內存時,內存分配器找到的空閑塊位置是不確定的,所以兩次申請的空間可能連續,也可能不連續。

2.鏈表的分類

鏈表可以分為幾種不同的類型,其中最常見的有八種,它們大致可以分為三類:

2.1?單向鏈表和雙向鏈表

(1)單向鏈表

在單向鏈表中,每個節點只包含一個指向下一個節點的指針。

(2)雙向鏈表

在雙向鏈表中,每個節點除了包含數據元素外,還包含兩個指針:一個指向下一個節點(稱為后繼節點),另一個指向前一個節點(稱為前驅節點)。

2.2 帶頭結點鏈表和不帶頭結點鏈表

(1)帶頭結點鏈表

帶頭結點的單鏈表是在鏈表的第一個數據元素結點之前附加一個結點,稱為頭結點。

(2)不帶頭結點鏈表

非帶頭結點的單鏈表不包含頭結點,鏈表的第一個結點即為數據元素結點,頭指針直接指向鏈表的第一個數據元素結點。

2.3?循環鏈表和不循環鏈表

(1)循環鏈表

循環鏈表是一種特殊類型的鏈表,其中最后一個節點指向第一個節點,形成一個循環。這種結構使得鏈表在邏輯上成為一個環狀結構。

(2)非循環鏈表

雖然有這么多的鏈表的結構,但是我們實際中最常用還是兩種結構:
?
無頭單向非循環鏈表

?帶頭雙向循環鏈

本篇博客將詳細講解一下無頭單向非循環鏈表。

無頭單向非循環鏈表是一種鏈表結構,其特點是沒有頭結點,且鏈表中的節點單向連接,不形成循環。這種鏈表結構相對簡單,一般不會單獨用來存儲數據,而是作為其他數據結構的子結構,如哈希桶、圖的鄰接表等。

3.鏈表的實現

3.1 單鏈表的功能

單鏈表一般要實現如下幾個功能:

  1. 打印單鏈表中的數據。
  2. 對單鏈表進行頭插(開頭插入數據)。
  3. 對單鏈表進行頭刪(開頭刪除數據)。
  4. 對單鏈表進行尾插(末尾插入數據)。
  5. 對單鏈表進行尾刪(末尾刪除數據)。
  6. 對單鏈表進行查找數據。
  7. 對單鏈表數據進行修改。
  8. 對指定位置的數據刪除。
  9. 對指定位置的數據插入。
  10. 銷毀單鏈表。

3.2單鏈表的定義

單鏈表的結點定義方式與我們以往定義的方式都不同,它是一個結構體中包含兩個成員。一個是存儲數值,一個存放下一個結點的地址。

//定義節點的結構
//數據+指向下一節點的指針
typedef int SLTDataType;typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;

3.3單鏈表功能實現

1.打印單鏈表數據

遍歷鏈表并打印每個節點的數據來展示鏈表的內容,直到遇到鏈表的末尾。

代碼實現

void SLTPrint(SLTNode* phead)
{// 初始化一個指針pcur,指向鏈表的頭節點SLTNode* pcur = phead;// 使用while循環遍歷鏈表,直到pcur指向NULL(即鏈表末尾)  while (pcur != NULL){printf("%d->", pcur->data);pcur = pcur->next;}// 循環結束后,打印"NULL"以明確表示鏈表的結束 printf("NULL\n");
}

2.單鏈表頭插

(1)創建節點

由于單鏈表每次插入都需要插入一個節點,因此我們可以寫一個創建節點的函數方便后續調用。

//申請內存
SLTNode* SLTCreateNode(SLTDataType x)
{// 為新的單鏈表節點分配內存空間 SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail:");exit(1);}// 初始化新節點的數據字段  newnode->data = x;newnode->next = NULL;return newnode;
}
(2)頭插代碼

在這里我們需要注意的是:單鏈表傳參需要二級指針。因為頭插數據需要改變一級指針plist的指向,而形參的改變無法影響實參,所以需要二級指針才能改變一級指針plist的指向。

//頭插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{// 斷言確保傳入的頭節點指針的地址非空,避免解引用空指針assert(pphead);// 創建一個新的節點,并初始化其數據為x  SLTNode* newnode = SLTCreateNode(x);// 將新節點的next指針指向當前頭節點newnode->next = *pphead;// 更新頭節點指針,使其指向新節點*pphead = newnode;
}

一、對?assert(pphead)?的理解

核心作用:確保二級指針?pphead?不為空,避免解引用空指針導致程序崩潰。

  • 邏輯背景
    代碼中存在?*pphead(解引用二級指針),而對空指針執行解引用操作(如?*NULL)會觸發未定義行為(通常導致程序崩潰)。
    assert(pphead)?會在程序運行時檢查?pphead?是否為?NULL
    • 若?pphead?為?NULL,程序會報錯并終止,提示開發者問題所在;
    • 若?pphead?非空,則繼續執行后續代碼。
  • 本質目的:通過斷言提前暴露潛在的空指針風險,提高代碼健壯性。

二、為什么需要傳遞二級指針?

核心原因:需要通過函數修改原始指針(如鏈表頭節點指針?head)的地址本身。

  • 指針操作的本質
    • 若僅傳遞一級指針?head(即結構體指針),函數內部只能修改?head?指向的結構體內容,無法改變?head?本身存儲的地址值(類似 “傳值” 效果)。
    • 若需要修改?head?本身的地址(例如頭節點變更、鏈表初始化等場景),必須傳遞?head?的地址,即二級指針?pphead(類似 “傳址” 效果)。

3.單鏈表頭刪

頭刪與頭插類似,都可能需要改變plist的指向,所以也需要二級指針。并且也需要防止鏈表為空的情況發生

代碼實現

//頭刪
void SLTPopFront(SLTNode** pphead)
{// 鏈表不能為空,確保pphead不是空指針// 且*pphead(即頭節點)也不是空指針assert(pphead && *pphead);// 保存頭節點的下一個節點的地址,// 以便刪除頭節點后能夠更新頭指針SLTNode* next = (*pphead)->next;free(*pphead);// 更新頭節點*pphead = next;
}

4.單鏈表尾插

代碼實現
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);//	*pphead就是指向第一個節點的指針//	處理空鏈表SLTNode* newnode = SLTCreateNode(x);// 判斷鏈表是否為空if (*pphead == NULL){// 如果鏈表為空,則新節點即為頭節點*pphead = newnode;}else{// 找尾SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}// 循環結束,ptail指向尾節點ptail->next = newnode;}
}

5.單鏈表尾刪

與單鏈表尾插類似,當鏈表只有一個頭結點時需要將plist置為空所以也需要二級指針。并且也需要防止鏈表為空的情況發生。

//尾刪
void SLTPopBack(SLTNode** pphead)
{// 鏈表不能為空,確保pphead不是空指針,// 且*pphead(即頭節點)也不是空指針assert(pphead && *pphead);// 如果鏈表只有一個節點if ((*pphead)->next == NULL)	//->優先級高于*{free(*pphead);*pphead = NULL;}else{// 鏈表有多個節點SLTNode* prev = *pphead;SLTNode* ptail = *pphead;// 遍歷鏈表直到找到尾節點while (ptail->next){prev = ptail;			// 更新prev節點ptail = ptail->next;	// 移動到下一個節點}free(ptail);	// 釋放尾節點prev->next = NULL;}
}

6.單鏈表數據查找

如果找到了就返回這個節點的地址,否則就返回空指針NULL。

代碼實現

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead;// 遍歷鏈表,直到當前節點為空(即到達鏈表末尾)while (pcur){if (pcur->data == x){return pcur;	// 找到當前節點的指針}pcur = pcur->next;}return NULL;
}

7.單鏈表數據修改

代碼實現
//數據修改 
void SLTModifity(SLTNode* pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos); SLTNode* cur = pphead;while (cur){if (cur == pos){cur->data = x;break;}cur = cur->next;}
}

8.指定位置數據插入

(1)指定位置之前插入數據
//在指定位置之前插入數據
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && *pphead);assert(pos);SLTNode* newnode = SLTCreateNode(x);// 如果插入位置是鏈表的第一個位置(即pos等于頭節點)// 則調用頭插函數if (pos == *pphead){SLTPushFront(pphead, x);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;}
}
(2)指定位置之后插入數據
//在指定位置之后插入數據
void SLTnsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTCreateNode(x);// 將新節點的next指針指向pos的下一個節點newnode->next = pos->next;// 將pos的next指針指向新節點,完成插入操作pos->next = newnode;
}

9.指定位置數據刪除

(1)刪除pos節點
//刪除pos節點
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pos);assert(pphead && *pphead);// 如果刪除的節點是頭節點if (pos == *pphead){SLTPopFront(pphead);	// 頭刪}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}// 將prev的next指針指向pos的下一個節點// 從而繞過pos節點,實現刪除prev->next = pos->next;free(pos);pos = NULL;}
}
(2)刪除pos之后的節點
//刪除pos之后的節點
void SLTraseAfter(SLTNode* pos)
{assert(pos && pos->next);// 創建一個臨時指針del,指向pos之后的節點,即要刪除的節點SLTNode* del = pos->next;// 將pos的next指針指向del的下一個節點,從而繞過del節點,實現刪除pos->next = del->next;free(del);del = NULL;
}

10.銷毀單鏈表

銷毀鏈表需要依次遍歷釋放節點。

//銷毀鏈表
void SListDesTory(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;while (pcur){// 保存當前節點的下一個節點的指針// 以便在釋放當前節點后能夠繼續遍歷SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

完整代碼
SList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//定義節點的結構
//數據+指向下一節點的指針
typedef int SLTDataType;typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;//打印
void SLTPrint(SLTNode* phead);
//尾插
void SLTPushBack(SLTNode** phead, SLTDataType);
//頭插
void SLTPushFront(SLTNode** phead, SLTDataType);
//尾刪
void SLTPopBack(SLTNode** pphead);
//頭刪
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);//在指定位置之前插入數據
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在制定位置之后插入數據
void SLTnsertAfter(SLTNode* pos, SLTDataType x);//刪除pos節點
void SLTErase(SLTNode** pphead, SLTNode* pos);
//刪除pos之后的節點
void SLTraseAfter(SLTNode* pos);//銷毀鏈表
void SListDesTory(SLTNode** pphead);//數據修改
void SLTModifity(SLTNode* pphead, SLTNode* pos, SLTDataType x);

SList.c

#include"SList.h"void SLTPrint(SLTNode* phead)
{// 初始化一個指針pcur,指向鏈表的頭節點SLTNode* pcur = phead;// 使用while循環遍歷鏈表,直到pcur指向NULL(即鏈表末尾)  while (pcur != NULL){printf("%d->", pcur->data);pcur = pcur->next;}// 循環結束后,打印"NULL"以明確表示鏈表的結束 printf("NULL\n");
}//申請內存
SLTNode* SLTCreateNode(SLTDataType x)
{// 為新的單鏈表節點分配內存空間 SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail:");exit(1);}// 初始化新節點的數據字段  newnode->data = x;newnode->next = NULL;return newnode;
}//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);//	*pphead就是指向第一個節點的指針//	處理空鏈表SLTNode* newnode = SLTCreateNode(x);// 判斷鏈表是否為空if (*pphead == NULL){// 如果鏈表為空,則新節點即為頭節點*pphead = newnode;}else{// 找尾SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}// 循環結束,ptail指向尾節點ptail->next = newnode;}
}//頭插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{// 斷言確保傳入的頭節點指針的地址非空,避免解引用空指針assert(pphead);// 創建一個新的節點,并初始化其數據為x  SLTNode* newnode = SLTCreateNode(x);// 將新節點的next指針指向當前頭節點newnode->next = *pphead;// 更新頭節點指針,使其指向新節點*pphead = newnode;
}//尾刪
void SLTPopBack(SLTNode** pphead)
{// 鏈表不能為空,確保pphead不是空指針,// 且*pphead(即頭節點)也不是空指針assert(pphead && *pphead);// 如果鏈表只有一個節點if ((*pphead)->next == NULL)	//->優先級高于*{free(*pphead);*pphead = NULL;}else{// 鏈表有多個節點SLTNode* prev = *pphead;SLTNode* ptail = *pphead;// 遍歷鏈表直到找到尾節點while (ptail->next){prev = ptail;			// 更新prev節點ptail = ptail->next;	// 移動到下一個節點}free(ptail);	// 釋放尾節點prev->next = NULL;}
}//頭刪
void SLTPopFront(SLTNode** pphead)
{// 鏈表不能為空,確保pphead不是空指針// 且*pphead(即頭節點)也不是空指針assert(pphead && *pphead);// 保存頭節點的下一個節點的地址,// 以便刪除頭節點后能夠更新頭指針SLTNode* next = (*pphead)->next;free(*pphead);// 更新頭節點*pphead = next;
}//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead;// 遍歷鏈表,直到當前節點為空(即到達鏈表末尾)while (pcur){if (pcur->data == x){return pcur;	// 找到當前節點的指針}pcur = pcur->next;}return NULL;
}//數據修改 
void SLTModifity(SLTNode* pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos); SLTNode* cur = pphead;while (cur){if (cur == pos){cur->data = x;break;}cur = cur->next;}
}//在指定位置之前插入數據
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && *pphead);assert(pos);SLTNode* newnode = SLTCreateNode(x);// 如果插入位置是鏈表的第一個位置(即pos等于頭節點)// 則調用頭插函數if (pos == *pphead){SLTPushFront(pphead, x);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;}
}//在指定位置之后插入數據
void SLTnsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTCreateNode(x);// 將新節點的next指針指向pos的下一個節點newnode->next = pos->next;// 將pos的next指針指向新節點,完成插入操作pos->next = newnode;
}//刪除pos節點
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pos);assert(pphead && *pphead);// 如果刪除的節點是頭節點if (pos == *pphead){SLTPopFront(pphead);	// 頭刪}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}// 將prev的next指針指向pos的下一個節點// 從而繞過pos節點,實現刪除prev->next = pos->next;free(pos);pos = NULL;}
}//刪除pos之后的節點
void SLTraseAfter(SLTNode* pos)
{assert(pos && pos->next);//創建臨時變量delSLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}//銷毀鏈表
void SListDesTory(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;while (pcur){// 保存當前節點的下一個節點的指針// 以便在釋放當前節點后能夠繼續遍歷SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

結束語

本節內容學習了鏈表的種類和單鏈表的實現。

感謝大家三連支持!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/96183.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/96183.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/96183.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

從零深入理解嵌入式OTA升級:Bootloader、IAP與升級流程全解析

引言&#xff08;Opening&#xff09;想象一下&#xff0c;你開發的一款智能水杯、一個環境監測設備或者一臺共享充電寶&#xff0c;已經部署到了成千上萬的用戶手中。突然&#xff0c;你發現了一個軟件bug&#xff0c;或者需要增加一個酷炫的新功能。你不可能派人跑到每個設備…

【Ansible】實施 Ansible Playbook知識點

1.清單概念與靜態清單文件是什么&#xff1f;答&#xff1a;Ansible 清單是被管理主機的列表&#xff0c;用于明確Ansible的管理范圍&#xff0c;分為靜態清單和動態清單。靜態清單是通過手動編輯的文本文件來定義被管主機&#xff0c;文件格式可以是INI格式或YAML格式。在INI格…

【Linux】vim工具篇

目錄一、vim的多模式1.1 命令模式1.1.1 光標移動1.1.2 復制及撤銷1.1.3 剪切及刪除1.1.4 替換1.1.5 批量化注釋/去注釋1.2 底行模式二、vim的配置個人主頁<—請點擊 Linux專欄<—請點擊 一、vim的多模式 vim是一款功能強大的文本編輯器&#xff0c;它編輯代碼主要圍繞命…

Spark 核心原理:RDD, DataFrame, DataSet 的深度解析

Apache Spark 是一個強大的分布式計算系統&#xff0c;以其內存計算、速度快、易用性強等特點&#xff0c;在大數據處理領域占據重要地位。理解 Spark 的核心原理&#xff0c;特別是其三種核心抽象——RDD, DataFrame, DataSet——對于高效地使用 Spark 至關重要。本文將深入解…

Docker 命令行的使用

1.Docker 命令列表[roothost1 ~]# docker Usage: docker [OPTIONS] COMMANDA self-sufficient runtime for containersCommon Commands:run Create and run a new container from an imageexec Execute a command in a running containerps List cont…

Redis Stream:輕量級消息隊列深度解析

&#x1f4e8; Redis Stream&#xff1a;輕量級消息隊列深度解析 文章目錄&#x1f4e8; Redis Stream&#xff1a;輕量級消息隊列深度解析&#x1f9e0; 一、Stream 數據結構解析&#x1f4a1; Stream 核心概念&#x1f4cb; Stream 底層結構? 二、消息生產與消費&#x1f68…

Android studio的adb和終端的adb互相搶占端口

在Android Studio調試時&#xff0c;有時候也需要借助終端的adb命令&#xff0c;他們互相搶占端 口&#xff0c;導致調試麻煩解決如下&#xff1a;① 終端adb的版本是&#xff1a;1.0.39路徑是:/usr/lib/android-sdk/platform-tools/adb② Android Studio使用的adb來源于Androi…

GEO服務商推薦:移山科技以劃時代高精尖技術引領AI搜索優化新紀元

引言&#xff1a;AI搜索生態重塑與GEO優化戰略地位躍升AI技術對信息檢索范式的顛覆GEO優化在企業增長中的核心作用第一章&#xff1a;AI搜索新紀元的企業營銷挑戰與機遇生成式AI成為用戶主要信息入口的行業趨勢企業在AI搜索中的“答案主權”爭奪戰GEO優化服務商的核心能力模型&…

Android SystemServer 系列專題【AttentionManagerService】

AttentionManagerService是framework中用來實現屏幕感知的一個系統級服務&#xff0c;他繼承于systemserver。我們可以通過dumpsys attention來獲取他的一些信息。如下針對屏幕感知的功能的引入來針對這個服務進行一個介紹。1、屏幕感知Settings UI實現屏幕感知的功能在A14上面…

nginx 反向代理使用變量的坑

nginx采用反向代理的時候使用變量的坑 正常情況&#xff1a; location ~ ^/prod-api(?<rest>/.*)?$ {# 假設 $mes_backend 形如: http://127.0.0.1:16889proxy_pass $mes_backend$rest$is_args$args;proxy_http_version 1.1;proxy_set_header Host $host;…

Origin繪制徑向條形圖|科研論文圖表教程

數據排列格式截圖&#xff0c;請查看每張圖↘右下角水印 目錄 數據排列格式截圖&#xff0c;請查看每張圖↘右下角水印 本 期 導 讀 No.1 理解圖形 1 定義 2 特點 3 適用場景 No.2 畫圖教程 1 導入數據&#xff0c;繪制圖形 2 設置繪圖細節 本 期 導 讀 徑…

MySQL InnoDB 的 MVCC 機制

前言 多版本并發控制&#xff08;MVCC&#xff09;是 MySQL InnoDB 存儲引擎實現高性能事務的核心機制。它通過創建數據快照&#xff0c;使得讀寫操作可以無鎖并發&#xff0c;極大地提升了數據庫的并發性能。本文將深入探討 MVCC 的工作原理、實現細節以及它與事務隔離級別的緊…

景區負氧離子氣象站:引領綠色旅游,暢吸清新每一刻

在綠色旅游成為消費主流的今天&#xff0c;游客對 “清新空氣” 的需求不再是模糊的期待&#xff0c;而是可感知、可選擇的具體體驗。景區負氧離子氣象站的出現&#xff0c;正以科技之力重塑綠色旅游格局&#xff0c;讓 “暢吸清新每一刻” 從口號變為觸手可及的現實&#xff0…

Pytorch筆記一之 cpu模型保存、加載與推理

Pytorch筆記一之 cpu模型保存、加載與推理 1.保存模型 首先&#xff0c;在加載模型之前&#xff0c;我們需要了解如何保存模型。PyTorch 提供了兩種保存模型的方法&#xff1a;保存整個模型和僅保存模型的狀態字典&#xff08;state dict&#xff09;。推薦使用第二種方式&…

當AI在代碼車間組裝模塊:初級開發者的創意反成「核心算法」

前言&#xff1a;哈嘍&#xff0c;大家好&#xff0c;今天給大家分享一篇文章&#xff01;并提供具體代碼幫助大家深入理解&#xff0c;徹底掌握&#xff01;創作不易&#xff0c;如果能幫助到大家或者給大家一些靈感和啟發&#xff0c;歡迎收藏關注哦 &#x1f495; 目錄當AI在…

技術視界 | 跨域機器人通信與智能系統:打破壁壘的開源探索

8 月 16 日&#xff0c;在 OpenLoong 社區舉辦的第九期線下分享會上&#xff0c;國家地方共建人形機器人創新中心的軟件開發負責人 Amadeus 博士帶來了一場主題為“跨域機器人通信與智能系統&#xff1a;打破行業壁壘的創新方案”的演講。深入探討了當前機器人領域的一個關鍵痛…

Android入門到實戰(八):從發現頁到詳情頁——跳轉、傳值與RecyclerView多類型布局

一. 引言在上一篇文章里&#xff0c;我們從零開始實現了 App 的 發現頁面&#xff0c;通過網絡請求獲取數據&#xff0c;并使用 RecyclerView 展示了劇集列表。但光有發現頁還不夠&#xff0c;用戶在點擊一部劇時&#xff0c;自然希望進入到一個更詳細的頁面&#xff0c;去查看…

【工具】41K star!網頁一鍵變桌面應用

項目中遇到了一個需要將現有的 web 頁面打包成一個 桌面應用 的需求。 最一開始想到的是 Electron&#xff0c;但是它還需要一些開發工作并且打包后的應用體積比較大&#xff0c;調研后發現了開源工具 Pake。 它能讓你用最輕量的方式&#xff0c;把任何網頁一鍵打包成跨平臺桌…

浪潮CD1000-移動云電腦-RK3528芯片-2+32G-安卓9-2種開啟ADB ROOT刷機教程方法

浪潮CD1000-移動云電腦-RK3528芯片-232G-安卓9-2種開啟ADB ROOT刷機教程方法 往期文章&#xff1a; 浪潮CD1000-移動云電腦-RK3528芯片-232G-安卓9-開啟ADB ROOT破解教程 地址1&#xff1a;浪潮CD1000-移動云電腦-RK3528芯片-232G-開啟ADB ROOT破解教程-CSDN博客 中國移動浪潮…

Day23_【機器學習—聚類算法—K-Means聚類 及評估指標SSE、SC、CH】

一、聚類算法概念屬于無監督學習算法&#xff0c;即有特征無標簽&#xff0c;根據樣本之間的相似性&#xff0c;將樣本劃分到不同的類別中。所謂相似性可以理解為歐氏距離、曼哈頓距離、切比雪夫距離... 。分類按顆粒度分為&#xff1a;粗聚類、細聚類。按實現方法分為&#xf…