数组
二次封装属于自己的数组
public class Array<E> {private E[] data;private int size; //用于记录数组中元素的数量/*** 构造函数,传入数组的容量capacity构造Array* @param capacity 容量*/public Array(int capacity) {data = (E[])new Object[capacity];size = 0;}/*** 默认给10个空间*/public Array() {this(10);}
java泛型类
- java语言不支持直接new一个泛型数组
E[] data = new E[capacity]; - 实际应该
E[] data = (E[])new Object[capacity]; - 泛型类的申明中
Array<Integer> array = new Array<>();不需要在后面再添加Integer
数组的增删改查
---------------------------------------------------增-------------------------------------------------------/*** 往数组后面添加一个元素* @param e*/public void addLast(E e){//if (data.length == size){// throw new IllegalArgumentException("AddLast failed . Array is full");//}////data[size] = e;//size++;add(size,e);}/*** 往数组前面添加一个元素* @param e*/public void addFirst(E e){add(0,e);}/*** 在index的位置插入e* @param index* @param e*/public void add(int index,E e){if (data.length == size){//扩容两倍resize(2*data.length);}if (index < 0 || index>size){throw new IllegalArgumentException("Add failed . Require index>0 or index>size");}for (int i = size-1 ; i>=index ; i--){data[i+1] = data[i];}data[index] = e;size++;}---------------------------------------------------增----------------------------------------------------------------------------------------------------------查-------------------------------------------------------/*** 获取index位置的元素* @param index* @return*/E get(int index){if (index < 0 || index > size){throw new IllegalArgumentException("Get failed. Index is Illegal");}return data[index];}/*** 修改index的索引位置的元素为e* @param index* @param e*/E set(int index,E e){if (index < 0 || index > size){throw new IllegalArgumentException("Set failed. Index is Illegal");}return data[index] = e;}/*** 查找数组是否有元素e* @param e* @return*/public boolean contains(E e){for (int i = 0 ; i<size ; i++){if (data[i].equals(e)){return true;}}return false;}/*** 查找数组是否有元素e所在的索引,不存在则返回-1* @param e* @return*/public int find(E e){for (int i = 0 ; i<size ; i++){if (data[i].equals(e)){return i;}}return -1;}---------------------------------------------------查----------------------------------------------------------------------------------------------------------删-------------------------------------------------------/*** 删除指定位置的元素,并返回当前值* @param index* @return*/public E remove(int index){if (index < 0 || index > size){throw new IllegalArgumentException("Remove failed. Index is Illegal");}E ret = data[index];for (int i = index+1 ; i<size ; i++){data[i-1] = data[i];}size--;//如果数元素个数是当前数组大小的四分之一(原因是复杂度震荡),则当前数组容量也减半if (size == data.length/4 && data.length/2 !=0){resize(data.length/2);}return ret;}/*** 删除第一个* @return*/public E removeFirst(){return remove(0);}/*** 删除最后一个* @return*/public E removeLast(){return remove(size-1);}/*** 检验数组中有没有e,如果存在就删除他** 但只能删除一个,因为元素会重复**/public void removeElement(E e){int index = find(e);if (index != -1){remove(index);}}---------------------------------------------------删-------------------------------------------------------
动态数组
- 简单来说就是要满的时候扩充两倍,只装了一半的时候缩小两倍
看一下这个resize函数就行/*** 如果插入时数组已经满了,那就扩容* @param newCapacity*/private void resize(int newCapacity){E[] newData = (E[])new Object[newCapacity];for (int i = 0; i < size; i++) {newData[i] = data[i];}data = newData;}
分析动态数组的复杂度

复杂度震荡
比如说当前数组的容量是10,当大小要达到10的时候扩容(capacity*2),此时数组大小是11,然后又减去1,数组大小变为10,此时又要resize,执行capacity/2,这样就执行了两次的resize
由于resize的时间复杂度是O(n),而上述又执行了两次O(n),所以耗费了更多的时间
解决方案:Lazy
当size == capacity/4 时,才将capacity减半
栈
栈的基本实现
public class ArrayStack<E> implements Stack<E> {Array<E> array;public ArrayStack(int capacity) {array = new Array<>(capacity);}public ArrayStack() {array = new Array<>();}@Overridepublic int getSize() {return array.getSize();}@Overridepublic boolean isEmpty() {return array.isEmpty();}@Overridepublic void push(E e) {array.addLast(e);}@Overridepublic E pop() {return array.removeLast();}@Overridepublic E peek() {return array.getLast();}@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("Queue: ");sb.append("front[");for (int i = 0; i < array.getSize(); i++) {sb.append(array.get(i));if (i != array.getSize()-1)sb.append(", ");}sb.append("] tail");return sb.toString();}}
栈的一个应用
/*** Valid-Parenttheses* 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。* 使用的是java.util包里面的stack** @Author Li Cheng* @Date 2021/9/17 14:10* @Version 1.0*/public class Solution {public boolean isValid(String s) {Stack<Character> stack = new Stack<>();for (int i = 0; i < s.length(); i++) {char c = s.charAt(i);if (c == '(' || c=='[' || c=='{')stack.push(c);else {if (stack.isEmpty()){return false;}char topChar = stack.pop();if (c==')' && topChar != '(')return false;if (c==']' && topChar != '[')return false;if (c=='}' && topChar != '{')return false;}}return stack.isEmpty();}public static void main(String[] args) {Solution solution = new Solution();System.out.println(solution.isValid("({})"));System.out.println(solution.isValid("({}"));}}
队列
队列的实现

数组队列
- 相对简单,就是用数组实现一个队列,但有点浪费空间,出列之后front就不能往前移了

public class ArrayQueue<E> implements Queue<E> {Array<E> array;public ArrayQueue(int capacity) {array = new Array<>(capacity);}public ArrayQueue() {array = new Array<>(10);}@Overridepublic int getSize() {return array.getSize();}@Overridepublic boolean isEmpty() {return array.isEmpty();}@Overridepublic void enqueue(E e) {array.addLast(e);}@Overridepublic E dequeue(E e) {return array.removeFirst();}@Overridepublic E getFront() {return array.getFirst();}@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("Queue: ");sb.append("front[");for (int i = 0; i < array.getSize(); i++) {sb.append(array.get(i));if (i != array.getSize()-1)sb.append(", ");}sb.append("] tail");return sb.toString();}}
循环队列
- 有点难理解,可以把这个队列想象成一个圆,在不停的循环直到满列
- 队列为空:front == tail
- 队列已经满了:
(tail + 1)%c == front,c表示的是队列的长度 - 出队,front在数组队列中是直接+1,但这里是
(front+1)%c - 入队,tail在数组队列中是直接+1,但这里是
(tail+1)%c 扩容 : 要将原队列中的数据从front~tail依次复制给新队列
```java
public class LoopQueueimplements Queue { private E[] data; private int front,tail; private int size;
public LoopQueue(int capacity) {
//因为是泛型,所以要强制类型转换data = (E[])new Object[capacity+1];front = 0;tail = 0;size = 0;
}
public LoopQueue() {
this(10);
}
public int getCapacity(){
return data.length-1;
}
@Override public int getSize() {
return size;
}
@Override public boolean isEmpty() {
return front == tail;
}
//入队 @Override public void enqueue(E e) {
//队列满了,就扩容if((tail+1)%data.length == front )resize(getCapacity()*2);data[tail] = e;tail = (tail+1) % data.length;size++;
}
private void resize(int capacity){
E[] newData = (E[])new Object[capacity+1];for (int i = 0; i < size; i++) {newData[i] = data[(i+front) % data.length];}data = newData;front = 0;tail = size;
}
//出队@Overridepublic E dequeue(E e) {if (isEmpty())throw new IllegalArgumentException("Cannot dequeue from an empty queue");E ret = data[front];data[front] = null;front = (front+1)%data.length;size--;//如果只剩下四分之一就缩小容量if (size == getCapacity()/4 && getCapacity()/2 !=0){resize(getCapacity()/2);}return ret;}@Overridepublic E getFront() {return data[front];}@Overridepublic String toString() {StringBuilder res = new StringBuilder();res.append(String.format("Queue: size = %d , capacity = %d\n",size,getCapacity()));res.append("front [");for (int i=front ; i != tail ; i = (i+1)%data.length){res.append(data[i]);if ((i+1)%data.length != tail){res.append(",");}}res.append("] tail");return res.toString();}
}
<a name="tFs8i"></a># 练习题<a name="G8mpb"></a>## 不浪费一个空间的循环队列```java/*** 不浪费一个空间的循环队列** @Author Li Cheng* @Date 2021/9/18 13:26* @Version 1.0*/public class LoopQueueNoWaste<E> implements Queue<E>{private E[] data;private int front,tail;private int size;public LoopQueueNoWaste(int capacity){data = (E[]) new Object[capacity];front = 0;tail=0;size=0;}public LoopQueueNoWaste() {this(10);}public int getCapacity(){return data.length;}@Overridepublic int getSize() {return size;}@Overridepublic boolean isEmpty() {return front == tail;}/*** 入队* @param o*/@Overridepublic void enqueue(E o) {//如果满了就扩容if (size == getCapacity()){resize(data.length*2);}data[tail] = o;tail = (tail+1)% data.length;size++;}//扩容public void resize(int capacity){E[] newData = (E[]) new Object[capacity];for (int i = 0; i < size; i++) {newData[i] = data[(i+front)% data.length];}data = newData;front = 0;tail = size;}/*** 出队* @param* @return*/@Overridepublic E dequeue() {if (isEmpty())throw new IllegalArgumentException("Cannot dequeue from an empty queue");E ret = data[front];data[front] = null;front = (front+1)% data.length;size--;//如果只剩下四分之一就缩小容量if (size == getCapacity()/4 && size/2!=0){resize(getCapacity()/2);}return ret;}@Overridepublic E getFront() {return data[front];}@Overridepublic String toString() {StringBuilder res = new StringBuilder();res.append(String.format("Queue: size = %d , capacity = %d\n",size,getCapacity()));res.append("front [");for (int i=front ; i != tail ; i = (i+1)%data.length){res.append(data[i]);if ((i+1)%data.length != tail){res.append(",");}}res.append("] tail");return res.toString();}}
没有size成员变量的循环队列
/*** 浪费一个空间但是没有size** @Author Li Cheng* @Date 2021/9/18 13:55* @Version 1.0*/public class LoopQueueNoSize<E> implements Queue<E>{private E[] data;private int front,tail;public LoopQueueNoSize(int capacity){data = (E[]) new Object[capacity+1];front = 0;tail=0;}public LoopQueueNoSize() {this(10);}public int getCapacity(){return data.length-1;}@Overridepublic int getSize() {int size = (tail + (data.length-front))%data.length;return size;}@Overridepublic boolean isEmpty() {return tail == front;}@Overridepublic void enqueue(E e) {//队列满了,就扩容if ((tail+1)% data.length == front)resize(getCapacity()*2);data[tail] = e;tail = (tail+1) % data.length;}public void resize(int capacity){E[] newData = (E[]) new Object[capacity+1];for (int i = 0; i < getSize(); i++) {newData[i] = data[(i+front) % data.length];}data = newData;tail = getSize();front = 0;}@Overridepublic E dequeue() {//队列为空就报错if (isEmpty())throw new IllegalArgumentException("Cannot dequeue an empty queue");E ret = data[front];data[front] = null;front = (front+1)% data.length;if (getSize() == getCapacity()/4 && getCapacity()/2 != 0){resize(getCapacity()/2);}return ret;}@Overridepublic E getFront() {return data[front];}@Overridepublic String toString() {StringBuilder res = new StringBuilder();res.append(String.format("Queue: size = %d , capacity = %d\n",getSize(),getCapacity()));res.append("front [");for (int i=front ; i != tail ; i = (i+1)%data.length){res.append(data[i]);if ((i+1)%data.length != tail){res.append(",");}}res.append("] tail");return res.toString();}}
实现双端队列
- 双端队列图示

/*** @Author Li Cheng* @Date 2021/9/18 18:14* @Version 1.0*/public class TwoHeadLoopQueue<E>{private E[] data;private int front,tail;private int size;public TwoHeadLoopQueue(int capacity) {data = (E[]) new Object[capacity];front = 0;tail = 0;size = 0;}public TwoHeadLoopQueue() {this(10);}public boolean isEmpty() {return size == 0;}public boolean isFull(){return size == getCapacity();}public int getCapacity(){return data.length;}public void addLast(E e){if (isFull()){resize(getCapacity()*2);}data[tail] = e;tail = (tail+1)% data.length;size++;}public void addFront(E e){if (isFull()){resize(getCapacity()*2);}front = front==0 ? data.length - 1 : front-1;data[front] = e;size++;}public E removeLast(){if (isEmpty()){throw new IllegalArgumentException("Cannot dequeue an empty queue");}E ret = data[tail];tail = tail == 0 ? data.length -1 : tail-1 ;data[tail] = null;size--;if (size == getCapacity()/4 && getCapacity()/2 != 0){resize(getCapacity()/2);}return ret;}public E removeFront(){if (isEmpty()){throw new IllegalArgumentException("Cannot dequeue an empty queue");}E ret = data[front];data[front] = null;front = (front+1)% data.length;size--;if (size == getCapacity()/4 && getCapacity()/2 != 0){resize(getCapacity()/2);}return ret;}public E getFront(){if ((isEmpty())){throw new IllegalArgumentException("Queue is Empty");}return data[front];}public E getLast(){if ((isEmpty())){throw new IllegalArgumentException("Queue is Empty");}//因为tail指向的是队尾元素的下一个位置,所以需要计算真正的位置return data[ tail==0? data.length -1 : tail-1];}private void resize(int capacity){E[] newData = (E[]) new Object[capacity];for (int i = 0; i < size; i++) {newData[i] = data[(i+front)% data.length];}data = newData;tail = size;front = 0;}@Overridepublic String toString() {StringBuilder res = new StringBuilder();res.append(String.format("Queue: size = %d , capacity = %d\n",size,getCapacity()));res.append("front [");for (int i=front ; i != tail ; i = (i+1)%data.length){res.append(data[i]);if ((i+1)%data.length != tail){res.append(",");}}res.append("] tail");return res.toString();}}
用栈实现队列
import java.util.LinkedList;import java.util.Queue;/**** 用队列实现栈* @Author Li Cheng* @Date 2021/9/20 18:26* @Version 1.0*/public class StackForQueue {private Queue<Integer> q;private int top; // 用来追踪记录栈顶元素将原先的复杂度O(n)变为O(1)public StackForQueue() {q = new LinkedList<>();}public boolean isEmpty(){return q.isEmpty();}public void push(int x){q.add(x);top = x;}public int pop(){//用q2来存储q队列n个元素之前的n-1个元素Queue<Integer> q2 = new LinkedList<>();for (int i = 0; i < q.size()-1; i++) {//每次从q中取出一个元素,都给top赋值//top中存储的就是q中除了队尾元素以外的最后一个元素//即最新的栈顶元素top = q.peek();q2.add(q.remove());}int ret = q.remove();q = q2;return ret;}public int top(){return top;}}
用队列实现栈
import java.util.Stack;/*** @Author Li Cheng* @Date 2021/9/20 18:53* @Version 1.0*/public class MyQueue {private Stack<Integer> stack;/** Initialize your data structure here. */public MyQueue() {stack = new Stack<>();}/** Push element x to the back of queue. */public void push(int x) {Stack<Integer> stack2 = new Stack<>();while (stack.size() > 0){stack2.push(stack.pop());}stack.push(x);while (stack2.size() > 0){stack.push(stack2.pop());}}/** Removes the element from in front of queue and returns that element. */public int pop() {return stack.pop();}/** Get the front element. */public int peek() {return stack.peek();}/** Returns whether the queue is empty. */public boolean empty() {return stack.isEmpty();}}
