数据结构与算法(2)

栈的顺序存储结构

栈的定义
栈是限定仅在表尾进行插入和删除操作的线性表
我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)
不含任何数据元素的栈称为空栈
栈又称为后进先出的线性表,简称LIFO结构
定义中说的是在线性表的表尾进行插入和删除操作,这里表尾是指栈顶,而不是栈底
栈的插入操作,也叫做进栈,也称压栈,入栈
栈的删除操作,也叫做出栈,也称弹栈
栈接口Stack的定义

package com.study.zhan;
/**
 * Stack是栈的接口
 * */
public interface Stack<E> {
	/**
	 * 获取栈中元素的个数
	 * @return 栈中的元素个数
	 * */
	public int getSize();
	/**
	 * 判断当前栈是否为空
	 * @return 是否为空值
	 * */
	public boolean isEmpty();
	/**
	 * 进栈一个元素e
	 * @param e即将进栈的元素
	 * */
	public void push(E e);
	/**删除最后一个元素
	 * 
	 * */
	public E pop();
	/**
	 * 获取最后一个元素
	 * */
	public E peek();
	/**
	 * 清空栈
	 * */
	public void clear();
	
	
}

栈的顺序存储结构ArrayStack的定义
ArrayStack完全可以把ArrayList一个工具,这两者是聚合关系

package com.study.zhan;

import com.study.shuzu.ArrayList;

public class ArrayStack<E> implements Stack<E> {
	/**
	 * 栈的实现完全可以借助于ArrayList
	 * */
	private ArrayList<E> list;
	/**
	 * 创建一个默认大小的栈(顺序表)
	 * */
	public ArrayStack() {
		list = new ArrayList<E>();
	}
	/**
	 * 创建一个容量为指定capacity大小的栈(顺序表s)
	 * */
	public ArrayStack(int capacity) {
		list = new ArrayList<E>(capacity);
	}
	/**
	 * 获取有效元素的个数
	 * */
	@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;
		}
		if(obj == this) {
			return true;
		}
		if(obj instanceof ArrayStack) {
			ArrayStack stack = (ArrayStack) obj;
			return list.equals(stack.list);
		}
		return false;
	}
	/**
	 * 重写toString方法,用来显示数据元素栈
	 * */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("ArrayStack: size="+ getSize() +",capacity="+ list.getCapacity() +"\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();
	}

}

栈的测试类

package com.study.zhan;

import java.util.Arrays;

public class TestStack {
	public static void main(String[] args) {
		ArrayStack<Integer> stack = new ArrayStack<Integer>();
		System.out.println(stack);
		for(int i = 1;i < 7;i++) {
			stack.push(i);
//			System.out.println(stack.isEmpty());
		}
		System.out.println(stack.getSize());
		System.out.println(stack);
		ArrayStack<Integer> stack2 = new ArrayStack<Integer>(20);
		stack2.push(1);
		stack2.push(2);
		stack2.push(3);
		stack2.push(4);
		stack2.push(5);
		stack2.push(6);		
		System.out.println(stack2);
		System.out.println(stack2.peek());
		stack2.pop();
		System.out.println(stack2);
		System.out.println(stack.equals(stack2));
	}
	
}

双端栈的顺序存储结构

双端栈的定义
是指将一个线性表的两端当做栈底分别进行入栈和出栈操作
在这里插入图片描述
双端栈的顺序存储结构ArrayStackDoubleEnd的定义

package com.study.zhan;

import com.study.shuzu.ArrayList;

public class ArrayStackDoubleEnd<E> implements Stack<E> {
	
	enum Direction{
		LEFT,RIGHT;
	}
	
	private E[] data;
	private int leftTop;		//左端栈的栈顶开始在-1
	private int rightTop;		//右端栈的栈顶开始在data.length
	private static int DEFAULT_SIZE = 10;
	
	public ArrayStackDoubleEnd() {
		this(DEFAULT_SIZE);
	}
	
	public ArrayStackDoubleEnd(int capacity) {
		data = (E[]) new Object[capacity];
		leftTop = -1;
		rightTop = data.length;
	}
	/**
	 * 判断双端栈是否满
	 * */
	private boolean isFull() {
		return leftTop+1 == rightTop;
	}
	/**
	 * 先指定的端口进栈元素
	 * */
	public void push(Direction dir,E e) {
		if(isFull()) {
			//扩容
			resize(data.length * 2);
		}
		if(dir == Direction.LEFT) {
			data[++leftTop] = e;
		} else {
			data[--rightTop] = e;
		}
	}
	
	//双指针解题思想
	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;
	}

	/**
	 * 从指定的端口出栈元素
	 * */
	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;
		}
	}
	/**
	 * 从指定的端口获取栈顶元素
	 * */
	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;
		}
	}
	/**
	 *  判断指定端口的栈是否为空
	 * */
	public boolean isEmpty(Direction dir) {
		if(dir == Direction.LEFT) {
			return leftTop == -1;
		} else {
			return rightTop == data.length;
		}
	}
	/**
	 * 清空指定端口的栈
	 * */
	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(isFull()) {
			resize(data.length * 2);
		}
		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("两端栈为空");
		}
		if(getSize(Direction.LEFT) > getSize(Direction.RIGHT)) {
			return pop(Direction.LEFT);
		} else {
			return pop(Direction.RIGHT);
		}
	}
	
	
	/**
	 * 那端多获取那端,一样多默认左
	 */
	@Override
	public E peek() {
		if(isEmpty()) {
				throw new IllegalArgumentException("双端栈为空");
		}
		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() {
		StringBuilder sb = new StringBuilder();
		sb.append("ArrayStackDoubleEnd: 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) {
		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;
	}
	
}

双端栈的顺序存储结构—入栈操作
在这里插入图片描述
数据元素分别一左一右进入栈,这里存在两个栈顶一个左栈顶一个右栈顶,同时也有两个栈底,一个左栈底一个右栈底
双端栈的顺序存储结构—出栈操作
同样也是那边栈的元素较少,这边栈内元素就不出
双端栈的测试类

package com.study.zhan;

import com.study.zhan.ArrayStackDoubleEnd.Direction;

public class TestStackDoubleEnd {

	public static void main(String[] args) {
		ArrayStackDoubleEnd<Integer> stack = new ArrayStackDoubleEnd<Integer>();
		for(int i = 1;i <= 5;i++) {
			stack.push(Direction.LEFT, i);
		}
		System.out.println(stack);
		for(int i = 6;i <= 10;i++) {
			stack.push(Direction.RIGHT, i);
		}
		System.out.println(stack);
		for(int i = 11; i <= 15;i++) {
			stack.push(i);
		}
		System.out.println(stack);
		for(int i = 1;i <= 10;i++) {
			stack.pop();
		}
		System.out.println(stack);
		ArrayStackDoubleEnd<Integer> stack1 = new ArrayStackDoubleEnd<Integer>();
		stack1.push(Direction.LEFT, 1);
		stack1.push(Direction.LEFT, 2);
		stack1.push(Direction.LEFT, 3);
		stack1.push(Direction.RIGHT, 6);
		stack1.push(Direction.RIGHT, 7);
		System.out.println(stack.equals(stack1));
		System.out.println(stack1.peek());
	}

}

队列的顺序存储结构

队列的定义
队列是指只允许在一端进行插入操作,而另一端负责进行删除操作的线性表
队列是一种先进先出的线性表,简称FIFO
允许插入的一端称为队尾,允许删除的一端称为对头
在这里插入图片描述
队列接口Queue的定义

package com.study.duilie;

public interface Queue<E> {
	
	public int getSize();
	public boolean isEmpty();
	public void clear();
	/**
	 * 入队一个新元素
	 * */
	public void enqueue(E e);
	/**
	 * 出队一个元素
	 * */
	public E dequeue();
	/**
	 * 获取队首元素(不删除)
	 * */
	public E getFront();
	/**
	 * 获取队尾元素(不删除)
	 * */
	public E getRear();
}

队列的顺序存储结构ArrayQueue的定义

package com.study.duilie;

import com.study.shuzu.ArrayList;

public class ArrayQueue<E> implements Queue<E>{
	
	
	private ArrayList<E> list;
	
	public ArrayQueue() {
		list = new ArrayList<E>();
	}
	
	public ArrayQueue(int capacity) {
		list = new ArrayList<E>(capacity);
	}
	@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();
	}
	}

队列的顺序存储结构—入队操作O(1)
在这里插入图片描述
入队就相当于是给ArrayList.addLast
队列的顺序存储结构—出队操作O(n)
在这里插入图片描述
出队就相当于ArrayList.removeFirist

循环队列的顺序存储结构

队列顺序存储结构的弊端
队列的顺序存储结构本身是由ArrayList实现的
在数据元素入队的时候,相当于在Array List表尾添加元素
在数据元素出队的时候,相当于在ArrayList表头删除元素
很明显,入队的时间复杂度O(1),出队的时间复杂度O(n)
线性表增删数据元素时间复杂度都是O(n),但是这个是按平均算的
队列的出队时间复杂度O(n),可不是按平均算的,因为每次出队都是O(n)
优化第一步
队头指针和队尾指针随着元素的变化而移动,这样入队和出队操作都将是O(1)
优化第二步
当队尾或队头指针达到尾部时,如果需要后移重新指向表头

注意:队列满的条件为:(Rear + 1) % n == Front
队列空的条件为:Rear == Front

优化第三步
将一个空间预留出来不存任何元素,尾指针始终指向这个null空间
在这里插入图片描述
循环队列的定义
创建ArrayQueueLoop该类还是需要继承Queue接口

package com.study.duilie;

public class ArrayQueueLoop<E> implements Queue<E> {
	 
	private E[] data;
	private int front;
	private int rear;
	private int size;
	private static int DEFAULT_SIZE = 10;
	/**创建一个默认长度为10的数组*/
	public ArrayQueueLoop() {
		this(DEFAULT_SIZE);
	}
	/**指定一个长度的数组*/
	public ArrayQueueLoop(int capacity) {
		data = (E[]) new Object[capacity + 1];
		front = 0;
		rear = 0;
		size = 0;
	}
	/**直接访问数据元素个数*/
	@Override
	public int getSize() {
		return size;
	}
/**判断队列是否为空*/
	@Override
	public boolean isEmpty() {
		return front == rear && size == 0;
	}
/**清空队列*/
	@Override
	public void clear() {
		size = 0;
		front = 0;
		rear = 0;
		//与其缩容清空不如重新创建数组
	}

	@Override
	public void enqueue(E e) {
		if((rear + 1) % data.length == front) {
			resize(data.length * 2 - 1);
		}
		data[rear] = e;
		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];
		}
		front = 0;
		rear = index;
		data = newData;
	}

	@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 super.toString();
	}

}

循环队列测试类

package com.study.duilie;

public class TestArrayQueueLoop {

	public static void main(String[] args) {
		ArrayQueueLoop<Integer> queue = new ArrayQueueLoop<Integer>();
		for (int i = 1; i <= 15; i++) {
			queue.enqueue(i);
		}
		System.out.println(queue);
		for (int i = 1; i <= 10; i++) {
			queue.dequeue();
		}
		System.out.println(queue);
		System.out.println(queue.getSize());
		System.out.println(queue.getFront());
		System.out.println(queue.getRear());
	}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值