ElasticSearch教程(詳解版)

????????本篇博客將向各位詳細介紹elasticsearch,也算是對我最近學完elasticsearch的一個總結,對于如何在Kibana中使用DSL指令,本篇文章不會進行介紹,這里只會介紹在java中如何進行使用,保證你看完之后就會在項目中進行上手,那我們開始本篇教程吧~

什么是ElasticSearch

  • Elasticsearch,基于lucene.分布式的Restful實時搜索和分析引擎(實時)
  • 分布式的實時文件存儲,每個字段都被索引并可被搜索
  • 高擴展性,可擴展至上百臺服務器,處理PB級結構化或非結構化數據
  • Elasticsearch用于全文檢索,結構化搜索,分析/合并使用

ElasticSearch是如何做到這么快的

  • 分布式存儲:ElasticSearch把數據存儲在多個節點上,從而減少單個節點的壓力,從而提高性能
  • 索引分片:ElasticSearch把索引分成多個分片,這樣可以讓查詢操作并行化,從而提高性能
  • 全文索引:ElasticSearch把文檔轉換成可搜索的結構化數據,從而提高效率
  • 倒排索引:ElasticSearch將文檔進行分詞處理,把每個詞在哪個文檔中出現過進行映射,并存儲這些信息,從而在搜索時,查詢這些分詞和搜索這些分詞存在在哪些文檔中,提高查詢效率
  • 異步請求處理:ElasticSearch能夠在請求到達時立即返回,避免長時間等待,提高效率

全文索引和倒排索引是什么

????????全文檢索是指計算機索引程序通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現的次數和位置,當用戶查詢時,檢索程序就根據事先建立的索引進行查找,并將查找的結果反饋給用戶的檢索方式。這個過程類似于通過字典中的檢索字表查字的過程。全文搜索搜索引擎數據庫中的數據。

  • 優點:

    • 可以給多個字段創建索引

    • 根據索引字段搜索、排序速度非常快

  • 缺點:

    • 根據非索引字段,或者索引字段中的部分詞條查找時,只能全表掃描。

????????倒排索引就和傳統的索引結構相反,傳統的索引是由文檔組成的,每個文檔中都包含了若干個詞匯,然后根據這些詞匯簡歷索引。而倒排索引則與其相反,倒排索引由詞匯構成,每個詞匯對應若干個文檔,然后根據這些文檔建立索引。

  • 優點:

    • 根據詞條搜索、模糊搜索時,速度非常快

  • 缺點:

    • 只能給詞條創建索引,而不是字段

    • 無法根據字段做排序

ElasticSearch和Mysql之間的映射關系

Mysql類型? ? ? ?ElasticSearch類型說明
VARCHARtext、keyword根據是否需要使用全文搜索或精確搜索選擇使用text或keyword
CHAR????????

keyword? ? ? ? ? ? ? ? ? ? ? ?

通常映射為keyword,因為它們存儲較短的、不經常變化的字符序列
BLOB/TEXT? ? ? ??text大文本塊使用text,適用于全文檢索
INT,BIGINTlong大多數整數型使用long,以支持更大的數值
TINYINTbyte較小的整數可以映射為byte類型
DECIMAL,FLOAT,DOUBLEdouble,float
DATE,DATETIME,TIMESTAMPdate
BOOLEANboolean

倒排索引建立步驟

es中建立倒排索引需要兩步,首先對文檔進行分詞,其次建立倒排索引

分詞

????????分詞的意思大概就是對文檔中的數據通過es的分詞器進行分割成一個個詞項,比如 “我是銀氨溶液” 這句話,經過分詞過后就是 “我”、“是”、“銀氨”、“溶液”,當然es的分詞器分為ik_smart分詞器和ik_max_word分詞,所以實際操作時這句話會被分解為不同的詞段。

es中的一些概念

????????文檔

????????elasticsearch是面向文檔(Document)存儲的,可以是數據庫中的一條商品數據,一個訂單信息。文檔數據會被序列化為json格式后存儲在elasticsearch中。

????????字段

Json文檔中往往包含很多的字段(Field),類似于數據庫中的列。

????????索引

索引(Index),就是相同類型的文檔的集合。因此,我們可以把索引當做是數據庫中的表。

????????映射

????????數據庫的表會有約束信息,用來定義表的結構、字段的名稱、類型等信息。因此,索引庫中就有映射(mapping),是索引中文檔的字段約束信息,類似表的結構約束。

對照圖表:

MySQLElasticsearch說明
TableIndex索引(index),就是文檔的集合,類似數據庫的表(table)
RowDocument文檔(Document),就是一條條的數據,類似數據庫中的行(Row),文檔都是JSON格式
ColumnField字段(Field),就是JSON文檔中的字段,類似數據庫中的列(Column)
SchemaMappingMapping(映射)是索引中文檔的約束,例如字段類型約束。類似數據庫的表結構(Schema)
SQLDSLDSL是elasticsearch提供的JSON風格的請求語句,用來操作elasticsearch,實現CRUD

RestAPI

首先,在這篇文章中不會將所有api都介紹完,所以這了貼上官方文檔的地址,以共各位查看:

ElasticSearch官方文檔

這里需要先引入es的依賴

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>

同時,你需要在pom文件中修改es的版本和你本地的一樣

?然后,我們需要把es注入到spring容器中

@Configuration
public class ElasticSearchClientConfig {@Beanpublic RestHighLevelClient restHighLevelClient(){RestHighLevelClient client=new RestHighLevelClient(RestClient.builder(new HttpHost("127.0.0.1", 9200, "http")));return client;}
}

?準備工作完成之后就可以開始接下來的學習啦~

索引的CRUD操作

索引的操作較為簡單,而且項目中實際都是對文檔進行操作,所以我這里貼出索引的相關操作代碼,并做出解釋,各位可以了解一下

 @Autowired@Qualifier("restHighLevelClient")private RestHighLevelClient client;//    測試索引的創建  Request@Testvoid testCreateIndex() throws Exception {
//        1、創建索引請求CreateIndexRequest request = new CreateIndexRequest("yinan_index");
//       2、客戶端執行請求,請求后獲得響應CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);System.out.println(createIndexResponse);}//    測試獲取索引@Testvoid testGetIndex() throws IOException {GetIndexRequest getIndex = new GetIndexRequest("yinan_index");boolean exists = client.indices().exists(getIndex, RequestOptions.DEFAULT);System.out.println(exists);}//    刪除索引@Testvoid testDeleteIndex() throws Exception {DeleteIndexRequest request = new DeleteIndexRequest("yinan_index");AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);System.out.println(delete.isAcknowledged());}

其中indices中包含了操作索引庫的所有方法,在創建索引的時候如果有多個字段,可以提前寫好?一個字符串常量,例如:

   public static final String MAPPING_TEMPLATE = "{\n" +"  \"mappings\": {\n" +"    \"properties\": {\n" +"      \"id\": {\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"name\":{\n" +"        \"type\": \"text\",\n" +"        \"analyzer\": \"ik_max_word\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"address\":{\n" +"        \"type\": \"keyword\",\n" +"        \"index\": false\n" +"      },\n" +"      \"price\":{\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"score\":{\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"brand\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"city\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"starName\":{\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"business\":{\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"location\":{\n" +"        \"type\": \"geo_point\"\n" +"      },\n" +"      \"pic\":{\n" +"        \"type\": \"keyword\",\n" +"        \"index\": false\n" +"      },\n" +"      \"all\":{\n" +"        \"type\": \"text\",\n" +"        \"analyzer\": \"ik_max_word\"\n" +"      }\n" +"    }\n" +"  }\n" +"}";

?然后將新增索引中的代碼修改成以下:

文檔的CRUD操作

對于文檔這里將重點介紹查詢的相關方法,其它操作只做簡單介紹。

查詢操作
MatchAll
/*** 查詢全部*/@Testvoid testMatchAll() throws IOException {//1.準備requestSearchRequest request = new SearchRequest("hotel");//2.準備參數request.source().query(QueryBuilders.matchAllQuery());//3.發起請求得到響應結果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析響應handleResponse(response);}

結果如下:

?從結果我們也可以看到這個api是查詢所有數據的方法,但是控制臺只顯示了10條數據,這是因為這個方法自動進行分頁處理,每頁10條數據。

Match
   /*** 全文檢索*/@Testvoid testMatch() throws IOException {//1.準備requestSearchRequest request = new SearchRequest("hotel");//2.準備參數request.source().query(QueryBuilders.matchQuery("all","如家"));//3.發起請求得到響應結果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析響應handleResponse(response);}

結果:

match用來做基本的模糊匹配,在es中會對文本進行分詞,在match查詢的時候也會對查詢條件進行分詞,然后通過倒排索引找到匹配的數據。

term
 @Testvoid testMatch() throws IOException {//1.準備requestSearchRequest request = new SearchRequest("hotel");//2.準備參數
//        request.source().query(QueryBuilders.matchQuery("all","如家"));request.source().query(QueryBuilders.termQuery("city","北京"));//3.發起請求得到響應結果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析響應handleResponse(response);}

結果:

?從結果來看,term是做精確查詢的,所以一般可以用在查詢某個具體的屬性的時候

multiMatchQuery
 @Testvoid testMatch() throws IOException {//1.準備requestSearchRequest request = new SearchRequest("hotel");//2.準備參數
//        request.source().query(QueryBuilders.matchQuery("all","如家"));
//        request.source().query(QueryBuilders.termQuery("city","北京"));request.source().query(QueryBuilders.multiMatchQuery("如家", "city", "name"));//3.發起請求得到響應結果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析響應handleResponse(response);}

?結果:

????multiMatchQuery接受兩個參數,一個是text,一個是fieldname,前者表示要查詢的內容,后者表示要在哪些字段中進行查詢,如果后者中的數據只有一個,那該方法和matchall一致,如果后者有多個,那查詢的結果必須要滿足其中的一個。
rangeQuery
 @Testvoid testMatch() throws IOException {//1.準備requestSearchRequest request = new SearchRequest("hotel");//2.準備參數
//        request.source().query(QueryBuilders.matchQuery("all","如家"));
//        request.source().query(QueryBuilders.termQuery("city","北京"));
//        request.source().query(QueryBuilders.multiMatchQuery("如家", "city", "name"));request.source().query(QueryBuilders.rangeQuery("price").gte(100).lte(150));//3.發起請求得到響應結果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析響應handleResponse(response);}

結果:

該方法主要做范圍查詢,相當于sql語句中的between....and...

布爾查詢
/*** 布爾查詢* @throws IOException*/@Testvoid testBool() throws IOException {//1.準備requestSearchRequest request = new SearchRequest("hotel");//2.準備參數BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();boolQuery.must(QueryBuilders.termQuery("city","北京"));boolQuery.filter(QueryBuilders.rangeQuery("price").lte(300));request.source().query(boolQuery);//3.發起請求得到響應結果SearchResponse response = client.search(request, RequestOptions.DEFAULT);System.out.println( "========"+response.getHits());//4.解析響應handleResponse(response);}/*** 解析響應結果* @param response*/private void handleResponse(SearchResponse response) {SearchHits searchHits = response.getHits();//總條數long total = searchHits.getTotalHits().value;System.out.println("共搜索到"+total+"條數據");//文檔數組SearchHit[] hits = searchHits.getHits();//遍歷for (SearchHit hit : hits){//獲取文檔sourceString json = hit.getSourceAsString();//反序列化HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);//獲取高亮結果Map<String, HighlightField> highlightFields = hit.getHighlightFields();if(!CollectionUtils.isEmpty(highlightFields)){//根據字段名獲取高亮結果HighlightField highlightField = highlightFields.get("name");if(highlightField != null){//獲取高亮值String name = highlightField.getFragments()[0].string();//替換hotelDoc.setName(name);}}System.out.println("hotDoc = "+hotelDoc);}

這里簡單說一下怎么得到的?handleResponse函數

通過以上圖片我們就不難看出?response的響應結構,因此我們就可以具體推出相關信息,所以沒有學習過在Kibana上使用DSL指令的朋友可以先去學習一下,然后再來運用到java中事半功倍。

當然,如果你只需要獲取數據和總條數,可以修改成以下形式:

 private PageResult handleResponse(SearchResponse response) {SearchHits hits = response.getHits();long total = hits.getTotalHits().value;SearchHit[] searchHits = hits.getHits();List<HotelDoc> hotels = new ArrayList<>();for (SearchHit searchHit : searchHits) {String source = searchHit.getSourceAsString();
//            反序列化HotelDoc hotelDoc = JSON.parseObject(source, HotelDoc.class);hotels.add(hotelDoc);}return new PageResult(total,hotels);}
分頁和排序 對結果的處理
/*** 分頁和排序 對結果的處理*/@Testvoid testPageAndSort() throws IOException {int page = 1,size = 5;//1.準備requestSearchRequest request = new SearchRequest("hotel");//2.準備參數request.source().query(QueryBuilders.matchAllQuery());//排序request.source().sort("price", SortOrder.ASC);//分頁request.source().from((page-1)*size).size(size);//3.發起請求得到響應結果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析響應handleResponse(response);}

當然,還有其它一些api這里還沒有介紹到,各位可以去官網進行查看詳細文檔說明~

?添加文檔
@Data
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {private String name;private Integer age;
}
 @Testvoid testAddDocument() throws IOException {
//        1、創建對象User user = new User("yinan", 20);
//        2、創建請求IndexRequest request = new IndexRequest("yinan_index");
//        規則  put /yinan_index/_doc/1request.id("1");request.timeout(TimeValue.timeValueSeconds(1));request.timeout("1s");
//        將數據放入請求  jsonrequest.source(JSON.toJSONString(user), XContentType.JSON);
//        客戶端發送請求,獲取相應的結果IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);System.out.println(indexResponse.toString());System.out.println(indexResponse.status());  //對應我們命令返回狀態}
批量添加文檔
 @Testvoid testBulkRequest() throws Exception {BulkRequest bulkRequest = new BulkRequest();bulkRequest.timeout("10s");ArrayList<User> userList = new ArrayList<>();userList.add(new User("yinan1", 21));userList.add(new User("yinan1", 21));userList.add(new User("yinan3", 26));userList.add(new User("yinan4", 24));userList.add(new User("yinan5", 27));userList.add(new User("yinan12", 20));for (int i = 0; i < userList.size(); i++) {bulkRequest.add(new IndexRequest("yinan_index").id("" + (i + 1)).source(JSON.toJSONString(userList.get(i)), XContentType.JSON));}BulkResponse itemResponses = client.bulk(bulkRequest, RequestOptions.DEFAULT);System.out.println(itemResponses.hasFailures());}
修改文檔
 @Testvoid testUpdateDocument() throws Exception {UpdateRequest request = new UpdateRequest("yinan_index", "1");request.timeout("1s");User user = new User("yinan_update", 21);request.doc(JSON.toJSONString(user), XContentType.JSON);UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);System.out.println(updateResponse.status());}
刪除文檔
   @Testvoid testDeleteDocument() throws Exception {DeleteRequest request = new DeleteRequest("yinan_index", "1");request.timeout("1s");DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT);System.out.println(deleteResponse.status());}

以上就是對es部分api的講解,當然只看api使用是遠遠不夠的,所以我們需要做一個小訓練來鞏固我們學習的東西。

資料已經置頂在本篇博客,有需要的請自取~

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

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

相關文章

Arduino燒錄esp8266

default_encoding: cp936 Assume aggressive ‘core.a’ caching enabled. Note: optional global include file ‘arduino_modified_sketch_764314\Blink.ino.globals.h’ does not exist. Read more at https://arduino-esp8266.readthedocs.io/en/latest/faq/a06-global-bui…

【計劃】裝修相關感想

計劃 Summary 從去年年底開始規劃、設計、落實家里的裝修&#xff0c;2024年4月正式開始裝修&#xff0c;一個人探索和學習了很多知識和概念。 準備把這些東西做一些記錄和分享&#xff0c;一方面記錄一些裝修的流程和中間的小細節便于第二次裝修的時候避免&#xff1b;另一方…

Android設備實時監控藍牙的連接、配對、開關3種狀態

一、簡介 Android設備&#xff0c;需要實時監控本機藍牙連接其他藍牙設備的狀態&#xff0c;包含&#xff1a;連接、配對、開關3種狀態。本文介紹了2種方法&#xff0c;各有優勢&#xff0c;下面來到我的Studio一起瞅瞅吧~ 二、定時器任務 Handler 功能方法 定時器任務 Hand…

寫字靜不下心?不如試試這些“笨方法”

夏天悄悄熱起來啦&#xff5e;有人說&#xff0c;想踏踏實實寫一會兒&#xff0c;但又靜不下心&#xff0c;耐不住性子&#xff0c;快收下這四個小錦囊&#xff0c;與古人一起笨拙精進吧&#xff01;    1、不論輸贏      每次課前&#xff0c;暄桐林曦老師總會強調&am…

AlloyTeam Web前端大會:深入探索前端的無限可能

AlloyTeam Web前端大會&#xff1a;深入探索前端的無限可能 在數字化浪潮的推動下&#xff0c;Web前端技術日新月異&#xff0c;成為引領行業發展的重要力量。AlloyTeam Web前端大會作為業界的盛會&#xff0c;匯聚了眾多前端領域的精英&#xff0c;共同探討前端的未來發展趨勢…

內網-win1

一、概述 1、工作組&#xff1a;將不同的計算機按功能(或部門)分別列入不同的工作組 (1)、查看&#xff08;windows&#xff09; 查看當前系統中所有用戶組&#xff1a;打開命令行--》net localgroup查看組中用戶&#xff1a;打開命令行 --》net localgroup 后接組名查看用戶…

FreeRTOS任務調度機制(源碼講解)

任務的調度機制(核心是鏈表)&#xff01;&#xff01;&#xff01; 使用鏈表來管理任務 在我前面寫的FreeRTOS任務(深入到源碼進行分析)&#xff0c;我創建了三個任務&#xff0c;他們的優先級都是一樣的&#xff0c;所以他們在FreeRTOS中是輪流執行的&#xff0c;實際上&…

19.1 簡易抽獎

準備一個數組&#xff0c;里面添加10個獎品數據&#xff0c;讓獎品數據快速的在盒子中隨機顯示&#xff0c;通過按鈕控制盒子里面的內容停止。 效果圖&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">&…

解釋Python中的PEP 8是什么 為什么它很重要

PEP 8 是 Python 的一個編碼規范&#xff0c;也稱為 Python 增強提案 8。它提供了一系列關于如何編寫清晰、一致的 Python 代碼的指導原則。這些原則涵蓋了代碼布局、命名約定、注釋、文檔字符串、編碼建議、導入語句、異常、全局變量、嵌套等方面。 為什么PEP 8很重要&#x…

npm install pubsub-js報錯的解決匯總

我在練習谷粒商城P83時&#xff0c;選擇分類時觸發向后端請求選擇分類catId綁定的品牌數據&#xff0c;發現前端控制臺報錯&#xff1a; "PubSub is not definded",找不到pubsub。 因為缺少pubsub包&#xff0c;所以開始安裝此包。 于是在網上一頓搜索猛如虎&…

xilinx ip自帶XDC只讀

檢查生成的IP核再目錄下顯示的文件類型是不是.xcix 如果是的話&#xff0c;重新生成為.xci 再二次編輯即可 或者 將框柱的部分不選擇&#xff0c;從新生成

MongoDB CRUD操作:批量寫操作

MongoDB CRUD操作&#xff1a;批量寫操作 文章目錄 MongoDB CRUD操作&#xff1a;批量寫操作關于批量操作的順序bulkWrite()支持的方法舉例向分片集合批量插入的策略預分割集合無序寫入 mongos避免單調節流 MongoDB提供了批量執行寫入操作的能力&#xff0c;但批量寫入操作只影…

《計算機工程與應用》最新投稿經驗2024年5月

研二下第一次投稿&#xff0c;深度學習長時間序列預測方向&#xff0c;選擇了《計算機工程與應用》期刊&#xff0c;是CSCD擴展刊北大核心&#xff0c;且在24年被EI收錄等等。4.10交稿到最后5.31收到錄用通知&#xff0c;歷時不到2個月&#xff0c;總的來說編輯部效率確實高。 …

LLM背后的基礎模型 1

寫在最前面的話 任何開源技術是最有生命力的&#xff0c;也是最具分享精神的。一直覺得大模型領域需要有一個系列能夠從零開始系統性的講述領域知識&#xff0c;給與這個領域的從業人員或者對其有興趣的門外漢及時的幫助。國外承擔“布道者”的公司眾多&#xff0c;而數磚公司…

云技術最全詳解

目錄 云技術 1.定義 2.特點 2.類型 2.1IaaS&#xff08;基礎設置即服務&#xff09; 2.2PaaS&#xff08;平臺即服務&#xff09; 2.3SaaS&#xff08;軟件即服務&#xff09; 3.云技術模型 3.1公有云 3.2私有云 3.3混合云 云技術 1.定義 云技術是一種云計算和存儲…

如何讓 LightRoom 每次導入照片后不自動彈出 SD 卡 LR

如何讓 LightRoom 每次導入照片后不自動彈出 SD 卡 LR 在導入窗口左上角有個選項&#xff1a; 導入后彈出 把這個去掉就可以了

Rust 基本語法

變量 整數 無符號整數以u開頭有符號整數以i開頭對于Rust默認整數是i32對于整數溢出 開發模式中編譯會檢測溢出&#xff0c;如果溢出會導致程序panic發布模式中編譯不會檢查可能會導致的溢出&#xff0c;如果運行時發生溢出&#xff0c;會執行環繞操作保證數值在范圍內且程序不…

Spark大數據 掌握RDD的創建

在Apache Spark中&#xff0c;彈性分布式數據集&#xff08;Resilient Distributed Dataset&#xff0c;簡稱RDD&#xff09;是一個核心的數據結構&#xff0c;用于表示不可變、可分區、可并行操作的元素集合。理解并掌握RDD的創建是使用Spark進行大數據處理的關鍵步驟之一。 …

Qt Creator(Qt 6.6)拷貝一行

Edit - Preference - Environment&#xff1a; 可看到&#xff0c;拷貝一行的快捷鍵是&#xff1a; ctrl Ins

數據結構-堆(帶圖)詳解

前言 本篇博客我們來仔細說一下二叉樹順序存儲的堆的結構&#xff0c;我們來看看堆到底如何實現&#xff0c;以及所謂的堆排序到底是什么 &#x1f493; 個人主頁&#xff1a;普通young man-CSDN博客 ? 文章專欄&#xff1a;數據結構_普通young man的博客-CSDN博客 若有問題 評…