栈
和队列的原理大家应该很熟悉了,队列是先进先出,栈是先进后出。
首先大家要知道 栈和队列是STL(C++标准库)里面的两个数据结构。
接下来介绍的栈和队列也是SGI STL里面的数据结构, 知道了使用版本,才知道对应的底层实现。
来说一说栈,栈先进后出,如图所示:
栈提供了pop和push等结构,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器。不像set和map提供迭代器iteratro来遍历所有元素。
栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。
从下图中可以看出,栈的内部结构,栈的底层实现可以是vector,deque,list 都是可以的, 主要就是数组和链表的底层实现。
deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。
我们也可以指定vector为栈的底层实现,初始化语句如下:
std::stack<int, std::vector<int>> third;// 使用vector为底层容器的栈
栈的c语言实现:
栈的主要操作有:入栈push 出栈 pop 是否为空isempty 是否为满isfull
插入:入栈,进栈,压栈
删除:出栈、弹栈
先进后出
上溢的判断
当栈为空的时候top=-1,压入一个元素top的值就会加1
这样a[0]就表示第一个元素
a【top】表示栈顶元素
当top=maxsize-1的时候,表示栈满了,再进元素就会溢出,称为上溢
下溢判断,当top等于-1的时候,再出栈就会发生下溢
一初始化:
typedef struct stack
{
int *data;
int top;
}stack;
int initstack(stack *s)
{
if(NULL == S)
return failure;
s->data = (int *)malloc(sizeof(int)*size);
s->top = -1
return success;
}
二、进栈
int pushstack(stack *s, int num)
{
if(NULL == s)
return failure;
if(s->top >= size -1)
return failure;
s->top = s->top + 1;
s->data[s->top] = num;
return success;
}
三、判断栈是否为空
int emptystack(stack s)
{
if(NULL == s)
return failure;
if(s.top = -1)
return success;
else
return failure;
}
四、栈顶操作
int gettop(stack s)
{
if(NULL == s)
return failure;
if(s.top = -1)
return failure;
int tmp = s.data[s.top];
return tmp;
}
五、出栈
int popstack(stack *s)
{
if(NULL == s)
return failure;
if(s->top == -1)
return failure;
int tmp;
tmp = s->data[s->top] ;
s->top = s->sop - 1;
return tmp;
}
六、清空栈
int clearstack(stack *s)
{
if(NULL == S)
return failure;
s->top = -1;
return success;
}
七、销毁栈
int destroystack(stack *s)
{
if(NULL == s)
return failure;
free(s->data);
s->data = NULL;
return success;
}
队列
中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。
也可以指定list 为起底层实现,初始化queue的语句如下:
std::queue<int, std::list<int>> third;// 定义以list为底层容器的队列
所以STL 队列也不被归类为容器,而被归类为container adapter( 容器适配器)。
队列的c语言实现
主要操作:
定义队列
入队:enqueue
出队:dequeue
是否为空:isempty
是否满:isfull
队列:只允许一端插入,一端进行删除的线性表
队尾入队,队头出队
front队头:队头的前一个元素指向
rear队尾:指向最后一个元素
队列的长度(rear - front + size)%size
实现循环(front + rear)%size
是否为空:front==rear
是否满:(rear + 1)%size = front
一、初始化
typedef struct sequeue
{
int *data;
int front;
int rear;
}sequeue;
int initqueue(sequeue *q)
{
if(NULL == q)
return failure;
q->data = (int *)malloc(sizeof(int)*size);
q->front = q->rear = 0;
return success;
}
二、进队(队尾)
int pushqueue(sequeue *q, int num)
{
if(NULL == q)
return failure;
if((q->rear + 1) %size == q->front)
return failure;
q->data[q->rear] = num;
q->rear = (q->rear + 1)%size;
return success;
}
三、判断是否为空
int emptyqueue(sequeue q)
{
if(NULL == q)
return failure;
if(q.fornt == q.rear)
return success;
else
return failure;
}
四、获取队头元素
int gettop(sequeue q)
{
if(NULL == q || q.front == q.rear)
return failure;
else
return q.data[q.front];
}
五、出队操作
int popqueue(sequeue *q)
{
if(NULL == q)
return failure;
if(q->front == q->rear)
return failure;
else
int num = q->data[q->front];
q->front = (q->front + 1 ) % size;
return num;
}
六、清空队列
int clearqueue(sequeue *q)
{
if(NULL == q)
return failure;
q->front = q->rear = 0;
return success;
}
七、销毁队列
int destroyqueue(sequeue *q)
{
if(NULL == q)
return filure;
if(q->data)
free(q->data);
q->data = NULL;
return success;
}
232.用栈实现队列
力扣题目链接(opens new window)
使用栈实现队列的下列操作:
push(x) -- 将一个元素放入队列的尾部。
pop() -- 从队列首部移除元素。
peek() -- 返回队列首部的元素。
empty() -- 返回队列是否为空。
使用栈来模式队列的行为,如果仅仅用一个栈,是一定不行的,所以需要两个栈一个输入栈,一个输出栈,这里要注意输入栈和输出栈的关系。
执行语句:
queue.push(1);
queue.push(2);
queue.pop(); 注意此时的输出栈的操作
queue.push(3);
queue.push(4);
queue.pop();
queue.pop();注意此时的输出栈的操作
queue.pop();
queue.empty();
在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。
/*
1.两个type为int的数组(栈),大小为100
第一个栈stackIn用来存放数据,第二个栈stackOut作为辅助用来输出数据
2.两个指针stackInTop和stackOutTop,分别指向栈顶
*/
typedef struct {
int stackInTop, stackOutTop;
int stackIn[100], stackOut[100];
} MyQueue;
/*
1.开辟一个队列的大小空间
2.将指针stackInTop和stackOutTop初始化为0
3.返回开辟的队列
*/
MyQueue* myQueueCreate() {
MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue));
queue->stackInTop = 0;
queue->stackOutTop = 0;
return queue;
}
/*
将元素存入第一个栈中,存入后栈顶指针+1
*/
void myQueuePush(MyQueue* obj, int x) {
obj->stackIn[(obj->stackInTop)++] = x;
}
/*
1.若输出栈为空且当第一个栈中有元素(stackInTop>0时),将第一个栈中元素复制到第二个栈中(stackOut[stackTop2++] = stackIn[--stackTop1])
2.将栈顶元素保存
3.当stackTop2>0时,将第二个栈中元素复制到第一个栈中(stackIn[stackTop1++] = stackOut[--stackTop2])
*/
int myQueuePop(MyQueue* obj) {
//优化:复制栈顶指针,减少对内存的访问次数
int stackInTop = obj->stackInTop;
int stackOutTop = obj->stackOutTop;
//若输出栈为空
if(stackOutTop == 0) {
//将第一个栈中元素复制到第二个栈中
while(stackInTop > 0) {
obj->stackOut[stackOutTop++] = obj->stackIn[--stackInTop];
}
}
//将第二个栈中栈顶元素(队列的第一个元素)出栈,并保存
int top = obj->stackOut[--stackOutTop];
//将输出栈中元素放回输入栈中
while(stackOutTop > 0) {
obj->stackIn[stackInTop++] = obj->stackOut[--stackOutTop];
}
//更新栈顶指针
obj->stackInTop = stackInTop;
obj->stackOutTop = stackOutTop;
//返回队列中第一个元素
return top;
}
//返回输入栈中的栈底元素
int myQueuePeek(MyQueue* obj) {
return obj->stackIn[0];
}
//若栈顶指针均为0,则代表队列为空
bool myQueueEmpty(MyQueue* obj) {
return obj->stackInTop == 0 && obj->stackOutTop == 0;
}
//将栈顶指针置0
void myQueueFree(MyQueue* obj) {
obj->stackInTop = 0;
obj->stackOutTop = 0;
}
/**
* Your MyQueue struct will be instantiated and called as such:
* MyQueue* obj = myQueueCreate();
* myQueuePush(obj, x);
* int param_2 = myQueuePop(obj);
* int param_3 = myQueuePeek(obj);
* bool param_4 = myQueueEmpty(obj);
* myQueueFree(obj);
*/
225. 用队列实现栈
力扣题目链接(opens new window)
使用队列实现栈的下列操作:
push(x) -- 元素 x 入栈
pop() -- 移除栈顶元素
top() -- 获取栈顶元素
empty() -- 返回栈是否为空
队列模拟栈,其实一个队列就够了,那么我们先说一说两个队列来实现栈的思路。
队列是先进先出的规则,把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并没有变成先进后出的顺序。
所以用栈实现队列, 和用队列实现栈的思路还是不一样的,这取决于这两个数据结构的性质。
但是依然还是要用两个队列来模拟栈,只不过没有输入和输出的关系,而是另一个队列完全用来备份的!
如下面动画所示,用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。
模拟的队列执行语句如下
class MyStack {
public:
queue<int> que;
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
que.push(x);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int size = que.size();
size--;
while (size--) { // 将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部
que.push(que.front());
que.pop();
}
int result = que.front(); // 此时弹出的元素顺序就是栈的顺序了
que.pop();
return result;
}
/** Get the top element. */
int top() {
return que.back();
}
/** Returns whether the stack is empty. */
bool empty() {
return que.empty();
}
};