最大堆的插入/刪除/調整/排序操作(圖解+程式)(JAVA)

來源:互聯網
上載者:User
堆有最大堆和最小堆之分,最大堆就是每個節點的值都>=其左右孩子(如果有的話)值的完全二叉樹。最小堆便是每個節點的值都<=其左右孩子值的完全二叉樹。 

  設有n個元素的序列{k1,k2,...,kn},若且唯若滿足下列關係時,稱之為堆。 
 

堆的三種基本操作(以下以最大堆為例): 
⑴最大堆的插入   

    由於需要維持完全二叉樹的形態,需要先將要插入的結點x放在最底層的最右邊,插入後滿 足完全二叉樹的特點; 
  然後把x依次向上調整到合適位置滿足堆的性質,例如中插入80,先將80放在最後,然後兩次上浮到合適位置. 
  時間:O(logn)。  “結點上浮” 
 

程式實現: 

 //向最大堆中插入元素, heap:存放堆元素的數組    public static void insert(List<Integer> heap, int value) {        //在數組的尾部添加        if(heap.size()==0)          heap.add(0);//數組下標為0的位置不放元素        heap.add(value);         //開始上升操作        // heapUp2(heap, heap.size() - 1);         heapUp(heap, heap.size() - 1);      }      //上升,讓插入的數和父節點的數值比較,當大於父節點的時候就和父節點的值相交換     public static void heapUp(List<Integer> heap, int index) {          //注意由於數值是從下標為1開始,當index = 1的時候,已經是根節點了         if (index > 1) {             //求出父親的節點             int parent = index / 2;              //擷取相應位置的數值             int parentValue = (Integer) heap.get(parent);             int indexValue = (Integer) heap.get(index);             //如果父親節點比index的數值小,就交換二者的數值             if (parentValue < indexValue) {                 //交換數值                 swap(heap, parent, index);                 //遞迴調用                 heapUp(heap, parent);             }          }     } 

⑵刪除 
   操作原理是:當刪除節點的數值時,原來的位置就會出現一個孔,填充這個孔的方法就是, 
把最後的葉子的值賦給該孔並下調到合適位置,最後把該葉子刪除。 
  
中要刪除72,先用堆中最後一個元素來35替換72,再將35下沉到合適位置,最後將葉子節點刪除。 
   “結點下沉” 

 

程式: /**     * 刪除堆中位置是index處的節點     * 操作原理是:當刪除節點的數值時,原來的位置就會出現一個孔     * 填充這個孔的方法就是,把最後的葉子的值賦給該孔,最後把該葉子刪除     * @param heap      */     public static void delete(List<Integer> heap,int index) {         //把最後的一個葉子的數值賦值給index位置         heap.set(index, heap.get(heap.size() - 1));         //下沉操作         //heapDown2(heap, index);         heapDown(heap, index);         //把最後一個位置的數字刪除         heap.remove(heap.size() - 1);     }     /**     * 遞迴實現     * 刪除堆中一個資料的時候,根據堆的性質,應該把相應的位置下移,才能保持住堆性質不變     * @param heap 保持堆元素的數組     * @param index 被刪除的那個節點的位置     */     public static void heapDown(List<Integer> heap, int index) {         //因為第一個位置儲存的是空值,不在考慮之內         int n = heap.size() - 2;          //記錄最大的那個兒子節點的位置         int child = -1;          //2*index>n說明該節點沒有左右兒子節點了,那麼就返回         if (2 * index > n) {             return;         } //如果左右兒子都存在         else if (2 * index < n) {              //定義左兒子節點             child = 2 * index;             //如果左兒子小於右兒子的數值,取右兒子的下標             if ((Integer) heap.get(child) < (Integer) heap.get(child + 1)) {                 child++;             }          }//如果只有一個兒子(左兒子節點)         else if (2 * index == n) {             child = 2 * index;         }          if ((Integer) heap.get(child) > (Integer) heap.get(index)) {             //交換堆中的child,和index位置的值             swap(heap, child, index);              //完成交換後遞迴調用,繼續下降             heapDown(heap, child);         }     }  

⑶初始化 
方法1:插入法: 
  從空堆開始,依次插入每一個結點,直到所有的結點全部插入到堆為止。 
  時間:O(n*log(n)) 
  方法2:調整法: 
    序列對應一個完全二叉樹;從最後一個分支結點(n div 2)開始,到根(1)為止,依次對每個分支結點進行調整(下沉),
以便形成以每個分支結點為根的堆,當最後對樹根結點進行調整後,整個樹就變成了一個堆。 
  時間:O(n) 
對的序列,要使其成為堆,我們從最後一個分支結點(10/2),其值為72開始,依次對每個分支節點53,18,36 45進行調整(下沉). 
 
 
 

程式:     /*根據樹的性質建堆,樹節點前一半一定是分支節點,即有孩子的,所以我們從這裡開始調整出初始堆*/       public static void adjust(List<Integer> heap){        for (int i = heap.size() / 2; i > 0; i--)              adjust(heap,i, heap.size()-1);                    System.out.println("=================================================");        System.out.println("調整後的初始堆:");          print(heap);      }    /**      * 調整堆,使其滿足堆得定義      * @param i      * @param n      */      public static void adjust(List<Integer> heap,int i, int n) {                 int child;          for (; i <= n / 2; ) {              child = i * 2;              if(child+1<=n&&heap.get(child)<heap.get(child+1))                  child+=1;/*使child指向值較大的孩子*/              if(heap.get(i)< heap.get(child)){                  swap(heap,i, child);                  /*交換後,以child為根的子樹不一定滿足堆定義,所以從child處開始調整*/                  i = child;                             }  else break;        }      }  

(4)最大堆排序  

 //對一個最大堆heap排序    public static void heapSort(List<Integer> heap) {                 for (int i = heap.size()-1; i > 0; i--) {           /*把根節點跟最後一個元素交換位置,調整剩下的n-1個節點,即可排好序*/              swap(heap,1, i);              adjust(heap,1, i - 1);          }      }  

(5)完整的代碼 

import java.util.*;  /** *實現的最大堆的插入和刪除操作 * @author Arthur */ public class Heap {      /**     * 刪除堆中位置是index處的值     * 操作原理是:當刪除節點的數值時,原來的位置就會出現一個孔     * 填充這個孔的方法就是,把最後的葉子的值賦給該孔,最後把該葉子刪除     * @param heap 一個最大堆     */     public static void delete(List<Integer> heap,int index) {         //把最後的一個葉子的數值賦值給index位置         heap.set(index, heap.get(heap.size() - 1));         //下沉操作         //heapDown2(heap, index);         heapDown(heap, index); //節點下沉        //把最後一個位置的數字刪除         heap.remove(heap.size() - 1);     }       /**      * 節點下沉遞迴實現     * 刪除一個堆中一個資料的時候,根據堆的性質,應該把相應的位置下移,才能保持住堆性質不變     * @param heap 保持最大堆元素的數組     * @param index 被刪除的那個節點的位置     */     public static void heapDown(List<Integer> heap, int index) {         //因為第一個位置儲存的是空值,不在考慮之內         int n = heap.size() - 2;          //記錄最大的那個兒子節點的位置         int child = -1;          //2*index>n說明該節點沒有左右兒子節點了,那麼就返回         if (2 * index > n) {             return;         } //如果左右兒子都存在         else if (2 * index < n) {              //定義左兒子節點             child = 2 * index;             //如果左兒子小於右兒子的數值,取右兒子的下標             if ((Integer) heap.get(child) < (Integer) heap.get(child + 1)) {                 child++;             }          }//如果只有一個兒子(左兒子節點)         else if (2 * index == n) {             child = 2 * index;         }          if ((Integer) heap.get(child) > (Integer) heap.get(index)) {             //交換堆中的child,和index位置的值             swap(heap, child, index);              //完成交換後遞迴調用,繼續下降             heapDown(heap, child);         }     }      //非遞迴實現     public static void heapDown2(List<Integer> heap, int index) {         int child = 0;//儲存左兒子的位置          int temp = (Integer) heap.get(index);         int n = heap.size() - 2;         //如果有兒子的話         for (; 2 * index <= n; index = child) {             //擷取左兒子的位置             child = 2 * index;             //如果只有左兒子             if (child == n) {                 child = 2 * index;             } //如果右兒子比左兒子的數值大             else if ((Integer) heap.get(child) < (Integer) heap.get(child + 1)) {                 child++;             }              //如果數值最大的兒子比temp的值大             if ((Integer) heap.get(child) >temp) {                 //交換堆中的child,和index位置的值                 swap(heap, child, index);             } else {                 break;             }         }     }           //列印鏈表     public static void print(List<Integer> list) {         for (int i = 1; i < list.size(); i++) {             System.out.print(list.get(i) + " ");         }         System.out.println();    }      //把堆中的a,b位置的值互換     public static void swap(List<Integer> heap, int a, int b) {         //臨時儲存child位置的值         int temp = (Integer) heap.get(a);          //把index的值賦給child的位置         heap.set(a, heap.get(b));          //把原來的child位置的數值賦值給index位置         heap.set(b, temp);     }      //向最大堆中插入元素     public static void insert(List<Integer> heap, int value) {            //在數組的尾部添加要插入的元素        if(heap.size()==0)          heap.add(0);//數組下標為0的位置不放元素        heap.add(value);         //開始上升操作        // heapUp2(heap, heap.size() - 1);         heapUp(heap, heap.size() - 1);      }      //節點上浮,讓插入的數和父節點的數值比較,當大於父節點的時候就和節點的值相交換     public static void heapUp(List<Integer> heap, int index) {          //注意由於數值是從小標為一開始,當index = 1的時候,已經是根節點了         if (index > 1) {             //儲存父親的節點             int parent = index / 2;              //擷取相應位置的數值             int parentValue = (Integer) heap.get(parent);             int indexValue = (Integer) heap.get(index);             //如果父親節點比index的數值小,就交換二者的數值             if (parentValue < indexValue) {                 //交換數值                 swap(heap, parent, index);                 //遞迴調用                 heapUp(heap, parent);             }          }     }      //非遞迴實現     public static void heapUp2(List<Integer> heap, int index) {         int parent = 0;         for (; index > 1; index /= 2) {             //擷取index的父節點的下標             parent = index / 2;              //獲得父節點的值             int parentValue = (Integer) heap.get(parent);             //獲得index位置的值             int indexValue = (Integer) heap.get(index);                          //如果小於就交換             if (parentValue < indexValue) {                 swap(heap, parent, index);             }         }     }      /*根據樹的性質建堆,樹節點前一半一定是分支節點,即有孩子的,所以我們從這裡開始調整出初始堆*/       public static void adjust(List<Integer> heap){        for (int i = heap.size() / 2; i > 0; i--)              adjust(heap,i, heap.size()-1);                    System.out.println("=================================================");        System.out.println("調整後的初始堆:");          print(heap);      }    /**      * 調整堆,使其滿足堆得定義      * @param i      * @param n      */      public static void adjust(List<Integer> heap,int i, int n) {                 int child;          for (; i <= n / 2; ) {              child = i * 2;              if(child+1<=n&&heap.get(child)<heap.get(child+1))                  child+=1;/*使child指向值較大的孩子*/              if(heap.get(i)< heap.get(child)){                  swap(heap,i, child);                  /*交換後,以child為根的子樹不一定滿足堆定義,所以從child處開始調整*/                  i = child;                             }  else break;        }      }       //對一個最大堆heap排序    public static void heapSort(List<Integer> heap) {                 for (int i = heap.size()-1; i > 0; i--) {          /*把根節點跟最後一個元素交換位置,調整剩下的n-1個節點,即可排好序*/              swap(heap,1, i);              adjust(heap,1, i - 1);          }      }     public static void main(String args[]) {         List<Integer> array = new ArrayList<Integer>(Arrays.asList(null, 1, 2, 5, 10, 3, 7, 11, 15, 17, 20, 9, 15, 8, 16));        adjust(array);//調整使array成為最大堆               delete(array,8);//堆中刪除下標是8的元素        System.out.println("刪除後");        print(array);        insert(array, 99);//堆中插入        print(array);         heapSort(array);//排序        System.out.println("將堆排序後:");        print(array);        System.out.println("-------------------------");        List<Integer> array1=new ArrayList<Integer>();        insert(array1,0);        insert(array1, 1);insert(array1, 2);insert(array1, 5);        insert(array1, 10);insert(array1, 3);insert(array1, 7);        insert(array1, 11);insert(array1, 15); insert(array1, 17);        insert(array1, 20);insert(array1, 9);        insert(array1, 15);insert(array1, 8);insert(array1, 16);        print(array1);                System.out.println("==============================");        array=new ArrayList<Integer>(Arrays.asList(null,45,36,18,53,72,30,48,93,15,35));        adjust(array);          insert(array, 80);//堆中插入          print(array);         delete(array,2);//堆中刪除80的元素         print(array);         delete(array,2);//堆中刪除72的元素         print(array);                  } } 

程式運行: 
D:\java>java   Heap 
================================================= 
調整後的初始堆: 
20 17 16 15 9 15 11 1 10 3 2 7 8 5 
刪除後 
20 17 16 15 9 15 11 5 10 3 2 7 8 
99 17 20 15 9 15 16 5 10 3 2 7 8 11 
將堆排序後: 
2 3 5 7 8 9 10 11 15 15 16 17 20 99 
------------------------- 
20 17 16 10 15 9 15 0 5 2 11 1 7 3 8 
============================== 
================================================= 
調整後的初始堆: 
93 72 48 53 45 30 18 36 15 35 
93 80 48 53 72 30 18 36 15 35 45 
93 72 48 53 45 30 18 36 15 35 
93 53 48 36 45 30 18 35 15 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.