4. 双端栈(ArrayDoubleEndStack)
(1)双端栈的定义
双端栈 是指将一个线性表的两端当做栈底分别进行入栈和出栈操作
主要利用了栈 栈底位置不变,而栈顶位置动态变化 的特性。
双端栈的特点:
- 双端栈是线性表的一种,更是栈的一个特殊分类
- 所以我们可以用动态数组和栈的思想来实现双端栈
- 毕竟由于其操作过于特殊,并不能借助ArrayList或ArrayStack实现
- 所以这里从头开始实现双端栈
(2)双端栈的扩容、缩容问题
(3)代码实现
package test;
import java.util.Iterator;
public class ArrayDoubleEndStack<E> implements Iterable<E> {
private int ltop;//左端栈顶
private int rtop;//右端栈顶
private E[] data;
private static int DAFUALT_CAPACITY = 10;
public ArrayDoubleEndStack(){
data = (E[]) new Object[DAFUALT_CAPACITY];
ltop = -1;
rtop = data.length;
}
public ArrayDoubleEndStack(int capacity){
DAFUALT_CAPACITY = capacity;
data = (E[]) new Object[DAFUALT_CAPACITY];
ltop = -1;
rtop = data.length;
}
public void pushLeft(E element){
if (ltop + 1 == rtop) {
resize(data.length * 2);
}
data[++ ltop] = element;
}
public void pushRight(E element){
if (ltop + 1 == rtop) {
resize(data.length * 2);
}
data[-- rtop] = element;
}
private void resize(int newLen) {
E[] newData = (E[]) new Object[newLen];
for (int i = 0; i < ltop; i++) {
newData[i] = data[i];
}
//复制右端栈的元素
int index = rtop;
for (int i = newLen - sizeRight(); i < newLen; i++) {
newData[i] = data[index ++];
}
rtop = newLen - sizeRight() - 1;
data = newData;
}
public E popLeft(){
if (isLeftEmpty()) {
throw new IllegalArgumentException("Left Stack Is Empty");
}
E ret = data[ltop --];
if (sizeLeft()+sizeRight() <= data.length / 4 && data.length > DAFUALT_CAPACITY){
resize(data.length/2);
}
return ret;
}
public E popRight(){
if (isRightEmpty()) {
throw new IllegalArgumentException("Left Stack Is Empty");
}
return data[rtop ++];
}
public E peekLeft(){
if (isLeftEmpty()) {
throw new IllegalArgumentException("Left Stack Is Empty");
}
return data[ltop];
}
public E peekRight(){
if (isRightEmpty()) {
throw new IllegalArgumentException("Left Stack Is Empty");
}
return data[rtop];
}
public boolean isLeftEmpty(){
return ltop == -1;
}
public boolean isRightEmpty(){
return rtop == data.length;
}
public int sizeRight(){
return ltop + 1;
}
public int sizeLeft(){
return data.length-rtop;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
if (isLeftEmpty() && isRightEmpty()){
sb.append(']');
}
for (int i = 0; i <= ltop; i++) {
sb.append(data[i]);
if (i == ltop && isRightEmpty()){
sb.append(']');
}else{
sb.append(',');
}
}
for (int i = rtop; i < data.length; i++) {
sb.append(data[i]);
if (i == data.length-1) {
sb.append(']');
}else{
sb.append(',');
}
}
return sb.toString();
}
@Override
public Iterator<E> iterator() {
return null;
}
}
第三节 队列
1.队列的定义
队列 是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
我们把允许删除的一端称为队首(front),插入的一端称为队尾(rear)
队列有以下基本概念:
- 不含任何数据元素的队列称为空队列
- 队列是一种先进先出(First In Last Out)的线性表,简称FIFO
- 队列本身是一个线性表,其数据元素具有线性关系,只不过它是一种特殊的线性表而
- 队列的插入操作,叫作入队
- 队列的删除操作,叫作出队
2. 队列接口的定义
同样队列可以顺序存储实现也可以链表存储实现,所以将共性抽取定义出Queue接口
public interface Queue<E> extends Iterable<E> {
public int size(); //队中元素数量
public boolean isEmpty(); //队空判断
public void offer(E element);//入队
public E poll();//出队
public E peek();//获取队首元素
public void clear();//清空队列
}
3.ArrayQueue类
该类为队列的顺序存储具体实现,因为队列本身就是一种特殊的线性表,所以我们可以借用之前完成的ArrayList来实现我们的ArrayQueue
package p2.array.lists;
import p1.interfaces.Queue;
import java.util.Iterator;
import java.util.Objects;
public class ArrayQueue<E> implements Queue<E> {
private ArrayList<E> list;
public ArrayQueue(){
list = new ArrayList<>();
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public void offer(E element) {
list.add(list.size(),element);
}
@Override
public E poll() {
return list.remove(0);
}
@Override
public E element() {
return list.get(0);
}
@Override
public void clear() {
list.clear();
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if(this == o){
return true;
}
if (o instanceof ArrayQueue){
ArrayQueue other = (ArrayQueue) o;
return list.equals(other.list);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(list);
}
@Override
public String toString() {
return list.toString();
}
@Override
public Iterator<E> iterator() {
return list.iterator();
}
}
4.队列实现文件夹遍历
树形图层序遍历的方法,如果是个目录,则入队,如果是文件,则直接打印文件名
代码如下:
package test;
import p2.array.lists.ArrayQueue;
import java.io.File;
public class DirTraversal {
public static void main(String[] args) {
File dir = new File("E:\\WorkSpace\\Java");
ArrayQueue<File> dirs = new ArrayQueue<>();
dirs.offer(dir);
/*
*
* 只要队列不为空,则出队一个对象,将该目录打开遍历,遇到目录进队。遇到文件打印
* 直到队列为空
*
* */
while (!dirs.isEmpty()){
File file = dirs.poll();
System.out.println("【"+file.getName()+"】");
File[] files = file.listFiles();
for (File f : files){
if(f.isFile()){
System.out.println(f.getName());
}else{
dirs.offer(f);
}
}
}
}
}
5.用栈实现队列
package test;
import p1.interfaces.Queue;
import p2.array.lists.ArrayStack;
import java.util.Iterator;
public class StackImQueue {
public static void main(String[] args) {
QueueImByStack<Integer> nums = new QueueImByStack<>();
for (int i = 0; i < 10; i++) {
nums.offer(i);
}
System.out.println(nums);
System.out.println(nums.poll());
System.out.println(nums);
}
}
class QueueImByStack<E> implements Queue<E> {
private ArrayStack<E> stackA;
private ArrayStack<E> stackB;
public QueueImByStack(){
stackA = new ArrayStack<>();
stackB = new ArrayStack<>();
}
@Override
public int size() {
return stackA.size();
}
@Override
public boolean isEmpty() {
return stackA.isEmpty();
}
@Override
public void offer(E element) {
stackA.push(element);
}
@Override
public E poll() {
while (stackA.size() != 1){
stackB.push(stackA.pop());
}
E ret = stackA.pop();
while(!stackB.isEmpty()){
stackA.push(stackB.pop());
}
return ret;
}
@Override
public E element() {
while (stackA.size() != 1){
stackB.push(stackA.pop());
}
E ret = stackA.peek();
while(!stackB.isEmpty()){
stackA.push(stackB.pop());
}
return ret;
}
@Override
public void clear() {
stackA.clear();
}
@Override
public String toString() {
return stackA.toString();
}
@Override
public Iterator iterator() {
return stackA.iterator();
}
}
6.循环队列(ArrayLoopQueue)
该循环队列的实现思想也是动态数组,但是由于操作元素的特殊性,并不能直接由ArrayList或ArrayQueue实现,所以从头开始定义ArrayLoopQueue
package p2.array.lists;
import p1.interfaces.Queue;
import java.util.Iterator;
public class ArrayLoopQueue<E> implements Queue<E>{
private E[] data;
private int front;
private int rear;
private int size;
private static int DAFAULT_CAPACITY = 10;
public ArrayLoopQueue(){
data =(E[])new Object[DAFAULT_CAPACITY + 1];
front = 0;
rear = 0;
size = 0;
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return front == rear;
}
@Override
public void offer(E element) {
if((rear + 1 ) % data.length == front){
resize(data.length * 2 - 1);
}
data[rear] = element;
rear = (rear + 1 ) % data.length;
size ++;
}
private void resize(int newLen) {
E[] newData = (E[])new Object[newLen];
int index = 0;
for (int i = front;i != rear;i = (i+1)%data.length){
newData[index++] = data[i];
}
data = newData;
front = 0;
rear = index;
}
@Override
public E poll() {
if(isEmpty()){
throw new IllegalArgumentException("queue is null");
}
E ret = data[front];
front = (front + 1) % data.length;
size --;
if (size <= (data.length - 1) / 4 && data.length - 1 > DAFAULT_CAPACITY){
resize((data.length+1)/2);
}
return ret;
}
@Override
public E element() {
if(isEmpty()){
throw new IllegalArgumentException("queue is null");
}
return data[front];
}
@Override
public void clear() {
data = (E[]) new Object[DAFAULT_CAPACITY];
rear = 0;
size = 0;
front = 0;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
if (isEmpty()){
sb.append(']');
}
for (int i = front; i != rear; i = (i+1)%data.length) {
sb.append(data[i]);
if((i + 1) %data.length == rear){
sb.append(']');
}else{
sb.append(',');
sb.append(' ');
}
}
return sb.toString();
}
@Override
public boolean equals(Object o) {
if(o == null) return false;
if (this == o){
return true;
}
if(o instanceof ArrayLoopQueue){
ArrayLoopQueue<E> other = (ArrayLoopQueue<E>) o;
if(size != other.size){
return false;
}
int i = front;
int j = other.front;
while (i != rear){
if(!data[i].equals(other.data[j])){
return false;
}
i = (i + 1 )% data.length;
j = (j + 1) % other.data.length;
}
return true;
}
return false;
}
@Override
public Iterator<E> iterator() {
return new ArrayLoopQueueIterator();
}
class ArrayLoopQueueIterator implements Iterator<E>{
private int cur = front;
@Override
public boolean hasNext() {
return cur != front;
}
@Override
public E next() {
E ret = data[cur];
cur = (cur + 1) % data.length;
return ret;
}
}
}
7. 双端队列
双端队列(double ended queue ,deque),是限定插入和删除操作在表的两端进行的线性表,是一种具有队列和栈的性质的数据结构
(1)双端队列的接口实现
package p1.interfaces;
public interface Deque<E> extends Queue<E> {
public void addFirst(E element);
public void addLast(E element);
public E removeFirst();
public E removeLast();
public E getFirst();
public E getLast();
}
(2)双端队列的代码实现
package p2.array.lists;
import p1.interfaces.Deque;
import p1.interfaces.Stack;
import java.util.Iterator;
public class ArrayDeque<E> implements Deque<E>, Stack<E> {
private E[] data;
private int front;
private int rear;
private int size;
private static int DAFAULT_CAPACITY = 10;
public ArrayDeque(){
data = (E[]) new Object[DAFAULT_CAPACITY + 1];
front = 0;
size = 0;
rear = 0;
}
@Override
public void addFirst(E element) {
if ((rear + 1) % data.length-1 == front){
resize(data.length*2-1);
}
front = (front - 1 + data.length) %data.length;
data[front] = element;
size ++;
}
@Override
public void addLast(E element) {
if (size == data.length-1){
resize(data.length*2-1);
}
data[rear] = element;
rear = (rear + 1) % data.length;
size ++;
}
private void resize(int newLen) {
E[] newData = (E[])new Object[newLen];
int index = 0;
for (int i = front;i != rear;i = (i+1)%data.length){
newData[index++] = data[i];
}
data = newData;
front = 0;
rear = index;
}
@Override
public E removeFirst() {
return null;
}
@Override
public E removeLast() {
if (isEmpty()){
throw new IllegalArgumentException("queue is null");
}
rear = (rear -1 + data.length) % data.length;
E ret = data[rear];
size--;
if (size <= (data.length - 1)/4 && data.length - 1 > DAFAULT_CAPACITY){
resize((data.length-1) / 2 + 1);
}
return ret;
}
@Override
public E getFirst() {
if (isEmpty()){
throw new IllegalArgumentException("queue is null");
}
return data[front];
}
@Override
public E getLast() {
if (isEmpty()){
throw new IllegalArgumentException("queue is null");
}
return data[(rear - 1 + data.length) %data.length];
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0 && front == rear;
}
@Override
public void push(E element) {
}
@Override
public E pop() {
return null;
}
@Override
public E peek() {
return getLast();
}
@Override
public void offer(E element) {
addLast(element);
}
@Override
public E poll() {
return removeFirst();
}
@Override
public E element() {
return getFirst();
}
@Override
public void clear() {
data = (E[]) new Object[DAFAULT_CAPACITY];
front = 0;
size = 0;
rear = 0;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
if (isEmpty()){
sb.append(']');
}
for (int i = front; i != rear; i = (i+1)%data.length) {
sb.append(data[i]);
if((i + 1) %data.length == rear){
sb.append(']');
}else{
sb.append(',');
sb.append(' ');
}
}
return sb.toString();
}
@Override
public boolean equals(Object o) {
if(o == null) return false;
if (this == o){
return true;
}
if(o instanceof ArrayDeque){
ArrayDeque<E> other = (ArrayDeque<E>) o;
if(size != other.size){
return false;
}
int i = front;
int j = other.front;
while (i != rear){
if(!data[i].equals(other.data[j])){
return false;
}
i = (i + 1 )% data.length;
j = (j + 1) % other.data.length;
}
return true;
}
return false;
}
@Override
public Iterator<E> iterator() {
return new ArrayDeque<E>.ArrayDequeIterator();
}
class ArrayDequeIterator implements Iterator<E>{
private int cur = front;
@Override
public boolean hasNext() {
return cur != front;
}
@Override
public E next() {
E ret = data[cur];
cur = (cur + 1) % data.length;
return ret;
}
}
}