堆的插入刪除實現和優化

堆的插入的時候實在樹的最下面插入。例如

           97          
      /          \     
     95          69    
   /    \      /    \  
  65    67    26    26 
 / \   / \   / \   / \ 
10 65 67               

在該樹中,插入一個新的數據的時候,是要在第四層67的右邊插入假如插入的數據是96.那麼此時不滿足最大堆(最小堆)那麼96比67大,所以96和67交換位置。96也比95大,所以96再次和95交換。此時96不必它的父節點97大,所以當前就是它應該存在的位置,最後得到新樹

         97          
      /          \     
     96          69    
   /    \      /    \  
  65    95    26    26 
 / \   / \   / \   / \ 
10 65 67  67   

import java.util.*;
import java.lang.*;

// 在堆的有關操作中,需要比較堆中元素的大小,所以Item需要extends Comparable
public class MaxHeap<Item extends Comparable> {

    protected Item[] data;
    protected int count;
    protected int capacity;

    // 構造函數, 構造一個空堆, 可容納capacity個元素
    public MaxHeap(int capacity){
        data = (Item[])new Comparable[capacity+1];
        count = 0;
        this.capacity = capacity;
    }

    // 返回堆中的元素個數
    public int size(){
        return count;
    }

    // 返回一個布爾值, 表示堆中是否爲空
    public boolean isEmpty(){
        return count == 0;
    }

    // 像最大堆中插入一個新的元素 item
    public void insert(Item item){

        assert count + 1 <= capacity;
        data[count+1] = item;
        count ++;
        shiftUp(count);
    }


    // 交換堆中索引爲i和j的兩個元素
    private void swap(int i, int j){
        Item t = data[i];
        data[i] = data[j];
        data[j] = t;
    }

    //********************
    //* 最大堆核心輔助函數
    //********************
    private void shiftUp(int k){

        while( k > 1 && data[k/2].compareTo(data[k]) < 0 ){
            swap(k, k/2);
            k /= 2;
        }
    }

    // 測試 MaxHeap
    public static void main(String[] args) {

        MaxHeap<Integer> maxHeap = new MaxHeap<Integer>(100);
        int N = 50; // 堆中元素個數
        int M = 100; // 堆中元素取值範圍[0, M)
        for( int i = 0 ; i < N ; i ++ )
            maxHeap.insert( new Integer((int)(Math.random() * M)) );
        System.out.println(maxHeap.size());

    }
}

輔助類,用於打印出樹的樣子
也就是這樣

The max heap size is: 31
Data in the max heap: 
98 81 97 74 78 80 53 50 63 68 61 56 71 37 47 1 45 33 56 6 62 48 17 9 51 1 31 0 2 12 44 

                       98                      
            /                      \           
           81                      97          
      /          \            /          \     
     74          78          80          53    
   /    \      /    \      /    \      /    \  
  50    63    68    61    56    71    37    47 
 / \   / \   / \   / \   / \   / \   / \   / \ 
1  45 33 56 6  62 48 17 9  51 1  31 0   2 12 44

// 我們的PrintableMaxHeap只能處理整數信息,所以繼承的是MaxHeap<Comparable<Integer>>
public class PrintableMaxHeap extends MaxHeap<Comparable<Integer>>{

    public PrintableMaxHeap(int capacity){
        super(capacity);
    }

    // 以樹狀打印整個堆結構
    public void treePrint(){

        if( size() >= 100 ){
            System.out.println("This print function can only work for less than 100 integer");
            return;
        }

        System.out.println("The max heap size is: " + size());
        System.out.println("Data in the max heap: ");
        for( int i = 1 ; i <= size() ; i ++ ){
            // 我們的print函數要求堆中的所有整數在[0, 100)的範圍內
            assert (Integer)data[i] >= 0 && (Integer)data[i] < 100;
            System.out.print(data[i] + " ");
        }
        System.out.println();
        System.out.println();

        int n = size();
        int maxLevel = 0;
        int numberPerLevel = 1;
        while( n > 0 ){
            maxLevel += 1;
            n -= numberPerLevel;
            numberPerLevel *= 2;
        }

        int maxLevelNumber = (int)Math.pow(2, maxLevel-1);
        int curTreeMaxLevelNumber = maxLevelNumber;
        int index = 1;
        for( int level = 0 ; level < maxLevel ; level ++ ){

            String line1 = new String(new char[maxLevelNumber*3-1]).replace('\0', ' ');

            int curLevelNumber = Math.min(count-(int)Math.pow(2,level)+1,(int)Math.pow(2,level));
            boolean isLeft = true;
            for( int indexCurLevel = 0 ; indexCurLevel < curLevelNumber ; index ++ , indexCurLevel ++ ){
                line1 = putNumberInLine( (Integer)data[index] , line1 , indexCurLevel , curTreeMaxLevelNumber*3-1 , isLeft );
                isLeft = !isLeft;
            }
            System.out.println(line1);

            if( level == maxLevel - 1 )
                break;

            String line2 = new String(new char[maxLevelNumber*3-1]).replace('\0', ' ');
            for( int indexCurLevel = 0 ; indexCurLevel < curLevelNumber ; indexCurLevel ++ )
                line2 = putBranchInLine( line2 , indexCurLevel , curTreeMaxLevelNumber*3-1 );
            System.out.println(line2);

            curTreeMaxLevelNumber /= 2;
        }
    }

    private String putNumberInLine( Integer num, String line, int indexCurLevel, int curTreeWidth, boolean isLeft){

        int subTreeWidth = (curTreeWidth - 1) / 2;
        int offset = indexCurLevel * (curTreeWidth+1) + subTreeWidth;
        assert offset + 1 < line.length();
        if( num >= 10 )
            line = line.substring(0, offset+0) + num.toString()
                    + line.substring(offset+2);
        else{
            if( isLeft)
                line = line.substring(0, offset+0) + num.toString()
                        + line.substring(offset+1);
            else
                line = line.substring(0, offset+1) + num.toString()
                        + line.substring(offset+2);
        }
        return line;
    }

    private String putBranchInLine( String line, int indexCurLevel, int curTreeWidth){

        int subTreeWidth = (curTreeWidth - 1) / 2;
        int subSubTreeWidth = (subTreeWidth - 1) / 2;
        int offsetLeft = indexCurLevel * (curTreeWidth+1) + subSubTreeWidth;
        assert offsetLeft + 1 < line.length();
        int offsetRight = indexCurLevel * (curTreeWidth+1) + subTreeWidth + 1 + subSubTreeWidth;
        assert offsetRight < line.length();

        line = line.substring(0, offsetLeft+1) + "/" + line.substring(offsetLeft+2);
        line = line.substring(0, offsetRight) + "\\" + line.substring(offsetRight+1);

        return line;
    }

    // 測試 PrintableMaxHeap
    public static void main(String[] args) {

        PrintableMaxHeap maxHeap = new PrintableMaxHeap(100);
        int N = 31; // 堆中元素個數
        int M = 100; // 堆中元素取值範圍[0, M)
        for( int i = 0 ; i < N ; i ++ )
            maxHeap.insert( new Integer((int)(Math.random() * M)) );
        maxHeap.treePrint();

    }
}

刪除

import java.util.*;
import java.lang.*;

// 在堆的有關操作中,需要比較堆中元素的大小,所以Item需要extends Comparable
public class MaxHeap<Item extends Comparable> {

    protected Item[] data;
    protected int count;
    protected int capacity;

    // 構造函數, 構造一個空堆, 可容納capacity個元素
    public MaxHeap(int capacity){
        data = (Item[])new Comparable[capacity+1];
        count = 0;
        this.capacity = capacity;
    }

    // 返回堆中的元素個數
    public int size(){
        return count;
    }

    // 返回一個布爾值, 表示堆中是否爲空
    public boolean isEmpty(){
        return count == 0;
    }

    // 像最大堆中插入一個新的元素 item
    public void insert(Item item){

        assert count + 1 <= capacity;
        data[count+1] = item;
        count ++;
        shiftUp(count);
    }

    // 從最大堆中取出堆頂元素, 即堆中所存儲的最大數據
    public Item extractMax(){
        assert count > 0;
        Item ret = data[1];

        swap( 1 , count );
        count --;
        shiftDown(1);

        return ret;
    }

    // 獲取最大堆中的堆頂元素
    public Item getMax(){
        assert( count > 0 );
        return data[1];
    }


    // 交換堆中索引爲i和j的兩個元素
    private void swap(int i, int j){
        Item t = data[i];
        data[i] = data[j];
        data[j] = t;
    }

    //********************
    //* 最大堆核心輔助函數
    //********************
    private void shiftUp(int k){

        while( k > 1 && data[k/2].compareTo(data[k]) < 0 ){
            swap(k, k/2);
            k /= 2;
        }
    }

    private void shiftDown(int k){

        while( 2*k <= count ){
            int j = 2*k; // 在此輪循環中,data[k]和data[j]交換位置
            if( j+1 <= count && data[j+1].compareTo(data[j]) > 0 )
                j ++;
            // data[j] 是 data[2*k]和data[2*k+1]中的最大值

            if( data[k].compareTo(data[j]) >= 0 ) break;
            swap(k, j);
            k = j;
        }
    }

    // 測試 MaxHeap
    public static void main(String[] args) {

        MaxHeap<Integer> maxHeap = new MaxHeap<Integer>(100);
        int N = 100; // 堆中元素個數
        int M = 100; // 堆中元素取值範圍[0, M)
        for( int i = 0 ; i < N ; i ++ )
            maxHeap.insert( new Integer((int)(Math.random() * M)) );

        Integer[] arr = new Integer[N];
        // 將maxheap中的數據逐漸使用extractMax取出來
        // 取出來的順序應該是按照從大到小的順序取出來的
        for( int i = 0 ; i < N ; i ++ ){
            arr[i] = maxHeap.extractMax();
            System.out.print(arr[i] + " ");
        }
        System.out.println();

        // 確保arr數組是從大到小排列的
        for( int i = 1 ; i < N ; i ++ )
            assert arr[i-1] >= arr[i];
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章