JAVA数据结构(二)

栈和队列

一、栈

  • 栈:栈是一种后进先出的数据结构(LIFO)
    在这里插入图片描述
  • 栈的实现:为了让逻辑更为清晰,采用接口实现。在接口中实现这5种方法
  1. 入栈
  2. 出栈
  3. 查看栈顶
  4. 查看栈的元素
  5. 查看栈是否为空
    在这里插入图片描述
  • 定义栈的接口,并实现上述五种抽象方法
public interface Stack<E> {
	int getSize();
	boolean isEmpty();
	void push(E e);
	E pop();
	E peek();
}
  • 利用自定义数组来实现自定义的堆栈
package cn.itcast.day2;

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

	myArray<E> array; //本质上栈是数组的一个子集,因此五种方法可以通过数组来进行实现
	public ArrayStack(int capacity) {
		array = new myArray<E>(capacity); 
	}
	public ArrayStack() {
		array = new myArray<E>();
	}
	@Override
	public int getSize() {
		return array.getSize();
	}
	@Override
	public boolean isEmpty() {
		return array.isEmpty();
	}
	
	public int getCapacity() {
		return array.getCapacity();
	}
	@Override
	public void push(E e) {
		try {
			array.addLast(e);
		} catch (Exception e1) {
			e1.printStackTrace();
		}
	}
	@Override
	public E pop() {
		return array.removeLast();
	}
	@Override
	public E peek() {
		return array.getLast();
	}
	public E getLast() {
		return array.get(array.getSize()-1);
	}
	public E getFirst() {
		return array.get(0);
	}
	@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();
	}
}

二、栈的应用
leetCode试题:有效的括号

思路:对于给定的字符串,首先声明一个栈,然后逐一遍历这个字符串的每一个字符。如果这个字符是一个左括号,不管是大括号、中括号还是小括号,只要是左括号我们就将他压入栈。当匹配到右括号的时候,我们看此时的栈顶元素是否能与该右括号匹配。如果能够匹配,则匹配成功,此时将栈顶的元素出栈。以此类推,如果整个字符串都已经遍历完,与此同时栈还为空的(栈里没有剩下其他的左括号),那么说明当前的字符串是一个合法的字符串匹配的字符串。一旦栈顶元素与当前遍历到的字符串元素不同,那么说明不匹配,则终止算法。
在这里插入图片描述

  • 代码
// 利用java自带的Stack求解
import java.util.Stack;
class Solution {
    public boolean isValid(String s) {
    	Stack<Character> stack = new Stack<Character>();
    	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;
    			}
    			char topChar = stack.pop();
    			if(c==')' && topChar!='(') {
    				return false;
    			} 
    			if(c==']' && topChar!='[') {
    				return false;
    			}
    			if(c=='}' && topChar!='{') {
    				return false;
    			}
    		}
    	}
    	return stack.isEmpty();	//注意:这里不能直接return true,必须先判定栈是否为空
    }
}

##python,摘自leetcode
class Solution:
    def isValid(self, s):
        while '{}' in s or '()' in s or '[]' in s:
            s = s.replace('{}', '')
            s = s.replace('[]', '')
            s = s.replace('()', '')
        return s == ''

三、队列Queue

  • 队列:队列是一种先进先出的数据结构(FIFO)
    在这里插入图片描述
  • 队列的实现:为了让逻辑更为清晰,采用接口实现。在接口中实现这5种方法
  1. 入列
  2. 出列
  3. 队首元素
  4. 队列元素
  5. 队列是否为空
    在这里插入图片描述
  • 接口代码实现
package cn.itcast.day2;

public interface Queue<E>{
	int getSize();
	boolean isEmpty();
	void enqueue(E e);
	E dequeue();
	E getFront();
}
  • 队列的实现
package cn.itcast.day2;

public class ArrayQueue<E> implements Queue<E> {
	
	private myArray<E> array;
	public ArrayQueue(int capacity) {
		array = new myArray<E>(capacity);
	}
	public ArrayQueue() {
		array = new myArray<E>();
	}
	@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) {
		try {
			array.addLast(e);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	@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();
	}
	public static void main(String[] args) {
		ArrayQueue<Integer> queue = new ArrayQueue<Integer>();
		for(int i=0;i<10;i++) {
			queue.enqueue(i);
			System.out.println(queue);
			
			if(i % 3==2) {
				queue.dequeue();
				System.out.println(queue);
			}
		}
	}
}

四、队列的改进:循环队列
由于原始队列的出对操作时均摊复杂度为O(n),因此为了降低复杂度,可以采用记录队首和队尾的方式来进行操作。
在这里插入图片描述
初始情况:front和tail都指向第一个元素(front==tail 队列为空)。入队操作如下:
在这里插入图片描述
当tail达到队尾时:
在这里插入图片描述
注意:此时索引为1的地方还为空,那么能不能继续再添加一个元素呢?答案是否定的,因为如果此时再添加一个元素,那么有front == tail。则此时 front == tail既可以表示队列为空,又可以表示队列为满,这种情况是我们不希望看到的。因此我们定义:(tail + 1) % c == front 时,队列已满(c为循环队列的长度)
在这里插入图片描述

  • 代码实现
package cn.itcast.day2;

public class LoopQueue<E> implements Queue<E>{
	private E[] data;
	private int front,tail;
	private int size;
	
	public LoopQueue(int capacity) {
		data = (E[])new Object[capacity + 1]; //循环数组要浪费一个单位
		front = 0;
		tail = 0;
		size = 0;
	}
	public LoopQueue() {
		this(10);
	}
	
	public int getCapacity() {
		return data.length-1; //循环数组要浪费一个单位
	}
	@Override
	public boolean isEmpty() {
		return front==tail;
	}
	@Override
	public int getSize() {
		return size;
	}
	@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[] newData = (E[]) new Object[newCapacity+1];//循环数组要浪费一个单位
		for(int i=0;i<size;i++) {
			/*在原来的数组data中,有可能front不是0,我们把队首元素放在新数组中索引为0的位置。这样比较方便
			 但是这样存在一个问题:newData[i]对应的不是data[i],而是data[(i+front)]。(有一个大小为front的偏移)
			 但是由于队列是循环队列,因此(i+front)有可能大于data.length,会产生越界,因此同样要取余
			 */
			newData[i] = data[(i+front)%data.length]; //
		}
		data = newData;
		front=0;
		tail=size; //维护队首和队尾
	}
	
	@Override
	public E dequeue() {
		if(isEmpty()) {
			throw new IllegalArgumentException("错误");
		}
		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("错误");
		}
		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(", ");
			}else {
				res.append("] tail");
			}
		}
		return res.toString();
	}
	public static void main(String[] args) {
		LoopQueue<Integer> queue = new LoopQueue<Integer>();
		for(int i=0;i<10;i++) {
			queue.enqueue(i);
			System.out.println(queue);
			
			if(i % 3==2) {
				queue.dequeue();
				System.out.println(queue);
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值