堆和優先隊列

堆(heap)

  • 堆是計算機科學中一類特殊的數據結構統稱,堆通常是一個可以被看做一棵樹的數組對象

  • 特點:

    1. 堆中的某個節點總是不大於或不小於其父節點的值
    2. 堆總是一顆完全二叉樹
  • 將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆成爲最小堆或者小根堆

  • 二叉堆是一顆完全二叉樹

  • 完全二叉樹:除了最後一層外,其他各層的節點數都達到了最大個數,第h層的所有結點都連續集中在最左邊,這就是完全二叉樹

  • 堆:就是以樹的結構進行表達
    在這裏插入圖片描述

  • 不過,我認爲堆需要注意的是它的結構,如果要向堆中添加一個元素,那麼我們需要的操作是,首先將這個元素插入堆的底部,然後我們還要進行解決的是與父母節點的交換問題,最大堆和最小堆的結構需要進行解釋

     //向堆中添加元素
        public void add(E e){
            data.addLast(e);
            siftUp(data.getSize()-1);
        }
        private void siftUp(int k){
            while (k>0&&data.get(parent(k)).compareTo(data.get(k))<0){
                data.swap(k,parent(k));
                k=parent(k);
            }
        }
    
  • 向堆中取出最大元素

      //取出堆中最大的元素
        public E extractMax(){
            E ret = findMax();
            data.swap(0,data.getSize()-1);
            data.removeLast();
            siftDown(0);
            return ret;
        }
        private void siftDown(int k){
            while (leftChild(k)<data.getSize())//這裏我們畫圖就可以理解,如果左孩子大於整個數組的長度的話,
                //我們可以理解爲左孩子沒有
            {
                int j = leftChild(k);
                if (j+1<data.getSize()&&data.get(j+1).compareTo(data.get(j))>0)
                    j = rightChild(k);//data[j]必須是左孩子和右孩子中的最大值
                if(data.get(k).compareTo(data.get(j))>=0)
                    break;
                data.swap(k,j);
                k=j;
            }
        }
    
  • replace方法:取出最大元素後,放入一個新的元素

    1. 可以先extractMax,再add,兩次O(logn)的操作

    2. 可以直接將堆頂元素替換以後下沉,一次O(logn)的操作

          //取出堆中最大的元素,並且替換成元素e
          public E replace(E e)
          {
              E ret = findMax();
              data.set(0,e);
              siftDown(0);
              return ret;
          }
      
  • heapify:將任意數組整理成堆的形狀

    1. 我們可以直接遍歷一個數組,然後將數組中的元素直接添加進一個新創建的就行

    2. 更快的方法,找到最後一個非葉子節點,不斷進行下沉操作就行

          //將任意數組整理成堆
          public Maxheap(E[] arr){
              data = new Array<>(arr);
              for (int i=parent(arr.length-1);i>=0;i--)
                  siftDown(i);
          }
      
  • 完整代碼:

    package 數據結構.MaxHeap;
    
    import 數據結構.棧.Array;
    
    public class Maxheap<E extends Comparable<E>> {
        private Array<E> data;
        public Maxheap(int capacity){
            data = new Array<>(capacity);
        }
        public Maxheap(){
            data = new Array<>();
        }
        //返回堆中所有元素個數
        public int size(){
            return data.getSize();
        }
        //確定堆是否爲空
        public boolean isEmpty(){
            return data.isEmpty();
        }
        //返回完全二叉樹的數組表示中,一個索引所表示元素的父親節點的索引
        private int parent(int index){
            if(index==0)
                throw new IllegalArgumentException("index-0 doesn't hava parent");
            return (index-1)/2;
        }
        //返回完全二叉樹的數組表示中,一個索引所表示的元素的左孩子節點的索引
        private int leftChild(int index){
            return index*2 + 1;
        }
        //返回完全二叉樹的數組表示中,一個索引所表示的元素的右孩子節點的索引
        private int rightChild(int index){
            return index*2+2;
        }
        //向堆中添加元素
        public void add(E e){
            data.addLast(e);
            siftUp(data.getSize()-1);
        }
        private void siftUp(int k){
            while (k>0&&data.get(parent(k)).compareTo(data.get(k))<0){
                data.swap(k,parent(k));
                k=parent(k);
            }
        }
        //查找堆中最大元素
        public E findMax(){
            if(data.getSize()==0)
                throw new IllegalArgumentException("Can not findMax when heap is,,,,");
            return data.get(0);
        }
        //取出堆中最大的元素
        public E extractMax(){
            E ret = findMax();
            data.swap(0,data.getSize()-1);
            data.removeLast();
            siftDown(0);
            return ret;
        }
        private void siftDown(int k){
            while (leftChild(k)<data.getSize())//這裏我們畫圖就可以理解,如果左孩子大於整個數組的長度的話,
                //我們可以理解爲左孩子沒有
            {
                int j = leftChild(k);
                if (j+1<data.getSize()&&data.get(j+1).compareTo(data.get(j))>0)
                    j = rightChild(k);//data[j]必須是左孩子和右孩子中的最大值
                if(data.get(k).compareTo(data.get(j))>=0)
                    break;
                data.swap(k,j);
                k=j;
            }
        }
        //取出堆中最大的元素,並且替換成元素e
        public E replace(E e)
        {
            E ret = findMax();
            data.set(0,e);
            siftDown(0);
            return ret;
        }
        //將任意數組整理成堆
        public Maxheap(E[] arr){
            data = new Array<>(arr);
            for (int i=parent(arr.length-1);i>=0;i--)
                siftDown(i);
        }
    
    }
    
    

優先隊列

  • 普通的隊列是一種先進先出的數據結構,元素在隊列尾追加,而從隊列頭刪除。在優先隊列中,元素被賦予優先級。當訪問元素時,具有最高優先級的元素最先刪除。優先隊列具有最高級先出 (first in, largest out)的行爲特徵。通常採用堆數據結構來實現

  • 可以使用最大堆來進行測試

    package 數據結構.MaxHeap;
    
    public interface Queue<E> {
        int getSize();
        boolean isEmpty();
        void  enqueue(E e);
        E dequeue();
        E getFront();
    
    }
    package 數據結構.MaxHeap;
    
    public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {
        private Maxheap<E> maxheap;
        public PriorityQueue(){
            maxheap = new Maxheap<>();
        }
    
        @Override
        public int getSize() {
            return maxheap.size();
        }
    
        @Override
        public boolean isEmpty() {
            return maxheap.isEmpty();
        }
    
        @Override
        public E getFront() {
            return maxheap.findMax();
        }
    
        @Override
        public void enqueue(E e) {
            maxheap.add(e);
        }
    
        @Override
        public E dequeue() {
            return  maxheap.extractMax();
        }
    }
    
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章