在对顺序表和链表有了一定的了解之后,我们来讨论两种更为具体的模型——栈和队列。
栈
栈的基本描述
栈(stack)是一种限定只在尾部插入和删除的模型结构,根据栈的这个特点我们把栈的修改原则称为先进后出,后进先出原则,我们把栈的头部称为栈底,栈的尾部称为栈顶,也就是说栈的一系列操作都是在栈顶完成的。
栈的图解
栈的基本操作
相对于顺序表和链表来说栈的操作就要简单许多,因为栈的一系列操作仅仅在栈顶完成。
只在尾部操作的话,我们一般选用顺序表来实现栈的编写。
栈的初始化
void StackInit(Stack *s){
s->top = 0;
}
压栈(实际是尾插)
void StackPush(Stack*s, char v){
s->array[s->top++] = v;
}
出栈(实际是尾删)
void StackPop(Stack*s){
s->top--;
}
查看栈顶元素
int StackTop(Stack*s){
return s->array[s->top - 1];
}
查看元素个数
int StackSize(Stack *s){
return s->top;
}
判断栈是否为空
bool StackEmpty(Stack *s){
return s->top == 0;//栈为空返回 true
}
队列
队列的基本描述
队列(queue)顾名思义,就像我们日常生活中的排队一样,从队尾走到队首,最先进队伍的人最先出队伍,队列的修改原则是先进先出。
队列的图解
队列的基本操作
队列限定只在尾部插入和头部删除,在队首和队尾操作,这样的操作用链表实现更为方便。
队列的初始化
void QueueInit(Queue *q){
q->head = q->last = NULL;
}
队列的进入(尾插)
void QueuePush(Queue *q, int v){
Node *node = (Node*)malloc(sizeof(Node));
node->value = v;
node->next = NULL;
if (q->head == NULL){
q->head = node;
}
else{
q->last->next = node;
q->last = node;
}
}
队列的出队(头删)
void QueuePop(Queue*q){
Node*second = q->head->next;
free(q->head);
q->head = second;
if (q->head == NULL){
q->last == NULL;
}
}
返回队首元素
int QueueFront(Queue*q){
return q->head->value;
}
队列大小
int QueueSize(Queue *q){
int size = 0;
for (Node*n = q->head; n != NULL; n = n->next){
size++;
}
return size;
}
判断队列是否为空
bool QueueEmpty(Queue *q){
return q->head == NULL;
}
了解了栈和队列的这些基本操作,接下来我们看看栈和队列的相互实现。
用队列实现栈
队列的特点是先进先出,而栈的特点是先进后出,这就要将一个队列中每次队首的元素移动到队尾,出最后一个进队列的元素。
具体实现:
class MyStack {
public:
queue<int> q;
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
//压栈没有特殊性
q.push(x);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
//出栈首先得挪动size-1个数
int size = q.size();
for(int i = 0;i<size-1;i++){
//拿出队首元素
int v = q.front();
//删除
q.pop();
//再从后进入队列
q.push(v);
}
int v = q.front();
q.pop();
return v;
}
/** Get the top element. */
int top() {
//出栈首先得挪动size-1个数
int size = q.size();
for(int i = 0;i<size-1;i++){
//拿出队首元素
int v = q.front();
//删除
q.pop();
//再从后进入队列
q.push(v);
}
int v = q.front();
q.pop();
q.push(v);
return v;
}
/** Returns whether the stack is empty. */
bool empty() {
//队列为空 栈就为空
return q.empty();
}
};
用栈实现队列
队列的特点是先进先出,而栈的特点是先进后出,所以用栈实现队列,我们应该在内部选取两个栈来完成队列的实现。
具体实现:
class MyQueue {
public:
stack<int> in;
stack<int> out;
/** Initialize your data structure here. */
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
in.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
//如果out为空,把所有in的数据导入到out
if(out.empty()){
while (!in.empty()){
int v = in.top();
in.pop();
out.push(v);
}
}
int v = out.top();
out.pop();
return v;
}
/** Get the front element. */
int peek() {
//如果out为空,把所有in的数据导入到out
if(out.empty()){
while (!in.empty()){
int v = in.top();
in.pop();
out.push(v);
}
}
int v = out.top();
return v;
}
/** Returns whether the queue is empty. */
bool empty() {
return in.empty() && out.empty();
}
};
最小栈
最小栈即每次出栈元素都是这个栈中的最小值,首先正常出栈是没法实现的(即图中的正常栈),我们必须再找一个栈用来存放每次压栈完成的最小值(即图中的最小栈),用x(压入正常栈的元素)和最小栈栈顶元素比较,如果小于最小栈栈顶元素,则将x压入最小栈,否则将最小栈栈顶元素压入最小栈。
具体实现:
class MinStack {
public:
stack<int> normal;
stack<int> min;
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
normal.push(x);
if(min.empty()||x<=min.top()){
min.push(x);
}
else{
min.push(min.top());
}
}
void pop() {
normal.pop();
min.pop();
}
int top() {
return normal.top();
}
int getMin() {
return min.top();
}
};
循环队列
循环队列即类似于环形的队列
具体实现:
class MyCircularQueue {
public:
int *array;//保存空间指针
int capacity;//容量
int size;//实际数据个数
int front;//当前队首数据下标
int vear;//当前队尾可用位置下标
/** Initialize your data structure here. Set the size of the queue to be k. */
MyCircularQueue(int k) {
array = (int*)malloc(sizeof(int)*k);
capacity = k;
size = 0;
front = 0;
vear = 0;
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
bool enQueue(int value) {
if(size == capacity){
return false;
}
else{
array[vear] = value;
vear = (vear+1)%capacity;
size++;
return true;
}
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
bool deQueue() {
if(size == 0){
return false;
}
else{
front = (front+1)%capacity;
size--;
return true;
}
}
/** Get the front item from the queue. */
int Front() {
if(size == 0){
return -1;
}
else{
return array[front];
}
}
/** Get the last item from the queue. */
int Rear() {
if(size == 0){
return -1;
}
else{
int index = (vear-1+capacity)%capacity;
return array[index];
}
}
/** Checks whether the circular queue is empty or not. */
bool isEmpty() {
return size == 0;
}
/** Checks whether the circular queue is full or not. */
bool isFull() {
return size == capacity;
}
};
栈的应用(深度优先):1.括号匹配 2.迷宫的暴力破解 3.函数调用栈 4.逆波兰表达式
队列的应用(广度优先):1.操作系统的调用 2.消息队列