有趣的题目系列一:实现具有最大值、最小值、中间值的栈和队列
2013-10-08 21:22
477 查看
番外知识:Java反射详解
番外二:
===========================================
题目来源:
1)http://blog.csdn.net/bigheadzzy/article/details/8002253
2)http://blog.csdn.net/yangzhongblog/article/details/12391959
文章中的大部分题目描述、代码均参考自上面二处,加以自己的一些理解。
-------------------------------------------------------------------切割-------------------------------------------------------------------------
Works Applications的笔试题:
要求实现下面两个接口,实现要求:运行速度快,每个操作时间复杂度不能相差太大。
一,实现Immutable的FIFO队列。也就是说要求入队和出队不会改变原来的队列。
二,实现可以查找最大、最小和中间值的FIFO队列。
接口一要求实现的API有:入队、出队、峰值和大小
接口二要求实现的有:入队、出队、中间值、最小值、最大值和大小
仔细对比,发现接口一和接口二的入队和出队的返回值是不同的。
原作者的代码是这样子的(我自己敲了一遍):
第二题:作者的代码:
个人感觉是使用的策略较为原始。
下面是另一篇博文的转载:
在研究“如何实现具有最大值、最小值和中间值的栈和队列”前,我们先考虑以下问题,然后由此过度到题目问题。
1)如何用两个栈实现队列
2)如何用两个队列实现栈
3)如何实现包含获取最小值函数getMin()的栈
4)如何实现包含获取中间值函数getMedian()的栈
5)如何实现包含获取最小值函数getMin()的队列
在研究问题前,我们可以用2个栈模拟一下具体操作过程,可以总结出以下规律:
入队:元素插入stack1;
出队:如果stack2中为空,先将stack1中元素入栈stack2,然后再将stack2的栈顶元素出栈。否则直接将stack2中元素出栈。
队列为空:stack1和stack2同时为空
队列大小:为stack1和stack2大小之和
具体过程见下图(图来自《剑指offer》)
Java实现代码如下
[java] view
plaincopy
public class QueueByStack<E extends Comparable<E>> {
private LinkedList stack1=null;
private LinkedList stack2=null;
//constructor
public QueueByStack(){
stack1=new LinkedList();
stack2=new LinkedList();
}//end QueueByStack
public void insert(E e){
stack1.addLast(e);
}//end insert()
public E remove(){
if(!isEmpty()){
if(stack2.isEmpty()){
stack1Tostack2();
}//end if
return (E)stack2.removeLast();
}//end if
else{
System.out.println("queue is empty");
return null;
}//end else
}//end remove
//将stack1中元素入栈stack2
private void stack1Tostack2(){
while(!stack1.isEmpty()){
stack2.addLast(stack1.removeLast());
}//end while
}//end stack1ToStack2()
public boolean isEmpty(){
return stack1.isEmpty() && stack2.isEmpty();
}//end isEmpty()
public int size(){
return (stack1.size()+stack2.size());
}//end size()
/**
* @param args
*/
public static void main(String[] args) {
QueueByStack q=new QueueByStack();
q.insert(1);
q.insert(2);
q.insert(3);
System.out.println(q.remove());
System.out.println(q.remove());
q.insert(4);
System.out.println(q.remove());
System.out.println(q.remove());
}
}
还是先用2个队列模拟栈的出栈和入栈过程,可以得出以下规律:
入栈:压入非空的那个队列
出栈:将非空队列中的n-1个元素压入空的队列中,然后将第n个元素出栈。
具体过程见下图(图来自《剑指offer》)
问题:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的getMin函数。在该栈中,调用getMin、push及pop的时间复杂度都是O(1).
思路:用一个辅助栈stack2记住每次入栈stack1的当前最小值:在stack1入栈时,往stack2中加入当前最小值;stack1元素出栈时,stack2也出栈一个元素。最小值从stack2中获取及栈顶元素。
如果能对栈中元素进行排序,那么排序好的中间值即为所求。问题3和问题4的具体代码如下,代码中同时实现了获取栈的最大值、最小值和中间值
[java] view
plaincopy
public class Stack<E extends Comparable<E>> {
private LinkedList<E> heartStack=new LinkedList<E>();
private LinkedList<E> curMinStack=new LinkedList<E>(); //辅助栈,用于记录当前最小值
private LinkedList<E> curMaxStack=new LinkedList<E>(); //辅助栈,用于记录当前最大值
public void push(E e){
heartStack.push(e);
//当前最小值入栈curMinStack
E currentMin=curMinStack.peek();
if(currentMin.compareTo(e)>0){
curMinStack.push(e);
}//end if
else
curMinStack.push(currentMin);
//当前最大值入栈curMinStack
E currentMax=curMinStack.peek();
if(currentMax.compareTo(e)<0){
curMaxStack.push(e);
}//end if
else
curMaxStack.push(currentMax);
}//end push()
public E pull(){
if(isEmpty())
return null;
else{
E e=heartStack.poll();
curMinStack.poll();
curMaxStack.poll();
return e;
}//end else
}//end pull()
public E getMax(){
return curMaxStack.peek();
}//end getMax()
public E getMin(){
return curMinStack.peek();
}//end getMin()
public int size(){
return heartStack.size();
}//end size()
public boolean isEmpty(){
return heartStack.isEmpty();
}//end isEmpty()
public E getMedian(){
E[] e=(E[]) heartStack.toArray();
Arrays.sort(e);
return e[e.length/2];
}//end getMedian()
public E[] toArray(){
return (E[])heartStack.toArray();
}//end toArray()
}
问题:定义队列的数据结构,请在该类型中实现一个能够得到队列的最小元素的getMin函数。在该队列中,调用getMin、insert及remove的时间复杂度都是O(1).
思路1:用最小堆实现优先队列,获取最小值时间复杂度为O(nlogn),但优先队列只能获取最小值,remove获取的不是先入队的元素。
思路2:
如果能用栈有效地实现队列,而栈的获取最小值的操作又很容易实现,那么队列的获取最小值的操作也很容易完成。
因为上面可用2个栈实现栈的min函数,而用2个栈可以实现队列。所以可以用已实现了获取最小值的栈stack1和stack2实现队列,而整个队列的最小值从min(stack1.getMin(),stack2.getMin())中获取。具体实现代码见问题6中代码。
问题5解决了用O(1)时间获取栈的最小值,那么解决最大值的问题也迎刃而解。对于获取队列的中间值,可以将队列中所有元素排序,然后获取排序后的中间值。
具体实现如下(代码中的stack类为上述问题4中已实现的可以获取最大值和最小值的栈):
[java] view
plaincopy
public class ExmaPeekableQueue<E extends Comparable<E>> implements IExamPeekableQueue{
Stack stack1=new Stack();
Stack stack2=new Stack();
@Override
public void enqueue(Comparable e) {
stack1.push(e);
}//end enqueue()
@Override
public Comparable<E> dequeue() {
if(stack2.isEmpty()){
stack2.push(stack1.pull());
}//end if
return stack2.pull();
}//end dequeue()
@Override
public Comparable<E> peekMedian() {
Comparable[] arr=null; //用于存储队列中当前元素的数组
if(stack1.isEmpty()){
arr=stack2.toArray();
}//end if
else if(stack2.isEmpty()){
arr=stack1.toArray();
}//end if
else{
arr=new Comparable[size()];
Comparable[] arrE1=stack1.toArray();
Comparable[] arrE2=stack2.toArray();
//将2个栈中的元素复制到一个数组中
int i=0;
for(;i<stack1.size();i++){
arr[i]=arrE1[i];
}//end for
for(int j=0;j<stack2.size();j++){
arr[++i]=arrE2[j];
}//end for
}//end else
Arrays.sort(arr);
return arr[arr.length/2];
}//end peekMedian()
@Override
public Comparable peekMaximum() {
Comparable max1=stack1.getMax();
Comparable max2=stack2.getMax();
if(max1.compareTo(max2)>0){
return max1;
}//end if
else
return max2;
}
@Override
public Comparable<E> peekMinimum() {
Comparable min1=stack1.getMin();
Comparable min2=stack2.getMin();
if(min1.compareTo(min2)>0){
return min2;
}//end if
else
return min1;
}//end peekMinimum()
@Override
public int size() {
return (stack1.size()+stack2.size());
}//end size()
}//end class
========================
当然如果知识为了实现栈的最大值最小值中间值功能,而不要求使用队列,那完全可以使用内部栈解决。
在上面的博文中,使用两个队列(数组)实现的栈的先入后出功能,
可以有两种实现模式:
当在Array1 输入1 2 3 之后要删除3 则需要借助Array2 -> Array1:空 Array2为 1 2
这个时候新来的元素应该插入到Array1 还是Array2呢。
如果是插入到Array2 则,若后面需要删除某元素,则必须整个数组移到Array1
如果是插入到Array1 则只需要将后面输入的移到Array2。这是个较为优化的方案。
番外二:
<?>是1.5的新特性,泛型 如果是?表示可以放Object类型以及他的子类
如果是String 表示只能接收String以及他的子类。 Class<?> c表示这个Class可以放任意的类,?表示object(所有类都隐性从Object继承的) Class<String> c 只能接收String和他的子类(本文中的<E>也是这种情况) Class c和Class<?>c性质是一样的
===========================================
题目来源:
1)http://blog.csdn.net/bigheadzzy/article/details/8002253
2)http://blog.csdn.net/yangzhongblog/article/details/12391959
文章中的大部分题目描述、代码均参考自上面二处,加以自己的一些理解。
-------------------------------------------------------------------切割-------------------------------------------------------------------------
Works Applications的笔试题:
要求实现下面两个接口,实现要求:运行速度快,每个操作时间复杂度不能相差太大。
一,实现Immutable的FIFO队列。也就是说要求入队和出队不会改变原来的队列。
package jp.co.worksap.recruiting; public interface ExamImmutableQueue<E> { public ExamImmutableQueue<E> enqueue(E e); public ExamImmutableQueue<E> dequeue(); public E peek(); public int size(); }
二,实现可以查找最大、最小和中间值的FIFO队列。
public interface ExamPeekableQueue<E extends Comparable<E>> { public void enqueue(E e); public E dequeue(); public E peekMedian(); public E peekMaximum(); public E peekMinimum(); public int size(); }
接口一要求实现的API有:入队、出队、峰值和大小
接口二要求实现的有:入队、出队、中间值、最小值、最大值和大小
仔细对比,发现接口一和接口二的入队和出队的返回值是不同的。
原作者的代码是这样子的(我自己敲了一遍):
import java.lang.reflect.Array; import java.util.ArrayList; import java.util.NoSuchElementException; import java.util.Stack; public class ExamImmutableQueueImpl<E> implements ExamImmutableQueue<E>{ /*根据题目的要求是要求入队和出队均不影响原队列,所以这里用了final*/ private final E[] queue; private final int size; public ExamImmutableQueueImpl(){//构造函数不需要标注<E> queue = null; size = 0; } public ExamImmutableQueueImpl(E[] queue){ this.queue = queue; size = queue.length; } @Override public ExamImmutableQueue<E> enqueue(E e) { // TODO Auto-generated method stub if(e == null) throw new IllegalArgumentException(); //需要考虑的是在内部变量插入一个新的元素,如何返回一个新的ExamImmutableQueueImpl对象 //E[] clone = new E[size + 1];Java不可以定义泛型数组,ArrayList或者LinkedList是已经成型的泛型数组 /* * 可以考虑的一个方法 * ArrayList<E> arr = new ArrayList<E>(); E[] clone = (E[]) arr.toArray(); */ @SuppressWarnings("unchecked") E[] clone = (E[])Array.newInstance(e.getClass(), size + 1); clone[0] = e; if(size != 0) System.arraycopy(queue, 0, clone, 1, size); return new ExamImmutableQueueImpl<E>(clone); } @Override public ExamImmutableQueue<E> dequeue() { // TODO Auto-generated method stub ////////////////////////////////// /*题目要求的是FIFO,由入队我们得知新元素将被置在内部数组的首位,那删除应该从后面开始删除*/ if(size == 0) throw new NoSuchElementException(); //E[] clone = (E[]) new Object[size - 1]; 用Object取代也是一种好方法 @SuppressWarnings("unchecked") E[] clone = (E[]) new Object[size - 1]; System.arraycopy(queue, 0, clone, 0, size - 1); return new ExamImmutableQueueImpl<E>(clone); } @Override public E peek() { // TODO Auto-generated method stub if(size == 0) throw new NoSuchElementException(); return (E) queue[size - 1]; } @Override public int size() { // TODO Auto-generated method stub return this.size; } }
第二题:作者的代码:
package jp.co.worksap.recruiting; import java.util.Collections; import java.util.LinkedList; import java.util.NoSuchElementException; public class ExamPeekableQueueImpl<E extends Comparable<E>> implements ExamPeekableQueue<E> { private LinkedList<SortedNode> objList = new LinkedList<SortedNode>(); private SortedNode head = null; private SortedNode tail = null; class SortedNode implements Comparable<SortedNode>{ public E e; public SortedNode next; public SortedNode(E e, SortedNode sn) { this.e = e; next = sn; } @Override public int compareTo(SortedNode sn) { // TODO Auto-generated method stub return this.e.compareTo(sn.e); } } private void testNull() { if (objList.isEmpty()) { head = null; tail = null; throw new NoSuchElementException("No element"); } } @Override public void enqueue(E e) { // TODO Auto-generated method stub if (e == null) throw new NoSuchElementException("Null object to eunqueue"); if (head == null) { SortedNode sn = new SortedNode(e, null); head = sn; tail = sn; objList.add(sn); } else { SortedNode sn = new SortedNode(e, null); tail.next = sn; tail = sn; objList.add(sn); Collections.sort(objList); } } @Override public E dequeue() { // TODO Auto-generated method stub testNull(); SortedNode sn = head; head = head.next; objList.remove(sn); Collections.sort(objList); return sn.e; } @Override public E peekMedian() { // TODO Auto-generated method stub testNull(); return objList.get(objList.size()/2).e; } @Override public E peekMaximum() { // TODO Auto-generated method stub testNull(); return objList.getLast().e; } @Override public E peekMinimum() { // TODO Auto-generated method stub testNull(); return objList.getFirst().e; } @Override public int size() { // TODO Auto-generated method stub return objList.size(); } }
个人感觉是使用的策略较为原始。
下面是另一篇博文的转载:
在研究“如何实现具有最大值、最小值和中间值的栈和队列”前,我们先考虑以下问题,然后由此过度到题目问题。
1)如何用两个栈实现队列
2)如何用两个队列实现栈
3)如何实现包含获取最小值函数getMin()的栈
4)如何实现包含获取中间值函数getMedian()的栈
5)如何实现包含获取最小值函数getMin()的队列
1 如何用两个栈实现队列
在研究问题前,我们可以用2个栈模拟一下具体操作过程,可以总结出以下规律:入队:元素插入stack1;
出队:如果stack2中为空,先将stack1中元素入栈stack2,然后再将stack2的栈顶元素出栈。否则直接将stack2中元素出栈。
队列为空:stack1和stack2同时为空
队列大小:为stack1和stack2大小之和
具体过程见下图(图来自《剑指offer》)
Java实现代码如下
[java] view
plaincopy
public class QueueByStack<E extends Comparable<E>> {
private LinkedList stack1=null;
private LinkedList stack2=null;
//constructor
public QueueByStack(){
stack1=new LinkedList();
stack2=new LinkedList();
}//end QueueByStack
public void insert(E e){
stack1.addLast(e);
}//end insert()
public E remove(){
if(!isEmpty()){
if(stack2.isEmpty()){
stack1Tostack2();
}//end if
return (E)stack2.removeLast();
}//end if
else{
System.out.println("queue is empty");
return null;
}//end else
}//end remove
//将stack1中元素入栈stack2
private void stack1Tostack2(){
while(!stack1.isEmpty()){
stack2.addLast(stack1.removeLast());
}//end while
}//end stack1ToStack2()
public boolean isEmpty(){
return stack1.isEmpty() && stack2.isEmpty();
}//end isEmpty()
public int size(){
return (stack1.size()+stack2.size());
}//end size()
/**
* @param args
*/
public static void main(String[] args) {
QueueByStack q=new QueueByStack();
q.insert(1);
q.insert(2);
q.insert(3);
System.out.println(q.remove());
System.out.println(q.remove());
q.insert(4);
System.out.println(q.remove());
System.out.println(q.remove());
}
}
2 如何用两个队列实现栈
还是先用2个队列模拟栈的出栈和入栈过程,可以得出以下规律:入栈:压入非空的那个队列
出栈:将非空队列中的n-1个元素压入空的队列中,然后将第n个元素出栈。
具体过程见下图(图来自《剑指offer》)
3 如何实现包含获取最小值函数的栈
问题:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的getMin函数。在该栈中,调用getMin、push及pop的时间复杂度都是O(1).思路:用一个辅助栈stack2记住每次入栈stack1的当前最小值:在stack1入栈时,往stack2中加入当前最小值;stack1元素出栈时,stack2也出栈一个元素。最小值从stack2中获取及栈顶元素。
4 如何实现包含获取中间值函数的栈
如果能对栈中元素进行排序,那么排序好的中间值即为所求。问题3和问题4的具体代码如下,代码中同时实现了获取栈的最大值、最小值和中间值[java] view
plaincopy
public class Stack<E extends Comparable<E>> {
private LinkedList<E> heartStack=new LinkedList<E>();
private LinkedList<E> curMinStack=new LinkedList<E>(); //辅助栈,用于记录当前最小值
private LinkedList<E> curMaxStack=new LinkedList<E>(); //辅助栈,用于记录当前最大值
public void push(E e){
heartStack.push(e);
//当前最小值入栈curMinStack
E currentMin=curMinStack.peek();
if(currentMin.compareTo(e)>0){
curMinStack.push(e);
}//end if
else
curMinStack.push(currentMin);
//当前最大值入栈curMinStack
E currentMax=curMinStack.peek();
if(currentMax.compareTo(e)<0){
curMaxStack.push(e);
}//end if
else
curMaxStack.push(currentMax);
}//end push()
public E pull(){
if(isEmpty())
return null;
else{
E e=heartStack.poll();
curMinStack.poll();
curMaxStack.poll();
return e;
}//end else
}//end pull()
public E getMax(){
return curMaxStack.peek();
}//end getMax()
public E getMin(){
return curMinStack.peek();
}//end getMin()
public int size(){
return heartStack.size();
}//end size()
public boolean isEmpty(){
return heartStack.isEmpty();
}//end isEmpty()
public E getMedian(){
E[] e=(E[]) heartStack.toArray();
Arrays.sort(e);
return e[e.length/2];
}//end getMedian()
public E[] toArray(){
return (E[])heartStack.toArray();
}//end toArray()
}
5 如何实现包含获取最小值函数的队列
问题:定义队列的数据结构,请在该类型中实现一个能够得到队列的最小元素的getMin函数。在该队列中,调用getMin、insert及remove的时间复杂度都是O(1).思路1:用最小堆实现优先队列,获取最小值时间复杂度为O(nlogn),但优先队列只能获取最小值,remove获取的不是先入队的元素。
思路2:
如果能用栈有效地实现队列,而栈的获取最小值的操作又很容易实现,那么队列的获取最小值的操作也很容易完成。
因为上面可用2个栈实现栈的min函数,而用2个栈可以实现队列。所以可以用已实现了获取最小值的栈stack1和stack2实现队列,而整个队列的最小值从min(stack1.getMin(),stack2.getMin())中获取。具体实现代码见问题6中代码。
6 如何实现具有最大值、最小值和中间值的栈和队列
问题5解决了用O(1)时间获取栈的最小值,那么解决最大值的问题也迎刃而解。对于获取队列的中间值,可以将队列中所有元素排序,然后获取排序后的中间值。具体实现如下(代码中的stack类为上述问题4中已实现的可以获取最大值和最小值的栈):
[java] view
plaincopy
public class ExmaPeekableQueue<E extends Comparable<E>> implements IExamPeekableQueue{
Stack stack1=new Stack();
Stack stack2=new Stack();
@Override
public void enqueue(Comparable e) {
stack1.push(e);
}//end enqueue()
@Override
public Comparable<E> dequeue() {
if(stack2.isEmpty()){
stack2.push(stack1.pull());
}//end if
return stack2.pull();
}//end dequeue()
@Override
public Comparable<E> peekMedian() {
Comparable[] arr=null; //用于存储队列中当前元素的数组
if(stack1.isEmpty()){
arr=stack2.toArray();
}//end if
else if(stack2.isEmpty()){
arr=stack1.toArray();
}//end if
else{
arr=new Comparable[size()];
Comparable[] arrE1=stack1.toArray();
Comparable[] arrE2=stack2.toArray();
//将2个栈中的元素复制到一个数组中
int i=0;
for(;i<stack1.size();i++){
arr[i]=arrE1[i];
}//end for
for(int j=0;j<stack2.size();j++){
arr[++i]=arrE2[j];
}//end for
}//end else
Arrays.sort(arr);
return arr[arr.length/2];
}//end peekMedian()
@Override
public Comparable peekMaximum() {
Comparable max1=stack1.getMax();
Comparable max2=stack2.getMax();
if(max1.compareTo(max2)>0){
return max1;
}//end if
else
return max2;
}
@Override
public Comparable<E> peekMinimum() {
Comparable min1=stack1.getMin();
Comparable min2=stack2.getMin();
if(min1.compareTo(min2)>0){
return min2;
}//end if
else
return min1;
}//end peekMinimum()
@Override
public int size() {
return (stack1.size()+stack2.size());
}//end size()
}//end class
========================
当然如果知识为了实现栈的最大值最小值中间值功能,而不要求使用队列,那完全可以使用内部栈解决。
在上面的博文中,使用两个队列(数组)实现的栈的先入后出功能,
可以有两种实现模式:
当在Array1 输入1 2 3 之后要删除3 则需要借助Array2 -> Array1:空 Array2为 1 2
这个时候新来的元素应该插入到Array1 还是Array2呢。
如果是插入到Array2 则,若后面需要删除某元素,则必须整个数组移到Array1
如果是插入到Array1 则只需要将后面输入的移到Array2。这是个较为优化的方案。
相关文章推荐
- 如何实现具有最大值、最小值和中间值的栈和队列
- 如何实现具有最大值、最小值和中间值的栈和队列
- 队列实现插入,弹出,找到最大值和最小值的操作
- 元素函数编程之美系列之栈和队列1—在O(1)的时间内得到栈的最大或者最小值
- [C++ 实现最大值优先队列和最小值优先队列]
- 【C++基本功补习】查找三个数据中的最大值、最小值、中间值,两种实现方案比较
- SSE图像算法优化系列七:基于SSE实现的极速的矩形核腐蚀和膨胀(最大值和最小值)算法。
- 基于堆的最大最小优先级队列的实现
- 腾讯2017年实习生编程题目(第三题)——有趣的数字 小Q今天在上厕所时想到了这个问题:有n个数,两两组成二元组,差最小的有多少对呢?差最大呢?
- 【九度】题目1512:用两个栈实现队列
- 题目1483:求最大最小数
- 题目:输入两个正整数m和n,求其最大公约数和最小公倍数。
- 题目1512:用两个栈实现队列
- 键盘输入3*3的整数矩阵,求两条对角线中最小的元素和最大的元素,使用普通指针变量实现。
- awk实现求和、平均、最大值和最小值的计算操作
- Sturcture.Heap 堆(最大堆实现(最小堆实现类似))
- 【剑指Offer面试编程题】题目1512:用两个栈实现队列--九度OJ
- 剑指offer 01-06解答思路以及代码(顺序数组找特定数字,替换空格字符,链表反转输出,重建二叉树,两个栈实现队列效果,旋转数组最小元素)
- 基于大顶堆实现的最大优先级队列
- awk实现求和、平均、最大值和最小值的计算操作