head 和 tail 是两个哨兵结点,不存储值。
MyList和MyIterator接口的代码和上节课一模一样
MyList:
package com.cskaoyan.list;
/**
* @author shihao
* @create 2020-05-15 17:28
*/
//想让MyArrayList和LinkedList也可以实现自己的foreach循环,
// 就得让MyList实现接口Iterable,表示有迭代的能力
public interface MyList<E> extends Iterable<E> {
boolean add(E e);
void add(int index, E e);
void clear();
boolean contains(Object obj);
E get(int index);
int indexOf(Object obj);
boolean isEmpty();
//MyIterator模仿的是listIterator
//应当实现一个迭代器,对MyList进行遍历
MyIterator<E> iterator();
MyIterator<E> iterator(int index);
int lastIndexOf(Object obj);
E remove(int index);
boolean remove(Object obj);
E set(int index, E e);
int size();
}
MyIterator:
package com.cskaoyan.list;
/**
* @author shihao
* @create 2020-05-16 8:09
*/
import java.util.Iterator;
//MyIterator模仿的是listIterator
//MyIterator
public interface MyIterator<E> extends Iterator<E> {
boolean hasNext();
boolean hasPrevious();
E next();
E previous();
int nextIndex();
int previousIndex();
void add(E e);
void remove();
void set(E e);
}
MyLinkedList:
package com.cskaoyan.list;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.NoSuchElementException;
/**
* @author shihao
* @create 2020-05-16 21:16
*/
public class MyLinkedList<E> implements MyList<E> {
//属性
//MyLinkedList底层是一个双端队列
private Node head;
private Node tail;
private int size;
private int modCount;
//不需要默认容量和最大容量
//Node不依赖于外部类对象而存在,所以将Node设置为static
//当然,加上static后就不能将value设置为E类型,
//因为泛型是创建对象的时候才把泛型参数传入进来的,泛型依赖于对象存在
private static class Node {
Node prev;
Object value;
Node next;
public Node(Object value) {
this.value = value;
}
public Node(Node prev, Object value, Node next) {
this.prev = prev;
this.value = value;
this.next = next;
}
}
//构造方法
//创建一个空链表
public MyLinkedList() {
head = new Node(null);
tail = new Node(head, null, null);
head.next = tail;
}
//应当在head之前和tail之后设置哨兵结点
//方法
/**
* 在线性表最后添加元素
*
* @param e 待添加的元素
* @return 如果添加成功返回true
*/
@Override
public boolean add(E e) {
add(size, e);
return true;
}
/**
* 在线性表指定位置添加元素
*
* @param index 指定索引位置
* @param e 待添加的元素
*/
@Override
public void add(int index, E e) {
checkIndexForAdd(index); //index的范围:[0,size]
//需要考虑一种特殊情况:index == size时
if (index == size) {
Node nodeToAdd = new Node(tail.prev, e, tail);
tail.prev.next = nodeToAdd;
tail.prev = nodeToAdd;
} else {
//获取索引为index的结点
Node node = getNode(index); //index的范围:[0,size-1]
//在node前面插入新节点
Node nodeToAdd = new Node(node.prev, e, node);
node.prev.next = nodeToAdd;
node.prev = nodeToAdd;
}
size++;
modCount++;
}
/**
* 获取index位置的结点
*
* @param index
* @return index位置的结点
* <p>
* A --> B --> C size=3
* A --> B --> C --> D size=4
*/
private Node getNode(int index) {
if (index * 2 < size) {
Node x = head.next;
//x最终指向索引为index的结点
for (int i = 0; i < index; i++) {
x = x.next;
}
return x;
} else {
Node x = tail.prev;
//x最终指向索引为index的结点
for (int i = size - 1; i > index; i++) {
x = x.prev;
}
return x;
}
}
private void checkIndexForAdd(int index) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("index = " + index + ", size = " + size);
}
}
/**
* 清空链表中所有元素
*/
@Override
public void clear() {
head.next = tail;
tail.prev = head;
size = 0;
modCount++;
}
/**
* 判断线性表中是否有与指定元素相等的元素
*
* @param obj 指定对象
* @return 如果存在这样额元素返回true,否则返回false
*/
@Override
public boolean contains(Object obj) {
return indexOf(obj) != -1;
}
/**
* 获取指定索引位置的元素
*
* @param index 指定索引位置
* @return 指定索引位置的元素
*/
@Override
@SuppressWarnings("unchecked")
public E get(int index) {
checkIndex(index);
return (E) getNode(index).value;
}
private void checkIndex(int index) {
if (index < 0 || index > size - 1) {
throw new IndexOutOfBoundsException("index = " + index + ", size = " + size);
}
}
/**
* 获取线性表中第一个与指定对象相等的元素
*
* @param obj 指定对象
* @return 第一个和指定对象相等的元素的索引,如果不存在这样的索引返回-1
*/
@Override
public int indexOf(Object obj) {
if (obj == null) {
Node x = head.next;
for (int i = 0; i < size; i++) {
if (x.value == null) return i;
x = x.next;
}
} else {
Node x = head.next;
for (int i = 0; i < size; i++) {
if (obj.equals(x.value)) return i;
x = x.next;
}
}
return -1;
}
/**
* 判断线性表是否为空
*
* @return 为空返回true,否则返回false
*/
@Override
public boolean isEmpty() {
return size == 0;
}
/**
* 获取最后一个与指定对象相等元素的索引
*
* @param obj 指定对象
* @return 最后一个与指定对象相等元素的索引,如果不存在这样的元素返回-1
*/
@Override
public int lastIndexOf(Object obj) {
if (obj == null) {
Node x = tail.prev;
for (int i = size - 1; i >= 0; i--) {
if (x.value == null) return i;
x = x.prev;
}
} else {
Node x = tail.prev;
for (int i = size - 1; i >= 0; i--) {
if (obj.equals(x.value)) return i;
x = x.prev;
}
}
return -1;
}
/**
* 删除指定索引位置的元素,并把被删除的元素返回
*
* @param index 索引
* @return 被删除的元素
*/
@Override
@SuppressWarnings("unchecked")
public E remove(int index) {
checkIndex(index);
Node node = getNode(index);
node.prev.next = node.next;
node.next.prev = node.prev;
size--;
modCount++;
return (E) node.value;
}
@Override
public boolean remove(Object obj) {
//不要用以下写法,因为indexOf会遍历一遍链表,remove(index)也会
//遍历一遍链表,其实整个方法只需遍历一次链表即可,我们却遍历了两次
/* int index = indexOf(obj);
if (index == -1) return false;
remove(index);
return true;*/
//只遍历一次
if (obj == null) {
Node x = head.next;
for (int i = 0; i < size; i++) {
if (x.value == null) {
//删除x结点
x.prev.next = x.next;
x.next.prev = x.prev;
size--;
modCount++;
return true;
}
x = x.next;
}
} else {
Node x = head.next;
for (int i = 0; i < size; i++) {
if (obj.equals(x.value)) {
//删除x结点
x.prev.next = x.next;
x.next.prev = x.prev;
size--;
modCount++;
return true;
}
x = x.next;
}
}
return false;
}
/**
* 将索引位置元素替换为新值
*
* @param index 索引
* @param e 新值
* @return 旧值
*/
@Override
public E set(int index, E e) {
checkIndex(index);
Node node = getNode(index);
E retValue = (E) node.value;
node.value = e;
return retValue;
}
/**
* 获取线性表中元素的个数
*
* @return 元素的个数
*/
@Override
public int size() {
return size;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
Node x = head.next;
while (x != tail) {
sb.append(x.value).append("-->");
x = x.next;
}
if (size != 0) {
sb.delete(sb.length() - 3, sb.length());
}
return sb.append("]").toString();
}
/**
* 获取迭代器,光标后面的元素的索引为0
*
* @return 迭代器
*/
@Override
public MyIterator<E> iterator() {
return new Itr();
}
/**
* 获取迭代器,光标后面的元素的索引为index
*
* @param index 光标后面的元素的索引
* @return 迭代器
*/
@Override
public MyIterator<E> iterator(int index) {
checkIndexForAdd(index);
return new Itr(index);
}
private class Itr implements MyIterator<E> {
//属性
int cursor; //光标后面元素的索引
Node node; //光标后面的结点
int expModCount = modCount; //期待被修改的次数
//lastRet表示上一次返回的结点
Node lastRet = null; //null表示最近没有返回结点或最近返回的结点已失效
//构造方法
//无需加public,因为外界不可能通过构造方法访问到Itr内部类
Itr() {
node = head.next;
}
Itr(int index) {
cursor = index;
if (index == size) {
node = tail;
} else {
node = getNode(index);
}
}
//方法
/**
* 判断光标后面是否还有元素
*
* @return 如果有元素返回true,没有元素返回false
*/
@Override
public boolean hasNext() {
return cursor != size;
}
/**
* 判断光标前面是否还有元素
*
* @return 如果有元素返回true,没有元素返回false
*/
@Override
public boolean hasPrevious() {
return cursor != 0;
}
/**
* 将光标往后移动一个元素,并把被光标越过的元素返回
*
* @return 被光标越过的元素
*/
@Override
@SuppressWarnings("unchecked")
public E next() {
checkConModException();
if (!hasNext()) {
throw new NoSuchElementException();
}
cursor++;
lastRet = node;
node = node.next;
return (E) lastRet.value;
}
private void checkConModException() {
if (expModCount != modCount) {
throw new ConcurrentModificationException();
}
}
/**
* 将光标往前移动一个位置,并把被光标越过的元素返回
*
* @return 被光标越过的元素
*/
@Override
@SuppressWarnings("unchecked")
public E previous() {
checkConModException();
if (!hasPrevious()) {
throw new NoSuchElementException();
}
cursor--;
node = node.prev;
lastRet = node;
return (E) lastRet.value;
}
/**
* 获取光标后面元素的索引
*
* @return
*/
@Override
public int nextIndex() {
return cursor;
}
/**
* 获取光标前面元素的索引
*
* @return
*/
@Override
public int previousIndex() {
return cursor - 1;
}
/**
* 在光标后面添加元素
*
* @param e 待添加的元素
*/
@Override
public void add(E e) {
//不用这句代码,add(cursor,e)的时间复杂度为O(n)
//MyLinkedList.this.add(cursor,e);
//完全可以在O(1)的时间复杂度下完成add操作
Node nodeToAdd = new Node(node.prev, e, node);
node.prev.next = nodeToAdd;
node.prev = nodeToAdd;
size++;
cursor++;
lastRet = null; //添加完元素后让最近返回的元素失效
//如果没有这句代码,本迭代器修改之后,其他迭代器是感知不到修改的
//也就是说理应报并发修改异常但却没报
//解释:expModCount = ++modCount;后,本迭代器的expModCount和外部类中的modCount值相同
//而在本迭代器创立之前的其他迭代器的expModCount可是比现在的++modCount小1,既然不相等
//在执行it.next()时,调用 checkConModException();就会报并发修改异常
expModCount = ++modCount;
}
/**
* 删除最近返回的元素
*/
@Override
public void remove() {
checkConModException();
//当最近返回的元素失效或者最近没有返回的元素时
if (lastRet == null) {
throw new IllegalStateException();
}
//删除node
lastRet.prev.next = lastRet.next;
lastRet.next.prev = lastRet.prev;
//4个属性值的变化
expModCount = ++modCount;
size--;
//如果是逆向遍历,node和lastRet是相等的
if (lastRet == node) {
node = node.next;
} else { //如果是正向遍历,node和lastRet是不相等的
cursor--;
}
lastRet = null;
}
/**
* 替换最近返回的元素
*
* @param e 替换后的值
*/
@Override
public void set(E e) {
checkConModException();
if (lastRet == null) {
throw new IllegalStateException();
}
lastRet.value = e;
lastRet = null;
}
}
public static void main(String[] args) {
MyLinkedList<String> list = new MyLinkedList<>();
System.out.println(list);
list.add("hello");
list.add("world");
list.add("java");
System.out.println(list);
// void add(int index, E e)
// list.add(0, "A");
// list.add(1, "A");
// list.add(list.size(), "A");
// list.add(list.size() + 1, "A");
// list.add(-1, "A");
// System.out.println(list);
// void clear(), int size(), boolean isEmpty()
/*System.out.println(list);
System.out.println(list.size());
System.out.println(list.isEmpty());
list.clear();
System.out.println(list);
System.out.println(list.size());
System.out.println(list.isEmpty());*/
// boolean contains(Object obj)
/*System.out.println(list.contains("java")); //true
System.out.println(list.contains("wuhan")); //false
System.out.println(list.contains(null));//false
list.add(null);
System.out.println(list.contains(null));//true*/
// int indexOf(Object obj)
/* list.add("hello");
System.out.println(list);
System.out.println(list.indexOf("hello"));
System.out.println(list.indexOf(null));
list.add(0, null);
list.add(null);
System.out.println(list);
System.out.println(list.indexOf(null));*/
// int lastIndexOf(Object obj)
/* list.add("hello");
System.out.println(list);
System.out.println(list.lastIndexOf("hello"));
System.out.println(list.lastIndexOf(null));
list.add(0, null);
list.add(null);
System.out.println(list);
System.out.println(list.lastIndexOf(null));*/
// E get(int index)
// System.out.println(list.get(1));
// System.out.println(list.get(-1));
// System.out.println(list.get(list.size()));
// E remove(index)
// System.out.println(list.remove(1));
// System.out.println(list.remove(-1));
// System.out.println(list.remove(list.size()));
// System.out.println(list);
// boolean remove(Object)
/*list.add("hello");
System.out.println(list.remove("hello"));
System.out.println(list);
System.out.println(list.remove(null));
System.out.println(list);
list.add(0, null);
list.add(null);
System.out.println(list);
System.out.println(list.remove(null));
System.out.println(list);*/
// E set(int index, E e)
// System.out.println(list.set(2, "JAVA"));
// System.out.println(list.set(-1, "JAVA"));
// System.out.println(list.set(list.size(), "JAVA"));
// System.out.println(list);
/*****************************************************
* Iterator
*****************************************************/
// MyLinkedList<String> list = new MyLinkedList<>();
// list.add("hello");
// list.add("world");
// list.add("java");
// boolean hasPrevious(), boolean hasNext()
// MyIterator<String> it = list.iterator();
// MyIterator<String> it = list.iterator(list.size());
// MyIterator<String> it = list.iterator(1);
// System.out.println(it.hasPrevious());
// System.out.println(it.hasNext());
// E previous(), E next()
/*MyIterator<String> it = list.iterator();
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());*/
/* MyIterator<String> it = list.iterator(list.size());
System.out.println(it.previous());
System.out.println(it.previous());
System.out.println(it.previous());
System.out.println(it.previous());*/
// int previousIndex() int nextIndex()
/*MyIterator<String> it = list.iterator();
System.out.println(it.previousIndex());
System.out.println(it.nextIndex());
it.next();
System.out.println(it.previousIndex());
System.out.println(it.nextIndex());
it.next();
System.out.println(it.previousIndex());
System.out.println(it.nextIndex());
it.next();
System.out.println(it.previousIndex());
System.out.println(it.nextIndex());*/
// void add(E e)
/*MyIterator<String> it = list.iterator(1);
it.add("A");
it.add("B");
it.add("C");
System.out.println(list);
System.out.println(it.next());*/
// 在"hello"后面添加元素
//测试两个迭代器的增加和遍历操作
// MyIterator<String> iterator = list.iterator();
// for (MyIterator<String> it = list.iterator(); it.hasNext(); ) {
// String s = it.next();
// if ("hello".equals(s)) {
// int index = it.nextIndex();
// it.add("A");
// }
// }
// System.out.println(list);
// //演示Itr中的add方法中的 expModCount = ++modCount; 这句代码的作用
// while (iterator.hasNext()) {
// String s = iterator.next();
// System.out.println(s);
// }
// void remove()
/*MyIterator<String> it = list.iterator();
// it.remove();
it.next();
it.remove();
System.out.println(list);*/
/* MyIterator<String> it = list.iterator(list.size());
it.previous();
//it.add("HELLO");
//add一次后lastRet所代表的最近返回的元素就失效了
it.remove();
System.out.println(list);
//remove一次后lastRet所代表的最近返回的元素就失效了
//it.remove();*/
// 删除"hello"
/*for(MyIterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
if ("hello".equals(s)) {
int index = it.previousIndex();
list.remove(index);
}
}
System.out.println(list);*/
/* MyIterator<String> it = list.iterator();
// it.set("HELLO");
it.next();
it.set("HELLO");
// it.set("HeLLo");
System.out.println(list);*/
/*MyIterator<String> it = list.iterator(list.size());
it.previous();
it.set("JAVA");
System.out.println(list);*/
}
}
栈的非顺序映象(链栈):
····················································································································································································································
栈的API:
入栈 (push):在栈顶添加元素,O(1)
出栈 (pop):从栈顶删除元素,O(1)
查看栈顶元素 (peek):访问栈顶元素但不弹出,O(1)
判空 (isEmpty):判断栈是否为空, 方便遍历,O(1)
栈的非顺序映象(链栈):
package com.cskaoyan.stack;
import java.util.EmptyStackException;
/**
* @author shihao
* @create 2020-05-18 22:37
* <p>
* 栈的非顺序映象
* API:
* void push(E e)
* E pop()
* E peek()
* boolean isEmpty()
*/
public class MyStack<E> {
//使用单链表作为底层的数据结构
private static class Node {
Object value;
Node next;
public Node(Object value) {
this.value = value;
}
public Node(Object value, Node next) {
this.value = value;
this.next = next;
}
}
//属性
private Node top;
//构造方法
//默认会实现无参构造方法,top会置为null
//方法
/**
* 入栈,即在栈顶添加元素
*
* @param e 待添加的元素
*/
public void push(E e) {
top = new Node(e, top);
}
/**
* 出栈,即删除栈顶元素
* @return 删除掉的元素
*/
@SuppressWarnings("unchecked")
public E pop(){
if (this.isEmpty()){
throw new EmptyStackException();
}
E removeValue = (E) top.value;
top = top.next;
return removeValue;
}
/**
* 查看栈顶元素
* @return 栈顶元素
*/
@SuppressWarnings("unchecked")
public E peek(){
if(this.isEmpty()){
throw new EmptyStackException();
}
return (E) top.value;
}
/**
* 判断栈是否为空
*
* @return 栈空返回true,否则返回false
*/
public boolean isEmpty() {
return top == null;
}
}
栈的顺序映象(顺序栈):
package com.cskaoyan.stack;
import java.util.EmptyStackException;
/**
* @author shihao
* @create 2020-05-19 7:26
* <p>
* 栈的顺序映象
* API:
* void push(E e)
* E pop()
* E peek()
* boolean isEmpty()
*/
public class MyStack02<E> {
//栈的默认容量
private static final int DEFAULT_CAPACITY = 10;
//栈的最大容量
private static final int MAX_CAPACITY = Integer.MAX_VALUE - 8;
//属性
private Object[] elements;
private int top = -1;
//构造方法
public MyStack02() {
elements = new Object[DEFAULT_CAPACITY];
}
public MyStack02(int capacity) {
if (capacity <= 0 || capacity > MAX_CAPACITY) {
throw new IllegalArgumentException("capacity = " + capacity);
}
elements = new Object[capacity];
}
//方法
/**
* 入栈,即在栈顶添加元素
*
* @param e 待添加的元素
*/
public void push(E e) {
if (top == (elements.length - 1)) {
//扩容
int newLength = calculateCapacity();
grow(newLength);
}
top++;
elements[top] = e;
}
private void grow(int newCapacity) {
//数组大小是不可变的,应当新建一个更大的数组,将原数组的元素传递给它
Object[] newArr = new Object[newCapacity];
for (int i = 0; i < elements.length; i++) {
newArr[i] = elements[i];
}
//elements指向新数组
elements = newArr;
}
private int calculateCapacity() {
if (elements.length == MAX_CAPACITY) {
throw new RuntimeException("Stack is too large!");
}
int newCapacity = elements.length + elements.length >> 1;
if (newCapacity > MAX_CAPACITY || newCapacity < 0) {
newCapacity = MAX_CAPACITY;
}
return newCapacity;
}
/**
* 出栈,即删除栈顶元素
*
* @return 删除掉的元素
*/
@SuppressWarnings("unchecked")
public E pop() {
if (top == -1) {
throw new EmptyStackException();
}
E retValue = (E) elements[top];
elements[top] = null;
top--;
return retValue;
}
/**
* 查看栈顶元素
*
* @return 栈顶元素
*/
@SuppressWarnings("unchecked")
public E peek() {
if (top == -1) {
throw new EmptyStackException();
}
return (E) elements[top];
}
/**
* 判断栈是否为空
*
* @return 栈空返回true,否则返回false
*/
public boolean isEmpty() {
return top == -1;
}
}
练习题:反转字符串(要求用栈实现)
输入:“ABC”
输出:“CBA”
思路:
a.遍历字符串,获取每一个字符。charAt(int index)
b.将字符入栈
c.依次将字符出栈,并拼接
public class Demo01 {
public static void main(String[] args) {
String s = "123456";
System.out.println(reverse(s));
}
private static String reverse(String original) {
MyStack02<Character> myStack = new MyStack02<>();
for (int i = 0; i < original.length(); i++) {
char c = original.charAt(i);
myStack.push(c);
}
StringBuilder sb = new StringBuilder();
while (!myStack.isEmpty()) {
sb.append(myStack.pop());
}
return sb.toString();
}
}
应用场景:
-
函数调用栈:如在主方法中调用一个fun方法,会为fun方法创建一个栈帧,而一定是fun方法先执行结束的,然后主方法再执行结束。先调用的方法(主方法)后执行完,后调用的方法(fun方法)先执行完。这正好满足FILO特性。
-
括号匹配问题
老师代码:
public class StackDemo2 {
public static void main(String[] args) {
// String original = "([{}])";
// String original = "()[]{";
String original = "public static void main(String[ args) {System.out.println(bracketMatched(original));}";
System.out.println(bracketMatched(original));
}
/*
时间复杂度:O(n)
空间复杂度:O(a):字符串中括号的个数
*/
public static boolean bracketMatched(String original) {
Deque<Character> stack = new LinkedList<>();
// 遍历字符串, 获取每一个字符
for (int i = 0; i < original.length(); i++) {
char c = original.charAt(i);
if (c == '(') stack.push(')');
if (c == '[') stack.push(']');
if (c == '{') stack.push('}');
if (c == ')' || c == ']' || c== '}') {
if (stack.isEmpty()) return false;
if (c != stack.pop()) return false;
}
}
// 遍历结束后
return stack.isEmpty();
}
}
我的代码(天勤、LeetCode之有效的括号):
class Solution {
public boolean isValid(String s) {
char[] stack=new char[s.length()];
int top=-1;
for(int i=0;i<s.length();i++){
char ch=s.charAt(i);
if(ch=='('||ch=='['||ch=='{')
stack[++top]=ch;
else{
if(top==-1)
return false; //缺左括号
char left=stack[top--];
if(!isMathced(left,ch))
return false; //左右括号不匹配
}
}
if(top!=-1)
return false; //缺右括号
return true;
}
private boolean isMathced(char left,char right){
if(left=='('&&right==')')
return true;
else if(left=='['&&right==']')
return true;
else if(left=='{'&&right=='}')
return true;
else
return false;
}
}
-
编译器利用栈实现表达式求值
具体代码看之前天勤笔记。 -
浏览器的前进后退功能
-
利用栈实现 DFS