一、栈(先进后出)
栈的顺序存储结构
栈的定义:栈是限定在表尾进行插入和删除操作的线性表
允许插入和删除的一端称为栈顶,另一端称为栈底。
双端栈的定义: 双端栈是指将一个线性表的两端当作栈底分别进行入栈和出栈的操作。
1.Stack 栈接口定义
/**
* Stack是栈的接口
* @author ABC
*
*/
public interface Stack<E> {
/**
* 获取栈中元素的个数
* @return 栈中元素的个数
*/
public int getSize();
/**
* 判断当前栈是否为空
* @return 是否为空布尔值
*/
public boolean isEmpty();
/**
* 进栈一个元素e
* @param e 即将进栈的元素
*/
public void push(E e);
/**
* 出栈一个元素
* @return 当前出栈的栈顶元素
* */
public E pop();
/**
* 获取当前栈顶元素
* @return 当前栈顶的元素 不弹栈
* */
public E peek();
/**
* 清空栈
* */
public void clear();
}
2.ArrayStack 顺序栈类定义
因为栈也是一种先进后出的线性表,所以栈的实现完全可以借助于ArrayList。
(1)成员属性
private ArrayList<E> list;
(2)构造函数
/**
* 创建一个默认大小的栈(顺序表)
*/
public ArrayStack(){
list = new ArrayList<E>();
}
/**
* 创建一个容量为指定capacity大小的栈(顺序表)
* @param capacity
*/
public ArrayStack(int capacity){
list = new ArrayList<E>(capacity);
}
(3)成员函数
@Override
public int getSize() {
return list.getSize(); //返回栈中的有效元素个数
}
@Override
public boolean isEmpty() {
return list.isEmpty(); //返回栈是否为空
}
@Override
public void push(E e) {
list.addLast(e); // 从栈顶压栈
}
@Override
public E pop() {
return list.removeLast(); //移除栈顶元素
}
@Override
public E peek() {
return list.getLast(); //获取栈顶元素
}
@Override
public void clear() {
list.clear(); //清空栈
}
@Override
public boolean equals(Object obj) {
//判断栈是否为空
if(obj==null){
return false;
}
//判断obj是否和它自己相等
if(obj==this){
return true;
}
//判断obj是否是ArrayStack的一个实例
if(obj instanceof ArrayStack){
ArrayStack stack = (ArrayStack) obj;
return list.equals(stack.list);
}
return false;
}
@Override
public String toString() {
//当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类
StringBuilder sb = new StringBuilder();
sb.append("ArrayStack: size="+getSize()+",capacity="+list.getSize()+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append('[');
for(int i =0; i <getSize();i++){
sb.append(list.get(i));
if(i == getSize()-1){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
3.ArrayStackDoubleEnd 顺序双端栈类定义
(1)成员变量
//创建一个枚举变量,用于控制方向
enum Direction{
LEFT,RIGHT;
}
private static int DEFAULT_SIZE = 10; //默认容量大小为10
private E[] data; //用于存储数据的容器
private int leftTop; //左端栈顶 开始在-1
private int rightTop;//右端栈顶 开始在data.length
(2)构造函数
public ArrayStackDoubleEnd(){
this(DEFAULT_SIZE);
}
public ArrayStackDoubleEnd(int capacity){
data= (E[]) new Object[capacity];
leftTop=-1;
rightTop=data.length;
}
(3)成员函数
由于双端栈左右都可以进行操作,有方向之分,所以双端栈不仅要实现Stack接口的方法,还要自己定义指定方向进行操作的方法。
/**
* 判断栈是否为空
* @return
*/
private boolean isFull(){
return leftTop+1==rightTop;
}
/**
* 扩容、缩容
* @param newLen
*/
private void resize(int newLen){
E[] newData = (E[]) new Object[newLen];
//左端复制
for(int i=0;i<=leftTop;i++){
newData[i] = data[i];
}
//右端复制
int index = data.length-1;
int i;
for(i=newData.length-1;i>=newData.length-data.length+rightTop;i--){
newData[i]=data[index--];
}
rightTop=i+1;
data=newData;
}
/**
* 向指定端口进元素
* @param dir
* @param e
*/
public void push(Direction dir,E e){
//判空
if(isFull()){
resize(2*data.length);
}
if(dir==Direction.LEFT){
data[++leftTop] = e;
}else{
data[--rightTop] = e;
}
}
/**
* 从指定端口出栈元素
* @param dir
* @return
*/
public E pop(Direction dir){
if(dir==Direction.LEFT){
if(leftTop==-1){
throw new IllegalArgumentException("左端栈为空");
}
E e = data[leftTop--];
if(getSize() <= data.length/4 && data.length>DEFAULT_SIZE){
//左端缩容
resize(data.length/2);
}
return e;
}else{
if(rightTop==data.length){
throw new IllegalArgumentException("右端栈为空");
}
E e =data[rightTop++];
if(getSize() <= data.length/4&&data.length>DEFAULT_SIZE){
//右端缩容
resize(data.length/2);
}
return e;
}
}
/**
* 从指定的端口获取栈顶元素
* @param dir
* @return
*/
public E peek(Direction dir){
if(dir==Direction.LEFT){
if(leftTop==-1){
throw new IllegalArgumentException("左端栈为空");
}
return data[leftTop];
}else{
if(rightTop==data.length){
throw new IllegalArgumentException("右端栈为空");
}
return data[rightTop];
}
}
/**
* 获取指定端口栈的元素个数
*/
public int getSize(Direction dir){
if(dir==Direction.LEFT){
return leftTop+1;
}else{
return data.length-rightTop;
}
}
/**
* 判断指定端口的栈是否为空
* @param dir
* @return
*/
public boolean isEmpty(Direction dir){
if(dir==Direction.LEFT){
return leftTop==-1;
}else{
return rightTop==data.length;
}
}
/**
* 清空指定端口栈
* @param dir
*/
public void clear(Direction dir){
if(dir==Direction.LEFT){
leftTop=-1;
}else{
rightTop=data.length;
}
}
/**
* 获取左端栈和右端栈元素的总和
*/
@Override
public int getSize() {
return getSize(Direction.LEFT)+getSize(Direction.RIGHT);
}
/**
* 判断左端栈和右端栈是否全为空
*/
@Override
public boolean isEmpty() {
return isEmpty(Direction.LEFT)&&isEmpty(Direction.RIGHT);
}
/**
* 哪端少,就进哪端
*/
@Override
public void push(E e) {
if(getSize(Direction.LEFT) <= getSize(Direction.RIGHT)){
push(Direction.LEFT,e);
}else{
push(Direction.RIGHT,e);
}
}
/**
* 哪端多,弹哪端
*/
@Override
public E pop() {
if(isEmpty()){
throw new IllegalArgumentException("双端栈为空");
}
// TODO Auto-generated method stub
if(getSize(Direction.LEFT)>getSize(Direction.RIGHT)){
return pop(Direction.LEFT);
}else{
return pop(Direction.RIGHT);
}
}
/**
* 哪端多,获取哪端,一样多默认左
*/
@Override
public E peek() {
// TODO Auto-generated method stub
if(isEmpty()){
throw new IllegalArgumentException("双端栈为空");
}
// TODO Auto-generated method stub
if(getSize(Direction.LEFT)>getSize(Direction.RIGHT)){
return peek(Direction.LEFT);
}else{
return peek(Direction.RIGHT);
}
}
/**
* 两端都清空
*/
@Override
public void clear() {
clear(Direction.LEFT);
clear(Direction.RIGHT);
}
@Override
public String toString() {
// TODO Auto-generated method stub
StringBuilder sb = new StringBuilder();
sb.append("ArrayStack: size="+getSize()+",capacity="+data.length+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append('[');
int count = 0;
for(int i =0;i<=leftTop;i++){
sb.append(data[i]);
count++;
if(count==getSize()){
sb.append(']');
}else{
sb.append(',');
}
}
for(int i=rightTop;i<data.length;i++){
sb.append(data[i]);
count++;
if(count==getSize()){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
/**
* 纯粹比较两个双端栈的内容
*/
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(obj==null){
return false;
}
if(obj==this){
return true;
}
if(obj instanceof ArrayStackDoubleEnd){
ArrayStackDoubleEnd<E> stack=(ArrayStackDoubleEnd)obj;
if(getSize()==stack.getSize()){
ArrayList<E> list1 = new ArrayList<E>(getSize());
ArrayList<E> list2 = new ArrayList<E>(getSize());
//拼接当前栈的左部
for(int i=0;i<=leftTop;i++){
list1.addLast(data[i]);
}
//拼接当前栈的右部
for(int i =rightTop;i<data.length;i++){
list1.addLast(data[i]);
}
//拼接传入栈的左部
for(int i=0;i<=stack.leftTop;i++){
list2.addLast(stack.data[i]);
}
//拼接传入栈的右部
for(int i =stack.rightTop;i<stack.data.length;i++){
list2.addLast(stack.data[i]);
}
return list1.equals(list2);
}
}
return false;
}
二、队列(先进先出)
定义:队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
队列也是一种特殊的线性表,有类似线性表的各种操作,不同的就是插入和删除元素。
1.Queue 队列接口定义
public interface Queue<E> {
/**
* 获取队列中有效元素的个数
* @return
*/
public int getSize();
/**
* 判断队列是否为空
* @return
*/
public boolean isEmpty();
/**
* 清空队列
*/
public void clear();
/**
* 入队一个新元素e
* @param e
*/
public void enqueue(E e);
/**
* 出队一个元素e
* @return
*/
public E dequeue();
/**
* 获取队首元素(不删除)
* @return
*/
public E getFront();
/**
* 获取队尾元素(不删除)
* @return
*/
public E getRear();
}
2.ArrayQueue 顺序队列类定义
(1)成员变量
private ArrayList<E> list;
(2)构造函数
public ArrayQueue(){
list = new ArrayList<E>();
}
public ArrayQueue(int capacity){
list =new ArrayList<E>(capacity);
}
(3)成员函数
@Override
public int getSize() {
return list.getSize();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public void clear() {
list.clear();
}
@Override
public void enqueue(E e) {
list.addLast(e);
}
@Override
public E dequeue() {
return list.removeFirst();
}
@Override
public E getFront() {
return list.getFirst();
}
@Override
public E getRear() {
return list.getLast();
}
@Override
public boolean equals(Object obj) {
if(obj==null){
return false;
}
if(obj==this){
return true;
}
if(obj instanceof ArrayQueue){
ArrayQueue queue = (ArrayQueue) obj;
return list.equals(queue.list);
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ArrayQueue: size="+getSize()+",capacity="+list.getSize()+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append('[');
for(int i =0; i <getSize();i++){
sb.append(list.get(i));
if(i == getSize()-1){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
3.ArrayQueueLoop 循环顺序队列类定义
假设队列的总个数不超过5个,但末尾位置已被占,再向后加就会产生数组越界。可实际上,队列的下标为0、1的位置空闲,这样的现象被称为“假溢出”,为了解决这种情况,所以就有了循环队列,队尾满了,就往对头加,插入和删除的时候也不需要移动其他元素。
(1)成员变量
private static int DEFAULT_SIZE = 10; //默认容量大小
private E[] data; //存储数据元素的容器
private int front; //对头指针
private int rear; //队尾指针
private int size; //有效元素的个数
(2)构造函数
/**
* 创建一个默认值为10线性表
*/
public ArrayQueueLoop(){
this(DEFAULT_SIZE);
}
/**
* 创建一个容量为指定capacity的线性表
* @param capacity 指定容量
*/
public ArrayQueueLoop(int capacity){
//capacity+1 预留一个空位,但是不算在data.length中
data = (E[]) new Object[capacity+1];
front = 0;
rear = 0;
size = 0;
}
(3)成员函数
@Override
public int getSize() {
return size;
}
/**
* 判断队列是否为空,判空的条件是 front == rear && size == 0
*/
@Override
public boolean isEmpty() {
return front == rear && size == 0;
}
@Override
public void clear() {
size = 0;
front = 0;
rear = 0;
}
/**
* 扩容、缩容
*/
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];
}
front = 0;
rear = index;
data = newData;
}
/**
* 入队一个新元素e之前要判断队列是否已满
* 判满条件 (rear+1)%data.length == front
*/
@Override
public void enqueue(E e) {
// TODO Auto-generated method stub
if((rear+1)%data.length == front){
//扩容
resize(2*data.length-1);
}
data[rear] = e;
rear = (rear+1)%data.length;
size++;
}
/**
* 出队一个元素之前要先判断队列是否为空
*/
@Override
public E dequeue() {
if(isEmpty()){
throw new IllegalArgumentException("队列为空!");
}
E e = data[front];
front = (front+1)%data.length;
size--;
if(size<=data.length/4&&data.length>DEFAULT_SIZE) {
//缩容
resize(data.length/2+1);
}
return e;
}
@Override
public E getFront() {
return data[front];
}
@Override
public E getRear() {
return data[(data.length+rear-1)%data.length];
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ArrayQueueLoop: size="+getSize()+",capacity="+(data.length-1)+"\n");
if(isEmpty()){
sb.append("[]");
}else{
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(',');
}
}
}
return sb.toString();
}