国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【數(shù)據(jù)結(jié)構(gòu)】帶你深入理解棧

這篇具有很好參考價值的文章主要介紹了【數(shù)據(jù)結(jié)構(gòu)】帶你深入理解棧。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔

一. 棧的基本概念??

棧是一種特殊的線性表。其只允許在固定的一端進(jìn)行插入和刪除元素的操作,進(jìn)行數(shù)據(jù)的插入和刪除的一端稱作棧頂,另外一端稱作棧底。棧不支持隨機(jī)訪問,棧的數(shù)據(jù)元素遵循后進(jìn)先出的原則,即LIFO(Late In First Out)。

也許有人曾經(jīng)聽說過壓棧入棧的術(shù)語,以下是它們的定義:

壓棧:棧的插入操作叫做進(jìn)棧/壓棧/入棧,插入數(shù)據(jù)是在棧頂
出棧:棧的刪除操作叫做出棧/彈棧,刪除數(shù)據(jù)也是在棧頂

我們結(jié)合動圖來理解棧的后進(jìn)先出:

棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔

二. 棧實(shí)現(xiàn)方法的分析與選擇??

2.1 引入

我們可以使用順序存儲結(jié)構(gòu)或者鏈?zhǔn)酱鎯Y(jié)構(gòu)來實(shí)現(xiàn)棧。換句話來說,我們可以使用之前學(xué)習(xí)過的順序表或者鏈表來實(shí)現(xiàn)棧,它們各自有自己的優(yōu)缺點(diǎn),下面我們就來分析分析。

2.2 用順序表來實(shí)現(xiàn)

以下是動態(tài)順序表實(shí)現(xiàn)棧的結(jié)構(gòu)體聲明和圖示:

typedef struct StackList
{
    STDataType* a; //指向動態(tài)開辟的空間
    int top; //棧頂所在下標(biāo),相當(dāng)于元素個數(shù)
    int capacity;//順序表容量
}ST;
棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔
優(yōu)點(diǎn):由于棧的插入和刪除數(shù)據(jù)符合 后進(jìn)先出的原則,我們 把順序表末端當(dāng)作棧頂,則插入數(shù)據(jù)和刪除數(shù)據(jù)就是 尾插尾刪 。而前面我們知道 順序表的尾插和尾刪效率非常高 ,時間復(fù)雜度為O(1)。

缺點(diǎn): 存在容量限制,當(dāng)容量不足是需要擴(kuò)容,擴(kuò)容需要成本。

2.3 用鏈表來實(shí)現(xiàn)

2.3.1 單鏈表實(shí)現(xiàn)(尾為棧頂)
typedef struct StackNode
{
    STDataType x;//數(shù)據(jù)域
    StackNode* nest;//指針域,指向下一結(jié)點(diǎn)
}ST;
struct Stack
{
    ST* phead;//指向第一個結(jié)點(diǎn)
    ST* tail;//指向尾結(jié)點(diǎn)
}
棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔

假如我們使用鏈表尾當(dāng)作棧頂,則對應(yīng)的插入刪除就是尾插尾刪。我們知道單鏈表的尾插和尾刪要先找到鏈表尾,時間復(fù)雜度是O(N)??赡苡腥藭?,那我定義一個尾指針來記錄鏈表尾部,想法很好,但是這樣雖然解決了尾插效率低的問題,但是尾刪除了要找到最后一個結(jié)點(diǎn),還要找到其前面的結(jié)點(diǎn),由于鏈表單向,最終還是要遍歷鏈表,沒有什么意義。


2.3.2 單鏈表實(shí)現(xiàn)(頭為棧頂)

我們知道,和順序表相反,單鏈表頭插和頭刪效率較高,時間復(fù)雜度為O(1)。我們就可以將鏈表頭當(dāng)作棧頂,這樣插入就相當(dāng)于頭插,刪除就相當(dāng)于頭刪,如下:

棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔
2.3.3 雙向鏈表實(shí)現(xiàn)

如果一定要使用鏈表以及把鏈表尾當(dāng)作棧頂,為了解決刪除需要找到尾結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)時間效率低的問題,我們可以用雙向鏈表來實(shí)現(xiàn)棧。雙向鏈表除了后繼指針還增加了前驅(qū)指針來指向上一個結(jié)點(diǎn),利用這個結(jié)構(gòu)可以直接得到上一個結(jié)點(diǎn),無需再遍歷鏈表,時間復(fù)雜度為O(1)。

typedef struct StackNode
{
    STDataType x;//數(shù)據(jù)域
    StackNode* nest;//后繼指針域,指向下一結(jié)點(diǎn)
    StackNode* prev;//前驅(qū)指針域,指向上一結(jié)點(diǎn)
}ST;
struct Stack
{
    ST* phead;//指向第一個結(jié)點(diǎn)
    ST* tail;//指向尾結(jié)點(diǎn)
}
棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔
2.3.4 總結(jié)
如果 沒有要求棧頂?shù)奈恢?/span>,則我們還是使用 單鏈表來實(shí)現(xiàn),將頭作為棧頂。這是因?yàn)? 雙向鏈表比單鏈表的結(jié)點(diǎn)多占用了一個前驅(qū)指針的空間,雖然現(xiàn)代計(jì)算機(jī)空間已然構(gòu)不成太大問題,但是能省則省,大伙們懂的 ??。
如果題目要求棧頂在鏈表尾的話,那還是老老實(shí)實(shí)用 雙向鏈表實(shí)現(xiàn)吧。
使用鏈表的缺點(diǎn)就是每次插入都要malloc新結(jié)點(diǎn),會消耗一定的時間成本。

2.4 選擇

我們推薦采用 順序表來實(shí)現(xiàn)對棧的操作,原因如下:
1. 棧的特性完美利用了順序表尾插尾刪效率高的特性,雖然需要擴(kuò)容,但是鏈表創(chuàng)建結(jié)點(diǎn)也同樣需要成本,而 順序表擴(kuò)容頻率不像鏈表一樣如此頻繁。
2. 我們知道CPU與主存速度上存在巨大差距,為了提高效率,CPU和主存之間還存在著 cache高速緩存CPU訪問cache的速度是快于主存的。每次CPU取數(shù)據(jù)時會訪問cache看看存不存在所需的數(shù)據(jù),如果不存在才會訪問主存,然后將數(shù)據(jù)所在的內(nèi)存塊加載到cache中。由于順序表空間是連續(xù)的,根據(jù)cache的 空間局部性原理,采用順序表 cache的命中率會高于鏈表,效率高。

三. 接口的實(shí)現(xiàn)?

3.1 棧的聲明

本文我們采用動態(tài)順序表來實(shí)現(xiàn)棧,結(jié)構(gòu)體的聲明如下:
typedef int STDataType;
typedef struct StackList
{
    STDataType* a;//指向動態(tài)開辟的空間
    int top; //棧頂所在下標(biāo),相當(dāng)于元素個數(shù)
    int capacity;//棧的容量
}ST;

和前面鏈表順序表一樣,我們不直接指定數(shù)據(jù)的類型,而是將類型重定義為STDataType,這樣做有利于提高代碼的可維護(hù)性。

3.2 初始化和銷毀

和其他數(shù)據(jù)結(jié)構(gòu)一樣,當(dāng)我們使用棧結(jié)構(gòu)之前需要對其進(jìn)行初始化,當(dāng)我們不再使用它是要對它進(jìn)行銷毀,具體代碼如下:

//初始化棧
void StackInit(ST* ps)  //需要改變實(shí)參,傳指針
{
    assert(ps);//確保傳入的指針不為空
    ps->a = (STDataType*)malloc(4 * sizeof(STDataType));//起初先分配四個字節(jié)空間
    ps->capacity = 4;
    ps->top = 0;
}

//銷毀棧
void StackDestroy(ST* ps)
{
    assert(ps);
    free(ps->a);//將??臻g釋放掉
    //將棧結(jié)構(gòu)中的信息清空
    ps->capacity = 0;
    ps->top = 0;
    ps->a = NULL;
}

3.3 入棧

由于棧只允許在固定的一端插入,我們又將末端當(dāng)作棧頂,因此入棧就是尾插。而順序表的尾插我們已經(jīng)很清楚了,往棧頂所在下標(biāo)放入數(shù)據(jù),然后棧頂下標(biāo)加1即可。效果和代碼如下:

棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔
//入棧
void StackPush(ST* ps, STDataType x)
{
    assert(ps);
    if (ps->top == ps->capacity) //元素個數(shù)等于容量,棧滿了,先擴(kuò)容
    {
        STDataType* temp = (STDataType*)realloc(ps->a, 2 * ps->capacity*sizeof(STDataType));
        if (temp == NULL)//失敗則退出程序
        {
            printf("擴(kuò)容失敗\n");
            exit(-1);
        }
        else
        {
            ps->a = temp;
            ps->capacity *= 2;
            temp = NULL;
        }
    }
    (ps->a)[ps->top] = x;//入棧
    (ps->top)++;//更新棧頂位置
}

3.4 出棧

和入棧一樣,出棧也只在固定的一端進(jìn)行。入棧是尾插,則出棧就是尾刪。而我們用順序表來實(shí)現(xiàn)棧,因此尾刪只需要將棧頂退后一位即可。

這里有人可能會將棧頂?shù)脑刂?然后再將棧頂位置后退一位。實(shí)際上這種方法并不可取,有以下兩種原因:
1. 如果棧頂?shù)脑乇旧砭褪?,那我們的行為就失去了意義。
2. 棧的元素類型不一定是整形,如果是浮點(diǎn)型或者結(jié)構(gòu)體,我們賦值為0顯然是不妥的。
棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔
//出棧
void StackPop(ST* ps)
{
    assert(ps);//確保傳入指針不為空
    assert(ps->top);//確保棧存在元素
    (ps->top)--;//更新棧頂
}

3.5 求棧頂元素

很簡單,我們可以直接根據(jù)棧頂所在的下標(biāo)得到棧頂元素,如下:

棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔
//求棧頂元素
STDataType StackTop(ST* ps)
{
    assert(ps);
    assert(ps->top);//確保棧中存在元素
    return ps->a[ps->top - 1];//棧頂元素所在下標(biāo)即為top-1
}

3.6 判空

在我們設(shè)計(jì)的棧結(jié)構(gòu)中,top實(shí)際上就等價于元素個數(shù),通過判斷top是否為0就可以知道棧是否為空。我們使用了C語言stdbool.h頭文件中的bool類型,其只能用來存放true(1)false(0)兩個值,分別代表真和假。代碼如下:

//判空
bool StackEmpty(ST* ps)
{
    assert(ps);
    return ps->top;//top為0則返回false,不為零返回true
}

3.7 求棧的元素個數(shù)

根據(jù)我們構(gòu)造的棧結(jié)構(gòu)體,棧頂top的值就是棧的元素個數(shù),直接返回即可:

//求棧的元素個數(shù)
int StackSize(ST* ps)
{
    assert(ps);
    return ps->top;
}

3.8 思考

會不會有人會有以下思考:
1. 求棧頂元素,判空,求元素個數(shù)都是用一行直接返回,這些接口會不會有些許多余,直接訪問結(jié)構(gòu)體相應(yīng)成員不就好了?
2. 為什么沒有實(shí)現(xiàn)查找,打印,修改等接口?

下面我們來分析一下:

  • 我們要知道,數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)方式多種多樣,在本文我們將棧元素個數(shù)作為棧頂?shù)南聵?biāo),那可不可以將最后一個元素的下標(biāo)作為棧頂下標(biāo)呢?實(shí)際上完全可以。那么就會出現(xiàn)一個問題,如果我們使用別人已經(jīng)封裝好的棧,我們要怎么知道棧頂元素下標(biāo)是top還是top-1呢?我們要怎么知道是top=0為空還是top=-1為空呢?我們要怎么知道元素個數(shù)是top還是top+1呢?我們完全不知道,只有設(shè)計(jì)者才知道,因此設(shè)計(jì)者往往會將這些功能再封裝成函數(shù),供我們直接調(diào)用。

  • 這是因?yàn)?span id="n5n3t3z" class="kdocs-color" style="background-color:#FBF5B3;">棧是一種限制型數(shù)據(jù)結(jié)構(gòu),其不支持隨機(jī)訪問,只允許在固定的一端(棧頂)進(jìn)行插入和刪除操作,不允許在其他的位置進(jìn)行任何操作。因此,棧不存在查找,打印,修改等對除棧頂之外的位置進(jìn)行操作的接口,否則會破壞棧的特性。為了遵循棧的特性,我們就不實(shí)現(xiàn)這些接口。

四. 完整代碼及效果展示??

按照以往慣例,我們采用多文件編寫的形式,將上述接口的定義實(shí)現(xiàn)放在Stack.c文件中,然后將接口的聲明和結(jié)構(gòu)體的定義放于Stack.h頭文件中,以達(dá)到封裝的效果。這樣我們?nèi)绻枰褂脳?,就只需要在文件中包含對?yīng)的頭文件Stack.h就可以使用我們上面定義的各種接口。以下為本文實(shí)現(xiàn)的完整代碼以及效果展示:

//Stack.h文件,用于聲明接口函數(shù),定義結(jié)構(gòu)體
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDataType;
typedef struct StackList
{
    STDataType* a;//指向動態(tài)開辟的空間
    int top; //棧頂所在下標(biāo),相當(dāng)于元素個數(shù)
    int capacity;//棧的容量
}ST;

//初始化
void StackInit(ST* ps);
//銷毀
void StackDestroy(ST* ps);
//出棧
void StackPop(ST* ps);
//入棧
void StackPush(ST* ps, STDataType x);
//求棧頂元素
STDataType StackTop(ST* ps);
//求棧元素個數(shù)
int StackSize(ST* ps);
//判空
bool StackEmpty(ST* ps);
//Stack.c文件,用于定義接口函數(shù)
#include"Stack.h"

//初始化棧
void StackInit(ST* ps)  //需要改變實(shí)參,傳指針
{
    assert(ps);//確保傳入的指針不為空
    ps->a = (STDataType*)malloc(4 * sizeof(STDataType));//起初先分配四個字節(jié)空間
    ps->capacity = 4;
    ps->top = 0;
}

//入棧
void StackPush(ST* ps, STDataType x)
{
    assert(ps);
    if (ps->top == ps->capacity) //元素個數(shù)等于容量,棧滿了,先擴(kuò)容
    {
        STDataType* temp = (STDataType*)realloc(ps->a, 2 * ps->capacity * sizeof(STDataType));
        if (temp == NULL)//失敗則退出程序
        {
            printf("擴(kuò)容失敗\n");
            exit(-1);
        }
        else
        {
            ps->a = temp;
            ps->capacity *= 2;
            temp = NULL;
        }
    }
    (ps->a)[ps->top] = x;//入棧
    (ps->top)++;//更新棧頂位置
}


//出棧
void StackPop(ST* ps)
{
    assert(ps);//確保傳入指針不為空
    assert(ps->top);//確保棧存在元素
    (ps->top)--;//更新棧頂
}

//求棧頂元素
STDataType StackTop(ST* ps)
{
    assert(ps);
    assert(ps->top);//確保棧中存在元素
    return ps->a[ps->top - 1];//棧頂元素所在下標(biāo)即為top-1
}

//求棧的元素個數(shù)
int StackSize(ST* ps)
{
    assert(ps);
    return ps->top;
}


//判空
bool StackEmpty(ST* ps)
{
    assert(ps);
    return ps->top;//top為0則返回false,不為零返回true
}

//銷毀棧
void StackDestroy(ST* ps)
{
    assert(ps);
    free(ps->a);//將??臻g釋放掉
    //將棧結(jié)構(gòu)中的信息清空
    ps->capacity = 0;
    ps->top = 0;
    ps->a = NULL;
}

最后, 我們在tesst.c文件調(diào)用棧各個接口進(jìn)行測試,如下:

//test.c文件,用于測試
#include"Stack.h"

void test01()
{
    ST s1;
    //初始化
    StackInit(&s1);
    //求元素個數(shù)
    printf("入棧前棧的元素個數(shù)為:%d\n", StackSize(&s1));
    //入棧
    StackPush(&s1,1);
    StackPush(&s1, 2);
    StackPush(&s1, 3);
    StackPush(&s1, 4);
    printf("入棧后棧的元素個數(shù)為:%d\n", StackSize(&s1));
    //由于無法遍歷打印,我們就交替使用 求棧頂元素-出棧 來顯示棧中元素
    printf("棧中元素:> ");
    while (StackEmpty(&s1))//棧不為空則繼續(xù)
    {
        //求棧頂元素
        printf("%d ", StackTop(&s1));
        //出棧
        StackPop(&s1);
    }
    //全部出棧
    printf("\n全部出棧后棧的元素個數(shù)為:%d\n", StackSize(&s1));
    //銷毀
    StackDestroy(&s1);
}

int main()
{
    test01();
    return 0;
}

以下是測試的最終效果:

棧的結(jié)構(gòu)體定義,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,學(xué)習(xí),棧和隊(duì)列,Powered by 金山文檔

以上,就是本期的全部內(nèi)容啦??

制作不易,能否點(diǎn)個贊再走呢??文章來源地址http://www.zghlxwxcb.cn/news/detail-780035.html

到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】帶你深入理解棧的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包