秋招各大廠,必考的優先隊列和堆排序


  秋招過了,春招還會遠么?真實面試題:工作一年同事跳槽,去某為,就考了一道:用數組實現堆排序,下面就來介紹一下堆排序的實現

 堆和優先隊列

  堆的定義 

n個元素的序列k={k0,k1,……,kn-1},當且僅滿足條件

(1)ki >= k2i+1 和 ki >= k2i+2      (2)ki <= k2i+1 和 ki <= k2i+2

(1)稱為大根堆 (2)稱為小根堆

可以把堆看成完全二叉樹。

  優先隊列

優先隊列是一種常見的抽象數據類型,它與"隊列"不同,不遵循"先進先出"原則,而遵循"最大元素先出"原則,出隊和優先級有關。 

優先隊列的基本操作有三個:

(1) 向優先隊列里插入一個元素

(2) 在優先隊列找出最大元素

(3) 刪除優先隊列中最大元素

可以用堆來實現優先隊列

  二叉堆  

  最大堆定義

  堆中任一節點總是大於其父節點的值,堆總是一顆完全二叉樹,本篇博客以實現最大堆為主

  數組實現二叉堆

  用二叉樹來實現堆,是比較好的,也可以用二叉樹的左右指針來實現,但這種太麻煩;因為是完全二叉樹,所以也可以用數組來實現二叉堆,見下圖:  PS:依舊是全博客園最丑圖

  

   

    

   說明:將數組的值來抽象成二叉樹,二叉樹圖上的紅色字就表示數組的下標,而且還能總結出藍色字體的規律。

  構建一個堆類,代碼如下:

  

template<typename Item>
class MaxHeap{
private:
    Item *data;    //數組
    int count;    //堆的大小
    int capacity;    //堆的容量
public:
    //構造函數 構建一個空堆
    MaxHeap(int capacity){

        data = new Item[capacity+1];
        count = 0;
        this->capacity = capacity;
    }
};

  

  最大堆的實現

  最大堆上插入元素 shift up

  直接上圖,再說明,如下圖:

  

  代碼如下:

  

 void shiftUp(int k){
        while(k>1 && data[k/2]<data[k]){
            swap(data[k/2],data[k]);
            k /= 2;
        }
    }

//向最大堆中插入元素
    void insert(Item item){
        assert(count+1<=capacity);
        data[count+1] = item;
        shiftUp(count+1);
        count++;
    }

  

  最大堆上取出元素 shift down

  直接上圖,如下:

  代碼如下:

  

void shiftDown(int k){
        while(2*k<=count){
            int j = 2*k;
            if(j+1<count && data[j+1]>data[j]) j++; //右孩子比左孩子,j移動
            if(data[k]>data[j]) break;
            swap(data[k],data[j]);
            k=j;
        }
    }

//從最大堆中取出堆頂元素
    Item extracMax(){
        assert(count > 0);
        Item ret = data[1];
        swap(data[1],data[count]);
        count--;
        shiftDown(1);
        return ret;
    }

  實現堆排序

  有了插入和取出,就可以實現堆排序了,代碼如下:

  

//將所有元素插入堆,再取出
template<typename T>
void heapSort1(T arr[],int n){

    MaxHeap<T> maxheap = MaxHeap<T>(n); 
    for(int i=0;i<n;i++)
        maxheap.insert(arr[i]);

    //從小到大排序
    for(int i=n-1;i>=0;i--)
        arr[i] = maxheap.extracMax();

}

  運行結果如下:

  

  

  heapify實現最大堆

  構造函數實現heapify過程  

就是用構造函數來實現最大堆,見下圖:

 

  

 

  代碼如下:

//構造函數,通過給定數組實現最大堆 O(n)
    MaxHeap(Item arr[],int n){
        
        data = new Item[n+1];
        capacity = n;

        for(int i=0;i<n;i++)
            data[i+1] = arr[i];
        count = n;

        for(int i=count/2;i>=1;i--)
            shiftDown(i);
    }

// heapSort2, 借助我們的heapify過程創建堆
// 此時, 創建堆的過程時間復雜度為O(n), 將所有元素依次從堆中取出來, 實踐復雜度為O(nlogn)
// 堆排序的總體時間復雜度依然是O(nlogn), 但是比上述heapSort1性能更優, 因為創建堆的性能更優
template<typename T>
void heapSort2(T arr[], int n){

    MaxHeap<T> maxheap = MaxHeap<T>(arr,n);
    for( int i = n-1 ; i >= 0 ; i-- )
        arr[i] = maxheap.extracMax();

}

  

  總體代碼

  Heap.h頭文件,代碼如下:

  

#ifndef HEAP_H_
#define HEAP_H_

#include<algorithm>
#include<cassert>
using namespace std;

template<typename Item>
class MaxHeap{
private:
    Item *data;    //數組
    int count;    //堆的大小
    int capacity;    //堆的容量

    void shiftUp(int k){
        while(k>1 && data[k/2]<data[k]){
            swap(data[k/2],data[k]);
            k /= 2;
        }
    }

    void shiftDown(int k){
        while(2*k<=count){
            int j = 2*k;
            if(j+1<count && data[j+1]>data[j]) j++; //右孩子比左孩子,j移動
            if(data[k]>data[j]) break;
            swap(data[k],data[j]);
            k=j;
        }
    }
public:
    //構造函數 構建一個空堆
    MaxHeap(int capacity){

        data = new Item[capacity+1];
        count = 0;
        this->capacity = capacity;
    }

    //構造函數,通過給定數組實現最大堆 O(n)
    MaxHeap(Item arr[],int n){
        
        data = new Item[n+1];
        capacity = n;

        for(int i=0;i<n;i++)
            data[i+1] = arr[i];
        count = n;

        for(int i=count/2;i>=1;i--)
            shiftDown(i);
    }

    ~MaxHeap(){
        delete[] data;
    }

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

    //判斷是否為空
    bool isEmpty(){
        return count==0;
    }

    //向最大堆中插入元素
    void insert(Item item){
        assert(count+1<=capacity);
        data[count+1] = item;
        shiftUp(count+1);
        count++;
    }

    //從最大堆中取出堆頂元素
    Item extracMax(){
        assert(count > 0);
        Item ret = data[1];
        swap(data[1],data[count]);
        count--;
        shiftDown(1);
        return ret;
    }

};

#endif
View Code

  heap.cpp如下:

  

#include<iostream>
#include<algorithm>
#include"Heap.h"
#include "SortTestHelper.h"

//將所有元素插入堆,再取出
template<typename T>
void heapSort1(T arr[],int n){

    MaxHeap<T> maxheap = MaxHeap<T>(n); 
    for(int i=0;i<n;i++)
        maxheap.insert(arr[i]);

    //從小到大排序
    for(int i=n-1;i>=0;i--)
        arr[i] = maxheap.extracMax();

}


// heapSort2, 借助我們的heapify過程創建堆
// 此時, 創建堆的過程時間復雜度為O(n), 將所有元素依次從堆中取出來, 實踐復雜度為O(nlogn)
// 堆排序的總體時間復雜度依然是O(nlogn), 但是比上述heapSort1性能更優, 因為創建堆的性能更優
template<typename T>
void heapSort2(T arr[], int n){

    MaxHeap<T> maxheap = MaxHeap<T>(arr,n);
    for( int i = n-1 ; i >= 0 ; i-- )
        arr[i] = maxheap.extracMax();

}

int main(){
    
    int n = 10;
    
    
    // 測試1 一般性測試
    //cout<<"Test for random array, size = "<<n<<", random range [0, "<<n<<"]"<<endl;
    int* arr1 = SortTestHelper::generateRandomArray(n,0,n);
    int* arr2 = SortTestHelper::copyIntArray(arr1, n);
        
    SortTestHelper::testSort("Heap Sort 1", heapSort1, arr1, n);
    SortTestHelper::testSort("Heap Sort 2", heapSort2, arr2, n);
    
    for(int i=0;i<n;i++)
        cout << arr1[i] << " ";
    cout << endl;
    
    for(int i=0;i<n;i++)
        cout << arr2[i] << " ";
    cout << endl;

    delete[] arr1;
    delete[] arr2;
    cout <<endl;
}
View Code

  

  十一月總結

  正如前面所說:秋招已過,春招還會遠么?18年還有一個月就要結束了,感覺比大學時過的時間還快!有個戰略要改變,之前定的年底之前一直要學數據結構和算法,這個計划要伴隨整個職業生涯了,最起碼一直到明年5月,都要一直堅持學!

  11月主要完成的

  •   學了python
  •         數據結構和算法完成了整體架構的學習和整理
  •         復習C++和操作系統

  12月計划

  •   最主要的計划就是有效深入的學習數據結構和算法
  •        鞏固C++、操作系統和網絡編程

  

  2018年最后一個月了,大家加油!

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM