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. WinDriver&amp;PCIE

    1.安装VS2012 安装VS2012略过,主要用它来做数据传输应用程序的,WINDRIVER提供了一系列API接口,方便了用户,使用户能直接进入用户态的编程,因为内核态的编程它已做好,不需要进行修改 ...

  2. Unbutu网卡驱动安装(Intel内置网卡8086:15b8)

    工作中需要在新的实验平台上安装unbuntu14.04操作系统,系统安装好之后发现无法连接网络,分析后是由于缺少网卡驱动的原因. 下面把分析问题的过程及安装网卡驱动步骤介绍如下: 查看PCI信息 su ...

  3. php 正则表达

    今天看书,看到代码里面出现了一段正则表达式匹配语句preg_match,感觉水很深的感觉,网上搜了一些资料,暂时没时间学习,但是觉得以后学的话有两个网址比较靠谱,如下: php正则表达式手册:php ...

  4. mvn deploy 报错:Return code is: 400, ReasonPhrase: Bad Request. ->

    mvn deploy 报错:Return code is: 400, ReasonPhrase: Bad Request. -> TEST通过没有报错,但是最终部署到Nexus中时出现错误. 后 ...

  5. Linux内存中的Cache真的能被回收么?

    在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个RHEL6的系统上,free命令的显示内容大概是这样一个状态: [root@tencent64 ~]# free       ...

  6. HDU 1213 How Many Tables (并查集)

    How Many Tables 题目链接: http://acm.hust.edu.cn/vjudge/contest/123393#problem/C Description Today is Ig ...

  7. CABasicAnimation精讲

    前言 本教程写了这个效果图的demo,同时总结CABasicAnimation的使用方法. 看完gif动画完,看到了什么?平移.旋转.缩放.闪烁.路径动画. 实现平移动画 实现平移动画,我们可以通过t ...

  8. IE取消访问剪贴板设置

    1. 启动IE. 2. Internet选项. 3. “安全”选项卡. 4. 自定义级别. 5. “安全设置-Internet区域”->脚本->允许对剪贴板进行编程访问(启用)

  9. 百度地图 >> 自定义控件

    前言 百度地图API中预定义的UI控件,比如NavigationControl平移缩放控件,CopyrightControl版权控件,MapTypeControl地图类型控件....,这些都继承自抽象 ...

  10. Bootstrap 列偏移\列嵌套\列排序

    1.列偏移 这个其实很简单就是通过一个样式类,通过.col-md-offset-*可以将列偏移到右侧.这些class通过使用*选择器将所有列增加了列的左侧margin.例如,.col-md-offse ...