【數據結構】跳表的概率模型詳解與其 C 代碼實現

文章目錄

  • 介紹
    • 關鍵組成部分
    • 讀者可以比對這張圖片去理解跳表 ![在這里插入圖片描述](https://i-blog.csdnimg.cn/direct/c5704b6276a14c3f9facdc3e55015bcc.jpeg#pic_center) 核心操作原理
    • 算法的概率模型
  • 跳表的 C代碼實現
    • 初始化跳躍表的節點、跳躍表本身
    • 跳表插入節點
    • 查找元素
    • 更新元素值
    • 刪除跳表的某個節點
    • 跳表的范圍查詢
    • 銷毀跳躍表
    • 按層級打印跳躍表
    • 測試代碼(主函數)

推薦一個零聲教育學習教程,個人覺得老師講得不錯,分享給大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK等技術內容,點擊立即學習: https://github.com/0voice 鏈接。

介紹

跳表本質上是一種概率性的、有序的鏈表數據結構。它的核心目標是在有序鏈表的基礎上,通過添加多層索引來加速查找、插入和刪除操作,使得這些操作的平均時間復雜度能達到 O(log n),媲美平衡二叉搜索樹(如AVL樹、紅黑樹),但通常實現起來更簡單,并且在某些并發場景下表現更好。

  1. 數據庫與存儲系統
    LevelDB/RocksDB
    應用:內存表(MemTable)實現
    優勢:高效的范圍查詢支持(SSTable構建);簡單的并發控制;自然的鍵排序

  2. 數據庫與存儲系統
    Apache Cassandra
    應用:內存中的有序數據結構
    優勢:支持快速范圍掃描和點查詢

  3. 緩存系統
    Redis有序集合(Sorted Set)
    應用:有序集合的底層實現之一
    優勢:
    1、O(log N) 的ZADD、ZRANGE、ZREM操作
    2、高效的范圍查詢(ZRANGEBYSCORE)
    3、與哈希表結合提供O(1)的點查詢

  4. 搜索引擎與倒排索引
    Lucene/Elasticsearch
    應用:詞典存儲和區間查詢
    優勢:快速術語查找;支持范圍查詢(如日期范圍);高效的內存使用

  5. 網絡路由與協議
    分布式哈希表(DHT)
    應用:Chord協議等P2P網絡
    優勢:高效的關鍵字查找(O(log N)跳);自然支持區間查詢

  6. 實時數據處理
    1、時間序列數據庫
    應用:存儲時間戳數據點
    優勢:高效的時間范圍查詢;按時間順序插入;支持滾動窗口統計
    2、金融交易系統
    應用:訂單簿管理
    優勢:快速價格查詢;高效的范圍掃描(獲取買賣盤)

相比于各種樹(紅黑樹、B/B+樹、SPLAY 樹),跳表的好處是很多的。

場景跳表優勢平衡樹劣勢
并發環境簡單的細粒度鎖或無鎖實現復雜的全局重平衡
范圍查詢底層鏈表自然支持順序訪問需要復雜的中序遍歷
實現復雜度代碼簡單,調試容易旋轉操作復雜,易出錯
內存局部性節點獨立,緩存友好指針密集,緩存不友好
性能預測平均O(log N),實踐穩定最壞情況保證但常數因子大

你可以把它想象成在一個有序的單鏈表(第0層)上,建立了一層或多層的“快速通道”(高層索引)。高層索引跨越更多的低層節點,讓你能更快地定位到目標區域。

跳表就像并排隨機生長的小草,我們查找數據就是類似于玩馬里奧游戲,從最左邊某處跳下,先跳到離自己最近的長得最高的那棵草上,然后平級跳躍,直至不能再跳才下降一層找其他地方跳。

在這里插入圖片描述
再仔細想一想,他真的很像一群并排生長的小草,小草的植高是隨機的。
在這里插入圖片描述

關鍵組成部分

  • 節點:
    存儲實際的數據值(Key)。
    包含一個forward指針數組(或叫next數組)。這個數組的大小等于該節點所在的層數(Level)。
    forward[i] 指向該節點在第 i 層上的下一個節點。

  • 層:
    第0層: 最底層,包含所有元素,是一個完整的有序鏈表。
    第1層及以上: 索引層。每一層都是其下一層的子集(一個更稀疏的有序鏈表)。層級越高,包含的節點越少,跨度越大。

  • 頭節點: 有一個特殊的頭節點(Head),它的層數等于跳表當前的最大層數(MaxLevel)。頭節點的 forward[i] 指向第 i 層的第一個實際節點(如果存在)。實際上是個一個哨兵節點。

  • 最大層數: 一個預先設定的或動態調整的限制,防止層數無限增長。通常用 MaxLevel 表示。

  • 隨機層數: 這是跳表“概率性”的核心。當插入一個新節點時,不是固定地把它加到所有層,而是用類似拋硬幣的方式(隨機算法)決定它應該出現在哪些層。常見方法是:

    • 從第1層開始(第0層肯定包含)。
    • 生成一個隨機數(比如0或1)。
      • 如果結果是“正面”(例如1),則將該節點添加到當前層,并嘗試向上一層(層數+1),重復拋硬幣。
      • 如果結果是“反面”(例如0),則停止增加層數。
    • 確保節點至少在第0層(所以通常從第1層開始“拋硬幣”)。

最終節點的層數 lvl 是一個介于1和MaxLevel之間的隨機值(以1為起點)。這個節點將出現在第0層到第 lvl-1 層(或第1層到第 lvl 層,取決于定義)。

讀者可以比對這張圖片去理解跳表
在這里插入圖片描述
核心操作原理

  • 查找:

    • 從最高層開始: 從頭節點的最高層開始。
    • 向右遍歷: 沿著當前層的 forward 指針向右移動,直到下一個節點的值大于等于目標值。
    • 向下一層: 如果下一個節點的值大于目標值,或者當前層沒有下一個節點了,則下降到下一層。
    • 重復: 重復步驟2和3,直到下降到第0層。
    • 檢查: 在第0層,當前節點的下一個節點如果等于目標值,則找到;否則不存在。
  • 插入:

    • 查找插入位置: 執行與查找類似的過程,但在下降過程中記錄每一層可能需要更新的前驅節點(即在新節點插入后,其 forward 指針需要指向新節點的那些節點)。這些節點存儲在 update 數組中,update[i] 保存第 i 層最后一個小于新節點值的節點。
  • 生成隨機層數: 使用拋硬幣法決定新節點的層數 lvl。

  • 創建新節點: 創建一個層數為 lvl 的新節點。

  • 更新指針:(可選)更新最大層數: 如果 lvl 大于當前跳表的最大層數,則更新頭節點的層數和跳表的 MaxLevel。

    • 對于每一層 i (從0 到 lvl-1):
      新節點的 forward[i] = update[i].forward[i]
      update[i].forward[i] = 新節點
  • 刪除:

    • 查找目標節點: 執行與查找類似的過程,同樣記錄每一層可能需要更新的前驅節點(存儲在 update 數組中),這些節點的 forward 指針指向目標節點(或即將指向)。
    • 找到目標節點: 在第0層,update[0].forward[0] 應該就是目標節點(如果存在)。
  • 更新指針:
    對于每一層 i (從0 到 目標節點層數-1):
    如果 update[i].forward[i] 等于目標節點,則 update[i].forward[i] = 目標節點.forward[i]

  • 刪除節點: 釋放目標節點內存。
    (可選)降低最大層數: 如果刪除的是最高層的唯一節點,可能需要降低頭節點的層數和跳表的 MaxLevel。

讀者可以繼續參考這張圖
在這里插入圖片描述

算法的概率模型

每個節點的層高有一套統計學操作程序決策,插入一個節點后,采用隨機數判斷是否提高層數。只要隨機數都指向要上升一級,那就層數加 1;但是只要某一次沒有指向層數上升的決定,那么決策終止,節點的層高就此敲定。

如果本科學過概率論,就會發現這就是經典的幾何分布。

幾何分布(Geometric Distribution)

  • 定義:在一次伯努利試驗中,成功的概率為 p(0 < p ≤ 1),失敗的概率為 q = 1 – p。
    獨立重復地進行該試驗,直到第一次成功為止,所進行的試驗總次數記為隨機變量 X。
    則 X 服從參數為 p 的幾何分布,記作?X~Ge(p)X \sim Ge(p)XGe(p)
  • 分布律(概率質量函數,PMF)
    對于 k = 1, 2, 3, … P(X=k)=qk?1pP(X = k) = q^{k-1} pP(X=k)=qk?1p
    解釋:前 k – 1 次均失敗(概率 q^{k-1}),第 k 次首次成功(概率 p)。
  • 期望(均值)
    ??E[X] = 1 / p
  • 方差(誤差)
    ??Var(X) = q / p2 = (1 – p) / p2

期望與方差的計算都是使用無窮級數里的冪級數函數項級數的技巧方法,讀者可以參考 《數學分析》。

對于我的 C 代碼實現,一般來說,每次決策都是有一半的概率提升層數,也有一半的概率不提升節點的層數,故而平均層數為兩層,
E[X]=1/p=2,當p=0.25時.E[X] = 1 / p=2, 當 p= 0.25 時.E[X]=1/p=2,p=0.25.

算法的查找復雜度(跳表總體層高 依較大概率 小于某個數)

我們記 NmaxN_{max}Nmax? 為節點總數為 NNN 時的總體層高(它是一個隨機變量),記 K(N) 為隨機變量 NmaxN_{max}Nmax? 的某個概率上界。一個節點的層高大于 K(N) 的概率是 1?12K(N)1-\frac{1}{2^{K(N)}}1?2K(N)1?,故而

P(Nmax≤K(N))≤(1?12K(N))NP(N_{max} \leq K(N)) \leq (1-\frac{1}{2^{K(N)}})^NP(Nmax?K(N))(1?2K(N)1?)N

此時我們發現,如果 K(N)=2?log?2NK(N) =2*\log_2 NK(N)=2?log2?N
P(Nmax≤K(N))≤(1?1N2)N=(1e)N→0,當N→∞.P(N_{max} \leq K(N)) \leq (1-\frac{1}{N^2})^N=(\frac{1}{e})^N\to 0,\; 當\;N\to \infty.P(Nmax?K(N))(1?N21?)N=(e1?)N0,N∞.

所以我們可以算出跳躍表的層高是依概率小于 2log?2N2\log_2 N2log2?N 的。

跳表的 C代碼實現

我們需要明確的一點是
數據結構=數據定義+數據的操作方法。數據結構 = 數據定義 + 數據的操作方法。 數據結構=數據定義+數據的操作方法。
首先是數據定義,每個節點都帶有一個跳躍數組 forword,與鍵值對。跳表需要包括頭節點(頭節點是一個哨兵節點)、當前元素數量、當前層高。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>//  跳表就像并排隨機生長的小草,我們查找數據就是類似于玩馬里奧游戲,從最左邊某處跳下,先跳到離自己最近的長得最高的那棵草上,然后平級跳躍,直至不能再跳才下降一層找其他地方跳
// 跳表最大層數,越大說明稀疏程度越高
#define MAX_LEVEL 16// 跳表節點結構
typedef struct Node {int key;int value;struct Node **forward;  // 每層的前進指針數組
} Node;// 跳表結構
typedef struct SkipList {Node *header;           // 頭節點int level;              // 當前最大層數int size;               // 元素數量
} SkipList;

初始化跳躍表的節點、跳躍表本身

// 創建新節點
Node* create_node(int level, int key, int value) {Node *node = (Node*)malloc(sizeof(Node));node->key = key;node->value = value;node->forward = (Node**)malloc(sizeof(Node*) * (level + 1));for (int i = 0; i <= level; i++) {node->forward[i] = NULL;}return node;
}// 初始化跳表
SkipList* create_skip_list() {SkipList *sl = (SkipList*)malloc(sizeof(SkipList));sl->level = 0;sl->size = 0;sl->header = create_node(MAX_LEVEL, INT_MIN, 0); // 頭節點鍵值為最小值,頭節點的層高一開始也是為 0// 初始化頭節點的每層指針for (int i = 0; i <= MAX_LEVEL; i++) {sl->header->forward[i] = NULL;}srand(time(NULL)); // 初始化隨機數種子return sl;
}

跳表插入節點

我們可以使用奇數與偶數的均勻二分,從而實現成功/失敗概率的 1:1。插入,首先進行的是跳躍查找定位到專門的節點。

// 隨機生成節點層數(偶數與奇數對半分)
int random_level() {int level = 0;while (rand() % 2 == 0 && level < MAX_LEVEL) {level++;}return level;
}// 插入元素
void insert(SkipList *sl, int key, int value) {Node *update[MAX_LEVEL + 1];    //  記錄條,記錄 key 的下確界Node *current = sl->header;// 從最高層開始查找插入位置for (int i = sl->level; i >= 0; i--) {while (current->forward[i] != NULL && current->forward[i]->key < key) {current = current->forward[i];}update[i] = current;}// 移動到第0層的下一個節點current = current->forward[0];// 如果鍵已存在,更新值if (current != NULL && current->key == key) {current->value = value;return;}// 隨機生成新節點層數int new_level = random_level();// 如果新節點層數大于當前跳表層數,更新高層指針if (new_level > sl->level) {for (int i = sl->level + 1; i <= new_level; i++) {update[i] = sl->header;}sl->level = new_level;}// 創建新節點Node *new_node = create_node(new_level, key, value);// 更新指針for (int i = 0; i <= new_level; i++) {new_node->forward[i] = update[i]->forward[i];update[i]->forward[i] = new_node;}sl->size++;
}

查找元素

// 查找元素
Node* search(SkipList *sl, int key) {Node *current = sl->header; //  current 是下確界// 從最高層開始查找for (int i = sl->level; i >= 0; i--) {while (current->forward[i] != NULL && current->forward[i]->key < key) {current = current->forward[i];}}// 移動到第0層的下一個節點current = current->forward[0];if (current != NULL && current->key == key) {return current;}return NULL;
}

更新元素值

// 更新元素值
void update(SkipList *sl, int key, int new_value) {Node *node = search(sl, key);if (node != NULL) {node->value = new_value;}
}

刪除跳表的某個節點

// 刪除元素
void delete(SkipList *sl, int key) {Node *update[MAX_LEVEL + 1];    // update 是下確界Node *current = sl->header;// 從最高層開始查找for (int i = sl->level; i >= 0; i--) {while (current->forward[i] != NULL && current->forward[i]->key < key) {current = current->forward[i];}update[i] = current;}// 移動到第0層的下一個節點current = current->forward[0];// 如果節點存在則刪除if (current != NULL && current->key == key) {// 更新各層指針for (int i = 0; i <= sl->level; i++) {if (update[i]->forward[i] != current) break;update[i]->forward[i] = current->forward[i];}// 釋放節點內存free(current->forward);free(current);// 更新跳表層數while (sl->level > 0 && sl->header->forward[sl->level] == NULL) {sl->level--;}sl->size--;}
}

跳表的范圍查詢

我們可以類比的是 B+ 樹的范圍查詢,因為它和 B+ 樹的范圍查詢一樣好用。讀者可以參考我寫的關于 B+ 樹的博客。

// 范圍查詢(高效實現)
void range_query(SkipList *sl, int start_key, int end_key) {Node *current = sl->header;// 定位到起始位置for (int i = sl->level; i >= 0; i--) {while (current->forward[i] != NULL && current->forward[i]->key < start_key) {current = current->forward[i];}}// 移動到起始節點current = current->forward[0];// 遍歷范圍內的節點printf("Range query [%d, %d]:\n", start_key, end_key);while (current != NULL && current->key <= end_key) {printf("  (%d, %d)\n", current->key, current->value);current = current->forward[0];}
}

銷毀跳躍表

// 銷毀跳表
void free_skip_list(SkipList *sl) {Node *current = sl->header;Node *next;// 釋放所有節點,跳表往右收縮,先刪頭節點while (current != NULL) {next = current->forward[0];free(current->forward);free(current);current = next;}//free(sl->header);free(sl);printf("all component have been release\n");
}

按層級打印跳躍表

// 打印跳表結構(調試用)
void print_skip_list(SkipList *sl) {printf("Skip List (level=%d, size=%d):\n", sl->level, sl->size);for (int i = sl->level; i >= 0; i--) {Node *node = sl->header->forward[i];printf("Level %d: ", i);while (node != NULL) {printf("%d(%d) → ", node->key, node->value);node = node->forward[i];}printf("NULL\n");}
}

測試代碼(主函數)

test_num 是指代測試規模。我們先插入一堆節點,然后按層級打印,緊接著是更改、查詢操作,最后是范圍查詢、刪除一個節點后再展示層級打印。


#define test_num 100
int main() {SkipList *sl = create_skip_list();for (int i=0; i<test_num; i++) {insert(sl, i+3, 3*i+17);}// 打印跳表結構print_skip_list(sl);// 查找示例Node *found = search(sl, 5);if (found) {printf("\nFound key 5, value=%d\n", found->value);}// 更新示例update(sl, 5, 555);printf("After update key 5: ");found = search(sl, 5);if (found) printf("value=%d\n", found->value);// 范圍查詢示例range_query(sl, test_num, 3*test_num);// 刪除示例delete(sl, 5);printf("\nAfter deleting key 5:\n");print_skip_list(sl);// 銷毀跳表free_skip_list(sl);return 0;
}

代碼運行

qiming@qiming:~/share/CTASK/data-structure$ gcc -o skiplist skiplist.c
qiming@qiming:~/share/CTASK/data-structure$ ./skiplist 
Skip List (level=7, size=100):
Level 7: 77(239) → NULL
Level 6: 77(239) → NULL
Level 5: 57(179)77(239) → NULL
Level 4: 30(98)57(179)68(212)77(239)88(272) → NULL
Level 3: 16(56)30(98)39(125)57(179)58(182)62(194)66(206)68(212)77(239)80(248)88(272)97(299) → NULL
Level 2: 5(23)14(50)16(56)18(62)30(98)39(125)57(179)58(182)62(194)66(206)68(212)77(239)80(248)88(272)90(278)94(290)96(296)97(299) → NULL
Level 1: 5(23)7(29)11(41)12(44)14(50)16(56)17(59)18(62)22(74)25(83)26(86)27(89)30(98)31(101)33(107)39(125)40(128)44(140)45(143)48(152)49(155)54(170)55(173)56(176)57(179)58(182)62(194)63(197)66(206)68(212)70(218)74(230)77(239)80(248)88(272)90(278)92(284)93(287)94(290)96(296)97(299) → NULL
Level 0: 3(17)4(20)5(23)6(26)7(29)8(32)9(35)10(38)11(41)12(44)13(47)14(50)15(53)16(56)17(59)18(62)19(65)20(68)21(71)22(74)23(77)24(80)25(83)26(86)27(89)28(92)29(95)30(98)31(101)32(104)33(107)34(110)35(113)36(116)37(119)38(122)39(125)40(128)41(131)42(134)43(137)44(140)45(143)46(146)47(149)48(152)49(155)50(158)51(161)52(164)53(167)54(170)55(173)56(176)57(179)58(182)59(185)60(188)61(191)62(194)63(197)64(200)65(203)66(206)67(209)68(212)69(215)70(218)71(221)72(224)73(227)74(230)75(233)76(236)77(239)78(242)79(245)80(248)81(251)82(254)83(257)84(260)85(263)86(266)87(269)88(272)89(275)90(278)91(281)92(284)93(287)94(290)95(293)96(296)97(299)98(302)99(305)100(308)101(311)102(314) → NULLFound key 5, value=23
After update key 5: value=555
Range query [100, 300]:(100, 308)(101, 311)(102, 314)After deleting key 5:
Skip List (level=7, size=99):
Level 7: 77(239) → NULL
Level 6: 77(239) → NULL
Level 5: 57(179)77(239) → NULL
Level 4: 30(98)57(179)68(212)77(239)88(272) → NULL
Level 3: 16(56)30(98)39(125)57(179)58(182)62(194)66(206)68(212)77(239)80(248)88(272)97(299) → NULL
Level 2: 14(50)16(56)18(62)30(98)39(125)57(179)58(182)62(194)66(206)68(212)77(239)80(248)88(272)90(278)94(290)96(296)97(299) → NULL
Level 1: 7(29)11(41)12(44)14(50)16(56)17(59)18(62)22(74)25(83)26(86)27(89)30(98)31(101)33(107)39(125)40(128)44(140)45(143)48(152)49(155)54(170)55(173)56(176)57(179)58(182)62(194)63(197)66(206)68(212)70(218)74(230)77(239)80(248)88(272)90(278)92(284)93(287)94(290)96(296)97(299) → NULL
Level 0: 3(17)4(20)6(26)7(29)8(32)9(35)10(38)11(41)12(44)13(47)14(50)15(53)16(56)17(59)18(62)19(65)20(68)21(71)22(74)23(77)24(80)25(83)26(86)27(89)28(92)29(95)30(98)31(101)32(104)33(107)34(110)35(113)36(116)37(119)38(122)39(125)40(128)41(131)42(134)43(137)44(140)45(143)46(146)47(149)48(152)49(155)50(158)51(161)52(164)53(167)54(170)55(173)56(176)57(179)58(182)59(185)60(188)61(191)62(194)63(197)64(200)65(203)66(206)67(209)68(212)69(215)70(218)71(221)72(224)73(227)74(230)75(233)76(236)77(239)78(242)79(245)80(248)81(251)82(254)83(257)84(260)85(263)86(266)87(269)88(272)89(275)90(278)91(281)92(284)93(287)94(290)95(293)96(296)97(299)98(302)99(305)100(308)101(311)102(314) → NULL
all component have been release
qiming@qiming:~/share/CTASK/data-structure$ 

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

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

相關文章

Verilog實現除法器

文章目錄基本原理確定除數最高位移位相減基本原理 若想得到yx\frac{y}{x}xy?的商和余數&#xff0c;一種最直觀的想法就是不斷地用yyy減掉xxx&#xff0c;直到y<xy< xy<x為止&#xff0c;寫成偽代碼如下 z 0 while y<x:y - xz 1這種算實在是太低效了&#xff…

EasyLive的一些疑問

目錄 一、pinia是什么 二、html的代碼片段又失效&#xff1f; 三、Request.js 四 、狀態管理庫 五、main.js:19 Uncaught SyntaxError: The requested module /src/utils/Api.js?t1745328489985 does not provide an export named default (at main.js:19:8)?編輯 六、…

C++(String):

目錄 string與C中字符串的區別&#xff1a; C字符串&#xff1a; string字符串&#xff1a; string的定義和初始化&#xff1a; 輸入字符串&#xff1a; 方式1&#xff1a; 方式2&#xff1a; 字符串的拼接的操作&#xff1a; 方式1&#xff1a;使用“” 方式2&#…

【Linux】Java線上問題,一分鐘日志定位

【Linux】Java線上問題&#xff0c;一分鐘日志定位1. 查看異常堆棧2. 實時叮新日志3. 翻歷史/壓縮日志4. 統計異常數量5. 多種異常一起查6. 反向過濾7. 同時滿足多個關鍵字查詢8. 定位最近一次異常9. 異常排行榜1. 查看異常堆棧 # 在 a.log 文件中查找包含 NullPointerExcepti…

智慧農業溫室大棚遠程監控物聯網系統解決方案

一、方案背景與目標隨著現代農業向智能化、精準化轉型&#xff0c;傳統溫室大棚管理面臨效率低、響應慢、成本高等痛點。本方案通過部署御控農業物聯網系統&#xff0c;實現溫室環境參數實時監測、設備遠程控制、數據智能分析及預警決策&#xff0c;助力農戶降低人工成本&#…

【剖析高并發秒殺】從流量削峰到數據一致性的架構演進與實踐

一、 挑戰&#xff1a;三高背景下的數據庫瓶頸秒殺場景的核心挑戰可以歸結為“三高”&#xff1a;高并發、高性能、高可用。而系統中最脆弱的一環&#xff0c;往往是我們的關系型數據庫&#xff08;如MySQL&#xff09;。它承載著最終的數據落地&#xff0c;其連接數、IOPS和CP…

Redisson最新版本(3.50.0左右)啟動時提示Netty的某些類找不到

文章目錄一、寫在前面二、解決方案1、解決方案2、一勞永逸3、確定redisson依賴netty的版本一、寫在前面 Redisson最新版本&#xff0c;大概3.47.0&#xff0c;在JDK8環境下&#xff08;實測JDK17也一樣&#xff09;會提示Netty的某些類找不到&#xff1a; Exception in threa…

MTK Linux DRM分析(八)- KMS drm_crtc.c

一、簡介 Linux DRM(Direct Rendering Manager)子系統是內核中管理圖形硬件的核心組件,而 CRTC(CRT Controller)又是其中的關鍵之一。它起源于過去控制陰極射線管(CRT)顯示器的控制器概念,如今在現代圖形顯示中依舊扮演著至關重要的角色。 可以把 CRTC 想象成圖形顯示…

vue+openlayers示例:適配arcgis矢量瓦片服務以及樣式(附源碼下載)

由于單位這邊有個項目是基于openlayers地圖引擎框架實現webgis地圖可視化功能&#xff0c;但是要調用第三方的arcgis矢量瓦片服務以及適配樣式&#xff0c;在這個背景下&#xff0c;基于openlayersvue實現適配arcgis矢量瓦片服務以及樣式效果&#xff0c;適合學習openlayers與前…

mybatis xml中表名 字段報紅解決

mybatis xml中表名 字段報紅解決

谷歌瀏覽器重定向url,谷歌瀏覽器瀏覽網頁修改url到本地

谷歌應用商店搜索插件requestly&#xff08;有個相似名稱的插件&#xff0c;選擇這個Requestly: Supercharge your Development & QA&#xff09; 安裝后打開插件網址https://app.requestly.io/rules/my-rules 新建規則rules->my rules-> new rule -> redirect …

教育場景下禁用html5播放器拖動進度條的例子

禁用視頻課程進度條的拖動功能&#xff0c;主要是為了強制學員按照課程設計的順序觀看內容&#xff0c;防止跳過關鍵知識點&#xff0c;從而保證學習效果和課程的完整性。 1.防止應試作弊&#xff1a; 在一些需要觀看視頻才能解鎖下一章節或完成測試的場景中&#xff0c;禁用…

async實戰

一、協程 協程是程序員人為創造 協程是一種用戶態內的上下文切換技術。通過一個線程實現代碼塊相互切換執行。yield返回生成器 yield from 代表&#xff0c;跳到 func2協程函數 通過函數名()&#xff0c;是執行不了的。需要把函數加入到loop里面來&#xff0c;才可以被執行。 把…

個人搭建小網站教程(云服務器Ubuntu版本)

目錄 1.配置云服務器&#xff08;略講&#xff09; 2.vscode連接&#xff08;ssh連接&#xff09; 3.本地壓縮項目包 4.傳輸項目 5.配置項目依賴 6.運行項目 1.啟動 FastAPI 后端&#xff08;Python 部分&#xff09; 2.啟動 Next.js 前端&#xff08;Node.js 部分&…

pion/webrtc v4.1.4 版本發布:關鍵特性與性能優化全面解析

引言 實時通信技術在現代互聯網應用中扮演著越來越重要的角色&#xff0c;從視頻會議到在線教育&#xff0c;從遠程醫療到物聯網設備交互&#xff0c;WebRTC技術已經成為實時音視頻通信的事實標準。作為Go語言中最成熟且廣泛使用的WebRTC實現&#xff0c;pion/webrtc項目持續推…

集成算法(聚類)

下面簡單集成算法代碼from sklearn.datasets import make_blobs from sklearn.cluster import KMeans import matplotlib.pyplot as plt# 創建數據集&#xff0c;生成 3 個中心的聚類數據&#xff0c;共 300 個樣本&#xff0c;每個樣本 2 個特征 X, _ make_blobs(n_samples30…

01 網絡信息內容安全--緒論

1 課程內容 網絡信息內容獲取技術網絡信息內容預處理技術網絡信息內容過濾技術社會網絡分析技術入侵檢測技術異常流量檢測技術對抗攻擊技術 2 理論研討 分為16個組 2.1 網絡信息內容獲取技術&#xff1a;第1組 【用DeepSeek網站爬蟲&#xff0c;數據獲取零成本&#xff01…

GPT-5:天變了嗎?還是風停了?

2025年8月8日&#xff0c;OpenAI 發布了 GPT-5。這次更新被許多人寄予厚望&#xff0c;也引發了不少爭議。對普通用戶來說&#xff0c;這是一場“又快又會做事”的智能盛宴&#xff1b;而對資深開發者和 AI 研究者而言&#xff0c;GPT-5 可能更像是一次不夠激進、略顯保守的版本…

生信分析自學攻略 | R語言數據篩選和修改

在《生信小白自學攻略》系列的前幾篇文章中&#xff0c;我們已經了解了 R 和 RStudio 的安裝、RStudio 的深度探索&#xff0c;以及 R 語言的基本數據類型和數據結構。現在&#xff0c;是時候深入探討如何運用 R 語言對數據進行精細化處理了。本篇推文將詳細介紹如何在 R 中對數…

從零開始學習概念物理(第13版)(1)

前言&#xff1a;對我來說&#xff0c;最有用的就是物理了&#xff0c;尤其是電磁學。但是要學好它&#xff0c;我得夯實我的基礎&#xff0c;前面更加基礎的數學和物理都不能拉下。現在我問了Deepseek推薦的國外物理書&#xff0c;這本《概念物理》是最適合我&#xff0c;等入…