排序--最大堆構造和堆排序(單步查看過程)

這裏先簡單說下最大堆的基本性質:數組

  • 最大堆必定是徹底二叉樹
  • 當父節點爲 n 時,左孩子爲 n * 2 + 1,右孩子爲 n * 2 + 2
  • 當孩子爲 n 時,其父節點爲: (n - 1) / 2 ----> 這一點很重要,在後面初始化的時候會用到
  • 父節點大於等於左孩子和右孩子,但左孩子不必定大於右孩子

瞭解以上基本性質以後,就能夠先看一下如何對一個序列作最大堆的初始化。3d


最大堆的構造

思路:過程就像冒泡同樣,從最序號最大的父節點開始,查看是否知足最大堆,若是不知足,則調整(調整以後,還要查看被調整的節點是否依然知足最大堆性質,若是不知足,則須要往下遍歷調整,這部分在後面的舉例中會有說明),若是知足,則繼續查看前一個父節點是否知足,直接最終的0節點。code

例如:這裏有個數組:x[] = {2 5 3 9 7 1 6},則對應樹爲:
blog

該序列長度爲7,最大下標爲6,則最大的父節點下標就是: (6 - 1)/ 2 是2(基本性質第三條),對應的數值是3,
他的左孩子是1,右孩子是6,右孩子比父節點大,因此應該調整一下這兩個節點,獲得:
排序

該節點調整完以後,再查看前一個父節點,下標爲1,對應的數值爲5,
他的左孩子是9,右孩子是7,不知足,因此父節點應該與左孩子進行交換,獲得:
string

繼續往前,再前面一個父節點下標爲0,數值爲2,左孩子是9,右孩子是6,不知足最大堆,父節點與左孩子交換,獲得:
io

交換以前,左孩子爲9,如今左孩子爲2,致使這個左孩子不知足最大堆性質,由於這個左孩子的左孩子大於左孩子,因此,這裏就出現了上面括號中所說的:調整完以後,還要查看被調整的節點是否依然知足最大堆的性質。
這裏還要對調整以後的節點繼續調整:
class

至此,一個最大堆就初始化完成了!二叉樹

堆排序

其實,明白了最大堆怎麼構造出來的以後,堆排序就很容易了。
想想,最大堆構造出來以後,其實就直接獲得了最大值:x[0],
若是把 x[0] 與最後一個數字交換一下,而後對剩下的數字從新按以前的方法構造一下,不就找到了第二大的數字了嗎?
此時第二大的數字就是x[0],把它與剛剛參加排序的最後的一個數字交換一下,而後再對剩下的數字排序一下,就能夠獲得第三大的數字,
這麼一直循環,就能夠把當前數組排序完成了。
接着剛剛的那個例題,先把9與參與排序的最後一個數字對換,獲得:
循環

此時參與排序的,就只有:3,7,6,5,2,1。
由於x[0]被調整了,因此要查看x[0]是否依然最大堆性質,顯然是不知足的,因此繼續調整x[0],獲得:

x[0]與x[1]互換以後,致使被調整的x[1]又不知足最大堆,那就再調整一下:

如今整個樹都知足最大堆了,也就獲得瞭如今參與排序的最大值x[0]爲7,
因此,x[0]與當前參與排序的最後一位交換,獲得:

此時參與排序的,只有:1,5,6,3,2。
按照上面步驟再次循環,這裏就不寫了,直接放圖:

上代碼:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


void swap(int *array, int i, int j)
{
    int temp = array[i];
    array[i] = array[j];
    array[j] = temp;
    return;
}

// 對當前父節點進行排序
// 查看該父節點是否知足最大堆,若是不知足則調整子節點
void sort (int *array, int father, int len)
{
    for (int lchild =father*2+1; lchild<len; lchild=father*2+1)
    {
        int k = lchild;  // 先用k指向左孩子
        int rchild = lchild + 1;
        if ((rchild < len) && (array[rchild] > array[lchild])) 
        {
            k = rchild;   // 若是有右孩子,且右孩子比左孩子還大,則更新k
        }

        // 這裏的k,指向了左右孩子中較大的那個
        if (array[k] > array[father])
        {
            swap(array, k, father); // 交換父親和孩子的數值

            father = k;  // 這裏就是查看被調整以後的節點k,是否依然知足最大堆
        }
        else
        {
            break;  // 當前節點不須要被調整
        }
    }
    return;
}

void print(int *array, int len)
{
    for (int i=0; i<len; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");
}

int main(void)
{
    int x[] = {2,5,3,9,7,1,6};
    int len = sizeof(x)/sizeof(int);

    print(x, len); // 先輸出原始序列

    // 最大子節點下標爲len-1,因此它的父節點是 (len-1-1) / 2
    for (int i = (len - 2)/2; i>=0; i--)
    {
        sort(x, i, len);
    }

    print(x, len); // 輸出初始化以後的最大堆

    for (int i=(len-1); i>0; i--)
    {
        swap(x, 0, i);  // 把最大的一個值放到末尾,而後對剩餘的數組進行排序
        sort(x, 0, i);
    }
    print(x, len); // 輸出排序以後的序列
    return 0;
}

最終輸出爲:
2 5 3 9 7 1 6 
9 7 6 5 2 1 3 
1 2 3 5 6 7 9
相關文章
相關標籤/搜索