栈的基本操作
分为三部分,实现入栈,出栈,获取栈顶元素,获取栈内有效元素个数,置空,销毁等操作。
Stack.h
#pragma once
#include <assert.h>
#include <stdio.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top;//标记栈顶,相当于顺序表中的size
int capacity;
}Stack;
void StackInit(Stack* s);
void StackDestroy(Stack *s);
void StackPush(Stack *s,STDataType data);
void StackPop(Stack *s);
STDataType StackTop(Stack *s);
int StackEmpty(Stack *s);
int StackSize(Stack *s);
Stack.c
#include "Stack.h"
#include <malloc.h>
#include <string.h>
void StackInit(Stack* s)
{
assert(s);
s->arr = (STDataType*)malloc(sizeof(STDataType)* 3);
if (NULL == s->arr)
{
assert(0);
return;
}
s->capacity = 3;
s->top = 0;
}
void CheckCapacity(Stack *s)
{
assert(s);
if (s->top == s->capacity)
{
int NewCapacity = s->capacity * 2;
STDataType* temp = (STDataType*)malloc(sizeof(STDataType)*NewCapacity);
if (NULL == temp)
{
assert(0);
return;
}
memcpy(temp, s->arr, s->top*sizeof(STDataType));
free(s->arr);
s->arr = temp;
s->capacity = NewCapacity;
}
}
void StackPush(Stack *s, STDataType data)
{
assert(s);
CheckCapacity(s);//扩容
s->arr[s->top] = data;
s->top++;
}
void StackPop(Stack *s)
{
assert(s);
if (0 == s->top)//栈存在但是栈顶没有元素
return;
s->top--;
}
STDataType StackTop(Stack *s)//获取栈顶元素
{
assert(s);
return s->arr[s->top - 1];
}
int StackSize(Stack *s)//获取栈内有效元素个数
{
assert(s);
return s->top;
}
int StackEmpty(Stack *s)
{
assert(s);
return 0 == s->top;
}
void StackDestroy(Stack *s)
{
assert(s);
if (s->arr)
{
free(s->arr);
s->arr = NULL;
s->capacity = 0;
s->top = 0;
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
#include <stdlib.h>
void menu()
{
printf("***********************************************\n");
printf("** 1.压栈 2.出栈 **\n");
printf("** 3.获取栈顶元素 4.获取栈内有效元素个数 **\n");
printf("** 5.清空 6.销毁 **\n");
}
void test()
{
Stack s;
int input = 0;
STDataType data = 0;
StackInit(&s);
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入要入栈的数:");
scanf("%d", &data);
StackPush(&s, data);
break;
case 2:
StackPop(&s);
break;
case 3:
data=StackTop(&s);
printf("栈顶元素为:%d\n", data);
break;
case 4:
data=StackSize(&s);
printf("有效元素个数为:%d\n", data);
break;
case 5:
StackEmpty(&s);
break;
case 6:StackDestroy(&s);
break;
}
} while (input);
}
int main()
{
test();
system("pause");
return 0;
}
附加C++实现栈的操作源代码(学习C++与Linux后)(github):
https://github.com/wangbiy/review/tree/master/stack
队列的基本操作
queue.h
#pragma once
typedef int QUDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QUDataType data;
}QueueNode;//结点
typedef struct Queue
{
QueueNode* front;
QueueNode* rear;
}Queue;
void QueueInit(Queue* pq);//初始化
void QueueDestroy(Queue* pq);//销毁
void QueuePush(Queue* pq, QUDataType data);//入队
void QueuePop(Queue* pq);//出队
QUDataType QueueFront(Queue* pq);//获取队头元素
QUDataType QueueBack(Queue* pq);//获取队尾元素
int Queuempty(Queue *pq);//置空
int QueueSize(Queue *pq);//获取有效元素个数
void QueueShow(Queue* pq);//显示
queue.c
#include "queue.h"
#include <assert.h>
#include <stdio.h>
#include <malloc.h>
void QueueInit(Queue* pq)
{
assert(pq);
pq->front = pq->rear = NULL;
}
void QueueDestroy(Queue* pq)//销毁
{
QueueNode* cur = NULL;
assert(pq);
cur = pq->front;
while (cur)
{
pq->front = cur->next;
free(cur);
cur = pq->front;
}
pq->front = pq->rear = NULL;
}
QueueNode* BuyQueueNode(QUDataType data)
{
QueueNode* pNewNode = (QueueNode*)malloc(sizeof(QueueNode));
if (pNewNode == NULL)
{
assert(0);
return NULL;
}
pNewNode->data = data;
pNewNode->next = NULL;
return pNewNode;
}
void QueuePush(Queue* pq, QUDataType data)//入队,尾插
{
assert(pq);
QueueNode* pNewNode = NULL;
pNewNode = BuyQueueNode(data);
if (NULL == pq->rear)//空队列
{
pq->front = pq->rear=pNewNode;
}
else
{
pq->rear->next = pNewNode;
pq->rear = pNewNode;
}
}
void QueuePop(Queue* pq)//出队
{
assert(pq);
if (NULL == pq->front)
{
return;
}
else if (pq->front==pq->rear)//只有一个节点
{
free(pq->front);
pq->front = pq->rear = NULL;
}
else//多个元素
{
QueueNode *Del = pq->front;
pq->front = Del->next;
free(Del);
}
}
QUDataType QueueFront(Queue* pq)//获取队头元素
{
assert(pq);
return pq->front->data;
}
QUDataType QueueBack(Queue* pq)//获取队尾元素
{
assert(pq);
return pq->rear->data;
}
int Queuempty(Queue *pq)//置空
{
assert(pq);
return NULL == pq->front;
}
int QueueSize(Queue *pq)//获取有效元素个数
{
QueueNode* cur = NULL;
int count = 0;
assert(pq);
cur = pq->front;
while (cur)
{
count++;
cur = cur->next;
}
return count;
}
void QueueShow(Queue* pq)
{
QueueNode* cur = NULL;
assert(pq);
cur = pq->front;
while (cur)
{
printf("%d---->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "queue.h"
#include <stdio.h>
#include <stdlib.h>
void menu()
{
printf("***************************************\n");
printf("**1.入队 2.出队 **\n");
printf("**3.获取队头元素 4.获取队尾元素 **\n");
printf("**5.获取有效元素个数 6.置空 **\n");
printf("**7.销毁 8.显示 **\n");
printf("***************************************\n");
}
void test()
{
int input = 0;
Queue q;
QUDataType data = 0;
QueueInit(&q);
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入要入队的元素:");
scanf("%d", &data);
QueuePush(&q, data);
break;
case 2:
QueuePop(&q);
break;
case 3:
data=QueueFront(&q);
printf("队头元素为:%d\n", data);
break;
case 4:
data = QueueBack(&q);
printf("队尾元素为:%d\n", data);
break;
case 5:
data=QueueSize(&q);
printf("有效元素个数为:%d\n", data);
break;
case 6:
Queuempty(&q);
break;
case 7:
QueueDestroy(&q);
break;
case 8:
QueueShow(&q);
break;
default:
break;
}
} while (input);
}
int main()
{
test();
system("pause");
return 0;
}
附加C++实现队列的基本操作源代码(学习Linux与C++后)(github):
https://github.com/wangbiy/review/tree/master/Queue
栈和队列面试题
1.括号匹配问题(c++版,比较方便)
class Solution {
public:
bool isValid(string s) {
std::stack<char> str;
int size = s.size();//取字符串长度
for (int i = 0; i < size; ++i)
{
char ch = s[i];
switch (ch)
{
case '(':
case '[':
case '{':
str.push(ch);
break;
case ')':
case ']':
case '}':
{
if (str.empty())
{
return false;
}
char left = str.top();
str.pop();
if (!((left == '('&&ch == ')')
|| (left == '['&&ch == ']') || (left == '{'&&ch == '}')))
{
return false;
}
break;
}
}
}
if (!str.empty())
{
return false;
}
return true;
}
};
分析:输入字符串,遍历取出字符串的每个字符,当遇到左括号时,入栈,当遇到右括号时,判断栈内是否为空,若为空,返回false,否则取出栈顶元素,出栈,与右括号比较判断是否匹配,如果不匹配,返回false,循环结束,若栈内还有元素,返回false,否则返回true。
2.用队列实现栈
class MyStack {
std::queue<int> q;
public:
MyStack() {
}
void push(int x) {
q.push(x);
}
int pop() {
int n=q.size();
for(int i=0;i<n-1;i++)
{
int v=q.front();
q.pop();
q.push(v);
}
int v=q.front();
q.pop();
return v;
}
int top() {
int n=q.size();
for(int i=0;i<n-1;i++)
{
int v=q.front();
q.pop();
q.push(v);
}
int v=q.front();
q.pop();
q.push(v);
return v;
}
bool empty() {
return q.empty();
}
};
分析:进行压栈操作实际上就是数据入队列,直接将x进入队列,出栈要满足后进先出原则,而队列满足先进先出原则,因此,应当让前n-1个元素先从头部出队列,再从尾部入队列,返回队头元素,查看栈顶元素一样的道理,唯一不同的是只是看一眼队头元素,之后将它进入队列,判断栈内是否为空就是判断队列是否为空。
3.用栈实现队列
class MyQueue {
std::stack<int> in;
std:: stack<int> out;
public:
MyQueue() {
}
void push(int x) {
in.push(x);
}
int pop() {
if(out.empty())
{
int n=in.size();
for(int i=0;i<n;i++)
{
int v=in.top();
in.pop();
out.push(v);
}
}
int v=out.top();
out.pop();
return v;
}
int peek() {
if(out.empty())
{
int n=in.size();
for(int i=0;i<n;i++)
{
int v=in.top();
in.pop();
out.push(v);
}
}
int v=out.top();
return v;
}
bool empty() {
return in.empty()&&out.empty();
}
};
分析:定义两个栈,一个保存进来的元素,一个保存出去的元素,要用栈实现队列,进队列实际上就是进栈,出队列时,如果out栈为空,将in栈的栈顶元素一一取出,进入out栈,之后一一去out栈的元素,出栈。取队列首部的元素与之道理一样,不过不用出栈,只用看一眼就行,判断队列是否为空,只要两个栈都为空,队列为空。
4.实现一个最小栈
class MinStack {
std:: stack<int> nomal;
std:: stack<int> min;
public:
MinStack() {
}
void push(int x) {
nomal.push(x);
if(min.empty()||x<=min.top())
{
min.push(x);
}
else
{
min.push(min.top());
}
}
void pop() {
nomal.pop();
min.pop();
}
int top() {
return nomal.top();
}
int getMin() {
return min.top();
}
};
分析:定义两个栈,一个存放每个元素,一个存放小的元素,先将一个元素进入nomal栈,如果min栈是空的或者接下来的元素小于min栈的栈顶元素,进入min栈,否则将min栈的栈顶元素进入min栈,这样min栈的栈顶元素一定为最小元素,在进行出栈操作时,nomal栈出一个,min栈出一个,取栈顶元素即是取nomal栈元素,取最小元素即是取min栈栈顶元素。
5.循环队列
class MyCircularQueue {
int *arr;
int size;
int front;
int rear;
int capacity;
public:
MyCircularQueue(int k) {
arr=(int*)malloc(sizeof(int)*k);
capacity=k;
size=0;
front=0;
rear=0;
}
bool enQueue(int value) {
if(size>=capacity)
{
return false;
}
arr[rear]=value;
rear=(rear+1)%capacity;
size++;
return true;
}
bool deQueue() {
if(size==0)
return false;
size--;
front=(front+1)%capacity;
return true;
}
int Front() {
if(size==0)
return -1;
return arr[front];
}
int Rear() {
if(size==0)
return -1;
int index=(rear+capacity-1)%capacity;
return arr[index];
}
bool isEmpty() {
return size==0;
}
bool isFull() {
return size==capacity;
}
};
分析:实现循环队列,开辟一个动态空间,实现入队操作,如果有效元素个数大于容量,返回false,将value放入队列,如果到达队尾,实现循环,通过(rear+1)%capacity,使得rear==front,重新开始,出队列具有相同的道理,出队,front向后走,一直到队尾,进行取模操作,到达队头,在取队尾元素时,如果队列为空,返回false,(rear+capacity-1)%capacity可得到队尾位置。
6、逆波兰表达式
分析:逆波兰表达式,即后缀表达式,例如:2+1*3的逆波兰表达式就是2 1 3 * +,那么如何给定一个逆波兰表达式来求值,首先:取出元素,如果该元素是数字,入栈,否则,先到栈顶取当前运算符的右操作数,出栈,然后又在栈顶取左操作数,出栈,进行当前运算,将结果压栈,继续进行,直到所有元素处理完,这时栈顶元素就是运算结果。注意区分是负号还是减号,如果是负号,说明这个元素后面还有元素,否则后面为’\0’。
代码见下:
int evalRPN(char ** tokens, int tokensSize){
Stack s;
int ret=0;
StackInit(&s);
for(int i=0;i<tokensSize;++i)
{
char* str=tokens[i];
if(!('+'==str[0] || ('-'==str[0]&&str[1]=='\0')||'*'==str[0]||'/'==str[0]))//说明不是运算符
{
StackPush(&s,atoi(str));
}
else//说明是运算符
{
int right=StackTop(&s);
StackPop(&s);
int left=StackTop(&s);
StackPop(&s);
switch(str[0])
{
case '+':
StackPush(&s,left+right);
break;
case '-':
StackPush(&s,left-right);
break;
case '*':
StackPush(&s,left*right);
break;
case '/':
StackPush(&s,left/right);
break;
}
}
}
ret=StackTop(&s);
StackDestroy(&s);
return ret;
}
7.验证栈序列
验证栈序列就是给一个入栈序列和出栈序列,判断这个出栈序列是否能由入栈序列得到,如果可以,返回true,否则返回false
分析:定义两个指针,一个指向入栈序列头,一个指向出栈序列头,如果此时栈是空的或者此时栈顶元素与出栈指针指向的元素不相等,继续让入栈序列的元素入栈,入栈指针向后走,直到遇到与出栈指针指向的元素相等的栈顶元素时,取出栈顶元素,出栈指针向后走,循环,直到出栈指针越界,说明出栈序列遍历完成,结束。
代码见下:
bool validateStackSequences(int* pushed, int pushedSize, int* popped, int poppedSize){
Stack s;
int inidx=0;
int outidx=0;
StackInit(&s);
while(outidx<poppedSize)
{
while(StackEmpty(&s)||StackTop(&s)!=popped[outidx])//栈为空或者栈顶元素不等于popped栈的元素,入栈
{
if(inidx<pushedSize)
{
StackPush(&s,pushed[inidx++]);
}
else
return false;
}
//栈顶元素等于popped栈的元素,出栈,outidx++
StackPop(&s);
outidx++;
}
return true;
}
8.用栈将递归转换为循环
例如,我们在实现单链表的逆置时,一般采用递归的方法,例如:
void printList(Node* pHead)
{
if(pHead)
{
printList(pHead->next);
printf("%d ",phead->data);
}
}
但是这种方法很有可能会造成栈溢出,可以发现,递归的过程类似于栈的特性先进后出,因此可以使用栈将递归转换为循环,这样就不会产生栈溢出的问题,将上述代码可以变为:
void printList(Node* pHead)
{
Node* cur=pHead;
Stack s;
StackInit(&s);
while(cur)
{
StackPush(&s,cur->data);
cur=cur->next;
}
while(!StackEmpty(&s))
{
printf("%d ",StackTop(&s));
StackPop(&s);
}
StackDestroy(&s);
}
栈和队列的区别:
1.栈是先进后出,队列是先进先出
2.栈只允许在一头进行插入和删除,队列在一端插入,另一端删除
3.在栈中遍历数据需要扫描全部元素,而队列可以从两端扫描,所以相对来说队列比较快
本文介绍了栈和队列的基本操作,给出了C和C++实现的源代码链接。还列举了多个栈和队列的面试题,如括号匹配、用队列实现栈等,并给出分析。最后阐述了栈和队列的区别,包括进出顺序、操作位置和遍历速度等方面。
750

被折叠的 条评论
为什么被折叠?



