1. 程式人生 > 實用技巧 >python7段數碼管繪製(顯示當前系統時間到小時)

python7段數碼管繪製(顯示當前系統時間到小時)

(二叉)堆是一種用陣列表示的完全二叉樹,並且任意節點滿足\(A[PARENT(i)]\geq A[i]\)的大小關係(最大堆):

完全二叉樹:對於高度為h的二叉樹,除了h層以外,其餘0到h-1層的節點都是滿的,且h層的節點都靠左邊。

完全二叉樹高度:對於n個節點的完全二叉樹,高度為\(\lfloor \log n\rfloor\)
證明:
對於擁有\(2^h \leq n \leq 2^{(h+1)}-1\)個節點的完全二叉樹的高度都是h
所以根據\(h \leq \log n < (h+1)\)推出,n個節點的完全二叉樹高度為\(\lfloor \log n\rfloor\)

建堆(makeHeap)

建堆的過程就是將初始化輸入的陣列重新排列,使其滿足\(A[PARENT(i)]\geq A[i]\)的大小關係。
假設對於元素\(A[i]\),其左右子節點\(A[LEFT(i)]\)\(A[RIGHT(i)]\)作為根的二叉樹已經滿足堆的順序性質了,那麼這時只要將元素\(A[i]\)不斷和左右子節點進行比較,並將\(A[i]\)和最大的子節點交換順序,直到\(A[i]\)就是最大的節點,或者\(A[i]\)變為了葉子節點。最終就得到了更大的堆,不斷進行這個過程,將子堆兩兩合併,最終整個陣列就變成了一個完整的堆了。
這裡有個很巧妙的計算過程,那就是從\(A[PARENT(heapSize)]\)

節點遞減遍歷到節點\(A[1]\),對每個節點執行上述的步驟,便完成了建堆操作:

  • 程式碼實現(C++):
#include <utility>

#define PARENT(i) (i / 2)
#define LEFT(i) (i * 2)
#define RIGHT(i) (i * 2 + 1)

template<typename RandomAccessIterator, typename SizeType, typename Compare>
void heapify(RandomAccessIterator first, SizeType i, SizeType size, Compare comp) {
    auto val = std::move(*(first + i - 1));
    for (auto child = RIGHT(i); child <= size; i = child, child = RIGHT(i)) {
        if (comp(*(first + child -1 - 1), *(first + child - 1))) {
            --child;
        }
        if (comp(val, *(first + child - 1))) {
            *(first + i - 1) = std::move(val);
            return;
        }
        *(first + i - 1) = std::move(*(first + child - 1));
    }
    auto leftChild = LEFT(i);
    if (leftChild <= size && !comp(val, *(first + leftChild - 1))) {
        *(first + i - 1) = std::move(*(first + leftChild - 1));
        i = leftChild;
    }
    *(first + i - 1) = std::move(val);
}

template<typename RandomAccessIterator, typename Compare>
void makeHeap(RandomAccessIterator first, RandomAccessIterator last, Compare comp) {
    auto size = last - first;
    for (auto i = PARENT(size); i > 0; --i) {
        heapify(first, i, size, comp);
    }
}

#undef PARENT
#undef LEFT
#undef RIGHT
  • 演算法複雜度
    • 最好情況:當輸入陣列本身就滿足堆的順序性質,那麼遍歷的時候什麼也不用做,總共遍歷了\(\frac{n}{2}\)個元素,時間複雜度為\(\Theta(n)\)
    • 最壞情況:當輸入的陣列逆序,每個元素都需要下降到葉子節點,時間複雜度為\(\Theta(n)\)

最壞情況計算過程:
對於包含\(2^h \leq n \leq 2^{(h+1)}-1\)個元素的堆,\(h-1\)層最多包含\(2^{(h-1)}\)個元素,每個元素最多比較2次,\(h-2\)層最多包含\(2^{h-2}\)個元素,每個元素最多比較4次,以此類推得到總的比較次數:

\[\sum_{i=1}^{h}{2(h-i+1)2^{i-1}} \]

由於等差乘等比數列公式\(\sum_{i=1}^{n}{(an+b)q^{n-1}}=(An+B)q^n-B\),其中\(A=\frac{a}{q-1}\)\(B=\frac{b-A}{q-1}\),所以上式中\(A=\frac{-2}{2-1}=-2\)\(B=\frac{2h+2+2}{2-1}=2h+4\)

\[\sum_{i=1}^{h}{2(h-i+1)2^{i-1}}=(-2h+2h+4)2^{h}-2h-4 \]

\[\begin{align} \sum_{i=1}^{h}{2(h-i+1)2^{i-1}} &= (-2h+2h+4)2^{h}-2h-4\\ &= 4\cdot 2^{h}-2h-4\\ &< 4n \end{align}\]