數據結構實驗之修理牧場

  • 2020 年 5 月 24 日
  • 筆記

數據結構與演算法實驗報告

修理牧場

     姓名:孫瑞霜 

 

一、實驗目的

1、熟練掌握學習的每種結構及其相應演算法;

2、理論聯繫實際,會對現實問題建模並設計相應演算法。

3、優化演算法,使得演算法效率適當提高

二、實驗要求

1. 認真閱讀和掌握教材上和本實驗相關的內容和演算法;

2. 上機將各種相關演算法實現;

3. 實現上面實驗目的要求的功能,並能進行簡單的驗證。

、實驗內容

認真學習 學習通->數據結構->資料->數據結構實驗指導書–陳越版,從第二章進階實驗1~10中任選一個來實現,編寫程式,輸入給定的數據,能得到給定的輸出。總結演算法思想、畫出流程圖,並思考程式有無改進空間,如何改進。

三、演算法描述

(採用自然語言描述)  

選擇了修理牧場

改進後方法二:我們將木頭的長度看成樹的節點,每切割一次木頭就相當與把節點分為兩個子節點,每個節點的權值就相當於分的的木頭長度。那麼最後這棵樹的總權值就相當於切割的代價。問題就變成求這棵樹的最小權值,這不就是Hoffman樹的作用嘛。所以這道題目就變成了求一棵Hoffman樹的權值。(選用兩種方法)

四、詳細設計

(畫出程式流程圖)

五、程式程式碼

(給出必要注釋)

(方法一)複雜程式碼

#include<stdio.h>

#include<stdlib.h>

#define MinData 0

//#define M 10000

#define MaxData 100

 

typedef struct TreeNode *HuffmanTree;

struct TreeNode{

//char Data;

int Weight;

HuffmanTree Left;

HuffmanTree Right;

};

typedef HuffmanTree ElementType;

typedef  struct  HeapStruct  *MinHeap;

struct  HeapStruct {

ElementType *Elements; /* 存儲堆元素的數組 */

int Size; /* 堆的當前元素個數 */

int Capacity; /* 堆的最大容量  */

};

//函數聲明

MinHeap Create( int MinSize );

void Insert( MinHeap H, ElementType item );

ElementType DeleteMin( MinHeap H );

MinHeap BuildMinHeap( MinHeap H );

int IsFull( MinHeap H );

int IsEmpty( MinHeap H );

void printfHeap( MinHeap H);

HuffmanTree Huffman( MinHeap H );

int PreOrderTraversal( HuffmanTree H,int sum );

 

MinHeap Create( int MaxSize )

{   /* 創建容量為MinSize的空的最小堆 */

HuffmanTree ht0;

MinHeap H =( MinHeap)malloc( sizeof( struct HeapStruct ) );

H->Elements =( ElementType *) malloc( (MaxSize+1) * sizeof( ElementType ) );

H->Size = 0;

H->Capacity =MaxSize;

/* 定義哨兵為小於堆中所有可能元素的值*/

ht0 = (HuffmanTree)malloc( sizeof( struct TreeNode) );

ht0->Left = NULL;

ht0->Right = NULL;

ht0->Weight = 0;

H->Elements[0] = ht0;

return H;

}

 

void printfHeap( MinHeap H)

{   

int i;

for(i=1;i<=H->Size;i++)

printf(“%d “,H->Elements[i]->Weight);

}

 

 

void Insert( MinHeap H, ElementType item )

{ /* 將元素item 插入最小堆H,其中H->Elements[0]已經定義為哨兵 */

    int i;

HuffmanTree hti;

 

if ( IsFull(H) ) {

printf(“最小堆已滿“);

return;

}

hti = (HuffmanTree)malloc( sizeof( struct TreeNode) );

hti->Left = item->Left;

hti->Right =  item->Right;

hti->Weight = item->Weight;

i = ++H->Size; /* i指向插入後堆中的最後一個元素的位置 */

for ( ; H->Elements[i/2]->Weight > item->Weight; i/=2 )

H->Elements[i] = H->Elements[i/2]; /* 向下過濾結點 */

H->Elements[i] = hti; /* item 插入 */

}

 

ElementType DeleteMin( MinHeap H )

{   /* 從最大堆H中取出鍵值為最大的元素,並刪除一個結點 */

    int Parent, Child;

    ElementType MinItem, temp;

 

    if ( IsEmpty(H) ) {

        printf(“最大堆已為空“);

        return H->Elements[0];

    }

 

    MinItem = H->Elements[1]; /* 取出根結點最小值 */

    /* 用最小堆中最後一個元素從根結點開始向上過濾下層結點 */

    temp = H->Elements[H->Size–];

for( Parent=1; Parent*2<=H->Size; Parent=Child ) {

Child = Parent * 2;

        if( (Child!= H->Size) && (H->Elements[Child]->Weight > H->Elements[Child+1]->Weight) )

            Child++;  /* Child指向左右子結點的較小者 */

        if( temp->Weight <= H->Elements[Child]->Weight ) break;

else  /* 移動temp元素到下一層 */

H->Elements[Parent] = H->Elements[Child];

    }

    H->Elements[Parent] = temp;

 

    return MinItem;

}

 

MinHeap BuildMinHeap( MinHeap H )

{   /* 這裡假設所有H->Size個元素已經存在H->Elements[]中     */

    /* 本函數將H->Elements[]中的元素調整,使滿足最小堆的有序性 */

int Parent, Child, i;

ElementType temp;

 

for( i = H->Size/2; i>0; i– ){ /*從最後一個結點的父結點開始 */

temp = H->Elements[i];

for( Parent=i; Parent*2<=H->Size; Parent=Child ) { /* 向下過濾 */

Child = Parent * 2;

            if( (Child!= H->Size) && (H->Elements[Child]->Weight > H->Elements[Child+1]->Weight) )

                Child++;  /* Child指向左右子結點的較小者 */

            if( temp->Weight <= H->Elements[Child]->Weight ) break;

else  /* 移動temp元素到下一層 */

H->Elements[Parent] = H->Elements[Child];

} /* 結束內部for循環對以H->Elements[i]為根的子樹的調整 */

H->Elements[Parent] = temp;

}

 

return H;

}

 

int IsFull( MinHeap H )

{

if(H->Size == H->Capacity -1)

return 1;

else

return 0;

 

}

 

int IsEmpty( MinHeap H )

{

if(H->Size == 0)

return 1;

else

return 0;

 

}

 

int main()

{

MinHeap h1;

int i;

int num;

 

HuffmanTree ht;

int M;

scanf(“%d”,&M);

h1=Create(M);

//printf(“請輸入序列:\n”);

for(i=1;i<=M;i++)

{

scanf(” %d”,&num);

ht = (HuffmanTree)malloc( sizeof( struct TreeNode) );

        ht ->Left = NULL;

        ht->Right = NULL;

        ht->Weight = num;

        h1->Elements[i]=ht;

h1->Size++;

}

ht=Huffman(h1);

printf(“%d\n”,PreOrderTraversal(ht,0));

}

 

HuffmanTree Huffman( MinHeap H )

{   /* 假設根據H->Size個權值所生成的跟結點已經存在H->Elements[]*/

int i,m;

HuffmanTree  T;

    BuildMinHeap(H); /*H->Elements[]按權值調整為最小堆*/

m=H->Size;

 

    for (i = 1; i < m; i++) { /*H->Size -1次合併*/

        T = (HuffmanTree)malloc( sizeof( struct TreeNode) ); /*建立一個新的根結點*/       

T->Left = DeleteMin(H);/*從最小堆中刪除一個結點,作為新T的左子結點*/        

T->Right = DeleteMin(H); /*從最小堆中刪除一個結點,作為新T的右子結點*/       

T->Weight = T->Left->Weight+T->Right->Weight; /*計算新權值*/

        Insert( H, T ); /*將新T插入最小堆*/

}

 

T=DeleteMin(H);//取合併後的樹根

 

return T;

}

//遞歸先序求哈夫曼樹的非葉子結點的權重和

int PreOrderTraversal(HuffmanTree T,int sum)

{

if(T)

{

if(T->Left&&T->Right)

sum+=T->Weight;

sum= PreOrderTraversal(T->Left,sum);

sum=PreOrderTraversal(T->Right,sum);

}

return sum;

}

(方法二)優化後程式碼

#include<stdio.h>

 

#define swap(a,b) a=a^b,b=a^b,a=a^b

 

int tail=0;     //堆的最後一個元素的下一個位置

 

void Insert(int *length,int temp);

int GetHeap(int *length);

int Solve(int *length);

 

int main(void)

{

    int N;

    int i;

    scanf(“%d”,&N);

    int length[N];

    int temp;

 

    for(i=0;i<N;i++){

      scanf(“%d”,&temp);

      Insert(length,temp);      //構造一個小頂堆

    }

 

    printf(“%d”,Solve(length));

 

    return 0;

}

 

void Insert(int *length,int temp)

{

    int son,father;

 

    son=tail;

    length[tail++]=temp;

    father=(son+(son&1))/2-1;

 

    if(!son)

      return ;

 

    while(length[son]<length[father]&&father>=0){

      swap(length[son],length[father]);    //交換兩節點位置

      son=father;

      father=(father+(father&1))/2-1;

    }

 

    return ;

}

 

int GetHeap(int *length)

{

    int result=length[0];

    int father=0;

    int son=1;

 

    length[0]=length[–tail];

 

    while(son<tail){

      if(length[son]>length[son+1])

        son++;                      //如果左孩子節點的數值更大

      if(length[father]>length[son]){

        swap(length[father],length[son]);

        father=son;

        son=2*son+1;

      }

      else

        break;

    }

 

    return result;

}

 

int Solve(int *length)

{

    int min1,min2;

    int result=0;

 

    if(tail==1)

      return 0;

 

    while(1!=tail){

      min1=GetHeap(length);

      min2=GetHeap(length);   //找打最小的兩塊板子

      result+=min1+min2;      //將其合為一塊板子

      Insert(length,min1+min2);     //繼續排序

    }

 

    return result;

}

 

六、測試和結果

(給出測試用例以及測試結果)

輸入樣例:

8

4 5 1 2 1 3 1 1

輸出樣例:49

 

七、用戶手冊

首先打開Dev,然後將程式碼粘貼到上面,先編譯看是否編譯成功,再根據下面測試的數據將程式碼運行後依次填入,便能得出相應的結果,當然也可以嘗試用其他測試數據來測驗一下,看看最後結果如何。