算法基础:栈和队列

栈和队列

目录

概念

常见操作

进制转换器(栈的方式)

栈实现括号匹配

算法考题

两个栈实现一个队列

思路

代码

两个队列实现栈

思路

代码

最小栈

题目描述

思路

代码实现

每日温度(Medium)

题目描述

代码实现

最大的矩形

题目描述

思路:

代码实现



概念

栈和队列也是线性表,其特殊性在于栈和队列的基本操作是线性表操作的子集(也具有顺序结构和链式结构),它们是操作受限的线性表,因此,可称为限定性的数据结构。

栈是一种只能从表的一端存取数据且遵循 "先进后出" 原则的线性存储结构。

队列中数据的进出要遵循 "先进先出" 的原则

常见操作

进制转换器(栈的方式)

例如,用户提供了一个十进制数:10,要求将此数据以二进制形式转换,则通过进制转换器转换的最终结果应该:1010。

#include <stdio.h>
#include <string.h>
#include <math.h>
int top=-1;//top变量时刻表示栈顶元素所在位置
void push(char * a,char elem){
    a[++top]=elem;
}
void pop(char * a){
    if (top==-1) {
        return ;
    }
    //输出时要按照正确的格式显示给用户
    if (a[top]>=10) {
        printf("%c",a[top]+55); //因为是10到35所以,要加55变成A-Z对应的ASCII码值,65-90
    }else{
        printf("%d",a[top]);
    }
    top--;
}
//将各进制数转换成十进制数
int scaleFun(char * data,int system){
    int k=(int)strlen(data)-1;
    int system_10_data=0;
    int i;
    for (i=k; i>=0; i--) {
        int temp;
        if (data[i]>=48 && data[i]<=57) { //0-9
            temp=data[i]-48;
        }else{
            temp=data[i]-55;  //A-Z的ASCII码值65-90,所以减55变成10-35
        }
        system_10_data+=temp*pow(system, k-i); // double pow(double x, double y) 返回 x 的 y 次幂,即 xy。
    }
    return system_10_data;
}
int main() {
    char data[100];
    int system;
    int newSystem;
    int system_10_data;
    printf("进制转换器,请输入原数据的进制(2-36):");
    scanf("%d",&system);
    getchar();
    printf("请输入要转换的数据:");
    scanf("%s",data);
    getchar();
    system_10_data=scaleFun(data, system);
    printf("请输入转换后的数据的进制:");
    scanf("%d",&newSystem);
    getchar();
    while (system_10_data/newSystem) { //十进制转其它进制,除其它进制,倒取余,用商继续除.
        push(data,system_10_data%newSystem );//倒取余,所以用栈
        system_10_data/=newSystem;
    }
    push(data,system_10_data%newSystem);
    printf("转换后的结果为:\n");
    while (top!=-1) {
        pop(data);
    }
}

栈实现括号匹配

//这次实现中涉及到的括号只包括小中大三种
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MaxSize 100
typedef struct{
	char data[MaxSize];
	int top;
}SqStack;

//顺序栈的初始化
bool InitStack(SqStack &S)
{
    for(int i=0;i<MaxSize;i++)S.data[i]='\0';
    S.top=-1;
    return true;
}

//判断栈空:
bool StackEmpty(SqStack S)
{
	if(S.top==-1)return true;
	else return false;
} 

//顺序栈入栈操作:
bool Push(SqStack &S,char x)
{
	if(S.top==(MaxSize-1))return false;
	S.data[++S.top]=x;
	return true;
}

//顺序栈出栈操作:
bool Pop(SqStack &S,char &x) 
{
	if(S.top==-1)return false;
	x=S.data[S.top--];
	return true;
}

//基于顺序栈的括号匹配算法: 
bool bracketCheck(char str[],int length)
{
	SqStack S;
	
	InitStack(S);
	for(int i=0;i<length;i++)
	{
		if(str[i]=='('||str[i]=='{'||str[i]=='[')
		{
			Push(S,str[i]);
		}
		else{
			if((str[i]==')'||str[i]=='}'||str[i]==']')&&StackEmpty(S))return false;
			
			char topElem;
			Pop(S,topElem);
			if(str[i]==')'&&topElem!='(')return false;
			if(str[i]=='}'&&topElem!='{')return false;
			if(str[i]==']'&&topElem!='[')return false;
		}
	}
	return StackEmpty(S);
}

int main()
{
	int j=0;char x;
	char g[MaxSize];
	memset(g,'\0',MaxSize);
	
	printf("输入你的括号组合:\n");
	scanf("%c",&x);
	while(x!='!')
	{
		g[j]=x;
		j++;
		scanf(" %c",&x);
	}
	
	printf("括号组合是否规范:  ");
	if(bracketCheck(g,j))printf("是\n");
	else printf("否\n");
	
	return 0; 
}

算法考题

两个栈实现一个队列

思路

(1) 使用两个栈A,B,其中假定A负责push操作,B负责pop操作。使用一个变量back_elem来存储最后添加的元素。

(2) 实现队列的push操作, 每次进行添加操作,都会相应得对栈A进行添加元素。并对back_elem赋值

(3) 实现队列的pop操作,每次进行删除操作,因为栈B负责pop操作,

首先判断栈B是否为空?

a.如果B为空,则判断A是否为空?

如果A也为空,则输出错误信息,此时队列为空。

如果A不为空,则将栈A中的所有数据存储到B中。执B.push(A.top()), A.pop(). 然后在对栈B 执行B.pop()操作,将队列的头元素删除

b.如果B不为空, 则直接对B执行 B.pop()操作。

(4)实现队列的front()操作,方法如pop操作相同,只是在最后一步使用B.top()返回值。

(5)实现队列的back()操作,因为我们变量back_elem保存着最后一个输入的数据,故直接将其返回。

(6)实现队列的size()操作,和empty()操作,就是对A,B分别执行操作。

代码

#include <iostream>
#include <stack>
#include <string>
using namespace std;
 
template<typename T>
class Queue {
private:
	stack<T>stackA;	//栈A
	stack<T>stackB;	//栈B
	T back_elem;	//用于存储新添加的元素
public:
	void push(T elem);	//将新元素压入队列(压入栈A)中
	void pop();			//将元素弹出队列(从栈B中弹出)
	T front();			//队首元素
	T back();			//队尾元素
	int size()const;    //队列长度
	bool empty()const;  //队列是否为空
};
 
/*
入队操作
实现队列的push操作, 每次进行添加操作,都会相应得对栈A进行添加元素。并对back_elem赋值
*/
template<typename T>
void Queue<T>::push(T elem)
{
	stackA.push(elem);//将元素压入队列
	back_elem = elem;	//存储新添加的元素
}
 
/*
出队操作
实现队列的pop操作,每次进行删除操作,因为栈B负责pop操作。
首先判断栈B是否为空?
a.如果栈B为空,则判断A是否为空?
		如果A也为空,则输出错误信息,此时队列为空。
		如果A不为空,则将栈A中的所有数据存储到B中。执B.push(A.top()), A.pop().然后在对栈B
			执行,B.pop()操作,将队列的头元素删除
b.如果栈B不为空, 则直接对栈B执行 B.pop()操作。
*/
template<typename T>
void Queue<T>::pop()
{
	//判断栈B是否为空?
	if (!stackB.empty())	//栈B不为空, 则直接对栈B执行 B.pop()操作。
	{
		stackB.pop();
	}
	else if (!stackA.empty())	//栈B为空,则判断栈A是否为空?栈A不为空,则将栈A中的所有数据
   //存储到B中。执B.push(A.top()), A.pop().然后在对栈B执行,B.pop()操作,将队列的头元素删除
	{
		stackB.push(stackA.top());
		stackA.pop();
	}
	else
	{
		std::cout << "error pop(),empty queue!" << std::endl;
	}
}
 
/*
队首元素
*/
template<typename T>
T Queue<T>::front()
{
	if (!stackB.empty())
	{
		return stackB.top();
	}
	else if (!stackA.empty())
	{
		while (!stackA.empty())
		{
			stackB.push(stackA.top());
			stackA.pop();
		}
		return stackB.top();
	}
	else
	{
		std::cout << "error front(),empty queue!" << std::endl;
	}
}
 
/*
队尾元素
*/
template<typename T>
T Queue<T>::back()
{
	if (!empty())
	{
		return back_elem;
	}
	else
	{
		std::cout << "error back(),empty queue!" << std::endl;
	}
}
 
/*
队列长度
*/
template<typename T>
int Queue<T>::size() const
{
	return stackA.size() + stackB.size();
}
 
/*
队列是否为空
*/
template<typename T>
bool Queue<T>::empty() const {
	return stackA.empty() && stackB.empty();
}
 
int main()
{
	Queue<int>queue;
	//入队操作
	queue.push(1);
	queue.push(2);
	queue.push(3);
	queue.push(4);
	cout << "Four times push() After:" << endl;
	//队首元素
	cout << "The queue front:" << queue.front() << endl;
	//队尾元素
	cout << "The queue back:" << queue.back() << endl;
	//队列size
	cout << "The queue size:" << queue.size() << endl;
	//出队操作
	queue.pop();
	queue.pop();
	queue.pop();
	queue.pop();
	cout << "----------------------------" << endl;
	cout << "Four times pop() After:" << endl;
	//队首元素
	cout << "The queue front:" << queue.front() << endl;
	//队尾元素
	cout << "The queue back:" << queue.back() << endl;
	//队列size
	cout << "The queue size:" << queue.size() << endl;
 
	//system("pause");
	return 0;
}

两个队列实现栈

思路

入栈和出栈,都在 queue1 中完成,而 queue2 作为中转空间。

  • 入栈:直接入 queue1 即可。
  • 出栈:把 queue1 中除最后一个元素外的所有元素都移动到 queue2 中,再将 queue1 转化为queue2,queue2转化为queue1

代码

#include<iostream>
#include<queue>
using namespace std;
 
template<typename T>
class CStack
{
public:
	CStack(){}
	~CStack(){}
	void push(const T &val)
	{
		if(queue1.empty() && queue2.empty())  //两个队列为空,则插入queue1
		{
			queue1.push(val);     
		}
 
		if(queue1.empty())         //如果queue1为空,则插入queue2
		{
			queue2.push(val);
		}
		else
		{
			queue1.push(val);      //如果queue2为空,则插入queue1
		}
	}
	T pop()
	{
		if(queue1.empty())        //如果queue1为空
		{
			if(queue2.empty())    //如果queue2为空,则说明栈为空
			{
				throw new exception("stack is empty");
			}
			else
			{
				if(queue2.size() == 1)   //如过queue2只有一个元素,直接退出
				{
					T result = queue2.front();
					queue2.pop();
					return result;
				}
				else
				{
					while(queue2.size() != 1)  //依次退出队中元素,直到队列中只有队尾元素
					{
						queue1.push(queue2.front());
						queue2.pop();
					}
					T result = queue2.front();  //将队尾元素退出
					queue2.pop();
					return result;
				}
			}
		}
		else          //如果queue1不为空
		{
			if(queue1.size() == 1)     //如果queue1中只有队尾元素时,直接退出
			{
				T result = queue1.front();
				queue1.pop();
				return result;
			}
			else
			{
				while(queue1.size() != 1)  //依次退出queue1中的元素到queue2,直到queue1只有队尾元素
				{
					queue2.push(queue1.front());
					queue1.pop();
				}
				T result = queue1.front(); //将队尾元素弹出
				queue1.pop();
				return result;
			}
		}
		
	}
private:
	queue<T> queue1;
	queue<T> queue2;
};
 
int main()
{
	CStack<char> stack;
	
	//测试用例1:
	stack.push('a');           //元素a入栈
	stack.push('b');           //元素b入栈
	stack.push('c');           //元素c入栈
	cout << "第1次出栈元素是:" << stack.pop() << endl;
	cout << "第2次出栈元素是:" << stack.pop() << endl;
	stack.push('d');           //元素d入栈
	cout << "第3次出栈元素是:" << stack.pop() << endl;
	cout << "第4次出栈元素是:"<< stack.pop() << endl;
	return 0;
}

最小栈

题目描述

设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

push(x) – 将元素 x 推入栈中。
pop() – 删除栈顶的元素。
top()– 获取栈顶元素。
getMin() – 检索栈中的最小元素。

思路

通过双栈来实现最小栈,因为这样可以在常数时间复杂度中得到栈内元素最小值。这是典型的空间换时间的操作。

入栈时要对两个栈同时进行操作,normal栈正常压栈,对于min栈进行参数条件判定:如果此时

最小栈为空 (说明是第一次压栈)

这个值小于此时最小栈的栈顶元素 (说明是压入了一个小于已入栈全部元素的值)

两条件是任满足其一即可进行最小栈的压栈,否则不满足条件执行最小栈的栈顶元素压栈,说明压入的这个值不是最小值,将目前最小值再次压入最小栈。

代码实现

#include <stdio.h>
#include <stack>

class MinStack {
public:
    MinStack() {
    }
    void push(int x) {
    	_data.push(x);
    	if (_min.empty()){
	    	_min.push(x);
	    }
	    else{
	    	if (x > _min.top()){
	    		x = _min.top();
	    	}
    		_min.push(x);
    	}
    }
    void pop() {
    	_data.pop();
    	_min.pop();
    }
    int top() {
        return _data.top();
    }
    int getMin() {
        return _min.top();
    }
private:
	std::stack<int> _data;
	std::stack<int> _min;
};

每日温度(Medium)

题目描述

根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

题目解析

  • 维护一个递减栈,若当前元素大于栈顶元素,则说明温度升高,栈顶元素出栈,两者下标的差值即为栈顶元素所在下标的所求天数。
  • 直到当前元素小于栈顶元素,当前元素入栈,继续判断。
  • 时间复杂度:O(N)。
  • 空间复杂度:O(N)。

代码实现

int* dailyTemperatures(int* T, int TSize, int* returnSize) {
    int* result = (int*)malloc(sizeof(int)*TSize);
    // 用栈记录T的下标。
    int* stack_index = malloc(sizeof(int)*TSize);
    *returnSize = TSize;
    result[TSize-1] = 0;
    // 栈顶指针。
    int top = 0;
    
    for (int i = 0; i < TSize; i++)
        result[i] = 0;
    for (int i = 0; i < TSize; i++) {
        // 若当前元素大于栈顶元素,栈顶元素出栈。即温度升高了,所求天数为两者下标的差值。
        while (top > 0 && T[i] > T[stack_index[top-1]]) {
            result[stack_index[top-1]] = i-stack_index[top-1];
            top--;
        }
        
        // 当前元素入栈。
        stack_index[top] = i;
        top++;
    }
    
    return result;
}

最大的矩形

题目描述

在横轴上放了n个相邻的矩形,每个矩形的宽度是1,而第i(1 ≤ i ≤ n)个矩形的高度是hi。这n个矩形构成了一个直方图。例如,下图中六个矩形的高度就分别是3, 1, 6, 5, 2, 3。

请找出能放在给定直方图里面积最大的矩形,它的边要与坐标轴平行。对于上面给出的例子,最大矩形如下图所示的阴影部分,面积是10。

思路:

我们可以维持一个单调递增的栈,为了便于计算矩形宽度,我们在栈里存放单个矩形的位置。
我们从左到右遍历高度数组,对于每个矩形的高度p,如果p大于等于当前栈顶储存位置的高度q,我们将p的位置也压入栈中;

如果p小于q,我们将栈中元素弹出,纪录高度,并记录当前遍历到的矩形p与新栈顶位置之差(实际上还需要减1),作为宽度,并更新结果。

代码实现

int largestRectangleArea(vector<int>& heights) {
	heights.push_back(0);		//插入空矩形,弹出栈中剩余矩形
	int len = heights.size(), area = 0, pre_index, height, width;
	stack<int> indices;
	for (int i = 0; i < len; i++) {
		while (!indices.empty() && heights[indices.top()] > heights[i]) {	//检查栈是否为空
			pre_index = indices.top();		//储存栈顶矩形的位置
			indices.pop();
			height = heights[pre_index];	//储存高度
			if (indices.empty()) {			//避免操作空栈
				width = i;					//若弹出至栈为空,因栈的递增性,边界可向左延伸至0
			} else {
				width = i - indices.top() - 1;		//储存宽度
			}
			area = area > (width * height) ? area : (width * height);		//更新结果
		}
		indices.push(i);
	}
	return area;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ym影子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值