1. 程式人生 > >C語言資料結構-二叉樹、哈夫曼、佇列小練習

C語言資料結構-二叉樹、哈夫曼、佇列小練習


原始碼地址 GitHub:https://github.com/GYT0313/C-DataStructure

1. 二叉樹

要求:

  1. 掌握二叉樹的二叉連結串列的建立方法;
  2. 掌握二叉樹的3種遍歷遞迴演算法;
  3. 掌握二叉樹的3種遍歷的非遞迴演算法。
    在這裡插入圖片描述
    程式碼:
#include <stdio.h>
#include <stdlib.h>

#define M 100

typedef struct node
{
    char data;
    struct node *lchild, *rchild;
}BTNode;



/*
 * 建立二叉樹:
 *
 *建立次序為從左到右
 *遇到 # 時返回上一層,標誌位變為 2 也就是該節點的右子樹
 *右子樹為 # 時出棧該節點,並訪問父節點的右子樹
 *
*/
BTNode *createBiTree( char *str )
{
    BTNode *s[M];   /* 棧 */
    BTNode *b=NULL, *p;  /* b:指向根節點, p:直接新創節點 */
    int top=-1, i=0, flag=1;

    while ( str[i] != '\0')
    {
        if ( str[i] != '#' )
        {
            p = (BTNode *)malloc(sizeof(BTNode));
            p->data = str[i];
            p->lchild = p->rchild = NULL;

            if ( b == NULL )    /* b 指向根節點 */
                b = p;
            else
            {
                switch ( flag )
                {
                case 1:       /* 左子樹 */
                    s[top]->lchild = p;
                    break;
                case 2:        /* 右子樹 */
                    s[top]->rchild = p;
                    top--;      /* 出棧 */
                    break;
                }
            }
            s[++top] = p;   /* 入棧 */
            flag = 1;
        }
        else
        {
            flag = 2;
            if ( str[i-1] == '#' )      /* 該節點沒有左右子樹,則出棧 */
                top--;
        }
        i++;
    }

    return b;
}

///////////////// 遞迴遍歷  ////////////////////////
/* 先序遞迴演算法 */
void PreOrder1( BTNode *b )
{
    if ( b == NULL )
        return ;
    else
    {
        printf(" %c", b->data);
        PreOrder1( b->lchild );
        PreOrder1( b->rchild );
    }
}

/* 中序遞迴演算法 */
void InOrder1( BTNode *b )
{
    if ( b == NULL )
        return ;
    else
    {
        InOrder1( b->lchild );
        printf(" %c", b->data);
        InOrder1( b->rchild );
    }
}


/* 後序遞迴演算法 */
void PostOrder1( BTNode *b )
{
    if ( b == NULL )
        return ;
    else
    {
        PostOrder1( b->lchild );
        PostOrder1( b->rchild );
        printf(" %c", b->data);
    }
}

///////////////// 非遞迴遍歷  ////////////////////////
///////////////// 非遞迴藉助於棧來實現


/* 先序非遞迴演算法 */
/*
 *先序遍歷從左到右,當p指向空時,該節點出棧,並且 p 指向該節點的右子樹
 *若右子樹也為空,則出棧該節點的父節點,並且 p 指向父節點的右子樹
 *
*/
void PreOrder( BTNode *b )
{
    BTNode *s[M], *p = b;
    int top=-1;

    while ( p != NULL || top != -1 )    /* p不為空,棧不為空 */
    {
        if ( p != NULL )        /* 訪問左子樹 */
        {
            printf(" %c", p->data);
            s[++top] = p;   /* 入棧 */
            p = p->lchild;
        }
        else        /* 訪問右子樹 */
        {
            p = s[top--];   /* p 指向棧頂,並且出棧*/
            p = p->rchild;
        }
    }
}

/* 中序非遞迴演算法 */
void InOrder( BTNode *b )
{
    BTNode *s[M], *p = b;
    int top=-1;

    while ( p != NULL || top != -1 )
    {
        if ( p != NULL )
        {
            s[++top] = p;
            p = p->lchild;
        }
        else
        {
            p = s[top--];
            printf(" %c", p->data);
            p = p->rchild;
        }
    }
}


/* 後序非遞迴演算法 */
/*
 *後序與前、中序不同,需要另一個棧來存放對應入棧的標誌位
 *當第三次訪問該節點時才打印該節點
 *
 *--當列印第一個數後,並不會返回執行if,而是執行else if,也就是父節點的右子樹
 *
*/
void PostOrder( BTNode *b )
{
    BTNode *s1[M], *s2[M], *p = b;
    int top=-1;

    while ( p != NULL || top != -1 )
    {
        if ( p != NULL )
        {
            s1[++top] = p;      /* 入棧 */
            p = p->lchild;
            s2[top] = 1;    /* 第 1 次訪問該節點 */
        }
        else if ( s2[top] == 1 )
        {
            p = s1[top];
            p = p->rchild;
            s2[top] = 2;    /* 第 2 次訪問該節點 */
        }
        else
        {
            p = s1[top--];  /* 出棧 */
            printf(" %c", p->data);
            p = NULL;       /* 跳過直接下次迴圈的 if 語句, 進行 else if 的判斷 */
        }
    }


}


int main()
{
    BTNode *b;
    char str[M];

    gets( str );
    b = createBiTree( str );

    ///////////////////////////////////////
    printf("              Recursion: \n\n");
    printf("PreOrder1:  ");
    PreOrder1( b );     /* 先序遞迴演算法 */
    printf("\n");

    printf("InOrder1:   ");
    InOrder1( b );     /* 中序遞迴演算法 */
    printf("\n");

    printf("PostOrder1: ");
    PostOrder1( b );     /* 後序遞迴演算法 */
    printf("\n\n\n");

    ////////////////////////////////////////
    printf("           No Recursion: \n\n");
    printf("PreOrder:   ");
    PreOrder( b );
    printf("\n");

    printf("InOrder:    ");
    InOrder( b );
    printf("\n");

    printf("PostOrder:  ");
    PostOrder( b );
    printf("\n");

    return 0;
}

2.哈夫曼編碼

要求:

  1. 掌握哈夫曼樹的建立方法;
  2. 掌握哈夫曼編碼的生成方法。
    在這裡插入圖片描述
    程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define N 8  //字母個數
#define M ( 2*N-1 )  //節點數目


typedef struct      /* HaffmanTree 結構體: weight  parent  lchild  rchild */
{
    int weight;
    int parent, lchild, rchild;
}HTNode;
typedef HTNode HaffmanTree[M];      /* HTNode 型別的陣列結構體 */


typedef struct      /* HaffmanTree 結構體: data  weight  code[N] */
{
    char data;
    int weight;
    char code[N];   /* 編碼 原本最長:N-1, 最後一位存放'\0'*/
}HTCode;
typedef HTCode HaffmanCode[N];      /* HTCode 型別的陣列結構體 */

/* 初始化 haffmancode */
int initHaffmanCode( HaffmanCode hc )  /* hc 是陣列 */
{
    int i;

    for (i=0; i<N; i++)
    {
        printf("Please input %d's code and weight: ", i+1);
        fflush(stdin);  /* 清空緩衝區 */
        scanf("%c%d", &hc[i].data, &hc[i].weight);
    }
}


/*初始化 haffmantree*/
/*
 *給葉節點賦 weight,其餘賦-1
 */
void initHaffmanTree( HaffmanTree ht, HaffmanCode hc )
{
    int i;

    for (i=0; i<N; i++)
    {
        ht[i].weight = hc[i].weight;    /*初始化 weight*/
    }

    for (i=0; i<M; i++)
    {
        ht[i].parent = ht[i].lchild = ht[i].rchild = -1;   /*賦值 -1*/
    }
}


/*建立 哈夫曼樹*/
void createHaffmanTree( HaffmanTree ht )
{
    int i, j;
    int min1, min2, index1, index2;     /*最小值、 次小值、 最小值下標、 次小值下標*/

    for (i=N; i<M; i++)     /*查詢 min1 和 min2*/
    {
        min1 = min2 = INT_MAX;  /* int型別的最大值  (引入標頭檔案limts.h)*/
        index1 = index2 = -1;
        for (j=0; j<i; j++)     /*注:每次查詢的範圍在擴大(+1)*/
        {
            if (ht[j].parent == -1)     /*沒有雙親才比較*/
            {
                if (ht[j].weight < min1)    /* ht[j].weight < min1 < min2 */
                {
                    min2 = min1;    /*原來的最小值變為了次小值*/
                    index2 = index1;
                    min1 = ht[j].weight;    /*新的最小值為 ht[j].weight */
                    index1 = j;
                }
                else if(ht[j].weight < min2)
                {
                    min2 = ht[j].weight;    /* min1 < ht[j].weight < min2 */
                    index2 = j;
                }
            }
        }
        ht[i].weight = min1 + min2;     /* 計算新權值,最小值 和 次小值相加得到二者的雙親節點的權值 */
        ht[i].lchild = index1;    /* 最小值在左 */
        ht[i].rchild = index2;   /* 次小在右 */              /* 修改父節點 */

        ht[index1].parent = ht[index2].parent = i;  /* 最小值 和 次小值的 parent */     /* 修改兩個子樹節點 */
    }
}

/*建立 哈夫曼程式碼*/
void createHaffmanCode( HaffmanTree ht, HaffmanCode hc )
{
    char code[N];   /*暫時存放哈夫曼程式碼*/
    int i, j, start;

    for (i=0; i<N; i++)     /*倒敘查詢*/
    {
        start = N - 1;
        code[start] = '\0';
        j = i;
        while(ht[j].parent != -1)   /*到達根節點跳出*/
        {
            code[--start] = ht[ht[j].parent].lchild == j?'0':'1'; /* 雙親的lchild == 該節點的下標,即值為0,否則為1 */
            j = ht[j].parent;   /*雙親的下標*/
        }
        strcpy(hc[i].code, &code[start]);   /*code 是陣列, 有值的地方的地址(因為是倒敘)賦給 hc[i].code(相當於賦首地址)*/
    }

}


/*列印 code*/
void printCode( HaffmanCode hc )
{
    int i;

    for (i=0; i<N; i++)
    {
        printf("%c的編碼是: %s\n", hc[i].data, hc[i].code);
    }
}



int main()
{
    HaffmanCode hc;
    HaffmanTree ht;

    initHaffmanCode(hc);       /*初始化*/
    initHaffmanTree(ht, hc);

    createHaffmanTree(ht);     /*建立*/
    createHaffmanCode(ht, hc);

    printCode(hc);

    return 0;
}

3. 佇列

要求:
1. 輸入說明:輸入為一行正整數,其中第1個數字N(N<=1000)為顧客總數,後面跟著N位顧客的編號。編號為奇數的顧客需要到A視窗辦理業務,為偶數顧客則去B視窗。數字以空格分隔。
2. 輸出說明:按業務處理完成的順序輸出顧客的編號。數字間以空格分隔,但最後一個編號不能有多餘的空格。
Question I:從鍵盤接收顧客編號
Question II:隨機生成顧客編號
Question III:按照 A-B 視窗處理速度

在這裡插入圖片描述
程式碼:

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

typedef struct node     /* 指向資料的結構體 */
{
    int Data;
    struct node *Next;
} Qnode;

typedef struct link     /* 指向節點的結構體 */
{
    Qnode *Front, *Rear;
    struct link *Next;
} LinkQueue;


/* 初始化 */
LinkQueue *initQueue()
{
    LinkQueue *q;
    Qnode *p;

    q = (LinkQueue *)malloc(sizeof(LinkQueue));
    q->Front = q->Rear = (Qnode *)malloc(sizeof(Qnode));
    q->Front->Next = NULL;

    return q;
}


/* 是否為空,為空返回1 */
int isEmpty( LinkQueue *q )
{
    if ( q->Front == q->Rear )
        return 1;
    else
        return 0;
}


/* 入隊 A 視窗*/
void EnqueueA( LinkQueue *q, int data )
{
    Qnode *p;

    p = (Qnode *)malloc(sizeof(Qnode));

    p->Data = data;
    p->Next = NULL;

    q->Rear->Next = p;
    q->Rear = p;
}

/* 入隊 B 視窗 */
void EnqueueB( LinkQueue *q, int data )
{
    Qnode *p;

    p = (Qnode *)malloc(sizeof(Qnode));

    p->Data = data;
    p->Next = NULL;

    q->Rear->Next = p;
    q->Rear = p;
}

/* 出隊 */
void OutQueue( LinkQueue *q, int *queue )
{
    if ( isEmpty(q) )
        return '#';

    Qnode *p, *m;

    p = q->Front->Next;
    q->Front->Next = p->Next;   /* 隊頭指向下一個節點 */
    *queue = p->Data;

    m = q->Front;
    while( m->Next )    /* m 指向隊尾*/
        m = m->Next;
    q->Rear = m;
}

/* Question I II 列印語句 */
void print( int N, LinkQueue *qA, LinkQueue *qB, int *queue )
{
    int i, flag=0;      /* flag 判斷 A 出隊次數 */
    /* 入隊 */
    for ( i=0; i<N; i++ )
    {
        if ( queue[i]%2 == 1 )  /* 奇數到 A 視窗 */
        {
            EnqueueA( qA, queue[i] );
        }
        else                    /* 偶數到 B 視窗 */
            EnqueueB( qB, queue[i] );
    }
    /* 出隊 */
    for ( i=0; i<N; i++ )
    {
        if ( flag < 2 && !isEmpty( qA ))    /* A 出隊 */
        {
            OutQueue( qA, &queue[i] );
            flag++;
        }
        else if ( !isEmpty( qB ) )          /* B 出隊 */
        {
            OutQueue( qB, &queue[i] );
            flag = 0;
        }
        else
        {
            if ( !isEmpty( qA ) )
                OutQueue( qA, &queue[i] );
            else if ( !isEmpty( qB ) )
                OutQueue( qB, &queue[i] );
            flag = 0;
        }
    }

    printf("The order of queue: ");
    for ( i=0; i<N; i++ )
        printf(" %d", queue[i]);
    printf("\n");
}

/* Question III 列印語句 */
void print2( int N, int NA, int NB, LinkQueue *qA, LinkQueue *qB, int *queue )
{
    int i, flag=0, flag2=0;     /* flag 判斷 A 出隊次數, flag2 判斷 B 出隊次數 */
    /* 入隊 */
    for ( i=0; i<N; i++ )
    {
        if ( queue[i]%2 == 1 )  /* 奇數到 A 視窗 */
        {
            EnqueueA( qA, queue[i] );
        }
        else                    /* 偶數到 B 視窗 */
            EnqueueB( qB, queue[i] );
    }
    /* 出隊 */
    for ( i=0; i<N; i++ )
    {
        if ( flag < NA && !isEmpty( qA ))       /* A 出隊 */
        {
            OutQueue( qA, &queue[i] );
            flag++;
            flag2 = 0;
        }
        else if ( !isEmpty( qB ) )          /* B 出隊 */
        {
            OutQueue( qB, &queue[i] );
            flag2++;
            if ( flag2 >= NB )      /* 判斷 B 出隊次數是否滿足 */
                flag = 0;
        }
        else
        {
            if ( !isEmpty( qA ) )
                OutQueue( qA, &queue[i] );
            else if ( !isEmpty( qB ) )
                OutQueue( qB, &queue[i] );
            flag = 0;
            flag2 = 0;
        }
    }

    printf("The order of queue: ");
    for ( i=0; i<N; i++ )
        printf(" %d", queue[i]);
    printf("\n");
}


int main()
{
    LinkQueue *qA, *qB;
    int N;
    int queue[1000], i;
    int NA, NB;

    qA = initQueue();
    qB = initQueue();

    printf("Question I:\n");        /* 從鍵盤接收 */
    printf("N: ");
    scanf("%d", &N);
    for ( i=0; i<N; i++ )
        scanf("%d", &queue[i]);
    print( N, qA, qB, queue );


    ////////////////////////////////////////////////////
    printf("\n");
    printf("Question II:\n");       /* 按照 rand() 計算 */
    printf("N: ");
    scanf("%d", &N);
    for ( i=0; i<N; i++ )
        queue[i] = rand()%1000+1;

    printf("The rand number: ");
    for(i=0; i<N; i++)
        printf("%d ", queue[i]);
        printf("\n");
    print( N, qA, qB, queue );


    ////////////////////////////////////////////////////
    printf("\n");
    printf("Question III:\n");      /* 按照 NA : NB 計算 */
    printf("N: ");
    scanf("%d", &N);
    printf("NA:NB: ");
    scanf("%d%d", &NA, &NB);
    for ( i=0; i<N; i++ )
        queue[i] = rand()%1000+1;

    printf("The rand number: ");
    for(i=0; i<N; i++)
        printf("%d ", queue[i]);
        printf("\n");
    print2( N, NA, NB, qA, qB, queue );

    return 0;
}