數據結構與算法 -- 哈夫曼樹思想與建立詳解1

 PS:什麼是哈夫曼樹?

  給定n個權值做爲n個葉子結點,構造一棵二叉樹,若該樹的帶權路徑長度達到最小,稱這樣的二叉樹爲最優二叉樹,也稱爲哈夫曼樹(Huffman Tree)。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。算法

計算規則:

  假設一組權值,一個權值是一個結點,12  34  2  5  7  ,在這其中找出兩個最小的權值,而後組成一個新的權值,再次找出最小的權值結點。如圖:編程

 

問題:

  1:若是程序找出兩個最小的權值,把兩個權值最小的相加結果再次添加到數組中呢。 

  2:完成第一步後,又怎麼讓他們成爲一個二叉樹呢。

  3:這個二叉樹的結構體怎麼定義呢,這組帶有權值的結點又以什麼方式存在呢。

思路:

  對於這種陸續找出兩個最小權值的算法能夠利用排序的方式,從小到大排序,那麼最左邊的就是最小的,這樣一來最小的權值能夠挑選出來了,接下來再利用特定的結構體(都有左孩子和右孩子還有存放權值的data域)讓每個權值結點存在一個數組中。這樣子不斷的操做數組,從數組中的5個元素到只有1個元素爲止,此時的這一個元素就是二叉樹的跟。而後再利用遍歷方式打印這個二叉樹便可。

代碼實現:

結構體定義

一個二叉樹的結構體,一個數組的結構體。能夠看出數組的結構體內部是包含一個二叉樹結點的結構體的。數組

/**
 * Created by 劉志通 on 2018/11/22.
 * @describe 哈夫曼樹的簡介
 * 編程思想:
 * 1:方式簡介:
 *      利用數組(二叉樹結構體類型),來存放初始權值(首次認爲權值就是一個樹跟,左右孩子分別是NULL),在數組初始化的以後排序,而後拿出index=0,1,更新
 *      權值根,
 * 2:所用知識:
 *      數組,鏈表存儲,二叉樹結構體。
 */
#include "stdlib.h"
#include "stdio.h"

/**
 * @describe 二叉樹結構體
 * 結構形式:Lchild data Rchild
 * */
typedef struct twoTree {
    int data;
    struct twoTree *Lchild, *Rchild;
} twoTree, *twoTreeL;
typedef struct {
    twoTreeL data[100];
    int length;
} arrayMy;

 初始化數組

arrayMy InitList(arrayMy &L) {
    // 初始化表,一個空表。
    L.length = 0;
    return L;
}
/**
 * 冒泡排序
 * */
 void swap(int *weigth,int n){
    for (int i = 0; i < n; i++){
        for (int j = 0; j+1<n-i; j++){
            if (weigth[j] > weigth[j+1]){
                int temp;
                temp = weigth[j];
                weigth[j] = weigth[j+1];
                weigth[j+1] = temp;
            }
        }
    }
 }

 數組的特定操做

對於初始化數組能夠看出是把每個元素寫成了二叉樹結構的一個結點添加到了一維數組中,初始狀態左右孩子都是NULL,data是權值,還有兩個方法是插入和刪除,之因此有這兩個方法是由於,要對數組進行操做的時候須要對數組元素更新,找出前兩個最小元素後,要立馬刪除這兩個元素,而後把這兩個元素相加獲得的結果添加到數組中,添加時要有序添加,不然還要排序。這裏面有註釋,不懂也能夠留言。ui

int ListDelete(arrayMy &L, int pos) {
    if (pos > L.length) {
        printf("刪除角標不合法");
        return -1;
    }
    printf("\n刪除%d\n",L.data[pos]->data);
    int i;
    //在插入的同時,i要保證在pos以及pos後方,入1,2,3,4,5當在第3個插入時,須把原有的第三個數據以及之後數據後移一位,空出第三個位置。
    for (i = pos; i <= L.length; i++) {
        L.data[i - 1] = L.data[i];
    }
    L.length--;
    return 0;//返回0表示成功;
}

int ListInsert(arrayMy &L, twoTreeL data) {
    printf("插入%d\n",data->data);

    int pos = 0;
    for (int i = 0; i < L.length; i++) {
        if (data->data >= L.data[i]->data) {
            pos = i;
            printf("pos---%d\n",pos);
            break;
        }
    }
    int i;
    //在插入的同時,i要保證在pos以及pos後方,入1,2,3,4,5當在第3個插入時,須把原有的第三個數據以及之後數據後移一位,空出第三個位置。
    for (i = L.length; i > pos; i--) {
        L.data[i] = L.data[i - 1];
    }
    L.data[pos] = data;
    L.length++;
    return 0;
}

/**
 * 數組初始化
 * */
void arrayInit(int *weigth, int n, arrayMy &arr) {

    int i;
    for (i = 0; i < n; i++) {
        twoTreeL treeL = (twoTreeL) malloc(sizeof(twoTree));
        treeL->Lchild = NULL;
        treeL->Rchild = NULL;
        treeL->data = weigth[i];
        arr.data[i] = treeL;
        arr.length++;
//        arr->lenght++;
    }
    printf("初始化成功");
}

哈夫曼算法

這是最重要的一點,首先拿到前兩個權值結點,相加計算獲得結果賦值給新建結點,而後刪除數組中的前兩個結點,插入新建結點,而後遞歸重複此操做。spa

/**
 * @describe 哈夫曼算法
 * */

arrayMy haFuManSF(arrayMy &arr) {
    printf("\n個數%d", arr.length);
    if (arr.length >= 2) {
        twoTreeL tA = arr.data[0];//拿到第一個結點data數,(權值最小)。
        twoTreeL tB = arr.data[1];//拿到第二個結點data數,(權值第二小)。
        twoTreeL tc = (twoTreeL) malloc(sizeof(twoTree));//建立新的結點
        tc->data = tA->data + tB->data;//相加結果加入到新建的data域。
        tc->Lchild = tA;//讓A稱爲左子樹
        tc->Rchild = tB;
        ListDelete(arr, 0);//刪掉數組的前兩個結點元素。
        ListDelete(arr, 0);
//        printf(" geshu %d  ",arr.length);
        printf("還剩  ");
        for(int i=0;i<arr.length;i++){
            printf("%d ", arr.data[i]->data);
        }
        ListInsert(arr, tc);//插入新建結點元素。
        printf("\n插入後  ");
        for(int i=0;i<arr.length;i++){
            printf("%d ", arr.data[i]->data);
        }
        haFuManSF(arr);//而後遞歸,重複上面操做。
    } else {
        return arr;
    }
//    printf(" geshu %d  ",arr.length);

    return arr;
}

 遞歸遍歷

//先中後序遍歷二叉樹
void diGuiBianLi(twoTreeL tL, int xl) {
    if (tL == NULL) {
        return;
    } else {
        if (xl == 1) {
            //先序遍歷
            printf("%d ", tL->data);
            diGuiBianLi(tL->Lchild, xl);
            diGuiBianLi(tL->Rchild, xl);
        } else if (xl == 2) {
            //中序遍歷
            diGuiBianLi(tL->Lchild, xl);
            printf("%d ", tL->data);
            diGuiBianLi(tL->Rchild, xl);
        } else if (xl == 3) {
            //後序遍歷
            diGuiBianLi(tL->Lchild, xl);
            diGuiBianLi(tL->Rchild, xl);
            printf("%d ", tL->data);
        }
    }
}

 調試:main

int main(void) {
    //新建權值數組
    int weigth[5] = {3,1 , 12, 4, 14};
    //排序(升序)
    swap(weigth,5);
    //建立數組(存放權值和個數)
    arrayMy arr;
    arr = InitList(arr);
    arrayInit(weigth, 5, arr);
    //檢測數組是否排序好
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr.data[i]->data);
    }
    //哈夫曼編程思想
    arr = haFuManSF(arr);
    twoTreeL treeL=arr.data[0];
    printf("\n");
    //先中後遍歷
    printf("\n先序遍歷: ");
    diGuiBianLi(treeL,1);
    printf("\n中序遍歷:");
    diGuiBianLi(treeL,2);
    printf("\n後序遍歷:");
    diGuiBianLi(treeL,3);

    return 0;
}

 完。調試

相關文章
相關標籤/搜索