三、表、栈和队列
3.1 抽象数据类型(ADT)
是带有一组操作的一些对象的集合。诸如表、集合、图以及他们各自的操作一起形成的这些对象都可以看成抽象数据类型,就像整数、实数等都是数据类型一样。
3.2 表ADT
3.2.1 表的简单数组实现
可以通过创建新数组的方式扩充原数组,优点是节省查找时间,代码如下:
public class Test1 {
public static void main(String[] args) {
int[] arr = new int[10];
// 扩大数组容量
int[] newArr = new int[arr.length * 2];
for(int i = 0; i < arr.length; i++){
newArr[i] = arr[i];
}
arr = newArr;
System.out.println(arr.length); //20
}
}
3.2.2 简单链表
节省插入和删除时间
3.2.3 Java Collection API中的表
1.Collection接口:位于java.util包中
public interface Collection<E> extends Iterable<E> {
//常用方法
int size(); //返回集合元素个数
boolean isEmpty(); //如集合为空则返回true
void clear(); //清空集合
boolean contains(Object o); //如果集合包含指定元素则返回true
boolean add(E e); //添加指定元素
boolean remove(Object o); //删除指定元素
Iterator<E> iterator(); //@return an {@code Iterator} over the elements in this collection
Object[] toArray(); //集合转数组
}
增强for循环:
public static void main(String[] args) {
//Collection是接口,完全抽象的不能new对象
//'Collection' is abstract; cannot be instantiated
// Collection<Integer> coll = new Collection<>();
Collection coll =new ArrayList();
coll.add(1);
coll.add(2);
coll.add(3);
print(coll); //1 2 3
}
public static <Integer> void print(Collection<Integer> coll){
for (Integer item : coll) {
System.out.println(item);
}
}
2.Iterator接口
public interface Iterable<T> {
Iterator<T> iterator(); //Returns an iterator over elements of type {@code T}.
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
实现Iterable接口的集合必须提供一个称为iterator的方法,该方法返回一个Iterator类型的对象。该Iterator是一个在java.util包中定义的接口。
public interface Iterator<E> {
boolean hasNext(); //@return {@code true} if the iteration has more elements
E next(); //@return the next element in the iteration
//Removes from the underlying collection the last element returned by this iterator
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
当编译器见到一个正在用于Iterable的对象的增强for循环时,它用对iterator方法的那些调用代替增强for循环以得到一个Iterator对象,然后调用next()和hasNext()。上面的print例程由编译器重写
public static <Integer> void print(Collection<Integer> coll){
Iterator<Integer> itr = coll.iterator();
while (itr.hasNext()){
Integer item = itr.next();
System.out.println(item);
}
}
Iterator的remove方法可以删除由next最新返回的值(此后不能再调用remove,直到再一次调用next之后)
Iterator的remove方法的优点:
1)Collection的remove方法必须首先找到要被删除的项(集合中每隔一项删除一项iteator效率更高)
2)如果对正在被迭代的集合进行结构上的改变,那么迭代器就不再合法,但如果迭代器调用自己的remove方法,那么迭代器仍然合法。
3.List接口、ArrayList类和LinkedList类
public interface List<E> extends Collection<E> {
//继承Collection接口的同时也继承Collection接口的所有方法
E get(int index); //返回指定位置元素
E set(int index, E element); //替换指定位置元素
void add(int index, E element); //向指定位置添加元素
E remove(int index); //移除指定位置的元素,重载方法
ListIterator<E> listIterator(); //@return a list iterator over the elements in this list
int indexOf(Object o); //根据指定元素获得第一次出现的索引值
int lastIndexOf(Object o); //根据指定元素获得最后一次出现的索引值
List<E> subList(int fromIndex, int toIndex); //根据索引获取一段元素(含左不含右)
}
List ADT两种流行的实现方式:
1)ArrayList类提供一种可增长数组的实现:对get和set的调用花费常数时间。
2)LinkedList类提供双链表实现:插入和删除均开销很小。缺点是不容易做索引。
public class Test4 {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
makeList1(list1,10); //o(N)
List<Integer> list2 = new LinkedList<>();
makeList1(list2,10); //o(N)
List<Integer> list3 = new ArrayList<>();
makeList2(list3,10); //o(N^2)
List<Integer> list4 = new LinkedList<>();
makeList2(list4,10); //o(N)
sum(list1); //o(N)
sum(list2); //o(N^2)
}
public static void makeList1(List<Integer> lst, int N){
lst.clear();
for (int i = 0; i < N; i++){
lst.add(i);
}
}
public static void makeList2(List<Integer> lst, int N){
lst.clear();
for (int i = 0; i < N; i++){
lst.add(0,i);
}
}
public static int sum(List<Integer> lst){
int total = 0;
for (int i = 0; i < lst.size(); i++) {
total += lst.get(i);
}
return total;
}
}
但如果使用增强for循环,那么它对任意List的运行时间都是o(N),因为迭代器可以有效地从一项到下一项推进。对搜索方法而言两种类都是低效的,对Collection的contains和remove两个方法的调用均花费线性时间。
4.例:提供一个例程,删除一个表中所有具有偶数值的项。
public class Test5 {
//删除偶数值项
public static void main(String[] args) {
//构造一个包含所有奇数的新表,然后清除原表,并将这些奇数拷回原表。
//本例实现遇到偶数值项将其删除。由于ArrayList的删除操作过于昂贵,所以选择LinkedList。
LinkedList<Integer> list = new LinkedList<>();
list.add(6);
list.add(5);
list.add(1);
list.add(4);
list.add(2);
//该方法对get和remove调用效率低
// removeEvensVer1(list);
//将get方法换为迭代器遍历法,java.util.ConcurrentModificationException印证上文所述迭代器中不可进行集合更改操作。
//增强for循环不懂只有当一项不被删除时它才继续向下推进
// removeEvensVer2(list);
//LinkedList对该迭代器的remove方法只花费常数时间,而ArrayList仍然是二次时间。
removeEvensVer3(list);
System.out.println(list); //[5, 1]
}
public static void removeEvensVer1(List<Integer> lst){
int i = 0;
while(i < lst.size()){
if(lst.get(i) % 2 == 0){
lst.remove(i);
}else{
i++;
}
}
}
public static void removeEvensVer2(List<Integer> lst){
for (Integer x : lst) {
if(x % 2 == 0){
lst.remove(x);
}
}
}
public static void removeEvensVer3(List<Integer> lst){
Iterator<Integer> itr = lst.iterator();
while(itr.hasNext()){
if(itr.next() % 2 == 0){
itr.remove();
}
}
}
}
5.关于ListIterator接口
public interface ListIterator<E> extends Iterator<E> {
boolean hasPrevious(); //类似于hasNext()
E previous(); //类似于next()
void set(E e); //替换最后一个元素,如用于从List中的所有偶数减1
void add(E e); //添加指定元素
}
3.4.ArrayList类的实现
3.4.1基本类
public class MyArrayList<AnyType> implements Iterable<AnyType>{
private static final int DEFAULT_CAPACITY = 10;
/** MyArrayList把大小及数组作为其数据成员进行存储 **/
private int theSize;
private AnyType [] theItems;
public MyArrayList(){
doClear();
}
public void clear(){
doClear();
}
private void doClear(){
theSize = 0;
ensureCapacity(DEFAULT_CAPACITY);
}
public int size(){
return theSize;
}
public boolean isEmpty(){
return size() == 0;
}
public void trimToSize(){ //后期扩充数组容量
ensureCapacity(size());
}
public AnyType get(int idx){
if(idx < 0 || idx >= size()){
throw new ArrayIndexOutOfBoundsException();
}
return theItems[idx];
}
public AnyType set(int idx, AnyType newVal){
if(idx < 0 || idx >= size()){
throw new ArrayIndexOutOfBoundsException();
}
AnyType old = theItems[idx];
theItems[idx] = newVal;
return old;
}
private void ensureCapacity(int newCapacity) { //前期一次性把数组容量设置为特别大
if(newCapacity < theSize){
return;
}
AnyType [] old = theItems;
theItems = (AnyType []) new Object[newCapacity];
for(int j = 0; j < size(); j++){
theItems[j] = old[j];
}
}
public boolean add(AnyType x){
add(size(),x);
return true;
}
public void add(int idx,AnyType x){
if(theItems.length == size()){
ensureCapacity(size() * 2 + 1);
}
for (int i = theSize; i > idx ; i--) {
theItems[i] = theItems[i - 1];
}
theItems[idx] = x;
theSize++;
}
public AnyType remove(int idx){
AnyType removedItem = theItems[idx];
for (int i = idx; i < size() - 1; i++) {
theItems[i] = theItems[i + 1];
}
theSize--;
return removedItem;
}
@Override
public java.util.Iterator<AnyType> iterator(){
return new ArrayListIterator();
}
private class ArrayListIterator implements java.util.Iterator<AnyType> {
private int current = 0;
@Override
public boolean hasNext(){
return current < size();
}
@Override
public AnyType next() {
if(!hasNext()){
throw new java.util.NoSuchElementException();
}
return theItems[current++]; //由于theItems不是ArrayListIterator的一部分,所以该语句非法
}
@Override
public void remove(){
MyArrayList.this.remove(--current);
}
}
}
3.4.2迭代器、Java嵌套类和内部类
@Override
public java.util.Iterator<AnyType> iterator(){
return new ArrayListIterator<AnyType>(this);
}
//嵌套类
private static class ArrayListIterator<AnyType> implements java.util.Iterator<AnyType> {
private int current = 0;
private MyArrayList<AnyType> theList;
public ArrayListIterator(MyArrayList<AnyType> list){
theList = list;
}
@Override
public boolean hasNext(){
return current < theList.size();
}
@Override
public AnyType next() {
return theList.theItems[current++];
}
}
ArrayListIterator是嵌套类,该类被放入MyArrayList内部,这个类就叫做外部类。此时必须用static来表示它是嵌套的,若无static,将得到一个内部类,嵌套类可设计为private。当声明一个内部类时,编译器则添加对外部类对象的一个隐式引用,该对象引起内部类对象的构造。如果外部类对象的名字是Outer,则隐式引用就是Outer.this。
@Override
public java.util.Iterator<AnyType> iterator(){
return new ArrayListIterator();
}
//内部类
private class ArrayListIterator implements java.util.Iterator<AnyType> {
private int current = 0;、
@Override
public boolean hasNext(){
return current < size();
}
@Override
public AnyType next() {
return theItems[current++];
}
@Override
public void remove(){
MyArrayList.this.remove(--current);
}
}
3.5LinkedList类的实现
public class MyLinkedList<AnyType> implements Iterable<AnyType> {
private int theSize;
private int modCount = 0;
private Node<AnyType> beginMarker;
private Node<AnyType> endMarker;
/**
* 私有嵌套Node类声明
*/
private static class Node<AnyType>{
public Node(AnyType d,Node<AnyType> p,Node<AnyType> n){
data = d;
prev = p;
next = n;
}
public AnyType data;
public Node<AnyType> prev;
public Node<AnyType> next;
}
public MyLinkedList(){
doClear();
}
public void clear(){
doClear();
}
private void doClear() {
beginMarker = new Node<AnyType>(null,null,null);
endMarker = new Node<AnyType>(null,beginMarker,null);
beginMarker.next = endMarker;
theSize = 0;
modCount++;
}
public int size(){
return theSize;
}
public boolean isEmpty(){
return size() == 0;
}
public boolean add(AnyType x){
add(size(),x);
return true;
}
public void add(int idx,AnyType x){
addBefore(getNode(idx,0,size()),x);
}
public AnyType get(int idx){
return getNode(idx).data;
}
public AnyType set(int idx,AnyType newVal){
Node<AnyType> p = getNode(idx);
AnyType oldVal = p.data;
p.data = newVal;
return oldVal;
}
public AnyType remove(int idx){
return remove(getNode(idx));
}
private void addBefore(Node<AnyType> p,AnyType x){
Node<AnyType> newNode = new Node<>(x,p.prev,p);
newNode.prev.next = newNode;
p.prev = newNode;
theSize++;
modCount++;
}
public AnyType remove(Node<AnyType> p){
p.next.prev = p.prev;
p.prev.next = p.next;
theSize--;
modCount--;
return p.data;
}
private Node<AnyType> getNode(int idx){
return getNode(idx,0,size() - 1);
}
private Node<AnyType> getNode(int idx,int lower,int upper){
Node<AnyType> p;
if(idx < lower || idx > upper){
throw new IndexOutOfBoundsException();
}
if(idx < size() / 2){
p = beginMarker.next;
for (int i = 0; i < idx; i++) {
p = p.next;
}
}else{
p = endMarker;
for (int i = size(); i > idx; i--) {
p = p.prev;
}
}
return p;
}
@Override
public java.util.Iterator<AnyType> iterator() {
return new LinkedListIterator();
}
/**
*内部类
*/
private class LinkedListIterator implements java.util.Iterator<AnyType> {
private Node<AnyType> current = beginMarker.next;
private int expectedModCount = modCount;
private boolean okToRemove = false;
@Override
public boolean hasNext() {
return current != endMarker;
}
@Override
public AnyType next() {
if(modCount != expectedModCount){
throw new java.util.ConcurrentModificationException();
}
if(!hasNext()){
throw new java.util.NoSuchElementException();
}
AnyType nextItem = current.data;
current = current.next;
okToRemove = true;
return nextItem;
}
@Override
public void remove() {
if(modCount != expectedModCount){
throw new java.util.ConcurrentModificationException();
}
if(!okToRemove){
throw new IllegalStateException();
}
MyLinkedList.this.remove(current.prev);
expectedModCount++;
okToRemove = false;
}
}
}
3.6栈ADT
3.6.1栈模型
栈是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈的顶端。栈也被称为LIFO(后进先出)表。继承List
public class Stack<E> extends Vector<E> {
E push(E item); //元素入栈
E pop(); //元素出栈
E peek(); //获取栈顶元素
boolean empty(); //判空
int search(Object o) //返回元素位置
}
3.6.2栈的实现
由于栈是一个表,因此任何实现表的方法都可以实现栈,常用的有链表实现与数组实现。
3.6.3应用
例1.平衡符号
public class EquilibriumSymbol {
public static void main(String[] args) {
String str = "[(])";
boolean b = equilibriumSymbol(str);
System.out.println(b);
}
public static boolean equilibriumSymbol(String str){
Stack<Character> stack = new Stack<>();
for(char c : str.toCharArray()){
if(c == '('){
stack.push(')');
}else if(c == '['){
stack.push(']');
}else if(c == '{'){
stack.push('}');
}else if(stack.isEmpty() || c != stack.pop()){
return false;
}
}
return stack.isEmpty();
}
}
例2.后缀表达式(逆波兰表达式)
public class PostfixExpression {
public static void main(String[] args) {
// 输入的逆波兰表达式有效
String str = "6 5 2 3 + 8 * + 3 + *";
int res = postfixExpression(str);
System.out.println(res);
}
public static int postfixExpression(String str){
Stack<Integer> stack = new Stack<>();
String[] strs = str.split(" ");
for(String c : strs){
switch (c){
case "+":
stack.push(stack.pop() + stack.pop());
break;
case "-":
int tmp1 = stack.pop();
stack.push(stack.pop() - tmp1);
break;
case "*":
stack.push(stack.pop() * stack.pop());
break;
case "/":
int tmp2 = stack.pop();
stack.push(stack.pop() / tmp2);
break;
default:
stack.push(Integer.valueOf(c));
}
}
return stack.pop();
}
}
例3.中缀到后缀的转换(标准形式表达式转换成逆波兰表达式)
public class PostfixConvertInfix {
public static void main(String[] args) {
String str = "a+b*c+(d*e+f)*g";
String res = postfixConvertInfix(str);
System.out.println(res);
}
public static String postfixConvertInfix(String str){
//栈用于存放符号
StringBuilder build = new StringBuilder();
Stack<Character> stack = new Stack<>();
for(char c : str.toCharArray()){
if(c >= 'a' && c <= 'z'){
build.append(c);
}else if(c == '('){
stack.push(c);
}else if(c == ')'){
while (!stack.isEmpty()){
if(stack.peek() == '('){
stack.pop();
break;
}else{
build.append(stack.pop());
}
}
}else {
if(stack.isEmpty() || '(' == stack.peek() || priority(c) > priority(stack.peek())){
stack.push(c);
}else{
while (!stack.isEmpty() && stack.peek() != '('){
if(priority(c) <= priority(stack.peek())){
build.append(stack.pop());
}
}
stack.push(c);
}
}
}
while (!stack.isEmpty()){
build.append(stack.pop());
}
return build.toString();
}
//设置优先级
public static int priority(char c){
if(c == '*' || c == '/'){
return 1;
}else if(c == '+' || c == '-'){
return 0;
}
return -1;
}
}
例4.方法调用,类似于平衡符号
3.7队列ADT
队列是一个一段插入、另一段删除的表。
3.7.1队列模型
末端插入元素enqueue,表的开头删除元素dequeue。
public interface Queue<E> extends Collection<E> {
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element(); //返回队列头元素
E peek();
}