鏈表漫游指南:C++ 指針操作的藝術與實踐

文章目錄

  • 0. 前言
  • 1. 鏈表的分類
  • 2. 單鏈表的實現
    • 2.1 鏈表的基本結構——節點(Node)
    • 2.2 核心操作詳解
      • 2.2.1 構造和析構
      • 2.2.2 插入操作
      • 2.2.3 刪除操作
      • 2.3.4 其他操作
    • 2.4 總結
  • 3. 雙向鏈表的實現
    • 3.1 基本結構設計
    • 3.2 基本操作
      • 3.2.1 初始化與銷毀
      • 3.2.2 插入與刪除的核心邏輯
      • 3.2.3 常用操作
    • 3.3 其他輔助函數
    • 3.4 總結
  • 4. 與順序表對比
    • 4.1 時間復雜度對比
    • 4.2優缺點總結

0. 前言

上一章的順序表底層是數組,封裝了增刪查改的功能接口成為了順序表。存儲順序表的空間是連續的,順序表非常大時,內存可能無法提供這么大的連續空間,由此,鏈表就被創造了。在一個復雜的系統中,空閑的內存空間散落在內存各處,而鏈表通過指針很好的鏈接起了各個分散的內存空間。

如圖:
在這里插入圖片描述

內存空間分配并不連續,但是指針稱為維系各個空間的橋梁

鏈表(linked list) 是一種線性數據結構,每個元素是一個節點對象,各個節點通過指針(或引用)相連接,指針(或引用)記錄了下一個節點的內存地址,通過它可以從當前節點訪問到下一個節點

1. 鏈表的分類

鏈表有多種結構,以下情況組合起來有232^323種:

  1. 單項或雙向

在這里插入圖片描述

  1. 帶頭或不帶頭

在這里插入圖片描述

  1. 循環或不循環

在這里插入圖片描述

這八種結構種,最常用的是最簡單和最復雜的兩種結構:

單鏈表(無頭單項不循環鏈表):結構簡單,一般作為其他數據結構的子結構

在這里插入圖片描述

雙向鏈表(帶頭雙向循環鏈表):結構最復雜,實際使用中基本都是此結構,操作簡單

在這里插入圖片描述

注意:我們把這個帶頭的頭稱為哨兵位,哨兵位不存儲有效數據(有些時候可能存儲鏈表節點個數),位于鏈表第一個有效節點(頭節點) 的前面。

現在我們來實現一下單鏈表和雙向鏈表

2. 單鏈表的實現

2.1 鏈表的基本結構——節點(Node)

鏈表的最小單元是節點,C++用結構體或類定義,鏈表的一個節點包含指針域數據域,指針域保存該節點指向下一個結點的地址,數據域保存該節點的有效值。還有一個頭指針的概念:頭指針是鏈表的門把手,指向鏈表頭節點的指針變量,是鏈表的入口

// 節點結構體,存儲數據和指向下一個結點的指針
struct Node {T _value; // 數據域Node* _next; // 指針域// 節點構造函數 確保新節點創建時_next處于安全狀態Node(const T& val) : _value(val), _next(nullptr) {}
};

然后我們將節點放入類中,加入單鏈表的基本信息:頭指針、節點個數

template <typename T>
class single_list{
private:// 節點結構體,存儲數據和指向下一個結點的指針struct Node {T _value;Node* _next;// 節點構造函數 確保新節點創建時_next處于安全狀態Node(const T& val) : _value(val), _next(nullptr) {}};Node* _head; // 頭指針,指向鏈表的第一個節點size_t _count; // 節點數量
};

我們引入類模板,能使鏈表的數據類型更多樣化,需要什么樣的類型我們就可以顯式實例化什么類型的鏈表結構

2.2 核心操作詳解

2.2.1 構造和析構

single_list() : _head(nullptr), _count(0) {}

利用初始化列表將鏈表初始化為空鏈表,方便后續操作

~single_list() { clear();}

調用清空函數刪除所有節點,該函數在下文實現

2.2.2 插入操作

  1. 頭插
// 頭插 O(1)
void push_front(const T& val) {// 創建新節點Node* new_node = new Node(val);new_node->_next = _head;_head = new_node;++_count;
}

在這里插入圖片描述

創建新的節點,新節點的指針指向原先的頭節點,然后更新頭指針的指向

  1. 尾插
// 尾插 O(n)
void push_back(const T& val) {Node* new_node = new Node(val);// 如果是空鏈表 new_node就是新的頭節點if (!_head) {_head = new_node;}else {// 創建臨時變量開始找尾節點Node* cur = _head;while (cur->_next != nullptr)  cur = cur->_next;cur->_next = new_node;}++_count;
}

在這里插入圖片描述

空鏈表要做單獨處理,新節點就是頭節點了;鏈表非空,則要創建臨時變量遍歷整個鏈表找尾,一定不能用頭指針遍歷,使用頭指針遍歷會丟失頭節點的數據

  1. 指定位置插入節點
// 插入指定位置 O(n)
void insert(int pos, const T& val) {// 確保pos的合法性// pos能取到_count,取到相當于尾插assert(pos >= 0 && pos <= _count);if (pos == 0) return push_front(val);// 創建臨時變量找pos位置的節點Node* cur = _head;// 找到pos的前一個節點 cur最終指向pos-1的位置for (size_t i = 0; i < pos - 1; i++)// 循環pos - 1次{cur = cur->_next;}Node* new_node = new Node(val);new_node->_next = cur->_next;cur->_next = new_node;++_count;
}

在這里插入圖片描述

對于用戶輸入的pos要做檢查,pos的值代表索引,從0開始。pos == 0,可以復用頭插法,而pos == _count則代表尾插,在下面的循環邏輯中,是找到pos的前一個節點,所以pos的邊界判斷都可以取到。然后更新指針指向即可

2.2.3 刪除操作

空鏈表不能執行刪除操作

  1. 頭刪
// 頭刪 O(1)
void pop_front() {// 空鏈表不能刪除assert(_head);// 非空鏈表Node* tmp = _head;_head = tmp->_next;delete tmp;--_count;
}

在這里插入圖片描述

  1. 尾刪
// 尾刪 0(n)
void pop_back() {// 空鏈表不能刪除assert(_head);// 非空鏈表  先找尾節點的先驅節點// 如果只有一個節點 刪除即可if (_head->_next == nullptr) {delete _head;_head = nullptr;}else {Node* cur = _head;while (cur->_next->_next) cur = cur->_next;delete cur->_next;cur->_next = nullptr;}--_count;
}

在這里插入圖片描述

  1. 刪除指定位置節點
// 刪除指定位置
void erase(int pos) { // 確保pos合法性assert(pos >= 0 && pos < _count); // pos == 0 頭刪if (pos == 0) {pop_front();return;}Node* cur = _head;for (size_t i = 0; i < pos - 1; i++) {cur = cur->_next;}Node* tmp = cur->_next; // 保存要刪除的節點cur->_next = cur->_next->_next;delete tmp; --_count;
}

在這里插入圖片描述

2.3.4 其他操作

size_t size() {return _count;
}bool empty() {return _count == 0;
}void clear() {while (_head) {Node* tmp = _head;_head = _head->_next;delete tmp;}_count = 0;
}void Print()
{if (!_head) { // 空鏈表處理std::cout << "nullptr" << std::endl;return;}Node* cur = _head;while (cur) { // 遍歷所有節點std::cout << cur->_value; if (cur->_next) {std::cout << "->";}cur = cur->_next;}std::cout << "->nullptr" << std::endl;
}

2.4 總結

操作實現邏輯時間復雜度
頭插(push_front)新節點指向原頭節點,更新頭指針O(1)O(1)O(1)
尾插(push_back)遍歷至尾節點,將其指針指向新節點O(n)O(n)O(n)
指定位置pos前插入(insert)找到目標位置前一個節點,插入新節點O(n)O(n)O(n)
頭刪(pop_front)保存原頭節點,頭指針后移,釋放原節點O(1)O(1)O(1)
尾刪(pop_back)遍歷至尾節點前一個節點,釋放尾節點O(n)O(n)O(n)
指定位置刪除(erase)找到目標位置前一個節點,釋放目標節點O(n)O(n)O(n)
清空(clear)從頭部開始,逐個釋放所有節點O(n)O(n)O(n)
  • 優點:
  1. 動態內存分配: 無需預先指定大小,按需分配內存
  2. 頭部操作高效: 頭刪頭插都是O(1)O(1)O(1)
  3. 插入刪除靈活: 不用像順序表一樣挪動大量數據,只需要修改指針
  • 缺點:
  1. 不支持隨機訪問: 訪問第n個元素需要從頭遍歷
  2. 尾部操作效率低: 尾插尾刪需要遍歷到尾部
  3. 指針管理復雜: 容易出現野指針、空指針問題
  4. 額外內存開銷: 每個節點需要額外空間存儲指針
  • 使用場景
  1. 需要頻繁進行頭部插入刪除操作
  2. 數量不確定,需要動態調整大小
  3. 內存有限,不適合順序表使用的場景
  4. 不需要隨機訪問元素

3. 雙向鏈表的實現

3.1 基本結構設計

雙向鏈表是帶頭雙向循環鏈表的簡稱,結合了三種思路:

  • **雙向:**每個節點有前驅指針_prev和后繼指針_next,方便前后遍歷
  • **循環:**鏈表尾節點_next指回哨兵位,哨兵位的_prev指向尾節點,整個鏈表形成一個環
  • **帶頭(帶哨兵位):**用一個特殊節點_sentinel作為表頭,不存儲有效數據,只起到占位、邊界作用

這樣做的優點是:

  • 統一了插入和刪除的邏輯,不用判別頭和尾
  • 空鏈表和非空鏈表的處理方式一致,封裝性更好
  • 代碼更簡潔,出錯率低

我們先設計一個內部節點結構Node

template <typename T>
struct Node {T _value;Node* _prev;Node* _next;Node(const T& val) : _value(val),_prev(nullptr),_next(nullptr){}
};

再設計鏈表類:double_list:

  • _sentinel:哨兵節點,始終存在。
  • _count:鏈表中有效節點的個數。
class double_list {
private:struct Node {T _value;Node* _prev;Node* _next;Node(const T& val) : _value(val),_prev(nullptr),_next(nullptr){}};Node* _sentinel; // 哨兵位size_t _count;
};

3.2 基本操作

3.2.1 初始化與銷毀

double_list() : _count(0) {// 初始化哨兵位 _prev  _next都指向自己_sentinel = new Node(T()); //不存儲有效數據_sentinel->_next = _sentinel;_sentinel->_prev = _sentinel;
}~double_list() { clear(); delete _sentinel; }

3.2.2 插入與刪除的核心邏輯

1. 在指定位置前插入

// 在pos節點前插入new_node
void insert_node(Node* pos, Node* new_node) {// 先確定好new_node的位置new_node->_prev = pos->_prev;new_node->_next = pos;// 這里順序不能反了pos->_prev->_next = new_node;pos->_prev = new_node;++_count;
}

在這里插入圖片描述

處理指針指向順序不能顛倒,要先考慮原有節點存在,這里詳細講一下,后續不再贅述。

對任意相鄰三點 A <-> B <-> C

  • A->_next == B
  • B->_prev == A
  • B->_next == C
  • C->_prev == B

做插入/刪除時,不要提前破壞老的不變量,要么先把新節點“準備好”,要么先把“鄰居接好”,保證任何時刻都能從 S 沿 _next_prev 找回環,不會丟失鏈。所以,在 pos 前插入 new_node 的正確順序為:設:B = pos A = B->_prev

安全順序(推薦)

  1. 先給新節點“自報家門”(只寫新節點,不動舊鏈)

    new_node->_prev = A;
    new_node->_next = B;
    
  2. 再接左邊的_next

    A->_next = new_node;
    
  3. 最后接右邊的_prev

    B->_prev = new_node;
    

這套順序的邏輯是:

  • 第1步只動 new_node,不破壞原有結構;
  • 第2步把 A 指向新節點,此時從左側走到 new_node 沒問題;
  • 第3步把 B 的前驅改為新節點,完成閉合;
  • 全程不會出現“鏈斷掉一截找不回來的窗口期”。

為什么不能顛倒?一個典型反例:

// ? 先改 B->_prev,再去用 pos->_prev
B->_prev = new_node;
B->_prev->_next = new_node; // 此時 B->_prev 已是 new_node,這行等價 new_node->_next = new_node,鏈炸

如果沒有緩存 A(而是反復寫 pos->_prev),一旦你先把 B->_prev 改成了 new_node,再通過 pos->_prev 想找到老的 A 就晚了——你拿到的是 new_node,從而把 new_node->_next 錯誤地指向 new_node 自己,造成環路破壞。

結論:如果你不緩存 A,就必須按先寫 new_node 的 _prev/_next,再寫 A->_next,最后寫 B->_prev的順序。
如果你緩存了:Node* A = B->_prev;,則可以適當調換 2、3 兩步的先后(先改 B->_prev 或先改 A->_next 都行),因為 A 已經固定,不會丟。

2. 刪除指定節點

// 刪除指定節點
void erase_node(Node* pos) {pos->_prev->_next = pos->_next;pos->_next->_prev = pos->_prev;delete pos;--_count;
}

在這里插入圖片描述

還是用這條鏈路舉例子:A <-> B <-> C

設:B = pos A = B->_prev C = B->_next

安全順序(必須這樣):

  1. 先把鄰居接好,繞過 B

    A->_next = C;
    C->_prev = A;
    
  2. 再釋放 B

    delete B;
    

為什么?

  • 如果你先 delete B,隨后再去訪問 B->_prevB->_next 來接回鄰居,就會解引用野指針,是未定義行為。
  • 先把 AC 直接連起來,保證環不斷;最后才安全釋放 B

常見錯誤:

delete B;              // ? 提前釋放
A->_next = B->_next;   // 立刻野指針解引用

有了這兩個基礎操作,頭插、尾插、頭刪、尾刪都能用它們實現。所以這兩個操作可以封裝到private中,僅供類自己使用。

3.2.3 常用操作

1. 頭插&尾插

// 頭插 在哨兵位后插入
void push_front(const T& val) {insert_node(_sentinel->_next, new Node(val));
}// 尾插 在哨兵位前插入
void push_back(const T& val) {insert_node(_sentinel, new Node(val));  
}

2. 頭刪&尾刪

// 頭刪 哨兵位的next
void pop_front() {if(empty()) throw std::out_of_range("List empty");erase_node(_sentinel->_next);
}// 尾刪 哨兵位的prev
void pop_back() {if (empty()) throw std::out_of_range("List empty");erase_node(_sentinel->_prev);
}

3. 訪問指定位置

// 訪問指定位置節點 用索引
T& at(int pos) {if(pos < 0 || pos >= _count) throw std::out_of_range("Index out of range");//創建臨時節點訪問鏈表// 哨兵位的next索引是0Node* cur = _sentinel->_next;for (size_t i = 0; i < pos; i++){cur = cur->_next;}return cur->_value;}

4. 指定位置插入&刪除

// 在pos位置節點前插入新節點
void insert(int pos, const T& val) {if(pos < 0 || pos > _count) throw std::out_of_range("Insert position out of range");// 要在pos前插入 就從哨兵位走Node* cur = _sentinel;for (size_t i = 0; i < pos; i++){cur = cur->_next;}insert_node(cur, new Node(val));}// 刪除pos位置的節點
void erase(int pos) {if(pos < 0 || pos >= _count) throw std::out_of_range("Erase position out of range");// 找pos位置 從索引為零的位置開始Node* cur = _sentinel->_next;for (size_t i = 0; i < pos; i++){cur = cur->_next;}erase_node(cur);
}

3.3 其他輔助函數

打印鏈表

void Print() {std::cout << "_sentinel->";Node* cur = _sentinel->_next;while (cur != _sentinel) {std::cout << cur->_value << "->";cur = cur->_next;}std::cout << "_sentinel" << std::endl;
}

清空鏈表

// 清空鏈表 保留哨兵位
void clear() {Node* cur = _sentinel->_next;while (cur != _sentinel) {Node* tmp = cur;cur = cur->_next;delete tmp;}_sentinel->_next = _sentinel;_sentinel->_prev = _sentinel;_count = 0;
}

判斷鏈表是否為空

bool empty() const { return _count == 0; }

返回鏈表長度

size_t size() const { return _count; }

3.4 總結

操作說明時間復雜度
push_front(val)頭插,在哨兵后插入新節點O(1)O(1)O(1)
push_back(val)尾插,在哨兵前插入新節點O(1)O(1)O(1)
insert(pos, val)在第 pos 個位置前插入新節點(需遍歷到 pos)O(n)O(n)O(n)
insert_node已知節點 pos 前插入 new_node,只改指針O(1)O(1)O(1)
pop_front()頭刪,刪除第一個節點O(1)O(1)O(1)
pop_back()尾刪,刪除最后一個節點O(1)O(1)O(1)
erase(pos)刪除第 pos 個節點(需遍歷到 pos)O(n)O(n)O(n)
erase_node刪除已知節點指針O(1)O(1)O(1)
at(pos)按索引訪問第 pos 個節點O(n)O(n)O(n)
  • 優點
  1. 帶哨兵節點:_sentinel 作為頭尾的“標志”,統一了邊界條件,避免空鏈表、頭刪、尾刪的特殊情況。
  2. 循環結構: 遍歷時不用判斷 nullptr,走到 _sentinel 就說明一圈結束。
  3. 雙向鏈表: 已知某個節點指針時,刪除/插入只需改前后兩個指針,操作 O(1)O(1)O(1)
  4. 動態存儲: 不需要連續內存,比數組更靈活。
  • 缺點
  1. 訪問效率低:atinserterase 都需要順序遍歷, O(n)O(n)O(n)
  2. 額外空間開銷大 每個節點需要兩個指針 _prev_next,比單鏈表開銷更高。
  3. 實現復雜: 插入/刪除時要維護兩個方向的指針,順序稍有不慎就會出現 bug(比如斷鏈、死循環)。

4. 與順序表對比

4.1 時間復雜度對比

操作數組 (順序表)單向鏈表雙向循環鏈表(帶頭)
按下標訪問O(1)O(1)O(1)O(n)O(n)O(n)O(n)O(n)O(n)
按值查找O(n)O(n)O(n)O(n)O(n)O(n)O(n)O(n)O(n)
頭插O(n)O(n)O(n)(需要整體移動)O(1)O(1)O(1)O(1)O(1)O(1)
尾插問O(1)O(1)O(1)(有容量時)O(1)O(1)O(1)O(1)O(1)O(1)
中間插入O(n)O(n)O(n)(移動元素)O(n)O(n)O(n)(遍歷到位置)O(n)O(n)O(n)(遍歷到位置)
頭刪O(n)O(n)O(n)(整體移動)O(1)O(1)O(1)O(1)O(1)O(1)
尾刪問O(1)O(1)O(1)O(n)O(n)O(n)(需找前驅)O(1)O(1)O(1)
刪除已知節點指針O(1)(僅數組下標已知時)O(n)O(n)O(n)(需找前驅)O(1)O(1)O(1)
空間使用連續空間,高效每節點額外 1 個指針每節點額外 2 個指針
遍歷O(n)O(n)O(n)O(n)O(n)O(n)O(n)O(n)O(n)

4.2優缺點總結

順序表

  • 優點:支持隨機訪問 O(1)O(1)O(1),內存利用率高,局部性好。
  • 缺點:插入/刪除開銷大(需要整體移動),容量固定或擴容開銷高。
  • 適用場景:頻繁訪問、較少插入刪除。

單鏈表

  • 優點:插入/刪除高效,內存分配靈活。
  • 缺點:不能快速找到前驅,刪除已知節點需 O(n)O(n)O(n),訪問慢。
  • 適用場景:需要頻繁插入/刪除,但多在鏈表頭部操作。

雙向鏈表(帶頭循環)

  • 優點
    • 插入/刪除效率高,尤其是頭尾操作 O(1)O(1)O(1)
    • 已知節點指針時,刪除/插入 O(1)O(1)O(1)
    • 帶頭哨兵,邏輯統一,不需要處理特殊情況。
  • 缺點
    • 空間開銷更大,每節點要兩個指針。
    • 隨機訪問仍是 O(n)O(n)O(n)
    • 實現復雜,指針操作容易出錯。
  • 適用場景:需要頻繁在任意位置插入/刪除的場景

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

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

相關文章

Claude Code賦能企業級開發:外賣平臺核心系統的智能化重構

開篇&#xff1a;萬億市場背后的技術挑戰中國外賣市場日訂單量超過1億單&#xff0c;每一單背后都是一個復雜的技術鏈條&#xff1a;用戶下單→商家接單→騎手搶單→實時配送→評價反饋。構建這樣一個支撐千萬級并發、涉及地理位置計算、實時調度、支付結算的超級平臺&#xff…

【使用Unsloth 微調】數據集的種類

1. 什么是數據集 對于大型語言模型&#xff08;LLMs&#xff09;&#xff0c;數據集是用于訓練模型的數據集合。為了訓練有效&#xff0c;文本數據需要能夠被分詞&#xff08;tokenized&#xff09;。創建數據集的關鍵部分之一是聊天模板&#xff08;chat template&#xff09;…

【碼蹄杯】2025年本科組省賽第一場

個人主頁&#xff1a;Guiat 歸屬專欄&#xff1a;算法競賽 文章目錄1. MC0455 四大名著-西游簽到2. MC0456 斬斷靈藤3. MC0457 符咒封印4. MC0458 移鐵術5. MC0459 昆侖墟6. MC0460 星空迷軌陣7. MC0461 排隊8. MC0462 最后一難正文 總共8道題。 1. MC0455 四大名著-西…

CentOS 10安裝Ollama

前置說明 linux服務器版本&#xff1a;CentOS10 ollama版本&#xff1a;v0.11.6 下載安裝包 下載安裝包 官網地址&#xff1a;Ollama 下載地址&#xff1a;Download Ollama 選擇linux平臺&#xff0c;由于使用官網提供的腳本直接安裝容易失敗&#xff0c;這里選擇手動下…

手機、電腦屏幕的顯示壞點檢測和成像原理

如今&#xff0c;手機和電腦屏幕已成為人們日常生活和工作中不可或缺的一部分。無論是處理文檔、觀看視頻&#xff0c;還是進行專業設計&#xff0c;屏幕的顯示質量都直接影響著用戶體驗。本文將介紹屏幕顯示的基本原理&#xff0c;包括RGB色素構成和成像機制&#xff0c;并進一…

文件與fd

文件與fd一、前置預備二、復習c語言文件三、系統文件認識3.1 系統層面有關文件的接口&#xff08;open&#xff09;&#xff1a;![在這里插入圖片描述](https://i-blog.csdnimg.cn/direct/b15577967d1445b08cd5252f2009683a.png)3.2 簡單使用open參數3.3 語言vs系統3.4 進一步理…

語義通信高斯信道仿真代碼

1?? 代碼 def AWGN(coding, snr, devicecpu):"""為輸入張量添加高斯白噪聲&#xff08;AWGN&#xff09;&#xff0c;根據指定的 SNR&#xff08;分貝&#xff09;控制噪聲強度。參數&#xff1a;coding (torch.Tensor): 輸入張量&#xff0c;形狀為 [batch_s…

unity中實現機械臂自主運動

目的&#xff1a;導入機械臂的fbx模型&#xff0c;利用C#編寫腳本實現機械臂的自主運動步驟1.在 Unity 中&#xff0c;右鍵點擊 “Assets” 文件夾&#xff0c;選擇 “Create” -> “C# Script” 來創建一個新的 C# 腳本命名為 “ArmController”。2.雙擊打開腳本&#xff0…

Python 版本與 package 版本兼容性檢查方法

網羅開發&#xff08;小紅書、快手、視頻號同名&#xff09;大家好&#xff0c;我是 展菲&#xff0c;目前在上市企業從事人工智能項目研發管理工作&#xff0c;平時熱衷于分享各種編程領域的軟硬技能知識以及前沿技術&#xff0c;包括iOS、前端、Harmony OS、Java、Python等方…

深入剖析分布式事務的Java實現:從理論到Seata實戰

文章目錄深入剖析分布式事務的Java實現&#xff1a;從理論到Seata實戰引言&#xff1a;分布式事務的現實挑戰1. 分布式事務理論基礎1.1 從ACID到CAP/BASE1.2 典型業務場景分析2. 主流分布式事務解決方案對比2.1 技術方案全景圖2.2 選型建議3. Seata框架深度解析3.1 Seata架構設…

自建知識庫,向量數據庫 (十一)之 量化對比余弦——仙盟創夢IDE

向量比對&#xff1a;開啟企業經營自動化搜索新視野在當今數字化時代&#xff0c;企業經營自動化已成為提升競爭力的關鍵。其中&#xff0c;搜索功能作為企業獲取信息、連接用戶與資源的重要入口&#xff0c;其效率和準確性直接影響企業的運營效率和用戶體驗。向量比對在企業經…

Spring Cloud系列—SkyWalking告警和飛書接入

上篇文章&#xff1a; Spring Cloud系列—SkyWalking鏈路追蹤https://blog.csdn.net/sniper_fandc/article/details/149948321?fromshareblogdetail&sharetypeblogdetail&sharerId149948321&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 目…

【問題】解決docker的方式安裝n8n,找不到docker.n8n.io/n8nio/n8n:latest鏡像的問題

問題概覽 用docker方式安裝n8n&#xff0c;遇到錯誤&#xff0c;安裝不了的問題&#xff1a; Unable to find image docker.n8n.io/n8nio/n8n:latest locally docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request can…

機器人控制基礎:串級PID控制算法的參數如何整定?

目錄 一、整定前的準備 二、內環(副環)參數整定(核心步驟) 1. 斷開主環,單獨測試內環 2. 內環參數整定(按 “比例→積分→微分” 順序) (1)比例系數(kp)整定 (2)積分系數(ki)整定 (3)微分系數(kd)整定(可選) 3. 驗證內環抗擾動能力 三、外環(主…

HTTP性能優化實戰指南(含代碼/圖表/案例)

HTTP性能優化實戰指南&#xff08;含代碼/圖表/案例&#xff09;一、性能優化關鍵指標TTFB&#xff08;Time To First Byte&#xff09;: 服務器響應時間FCP&#xff08;First Contentful Paint&#xff09;: 首內容渲染時間LCP&#xff08;Largest Contentful Paint&#xff0…

QT代碼框架小案例:一個簡單的時間類(Time)及其實例化程序,模擬了時間的設置、顯示和自動流逝功能,類似一個簡易電子時鐘。

一、代碼框架二、運行終端顯示三、代碼詳細注釋test.pro# 指定項目類型為應用程序&#xff08;而非庫或其他類型&#xff09; TEMPLATE app# 配置項目&#xff1a;啟用控制臺輸出&#xff0c;使用C11標準 CONFIG console c11# 移除配置&#xff1a;不生成應用程序捆綁包&…

Nacos-11--Nacos熱更新的原理

在Nacos中&#xff0c;當監聽到配置變化后&#xff0c;Nacos提供了相關機制&#xff08;長輪詢或gRPC&#xff09;讓客戶端能夠監聽到配置的變化&#xff0c;并觸發相應的監聽器&#xff08;Listener&#xff09;&#xff0c;但具體的處理邏輯需要根據實際需求來實現。 1、熱更…

fastapi 的BackgroundTasks

什么是 BackgroundTasks&#xff1f;BackgroundTasks 是 FastAPI 提供的一個強大工具&#xff0c;它允許你將一些非緊急的、耗時的操作&#xff08;例如發送郵件、處理數據、調用第三方 API 等&#xff09;放到“后臺”去執行&#xff0c;而不是讓用戶一直等待這些操作完成。它…

Python 十進制轉二進制

在 Python 中&#xff0c;將十進制整數轉換為二進制有多種方法。以下是幾種常見的方式&#xff1a;1. 使用 bin() 函數bin() 是 Python 內置函數&#xff0c;可以將十進制整數轉換為二進制字符串。語法bin(n)示例n 13 binary_str bin(n) print(binary_str) # 輸出: 0b1101說…

合并工作表,忽略手動隱藏行超簡單-Excel易用寶

同事小麗有一個工作簿&#xff0c;文件中有多個工作表&#xff0c;每個工作表中有多行數據&#xff0c;這些表格中數據是有手動隱藏行的&#xff0c;她想把這些表格的數據忽略隱藏行合并到一個工作表中&#xff0c;但是使勁渾身解數&#xff0c;各種折騰&#xff0c;都會把隱藏…