1、数据结构概述
栈和队列,也属于线性表,因为它们也都用于**存储逻辑关系为 "一对一"** 的数据,但由于它们比较特殊,因此将其单独作为一章,做重点讲解。
使用栈结构存储数据,讲究“先进后出”,即最先进栈的数据,最后出栈;使用队列存储数据,讲究 "先进先出",即最先进队列的数据,也最先出队列。
栈也可分为顺序栈和链表,队列也分为顺序队列和链队列, 也就是说,栈和队列都可以使用数组和链表两种线性结构来表示。
1.1 栈
常见的栈操作
- push(x) – 元素 x 入栈 (时间复杂度O(1))
- pop() – 移除栈顶元素 (时间复杂度O(1))
- top() – 获取栈顶元素 (时间复杂度O(1))
- empty() – 返回栈是否为空 (时间复杂度O(1))
也就是说,栈这种数据结构的添加、删除操作的时间复杂度都是O(1)
顺序存储结构下的栈,自己实现的代码如下
typedef struct {
int *data; //栈中元素存放的地方
int size; //当前栈中有多少个元素
int capacity; //栈的最大深度
} MyStack;
MyStack *stackCreate(int capacity)
{
MyStack *stack = (MyStack *)malloc(sizeof(MyStack));
stack->data = (int *)malloc(sizeof(int)*capacity);
stack->size = 0;
stack->capacity = capacity;
return stack;
}
void stackPush(MyStack *obj, int value)
{
obj->data[obj->size] = value;
obj->size++;
}
void stackPop(MyStack *obj)
{
obj->size--;
}
bool stackEmpty(MyStack *obj)
{
if(obj->size == 0)
{
return true;
}
else
{
return false;
}
}
int stackTop(MyStack *obj)
{
return obj->data[obj->size-1];
}
void stackfree(MyStack *obj)
{
free(obj->data);
free(obj);
}
链式存储结构下的栈,代码如下
1.2 队列
队列具有以下两个特点:
- 数据从队列的一端进,另一端出;
- 数据的入队和出队遵循"先进先出"的原则;
用C代码实现一个循环队列
typedef struct {
int *data; //队列中的数据
int front; //对头指针
int rear; //队尾指针
int size; //队列总长度
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k)
{
MyCircularQueue *obj = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
obj->data = (int *)malloc(sizeof(int)*k);
obj->front = -1;
obj->rear = -1;
obj->size = k;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
if(obj->front==-1 && obj->rear==-1)
{
return true;
}
return false;
}
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
if( (obj->rear+1)%obj->size == obj->front )
{
return true;
}
return false;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
if(myCircularQueueIsFull(obj))
{
return false;
}
obj->rear = (obj->rear+1)%obj->size;
obj->data[obj->rear] = value;
if(obj->front == -1)
{
obj->front = 0;
}
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
{
return false;
}
if(obj->front == obj->rear)
{
obj->data[obj->front] = -1;
obj->front = -1;
obj->rear = -1;
}
else
{
obj->data[obj->front] = -1;
obj->front = (obj->front+1)%obj->size;
}
return true;
}
//获取队首元素。如果队列为空,返回 -1 。
int myCircularQueueFront(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->data[obj->front];
}
//获取队尾元素。如果队列为空,返回 -1 。
int myCircularQueueRear(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->data[obj->rear];
}
void myCircularQueueFree(MyCircularQueue* obj)
{
free(obj->data);
obj->data = NULL;
free(obj);
}
2、编程实践
参考题目连接
232-用栈实现队列
225-用队列实现栈
20-有效的括号
1047- 删除字符串中的所有相邻重复项
套用上面的常见操作接口,实现以下1047题
//把栈的一些通用接口作为模板记住, 以后做题的时候能够默写下来
typedef struct {
char *data; //栈中元素存放的地方
int size; //当前栈中有多少个元素
int capacity; //栈的最大深度
} MyStack;
MyStack *stackCreate(int capacity)
{
MyStack *stack = (MyStack *)malloc(sizeof(MyStack));
stack->data = (char *)malloc(sizeof(char)*capacity);
stack->size = 0;
stack->capacity = capacity;
return stack;
}
void push(MyStack *obj, char value)
{
obj->data[obj->size] = value;
obj->size++;
}
void pop(MyStack *obj)
{
obj->size--;
}
bool isEmpty(MyStack *obj)
{
if(obj->size == 0)
{
return true;
}
else
{
return false;
}
}
char getTop(MyStack *obj)
{
return obj->data[obj->size-1];
}
int getSize(MyStack *obj)
{
return obj->size;
}
void stackfree(MyStack *obj)
{
free(obj->data);
obj->data = NULL;
free(obj);
obj = NULL;
}
//使用栈的思想
char * removeDuplicates(char * s)
{
int i = 0;
int len = strlen(s);
MyStack *stk;
//创建一个栈, 栈元素个数为len
stk = stackCreate(len);
for(i=0; i<len; i++)
{
//如果判断发现当前元素与栈顶元素相同时, 弹栈操作
if( !isEmpty(stk) && getTop(stk) == s[i] )
{
pop(stk);
}
else
{
push(stk, s[i]);
}
}
//遍历完成之后, 剩余的元素就是答案
int size = getSize(stk);
char *str = (char *)malloc(sizeof(char)*(size+1));
for(i=0; i<size; i++)
{
str[size-1-i] = getTop(stk);
pop(stk);
}
str[size] = '\0';
//释放栈空间
stackfree(stk);
return str;
}