堆栈与队列

552 阅读6分钟

一、数据结构的基本概念

1、逻辑结构,数据间的逻辑关系

(1) 集合机构: 集  结构中的数据元素除了“属于同一个集合”之外,没有任何关系

(2) 线性结构: 表  结构中的数据元素具有一对一的前后关系

(3) 树型结构: 树  结构中的数据元素具有一对多的父子关系

(4) 网状结构: 图  结构中的数据元素具有多对多的交叉映射关系

2、物理结构, 数据的存储

(1) 顺序结构: 结构中的数据元素存放在一段连续的地址空间中,随机访问方便,空间利用率低,插入删除不便.

(2) 链式结构: 结构中的数据元素存放在彼此独立的地址空间中, 每个独立的空间被称为节点,节点除了保存数据以外,还保存另一个或几个相关节点的地址, 空间利用率高, 插入删除方便, 随机访问不便.

顺序结构和链式结构互补, 优点和缺点相反

3、逻辑结构与物理结构关系

顺序数组顺序树复合
链式链表链式树

二、数据结构的基本运算

1、创建/销毁

2、插入/删除

3、获取/修改

4、排序/查找

三、数据结构的基本实现

1、堆栈

(1) 基本特征: 后进先出 (像个一边开口的桶)

(2) 基本操作: 压入(push)、弹出(pop)

(3) 实现要点: 初始化空间、栈顶指针、判空判满

范例:

A、用数组方式实现
// .h
// 基于数组的堆栈
#ifndef _SA_H
#define _SA_H

#include <sys/types.h>
using namespace std;

// 堆栈
typedef struct Stack
{
    int*   arr;  // 数组
    size_t cap;  // 容量
    size_t top;  // 栈顶
} STACK;


// 分配内存并初始化为空堆栈
void stack_init(STACK* stack, size_t cap);

// 释放内存并恢复到初始状态
void stack_deinit(STACK* stack);

// 判断是否满
int stack_full(STACK* stack);

// 判断是否为空
int stack_empty(STACK* stack);

// 压入
void stack_push(STACK* stack, int data);

// 弹出
int stack_pop(STACK* stack);

// 栈顶
int stack_top(STACK* stack);

// 数量
int stack_size(STACK* stack);

#endif
// .c
#include <stdlib.h>
#include "textC.h"

// 分配内存并初始化为空堆栈
void stack_init(STACK* stack, size_t cap)
{
    stack->arr = (int*)malloc (cap * sizeof(int));
    stack->cap = cap;
    stack->top = 0;
}

// 释放内存并恢复到初始状态
void stack_deinit(STACK* stack)
{
    free(stack->arr);
    stack->arr = NULL;
    stack->cap = 0;
    stack->top = 0;
}

// 判断是否满
int stack_full(STACK* stack)
{
    return stack->top >= stack->cap;
}

// 判断是否为空
int stack_empty(STACK* stack)
{
    return ! stack->top;
}

// 压入
void stack_push(STACK* stack, int data)
{
    stack->arr[stack->top++] = data;
}

// 弹出
int stack_pop(STACK* stack)
{
    return stack->arr[--stack->top];
}

// 栈顶
int stack_top(STACK* stack)
{
    return stack->arr[stack->top - 1];
}

// 数量
int stack_size(STACK* stack)
{
    return stack->top;
}


int main(void)
{
    STACK stack;
    stack_init(&stack, 10);

    int i = 0;

    while(!stack_full(&stack))
    {
        stack_push(&stack, i++);
    }

    printf("size = %d\n", stack_size(&stack));

    while(!stack_empty(&stack))
    {
        printf("%d\n", stack_pop(&stack));
    }
    stack_deinit(&stack);

return 0;
}
B、用链表方式实现
// .h
// 基于数组的堆栈
#ifndef _SL_H
#define _SL_H

#include <sys/types.h>

// 节点
typedef struct StackNode {
    int data;  // 数据
    struct StackNode* next;  // 后指针
} STACK_NODE;

// 堆栈
typedef struct Stack
{
    STACK_NODE* top;  // 栈顶
} STACK;


// 初始化为空堆栈
void stack_init(STACK* stack);

// 释放剩余节点并恢复到初始状态
void stack_deinit(STACK* stack);

// 判断是否为空
int stack_empty(STACK* stack);

// 压入
void stack_push(STACK* stack, int data);

// 弹出
int stack_pop(STACK* stack);

// 栈顶
int stack_top(STACK* stack);

// 数量
int stack_size(STACK* stack);

#endif
// .c

#include <stdlib.h>
#include "textC.h"


// 创建节点
static STACK_NODE* create_node(int data, STACK_NODE* next) 
{
    STACK_NODE* node = (STACK_NODE*)malloc (sizeof (STACK_NODE));
    node->data = data;
    node->next = next;
    return node;
}

// 销毁节点
static STACK_NODE* destroy_node(STACK_NODE* node)
{
    STACK_NODE* next = node->next;
    free(node);
    return next;
}

// 初始化为空堆栈
void stack_init(STACK* stack)
{
    stack->top = NULL;
}

// 释放剩余节点并恢复到初始状态
void stack_deinit(STACK* stack)
{
    while (stack->top)
    {
            stack->top = destroy_node(stack->top);
    }
}

// 判断是否为空
int stack_empty(STACK* stack)
{
    return ! stack->top;
}

// 压入
void stack_push(STACK* stack, int data)
{
    stack->top = create_node(data, stack->top);
}

// 弹出
int stack_pop(STACK* stack)
{
    int data = stack->top->data;
    stack->top = destroy_node(stack->top);
    return data;
}

// 栈顶
int stack_top(STACK* stack)
{
    return stack->top->data;
}

// 数量
int stack_size(STACK* stack)
{
    int count = 0;
    STACK_NODE* node = NULL;
    for (node = stack->top; node; node = node->next) {
        ++count;  // 前++ 比 后++ 效率高, 因为后运算要返回历史值,需要拷贝副本.而前++只需返回值本身
    }
    return count;
}


int main(void)
{
    STACK stack;
    stack_init(&stack);

    int i = 0;

    while(i < 10)
    {
        stack_push(&stack, i++);
    }

    printf("size = %d\n", stack_size(&stack));

    while(!stack_empty(&stack))
    {
        printf("%d\n", stack_pop(&stack));
    }
    stack_deinit(&stack);

return 0;
}
C、输入一个整数和进制数, 以该进制打印该数
#include <stdio.h>
#include "cstack.h"

// 输入一个整数和进制数, 以该进制打印该数

int main(void)
{
    printf("please enter a num : ");
    unsigned int num;
    scanf("%d", &num);

    printf("please enter 进制 : ");
    int base;
    scanf("%d", &base);

    STACK stack;
    stack_init(&stack);

    do {
        stack_push(&stack, num % base);
    } while( num /= base );

    printf("The result is : \n");

    while (! stack_empty(&stack)) 
    {
        int digit = stack_pop(&stack);
        if (digit >= 10) 
        {
                printf("%c", digit-10+'A');
        }
        else {
                printf("%d", digit);
        }

    }
    printf("\n");
    stack_deinit(&stack);

    return 0;
}

2、队列

(1) 基本特征: 先进先出(FIFO) (像管道)

(2) 基本操作: 压入(push)、弹出(pop)

(3) 实现要点: 初始化空间、前指针front弹出, 后指针rear压入、循环使用、判空判满

范例:

A、用数组方式实现
// .h
// 基于数组的队列

#ifndef _Q_H
#define _Q_H

#include <sys/types.h>

typedef struct Queue
{
    int* arr;  // 数组
    size_t cap;  // 容量
    size_t front;   // 前端, 弹出
    size_t rear;  // 后端, 压入
    size_t size;  // 数量
} QUEUE;


// 分配内存并初始化为空队列
void queue_init(QUEUE* queue, size_t cap);

// 释放内存并恢复到初始状态
void queue_deinit(QUEUE* queue);

// 判断是否满
int queue_full(QUEUE* queue);

// 判断是否空
int queue_empty(QUEUE* queue);

// 压入
void queue_push(QUEUE* queue, int data);

// 弹出
int queue_pop(QUEUE* queue);

// 队首
int queue_front(QUEUE* queue);

// 数量
size_t queue_size(QUEUE* queue);

#endif
// .c
#include <stdio.h>
#include <stdlib.h>
#include "cqueue.h"

// 分配内存并初始化为空队列
void queue_init(QUEUE* queue, size_t cap)
{
    queue->arr = (int *)malloc(sizeof (int) * cap);
    queue->cap = cap;
    queue->front = queue->rear = 0;
    queue->size = 0;
}

// 释放内存并恢复到初始状态
void queue_deinit(QUEUE* queue)
{
    free(queue->arr);
    queue->arr = NULL;
    queue->cap = 0;
    queue->front = queue->rear = 0;
    queue->size = 0;
}

// 判断是否满
int queue_full(QUEUE* queue)
{
    return queue->size >= queue->cap;
}

// 判断是否空
int queue_empty(QUEUE* queue)
{
    return ! queue->size;
}

// 压入
void queue_push(QUEUE* queue, int data)
{
    if (queue->rear >= queue->cap) {
        queue->rear = 0;
    }
    ++queue->size;
    queue->arr[queue->rear++] = data;
}

// 弹出
int queue_pop(QUEUE* queue)
{
    if (queue->front >= queue->cap) {
        queue->front = 0;
    }
    --queue->size;
    return queue->arr[queue->front++];
}

// 队首
int queue_front(QUEUE* queue)
{
    if (queue->front >= queue->cap) {
        queue->front = 0;
    }
    return queue->arr[queue->front];
}

// 数量
size_t queue_size(QUEUE* queue)
{
    return queue->size;
}
#include <stdio.h>
#include "cqueue.h"

int main(void)
{
    QUEUE queue;
    queue_init(&queue, 4);
    queue_push(&queue, 10);
    queue_push(&queue, 20);
    queue_push(&queue, 30);
    queue_push(&queue, 40);

    printf("%s\n", queue_full(&queue) ? "满" : "不满");

    printf("%d\n", queue_pop(&queue));  // 10
    printf("%d\n", queue_pop(&queue));  // 20

    queue_push(&queue, 50);
    queue_push(&queue, 60);

    printf("%d\n", queue_pop(&queue));  // 30
    printf("%d\n", queue_pop(&queue));  // 40
    printf("%d\n", queue_pop(&queue));  // 50
    printf("%d\n", queue_pop(&queue));  // 60

    printf("%s\n", queue_empty(&queue) ? "空" : "不空");

    queue_deinit(&queue);

    return 0;
}
B、用链表方式实现
// .h
#ifndef _Q_H
#define _Q_H

#include <sys/types.h>

// 基于链表的队列

typedef struct QueueNode
{
    int data;  // 数据
    struct QueueNode* next;  // 后指针
} QUEUE_NODE;

typedef struct Queue
{
    QUEUE_NODE* front;  // 前端 弹出
    QUEUE_NODE* rear;   // 后端 压入
} QUEUE;


// 初始化为空队列
void queue_init(QUEUE* queue);

// 释放内剩余节点并恢复到初始状态
void queue_deinit(QUEUE* queue);

// 判断是否空
int queue_empty(QUEUE* queue);

// 压入
void queue_push(QUEUE* queue, int data);

// 弹出
int queue_pop(QUEUE* queue);

// 队首
int queue_front(QUEUE* queue);

// 数量
size_t queue_size(QUEUE* queue);

#endif
// .c
#include <stdio.h>
#include <stdlib.h>
#include "cqueue.h"

// 创建节点
static QUEUE_NODE* create_node(int data)
{
    QUEUE_NODE* node = (QUEUE_NODE*)malloc(sizeof(QUEUE_NODE));
    node->data = data;
    node->next = NULL;
    return node;
}

// 销毁节点
static QUEUE_NODE* destroy_node(QUEUE_NODE* node)
{
    QUEUE_NODE* next = node->next;
    free(node);
    return next;
}

// 分配内存并初始化为空队列
void queue_init(QUEUE* queue)
{
    queue->front = queue->rear = NULL;
}

// 释放内存并恢复到初始状态
void queue_deinit(QUEUE* queue)
{
    while (queue->front) {
        queue->front = destroy_node(queue->front);
    }
    queue->rear = NULL;
}

// 判断是否空
int queue_empty(QUEUE* queue)
{
    return ! queue->front && ! queue->rear;
}

// 压入
void queue_push(QUEUE* queue, int data)
{
    QUEUE_NODE* node = create_node(data);
    if (queue->rear) {
        queue->rear->next = node;
    }
    else {
        queue->front = node;
    }
    queue->rear = node;
}

// 弹出
int queue_pop(QUEUE* queue)
{
    int data = queue->front->data;
    queue->front = destroy_node(queue->front);
    if (!queue->front) {
        queue->rear = NULL;
    }
    return data;
}

// 队首
int queue_front(QUEUE* queue)
{
    return queue->front->data;
}

// 数量
size_t queue_size(QUEUE* queue)
{
    size_t size = 0;
    QUEUE_NODE* node = NULL;
    for (node = queue->front; node; node = node->next) {
        ++size;
    }

    return size;
}
#include <stdio.h>
#include "cqueue.h"

int main(void)
{
    QUEUE queue;
    queue_init(&queue);
    for (int i = 0; i < 10; i++) {
        queue_push(&queue, i*10);	
    }

    printf("长度 = %zu\n", queue_size(&queue));  // 10

    while (! queue_empty(&queue)) {
        printf("%d\n", queue_pop(&queue));
    }

    queue_deinit(&queue);

    return 0;
}
C、用堆栈的方式实现队列

创建两个堆栈, 一个用来输入, 一个用来输出. 将输入栈pop到输出栈. 最后再pop输出栈.  就可以实现队列的效果

// .h
// 基于堆栈的队列

#ifndef _SQ_H
#define _SQ_H

#include "cstack.h"

typedef struct Queue
{
    STACK input;
    STACK output;
} QUEUE;


// 初始化为空队列
void queue_init(QUEUE* queue);

// 释放内剩余节点并恢复到初始状态
void queue_deinit(QUEUE* queue);

// 判断是否空
int queue_empty(QUEUE* queue);

// 压入
void queue_push(QUEUE* queue, int data);

// 弹出
int queue_pop(QUEUE* queue);

// 队首
int queue_front(QUEUE* queue);

// 数量
size_t queue_size(QUEUE* queue);

#endif
// .c
#include <stdio.h>
#include <stdlib.h>
#include "cstack_queue.h"

// 分配内存并初始化为空队列
void queue_init(QUEUE* queue)
{
    stack_init(&queue->input);
    stack_init(&queue->output);
}

// 释放内存并恢复到初始状态
void queue_deinit(QUEUE* queue)
{
    stack_deinit(&queue->input);
    stack_deinit(&queue->output);
}

// 判断是否空
int queue_empty(QUEUE* queue)
{
    return stack_empty(&queue->input) && stack_empty(&queue->output);
}

// 压入
void queue_push(QUEUE* queue, int data)
{
    stack_push(&queue->input, data);
}

// 弹出
int queue_pop(QUEUE* queue)
{
    if (stack_empty(&queue->output)) {
        while(!stack_empty(&queue->input)) {
            stack_push(&queue->output, stack_pop(&queue->input));
        }
    }
    return stack_pop(&queue->output);
}

// 队首
int queue_front(QUEUE* queue)
{
    if (stack_empty(&queue->output)) {
        while(!stack_empty(&queue->input)) {
            stack_push(&queue->output, stack_pop(&queue->input));
        }
    }
    return stack_top(&queue->output);
}

// 数量
size_t queue_size(QUEUE* queue)
{
    return stack_size(&queue->input) + stack_size(&queue->output);
}
#include <stdio.h>
#include "cstack_queue.h"

int main(void)
{
    QUEUE queue;
    queue_init(&queue);

    int i;
    for (i = 0; i < 10; i++) {
        queue_push(&queue, i);
    }

    printf("长度 = %zu\n", queue_size(&queue));

    printf("%d\n", queue_pop(&queue));
    printf("%d\n", queue_pop(&queue));
    printf("%d\n", queue_pop(&queue));

    for (; i < 15; i++) {
        queue_push(&queue, i);
    }

    while (! queue_empty(&queue)) {
        printf("%d\n", queue_pop(&queue));
    }

    queue_deinit(&queue);

    return 0;
}