1. 程式人生 > >堆實現及常見面試題

堆實現及常見面試題

一、堆的概念:
關鍵碼的集合按完全二叉樹的順序儲存方式儲存在一維陣列中,並滿足:對於所有節點它的父節點關鍵碼都大於子節點(或都小於它的子節點),這樣的堆稱為最大堆(或最小堆)。
二、堆相關的問題
1.簡單實現一個堆 堆的建立、放入資料、刪除資料、判斷是否為堆
2.TopK問題,求陣列中最大的K個元素
3.堆排序(實質是一個選擇排序),一個數組的升序、降序 
三、下面例子中,博主是以建最大堆的方式實現的堆,在實現TopK中需要建小堆,博主又另寫了一個建小堆的函式。
***Autually***, 這是一種較為笨的方法,可以實現一個仿函式,增加函式的複用性,這樣建小堆建大堆就不用建不同的堆了。
程式碼如下:
#pragma once

#include <iostream>
#include <vector>
#define N 10000
using namespace std;

template<class T>
class Heap
{
public:
    Heap()
    {}

    Heap(T* a, size_t n)
    {
        _a.reserve(n);
        for (size_t i = 0; i < n; i++)
        {
            _a.push_back(a[i]);
        }
        //建堆
for (int i = (_a.size() - 2) / 2; i >= 0; --i)//從倒數第一個非葉節點開始 { AdjustDown(i); } } HeapT(T* a, size_t k)//實現TopK建堆方式 { _a.reserve(k); for (size_t i = 0; i < k; i++) { _a.push_back(a[i]); } for (int
i = (_a.size() - 2) / 2; i >= 0; --i)//建小堆 { AdjustDownL(i); } } void TopK(T* a) { for (size_t i = 100; i < N;) { if (_a[0] < a[i]) { _a[0] = a[i]; AdjustDownL(0); ++i; } else { ++i; } } } void HeapRiseSort(T* a, size_t k)//升序建大堆 降序降序建小堆 { _a.reserve(k); int end = k - 1; Heap(a, k);//建堆 while (end > 0) { swap(_a[0], _a[end]); end--; AdjustDown(0); //堆排序(升序),每次把堆頂資料放到陣列後面,再利用前end個元素重新建大堆 } } void Print() { for (size_t i = 0; i < _a.size(); i++) { cout << _a[i] << " "; } cout << endl; } void Push(const T& x) { _a.push_back(x); AdjustUp(_a.size()-1); } void AdjustUp(int child) { int parent = (child - 1) >> 1; while (child != 0) { if (_a[child] > _a[parent]) { swap(_a[child], _a[parent]); child = parent; parent = (child - 1) >> 1; } else { break; } } } void Pop() { swap(_a[0], _a[_a.size() - 1]); _a.pop_back(); AdjustDown(0); } void AdjustDown(int root) { int parent = root; int child = parent * 2 + 1; while (child < _a.size()) { if (child + 1 < _a.size() && _a[child + 1] > _a[child])//選出大的孩子(存在右孩子) { ++child; } if (_a[child] > _a[parent]) { swap(_a[child], _a[parent]); parent = child; child = child * 2 + 1; } else { break; } } } void AdjustDownL(int root) { int parent = root; int child = parent * 2 + 1; while (child < _a.size()) { if (child + 1 < _a.size() && _a[child + 1] < _a[child])//選出大的孩子(存在右孩子) { ++child; } if (_a[child] < _a[parent])//建最小堆 { swap(_a[child], _a[parent]); parent = child; child = child * 2 + 1; } else { break; } } } int IsBHeap() { for (int i = _a.size() - 1; i >= 0; i++) { return _IsBHeap(i)==true;//為真返回1,假0 } } protected: bool _IsBHeap(int root) { int child = root; int parent = (child - 1) >> 1; while (child<_a.size()) { int large = child; if (child + 1<_a.size() && _a[child + 1]>_a[child]) { large = child + 1; } if (_a[large] < _a[parent]) { return true; } else { return false; } } return false; } vector<T> _a; };

測試用例:

#pragma once

#include "Heap.h"


void Heaptest()
{
    int arr[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };
    Heap<int>h1(arr, sizeof(arr)/sizeof(arr[0]));
    h1.Print();
    h1.Pop();
    h1.Print();
    h1.Push(20);
    h1.Print();

    h1.Push(2);
    cout << h1.IsBHeap() << endl;
}

void topKTest()
{
    int arr[N] = { 0 };
    for (size_t i = 0; i < N; i++)
    {
        arr[i] = rand() % N;
    }
    arr[100] = 12000;
    arr[999] = 11145;
    arr[888] = 10008;
    arr[118] = 19999;
    arr[365] = 19880;
    arr[254] = 13444;
    arr[4579] = 14555;
    arr[1000] = 47839;
    arr[9992] = 19875;
    arr[1723] = 98345;
    HeapT<int>h1(arr, 10);
    h1.TopK(arr);
    h1.Print();
}

//堆排序實質上是一種選擇排序,選出最大的與尾部資料交換,然後堆元素個數--,重新建堆,相當於沒把剛剛選出的最大的數算在重新建的堆中
void Heaptest()
{
    int arr[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };
    Heap<int>h1(arr, 10);
    h1.HeapRiseSort(arr,10);
    h1.Print();
}

堆就講到這裡啦,之後會給大家補充仿函式和堆的相關知識,拜拜~