Multiple Stacks

·

5 min read

Introduction

所謂的多個堆疊,是指在一個陣列裡實現K個堆疊,如下方圖片裡,3個堆疊要平分陣列裡的位址

要實現多個堆疊有兩個方法可以實現

  1. Simple method:將陣列劃分為 n/k

  2. Efficient method:節省空間

Implementation

Simple method

結構定義

push()

加入時要先判斷該堆疊是否已經到下一個堆疊的bottom了

pop()

刪除時只要判斷同一個堆疊是否相同就好,然後再把該堆疊的top遞減

完整的程式碼

#include <stdio.h>

#define MAX  10
#define UNIT  2 // 代表在這個程式中,會有 2 個堆疊(stack),要分割成多少個堆疊

void pushFunction(int number1);
void popFunction(int number2);
void listFunction(int number3);

char MS[MAX][20]; /* 多個堆疊陣列 */
int t[(MAX / UNIT)], b[(MAX / UNIT)]; /* 每個堆疊的頂端和底端 */

int main()
{
    int option, i;
    int number1, number2, number3;
    // 初始化多個堆疊的陣列。透過迴圈依序對每個堆疊進行初始化
    for (i = 0; i <= UNIT - 1; i++) {
        // 如果 UNIT 等於 1,表示只有一個堆疊,此時將 t[1] 和 b[1] 設為最後一個元素的索引(9),t[0] 和 b[0] 設為 -1,表示堆疊為空
        // 會先把t陣列和b陣列第1個元素設成4,然後把第0個元素設成-1,並離開迴圈
        if (UNIT == 1) {
            t[i + 1] = MAX - 1;
            b[i + 1] = MAX - 1;
            t[i] = i * (MAX / UNIT) - 1;
            b[i] = i * (MAX / UNIT) - 1;
        }
        // 如果 UNIT 大於 1,表示有多個堆疊,將 t[i] 和 b[i] 初始化為 i*(MAX/UNIT)-1,表示第 i 個堆疊的頂端和底部都在第 i 個區間的最後一個位置
        // 將t陣列和b陣列的第0個元素設成-1,然後將第1個元素設成4,並離開迴圈
        // t[0]為第一個堆疊的頂端,t[1]為第二個堆疊的頂端
        else {
            t[i] = i * (MAX / UNIT) - 1;
            b[i] = i * (MAX / UNIT) - 1;
        }
    }
    while (1) {
        printf("\n *****************************\n");
        printf("        <1> insert (push)\n");
        printf("        <2> delete (pop)\n");
        printf("        <3> list\n");
        printf("        <4> display the whole array\n");
        printf("        <5> quit\n");
        printf(" *****************************\n");
        printf(" Please enter your choice...");
        scanf_s("%d", &option);
        switch (option) {
        case 1:
            printf("\n The total number of Stack is %d!!", UNIT);
            printf("\n Please enter the number of stack? ");
            scanf_s("%d", &number1);
            if (number1 > UNIT)
                printf(" Sorry, the number is out of Stack total         number !!\n");
            else
                pushFunction(number1);
            break;
        case 2:
            printf("\n Please enter the number of stack? ");
            scanf_s("%d", &number2);
            if (number2 > UNIT)
                printf(" Sorry, the number is out of Stack total number!!\n");
            else
                popFunction(number2);
            break;
        case 3:
            printf("\n Please enter the number of stack? ");
            scanf_s("%d", &number3);
            if (number3 > UNIT)
                printf(" Sorry, the number is out of Stack total number!!\n");
            else
                listFunction(number3);
            break;
        case 4:
            printf("\n");
            for (int i = 0; i < MAX; i++)
            {
                printf("%s ", MS[i]);
            }
            break;
        case 5:
            printf("\n");
            return 0;
        }
    }
}
// number1 代表要對哪一個 stack 執行 push 操作
void pushFunction(int number1)
{        // t[1 - 1] == b[1]
         // t[0] = -1   b[1] = 4
         // 第二個堆疊不能放滿,不然會發生錯誤,可以把UNIT改成3,這樣第二個堆疊就能夠放滿
         // 當某個堆疊的最上面的位置正好會等於下一個堆疊最下面的位置(第一個堆疊最上面的位置是t[4],正好會和下一個堆疊的起始位置一樣)
    if (t[number1 - 1] == b[number1]) {
        printf(" Stack%d is full!\n", number1);
        printf(" Please choose another Stack to insert!!\n");
    }
    else {
        // 當第一次加入時t[0] = -1,所以要用前置加將t[0] = 0
        printf(" Please enter item to insert: ");
        scanf_s("%s", MS[++t[number1 - 1]], 20);
        printf(" You insert %s to #%d stack!!\n", MS[t[number1 - 1]], number1);
    }
}

void popFunction(int number2)
{
    if (t[number2 - 1] == b[number2 - 1])
        printf("\n No item, stack%d is empty!\n", number2);
    else {
        printf("\n Item %s in Stack %d is deleted\n", MS[t[number2 - 1]], number2);
        t[number2 - 1]--;
    }
}

void listFunction(int number3)
{
    int count = 0, i, startNumber = 0;
    if (t[number3 - 1] == b[number3 - 1])
        printf("\n No item, stack is empty\n");
    else {
        // 計算指定的堆疊的起始索引位置,假如要印出第一個堆疊的內容,那麼startNumber會等於-1
        startNumber = (number3 - 1) * (MAX / UNIT) - 1;
        printf("\n  ITEM\n");
        printf(" ------------------\n");
        for (i = startNumber + 1; i <= t[number3 - 1]; i++) {
            printf("  %-20s\n", MS[i]);
            count++;
        }
        printf(" ------------------\n");
        printf("  Total item: %d\n", count);
    }
}

Efficient method

利用三個陣列來儲存

  1. data陣列:存放資料

  2. next陣列:下一個能存放的位址,或是刪除時同一個堆疊下一個的位址

  3. top陣列:每個堆疊的頂端

結構定義

push()

新增一個變數用來儲存目前到第幾個位置,然後更新counter準備下一次的加入,然後把下一個能儲存的位置,改成該堆疊top所指向的位置,這樣之後要刪除時,指向該堆疊的top就會是這個元素,然後把top改成目前要新增陣列的位置,最後存入元素。

pop()

新增一個變數,指向該堆疊的top位置,然後把top往後一個位置移動,然後把該位置改成可以隨時新增的狀態,然後把counter改成該位置,以方便重新利用該位置。

完整的程式碼

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

// Structure of stack
struct NStack
{
    int element; // 陣列存放多少個元素
    int part;    // 多少個堆疊
    int* data;
    int* top;
    int* next;
    int counter; // 存放的位置
};

struct NStack* makeStack(int element, int n)
{
    /* 當堆疊的數量小於0或陣列數量為0就回傳錯誤 */
    if (n <= 0 || element == 0)
    {
        return NULL;
    }
    // Create dynamic NStack
    struct NStack* s = (struct NStack*)malloc(sizeof(struct NStack));
    if (s == NULL)
    {
        printf("\n Memory Overflow, when creating a new Stack\n");
    }
    else
    {
        // Create memory of queue elements
        s->data = (int*)malloc(element * sizeof(int));
        s->next = (int*)malloc(element * sizeof(int));
        s->top = (int*)malloc(n * sizeof(int));
        s->counter = 0;
        s->element = element;
        s->part = n;
        int i = 0;
        // Set the initial value of front and tail of queue
        for (i = 0; i < n; ++i)
        {
            s->top[i] = -1;
        }
        // Set next and data value
        for (i = 0; i < element - 1; ++i)
        {
            s->data[i] = 0;
            s->next[i] = i + 1;
        }
        s->next[element - 1] = -1;
        s->data[element - 1] = 0;
    }
    return s;
}

// Determine that given stack is empty or not
int isEmpty(struct NStack* s, int selection)
{
    return s->top[selection] == -1;
}

// Determine that given stack is full or not
int isFull(struct NStack* s)
{
    if (s->counter == -1)
    {
        return 1;
    }
    return 0;
}

// Pushing an item in stack number 'sn' where sn is from 0 to k-1
void push(struct NStack* s, int selection, int data)
{
    int data;
    printf(" Please enter item to insert: ");
    scanf("%d", &data);

    if (selection < 0 || selection >= s->part)
    {
        // When given stack is out of range
        return;
    }
    if (isFull(s) == 1)
    {
        printf("\n Stack is Full \n");
    }
    else
    {
        int location = s->counter;
        s->counter = s->next[location];
        s->next[location] = s->top[selection];
        s->top[selection] = location;
        s->data[location] = data;
    }
}

// Handles the request of remove selected stack element
int pop(struct NStack* s, int selection)
{
    if (selection < 0 || selection >= s->part)
    {
        // When given stack is out of range
        return -1;
    }
    if (isEmpty(s, selection) == 1)
    {
        printf("\n Stack is Empty \n");
        return -1;
    }
    else
    {
        int location = s->top[selection];
        s->top[selection] = s->next[location];
        s->next[location] = s->counter;
        s->counter = location;
        return s->data[location];
    }
}

void printStack(struct NStack* s)
{
    int location = 0;
    for (int i = 0; i < s->part; ++i)
    {
        printf("\n Stack %d : ", i);
        // Select Stack
        location = s->top[i];
        while (location != -1)
        {
            printf(" %d", s->data[location]);
            location = s->next[location];
        }
    }
    printf("\n");
}

int main()
{
    int choice, stack;
    struct NStack* s = makeStack(15, 4);
    while (1)
    {
        printf("\n *****************************\n");
        printf("        <1> insert (push)\n");
        printf("        <2> delete (pop)\n");
        printf("        <3> Display the whole array\n");
        printf("        <4> quit\n");
        printf(" *****************************\n");
        printf(" Please enter your choice...\n");
        scanf_s("%d", &choice);
        switch (choice)
        {
        case 1:
            printf("\n Please enter the number of stack? ");
            scanf_s("%d", &stack);
            push(s, stack);
            break;
        case 2:
            printf("\n Please enter the number of stack? ");
            scanf_s("%d", &stack);
            printf("\n Remove element of Stack %d is : %d\n", stack, pop(s, stack));
            break;
        case 3:
            printStack(s);
            break;
        case 4:
            return 0;
        }
    }
}

兩個方法的優缺點

第一個方法的優點是實現比較簡單,透過arr[n/k]的方式,來區分每個堆疊的位址,但缺點是陣列的空間使用效率比較低,因為每個堆疊都有自己的空間,所以當某一個堆疊比較少資料時,就會有比較多空間沒有用到,但其他堆疊無法使用該空間。

第二個方法的優點是只要陣列還有空間,其他的堆疊就都可以使用該空間,所以不會浪費空間。