数据结构之使用java实现动态数组及用动态数组实现栈、队列

本文探讨了在Java中如何实现动态数组,并基于动态数组构建栈和队列。在动态数组部分,强调了Java中泛型数组的创建方式,以及在添加和删除元素时如何优化性能。栈的实现利用动态数组并提供了getLast和getFirst方法。队列部分,对比了普通队列(ArrayQueue)和循环队列(LoopQueue)在出队操作上的效率差异。同时,提供了一个使用括号的有效性判断问题作为应用场景。
摘要由CSDN通过智能技术生成

1.动态数组

1)注意点

java不支持直接new出泛型数组,需要使用ojbect进行转型

	public Array(int capacity) {
		this.data = (E[]) new Object[capacity];
		this.size = 0;
	}

使用add和remove改变数组长度时
若每次改变一个数组长度会影响性能
若每次改变二倍长度的话,均摊复杂度为O(1)

	public void add(int index,E e) {
		if (index<0||index>size) {
			throw new IllegalArgumentException("add failed.index<0 or index>size");
		}
		if (size==data.length) {
			reSize(2*data.length);
		}
		for (int i = size-1; i >= index ; i--) {
			data[i+1] = data[i];
		}
		data[index] = e;
		size++;
	}
	
	public E remove(int index) {
		if (index<0||index>=size) {
			throw new IllegalArgumentException("remove failed. index is illegal");
		}
		E ret = data[index];
		for (int i = index+1; i < data.length; i++) {
			data[i-1] = data[i];
		}
		size--;
		data[size] = null;
		if (size==data.length/2 && data.length!=0) {
			reSize(data.length/2);
		}
		return ret;
	}

但是这样有复杂度震荡的问题
假设数组长度为10,数组元素个数为10
使用addLast增加元素,使数组长度扩容两倍变为20
再使用removeLast删除元素,使数组长度缩减两倍变为10
反复此操作非常耗时
此时,addLast和removeLast的时间复杂度都为O(n)
所以在removeLast时判断数组长度缩减为的1/4时再将数组长度缩减一半,使用这种Lazy方式解决

	public E remove(int index) {
		if (index<0||index>=size) {
			throw new IllegalArgumentException("remove failed. index is illegal");
		}
		E ret = data[index];
		for (int i = index+1; i < data.length; i++) {
			data[i-1] = data[i];
		}
		size--;
		data[size] = null;
		if (size==data.length/4 && data.length!=0) {
			reSize(data.length/2);
		}
		return ret;
	}

2)源码


public class Array<E> {
	
	private E[] data;
	
	private int size;

	public Array(int capacity) {
		this.data = (E[]) new Object[capacity];
		this.size = 0;
	}
	
	public Array() {
		this(10);
	}

	public int getSize() {
		return size;
	}
	
	public int getCapacity() {
		return data.length;
	}
	
	public boolean isEmpty() {
		return size==0;
	}
	
	public void addLast(E e) {
		add(size, e);
	}
	
	public void addFirst(E e) {
		add(0,e);
	}
	
	public void add(int index,E e) {
		if (index<0||index>size) {
			throw new IllegalArgumentException("add failed.index<0 or index>size");
		}
		if (size==data.length) {
			reSize(2*data.length);
		}
		for (int i = size-1; i >= index ; i--) {
			data[i+1] = data[i];
		}
		data[index] = e;
		size++;
	}
	
	public void reSize(int newCapacity) {
		E[] newData = (E[])new Object[newCapacity];
		for (int i = 0; i < size; i++) {
			newData[i] = data[i];
		}
		data = newData;
	}

	@Override
	public String toString() {
		StringBuilder res = new StringBuilder();
		res.append(String.format("Array:size:%d , capacity = %d\n", size,data.length));
		res.append('[');
		for (int i = 0; i < size; i++) {
			res.append(data[i]);
			if (i!=size-1) {
				res.append(",");
			}
		}
		res.append(']');
		return res.toString();
	}
	
	public E get(int index) {
		if (index<0||index>=size) {
			throw new IllegalArgumentException("get failed.index is illegal");
		}
		return data[index];
	}
	
	public void set(int index, E e) {
		if (index<0||index>=size) {
			throw new IllegalArgumentException("set failed.index is illegal");
		}
		data[index] = e;
	}
	
	public boolean contains(int index,E e) {
		for (int i = 0; i < data.length; i++) {
			if (data[i].equals(e)) {
				return true;
			}
		}
		return false;
	}
	
	public int find(E e) {
		for (int i = 0; i < data.length; i++) {
			if (data[i].equals(e)) {
				return i;
			}
		}
		return -1;
	}
	
	public E remove(int index) {
		if (index<0||index>=size) {
			throw new IllegalArgumentException("remove failed. index is illegal");
		}
		E ret = data[index];
		for (int i = index+1; i < data.length; i++) {
			data[i-1] = data[i];
		}
		size--;
		data[size] = null;
		if (size==data.length/4 && data.length!=0) {
			reSize(data.length/2);
		}
		return ret;
	}
	
	public E removeFirst() {
		return remove(0);
	}
	
	public E removeLast() {
		return remove(size-1);
	}
	
	public void removeElement(E e) {
		int index = find(e);
		if (index!=-1) {
			remove(index);
		}
	}
}



2.栈

1)注意点

栈的实现需要使用动态数组类
在动态数组类中再加入getLast和getFirst方法

	public E getLast() {
		return get(size-1);
	}
	
	public E getFirst() {
		return get(0);
	}

源码中的push和pop方法均摊复杂度均为O(1)
其他方法的时间复杂度为O(1)

2)源码

创建Stack接口

public interface Stack<E> {
	
	int getSize();
	
	boolean isEmpty();
	
	void push(E e);
	
	E pop();
	
	E peak();
}

创建Stack接口实现类ArrayStack即栈

public class ArrayStack<E> implements Stack<E>{

	Array<E> array;
	
	public ArrayStack(int capacity) {
		array = new Array<>(capacity);
	}

	public ArrayStack() {
		array = new Array<>();
	}
	
	public int getCapcity() {
		return array.getCapacity();
	}
	
	@Override
	public int getSize() {
		return array.getSize();
	}

	@Override
	public boolean isEmpty() {
		return array.isEmpty();
	}

	@Override
	public void push(E e) {
		array.addLast(e);
	}

	@Override
	public E pop() {
		return array.removeLast();
	}

	@Override
	public E peak() {
		return array.getLast();
	}

	@Override
	public String toString() {
		StringBuilder res = new StringBuilder();
		res.append("Stack:");
		res.append('[');
		for (int i = 0; i < array.getSize(); i++) {
			res.append(array.get(i));
			if (i!=array.getSize()-1) {
				res.append(", ");
			}
		}
		res.append("] top");
		return res.toString();
	}
}

3)应用

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

	public boolean isVaild(String s) {
		Stack<Character> stack = new Stack<>();
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if (c == '(' || c == '[' || c == '{') {
				stack.push(c);
			}else {
				if (stack.isEmpty()) {
					return false;
				}
				Character topChar = stack.pop();
				if (c == ')' && topChar != '(' ) {
					return false;
				}
				if (c == ']' && topChar != '[' ) {
					return false;
				}
				if (c == '}' && topChar != '{' ) {
					return false;
				}
			}
		}
		return stack.isEmpty();
	}

3.队列

1)注意点

队列的实现需要使用动态数组类
同样,在动态数组类中再加入getLast和getFirst方法

	public E getLast() {
		return get(size-1);
	}
	
	public E getFirst() {
		return get(0);
	}

使用动态数组实现的普通队列:ArrayQueue中的dequeue出队方法,每次出队都需要前移数组中所有的元素,所以复杂度为O(n),使用动态数组实现循环队列:LoopQueue可以解决,LoopQueue中的dequeue出队方法,每次出队改变队首下标front,均摊复杂度为O(1)
俩种队列的enqueue入队方法均摊复杂度都为O(1)

2)源码

创建Queue接口

public interface Queue<E> {
	
	int getSize();
	
	boolean isEmpty();
	
	void enqueue(E e);
	
	E dequeue();
	
	E getFront();

}

创建接口实现类ArrayQueue

public class ArrayQueue<E> implements Queue<E>{
	
	private Array<E> array;

	public ArrayQueue() {
		array = new Array<>();
	}
	
	public ArrayQueue(int capacity) {
		array = new Array<>(capacity);
	}
	
	@Override
	public int getSize() {
		return array.getSize();
	}

	@Override
	public boolean isEmpty() {
		return array.isEmpty();
	}

	public int getCapacity() {
		return array.getCapacity();
	}
	
	@Override
	public void enqueue(E e) {
		array.addLast(e);
	}

	@Override
	public E dequeue() {
		return array.removeFirst();
	}

	@Override
	public E getFront() {
		return array.getFirst();
	}

	@Override
	public String toString() {
		StringBuilder res = new StringBuilder();
		res.append("Queue: ");
		res.append("front [");
		for (int i = 0; i < array.getSize(); i++) {
			res.append(array.get(i));
			if (i!=array.getSize()-1) {
				res.append(", ");
			}
		}
		res.append("] tail");
		return res.toString();
	}

}

创建接口实现类LoopQueue

public class LoopQueue<E> implements Queue<E> {

	private E[] data;

	private int front;

	private int tail;

	private int size;

	public LoopQueue() {
		this(10);
	}

	public LoopQueue(int capacity) {
		data = (E[]) new Object[capacity + 1];
		front = 0;
		tail = 0;
		size = 0;
	}

	public int getCapacity() {
		return data.length - 1;
	}

	@Override
	public int getSize() {
		return size;
	}

	@Override
	public boolean isEmpty() {
		return front == tail;
	}

	@Override
	public void enqueue(E e) {
		if ((tail+1)%data.length==front) {
			resize(getCapacity()*2);
		}
		data[tail] = e;
		tail = (tail+1)%data.length;
		size++;
	}

	private void resize(int newCapacity) {
		E[] newdate = (E[]) new Object[newCapacity+1];
		for (int i = 0; i < size; i++) {
			newdate[i] = data[(i+front)%data.length];
		}
		data = newdate;
		front = 0;
		tail = size;
	}

	@Override
	public E dequeue() {
		if (isEmpty()) {
			throw new IllegalArgumentException("cannot dequeue from an empty queue");
		}
		E ret = data[front];
		data[front] = null;
		front = (front+1)%data.length;
		size--;
		if (size == getCapacity()/4 && getCapacity()*2!=0) {
			resize(getCapacity()/2);
		}
		return ret;
	}

	@Override
	public E getFront() {
		if (isEmpty()) {
			throw new IllegalArgumentException("queue is empty");
		}
		return data[front];
	}

	@Override
	public String toString() {
		StringBuilder res = new StringBuilder();
		res.append(String.format("queue: size = %d , capacity = %d\n", size,getCapacity()));
		res.append("front [");
		for (int i = front; i != tail; i = (i+1)%data.length) {
			res.append(data[i]);
			if ((i+1)%data.length != tail) {
				res.append(", ");
			}
		}
		res.append("] tail");
		return res.toString();
	}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值