以下是学习恋上数据结构与算法的记录,本篇主要内容是Java实现栈
◼栈是一种特殊的线性表,只能在一端进行操作
●往栈中添加元素的操作,一般叫做push,入栈
●从栈中移除元素的操作,一般叫做pop,出栈(只能移除栈顶元素,也叫做:弹出栈顶元素)
●后进先出的原则,Last In First Out,LIFO(类似于火车高铁进出站)
栈的内部实现可以使用动态数组、链表等数据结构
◼栈的接口设计:
●int size();// 元素的数量
●boolean isEmpty();// 是否为空
●void push(E element);// 入栈
●E pop();// 出栈
●E top();// 获取栈顶元素
●void clear();// 清空栈底栈顶
直接使用之前的动态数组或者链表
public class Stack<E> {
List<E> list = new ArrayList<>();
// 元素的数量
public int size() {
return list.size();
}
// 是否为空
public boolean isEmpty() {
return list.isEmpty();
}
// 入栈
public void push(E element) {
list.add(element);
}
// 出栈
public E pop() {
return list.remove(list.size()-1);
}
//换取栈顶元素
public E top() {
return list.get(list.size()-1);
}
浏览器的前进和后退,或者软件的撤销(Undo)、恢复(Redo)功能就是使用了栈来实现的(两个栈结构)。
●后退操作图
每次当前访问的页面都是栈顶元素,后退则是将当前页面放入另一个空栈中记录下来,该操作是为了前进操作返回页面元素做准备,防止页面丢失前进不了。
●前进操作图
●若访问新网站,需要将右栈清空,放弃无用元素。
◼队列是一种特殊的线性表,只能在头尾两端进行操作
●队尾(rear):只能从队尾添加元素,一般叫做enQueue,入队
●队头(front):只能从队头移除元素,一般叫做deQueue,出队
●先进先出的原则,First In First Out,FIFO
◼队列的接口设计:
●int size();// 元素的数量
●boolean isEmpty();// 是否为空
●void clear();// 清空
●void enQueue(E element);// 入队
●E deQueue();// 出队
●E front();// 获取队列的头元素
●动态数组、链表都可以实现队列,但优先使用双向链表,因为队列主要是往头尾操作元素
public class Queue<E> {
private List<E> list = new LinkedList<E>();
// 元素的数量
public int size() {
return list.size();
}
//清空
public void clear() {
list.clear();
}
// 是否为空
public boolean isEmpty() {
return list.isEmpty();
}
// 入队
public void enQueue(E element) {
list.add(element);
};
// 出队
public E deQueue() {
return list.remove(0);
}
public E front() {
return list.get(0);
}
}
◼循环队列(Circle Queue)
◼其实队列底层也可以使用动态数组实现,并且各项接口也可以优化到O(1) 的时间复杂度,这个用数组实现并且优化之后的队列也叫做:循环队列 ,底层用数组实现。
假设初始循环队列,多了一个队头属性记录着头元素的下标。
出队
入队
◼数组实现循环队列
public class CircleQueue<E> {
private int front;//队头下标
private int size;//元素数量
private E[] elements;//所有的元素
private static final int DEFAULT_CAPACITY = 10;//默认大小
//构造函数创建默认大小队列存储元素
public CircleQueue() {
elements = (E[]) new Object[DEFAULT_CAPACITY];
}
// 元素的数量,直接返回size
public int size() {
return size;
}
// 是否为空,size为0即队列是空的
public boolean isEmpty() {
return size==0;
}
//清空,front属性清空,循环队列全部置为null,size=0
public void clear() {
front=0;
for(int i=0;i<size;i++) {
elements[i]=null;
}
size=0;
}
// 入队
public void enQueue(E element) {
ensureCapacity(size+1);//保证队列容量
//elements[队尾位置 size]=element(入队元素值)
elements[index(size)]=element;
size++;
};
// 出队
public E deQueue() {
E frontElement = elements[front];//记录出队元素
elements[front] = null;//队列【队头位置】=null清空
front = index(1);//队头下标+1
size--;
return frontElement;
}
//查看队头元素
public E front() {
return elements[front];
}
//输出打印方法
public String toString() {
StringBuilder string = new StringBuilder();
string.append("capcacity=").append(elements.length)
.append(" size=").append(size)
.append(" front=").append(front)
.append(", [");
for (int i = 0; i < elements.length; i++) {
if (i != 0) {
string.append(", ");
}
string.append(elements[i]);
}
string.append("]");
return string.toString();
}
//索引映射封装方法
private int index(int index) {
return(front+index)%elements.length;
}
/**
* 保证要有capacity的容量
* @param capacity
*/
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
if (oldCapacity >= capacity) return;
// 新容量为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[index(i)];
}
elements = newElements;
// 重置front
front = 0;
}
}
◼%运算符优化
●尽量避免使用乘*、除/、模%、浮点数运算,效率低下
private int index(int index) {
index += front;
//(front+index)%elements.length
return index - (index >= elements.length ? elements.length : 0);
}
●已知n>=0,m>0 ,n%m等价于n –(m > n ? 0 : m) 的前提条件:n < 2m