日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Algorithms_基础数据结构(02)_线性表之链表_单向链表

發(fā)布時間:2025/3/21 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Algorithms_基础数据结构(02)_线性表之链表_单向链表 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 大綱圖
  • 鏈表的經(jīng)典面試題目
    • 如何設計一個LRU緩存淘汰算法
    • 約瑟夫問題
  • 順序表VS 鏈表
  • 鏈表的定義
  • 鏈表的特點
  • 常見的鏈表結
  • 單向鏈表
    • 單向鏈表的查找
    • 單向鏈表的插入
      • 頭插
      • 尾部插入
      • 中間插入
    • 單向鏈表的刪除
      • 刪除頭節(jié)點
      • 刪除中間的節(jié)點
      • 刪除尾部節(jié)點
    • Code
  • 總結


大綱圖


鏈表的經(jīng)典面試題目

如何設計一個LRU緩存淘汰算法

tip:單向鏈表


約瑟夫問題

N個人圍成一圈,從第一個開始報數(shù),第M個將被殺掉,最后剩下一個,其余人都將被殺掉。

舉個例子: 假設N=6,M=5,被殺掉的順序是:5,4,6,2,3,1。
現(xiàn)在問你最后留下的人是誰?
比如N=6,M=5 ,留下的就是1

1 2 3 4 5 6 => 6 1 2 3 4 => 6 1 2 3 =>1 2 3 => 1 3 => 1

tips: 單向循環(huán)鏈表


順序表VS 鏈表


鏈表的定義

鏈表是一種物理存儲單元上非連續(xù)、非順序的存儲結構,數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序實現(xiàn)的。

鏈表由一系列結點(鏈表中每一個元素稱為結點)組成,結點可以在運行時動態(tài)生成。

每個結點包括兩個部分:一個是存儲數(shù)據(jù)元素的數(shù)據(jù)域,另一個是存儲下一個結點地址的指針域。 比如下面這種

相比于線性表順序結構,操作復雜。由于不必須按順序存儲,鏈表在插入的時候可以達到O(1)的復雜度,比另一種線性表順序表快得多,但是查找一個節(jié)點或者訪問特定編號的節(jié)點則需要O(n)的時間,而線性表和順序表相應的時間復雜度分別是O(logn)和O(1)。

使用鏈表結構可以克服數(shù)組鏈表需要預先知道數(shù)據(jù)大小的缺點,鏈表結構可以充分利用計算機內存空間,實現(xiàn)靈活的內存動態(tài)管理。但是鏈表失去了數(shù)組隨機讀取的優(yōu)點,同時鏈表由于增加了結點的指針域,空間開銷比較大。


鏈表的特點

  • 不需要連續(xù)的內存空間
  • 有指針引用

常見的鏈表結

三種最常見的鏈表結構:單向鏈表、雙向鏈表 和循環(huán)鏈表 (單向循環(huán)鏈表、雙向循環(huán)鏈表)


單向鏈表

單向鏈表是由一個個節(jié)點組成的,每個節(jié)點是一種信息集合,包含元素本身以及下一個節(jié)點的地址。

從單鏈表圖中,可以發(fā)現(xiàn),有兩個結點是比較特殊的,它們分別是第一個結點和最后一個結點。

我們一般把第一個結點叫作頭結點,把最后一個結點叫作尾結點。

其中,頭結點用來記錄鏈表的基地址。有了它,我們就可以遍歷得到整條鏈表。

而尾結點特殊的地方是:指針不是指向下一個結點,而是指向一個空地址NULL,表示這是鏈表上最后一個結點。


單向鏈表的查找

沒啥好說的,從頭結點開始遍歷,直到找到停止,不存在的話,就是全部遍歷了,查找的時間復雜度為O(n)

code如下

/*** 根據(jù)值,找到對應的節(jié)點* 從頭節(jié)點 開始遍歷** @param data* @return*public ArtisanNode find(Object data) {// 頭節(jié)點的臨時變量,直接用head就把head給改變了,不可取。ArtisanNode currentNode = head;// 遍歷while (currentNode != null) {if (currentNode.data == data) { // 如果匹配,終止循環(huán)break;} else {currentNode = currentNode.next; // 不匹配則將下個節(jié)點賦值給當前節(jié)點,繼續(xù)循環(huán)}}System.out.println("當前節(jié)點:" + currentNode.toString());return currentNode;}

單向鏈表的插入

插入的話 分為三種場景,頭插、尾插、中間插入


頭插

頭插就是插入頭部節(jié)點。 流程如下


尾部插入

尾插就是插入尾部節(jié)點。 流程如下


中間插入


單向鏈表的刪除


刪除頭節(jié)點


刪除中間的節(jié)點


刪除尾部節(jié)點


Code

/*** @author 小工匠* @version v1.0* @create 2020-01-01 07:36* @motto show me the code ,change the word* @blog https://artisan.blog.csdn.net/* @description**/public class ArtisanSingleLinkedList {// 頭節(jié)點private ArtisanNode head;// 單向鏈表的長度private int size;/*** 構造函數(shù)*/public ArtisanSingleLinkedList() {this.size = 0; // 初始化長度為0this.head = null;// 初始化head為null}/*** 頭插** @param data*/public void add2Head(Object data) {ArtisanNode node = new ArtisanNode(data); // 初始化一個Nodenode.next = head;// 將這個新Node的next指向headthis.head = node;// 把這個新的node置為headsize++; // 更新鏈表容量}/*** 尾插** @param data*/public void add2Tail(Object data) {ArtisanNode node = new ArtisanNode(data);ArtisanNode currentNode = head;while (currentNode != null){ // 遍歷if (currentNode.next == null){ // next為null,說明到了tailcurrentNode.next = node; // 將next節(jié)點指向新增節(jié)點break; // 跳出循環(huán)}else {currentNode = currentNode.next; // 不匹配則將下個節(jié)點賦值給當前節(jié)點,繼續(xù)循環(huán)}}size++;}/*** 插入鏈表的中間 假設在第N個位置插入** @param data*/public ArtisanNode add2Nth(Object data, int position) {ArtisanNode node = new ArtisanNode(data);if (position == 0) { // 如果position = 0 ,頭插add2Head(data);} else { // 找到對應的位置// 頭節(jié)點的臨時變量,直接用head就把head給改變了,不可取。ArtisanNode currentNode = head;for (int i = 1; i < position; i++) {currentNode = currentNode.next; // 一直往后遍歷}node.next = currentNode.next; // 當前節(jié)點的next節(jié)點 賦值給 新節(jié)點的nextcurrentNode.next = node; // 當前節(jié)點的next節(jié)點指向新節(jié)點}size++; // 更新鏈表容量return node;}/*** 根據(jù)值,找到對應的節(jié)點* 從頭節(jié)點 開始遍歷** @param data* @return*/public ArtisanNode find(Object data) {// 頭節(jié)點的臨時變量,直接用head就把head給改變了,不可取。ArtisanNode currentNode = head;// 遍歷while (currentNode != null) {if (currentNode.data == data) { // 如果匹配,終止循環(huán)break;} else {currentNode = currentNode.next; // 不匹配則將下個節(jié)點賦值給當前節(jié)點,繼續(xù)循環(huán)}}System.out.println("當前節(jié)點:" + currentNode.toString());return currentNode;}/*** @return 單向鏈表當前的容量*/public int getSize() {System.out.println("ArtisanSingleLinkedList 當前的容量為:" + size);return size;}/*** 刪除頭節(jié)點* 時間復雜度 O(1)*/public ArtisanNode deleteHead(){this.head = head.next ; // 將頭節(jié)點的nextsize--;return head;// 返回頭節(jié)點}/*** 刪除指定位置的節(jié)點** 時間復雜度 O(n)*/public ArtisanNode deleteNth(int position){ArtisanNode currentNode = head;if (position == 0 ){deleteHead();}else{for (int i = 1; i < position; i++) {currentNode = currentNode.next;}currentNode.next = currentNode.next.next; //cur.next 表示的是刪除的點,后一個next就是我們要指向的}size--;return currentNode.next; // 返回被移除的節(jié)點}/*** 刪除尾部節(jié)點*/public ArtisanNode deleteTail(){ArtisanNode currentNode = head;ArtisanNode previosNode = head;while (currentNode != null){if (currentNode.next == null){previosNode.next = null;break;}else {previosNode = currentNode;currentNode = currentNode.next;}}size--;return previosNode; // 返回尾結點}public void print(){ArtisanNode currentNode = head;while(currentNode != null){System.out.print(currentNode.data+" -> ");// 從頭節(jié)點開始輸出currentNode = currentNode.next;}System.out.println();}public static void main(String[] args) {ArtisanSingleLinkedList single = new ArtisanSingleLinkedList();// 頭插single.add2Head(5);single.add2Head(4);single.add2Head(3);single.add2Head(2);single.add2Head(1);single.getSize();single.print();// 查找數(shù)據(jù)single.find(4);// 指定位置插入single.add2Nth("InsertedData", 2);single.getSize();single.print();// 尾插single.add2Tail("小工匠");single.print();single.getSize();// 刪除中間節(jié)點single.deleteNth(2);single.print();// 刪除尾部節(jié)點single.deleteTail();single.getSize();single.print();} }/*** Node節(jié)點*/ class ArtisanNode {Object data; // 數(shù)據(jù)域ArtisanNode next; // 指針域,指向下一個節(jié)點/*** 構造函數(shù)** @param data*/public ArtisanNode(Object data) {this.data = data;}@Overridepublic String toString() {return "ArtisanNode{" +"data=" + data +", next=" + next +'}';} }


總結

  • 1. 說到數(shù)組就要想到下標,查找使用下標去查找,因為根據(jù)下標查找的時間復雜度為O(1).
    不要試圖使用根據(jù)元素的值去查找,因為這樣的話,根據(jù)值去查找,時間復雜度為O(n)

  • 2. 數(shù)組的話,需要考慮數(shù)組越界的問題, 合理的動態(tài)擴容/縮容 , 減少不必要的內存浪費

  • 3. CPU可以把數(shù)組緩存到CPU內部,執(zhí)行效率比鏈表更高。 為啥可以緩存數(shù)組呢? ---->
    數(shù)組是在內存里連續(xù)的, 索引找到了首地址,其他元素也就都找到了。 鏈表在內存中 是沒有規(guī)律的,通過指針項鏈,沒發(fā)被CPU緩存。

  • 4. 說到鏈表,不需要考慮下標的問題,所以不能根據(jù)下邊來查找,肯定是需要根據(jù)數(shù)據(jù)域存儲的數(shù)據(jù)來查找,從頭遍歷,通過next執(zhí)行,一直遍歷下去,直到找到數(shù)據(jù)位置。所以時間復雜度為O(n)

  • 5. 鏈表 雖然查找慢,但是插入和刪除快啊,動動指針即可,不用挪元素。時間復雜度O(1)

  • 6. 鏈表 因為本身支持動態(tài)擴容,所以一定要考慮 鏈表占用的內存大小。。。切記。

總結

以上是生活随笔為你收集整理的Algorithms_基础数据结构(02)_线性表之链表_单向链表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。