數據結構 雙鏈表與LinkedList

本節目標:

  1. 認識并且能夠實現一個雙鏈表
  2. 認識LinkedList類并且知道如何去使用

1.雙鏈表

概念

在數據結構中,雙鏈表(Doubly Linked List)?是一種常見的線性數據結構,它由一系列節點組成,每個節點不僅包含數據域,還包含兩個指針域,分別指向其前驅節點(前一個節點)?和后繼節點(后一個節點)。這種結構相比單鏈表(僅含后繼指針),在節點訪問和操作上更加靈活,可以表示成這樣:

相較于單鏈表,雙鏈表多了一個前驅節點prev,它儲存的是這個節點的前一個節點的地址,即它指向這個節點的前一個節點

實現雙鏈表

在上一節中,我們已經實現不帶頭不循環的單鏈表了,那么這里實現的是不帶頭不循環的雙鏈表。以下是這個雙鏈表包含的一些操作方法:

// 不帶頭雙向鏈表實現

public class MyLinkedList {

????????//頭插法

????????public void addFirst(int data){ }

????????//尾插法

????????public void addLast(int data){}

????????//任意位置插入,第一個數據節點為0號下標

????????public void addIndex(int index,int data){}

????????//判斷雙鏈表當中是否有包含key節點

????????public boolean contains(int key){

? ? ? ? ? ? ? ? return false;

????????}

????????//刪除第一次出現關鍵字為key的節點

????????public void remove(int key){}

????????//刪除所有值為key的節點

????????public void removeAllKey(int key){}

????????//得到雙鏈表的長度

????????public int size(){

? ? ? ? ? ? ? ? return -1;

????????}

? ? ? ? //打印雙鏈表中的數據

????????public void display(){}

????????//清空雙鏈表

????????public void clear(){}

在實現這些操作之前,先創建節點類 ListNode:

public class ListNode {public int val;public ListNode prev;public ListNode next;public ListNode(int val) {this.val = val;}
}

還是老規矩,實現順序由簡到繁。

(1) 打印雙鏈表中的數據

要求:將雙鏈表中的數據一個個打印出來。

思路:實現方式與單鏈表的打印一樣,通過遍歷的方式,一個個去打印。

public class MyLinkedList {ListNode head;ListNode last;//打印雙鏈表中的數據public void display() {ListNode cur = this.head;while (cur != null) {System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}
}

因為雙鏈表中前驅結點指向該節點的前一個節點,因此它能夠從后往前訪問,所以用 last

來表示雙鏈表的尾節點。

(2) 得到雙鏈表的長度

要求:通過這個方法獲得雙鏈表的長度。

思路:定義一個變量用于計數,通過遍歷的方式進行計數,最后返回這個變量。

//得到雙鏈表的長度public int size() {ListNode cur = this.head;int size = 0; while (cur != null) {size++;cur = cur.next;}return size;}

(3) 判斷雙鏈表當中是否有包含key節點

要求:判斷雙鏈表中是否有包含key的節點,如果有就返回true,否則返回false。

思路:依舊通過遍歷的方式去一個個判斷。

//判斷雙鏈表當中是否有包含key節點public boolean contains(int key){ListNode cur = this.head;while (cur != null) {if (cur.val == key) {return true;}cur = cur.next;}return false;}

(4) 頭插法

要求:在雙鏈表的頭部位置插入一個新節點。

思路:這里與單鏈表不一樣,需要考慮兩種情況:1.插入前鏈表是空的;2.插入前鏈表不為空。

即下圖所示:

//頭插法public void addFirst(int data){ ListNode cur = new ListNode(data);if (this.head == null) {this.head = this.last = cur;}else {cur.next = this.head;this.head.prev = cur;this.head = cur;}}

進行測試:

public class Test {public static void main(String[] args) {MyLinkedList myLinkedList = new MyLinkedList();myLinkedList.addFirst(1);myLinkedList.addFirst(2);myLinkedList.addFirst(3);myLinkedList.display();}
}//運行結果
3 2 1 

符合預期。

(5) 尾插法

要求:在雙鏈表的尾部插入一個新節點。

思路:這里也分兩種情況,與頭插法一樣。需要注意的是,在鏈表不為空的情況下,我們應該對尾節點last進行操作,而不是頭節點head。

//尾插法public void addLast(int data){ListNode cur = new ListNode(data);if (this.head == null) {this.head = this.last = cur;}else {this.last.next = cur;cur.prev = this.last;this.last = cur;}}

進行測試:

public class Test {public static void main(String[] args) {MyLinkedList myLinkedList = new MyLinkedList();myLinkedList.addLast(1);myLinkedList.addLast(2);myLinkedList.addLast(3);myLinkedList.display();}
}//運行結果
1 2 3 

符合預期。

(6)?任意位置插入,第一個數據節點為0號下標

要求:在雙鏈表中合法的任一位置插入一個新節點,第一個節點為0號下標。

思路:可以分3種情況處理:頭插、尾插和中間插入。頭插與尾插已經解決了,因此現在只需要解決中間插入即可。如下圖所示:

當然,在進行插入操作前,需要判斷插入位置是否合法。

插入位置非法異常類:

public class InsertIllegalException extends RuntimeException {public InsertIllegalException() {}public InsertIllegalException(String str) {super(str);}
}

判斷插入位置是否合法:

//判斷插入位置是否合法private void isIllegal(int index) {if (index < 0 || index > size()) {throw new InsertIllegalException("插入位置不合法!");}}

插入操作:

//任意位置插入,第一個數據節點為0號下標public void addIndex(int index,int data){try {isIllegal(index);int len = size();//頭插if (index == 0) {addFirst(data);return;}//尾插if (index == len) {addLast(data);return;}//中間插入ListNode newN = new ListNode(data);ListNode cur = this.head;while (index != 0) {cur = cur.next;index--;}newN.next = cur;cur.prev.next = newN;newN.prev = cur.prev;cur.prev = newN;}catch (InsertIllegalException e) {e.printStackTrace();}}

進行測試:

public class Test {public static void main(String[] args) {MyLinkedList myLinkedList = new MyLinkedList();myLinkedList.addLast(1);myLinkedList.addLast(2);myLinkedList.addLast(3);myLinkedList.display();myLinkedList.addIndex(1,100);myLinkedList.display();}
}//運行結果
1 2 3 
1 100 2 3 

符合預期。

(7)?刪除第一次出現關鍵字為key的節點

要求:將雙鏈表中第一次出現包含key的節點刪掉。

思路:這里要分為三種情況:key在鏈表頭部、key在鏈表尾部和key在鏈表中間,但在開始前需要判斷鏈表是否為空。分析如下圖所示:

//刪除第一次出現關鍵字為key的節點public void remove(int key){if (this.head == null) {System.out.println("鏈表為空!");return;}ListNode cur = this.head;boolean mark = false;  //用于標記鏈表中是否有key,有的話置為truewhile (cur != null) {//先找keyif (cur.val == key) {mark = true;//當cur位于頭節點if (cur == this.head) {this.head = this.head.next;this.head.prev = null;}else {//位于中間或者尾節點cur.prev.next = cur.next;//位于尾節點if (cur.next == null) {this.last = this.last.prev;}else {//位于中間cur.next.prev = cur.prev;}}//刪除完成!return;}cur = cur.next;}if (mark == false) {System.out.println("鏈表中沒有" + key);}}

但是這里還有一個不足的地方,就是當鏈表中僅有一個節點時(即head == last且該節點的值為key),這時候 head.prev就會引發空指針異常,所以要對key在頭節點的情況進行補充處理:

//刪除第一次出現關鍵字為key的節點public void remove(int key){if (this.head == null) {System.out.println("鏈表為空!");return;}ListNode cur = this.head;boolean mark = false;  //用于標記鏈表中是否有key,有的話置為truewhile (cur != null) {//先找keyif (cur.val == key) {mark = true;//當cur位于頭節點if (cur == this.head) {this.head = this.head.next;//當刪除后鏈表不為空,即鏈表不僅僅有一個節點if (this.head != null) {this.head.prev = null;}else {//刪除后鏈表為空,即鏈表僅有一個節點,這里更新尾節點lastthis.last = null;}}else {//位于中間或者尾節點cur.prev.next = cur.next;//位于尾節點if (cur.next == null) {this.last = this.last.prev;}else {//位于中間cur.next.prev = cur.prev;}}//刪除完成!return;}cur = cur.next;}if (mark == false) {System.out.println("鏈表中沒有" + key);}}

進行測試:

public class Test {public static void main(String[] args) {MyLinkedList myLinkedList = new MyLinkedList();myLinkedList.addLast(1);myLinkedList.addLast(2);myLinkedList.addLast(3);myLinkedList.addLast(1);myLinkedList.display();myLinkedList.remove(1);myLinkedList.display();}
}//運行結果:
1 2 3 1 
2 3 1 

符合預期。

(8)?刪除所有值為key的節點

要求:將鏈表中所有值為key的節點都刪掉。

思路:只需要在刪除第一次出現關鍵字為key的節點的方法的基礎上進行修改即可,把return去掉,使得循環繼續進行,繼續找key的節點,繼續刪除,直到遍歷結束,此時已將所有值為key的節點刪除。

//刪除所有值為key的節點public void removeAllKey(int key) {if (this.head == null) {System.out.println("鏈表為空!");return;}ListNode cur = this.head;boolean mark = false;  //用于標記鏈表中是否有key,有的話置為truewhile (cur != null) {//先找keyif (cur.val == key) {mark = true;//當cur位于頭節點if (cur == this.head) {this.head = this.head.next;//當刪除后鏈表不為空,即鏈表不僅僅有一個節點if (this.head != null) {this.head.prev = null;} else {//刪除后鏈表為空,即鏈表僅有一個節點,這里更新尾節點lastthis.last = null;}} else {//位于中間或者尾節點cur.prev.next = cur.next;//位于尾節點if (cur.next == null) {this.last = this.last.prev;} else {//位于中間cur.next.prev = cur.prev;}}}cur = cur.next;}if (mark == false) {System.out.println("鏈表中沒有" + key);}}

不過這里可能會有一個潛在的問題,就是當我們刪除某一個節點,cur指向的節點已經被移除,此時執行cur = cur.next可能會出現問題。舉個例子:

現在有一個雙鏈表:

2 <-> 3 <-> 2 <-> 4

現在要刪掉所有的2

1.首先,cur指向第一個2,刪除后head變為3節點;

2.接著執行cur = cur.next時,此時的cur仍然指向已經被刪除的第一個 2 節點;

3.這個節點的next雖然可能還指向 3,但這是不安全的(已刪除節點的引用應該被視為無效)

因此出于安全考慮,可以用一個變量記錄cur的=指向的下一個節點。

//刪除所有值為key的節點public void removeAllKey(int key) {if (this.head == null) {System.out.println("鏈表為空!");return;}ListNode cur = this.head;boolean mark = false;  //用于標記鏈表中是否有key,有的話置為truewhile (cur != null) {ListNode curN = cur.next;//先找keyif (cur.val == key) {mark = true;//當cur位于頭節點if (cur == this.head) {this.head = this.head.next;//當刪除后鏈表不為空,即鏈表不僅僅有一個節點if (this.head != null) {this.head.prev = null;} else {//刪除后鏈表為空,即鏈表僅有一個節點,這里更新尾節點lastthis.last = null;}} else {//位于中間或者尾節點cur.prev.next = cur.next;//位于尾節點if (cur.next == null) {this.last = this.last.prev;} else {//位于中間cur.next.prev = cur.prev;}}}cur = curN;}if (mark == false) {System.out.println("鏈表中沒有" + key);}}

進行測試:

public class Test {public static void main(String[] args) {MyLinkedList myLinkedList = new MyLinkedList();myLinkedList.addLast(2);myLinkedList.addLast(1);myLinkedList.addLast(2);myLinkedList.addLast(3);myLinkedList.addLast(2);myLinkedList.display();myLinkedList.removeAllKey(2);myLinkedList.display();}
}//運行結果:
2 1 2 3 2 
1 3 

符合預期。

(9) 清空雙鏈表

要求:將雙鏈表清空。

思路:粗暴的方式就不介紹了(直接將head和last置空),溫柔的方式:通過遍歷,一個個將節點的prev和next置空,最后將head和last置空。

//清空雙鏈表public void clear() {ListNode cur = this.head;while (cur != null) {ListNode curN = cur.next;cur.prev = null;cur.next = null;cur = curN;}this.head = null;this.last = null;}

進行測試:

public class Test {public static void main(String[] args) {MyLinkedList myLinkedList = new MyLinkedList();myLinkedList.addLast(2);myLinkedList.addLast(1);myLinkedList.addLast(2);myLinkedList.addLast(3);myLinkedList.addLast(2);myLinkedList.display();myLinkedList.clear();myLinkedList.display();}
}//運行結果:
2 1 2 3 2 

符合預期。

到此,我們就成功實現了一個不帶頭不循環的雙鏈表。

完整代碼

public class MyLinkedList {ListNode head;ListNode last;//打印雙鏈表中的數據public void display() {ListNode cur = this.head;while (cur != null) {System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}//得到雙鏈表的長度public int size() {ListNode cur = this.head;int size = 0;while (cur != null) {size++;cur = cur.next;}return size;}//判斷雙鏈表當中是否有包含key節點public boolean contains(int key){ListNode cur = this.head;while (cur != null) {if (cur.val == key) {return true;}cur = cur.next;}return false;}//頭插法public void addFirst(int data){ListNode cur = new ListNode(data);if (this.head == null) {this.head = this.last = cur;}else {cur.next = this.head;this.head.prev = cur;this.head = cur;}}//尾插法public void addLast(int data){ListNode cur = new ListNode(data);if (this.head == null) {this.head = this.last = cur;}else {this.last.next = cur;cur.prev = this.last;this.last = cur;}}//判斷插入位置是否合法private void isIllegal(int index) {if (index < 0 || index > size()) {throw new InsertIllegalException("插入位置不合法!");}}//任意位置插入,第一個數據節點為0號下標public void addIndex(int index,int data){try {isIllegal(index);int len = size();//頭插if (index == 0) {addFirst(data);return;}//尾插if (index == len) {addLast(data);return;}//中間插入ListNode newN = new ListNode(data);ListNode cur = this.head;while (index != 0) {cur = cur.next;index--;}newN.next = cur;cur.prev.next = newN;newN.prev = cur.prev;cur.prev = newN;}catch (InsertIllegalException e) {e.printStackTrace();}}//刪除第一次出現關鍵字為key的節點public void remove(int key){if (this.head == null) {System.out.println("鏈表為空!");return;}ListNode cur = this.head;boolean mark = false;  //用于標記鏈表中是否有key,有的話置為truewhile (cur != null) {//先找keyif (cur.val == key) {mark = true;//當cur位于頭節點if (cur == this.head) {this.head = this.head.next;//當刪除后鏈表不為空,即鏈表不僅僅有一個節點if (this.head != null) {this.head.prev = null;}else {//刪除后鏈表為空,即鏈表僅有一個節點,這里更新尾節點lastthis.last = null;}}else {//位于中間或者尾節點cur.prev.next = cur.next;//位于尾節點if (cur.next == null) {this.last = this.last.prev;}else {//位于中間cur.next.prev = cur.prev;}}//刪除完成!return;}cur = cur.next;}if (mark == false) {System.out.println("鏈表中沒有" + key);}}//刪除所有值為key的節點public void removeAllKey(int key) {if (this.head == null) {System.out.println("鏈表為空!");return;}ListNode cur = this.head;boolean mark = false;  //用于標記鏈表中是否有key,有的話置為truewhile (cur != null) {ListNode curN = cur.next;//先找keyif (cur.val == key) {mark = true;//當cur位于頭節點if (cur == this.head) {this.head = this.head.next;//當刪除后鏈表不為空,即鏈表不僅僅有一個節點if (this.head != null) {this.head.prev = null;} else {//刪除后鏈表為空,即鏈表僅有一個節點,這里更新尾節點lastthis.last = null;}} else {//位于中間或者尾節點cur.prev.next = cur.next;//位于尾節點if (cur.next == null) {this.last = this.last.prev;} else {//位于中間cur.next.prev = cur.prev;}}}cur = curN;}if (mark == false) {System.out.println("鏈表中沒有" + key);}}//清空雙鏈表public void clear() {ListNode cur = this.head;while (cur != null) {ListNode curN = cur.next;cur.prev = null;cur.next = null;cur = curN;}this.head = null;this.last = null;}
}

2.LinkedList

2.1 概念

LinkedList的底層是雙向鏈表結構,由于鏈表沒有將元素存儲在連續的空間中,元素存儲在單獨的節點中,然后通過引用將節點連接起來了,因此在在任意位置插入或者刪除元素時,不需要搬移元素,效率比較高。

在集合框架中,LinkedList也實現了List接口,具體如下:

【說明】

  1. LinkedList實現了List接口
  2. LinkedList的底層使用了雙向鏈表
  3. LinkedList沒有實現RandomAccess接口,因此LinkedList不支持隨機訪問
  4. LinkedList的任意位置插入和刪除元素時效率比較高,時間復雜度為O(1)
  5. LinkedList比較適合任意位置插入的場景

2.2 使用

構造方法

LinkedList的構造方法:

方法解釋
LinkedList()無參構造
public LinkedList(Collection <? extends E> c)使用其他集合容器中元素構造List

舉例:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class Test {public static void main(String[] args) {//使用第一種構造方法,構造一個空的LinkedListLinkedList<Integer> linkedList = new LinkedList<>();//List<Integer> linkedList = new LinkedList<>();這種寫法等于上一條語句,//因此LinkedList實現了List接口,因此可以用List去接收//可以插入數據linkedList.addFirst(1);linkedList.addFirst(2);linkedList.addFirst(3);System.out.println(linkedList);System.out.println("=============");//使用第二種構造方法List<Integer> list = new ArrayList<>();  //創建一個ArrayList,并往里面插入一些數據list.add(1);list.add(2);list.add(3);System.out.println(list);System.out.println("=============");//可以ArrayList構造LinkedListList<Integer> list1 = new LinkedList<>(list);System.out.println(list1);}
}//運行結果:
[3, 2, 1]
=============
[1, 2, 3]
=============
[1, 2, 3]

其他常用方法

方法解釋
boolean add(E e)尾插 e
void add(int index, E element)將 e 插入到 index 位置
boolean addAll(Collection <? extends E> c)尾插 c 中的元素
E remove(int index)刪除 index 位置元素
boolean remove(Object o)刪除遇到的第一個 o
E get(int index)獲取下標 index 位置元素
E set(int index, E element)將下標 index 位置元素設置為 element
void clear()清空
boolean contains(Object o)判斷 o 是否在線性表中
int indexOf(Object o)返回第一個 o 所在下標
int lastIndexOf(Object o)返回最后一個 o 的下標
List subList(int fromIndex, int toIndex)截取部分 list

LinkedList的遍歷

LinkedList的遍歷與ArrayList的遍歷一樣,有三種方法,分別是通過for遍歷、通過for-each遍歷和通過迭代器遍歷。具體請看:

數據結構 ArrayList與順序表-CSDN博客

2.3 ArrayList與LinkedList的區別

不同點ArrayListLinkedList
存儲空間上物理上一定連續邏輯上連續,但物理上不一定連續
隨機訪問支持:O(1)不支持:O(N)
頭插需要搬移元素,效率低O(N)只需修改引用的指向,時間復雜度為O(1)
插入空間不夠時需要擴容沒有容量的概念
應用場景元素高效存儲+頻繁訪問任意位置插入和刪除頻繁

到此,雙鏈表與LinkedList的內容完結。感謝您的觀看,如有不對的地方還請指出,謝謝!

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

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

相關文章

如何解決 JetBrains IntelliJ IDEA 2024.2 和 2025.2 新版本區域選擇問題:key is invalid

如何解決 JetBrains IntelliJ IDEA 2024.2 和 2025.2 新版本區域選擇問題&#xff1a;key is invalid 在 JetBrains 發布的 IntelliJ IDEA、PyCharm 2024.2 和 2025.2 新版本中&#xff0c;增加了一個新的功能——區域選擇。在設置菜單中&#xff0c;你可以找到這一選項&#…

GSON 框架下百度天氣 JSON 數據轉 JavaBean 的實戰攻略

目錄 前言 一、百度天氣JSON 1、請求參數 2、返回參數 3、屬性映射 二、GSON屬性映射實戰 1、類對象映射 2、屬性字段映射 3、日期數據映射 三、天氣接口對象展示 1、接口調用 2、Java屬性打印輸出 四、總結 前言 在當今數字化時代&#xff0c;數據的高效處理與轉換…

NAS技術在縣級融媒體中心的架構設計與安全運維淺析

NAS技術在縣級融媒體中心的架構設計與安全運維淺析 ——原理剖析、應用實踐與防御體系建設作者&#xff1a;高級網絡安全工程師 吉林?鎮賚融媒 劉曉偉 最后更新&#xff1a;2025年8月 適用對象&#xff1a;媒體行業網絡安全從業者一、NAS技術核心原理剖析 1. 基礎架構 NAS&am…

CobaltStrike的搭建和使用

下載CobaltStrike環境建議使用jdk17&#xff0c;其他java版本有些功能可能無法使用通過網盤分享的文件&#xff1a;CS4.7key-mht.zip 鏈接: https://pan.baidu.com/s/1CRd1x4r6EIk14BD3UCLgxw?pwdevf4 提取碼: evf4將下載的文件分別放在服務器和 本地/kali 上 也就是服務器為…

【Altium designer】一鍵給多個器件添加參數

目的: 一鍵給N個元器件/Part添加參數和修改參數值,比如一鍵給多個電阻添加“備注”并賦予備注的內容為“不焊接”,或者更改“備注”的內容為“不焊接”或空。 背景: 剛入門用AD畫原理圖,因為原理圖的電阻、電容和芯片等等的冗余/兼容設計太多,增加備注不焊的元器件位號…

熟練掌握switch語句:技巧與運用

目錄 一、switch語句基礎 基本語法結構&#xff1a; 在C/C中&#xff1a; 注意事項&#xff1a; 二、if與switch語句對比 示例&#xff1a;計算整數除以3的余數 使用if語句實現&#xff1a; 使用switch語句實現&#xff1a; 三、break語句的作用 示例&#xff08;無br…

【03】廈門立林科技——立林科技 嵌入式 校招筆試,題目記錄及解析

廈門立林科技——立林科技 嵌入式 校招筆試&#xff0c;題目記錄及解析 1.下面的程序的輸出是&#xff08;&#xff09;。2.在頭文件中#ifndef/#define/#endif的作用是4.執行下面程序中的輸出語句后,輸出的結果是()6.在32位處理器上,運行如下程序后p的值為()。10.設有兩字符串“…

C++算法(數據結構)版

C算法&#xff08;數據結構&#xff09;版 有些題目不是完整的題目&#xff0c;如需查看完整的題目請移步到acwing的算法基礎課中 文章目錄C算法&#xff08;數據結構&#xff09;版單鏈表思路&#xff1a;雙鏈表思路&#xff1a;棧思路&#xff1a;隊列思路&#xff1a;單調棧…

算法訓練營DAY57 第十一章:圖論part07

prim算法精講 53. 尋寶&#xff08;第七期模擬筆試&#xff09; 題目描述&#xff1a; 在世界的某個區域&#xff0c;有一些分散的神秘島嶼&#xff0c;每個島嶼上都有一種珍稀的資源或者寶藏。國王打算在這些島嶼上建公路&#xff0c;方便運輸。 不同島嶼之間&#xff0c;…

最短路問題從入門到負權最短路

一&#xff0c;BFS層次最短路/*題目描述 題目描述 給出一個 N 個頂點 M 條邊的無向無權圖&#xff0c;頂點編號為 1~N。 問從頂點 1 開始&#xff0c;到其他每個點的最短路有幾條。 輸入格式 第一行包含 2 個正整數 N,M&#xff0c;為圖的頂點數與邊數。 接下來 M 行&#xff…

AI智能體小白入門指南

AI智能體小白入門指南 ——什么是AI智能體&#xff1f;它們如何工作&#xff1f; 一、AI智能體是什么&#xff1f; AI智能體&#xff08;AI Agent&#xff09;是能感知環境、自主決策并執行動作的人工智能系統。 類比理解&#xff1a;像一個“虛擬機器人”或“數字助手”&#…

《設計模式》策略模式

1.策略模式定義 策略模式&#xff08;Strategy Pattern&#xff09;是一種行為型設計模式&#xff0c;它定義了一組算法&#xff0c;將每個算法封裝起來&#xff0c;并使它們可以相互替換&#xff0c;從而讓算法的變化獨立于使用它的客戶&#xff08;Client&#xff09;。 換…

AWS DMS 深度解析:從遷移任務到復制任務 - 全流程指南與最佳實踐

AWS Database Migration Service (DMS) 是一項強大的云服務,用于在源數據庫和目標數據庫之間安全地遷移數據。其核心優勢在于支持幾乎零停機時間的遷移,這主要歸功于其“變更數據捕獲 (CDC)”功能。理解遷移任務 (Migration Task) 和復制任務 (Replication Task) 的關系與操作…

國企社招 | 中國郵政2025年社會招聘開啟

添加圖片注釋&#xff0c;不超過 140 字&#xff08;可選&#xff09; 添加圖片注釋&#xff0c;不超過 140 字&#xff08;可選&#xff09; 添加圖片注釋&#xff0c;不超過 140 字&#xff08;可選&#xff09; 原文鏈接&#xff1a;“郵”你“政”好 | 廣東郵政2025年社會…

linux添加自啟動

linux添加自啟動 配置步驟&#xff1a; 創建systemd服務文件 sudo nano /etc/systemd/system/tme-vod.service將下面artifact中的內容復制到該文件中。 [Unit] DescriptionTME VOD Service Afternetwork.target[Service] Typesimple Userroot Grouproot WorkingDirectory/data/…

輕量級解決方案:如何高效處理Word轉PDF?

文檔格式轉換時&#xff0c;手動逐個處理總顯得效率低下。它的體積小巧&#xff0c;不到1MB&#xff0c;且無界面設計&#xff0c;運行極簡&#xff1a;將其與Word文件放入同一目錄&#xff0c;雙擊啟動&#xff0c;程序便會自動完成所有文檔的PDF轉換。操作零復雜度&#xff0…

Redis 數據傾斜

Redis 數據傾斜指的是在 Redis 集群模式下&#xff0c;數據&#xff08;以及相應的訪問請求和負載&#xff09;在各個分片&#xff08;Shard&#xff09;之間分布嚴重不均勻的現象。這會導致部分節點成為熱點或超載&#xff0c;而其他節點資源閑置&#xff0c;最終引發性能瓶頸…

Java基礎-TCP通信(多發多收和一發一收)

目錄 案例要求&#xff1a; 實現思路&#xff1a; 代碼&#xff1a; User:客戶端 Client:服務端 總結&#xff1a; 案例要求&#xff1a; 實現TCP通信的多發多收和一發一收,多發多收去掉各自的while循環就是一發一收,本文只模擬一發一收 實現思路&#xff1a; 客戶端(U…

WinForm 對話框的 Show 與 ShowDialog:阻塞與非阻塞的抉擇

目錄 核心概念&#xff1a;阻塞與非阻塞 Show 與 ShowDialog 的詳細對比 代碼示例&#xff1a;兩種方式的實現差異 使用 Show () 顯示非模態對話框 使用 ShowDialog () 顯示模態對話框 適用場景分析 適合使用 Show () 的場景 適合使用 ShowDialog () 的場景 最佳實踐與…

曉知識: 動態代理與靜態代理的區別

動態代理與靜態代理的區別 代理模式是一種常見的設計模式&#xff0c;用于在不修改原始類的情況下擴展其功能。代理分為靜態代理和動態代理兩種&#xff0c;它們在實現方式、適用場景和靈活性上有顯著差異。 靜態代理 靜態代理在編譯時就已經確定代理類和被代理類的關系。代理類…