1. 程式人生 > >【 C 】經典抽象資料型別(ADT)之堆疊(用靜態陣列實現堆疊)

【 C 】經典抽象資料型別(ADT)之堆疊(用靜態陣列實現堆疊)

堆疊簡介

堆疊(stack)最鮮明的特點就是後進先出(Last-In First-Out,LIFO)的資料進出方式。

基本的堆疊操作通常被稱為 push 和 pop。push就是將一個新值壓入到堆疊的頂部, pop就是把堆疊頂部的值移出堆疊並返回這個值。堆疊只提供對它的頂部值的訪問。

傳統的堆疊介面中,訪問頂部元素的唯一方法就是把它移除。另一類堆疊介面提供三種基本的操作:push,pop,top。push和前面所說的一樣,把一個新值壓入堆疊的頂部,pop有點不一樣的是隻把頂部元素從堆疊中移除,並不返回這個值。top返回頂部元素的值,但它並不把頂部元素從堆疊中移除。下面統統採用這種方式!

我們需要兩個額外的函式來使用堆疊。一個空堆疊不能執行pop操作,所以我們需要一個函式告訴我們堆疊是否為空。在實現堆疊時,如果存在最大長度限制,那麼我們也需要另一個函式告訴我們堆疊是否已滿。

這裡提前說一下,這兩個函式一個是判斷堆疊是否已空的函式(is_empty),如果堆疊為空,返回TRUE,否則返回FALSE;另一個是判斷堆疊是否已滿的函式(is_full),如果已滿,返回TRUE,否則返回FALSE。

在真正的堆疊實現中,這兩個函式需要配合assert巨集使用,後面你就將看到,關於assert巨集的基礎知識,見博文:【 C 】assert.h 簡明介紹

如果不瞭解什麼是巨集,可以參考博文:【 C 】巨集 簡記

堆疊介面

什麼是堆疊介面,實際上就是一個頭檔案,聲明瞭堆疊的一些操作,包含堆疊需要使用的函式的一些原型!

堆疊是最容易實現的ADT之一。它的基本方法是當值被push到堆疊時把它們儲存於陣列中連續的位置上。你必須記住最近一個被push的值的下標。如果需要執行pop操作,你只需要簡單地減少這個下標值就好了。

下面的標頭檔案描述了一個堆疊模組的非傳統介面:

//一個堆疊模組的介面

#define STACK_TYPE int //堆疊所儲存值的型別

//push
//把一個新值壓入到堆疊中,它的引數是需要被壓入的值。
void push( STACK_TYPE value );

//pop
//從堆疊彈出一個值,並將其丟棄
void pop( void );

// is_empty
// 如果堆疊為空,返回TRUE,否則返回FALSE

int is_empty( void );

// is_full
// 如果堆疊已滿,返回TRUE,否則返回FALSE
int is_full( void );

儲存為:stack.h

注意堆疊介面只包含了使用者使用堆疊所需要的資訊,特別是它並沒有展示堆疊的實現方式。事實上,對這個標頭檔案稍作修改,它可以用於三種實現方式。用這種方式定義介面是一種好方法,因為它防止使用者以為它依賴於某種特定的實現方式。

具體實現方式後面會一一道來。

這個介面的一個有趣的特徵就是儲存於堆疊中的值的型別的宣告方式。在編譯這個堆疊模組之前,使用者可以修改這個型別以適合自己的需要。例如上面宣告為:#define STACK_TYPE int     //堆疊所儲存值的型別

實現堆疊

用靜態陣列實現堆疊

//用靜態陣列實現堆疊,陣列的長度只能通過修改#define定義
//並對模組重新編譯來實現
#include <assert.h>
#include "stack.h"

#define STACK_SIZE 100  //堆疊中值數量的最大限制

//定義堆疊陣列和棧頂下標
static STACK_TYPE stack[STACK_SIZE];
static int top_element = -1;

//push
void push( STACK_TYPE value )
{
    assert( !is_full() ); //判斷堆疊是否已經滿了,未滿則繼續執行,否則退出
    top_element += 1;
    stack[ top_element ] = value;
}

//pop
void pop( void )
{
    assert( !is_empty() ); //判斷堆疊是否是空的,如果不是,則繼續執行,否則退出
    top_element -= 1;
}

//top
STACK_TYPE top( void )
{
    assert( !is_empty() ); //堆疊未空,則繼續
    return stack[ top_element ];    
}

//is_empty
int is_empty( void )
{
    return top_element == -1; //堆疊為空,則返回TRUE,否則返回FALSE
}

//is_full
int is_full( void )
{
    return top_element == STACK_SIZE - 1; //堆疊已滿,返回TRUE,否則返回FALSE
}

變數top_element保持堆疊頂部元素的下標值。它的初始值為-1,提示堆疊為空。push函式在儲存新元素之前先增加這個變數的值,這樣top_element始終包含頂部元素的下標值。

這個堆疊模組的一個值得注意的特性就是它使用assert巨集來防止非法操作,諸如從一個空堆疊中彈出一個元素或者從一個已滿堆疊中壓入元素。這個斷言呼叫is_full和is_empty函式,而不是測試top_element本身。如果你以後決定使用不同的方法來檢測空堆疊或者滿堆疊,使用這種方法顯然要容易很多。

對於使用者無法消除的錯誤,使用斷言是很合適的。

下篇博文再說用動態陣列實現堆疊以及使用鏈式結構實現堆疊!