参考: 里科《C和指针》- 经典抽象数据类型
本章涉及堆栈、队列、二叉搜索树、泛型等,代码太多,应该参考leetcode题目。本文只包含栈的数组实现(静态数组和动态数组)和链表实现,以及队列的数组实现。
堆栈 stack
1)使用静态数组实现堆栈
- 把不属于外部接口的内容都声明为static,可以防止用户越界访问。
- 使用assert防止非法操作,针对的是用户可以自己检查的内容
- 使用is_full和is_empty是为了之后可以修改测试空堆栈和满堆栈的情况
/*
* 堆栈的接口 stack.h
*/
#define STACK_TYPE int
/* 把一个新值压到堆栈中 */
void push(STACK_TYPE value);
/* 从堆栈中弹出一个值,并将其丢弃 */
void pop(void);
/* 返回栈顶元素,但不对堆栈进行修改*/
STACK_TYPE top(void);
/* 如果堆栈为空,返回TRUE,否则FALSE */
int is_empty(void);
/* 如果堆栈已满,返回TRUE,否则FALSE */
int is_full(void);
/* ************************** */
/* 用一个静态数组实现堆栈,数组的长度只能通过修改#define定义,并重新编译实现 */
#include "stack.h"
#include <assert.h>
/* 堆栈中值数量的最大限制 */
#define STACK_SIZE 100
/* 存储堆栈中值的数组和一个指向堆栈顶部元素的指针 */
static STACK_TYPE stack[ STACK_SIZE ];
static int top_element = -1;
/* 把一个新值压到堆栈中 */
void push(STACK_TYPE value) {
assert( !is_full() );
top_element += 1;
stack[top_element] = value;
}
/* 从堆栈中弹出一个值,并将其丢弃 */
void pop(void) {
assert( !is_empty() );
top_element -= 1;
}
/* 返回栈顶元素,但不对堆栈进行修改*/
STACK_TYPE top(void) {
assert( !is_empty() );
return stack[top_element];
}
/* 如果堆栈为空,返回TRUE,否则FALSE */
int is_empty(void) {
return top_element == -1;
}
/* 如果堆栈已满,返回TRUE,否则FALSE */
int is_full(void) {
return top_element == ( STACK_SIZE - 1 );
}
2)使用动态数组实现堆栈
- 内存有限的环境中,使用assert检查内存是否分配成功不合适,因为失败的话直接停止了。可以让create_stack返回一个值,提示内存是否分配成功,如果失败的话,用户可以换小一点的值再试。
/* 创建堆栈,size指定可以保存多少个元素 */
void create_stack( size_t size);
/* 销毁堆栈,释放内存 */
void destroy_stack( void );
/* 相比静态数组实现,需要添加和修改的地方如下,其他函数不变 */
#include <malloc.h>
/* 存储堆栈中值的数组和一个指向堆栈顶部元素的指针 */
static STACK_TYPE *stack;
static size_t stack_size;
static int top_element = -1;
void create_stack(size_t size) {
assert( stack_size == 0 );
stack_size = size;
stack = (STACK_TYPE *)malloc( stack_size * sizeof(STACK_TYPE) );
assert( stack != NULL );
}
void destroy_stack(void) {
assert( stack_size > 0 );
stack_size = 0; // 设置size
free(stack); // 释放空间
stack = NULL; // 指针设为NULL
}
/* 如果堆栈为空,返回TRUE,否则FALSE */
int is_empty(void) {
assert( stack_size > 0 ); // 增加
return top_element == -1;
}
/* 如果堆栈已满,返回TRUE,否则FALSE */
int is_full(void) {
assert(stack_size > 0); // 增加
return top_element == ( stack_size - 1 );
}
3)链式堆栈
- stack指针一直指向栈顶
- 因为链式堆栈不会满,所以is_full始终是FALSE,且创建时不需要做工作
- destroy_stack使用了is_empty和pop
#define FALSE 0
typedef struct STACK_NODE {
STACK_TYPE value;
struct STACK_NODE *next;
} StackNode;
/* 指向堆栈中第一个节点的指针 */
static StackNode *stack;
void create_stack(size_t size) {}
void destroy_stack(void) {
while (!is_empty())
pop();
}
/* 把一个新值压到堆栈中 */
void push(STACK_TYPE value) {
StackNode* new_node;
new_node = (StackNode*)malloc(sizeof(StackNode));
assert(new_node != NULL);
new_node->value = value;
new_node->next = stack;
stack = new_node;
}
/* 从堆栈中弹出一个值,并将其丢弃 */
void pop(void) {
StackNode* first_node;
assert( !is_empty() );
first_node = stack; // 改指向
stack = first_node->next;
free(first_node); // 释放
}
/* 返回栈顶元素,但不对堆栈进行修改*/
STACK_TYPE top(void) {
assert( !is_empty() );
return stack->value;
}
/* 如果堆栈为空,返回TRUE,否则FALSE */
int is_empty(void) {
return stack == NULL;
}
/* 如果堆栈已满,返回TRUE,否则FALSE */
int is_full(void) {
return FALSE;
}
队列
队列的接口:
/*
* 队列的接口 queue.h
*/
#define QUEUE_TYPE int
/* 只适用于动态分配数组的队列 */
void create_queue(size_t size);
/* 只适用于链式和动态分配数组的队列 */
void destroy_queue(void);
void insert(QUEUE_TYPE value);
void _delete(void);
QUEUE_TYPE first(void);
/* 如果堆栈为空,返回TRUE,否则FALSE */
int is_empty(void);
/* 如果堆栈已满,返回TRUE,否则FALSE */
int is_full(void);
为了尽可能利用空间,使用循环数组
- 留一个空间不用,使得队列“满”时front和rear的值不同
- front和rear可以用任何值初始化
- 队列空的条件是(rear + 1) % QUEUE_SIZE == front
- 队列满的条件是(rear + 2) % QUEUE_SIZE == front
#define QUEUE_SIZE 100 /* 队列中元素的最大数量 */
#define ARRAY_SIZE (QUEUE_SIZE + 1)
static QUEUE_TYPE queue[ ARRAY_SIZE ];
static size_t front = 1; // 从1开始.当只有1个元素时都指向1
static size_t rear = 0;
void insert(QUEUE_TYPE value) {
assert( !is_full() );
rear = (rear + 1) % ARRAY_SIZE;
queue[rear] = value;
}
/*
* 删除元素时只要移动front即可
*/
void _delete(void) {
assert( !is_empty() );
front = (front + 1) % ARRAY_SIZE;
}
QUEUE_TYPE first(void) {
assert( !is_empty() );
return queue[front];
}
int is_empty(void) {
return (rear + 1) % ARRAY_SIZE == front;
}
int is_full(void) {
return (rear + 2) % ARRAY_SIZE == front;
}