国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

淺淺理解一下堆

這篇具有很好參考價值的文章主要介紹了淺淺理解一下堆。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

一、堆的定義及本質(zhì)

二、堆的核心操作

1、向下調(diào)整

2、堆的創(chuàng)建

3、向上調(diào)整

?三、堆的比較器傳入及堆中簡單函數(shù)的實現(xiàn)

四、堆的應用

1、用于OS調(diào)度進程

2、topk問題

?3、堆排序


一、堆的定義及本質(zhì)

堆在Java中是以優(yōu)先級隊列來表現(xiàn)的(PrityQueue),不傳入外部比較器則以小堆來實現(xiàn)(取出最小值)

前提:優(yōu)先級隊列中的元素具備比較能力(1.元素類型本身是可以比較的 2.通過構(gòu)造方法傳入一個外部比較器)

堆的作用:常用來在頻繁變動的數(shù)據(jù)集中找出最值

堆的本質(zhì):邏輯上是完全二叉樹,本質(zhì)上是數(shù)組

淺淺理解一下堆

堆的定義:

堆總是滿足下列性質(zhì):

1.堆中某個節(jié)點的值總是不大于或不小于其父節(jié)點的值;

2、堆總是一棵完全二叉樹。

將根節(jié)點最大的堆叫做最大堆或大根堆,根節(jié)點最小的堆叫做最小堆或小根堆。常見的堆有二叉堆、斐波那契堆等。堆是非線性數(shù)據(jù)結(jié)構(gòu),相當于一維數(shù)組,有兩個直接后繼。

?對于一個結(jié)點 2*index+1 是他的左節(jié)點 2*index+2是右節(jié)點? ?(index-1)/ 2 可以求得父節(jié)點

二、堆的核心操作

1、向下調(diào)整

當我們從堆頂取走一個最值時,這時堆的結(jié)構(gòu)已經(jīng)發(fā)生變化,我們往往用堆的最后一個結(jié)點來代替堆的根,但此時有可能這個值已經(jīng)不滿足堆的定義,我們需要對其進行向下調(diào)整,此時只有堆根部不滿足堆的定義

淺淺理解一下堆

?我們此時對根部進行調(diào)整

我們首先想到在根的左右結(jié)點中找到最小的值來判斷是否大于根,如果根已經(jīng)是最小值,則滿足堆的定義,不需要進行交換,否則根與最小值進行交換

 //對堆的根部進行向下調(diào)整
    //結(jié)束調(diào)整的條件?  已經(jīng)滿足堆的結(jié)構(gòu),無需調(diào)整    當前結(jié)點已經(jīng)是葉子結(jié)點,下邊沒有節(jié)點,無需調(diào)整
    public void justDown(int arr[],int size,int index){
        int left=2*index+1;//判斷左子樹是否存在,如果沒有左子樹,必沒有右子樹(完全二叉樹性質(zhì))
        while (left<size){
            int min=left+1<size&&arr[left+1]<arr[left]?left+1:left;//找出左右子樹中的最小值
            int largest=arr[index]<=arr[min]?index:min;//在根和最小值中再找出最小值
            if (largest==index) break;//如果根就是最小值 堆結(jié)構(gòu)已滿足 退出循環(huán)
            swap(arr,index,min);  //不滿足交換根和最小值,根等于最小值進行下一次調(diào)整
            index=min;
            left=index*2+1;
        }
    }

    private void swap(int[] arr, int index, int min) {
        int temp=arr[index];
        arr[index]=arr[min];
        arr[min]=temp;
    }

2、堆的創(chuàng)建

在我們已經(jīng)掌握向下調(diào)整的情況下,如何將一個完全二叉樹調(diào)整為堆呢?

我們可以對每個結(jié)點進行向下調(diào)整,使每個結(jié)點都滿足堆的定義

但我們知道向下調(diào)整的結(jié)束條件是當前結(jié)點已經(jīng)滿足堆的結(jié)構(gòu)或者當前結(jié)點是葉子結(jié)點,因此我們沒有必要對葉子結(jié)點進行調(diào)整,而在完全二叉樹中我們很容易找到最后一個結(jié)點(size-1)是最后一個結(jié)點的下標 而他的父節(jié)點是(size-1-1)/ 2? ?那么自他的父節(jié)點之后就全是葉子節(jié)點

public void creatHeap(int[] arr,int size){
        for (int i=(size-2)/2;i>=0;i--){
            justDown(arr,size,i);
        }
    }

為什么從上到下不行?因為我們向下調(diào)整是由要求的,必須左右子樹已經(jīng)是一個堆結(jié)構(gòu)了 如果不理解可以畫圖。。

淺淺理解一下堆?

在經(jīng)歷一次向下調(diào)整時我們發(fā)現(xiàn)最小的1永遠沒有走到頂部的機會,為什么呢,因為我們沒有滿足向下調(diào)整的定義。沒有找到真正最小的那個數(shù)調(diào)整到堆頂?

3、向上調(diào)整

在我們創(chuàng)建完成一個堆的時候,往往需要往堆里邊添加新的元素,由于我們從數(shù)組【0,size)是已經(jīng)創(chuàng)建好的堆結(jié)構(gòu),在我們新添加一個元素時,往往需要向上調(diào)整

淺淺理解一下堆

public void justUp(int[] arr,int index){
        int parent=(index-1)/2;//父節(jié)點下標
        while (arr[index]>arr[parent]){//當子節(jié)點大于父節(jié)點時我們需要向上調(diào)整
            swap(arr,index,parent);
            index=parent;//新的Index是他的父節(jié)點
            parent=(index-1)/2;//新的parent結(jié)點
        }
    }

?三、堆的比較器傳入及堆中簡單函數(shù)的實現(xiàn)

假設當前有一個person類,里邊的屬性有姓名和年齡,此時person是默認不支持比較的

 public class person{
        String name;
        int age;
    }

在不修改person類的情況下我們?nèi)绾螌崿F(xiàn)比較年齡呢?

答案是采用外部比較器

static class PersonComparator implements Comparator<Person>{

        @Override
        public int compare(Person o1, Person o2) {
            return o1.age-o2.age;
        }
    }
 static class PersonComparator implements Comparator<Person>{

        @Override
        public int compare(Person o1, Person o2) {
            return o1.age-o2.age;
        }
    }

    public static void main(String[] args) {
        PriorityQueue<Person> pq=new PriorityQueue(new PersonComparator());
        pq.add(new Person("小明",11));
        pq.add(new Person("小紅花",9));
        pq.add(new Person("小剛",21));
        while (!pq.isEmpty()){
            System.out.println(pq.poll().toString());
        }

    }

淺淺理解一下堆

堆的簡單函數(shù)全實現(xiàn):

public class MyPriorityQueue{
    long arr[];
    int size;
    public MyPriorityQueue(){//由于我們的元素類型是long類型沒有采用泛型,所以不涉及傳入比較器
        arr=new long[20];
        size=0;
    }

    public void add(long val){//往堆中加入一個元素
        ensureCapacity();//保證數(shù)組空間夠用
        arr[size]=val;
        justUp(arr,size);//新加入的元素需要向上調(diào)整維持一個堆結(jié)構(gòu)
        size++;
    }

    public long peek(){//查看堆頂部元素
        if (size<0){
            throw new RuntimeException("隊列是空的");
        }
        return arr[0];
    }

    public long poll(){//彈出堆頂部的元素
        if (size<0){
            throw new RuntimeException("隊列是空的");
        }
        long res=arr[0];//先記錄需要返回的元素
        arr[0]=arr[size-1];//把最后一個元素調(diào)整到堆的頂部并向下調(diào)整
        arr[size-1]=0;//最后一個元素交換到堆頂,那么它就要改為默認值
        size--;
        justDown(arr,size,0);
        return res;
    }

    public int size(){
        return size;
    }

    private void justDown(long[] arr, int size, int index) {
        int left=index*2+1;
        while (left<size){//如果還有子節(jié)點(不是葉子節(jié)點)
            int min=left+1<size&&arr[left+1]<arr[left]?left+1:left;//找到左右子結(jié)點中的最小值
            int largest=arr[min]<arr[index]?min:index;//找到最小的值
            if (largest==index) break;//如果index本身就是最小值說明他已經(jīng)滿足堆結(jié)構(gòu)不需要調(diào)整
            swap(arr,min,index);//交換index和最小值
            index=min;//新的index
            left=index*2+1;//新的最小值
        }
    }

    public void check(){//檢查是否滿足堆結(jié)構(gòu)
        if (size<0||size>arr.length){
            throw new RuntimeException("size越界");
        }
        for (int i=0;i<size;i++){
            int left = 2 * i + 1;
            int right = 2 * i + 2;

            if (left >= size) {
                // 說明是葉子,跳過
                continue;
            }

            // 左孩子破壞了規(guī)則
            if (arr[i] > arr[left]) {
                throw new RuntimeException(String.format("[%d] 位置的值大于其左孩子的值了", i));
            }

            // 右孩子破壞了規(guī)則
            if (right < size && arr[i] > arr[right]) {
                throw new RuntimeException(String.format("[%d] 位置的值大于其右孩子的值了", i));
            }
        }
    }

    private void justUp(long[] arr, int index) {
       while (index!=0){
           int parent=(index-1)/2;
           if (arr[parent]<=arr[index]) return;
           swap(arr,index,parent);
           index=parent;
       }
    }

    public boolean isEmpty(){
        return size==0;
    }

    private void ensureCapacity() {
        if (size<arr.length) return;
        arr= Arrays.copyOf(arr,size*2);
    }

    public void swap(long[] arr,int i,int j){
        long temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
}

四、堆的應用

1、用于OS調(diào)度進程

淺淺理解一下堆

2、topk問題

topk問題其實在現(xiàn)實生活有很多場景,比如說淘寶的好物top10等等

假如說我們要在很多數(shù)據(jù)中找到前k個最大的數(shù)? 我們有什么思路呢?

1.對所有的數(shù)據(jù)進行排序再取出前十

這種方法無疑消耗的時間成本和空間成本是是非常高的

時間復雜度達到了O(N*logN) 空間復雜度是O(N)

2.把所有的數(shù)據(jù)入堆,在取出前k個元素?

第二種方法的時間復雜度相較于第一種減少了很多 大概是O(k*logN),但我們忽略了在數(shù)據(jù)非常龐大時,堆的向下調(diào)整操作往往也需要耗費大量時間,并且空間復雜度也達到了驚人的O(N)

3.我們采用建小堆,小堆的size就是k,如果當前元素比小堆中最小元素大,我們就把它加進去,到最后堆里的元素就是topk

并且空間復雜度也只有O(k)

代碼如下:

public class Solution {
    public int[] smallestK(int[] arr, int k) {
        if (k==0){ //處理特殊情況,防止堆為空造成的index越界
            return new int[0];
        }
        PriorityQueue<Integer> pq=new PriorityQueue<>(((o1, o2) -> o2-o1));
        for (int i=0;i<k;i++){ //添加k個元素進入堆
            pq.add(arr[i]);
        }
        for (int i=k;i<arr.length;i++){//維持堆中的元素是最小的k個
            if (arr[i]<pq.peek()){
                pq.poll();
                pq.add(arr[i]);
            }
        }
        int[] res=new int[k];//返回這k個元素
        int i=0;
        while (!pq.isEmpty()){
            res[i++]=pq.poll();
        }
        return res;

    }
}

?3、堆排序

堆排序是一種利用堆結(jié)構(gòu)找到最值然后不斷取出進行排序,本質(zhì)上是一種選擇排序,不過由于堆找出并維持最值的時間復雜度是O(N*longN)所以他是一種高效的排序

我們把需要排序的數(shù)組看? 無序加有序的形式 一旦我們找到最值元素 我們就把它交換到最后邊

因此總的排序要進行arr.length-1次交換

public void swap(long[] arr,int i,int j){
        long temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }

    public long[] heapSort(long[] nums){
        //首先我們需要建立大堆,把大的元素找出并交換到最后邊
        buildHeap(nums);
        for (int i=0;i<nums.length-1;i++){
            swap(nums,0,arr.length-1-i);
            justDown(nums,arr.length-i-1,0);
        }
        return nums;
    }

    private void buildHeap(long[] nums) {
        for (int i=(nums.length-2)/2;i>=0;i--){
            justDown(nums,nums.length,i);
        }
    }

?文章來源地址http://www.zghlxwxcb.cn/news/detail-404841.html

到了這里,關于淺淺理解一下堆的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 文件目錄操作——Linux命令核心

    文件目錄操作——Linux命令核心

    目錄 相對路徑和絕對路徑 ?查看和切換工作目錄 pwd 顯示當前工作目錄的絕對路徑 cd 切換目錄 列出目錄內(nèi)容 ls 列出目錄的內(nèi)容 創(chuàng)建和刪除目錄文件夾 mkdir創(chuàng)建一個新目錄 rmdir 刪除一個空的目錄 touch 創(chuàng)建空文件 cp復制文件或者目錄 rm 刪除文件或目錄 ?mv移動文件與目錄或重

    2024年02月01日
    瀏覽(32)
  • 快速上手Linux核心命令(三):文件和目錄操作命令

    快速上手Linux核心命令(三):文件和目錄操作命令

    目錄 前言 cd 切換目錄 pwd 顯示當前路徑 ls 顯示目錄下內(nèi)容及相關屬性信息 mkdir 創(chuàng)建目錄 tree 以樹形結(jié)構(gòu)顯示目錄下的內(nèi)容 touch 創(chuàng)建空白文件或改變文件的時間戳屬性 cp 復制文件或目錄 mv 移動或重命名文件 rm 刪除文件或目錄 chown 改變文件或目錄的用戶用戶組 chmod 改變文件

    2023年04月23日
    瀏覽(28)
  • 淺淺記錄一下對某音的X-Agus、X-Gorgon、X-Khronos、X-Ladon研究(僅學習使用)

    近期比較閑,于是對該app進行了逆向研究 前兩年也對這個app進行了研究,那時候還沒有什么加密參數(shù) 可以很正常的進行采集 現(xiàn)在發(fā)現(xiàn)連包都抓不到了 于是查看了相關資料 發(fā)現(xiàn)該app走的不是正常的http/s協(xié)議 于是我hook了傳輸協(xié)議 就可以正常抓包了 抓包后就發(fā)現(xiàn)多了好幾個加

    2024年01月19日
    瀏覽(20)
  • 認清現(xiàn)實重新理解游戲的本質(zhì)

    認清現(xiàn)實重新理解游戲的本質(zhì)

    本文為觀后總結(jié):https://www.bilibili.com/video/BV1CT4y1J7ku/ 游戲會成為我們工作和學習拖延的幫兇,游戲?qū)ξ易畲蟮奈:褪峭涎樱?玩了幾十年游戲的人生注定不會過的那么精彩 ,因為所有的精彩都留在了虛擬的游戲世界(游戲開發(fā)商服務器數(shù)據(jù)庫里的一條數(shù)據(jù)) 游戲一直麻痹著

    2024年02月15日
    瀏覽(23)
  • 線性代數(shù)的本質(zhì)——幾何角度理解

    線性代數(shù)的本質(zhì)——幾何角度理解

    B站網(wǎng)課來自 3Blue1Brown的翻譯版,看完醍醐灌頂,強烈推薦: 線性代數(shù)的本質(zhì) 本課程從幾何的角度翻譯了線代中各種核心的概念及性質(zhì),對做題和練習效果有實質(zhì)性的提高,下面博主來總結(jié)一下自己的理解 在物理中的理解是一個有 起點和終點的方向矢量 ,而在計算機科學中

    2024年02月02日
    瀏覽(29)
  • Go 方法介紹,理解“方法”的本質(zhì)

    目錄 Go 方法介紹,理解“方法”的本質(zhì) 一、認識 Go 方法 1.1 基本介紹 1.2 聲明 1.2.1 引入 1.2.2 一般聲明形式 1.2.3 receiver 參數(shù)作用域 1.2.4 receiver 參數(shù)的基類型約束 1.2.5 方法聲明的位置約束 1.2.6 如何使用方法 二、方法的本質(zhì) 三、巧解難題 我們知道,Go 語言從設計伊始,就不支

    2024年02月06日
    瀏覽(24)
  • 機器學習基礎(一)理解機器學習的本質(zhì)

    機器學習基礎(一)理解機器學習的本質(zhì)

    ????????導讀: 在本文中,將深入探索機器學習的根本原理,包括基本概念、分類及如何通過構(gòu)建預測模型來應用這些理論。 目錄 機器學習 機器學習概念 相關概念 機器學習根本:模型 數(shù)據(jù)的語言:特征與標簽 訓練與測試:模型評估 機器學習的分類 監(jiān)督學習:有指導

    2024年02月20日
    瀏覽(21)
  • 本質(zhì)安全設備標準(IEC60079-11)的理解(四)

    本質(zhì)安全設備標準(IEC60079-11)的理解(四)

    IEC60079-11使用了較長的篇幅來說明設計中需要考慮到的各種間距, 這也從一定程度上說明了間距比較重要,在設計中是需要認真考慮。 從直覺上來講,間距越大,電路越安全,因為間距比較大,不容易產(chǎn)生電弧。同時間距比較大,也易于散熱,從溫度角度來說,設備也比較安

    2024年02月15日
    瀏覽(64)
  • 【云原生-深入理解Kubernetes-1】容器的本質(zhì)是進程

    【云原生-深入理解Kubernetes-1】容器的本質(zhì)是進程

    大家好,我是秋意零。 ?? CSDN作者主頁 ?? 博客主頁 ?? 簡介 ?? 普通本科生在讀 在校期間參與眾多計算機相關比賽,如:?? “省賽”、“國賽” ,斬獲多項獎項榮譽證書 ?? 各個平臺, 秋意零/秋意臨 賬號創(chuàng)作者 ?? 云社區(qū) 創(chuàng)建者 點贊、收藏+關注下次不迷路! 歡迎加

    2024年02月02日
    瀏覽(29)
  • <C++> list容器本質(zhì)|常用接口|自定義排序規(guī)則

    <C++> list容器本質(zhì)|常用接口|自定義排序規(guī)則

    ?作者簡介:熱愛后端語言的大學生,CSDN內(nèi)容合伙人 ?精品專欄:C++面向?qū)ο???系列專欄:C++泛型編程 ??前言 今天把 list 容器的基本操作、常用接口做一個系統(tǒng)的整理,結(jié)合具體案例熟悉自定義內(nèi)部排序方法的使用。 list 與 vector 是STL中最常用的兩個容器,如果對vector

    2024年02月01日
    瀏覽(29)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包