Introduction
所謂的多個堆疊,是指在一個陣列裡實現K個堆疊,如下方圖片裡,3個堆疊要平分陣列裡的位址
要實現多個堆疊有兩個方法可以實現
Simple method:將陣列劃分為 n/k
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
利用三個陣列來儲存
data陣列:存放資料
next陣列:下一個能存放的位址,或是刪除時同一個堆疊下一個的位址
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]的方式,來區分每個堆疊的位址,但缺點是陣列的空間使用效率比較低,因為每個堆疊都有自己的空間,所以當某一個堆疊比較少資料時,就會有比較多空間沒有用到,但其他堆疊無法使用該空間。
第二個方法的優點是只要陣列還有空間,其他的堆疊就都可以使用該空間,所以不會浪費空間。