算法习题3:设计包含min函数的栈

本文介绍了一种特殊的数据结构——栈,并在此基础上增加了min函数以获取栈中最小元素,通过三种不同的实现方式,确保了min、push及pop操作的时间复杂度均为O(1)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计包含min函数的栈
定义栈的数据结构 ,要求添加一个min函数,能够得到栈的最小元素。
要求函数min、push以及po p的时间复杂度 都是O(1)。

我综合了别人的博客 最后一种给的一种非常规解法
其他两种是看见这个题目后想到的,第二种是把第一种另外一个栈结合到原栈里,可能与原有的栈略有不同,但是也是先进后出的结构!

第一种  两个栈解决时间
  要考虑的问题是:如何实现时间复杂度为1,那就是得牺牲空间复杂度了,所以我们这里需要有个指针指向
  当前最小的节点,但是如果pop掉了该节点,那么最小指针应该指向谁呢?
  重新遍历找最小???那pop的时间复杂度变成o(n)
  所以这里还需要一个链表专门记录最小节点的

所以对于这种时间复杂度有要求的一般都是再建立一个辅助空间来存储所需信息
我这里建立的栈是head<--node<--。。。<--  这种反向链表形式

//============================================================================
// Name        : MinStack.cpp
// Author      : YLF
// Version     :
// Copyright   : Your copyright notice
// Description : get min in stack, Ansi-style
// 要考虑的问题是:如何实现时间复杂度为1 ,那就是得牺牲空间复杂度了,所以我们这里需要有个指针指向
// 当前最小的节点,但是如果pop掉了该节点,那么最小指针应该指向谁呢?
// 重新遍历找最小???那pop的时间复杂度变成o(n)
// 所以这里还需要一个链表专门记录最小节点的
//============================================================================

#include <iostream>
using namespace std;

struct LinkNode{
	int value;
	LinkNode* pPre;
};

void push(int);
LinkNode* pop();
LinkNode* getMin();
void printStack();
void preInput();

LinkNode* head = NULL;
LinkNode* cur = NULL;

LinkNode* minHead = NULL;
LinkNode* minCur = NULL;

int main() {
	int input = 0;
	preInput();
	cin>>input;

	while(input!=-1){

		switch(input){
		case 1:
			cout<<"input num:";
			cin>>input;
			push(input);
			break;
		case 2:
			LinkNode* pp;
			pp = pop();
			if(pp == NULL)
				cout<<"stack is null"<<endl;
			else
				cout<<"pop:"<<pp->value<<endl;
			break;
		case 3:
			if(minCur!=NULL)
				cout<<"min value="<<minCur->value<<endl;
			else
				cout<<"the stack is null"<<endl;
			break;
		}
		printStack();
		cout<<endl;

		preInput();
		cin>>input;
	}
	return 0;
}

void push(int value)
{
	LinkNode* p = new LinkNode();
	p->value = value;
	p->pPre = NULL;
	if(head == NULL){
		head = p;
		//给最小的单独建立一个链表
		LinkNode* pMin = new LinkNode();
		pMin->value = value;
		pMin->pPre = NULL;
		minHead = pMin;
		minCur = pMin;
	}
	else{
		p->pPre = cur;
		if(value <= minCur->value){
			LinkNode* pMin = new LinkNode();
			pMin->value = value;
			pMin->pPre = minCur;
			minCur = pMin;
		}
	}
	cur = p;
}

LinkNode* pop()
{
	if(head == NULL)
		return NULL;
	LinkNode* temp = cur;

	if(cur!=head){
		cur = cur->pPre;
	}else{
		head = NULL;
		cur = NULL;
		minHead = NULL;
		minCur = NULL;
		return temp;
	}

	if(temp->value == minCur->value){
		LinkNode* minTemp = minCur;
		minCur = minCur->pPre;
		delete minTemp;
	}
	return temp;
}

LinkNode* getMin(){
	return minCur;
}

void printStack()
{
	LinkNode* temp = cur;

	cout<<"----out-----"<<endl;
	cout<<"stack:"<<endl;
	while(temp!=NULL){
		cout<<temp->value<<" ";
		temp = temp->pPre;
	}

	temp = minCur;
	cout<<endl<<"min stack:"<<endl;
	while(temp!=NULL){
		cout<<temp->value<<" ";
		temp = temp->pPre;
		}
	cout<<endl<<"-----------"<<endl;
}

void preInput(){
	cout<<"---------------menu--------------------"<<endl;
	cout<<"input 1:push  2:pop  3:getMin   -1 exit"<<endl;
	cout<<"---------------------------------------"<<endl;
}


第二种:优化第一种,把另一个栈信息写入第一个栈

就是建立这样一个结构体
struct LinkNode{
	int value;
	LinkNode* pPre;
	LinkNode* pMinPre;//这个指针用来指向比他更小的前一个节点
};

这个结构体每个节点如果是相对以前来说最小的,那么他存放一个指针,记录前面被取代的那个元素位置
这样就可以里利用链表的性质找到之前更小的了  例如1是最小的,如果pop()到1 的话,那么最小指针指向pMinPre也就是3了
图一
//============================================================================
// Name        : MinStack_2.cpp
// Author      : YLF
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
using namespace std;


void push(int);
int pop();
int getMin();
void printStack();
void preInput();

//return -1 means exit

struct LinkNode{
	int value;
	LinkNode* pPre;
	LinkNode* pMinPre;//这个指针用来指向比他更小的前一个节点
};

LinkNode* head;
LinkNode* cur;
LinkNode* pMin;//指向当前最小的

int main() {
	int input = 0;
		preInput();
		cin>>input;

		while(input!=-1){
			int re = 0;
			switch(input){
			case 1:
				cout<<"input num:";
				cin>>input;
				push(input);
				break;
			case 2:
				re = pop();
				if(-1 == re)
					cout<<"stack is null"<<endl;
				else
					cout<<"pop:"<<re<<endl;
				break;
			case 3:
				re = getMin();
				if(-1 != re)
					cout<<"min value="<<re<<endl;
				else
					cout<<"the stack is null"<<endl;
				break;
			}
			printStack();
			cout<<endl;

			preInput();
			cin>>input;
		}
		return 0;
}

void push(int value)
{
	//新建节点
	LinkNode* p = new LinkNode();
	p->value = value;
	p->pPre = NULL;
	p->pMinPre = NULL;

	if(head == NULL){
		head = p;
		pMin = p;
	}else{
		p->pPre = cur;
		if(value <= pMin->value){
			p->pMinPre = pMin;
			pMin = p;
		}
	}
	cur = p;
}

int pop()
{
	LinkNode* p = cur;

	if(head == NULL)
		return -1;
	int re = 0;
	if(head == cur){
		re = cur->value;
		head = NULL;
		cur = NULL;
		pMin = NULL;
	}else{
		if(cur == pMin)
			pMin = pMin->pMinPre;
		cur = cur->pPre;
	}

	delete p;
	return re;
}

int getMin(){
	if(pMin!=NULL){
		return pMin->value;
	}
	return -1;
}

void printStack()
{
	LinkNode* temp = cur;

	cout<<"----out-----"<<endl;
	cout<<"stack:"<<endl;
	while(temp!=NULL){
		cout<<temp->value<<" ";
		temp = temp->pPre;
	}

	temp = pMin;
	cout<<endl<<"min stack:"<<endl;
	while(temp!=NULL){
		cout<<temp->value<<" ";
		temp = temp->pMinPre;
		}
	cout<<endl<<"-----------"<<endl;
}

void preInput(){
	cout<<"---------------menu--------------------"<<endl;
	cout<<"input 1:push  2:pop  3:getMin   -1 exit"<<endl;
	cout<<"---------------------------------------"<<endl;
}


第三种:存储信息充分利用
大家可以参见上面这篇博客

就是存放的value不再是原始数据,而是转换过的数据,
比如

我们先存放第一个元素,5  存放后面的元素 如果
(1)value <= minVlaue    则存放value-minValue   例如4 存放的是4-5 (肯定<=0这是个重要的分水岭,用来恢复前一个最小值)  同时设置最小值
(2)value > minValue      则存放value-minValue    例如7  则7-5 肯定大于0 喽  所以pop时候注意如果<=0 那么要更新最小值

那么pop时候也要进行响应转换  见图描述 时间关系 代码以后再写了,大家理解下 还是挺容易的  想法不错!赞!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值