數據結構與算法之如何基於順序存儲和鏈式存儲設計一個隊列

前言

  上一篇,我們學習了棧的結構,以及基於順序存儲鏈式存儲兩個不同角度如何設計一個棧,以及一些對的常規操作。
那麼棧本篇來看一下隊列的結構,以及如何基於順序存儲鏈式存儲兩個不同角度設計一個隊列

1.  隊列的結構

  隊列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作。

  和一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱爲隊尾,進行刪除操作的端稱爲隊頭。

隊列的示意圖:

隊列以順序存儲的方式存儲時:

假如長度爲 5 的隊列,初始狀態,空隊列如所示,frontrear指針均指向下標爲 0 的位置。然後依次入隊C1C2C3front指針依然指向下標爲 0 位置,而rear指針指向下標爲 3 的位置。然後依次C1C2出隊,移動front到 2 的位置,然後C4C5C6入隊,C3出隊,就會形成下面圖的形式


這時,rear後面沒有可以入隊的空間,由於隊列的規則,只能從一段入隊,導致front前面的位置無法存儲數據,而形成隊列已滿的形式,我們稱這種情況爲 假溢出

爲了防止 假溢出,我們可以設計一個循環隊列,如下圖:

我們可以犧牲一個存儲單元,來判斷是否滿對。

判斷空對:Q.front = Q.rear

判斷滿對:Q.front = (Q.rear + 1) % MaxSize

2.  順序存儲隊列的設計

2.1  順序存儲隊列的設計

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXSIZE 20  // 存儲空間初始化分配量

typedef int Status;
typedef int QElemType;

// 循環隊列的順序存儲結構
typedef struct {
    QElemType data[MAXSIZE];
    int front;  // 頭指針
    int rear;   // 尾指針,若隊列不空,指向隊列尾元素的下一個位置
}SqQueue;

判斷空對:Q.front = Q.rear

判斷滿對:Q.front = (Q.rear + 1) % MaxSize

2.2  順序存儲隊列的基本操作

  • 初始化一個空隊列
// 初始化一個空隊列
Status InitQueue(SqQueue *Q) {
    Q->front = 0;
    Q->rear  = 0;
    return OK;
}
  • 清空隊列
// 清空隊列
Status ClearQueue(SqQueue *Q) {
    Q->front = Q->rear = 0;
    return OK;
}
  • 判斷隊列是否爲空
Status QueueEmpty(SqQueue Q) {
    if (Q.front == Q.rear) { // 空隊列標記
        return TRUE;
    } else {
        return FALSE;
    }
}
  • 返回元素個數,即:隊列的當前長度
int QueueLength(SqQueue Q) {
    // + MAXSIZE,循環隊列,防止出現負數
    return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
  • 返回隊列Q的隊頭元素
Status GetHead(SqQueue Q, QElemType *e) {
    // 判斷隊列是否爲空
    if (Q.front == Q.rear) {
        return ERROR;
    }
    *e = Q.data[(Q.front)];
    return OK;
}
  • 入隊
Status EnQueue(SqQueue *Q, QElemType e) {
    // 判斷是否隊列已滿
    if ((Q->rear + 1) % MAXSIZE == Q->front) {
        printf("隊列已滿\n");
        return ERROR;
    }
    // 將元素賦值給隊尾
    Q->data[Q->rear] = e;
    // rear指針後移,若到最後則轉到數組頭部
    Q->rear = (Q->rear + 1)%MAXSIZE;
    return OK;
}
  • 出隊
Status DeQueue(SqQueue *Q, QElemType *e) {
    // 判斷是否爲空
    if (Q->rear == Q->front) {
        return ERROR;
    }
    // 隊頭元素賦值給e
    *e = Q->data[Q->front];
    
    //front 指針向後移動一位,若到最後則轉到數組頭部
    Q->front = (Q->front+1)%MAXSIZE;
    return OK;
}
  • 遍歷隊列元素
Status QueueTraverse(SqQueue Q) {
    int i;
    i = Q.front;
    while (i != Q.rear) {
        printf("%d  ", Q.data[i]);
        i = (i + 1)%MAXSIZE;
    }
    printf("\n");
//    int e;
//    for (i = Q.front; i<Q.rear; i++){
//        e = Q.data[i];
//        printf(" %d  ", e);
//    }
//    printf("\n");

    return OK;
}

3.  鏈式存儲隊列的設計

3.1  鏈式存儲隊列的設計

鏈式存儲的隊列,需要設計一個節點

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;
typedef int QElemType;

// 節點結構
typedef struct QNode {
    struct QNode *next;
    QElemType     data;
}QNode, *QueuePtr;

// 隊列的鏈表結構
typedef struct {
    QueuePtr front;  // 頭指針
    QueuePtr rear;   // 尾指針
}LinkQueue;

3.2  鏈式存儲隊列的基本操作

  • 初始化隊列
Status InitQueue(LinkQueue *Q) {
    
    // 頭尾指針指向新節點
    Q->front = Q->rear = (QueuePtr )malloc(sizeof(QNode));
    // 判斷是否創建成功
    if (!Q->front) {
        return ERROR;
    }
    // 頭結點的指針域置空
    Q->front->next = NULL;

    return OK;
}

  • 銷燬隊列
Status DestoryQueue(LinkQueue *Q) {
    // 遍歷節點,釋放
    while (Q->front) {
        // 尾指針已經失去意義,作爲臨時遍歷使用
        Q->rear = Q->front->next;
        free(Q->front);
        Q->front = Q->rear;
    }
    return OK;
}
  • 隊列置空
Status ClearQueue(LinkQueue *Q) {
    QueuePtr p,q;
    // 頭尾指針相等
    Q->rear = Q->front;
    p = Q->front->next;
    Q->front->next = NULL;
    
    while (p) {
        q = p;
        p = p->next;
        free(q);
    }
    return OK;
}
  • 判斷是否爲空,即判斷頭尾節點是否相等
Status QueueEmpty(LinkQueue Q){
    if (Q.front == Q.rear)
        return TRUE;
    else
        return FALSE;
}
  • 獲取長度
int QueueLength(LinkQueue Q) {
    int i = 0;
    QueuePtr p = Q.front;
    while (p != Q.rear) {
        i ++;
        p = p->next;
    }
    return i;
}

也可以在設計隊列的時候,加一個變量count入隊出隊+1-1,來計數。

  • 入隊 不用判斷是否滿隊
Status EnQueue(LinkQueue *Q, QElemType e) {
    // 創建新節點
    QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
    if (!s) {
        return ERROR;
    }
    
    s->data = e;
    s->next = NULL;
    
    // 出入隊尾
    Q->rear->next = s;
    
    //修改隊尾指針
    Q->rear = s;
    
    return OK;
}

  • 出隊 需要判斷是否爲空
Status DeQueue(LinkQueue *Q, QElemType *e) {

    QueuePtr p;
    // 判斷空
    if (Q->front == Q->rear) {
        return ERROR;
    }
    
    p = Q->front->next;
    *e= p->data;
    
    Q->front->next = p->next;
    
    //若隊頭就是隊尾,則刪除後將rear指向頭結點.
    if(Q->rear == p) Q->rear = Q->front;
    
    free(p);
    return OK;
}

  • 獲取頭元素
Status GetHead(LinkQueue Q,QElemType *e) {
    //隊列非空
    if (Q.front != Q.rear) {
        //返回隊頭元素的值,隊頭指針不變
        *e =  Q.front->next->data;
        return TRUE;
    }
    
    return  FALSE;
}
  • 遍歷元素
Status QueueTraverse(LinkQueue Q){
    
    QueuePtr p;
    p = Q.front->next;
    while (p) {
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}
···
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章