数据结构--栈Stack和队列Queue(三)

以下是学习恋上数据结构与算法的记录,本篇主要内容是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

猜你喜欢

转载自blog.csdn.net/qq_44961149/article/details/104538325