原文:
Imagine a (literal) stack of plates. If the stack gets too high, it might topple. Therefore, in real life, we would likely start a new stack when the previous stack exceeds some threshold. Implement a data structure SetOfStacks that mimics this. SetOfStacks
should be composed of several stacks, and should create a new stack once the previous one exceeds capacity. SetOfStacks.push() and SetOfStacks.pop() should behave identically to a single stack (that is, pop() should return the same values as it would if there
were just a single stack).
FOLLOW UP
Implement a function popAt(int index) which performs a pop operation on a specific sub-stack.
译文:
栈就像叠盘子,当盘子叠得太高时,就会倾斜倒下。因此,在真实的世界中,当一叠盘子 (栈)超过了一定的高度时,我们就会另起一堆,再从头叠起。实现数据结构SetOfStacks 来模拟这种情况。SetOfStacks由几个栈组成,当前一栈超出容量时,需要创建一个新的栈 来存放数据。SetOfStacks.push()和SetOfStacks.pop()的行为应当和只有一个栈时 表现的一样。
进一步地,
实现函数popAt(int index)在指定的子栈上进行pop操作。
思路:
我们要设计自己的栈(Stack),栈节点(Node)。然后用一个ArrayList<Stack>来构成栈组SetOfStacks。
我们的栈里面的节点以链表方式存储最佳,因为我们要频繁对栈顶栈底元素操作,如果用array将会导致大量的元素移动。所以我们用链表方式存储栈内的节点,同时对每个栈,我们还维护一个capacity的变量,用来track当前栈内元素个数!
因此,就不难理解Node类的数据结构,Stack类中比较有意思的是join,相当于连接两个节点,在每push进一个新元素时,新元素都要和旧的栈顶元素做join。还有一个函数叫做removeBottom,并不expose给user调用,而是在SetOfStacks的leftShift函数中会用到。
至于SetOfStacks类,有一个原则就是,压入的新元素总是被压倒最后一个栈,这样能保证前面的栈都是满的!所以没有pushAt函数,但是我们支持popAt函数,就是制定在某个栈上pop,当某个栈(除了最后一个栈)被pop掉栈顶元素,那么那个栈就不是满的了,所以我们要递归地从下一个栈,把它的栈底元素移到当前栈。这就需要用到leftShift函数。具体步骤可参考代码,已经详细注释!
这样我们就封装好了SetOfStacks类,也是用户唯一能看到的类。
package Stack_Queue;
import java.util.ArrayList;
public class S3_3 {
//=================================== Node 自定义栈节点,以链表节点方式存在
static class Node {
public Node above;
public Node below;
public int value;
public Node (int value) {
this.value = value;
}
}
//=================================== Stack 自定义的Stack类,以链表方式存储数据!
static class Stack {
private int capacity;
public Node top;
public Node bottom;
public int size = 0;
public Stack(int capacity) {
this.capacity = capacity;
}
// 判断stack是否满了
public boolean isFull() {
return size == capacity;
}
// 判断stack是否为空
public boolean isEmpty() {
return size == 0;
}
// 让栈内的两个元素产生关联
public void join(Node above, Node below) {
if(below != null) {
below.above = above;
}
if(above != null) {
above.below = below;
}
}
// 压入一个元素
public boolean push(int val) {
if(size >= capacity) {
return false;
}
size++;
Node newNode = new Node(val);
if(size == 1) { // 该初始化bottom的情况
bottom = newNode;
}
join(newNode, top); // 把newNode放在原top的上面
top = newNode; // 更新栈顶指针
return true;
}
// 弹栈
public int pop() {
Node ret = top;
top = top.below; // 更新栈顶指针
size--; // 更新栈size
return ret.value;
}
// 移除栈底元素,返回被移除的值
public int removeBottom() {
Node ret = bottom;
bottom = bottom.above;
if(bottom != null) {
bottom.below = null;
}
size--;
return ret.value;
}
}
//=================================== SetOfStacks 栈组
static class SetOfStacks {
ArrayList<Stack> stacks = new ArrayList<Stack>();
public int capacity;
public SetOfStacks(int capacity) {
this.capacity = capacity;
}
// 从stacks中得到最后一个Stack
public Stack getLastStack() {
if(stacks.size() == 0) {
return null;
}
return stacks.get(stacks.size() - 1);
}
// 把元素压入SetOfStacks
public void push(int val) {
Stack last = getLastStack();
if(last != null && !last.isFull()) {
last.push(val);
} else{ // 最后一个stack已经满了,或者stacks为空,必须新建最后一个stack
Stack stack = new Stack(capacity);
stack.push(val);
stacks.add(stack);
}
}
// 从SetOfStacks中弹栈
public int pop() {
Stack last = getLastStack();
int val = last.pop();
if(last.size == 0) { // 如果最后一个栈空了,就从stacks中移除
stacks.remove(stacks.size()-1);
}
return val;
}
// 在第index个栈上弹栈
public int popAt(int index) {
return leftShift(index, true);
}
// 一个重要的辅助函数,移除当前栈顶元素,并利用递归移动下一个栈底元素到当前栈顶
public int leftShift(int index, boolean removeTop) {
Stack stack = stacks.get(index);
int removedItem;
if(removeTop) {
removedItem = stack.pop();
} else {
removedItem = stack.removeBottom();
}
if(stack.isEmpty()) {
stacks.remove(index);
} else if(index+1 < stacks.size()) { // 确保有下一个栈,否则证明现在的栈已经是最后一个栈了!
// 把下一个栈的栈底元素移到当前栈的栈顶,这样能保证除了最后一个栈,其他栈都是满的!
int v = leftShift(index+1, false);
stack.push(v);
}
return removedItem;
}
// 查看SetOfStacks栈组是否为空,只要看最后一个栈是否为空即可
public boolean isEmpty() {
Stack last = getLastStack();
return last==null || last.isEmpty();
}
}
public static void main(String[] args) {
int capacity_per_substack = 5;
SetOfStacks set = new SetOfStacks(capacity_per_substack);
for (int i = 0; i < 34; i++) {
set.push(i);
}
for (int i = 0; i < 34; i++) {
System.out.println("Popped " + set.pop());
}
}
}
分享到:
相关推荐
Stack_Queue_Stack_源码
c++stack_和_queue用法,很好的介绍了STL中stack和queue的用法,及其使用方法
前端大厂最新面试题-stack_queue
Data structure: Stack->Queue Example
常用的C++数据结构算法,包括队列、堆栈、链表...等.以模板类型式实现
本源码实现了两个类,一个是带有最大值的栈和一个是带有最大值的队列。栈利用了两个C++的stack,队列利用了C++的queue。
实现栈,队列,链表的简单操作,其中链表包括单链表,双链表,循环链表。
NXP 的 LIN 协议栈,之前一直用的 4.5.7 的版本,使用过程中有好几个问题都是手动修复的。最近在官网上看见了 4.6.6 的版本,下载测试了一遍,发现之前手动修复的几个问题都已经 OK 了。
内容包括: 嵌入式TCPIP协议栈应用主机端程序(VC6源码);如何构造嵌入式Linux系统;基于ARM的嵌入式TCPIP协议的实现等
Java_Stack_Queue Java中使用链表的堆栈和队列实现
Pro_MERN_Stack_Full_Stack_Web_App_Development_with_Mongo,_Express,_React,_and_Node
Lists_Stack_Queue_PriorityQueue.java
TI公司提供2015年11月最新基于ZigBee无线智能家居开发协议(Z_Stack_Home_1.2.2a.exe) 由于只能上传小于60MB文件,将150MB原文件分割3个部分上传,分别为: Z_Stack_Home_1.2.2a.exe.part1.rar Z_Stack_Home_1.2.2a....
stack_queue_p2pchat_practice:只是使用Stack和Queue技术的Java实践聊天应用程序
tcpip_stack_v1_2_TCP,IP_TCP_IP_udpmac_UDP_tcp.zip
飞思卡尔官方最新LIN栈源码,可用于LIN应用开发参考和LIN的学习
J1939协议栈源码,有详细的使用说明,在KEIL C166 (XC2331D)上编译通过。
开源的usbport 的usb主机协议栈,本协议栈可以支持多种不同的usb控制芯片,芯片的驱动可以动态加载和卸载,usbport采用C语言编写,可以使用于arm7/arm9/avr等多种平台。
TI_CC254X源代码 适合于IAR开发工具 TI_BLE_Sample_Applications_Guide GAP OSAL HAL