自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

廣度優(yōu)先搜索算法應(yīng)用于Swift手游開(kāi)發(fā)

譯文
移動(dòng)開(kāi)發(fā) iOS 算法
廣度優(yōu)先搜索算法最初是由克里斯·皮爾策(Chris Pilcher,https://github.com/chris-pilcher)實(shí)現(xiàn)的。在本教程中,我們將根據(jù)需要對(duì)其實(shí)現(xiàn)格式方面稍加重構(gòu)。

【51CTO.com快譯】Swift算法俱樂(lè)部(https://github.com/raywenderlich/swift-algorithm-club)是一個(gè)開(kāi)放源碼項(xiàng)目,其目的是使用Swift語(yǔ)言實(shí)現(xiàn)一些常用的算法和數(shù)據(jù)結(jié)構(gòu)。

在每個(gè)月份,SAC團(tuán)隊(duì)都會(huì)在www.raywenderlich.com網(wǎng)站上以教程形式公布一個(gè)很酷的數(shù)據(jù)結(jié)構(gòu)或算法。因此,如果您想要了解更多關(guān)于Swift語(yǔ)言實(shí)現(xiàn)的算法和數(shù)據(jù)結(jié)構(gòu),您可以一直關(guān)注這個(gè)網(wǎng)站。

在本教程中,你將學(xué)習(xí)一種經(jīng)典的搜索和尋路算法,即廣度優(yōu)先搜索算法。

廣度優(yōu)先搜索算法最初是由克里斯·皮爾策(Chris Pilcher,https://github.com/chris-pilcher)實(shí)現(xiàn)的。在本教程中,我們將根據(jù)需要對(duì)其實(shí)現(xiàn)格式方面稍加重構(gòu)。

本教程假定您已經(jīng)了解基于Swift語(yǔ)言實(shí)現(xiàn)的圖論中的鄰接表算法(https://www.raywenderlich.com/152046/swift-algorithm-club-graphs-adjacency-list)和隊(duì)列算法(https://www.raywenderlich.com/148141/swift-algorithm-club-swift-queue-data-structure),或具有相類似的基礎(chǔ)知識(shí)。

注意,如果您對(duì)Swift算法俱樂(lè)部感興趣并且是新手,您可以訪問(wèn)這個(gè)地址https://www.raywenderlich.com/135533/join-swift-algorithm-club

入門(mén)

在Swift圖論鄰接表算法(https://www.raywenderlich.com/152046/swift-algorithm-club-graphs-adjacency-list)中,我們提出了一種描述圖中的各對(duì)象及其間關(guān)系的方法。在一個(gè)圖中,每個(gè)對(duì)象表示為一個(gè)頂點(diǎn),而每個(gè)關(guān)系表示為一條邊。

例如,可以用圖來(lái)描述一個(gè)迷宮。在迷宮中的每個(gè)接合點(diǎn)可以由一個(gè)頂點(diǎn)來(lái)描述,而接合點(diǎn)之間的每個(gè)通道可以使用邊來(lái)描述。

可以用圖來(lái)描述一個(gè)迷宮

廣度優(yōu)先搜索是在1950年被E. F. Moore(https://en.wikipedia.org/wiki/Edward_F._Moore)發(fā)現(xiàn)的,這種算法并不只是為了尋找一條穿過(guò)迷宮的通路,而是為了尋找穿越迷宮的最短路徑的算法。此廣度優(yōu)先搜索算法背后的想法倒是很簡(jiǎn)單的︰

  • 搜索圍繞從某一源點(diǎn)出發(fā)的一組移動(dòng)對(duì)應(yīng)的每一個(gè)位置。
  • 然后,以增量步長(zhǎng)方式修改這個(gè)數(shù)字,直到找到目的地。

下面,讓我們來(lái)分析一下具體的例子。

舉例

假設(shè)你是在一個(gè)迷宮的入口處,請(qǐng)參考下圖。

假設(shè)你是在一個(gè)迷宮的入口處

廣度優(yōu)先搜索算法以如下方式工作︰

搜索你的當(dāng)前位置。如果這是目的地,則停止搜索。

1. 搜索你的當(dāng)前位置。如果這是目的地,則停止搜索。

2.搜索您所在位置的鄰點(diǎn)位置。如果其中任何之一是目的地,則停止搜索。

搜索您所在位置的鄰點(diǎn)位置。如果其中任何之一是目的地,則停止搜索。

3.搜索這些位置的所有鄰接點(diǎn)位置。如果任何其中之一是目的地,則停止搜索。

搜索這些位置的所有鄰接點(diǎn)位置。如果任何其中之一是目的地,則停止搜索。

4.***,如果存在一條到達(dá)目的地的路線,則稱發(fā)現(xiàn)了一條通路,并以從原點(diǎn)出發(fā)的最少移動(dòng)步數(shù)存儲(chǔ)起這條通道。如果你已經(jīng)用完了要搜索的位置,則說(shuō)明不存在一條到達(dá)目標(biāo)的的通路。

【注】就廣度優(yōu)先搜索算法而言,最短路線是指從一個(gè)位置到達(dá)下一個(gè)位置的最少移動(dòng)步數(shù)。

在我們提供的迷宮例子中,廣度優(yōu)先搜索算法認(rèn)為迷宮中房間之間的所有通道都具有相同的長(zhǎng)度,當(dāng)然是實(shí)際中這可能不是真實(shí)的。你可以把通過(guò)迷宮的最短路線看作最短方向列表,而不是最短的距離。

在未來(lái)的教程中,我們將探索最短距離的路徑尋找算法。

Swift廣度優(yōu)先搜索算法

從現(xiàn)在開(kāi)始,讓我們具體分析一下基于Swift語(yǔ)言實(shí)現(xiàn)的廣度優(yōu)先搜索算法。

為此,請(qǐng)先下載本教程的初始源碼(https://koenig-media.raywenderlich.com/uploads/2017/03/BreadthFirstSearch-Start.playground.zip),其中提供了基于Swift語(yǔ)言實(shí)現(xiàn)的鄰接表和隊(duì)列的數(shù)據(jù)結(jié)構(gòu)。

【注意】如果你想了解Swift語(yǔ)言實(shí)現(xiàn)的鄰接表和隊(duì)列的數(shù)據(jù)結(jié)構(gòu)是如何工作的,你可以使用命令View\Navigators\ Show Project Navigator來(lái)分析有關(guān)代碼。您還可以具體學(xué)習(xí)如何使用Swift語(yǔ)言構(gòu)建這些鄰接表和隊(duì)列教程。

首先,我們定義一個(gè)名為Graphable的協(xié)議;所有的圖形數(shù)據(jù)結(jié)構(gòu)都要遵從該協(xié)議。然后,我們要擴(kuò)展該協(xié)議;這樣一來(lái),我們可以將廣度優(yōu)先搜索應(yīng)用于所有的圖類型中。

下面是Graphable協(xié)議看起來(lái)的樣子︰

  1. public protocol Graphable { 
  2.   associatedtype Element: Hashable 
  3.   var description: CustomStringConvertible { get } 
  4.   
  5.   func createVertex(data: Element) -> Vertex<Element> 
  6.   func add(_ type: EdgeType, from source: Vertex<Element>, to destination: Vertex<Element>, weight: Double?) 
  7.   func weight(from source: Vertex<Element>, to destination: Vertex<Element>) -> Double
  8.   func edges(from source: Vertex<Element>) -> [Edge<Element>]? 
  9.  

在上面下載的初始示例工程的較靠上的位置(在語(yǔ)句 import XCPlayground的后面即可),創(chuàng)建我們的擴(kuò)展:

  1. extension Graphable { 
  2.   public func breadthFirstSearch(from source: Vertex<Element>, to destination: Vertex<Element>)  
  3.   -> [Edge<Element>]? { 
  4.   
  5.   } 
  6.  

讓我們概括一下此函數(shù)簽名︰

  • 其中聲明了一個(gè)函數(shù),它接收兩個(gè)頂點(diǎn)參數(shù):***個(gè)是源點(diǎn),即我們的出發(fā)點(diǎn);第二個(gè)是目標(biāo)點(diǎn),即我們的目標(biāo)。此函數(shù)返回一條以邊對(duì)象形式組成的路線(從源點(diǎn)出發(fā)直到目標(biāo)位置)。
  • 如果路線存在,我們還指望它以排序方式存儲(chǔ)!路線中的***條邊將從源頂點(diǎn)開(kāi)始,而路線中的***一條邊將以目標(biāo)頂點(diǎn)終結(jié)。對(duì)于路線中的每一對(duì)相鄰邊,***條邊的目的點(diǎn)將與第二條邊的源點(diǎn)成為同一頂點(diǎn)。
  • 如果源點(diǎn)與目的點(diǎn)是一樣的,則說(shuō)明這條路線是一個(gè)空數(shù)組。
  • 如果路線不存在,該函數(shù)應(yīng)返回nil。

廣度優(yōu)先搜索依賴于按正確的順序訪問(wèn)的頂點(diǎn)。要訪問(wèn)的***個(gè)頂點(diǎn)總是對(duì)應(yīng)于源點(diǎn)。之后,我們會(huì)分析源點(diǎn)的鄰結(jié)點(diǎn);然后,以此類推下去。我們每訪問(wèn)一個(gè)頂點(diǎn),便將其結(jié)點(diǎn)添加到隊(duì)列的后面。

因?yàn)槲覀兇饲耙呀?jīng)了解了隊(duì)列知識(shí),所以這里我們可以直接使用它!

于是,我們可以把上面的函數(shù)更新成下面這樣︰

  1. public func breadthFirstSearch(from source: Vertex<Element>, to destination: Vertex<Element>)  
  2.  -> [Edge<Element>]? { 
  3.   
  4.  var queue = Queue<Vertex<Element>>() 
  5.   queue.enqueue(source) // 1 
  6.   
  7.   while let visitedVertex = queue.dequeue() { // 2 
  8.     if visitedVertex == destination { // 3 
  9.       return [] 
  10.     } 
  11.     // TODO... 
  12.   } 
  13.   return nil // 4 
  14.   
  15.  

下面,我們來(lái)逐步分析一下上面的代碼:

1. 首先,創(chuàng)建一個(gè)頂點(diǎn)隊(duì)列,并把源點(diǎn)加入隊(duì)列。

2. 從隊(duì)列中出列一個(gè)頂點(diǎn)(只要隊(duì)列非空),并稱之為訪問(wèn)頂點(diǎn)。

3. 在***迭代中,訪問(wèn)頂點(diǎn)將是源點(diǎn),而且隊(duì)列會(huì)立即為空。然而,如果訪問(wèn)源結(jié)點(diǎn)添加更多的結(jié)點(diǎn),則搜索會(huì)繼續(xù)進(jìn)行下去。

4. 檢測(cè)是否訪問(wèn)頂點(diǎn)是目標(biāo)頂點(diǎn)。如果是,則立即結(jié)束搜索。目前情況下,你將返回一個(gè)空的列表——這與目標(biāo)結(jié)點(diǎn)找到時(shí)是一樣的情況。然后,將構(gòu)造一條更為細(xì)致的線路。

5. 如果隊(duì)列中頂點(diǎn)為空,則返回nil。這意味著,目標(biāo)結(jié)點(diǎn)沒(méi)有找到;有可能不存在相對(duì)于它的一條通路。

接下來(lái),我們需要把訪問(wèn)結(jié)點(diǎn)的所有鄰居結(jié)點(diǎn)加入隊(duì)列中。為此,可以使用如下代碼:

  1. let neighbourEdges = edges(from: visitedVertex) ?? [] // 1 
  2. for edge in neighbourEdges { 
  3.   queue.enqueue(edge.destination) 
  4. } // 2  

再讓我們作細(xì)致的分析:

1.這里使用Graphable協(xié)議的edges(from:)函數(shù)來(lái)取得訪問(wèn)結(jié)點(diǎn)的邊數(shù)組。記住,edges(from:)函數(shù)返回的是一個(gè)可選的邊數(shù)組。這意味著,如果此該數(shù)組為空,或者nil,則不存在以此結(jié)點(diǎn)開(kāi)始的邊。

因?yàn)?為了我們的搜索目的)空表和nil意思一樣——沒(méi)有鄰結(jié)點(diǎn)可入隊(duì)列;所以,我們使用一個(gè)空列表來(lái)nil聚合可選數(shù)組,從而去掉可選功能。

2.現(xiàn)在,你可以在邊列表上安全地進(jìn)行for循環(huán)迭代,從而把每一個(gè)邊的目標(biāo)結(jié)點(diǎn)入隊(duì)列。

到此,我們的任務(wù)還沒(méi)有完全完成。事實(shí)上,在此搜索算法中還存在一處微妙的危險(xiǎn)!如果你在此示例中運(yùn)行搜索算法會(huì)遇到什么問(wèn)題呢?在此,我們可以不考慮這樣一個(gè)事實(shí),即寶物房間沒(méi)有關(guān)連到圖上。

我們不妨去手工方式推算一下每當(dāng)我們?cè)L問(wèn)一個(gè)頂點(diǎn)時(shí)將會(huì)發(fā)生什么。

如果你在此示例中運(yùn)行搜索算法會(huì)遇到什么問(wèn)題呢?答案如下:

1. 寬度優(yōu)先搜索算法將創(chuàng)建一個(gè)隊(duì)列,并把入口處房間入隊(duì)列。

2. 當(dāng)***次進(jìn)入到while循環(huán)時(shí),我們把入口房間出隊(duì)列并訪問(wèn)它。入口房間中不存在寶物,這樣我們可以搜索入口房間的所有鄰居房間。入口房間有一個(gè)鄰居房間,即蜘蛛房間。于是,我們把它入隊(duì)列。

3. 當(dāng)?shù)诙芜M(jìn)入到while循環(huán)時(shí),我們把蜘蛛房間出隊(duì)列并訪問(wèn)它。因?yàn)橹┲敕块g中也沒(méi)有寶物,所以我們進(jìn)一步搜索蜘蛛房間的所有鄰居房間。蜘蛛房間有一個(gè)鄰居房間,即入口房間,于是我們把它入隊(duì)列。

4. 當(dāng)?shù)谌芜M(jìn)入到while循環(huán)時(shí),我們把入口房間出隊(duì)列……

問(wèn)題是:我們以前已經(jīng)達(dá)到過(guò)這個(gè)位置了!

為了修正這個(gè)問(wèn)題,我們需要記下我們?cè)?jīng)訪問(wèn)過(guò)的頂點(diǎn)信息,以便我們確保不會(huì)第二次訪問(wèn)它們。

有好幾種辦法可以解決這個(gè)問(wèn)題。你可以像如下這樣更新你的代碼:

  1. public func breadthFirstSearch(from source: Vertex<Element>, to destination: Vertex<Element>) -> [Edge<Element>]? { 
  2.   var queue = Queue<Vertex<Element>>() 
  3.   queue.enqueue(source) 
  4.   var enqueuedVertices = Set<Vertex<Element>>() // 1 
  5.   
  6.   while let visitedVertex = queue.dequeue() { 
  7.     if visitedVertex == destination { 
  8.       return [] 
  9.     } 
  10.    let neighbourEdges = edges(from: visitedVertex) ?? [] 
  11.     for edge in neighbourEdges { 
  12.       if !enqueuedVertices.contains(edge.destination) { // 2 
  13.         enqueuedVertices.insert(visitedVertex) // 3 
  14.         queue.enqueue(edge.destination) 
  15.       } 
  16.     } 
  17.   } 
  18.   return nil 
  19.  

讓我們回顧一下發(fā)生了什么變化︰

1.上面的代碼將創(chuàng)建一個(gè)頂點(diǎn)數(shù)組,用來(lái)描述到目前為止你遇到過(guò)的頂點(diǎn)的列表。請(qǐng)記住,Vertex類型是Hashable,所以我們不需要做任何更多的工作來(lái)構(gòu)建一個(gè)頂點(diǎn)集。

2.每當(dāng)檢查相鄰的頂點(diǎn),都要首先檢查此前是否遇到過(guò)該結(jié)點(diǎn)。

3.如果你以前沒(méi)有遇到過(guò)該結(jié)點(diǎn),則將它添加到兩個(gè)隊(duì)列中:要處理的頂點(diǎn)列表(隊(duì)列結(jié)構(gòu))和遇到的頂點(diǎn)列表(enqueuedVertices)。

這意味著,上面的搜索算法是相當(dāng)安全的。但是,現(xiàn)在你還不能訪問(wèn)比開(kāi)始的那個(gè)圖中更多的頂點(diǎn),因此搜索最終必須終止。

發(fā)現(xiàn)回路

我們的算法快要成功了!

到目前為止,你已經(jīng)知道如果找不到目的地,你會(huì)返回一個(gè)nil值。但是,如果你找到了目的地,你需要找到你的往回走的線路。不幸的是,每個(gè)你訪問(wèn)過(guò)的房間都已經(jīng)出隊(duì)列了,對(duì)于如何找到目的地沒(méi)有留下任何記錄信息!

為了記錄下搜索信息,你需要使用一個(gè)字典結(jié)構(gòu)來(lái)替換你搜索過(guò)的頂點(diǎn)集合,其中需要包含所有您搜索過(guò)的頂點(diǎn)信息和你如何到達(dá)該頂點(diǎn)的信息。我們不妨把這想像為探索一個(gè)迷宮,使用一個(gè)帶箭頭的粉筆標(biāo)記指向所有你探索過(guò)的房間——通過(guò)這種方式來(lái)記憶下到達(dá)入口處的所有信息。

如果我們保持跟蹤所有的箭頭——針對(duì)我們?cè)L問(wèn)過(guò)的任何房間,我們只可以查找我們沿著到達(dá)它的邊緣。這個(gè)邊將引回到我們?cè)缧r(shí)候訪問(wèn)過(guò)的房間。當(dāng)然,我們也可以查找我們沿著到達(dá)它的邊,如此下去……直到開(kāi)頭的那一條邊。

讓我們來(lái)試試這種想法——創(chuàng)建下面的Visit枚舉類型。注意,你必須在Graphable擴(kuò)展的外部創(chuàng)建這個(gè)類型,因?yàn)镾wift 3不支持嵌套泛型類型。

  1. enum Visit<Element: Hashable> { 
  2.   case source 
  3.   case edge(Edge<Element>) 
  4.  

在我們的查詢表中,***列中的每個(gè)項(xiàng)都是一個(gè)頂點(diǎn)(Vertex),但第二列中的每個(gè)項(xiàng)并不都是一條邊(Edge);一個(gè)頂點(diǎn)(Vertex)總是源頂點(diǎn);否則,就會(huì)出現(xiàn)嚴(yán)重的錯(cuò)誤,從而導(dǎo)致我們永遠(yuǎn)走不出圖去!

接下來(lái),按照如下所示修改您的方法︰

  1. public func breadthFirstSearch(from source: Vertex<Element>, to destination: Vertex<Element>) -> [Edge<Element>]? { 
  2.   var queue = Queue<Vertex<Element>>() 
  3.   queue.enqueue(source) 
  4.   var visits : [Vertex<Element> : Visit<Element>] = [source: .source] // 1 
  5.   
  6.   while let visitedVertex = queue.dequeue() { 
  7.     // TODO: Replace this... 
  8.     if visitedVertex == destination { 
  9.      return [] 
  10.     } 
  11.     let neighbourEdges = edges(from: visitedVertex) ?? [] 
  12.     for edge in neighbourEdges { 
  13.       if visits[edge.destination] == nil { // 2 
  14.         queue.enqueue(edge.destination) 
  15.         visits[edge.destination] = .edge(edge) // 3 
  16.       } 
  17.     } 
  18.   } 
  19.   return nil 
  20.  

讓我們回顧一下上面的代碼中發(fā)生了什么變化︰

1. 這將創(chuàng)建一個(gè)對(duì)應(yīng)于Vertex鍵和Visit值的字典,并使用源頂點(diǎn)作為從“源點(diǎn)”處的訪問(wèn)來(lái)初始化這個(gè)字典。

2. 如果字典里沒(méi)有對(duì)應(yīng)于某一個(gè)頂點(diǎn)的入口,那么說(shuō)明此頂點(diǎn)還沒(méi)有加入隊(duì)列。

3. 每當(dāng)你入隊(duì)一個(gè)頂點(diǎn)時(shí),你并不只是把它放進(jìn)一個(gè)頂點(diǎn)集合中,還要記錄下到達(dá)它對(duì)應(yīng)的邊。

***,你可以從目標(biāo)到入口進(jìn)行回溯!并且使用TODO注釋處的實(shí)際代碼來(lái)更新if語(yǔ)句︰

  1. if visitedVertex == destination { 
  2.   var vertex = destination // 1 
  3.   var route : [Edge<Element>] = [] // 2 
  4.   
  5.   while let visit = visits[vertex], 
  6.     case .edge(let edge) = visit { // 3 
  7.   
  8.     route = [edge] + route 
  9.     vertex = edge.source // 4 
  10.   
  11.   } 
  12.   return route // 5 
  13.  

讓我們分析一下上面的代碼︰

1.首先創(chuàng)建一個(gè)新的變量,用來(lái)存儲(chǔ)作為路線的一部分的每個(gè)頂點(diǎn)。

2.還要?jiǎng)?chuàng)建一個(gè)變量來(lái)存儲(chǔ)路線。

3.創(chuàng)建一個(gè)while循環(huán);只要訪問(wèn)字典有一個(gè)頂點(diǎn)入口并且只要此入口是一條邊,則該循環(huán)將會(huì)繼續(xù)下去。如果該入口是一個(gè)源點(diǎn),則該while循環(huán)將結(jié)束。

4.把邊添加到你的路線的開(kāi)頭,并把頂點(diǎn)設(shè)置為該邊的源點(diǎn)。現(xiàn)在,你距離開(kāi)始處更接近了一步。

5.while循環(huán)結(jié)束;所以,你的路線現(xiàn)在也必須完成。

[[185897]]

到此為止,我們解決了所有問(wèn)題!你可以在示例工程文件的結(jié)束處加入如下代碼來(lái)進(jìn)行測(cè)試:

  1. if let edges = dungeon.breadthFirstSearch(from: entranceRoom, to: treasureRoom) { 
  2.   for edge in edges { 
  3.     print("\(edge.source) -> \(edge.destination)"
  4.   } 
  5.  

相應(yīng)地,你應(yīng)當(dāng)在控制臺(tái)上觀察到如下的輸出結(jié)果:

  1. Entrance -> Rat 
  2.  
  3. Rat -> Treasure  

小結(jié)

我真心希望廣大讀者朋友能夠喜歡本教程提供的基于Swift語(yǔ)言的廣度優(yōu)先搜索算法!

在本教程中,我們已經(jīng)擴(kuò)展了所有Graphable數(shù)據(jù)類型的行為,所以您可以搜索從任一頂點(diǎn)到任何其他頂點(diǎn)的路線。更妙的是,你得到的是一條擁有最少步數(shù)的路線。

你可以從網(wǎng)址https://koenig-media.raywenderlich.com/uploads/2017/03/BreadthFirstSearch-Final.playground.zip處下載包含所有上述代碼的改進(jìn)的示例工程源碼。你也可以在Swift算法俱樂(lè)部存儲(chǔ)庫(kù)中找到原始廣度優(yōu)先搜索算法的原始實(shí)現(xiàn)代碼并參與進(jìn)一步討論。

事實(shí)上,這個(gè)算法僅僅是Swift算法俱樂(lè)部存儲(chǔ)庫(kù)中的一個(gè)小部分,感興趣的讀者可以進(jìn)一步查閱這些代碼庫(kù)(https://github.com/raywenderlich/swift-algorithm-club)。

【51CTO譯稿,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文譯者和出處為51CTO.com】

責(zé)任編輯:龐桂玉 來(lái)源: 51CTO.com
相關(guān)推薦

2025-02-26 05:00:00

DFS算法遞歸

2020-12-21 14:40:10

Java技術(shù)開(kāi)發(fā)

2023-10-30 00:14:34

Agent提示工程LLM

2015-06-15 18:07:07

2009-06-10 15:57:49

AndroidMIPS架構(gòu)

2024-03-19 15:09:07

2023-11-06 10:07:22

模型人工智能數(shù)字

2023-12-27 08:00:00

SQL數(shù)據(jù)庫(kù)LMQL

2021-12-13 10:50:24

軟件開(kāi)發(fā) 技術(shù)

2012-02-29 13:32:28

Java

2023-05-30 07:58:01

谷歌搜索算法

2015-06-15 18:45:28

App Thinnin應(yīng)用瘦身

2013-06-17 14:30:56

手游開(kāi)發(fā)工具CocoStudioCocos2d-x

2020-10-17 11:14:19

數(shù)據(jù)結(jié)構(gòu)與算法系列

2018-10-12 15:15:45

電商搜索算法

2009-05-06 16:54:05

firefoxseo瀏覽器

2023-05-05 14:11:12

區(qū)塊鏈航空領(lǐng)域

2022-01-27 15:33:11

區(qū)塊鏈金融技術(shù)

2022-10-24 10:20:28

物聯(lián)網(wǎng)智能計(jì)量

2014-08-06 14:56:15

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)