1. 面试的时候,栈和队列经常会成对出现来考察。本文包含栈和队列的如下考试内容:

(1)栈的创建

(2)队列的创建

(3)两个栈实现一个队列

(4)两个队列实现一个栈

(5)设计含最小函数min()的栈,要求min、push、pop、的时间复杂度都是O(1)

(6)判断栈的push和pop序列是否一致

2. 具体分析:

(1)栈的创建:

我们接下来通过链表的形式来创建栈,方便扩充。

代码实现:

 public class Stack {
     public Node head;
     public Node current;
     //方法:入栈操作
     public void push(int data) {
         if (head == null) {
             head = new Node(data);
             current = head;
         } else {
             Node node = new Node(data);
             node.pre = current;//current结点将作为当前结点的前驱结点
             current = node;  //让current结点永远指向新添加的那个结点
         }
     }
     public Node pop() {
         if (current == null) {
             return null;
         }
         Node node = current; // current结点是我们要出栈的结点
         current = current.pre;  //每出栈一个结点后,current后退一位
         return node;
     }
     class Node {
         int data;
         Node pre;  //我们需要知道当前结点的前一个结点
         public Node(int data) {
             this.data = data;
         }
     }
     public static void main(String[] args) {
         Stack stack = new Stack();
         stack.push(1);
         stack.push(2);
         stack.push(3);
         System.out.println(stack.pop().data);
         System.out.println(stack.pop().data);
         System.out.println(stack.pop().data);
     }
 }

入栈操作时,14、15行代码是关键。

运行效果:

(2)队列的创建:

队列的创建有两种形式:基于数组结构实现(顺序队列)、基于链表结构实现(链式队列)。

我们接下来通过链表的形式来创建队列,这样的话,队列在扩充时会比较方便。队列在出队时,从头结点head开始。

代码实现:

入栈时,和在普通的链表中添加结点的操作是一样的;出队时,出的永远都是head结点。

 public class Queue {
     public Node head;
     public Node curent;
     //方法:链表中添加结点
     public void add(int data) {
         if (head == null) {
             head = new Node(data);
             curent = head;
         } else {
             curent.next = new Node(data);
             curent = curent.next;
         }
     }
     //方法:出队操作
     public int pop() throws Exception {
         if (head == null) {
             throw new Exception("队列为空");
         }
         Node node = head;  //node结点就是我们要出队的结点
         head = head.next; //出队之后,head指针向下移
         return node.data;
     }
     class Node {
         int data;
         Node next;
         public Node(int data) {
             this.data = data;
         }
     }
     public static void main(String[] args) throws Exception {
         Queue queue = new Queue();
         //入队操作
         for (int i = 0; i < 5; i++) {
             queue.add(i);
         }
         //出队操作
         System.out.println(queue.pop());
         System.out.println(queue.pop());
         System.out.println(queue.pop());
     }
 }

运行效果:

(3)两个栈实现一个队列:

思路:

栈1用于存储元素,栈2用于弹出元素,负负得正。

说的通俗一点,现在把数据1、2、3分别入栈一,然后从栈一中出来(3、2、1),放到栈二中,那么,从栈二中出来的数据(1、2、3)就符合队列的规律了,即负负得正。

完整版代码实现:

 import java.util.Stack;
 public class Queue {
     private Stack<Integer> stack1 = new Stack<>();//执行入队操作的栈
     private Stack<Integer> stack2 = new Stack<>();//执行出队操作的栈
     //方法:给队列增加一个入队的操作
     public void push(int data) {
         stack1.push(data);
     }
     //方法:给队列正价一个出队的操作
     public int pop() throws Exception {
         if (stack2.empty()) {//stack1中的数据放到stack2之前,先要保证stack2里面是空的(要么一开始就是空的,要么是stack2中的数据出完了),不然出队的顺序会乱的,这一点很容易忘
             while (!stack1.empty()) {
                 stack2.push(stack1.pop());//把stack1中的数据出栈,放到stack2中【核心代码】
             }
         }
         if (stack2.empty()) { //stack2为空时,有两种可能:1、一开始,两个栈的数据都是空的;2、stack2中的数据出完了
             throw new Exception("队列为空");
         }
         return stack2.pop();
     }
     public static void main(String[] args) throws Exception {
         Queue queue = new Queue();
         queue.push(1);
         queue.push(2);
         queue.push(3);
         System.out.println(queue.pop());
         queue.push(4);
         System.out.println(queue.pop());
         System.out.println(queue.pop());
         System.out.println(queue.pop());
     }
 }

注意第22行和第30行代码的顺序,以及注释,需要仔细理解其含义。

运行效果:

(4)两个队列实现一个栈:

思路:

将1、2、3依次入队列一, 然后最上面的3留在队列一,将下面的2、3入队列二,将3出队列一,此时队列一空了,然后把队列二中的所有数据入队列一;将最上面的2留在队列一,将下面的3入队列二。。。依次循环。

代码实现:

 import java.util.ArrayDeque;
 import java.util.Queue;
 public class Stack {
     Queue<Integer> queue1 = new ArrayDeque<Integer>();
     Queue<Integer> queue2 = new ArrayDeque<Integer>();
     //方法:入栈操作
     public void push(int data) {
         queue1.add(data);
     }
     //方法:出栈操作
     public int pop() throws Exception {
         int data;
         if (queue1.size() == 0) {
             throw new Exception("栈为空");
         }
         while (queue1.size() != 0) {
             if (queue1.size() == 1) {
                 data = queue1.poll();
                 while (queue2.size() != 0) {  //把queue2中的全部数据放到队列一中
                     queue1.add(queue2.poll());
                     return data;
                 }
             }
             queue2.add(queue1.poll());
         }
         throw new Exception("栈为空");//不知道这一行的代码是什么意思
     }
     public static void main(String[] args) throws Exception {
         Stack stack = new Stack();
         stack.push(1);
         stack.push(2);
         stack.push(3);
         System.out.println(stack.pop());
         System.out.println(stack.pop());
         stack.push(4);
     }
 }

运行效果:

(5)设计含最小函数min()的栈,要求min、push、pop、的时间复杂度都是O(1)。

min方法的作用是:就能返回是栈中的最小值。【微信面试题】

普通思路:

一般情况下,我们可能会这么想:利用min变量,每次添加元素时,都和min元素作比较,这样的话,就能保证min存放的是最小值。但是这样的话,会存在一个问题:如果最小的元素出栈了,那怎么知道剩下的元素中哪个是最小的元素呢?

改进思路:

这里需要加一个辅助栈,用空间换取时间。辅助栈中,栈顶永远保存着当前栈中最小的数值。具体是这样的:原栈中,每次添加一个新元素时,就和辅助栈的栈顶元素相比较,如果新元素小,就把新元素的值放到辅助栈中,如果新元素大,就把辅助栈的栈顶元素再copy一遍放到辅助栈的栈顶;原栈中,出栈时,

完整代码实现:

 import java.util.Stack;
 public class MinStack {
     private Stack<Integer> stack = new Stack<Integer>();
     private Stack<Integer> minStack = new Stack<Integer>(); //辅助栈:栈顶永远保存stack中当前的最小的元素
     public void push(int data) {
         stack.push(data);  //直接往栈中添加数据
         //在辅助栈中需要做判断
         if (minStack.size() == 0 || data < minStack.peek()) {
             minStack.push(data);
         } else {
             minStack.add(minStack.peek());   //【核心代码】peek方法返回的是栈顶的元素
         }
     }
     public int pop() throws Exception {
         if (stack.size() == 0) {
             throw new Exception("栈中为空");
         }
         int data = stack.pop();
         minStack.pop();  //核心代码
         return data;
     }
     public int min() throws Exception {
         if (minStack.size() == 0) {
             throw new Exception("栈中空了");
         }
         return minStack.peek();
     }
     public static void main(String[] args) throws Exception {
         MinStack stack = new MinStack();
         stack.push(4);
         stack.push(3);
         stack.push(5);
         System.out.println(stack.min());
     }
 }

运行效果:

(6)判断栈的push和pop序列是否一致:

通俗一点讲:已知一组数据1、2、3、4、5依次进栈,那么它的出栈方式有很多种,请判断一下给出的出栈方式是否是正确的?

例如:

数据:

1、2、3、4、5

出栈1:

5、4、3、2、1(正确)

出栈2:

4、5、3、2、1(正确)

出栈3:

4、3、5、1、2(错误)

完整版代码:

 import java.util.Stack;
 public class StackTest {
     //方法:data1数组的顺序表示入栈的顺序。现在判断data2的这种出栈顺序是否正确
     public static boolean sequenseIsPop(int[] data1, int[] data2) {
         Stack<Integer> stack = new Stack<Integer>(); //这里需要用到辅助栈
         for (int i = 0, j = 0; i < data1.length; i++) {
             stack.push(data1[i]);
             while (stack.size() > 0 && stack.peek() == data2[j]) {
                 stack.pop();
                 j++;
             }
         }
         return stack.size() == 0;
     }
     public static void main(String[] args) {
         Stack<Integer> stack = new Stack<Integer>();
         int[] data1 = {1, 2, 3, 4, 5};
         int[] data2 = {4, 5, 3, 2, 1};
         int[] data3 = {4, 5, 2, 3, 1};
         System.out.println(sequenseIsPop(data1, data2));
         System.out.println(sequenseIsPop(data1, data3));
     }
 }

代码比较简洁,但也比较难理解,要仔细体会。

运行效果:

数据结构笔记02:Java面试必问算法题的更多相关文章

  1. Java面试必问之Hashmap底层实现原理(JDK1.7)

    1. 前言 Hashmap可以说是Java面试必问的,一般的面试题会问: Hashmap有哪些特性? Hashmap底层实现原理(get\put\resize) Hashmap怎么解决hash冲突? ...

  2. 一线大厂Java面试必问的2大类Tomcat调优

    一.前言 最近整理了 Tomcat 调优这块,基本上面试必问,于是就花了点时间去搜集一下 Tomcat 调优都调了些什么,先记录一下调优手段,更多详细的原理和实现以后用到时候再来补充记录,下面就来介绍 ...

  3. Java面试必问

    1. 图灵 1.1 聊聊哈希算法与HashMap 1)一个优秀的哈希算法有什么特点? 快速.不可逆.敏感性.低碰撞性 2)自己写一个Hash算法 取模 3)Java中的Hash算法 HashMap,侧 ...

  4. java面试必问问题总结

    1. 自我介绍 2. get跟load的区别 3. 什么是重载,什么是重写 4. HashTable跟HashMap的区别 5. Jsp九大隐式对象 6. Forword和redirect 的区别 7 ...

  5. Java面试必问:ThreadLocal终极篇 淦!

    点赞再看,养成习惯,微信搜一搜[敖丙]关注这个互联网苟且偷生的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系列 ...

  6. Java面试必问-ThreadLocal

    前言 在面试环节中,考察"ThreadLocal"也是面试官的家常便饭,所以对它理解透彻,是非常有必要的. 有些面试官会开门见山的提问: “知道ThreadLocal吗?” “讲讲 ...

  7. java面试必问:多线程的实现和同步机制,一文帮你搞定多线程编程

    前言 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种资源和状态信息,包括打开的文件.子进程和信号处理. 线 ...

  8. Java面试必问,ThreadLocal终极篇

    转载自掘金占小狼博客. 前言 在面试环节中,考察"ThreadLocal"也是面试官的家常便饭,所以对它理解透彻,是非常有必要的. 有些面试官会开门见山的提问: “知道Thread ...

  9. Java面试必问之Hashmap底层实现原理(JDK1.8)

    1. 前言 上一篇从源码方面了解了JDK1.7中Hashmap的实现原理,可以看到其源码相对还是比较简单的.本篇笔者和大家一起学习下JDK1.8下Hashmap的实现.JDK1.8中对Hashmap做 ...

随机推荐

  1. 自学hadoop(三)

    1) 关于hadoop在eclipse插件.经过自己的摸爬滚打.总结一下三条.     a) 2.0或者0.23.0吧 google比较方便.其他的可以自己编译.(这个我不敢保证.我本地环境事2.1. ...

  2. 一起刷LeetCode3-Longest Substring With Repeating Characters

    拖延症太严重了TAT,真心要处理一下这个问题了,感觉很不好! --------------------------------------------------------------------- ...

  3. django form关于clean及cleaned_data的说明 以及4种初始化

      1.form类的运行顺序是init,clean,validte,save其中clean和validate会在form.is_valid()方法中被先后调用.(这里留有一个疑问,结构完全相同的两个f ...

  4. 自定义实现InputFormat、OutputFormat、输出到多个文件目录中去、hadoop1.x api写单词计数的例子、运行时接收命令行参数,代码例子

    一:自定义实现InputFormat *数据源来自于内存 *1.InputFormat是用于处理各种数据源的,下面是实现InputFormat,数据源是来自于内存. *1.1 在程序的job.setI ...

  5. JVM内存结构

    前言 在Java语言开发过程中,out of memory错误是很常见的一种错误.对于JVM的内存结构有更深入的了解,更更好的帮我们排查此类问题,有效的避免此类问题发生.在JAVA 8中内存结构有进行 ...

  6. JS一定要放在Body的最底部么? 聊聊浏览器的渲染机制

    请参看文章 https://segmentfault.com/a/1190000004292479 网上的回答: 1.js加载会阻塞其它内容加载,使页面加载时间更长,尤其是js文件太大,有的页面js文 ...

  7. viPlugin安装破解

    viPlugin是一个eclipse 针对vi的插件,使用此插件可以让你在使用eclipse进行编码时使用几乎所有vi命令,可以极大的提高开发编码效率.  安装  点击你的eclipse中的 help ...

  8. FloatingActionButton的一点学习感悟

    最近在学习android材料设计的新控件,前面一篇文章讲到 CoordinatorLayout 结合几个新控件可以实现的几个效果.其中第一个是,Coordinatorlayout + Floating ...

  9. SQLserver锁和事务隔离级别的比较与使用(转)

    SQLserver锁和事务隔离级别的比较与使用(转) http://www.cnblogs.com/chenlulouis/archive/2010/12/06/1898014.html http:/ ...

  10. spark结合 Openfire服务器,发送聊天消息

    1.下载OpenFire服务器,进行安装,参考http://www.cnblogs.com/hoojo/archive/2012/05/17/2506769.html 2.程序运行客户端:下载客户端代 ...