漫画算法-学习笔记(23)

漫画算法-小灰的算法之旅(23)

1. 如何用栈实现队列

Q: 用两个栈来模拟一个队列,要求实现队列的两个基本操作,入队和出队。

思路

栈的特点是先入后出(FILO),出入元素都是在同一端(栈顶)。

队列的特点是先入先出(FIFO),出入元素是在不同的两端(队头和队尾)。

由于我们可以使用两个栈,那么可以让其中一个栈作为队列的入口,负责插入新元素;另一个栈作为队列的出口,负责移除老元素。

队列的主要操作无非有两个:入队列、出队列。

在模拟入队列操作时:每一个元素都被压入到栈A中。

让元素1入队列。

让元素2入队列。

让元素3入队列。

这时,我们希望最先入队的元素1出队列,需要怎么做呢?

让栈A中的所有元素按顺序出栈,再按照出栈顺序压入栈B。这样以来,元素从栈A弹出并压入栈B的顺序是3,2,1;和当初进入栈A的顺序1,2,3,是相反的。

此时让元素1出队列,也就是让元素1从栈B中弹出。

让元素2出队列。

如果此时,又想做入队列操作呢?当有新元素入队时,重新把新元素压入栈A中。

让元素4入队。

此时出队操作仍然从栈B中弹出元素。

让元素3出队。

此时栈B已经空了,如果再想出队列该怎么办呢?也不难,只要栈A中还有元素,就像刚才一样,把栈A中的元素弹出并压入栈B即可。

让元素4出队列。

时间复杂度

入队操作的时间复杂度显然是O(1)。至于出队操作,如果涉及栈A和栈B的元素迁移,那么一次出队的时间复杂度是O(n);如果不用迁移,时间复杂度是O(1)。

代码实现


private Stack<Integer> stackA=new Stack<Integer>();
private Stack<Integer> stackB=new Stack<Integer>();

/**
* 入队操作
* param element 入队的元素
*/
public void enQueue(int element){
  stackA.push(element);
}


/**
* 出队操作
*/
public Integer deQueue(){
  if(stackB.isEmpty()){
    if(stackA.isEmpty()){
      return null;
    }
    transfer();
  }
  return stackB.pop();
}

/**
*  栈A元素转移到栈B
*/
private void transfer(){
  while(!stackA.isEmpty()){
    stackB.push(stackA.pop());
  }
}

public static void main(String[] args) throws Exception{
  StackQueue stackQueue=new StackQueue();
  stackQueue.enQueue(1);
  stackQueue.enQueue(2);
  stackQueue.enQueue(3);
  System.out.println(stackQueue.deQueue());
  System.out.println(stackQueue.deQueue());
  stackQueue.enQueue(4);
  System.out.println(stackQueue.deQueue());
  System.out.println(stackQueue.deQueue());
}
golang实现
type MyQueue struct {
	inStack  []int
	outStack []int
}

func NewQueue() *MyQueue {
	return &MyQueue{}
}

// 将元素插入到队尾
func (q *MyQueue) enQueue(x int) {
	q.inStack = append(q.inStack, x)
}

// 将元素出队列
func (q *MyQueue) deQueue() int {
	// 如果outStack 为空,则将inStack栈中的数据出栈,并入栈到outStack
	if q.isOutStackEmpty() {
		q.inStackToOutStack()
	}
	val := q.outStack[len(q.outStack)-1]
	// outStack 出栈
	q.outStack = q.outStack[:len(q.outStack)-1]
	return val
}

func (q *MyQueue) inStackToOutStack() {
	for len(q.inStack) > 0 {
		// outStack 入栈
		q.outStack = append(q.outStack, q.inStack[len(q.inStack)-1])
		// inStack 出栈
		q.inStack = q.inStack[:len(q.inStack)-1]
	}
}

// 返回当前队头头部元素
func (q *MyQueue) peek() int {
	if q.isOutStackEmpty() {
		q.inStackToOutStack()
	}
	return q.outStack[len(q.outStack)-1]
}

func (q *MyQueue) isEmpty() bool {
	return q.isInStackEmpty() && q.isOutStackEmpty()
}
func (q *MyQueue) isOutStackEmpty() bool {
	return len(q.outStack) == 0
}
func (q *MyQueue) isInStackEmpty() bool {
	return len(q.inStack) == 0
}

func main() {

	stackQueue := NewQueue()
	stackQueue.enQueue(1)
	stackQueue.enQueue(2)
	stackQueue.enQueue(3)
	log.Println("deQueue value is",stackQueue.deQueue())
	log.Println("deQueue value is",stackQueue.deQueue())
	stackQueue.enQueue(4)
	log.Println("myQueue head value is",stackQueue.peek())
	log.Println("deQueue value is",stackQueue.deQueue())
	log.Println("myQueue is empty:",stackQueue.isEmpty())
	log.Println("deQueue value is",stackQueue.deQueue())
	log.Println("myQueue is empty:",stackQueue.isEmpty())
}
总结

本题思路其实还是比较清晰的。
将一个栈当作输入栈inStack,用于压入enQueue传入的数据;另一个栈outStack当作输出栈,用于deQueuepeek操作。每次deQueuepeek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值