数据结构线性表之栈与队列
1.栈
1.1.什么是栈?
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last
In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
1.2.栈的实现
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
栈一般可以分为:
1.静态栈:使用定长数组存储
typedef int STDataType;
#define N 10
typedef struct Stack
{
STDataType a[N];
int top; // 栈顶
}Stack;
2.动态栈:使用动态开辟的数组存储
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; // 栈顶
int capacity; // 容量
}Stack;
1.3.栈的实现(github链接):
栈的实现
1.4.栈的OJ习题
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。OJ链接
思路分析:利用栈的后进先出原理。遍历字符串,当输入为" ({[ " 时,入栈;当输入为 “]})” 和栈顶元素匹配则出栈,直到遍历字符串结束,若栈最终为空则返回 true;如果不匹配则直接结束。
具体代码如下:
//由于C语言库中没有栈,故此处需要栈的实现代码
bool isValid(char * s){
Stack st;
StackInit(&st);
while(*s)
{
if(*s == '[' || *s == '(' || *s == '{')
{
StackPush(&st, *s);
++s;
}
else
{
if(StackEmpty(&st) == 1)
return false;
char top = StackTop(&st);
StackPop(&st);
if(*s == ']' && top == '[')
{
++s;
continue;
}
else if(*s == ')' && top == '(')
{
++s;
continue;
}
else if(*s == '}' && top == '{')
{
++s;
continue;
}
else
{
return false;
}
}
}
bool ret = StackEmpty(&st) == 1;
StackDestory(&st);
return ret;
2.队列
2.1.什么是队列?
队列:一种特殊的线性表,只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。队列具有先进先出FIFO(First In First Out)的特点。
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
2.2.队列的实现
队列也可以用数组或者链表的结构实现,使用链表的结构实现更优,因为使用数组的结构,出队列在数组头上出数据,效率会降低。
队列的创建
// 链式结构
typedef struct QListNode
{
struct QListNode* pNext;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front;
QNode* rear;
}Queue;
2.3.队列的实现(github链接):
队列的实现
2.4.循环队列
实际中我们还会使用一种队列叫循环队列。如操作系统中的生产者、消费者模型。环形队列可以使用数组(取模)实现,也可以使用循环链表实现。
2.4.1.判断队空与队满
队空:Q.rear = Q.front
队满:Q.rear + 1 = Q.front
2.4.2.实现循环队列
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。OJ链接
具体代码如下:
typedef struct {
int* _a;
int _n;
int _front;
int _rear;
} MyCircularQueue;
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
/** Initialize your data structure here.
Set the size of the queue to be k. */
MyCircularQueue* myCircularQueueCreate(int k)
{
MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
cq->_a = (int*)malloc(sizeof(int)*(k + 1));
cq->_n = k + 1; cq->_front = cq->_rear = 0;
return cq;
}
/** Insert an element into the circular queue.
Return true if the operation is successful. */
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->_a[obj->_rear] = value;
obj->_rear++;
if(obj->_rear == obj->_n)
obj->_rear = 0;
return true;
}
/** Delete an element from the circular queue.
Return true if the operation is successful. */
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
++obj->_front;
if(obj->_front == obj->_n)
obj->_front = 0;
return true;
}
/** Get the front item from the queue. */
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->_a[obj->_front];
}
/** Get the last item from the queue. */
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
int prevRear = obj->_rear - 1;
if (obj->_rear == 0)
prevRear = obj->_n - 1;
return obj->_a[prevRear];
}
/** Checks whether the circular queue is empty or not. */
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->_front == obj->_rear;
}
/** Checks whether the circular queue is full or not. */
bool myCircularQueueIsFull(MyCircularQueue* obj) {
int nextRear = obj->_rear + 1;
nextRear %= obj->_n;
return nextRear == obj->_front;
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->_a);
free(obj);
}
2.5.队列的OJ习题
1.用队列实现栈。OJ链接
思路分析:用两个队列q1和q2,总是保持一个队列为空。
1.入栈时向非空的一个队列push;
2.出栈时,设q1非NULL,则从q1队头取元素入队到q2中,直到q1中剩下一个元素,这个元素即为栈顶元素。
3.求top直接返回非空队列的队尾即可。
2.用栈实现队列。OJ链接
思路分析:
入队:把 pushst中所有的元素移到 popst 中,接着把新元素压入 pushst。再把 pushst中的元素移到 popst 中,最后把popst中所有的元素弹出。
出队:直接从 popst 弹出就可以了,因为popst 的栈顶元素就是队列的队首元素。
OJ代码(github链接)
OJ代码实现