數據結構—棧和隊列

前言

本章節開始數據結構第二篇,棧和隊列:
棧:算法

  • 棧的存儲結構
  • 棧的基本操做

隊列:數組

  • 隊列的存儲結構
  • 隊列的基本操做

咱們把相似於彈夾那種先進後出的數據結構稱爲棧,棧是限定僅在表尾進行插入和刪除操做的線性表,咱們把容許插入和刪除的一端稱爲棧頂,另外一端稱爲棧底,不含任何數據元素的棧稱爲空棧,棧又稱後進後出的線性表,簡稱LIFO結構。數據結構

棧首先是一個線性表,也就是說,棧元素具備線性關係,即前驅後繼關係,只不過它是一種特殊的線性表而已。app

棧的特殊之處在於限制了這個線性表的插入和刪除位置,它始終只在棧頂進行。這也就使得:棧底是固定的,最早進棧的只能在棧底。ui

棧的插入操做,叫作進棧;棧的刪除操做叫作出棧。spa

1.棧的存儲結構

用來存放棧的數據元素對應的數據存儲結構稱爲棧的存儲結構。.net

1.1棧的順序存儲結構

棧是線性表的特例,因此棧的順序存儲結構其實就是線性表順序存儲結構的簡稱,咱們簡稱爲順序棧。線性表是用數組來實現的,對於棧這種只能一頭插入刪除的線性表來講,用數組下標爲0(棧底不變,只須要跟蹤棧頂的變化便可)的一端做爲棧底比較合適。
指針

順序棧定義以下:code

typedef struct
{

    int data[maxsize];    //定義一個數組大小爲maxsize的數組,用來存放棧中數據元素
    int top;              //棧頂指針
}SqStack;                 //順序棧定義
複製代碼

1.2棧的鏈式存儲結構

棧頂放在單鏈表的頭部,用鏈表來存儲棧的的數據結構稱爲鏈棧。
orm

鏈棧結點定義以下:

typedef struct LNode
{

    int data;                //數據域
    struct LNode *next;      //指針域
}LNode;                      //鏈棧結點
複製代碼

2.棧的操做

2.1順序棧的操做

對於順序棧,一共有4個要素,包括兩個特殊狀態和兩個操做。

特殊狀態:
1)棧空狀態:st.top == -1,也有的用st.top = 0表示棧空,這個時候棧頂位置爲0。
2)棧滿狀態:st.top == maxsize-1表示棧滿。maxsize爲棧中最大元素個數,maxsize-1爲棧滿時棧頂元素在數組中的位置,因數組位置是從0開始的。

操做:
順序棧的進棧和出棧操做都是在棧頂進行的,因此只須要更改棧頂位置便可達到進棧和出棧的目的。

1)初始化棧:

void initStack(SqStack &st)    //初始化棧
{
    st.top = -1;               //棧頂指針設置爲-1
}
複製代碼

2)進棧操做:

int push(SqStack &st,int x)
{
    if(st.top == maxsize-1)    //判斷棧是否滿,若是滿,則不能進棧
        return 0;
    ++(st.top);                //棧頂指針位置加1
    st.data[st.top] = x        //x進棧,放在st.top位置
        return 1;
}
複製代碼

3)出棧操做:
出棧與進棧是相對應的操做

int push(SqStack &st,int x)
{
    if(st.top == -1)           //判斷棧是否爲空,若是空,則不能進行出棧
        return 0;
    x = st.data[st.top]     //先把棧頂元素取出來
    --(st.top);                 //棧頂指針位置減1
    return 1;
}
複製代碼

4)簡化版的操做:

/*初始化棧*/
int stack[maxsize];
int top = -1;

/*元素x進棧*/
stack[++top] = x

/*元素x出棧*/
x = stack[top--]

/*注意++top和top++的區別*/
top = 1
a = ++top
b = top++

a = 2
b = 1
複製代碼

2.2鏈棧的操做

與順序棧對應,鏈棧也有4個元素,包括兩個狀態和兩個操做。

狀態:
1)棧空:lst -> next == NULL,即棧沒有後繼結點時,棧爲空。
2)棧滿:若是存儲空間無限大的話,不會存在棧滿的狀況。

操做:
鏈棧的進棧就是頭插法創建鏈表的插入操做;出棧就是單鏈表的刪除操做。

鏈棧的插入操做
鏈棧的插入操做

棧的刪除操做
棧的刪除操做

1)鏈棧初始化:

void initStack(LNode *&lst)
{
    lst = (LNode*)malloc(sizeof(LNode));    //製造一個頭結點
    lst -> next = NULL;                     //初始頭結點指向爲NULL
}
複製代碼

2)進棧:

void push(LNode *lst,int x)
{
    LNode *p;
    p = (LNode*)malloc(sizeof(LNode));    //爲進棧元素申請結點空間
    p -> next =NULL;                      //初始化結點不指向任何元素

    /*進棧,至關於鏈表的頭插法*/
    p -> data = x;    //將x賦值給p結點的值域
    p -> next = lst -> next;    //p指針指向原lst指向的結點
    lst -> next = p;            //lst指向結點p
}
複製代碼

3)出棧:

int pop(LNode *lst,int &x)
{
    LNode *p;
    if(lst -> next == NULL)    //棧空則不能出棧,返回0;而棧不會滿,因此在進棧的時候未做判斷
        return 0;
    /*出棧,至關於鏈表的刪除結點*/
    p = lst -> next;
    x = p -> data;
    lst -> next = p -> next;
    free(p);
    return 1;
}
複製代碼

4)簡化版操做:

/*元素(指針p所指)進棧操做*/
/*相似於頭插法創建鏈表*/
p -> next = lst -> next;    //將空棧的頭結點指向p
lst -> next = p;            //將指針p指向空棧頭結點


/*出棧操做(出棧元素保存在x中)*/
/*相似於單鏈表的刪除操做*/
p = lst -> next;
x = p -> data;
lst -> next = p -> next;
free(p);
複製代碼

隊列:

隊列是隻容許在一端進行插入操做,而在另外一端進行刪除操做的線性表,隊列是一種先進先出的線性表,簡稱FIFO,容許插入的一端稱爲隊尾(Rear),容許刪除的一端稱爲隊頭(Front)。向隊中插入元素稱爲進隊,新元素進隊後成爲新的隊尾元素;向隊中刪除元素稱爲出隊,元素出隊後,其後繼元素就成爲新的隊頭元素。

1.隊列的存儲結構

用來存儲隊列數據元素的數據結構。

1.1隊列的順序存儲結構:

使用順序表存儲隊列時,隊列元素的出隊是在隊頭,即下標爲0的地方,當有元素出隊時,出隊元素後面的全部元素都須要向前移動,保證隊列的隊頭始終處在下標爲0的位置,此時會大大增長時間複雜度。


用順序表來存儲隊列元素的數據結構稱爲隊列的順序存儲結構,定義以下:

typedef struct
{

    int data[maxsize];        //定義數組
    int front;                //隊首指針
    int rear;                 //隊尾指針
}SqQuene;                     //順序隊列定義
複製代碼

1.2隊列的鏈式存儲結構:

用鏈表來存儲隊列元素的數據結構稱爲隊列的鏈式存儲結構,定義以下:


隊列結點類型定義:

typedef struct QNode
{

    int data;                //數據域
    struct QNode *next;      //指針域
}QNode;                      //隊結點類型定義
複製代碼

鏈隊類型定義:

typedef struct
{

    QNode *front;        //隊頭指針
    QNode *rear;         //隊尾指針
}LiQuene;                //鏈隊類型定義
複製代碼

2.隊列操做

2.1循環隊列

由於順序隊列出隊時時間複雜度較高,有問題老是要解決的,爲何必定要讓隊頭出如今下標爲0的位置呢?因此有人提出了不去限制隊列元素必須存儲在數組的前n個單元這一條件,這樣隊頭元素就不須要必定在下標爲0的位置。可是隨着隊列元素的出隊,隊頭指針在向後移動,假設隊尾指針已經在maxsize-1的位置,這個時候雖然隊列還有存儲空間,可是隊尾已經沒法進隊了,好比下圖這樣:


雖然下標爲0和1的位置處還有空間,可是隊尾已經沒法再有新元素進隊,咱們把這種狀況稱爲「假溢出」,爲了解決這種假溢出的問題,就提出了循環隊列的概念,讓隊列的頭尾進行相連,這種頭尾相連的順序存儲結構稱爲循環隊列。

循環隊列須要損失必定的空白,這樣只有在隊空的時候纔會出現front=rear。

循環隊列的要素:

兩個狀態:
隊空狀態:

qu.rear = qu.front
複製代碼

隊滿狀態:

(qu.rear+1)%maxsize == qu.front
複製代碼

兩個操做:
元素x進隊操做(移動隊尾指針)

qu.rear = (qu.rear+1)%maxSize;
qu.data[qu.rear] = x;
複製代碼

元素x出隊操做(移動隊頭指針)

qu.front = (qu.front+1)%maxSize;
x = qu.data[qu.front];
複製代碼

初始化隊列算法:

void initQueue(SqQueue &qu)
{
    qu.front = qu.rear = 0;隊首和隊尾指針重合,而且指向0
}
複製代碼

進隊算法:

int enQueue(SqQueue &qu,int x)
{
    if ((qu.rear + 1) % maxSize == qu.front)    //隊滿的判斷條件,若是隊滿則不能進隊,返回0
        return 0;
    qu.rear = (qu.reat+1)%maxSize;              //若隊不滿,先移動隊尾指針
    qu.data[qu.rear] = x;                       //元素x進隊
    return 1;
}
複製代碼

出隊算法:

int enQueue(SqQueue &qu,int &x)
{
    if (qu.rear == qu.front)    //隊空的判斷條件,若是隊空則不能出隊,返回0
        return 0;
    qu.front = (qu.front+1)%maxSize;              //若隊不空,先移動隊首指針
    x = qu.data[qu.front] ;                       //元素x出隊
    return 1;
}
複製代碼

2.2鏈隊:

鏈隊就是採用鏈式存儲結構存儲隊列。鏈隊的四個要素:隊空和隊滿,元素進隊和出隊操做。


隊空狀態:

lqu -> rear == NULLor lqu -> front == NULL
複製代碼

隊滿狀態:
通常來講不存在隊滿的狀況,只要內存足夠大。

元素進隊操做(指針p指向進隊元素)

lqu -> rear -> next = p;
lqu -> rear = p;
複製代碼

元素出隊操做(x存儲出隊元素)

p = lqu -> front;
lqu -> front = p -> next;
x = p -> data;
free(p);
複製代碼

初始化鏈隊算法

void initQueue(LiQuene *&lqu)
{
    lqu = (LiQueue*)malloc(sizeof(LiQueue));
    lqu -> front = lqu -> rear = NULL;
}
複製代碼

判斷隊空算法

int isQueueEmpty(LiQueue *lqu)
{
    if(lqu -> rear == NULL || lqu -> front == NULL)
        return 1;
    else
        return 0;
}
複製代碼

入隊算法

void enQueue(LiQueue *lqu,int x)
{
    QNode *p;
    p = (QNode*)malloc(sizeof(QNode));
    p -> data = x;
    p -> next =NULL;
    if(lqu -> rear == NULL)
        lqu -> front = lqu -> rear = p;    //若是隊列爲空,則新結點既是隊尾結點也是隊首結點
    else
    {
        lqu -> rear -> next = p;           //將新結點連接到隊尾,rear指向該結點
        lqu -> rear = p;
    } 
}
複製代碼

出隊算法

int deQueue(LiQueue *lqu,int &x)
{
    QNode *p;
    if(lqu -> rear == NULL)    //判斷隊空,若是爲空,則不能出隊
        return 0;
    else
        p = lqu -> front;
    if(lqu -> front == lqu -> rear)    //隊列中只有一個結點時的出隊操做
        lqu -> front = lqu -> rear =NULL
    else
        lqu -> front = lqu -> front -> next;
    x = p -> data;
    free(q);
    return 1;
}
複製代碼
相關文章
相關標籤/搜索