数据结构:栈与队列核心总结

37 阅读3分钟

栈(Stack)

栈的定义

  • 栈是 后进先出(LIFO) 的数据结构
  • 栈操作核心:Push、Pop、Top(取栈顶)、判空

顺序栈(顺序存储)

#define MAXSIZE 100

typedef struct {
    int data[MAXSIZE];
    int top;  // 栈顶下标,初始化为 -1
} SqStack;

// 初始化
void InitStack(SqStack &s) {
    s.top = -1;
}

// 判空
bool IsEmpty(SqStack &s) {
    return s.top == -1;
}

// 入栈
bool Push(SqStack &s, int x) {
    if (s.top == MAXSIZE - 1) return false;  // 栈满
    s.data[++s.top] = x;
    return true;
}

// 出栈
bool Pop(SqStack &s, int &x) {
    if (IsEmpty(s)) return false;
    x = s.data[s.top--];
    return true;
}

// 取栈顶
bool GetTop(SqStack &s, int &x) {
    if (IsEmpty(s)) return false;
    x = s.data[s.top];
    return true;
}

链式栈(链栈)

typedef struct StackNode {
    int data;
    struct StackNode *next;
} StackNode;

typedef struct {
    StackNode *top; // 栈顶指针
} LinkStack;

// 初始化
void InitStack(LinkStack &s) {
    s.top = NULL;
}

// 判空
bool IsEmpty(LinkStack &s) {
    return s.top == NULL;
}

// 入栈
bool Push(LinkStack &s, int x) {
    StackNode *p = (StackNode *)malloc(sizeof(StackNode));
    if (!p) return false; // 内存分配失败
    p->data = x;
    p->next = s.top;
    s.top = p;
    return true;
}

// 出栈
bool Pop(LinkStack &s, int &x) {
    if (IsEmpty(s)) return false;
    StackNode *p = s.top;
    x = p->data;
    s.top = p->next;
    free(p);
    return true;
}

顺序栈 vs 链式栈

特性顺序栈(数组实现)链式栈(链表实现)
存储结构数组链表节点
栈顶操作用 top 下标操作数组用 top 指针操作链表头
空间容量固定,需要事先定义 MAXSIZE动态分配,不受数组长度限制
判空top == -1top == NULL
判满top == MAXSIZE - 1不需要判断(除非内存耗尽)
入栈操作top++,数组赋值头插法,动态分配节点
出栈操作top--,返回数组元素删除栈顶结点,释放内存
时间复杂度O(1)O(1)
空间利用可能浪费数组空间不会浪费空间,但每个节点有指针开销
优点内存连续,访问快动态扩展,容量灵活
缺点容量固定,可能溢出节点额外指针开销,操作稍慢

队列(Queue)

队列的定义

  • 队列是 先进先出(FIFO) 的数据结构
  • 核心操作:入队(EnQueue)、出队(DeQueue)、判空、判满

顺序队列

#define MAXSIZE 100
typedef struct {
    int data[MAXSIZE];
    int front; // 队头下标
    int rear;  // 队尾下标(下一个插入位置)
} SqQueue;

void InitQueue(SqQueue &q) { q.front = q.rear = 0; }

bool IsEmpty(SqQueue &q) { return q.front == q.rear; }

bool EnQueue(SqQueue &q, int x) {
    if (q.rear == MAXSIZE) return false; // 队满
    q.data[q.rear++] = x;
    return true;
}

bool DeQueue(SqQueue &q, int &x) {
    if (IsEmpty(q)) return false;
    x = q.data[q.front++];
    return true;
}

链式队列

struct QNode {
    int data;
    QNode *next;
};

struct LinkQueue {
    QNode *front;
    QNode *rear;
};

void InitQueue(LinkQueue &q) { q.front = q.rear = NULL; }

bool IsEmpty(LinkQueue &q) { return q.front == NULL; }

bool EnQueue(LinkQueue &q, int x) {
    QNode *p = (QNode *)malloc(sizeof(QNode));
    p->data = x;
    p->next = NULL;
    if (IsEmpty(q)) q.front = q.rear = p;
    else { q.rear->next = p; q.rear = p; }
    return true;
}

bool DeQueue(LinkQueue &q, int &x) {
    if (IsEmpty(q)) return false;
    QNode *p = q.front;
    x = p->data;
    q.front = q.front->next;
    if (q.front == NULL) q.rear = NULL;
    free(p);
    return true;
}

顺序循环队列

#define MAXSIZE 5

typedef struct {
    int data[MAXSIZE];
    int front;  // 队头下标
    int rear;   // 队尾下标(下一个插入位置)
} CirQueue;

// 初始化
void InitQueue(CirQueue &q) {
    q.front = q.rear = 0;
}

// 判空
bool IsEmpty(CirQueue &q) {
    return q.front == q.rear;
}

// 入队
bool EnQueue(CirQueue &q, int x) {
    if ((q.rear + 1) % MAXSIZE == q.front) return false; // 队满
    q.data[q.rear] = x;
    q.rear = (q.rear + 1) % MAXSIZE;
    return true;
}

// 出队
bool DeQueue(CirQueue &q, int &x) {
    if (IsEmpty(q)) return false;  // 队空
    x = q.data[q.front];
    q.front = (q.front + 1) % MAXSIZE;
    return true;
}

链式循环队列

struct QNode {
    int data;
    QNode* next;
};

struct LinkCirQueue {
    QNode* front;
    QNode* rear;
};

// 初始化
void InitQueue(LinkCirQueue &q) {
    q.front = q.rear = NULL;
}

// 判空
bool IsEmpty(LinkCirQueue &q) {
    return q.front == NULL;
}

// 入队
bool EnQueue(LinkCirQueue &q, int x) {
    QNode* node = new QNode;
    node->data = x;
    if (IsEmpty(q)) {
        node->next = node; // 自己循环
        q.front = q.rear = node;
    } else {
        node->next = q.front;  // 新结点指向队头
        q.rear->next = node;   // 原队尾指向新结点
        q.rear = node;         // 更新队尾
    }
    return true;
}

// 出队
bool DeQueue(LinkCirQueue &q, int &x) {
    if (IsEmpty(q)) return false;
    x = q.front->data;
    if (q.front == q.rear) { // 只有一个元素
        free(q.front);
        q.front = q.rear = NULL;
    } else {
        QNode* temp = q.front;
        q.front = q.front->next;  // 更新队头
        q.rear->next = q.front;   // 队尾指向新队头
        free(temp);
    }
    return true;
}

顺序 vs 链式循环队列

特性顺序循环队列链式循环队列
存储结构数组链表
容量限制固定容量动态容量
判空方法front == rearfront == NULL
判满方法(rear + 1) % MAXSIZE == front(牺牲一个空间)
或 count == MAXSIZE(全空间用)
不用判满,动态分配
优点内存连续、访问快动态、扩展性好
缺点容量固定,满了需要扩容链表占用额外指针空间,操作稍慢