1. 什么是队列 (Queue)
队列 (queue) 是一种采用先进先出 (first in first out) 策略的抽象数据结构。最常用的就是在宽度优先搜索 (BFS) 中,记录待扩展的节点。
队列内部存储元素的方式一般有两种,数组(array) 和链表(linked list) 。两者的主要区别就是:
- 数组对随机访问有较好性能
- 链表对插入和删除元素有较好性能
1.1 用链表实现队列
1) 链表是由多个节点构成的,一个节点由两部分组成:数据域和指针域
在队列中,我们只需要用两个指针head和tail分别指向链表的头部和尾部即可实现队列的基本功能
public class MyQueue {
// @param item: An integer
// @return: nothing
ListNode head, tail;
public MyQueue(){
head = tail = null;
}
public void enqueue(int item) {
if(head == null){
tail = new ListNode(item);
head = tail;
}else{
tail.next = new ListNode(item);
tail = tail.next;
}
}
// @return: An integer
public int dequeue() {
if(head != null){
int res = head.val;
head = head.next;
return res;
}
return -1;
}
}
class ListNode{
int val;
ListNode next;
public ListNode(int val){
this.val = val;
next = null;
}
}
注:
- 一个".java"源文件中可以有多个类,但只能有一个public类,并且public类名必须与文件名相一致。
- this表示对当前对象的引用。表示用类的成员变量,而非函数参数,注意在成员变量和函数参数同名时进行区分。
1.2 用数组实现循环队列
1)为了克服“假溢出“现象,把存储队列元素的表从逻辑上看成一个环,成为循环队列。
思路:在循环队列中,通过成员变量数组来表示一组地址连续的存储单元,再定义两个整型变量head和tail分别指示队首和队尾的位置,循环效果则用“模运算”实现。
将tail对 (数组长度)MAXSIZE取模,每当tail达到末尾时,使其回到队首。如果这样我们发现一个问题,队列为空和队列已满的条件都是 tail % MAXSIZE = head
为了避免这种无法判断的情况,我们规定当循环队列剩一个空位置时,就认为队列已满。这样队列已满的条件就成了 (tail+1) % MAXSIZE = head
public class MyQueue {
/*
* @param item: An integer
* @return: nothing
*/
int maxsize = 100000;
int[] arr;
int head, tail;
public MyQueue(){
head = tail = 0;
arr = new int[maxsize];
}
public void enqueue(int item) {
if((tail+1) % maxsize == head){
return;
}
arr[tail++] = item;
tail %= maxsize;
}
/*
* @return: An integer
*/
public int dequeue() {
if(head == tail){
return -1;
}
int val = arr[head++];
head %= maxsize;
return val;
}
2. Java常用的几个interface
1)Set可以知道某物是否存在于集合中,不会存储重复元素。Set的实现类在面试中常用的是:
- HashSet:无重复数据,可以有空数据,数据无序
- TreeSet:无重复数据,不能有空数据,数据有序
Set<String> set = new HashSet<>();
for(int i=0;i<6;i++){
set.add(i + "");
}
Iterator<String> iter = set.iterator();
while(iter.hasNext()){
system.out.print(iter.next() + " ");
}
2) Map用于存储具有映射关系的数据。Map中存了两组数据 (key和value),它们都可以是任何引用类型的数据,key不能重复,我们可以通过key取到对应的value。Map的实现类在面试中常用的是:
- HashMap:key无重复,value允许重复;允许key和value为空;数据无序
- TreeMap:key无重复,value允许重复;key不允许有null,value允许有null;有序
3) List是一个元素有序、可以重复、可以为null的集合。List的实现类在面试中常用的是:
- ArrayList:基于动态数组实现
- LinkedList:基于链表实现
4) Queue支持FIFO,即队首删除、队尾添加,其实现类有:
- PriorityQueue:基于堆(heap)实现;非FIFO (最先出队列的是优先级最高的元素)
- Queue:基于链表实现;FIFO
Queue<String> queue = new LinkedList<String>();
queue.offer("a");
queue.poll(); // 显示a
queue.peek(); // 显示null
- add(E), offer(E)在尾部添加:它们都建议实现类禁止添加null元素,否则会报空指针NullPointerException,LinkedList没有这样的要求;add方法在添加失败 (队列已满)时,会报一些运行时错误,offer方法在添加失败时会返回false。
- remove(), poll()删除并返回头部:队列为空时,remove方法会报NoSuchElementException,poll方法返回null。
- element(), peek()获取头部但不删除:队列为空时,element方法抛出异常,peek方法只会返回null。
3. Interface & 抽象类
1)interface语法规则
interface只包含方法的定义和常量,而没有方法的实现和变量。
interface中的所有成员变量都默认是由public static final修饰的。
interface中的所有方法都默认是由public abstract修饰的。
实现interface的类中,必须提供interface中所有方法的具体实现内容。
多个不同的类可以实现同一个interface,一个类可以实现多个不同的interface。
interface可以继承另一个interface,使用extends关键字。
@override是一种注释,标明该方法是继承自父类或interface的实现
2)java多态
父类引用变量可以指向子类对象,前提是必须有子父类关系或类实现interface关系。
interface类型变量只能调用在子类对象中实现的自身定义的方法
3)抽象类
抽象类不能实例化
抽象类和抽象方法必须用关键字abstract修饰
抽象方法只能存在于抽象类中,但抽象类不一定有抽象方法
若抽象类的子类是具体类,则该子类必须重写抽象类中的所有抽象方法
//Animal类
public abstract class Animal {
public void sleep() {
System.out.println("sleep");
}
public abstract void eat();
}
4)抽象类与接口的比较
单继承,多实现
interface中的方法都必须是public的,对外部开放;而abstract class的方法可以设置为protected
4. 拓展
1)泛型
2)取模运算(即取余数):a%b
3)protected修饰符的访问权限:
- 基类的protected成员是包内可见的,并对子类可见。
- 若子类与基类不在同一包中,那么在子类中,可以访问其从基类继承而来的protected方法,而不能访问基类的实例的protected方法。
4)控制台输入输出
Scanner scan = new Scanner(System.in);
while(scan.hasNextLine()){
String str = scan.nextLine();
}
scan.close();
使用特定分隔符:先调用scan.useDelimiter(),然后scan.next()或scan.nextInt()返回被分隔符分割的字符。