数据结构与算法 - 栈 队列

目录

 

栈stack 

栈的接口设计

动态数组实现栈

练习题

有效的括号

队列

队列的接口设计

简单实现一个queue

 练习 - 用栈实现队列

双端队列

双端队列接口设计

双端队列源码 

 循环队列 

入队列

出队列


栈stack 

  • 栈是一种特殊的线性表,只能在一端进行操作
  • 往栈中添加元素的操作,一般叫做push,入栈
  • 从栈中移除元素的操作,一般叫做pop,出栈
  • 后进先出的原则

                                       

栈的接口设计

int size(); // 元素的数量
boolean isEmpty(); // 是否为空
void push(E element); // 入栈
E pop(); // 出栈
E top(); // 获取栈顶元素
void clear(); // 清空

栈的内部实现是否可以直接利用以前学过的数据结构 ?

  • 动态数组、链表 

动态数组实现栈

public class Stack <E> {

	private List<E> list = new ArrayList<>();
	
	public void clear(){
		list.clear();
	}
	
	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);
	}
	
}

练习题

有效的括号

  • 解法1:
public boolean isValid(String s) {
	while(s.contains("{}") || s.contains("[]") || s.contains("()")){
		s.replace("{}", "");
		s.replace("[]", "");
		s.replace("()", "");
	}
	return s.isEmpty();
}
  • 解法2: 
public boolean isValid2(String s){
	Stack<Character> stack = new Stack<>();
	int len = s.length();
	for(int i = 0; i < len; i++){
		char c = s.charAt(i);
		if(c == '(' || c == '[' || c == '{'){ // 左括号
			stack.push(s.charAt(i)); // 左字符入栈
		}else{ // 右括号
			if(stack.isEmpty()) return false; // 没有左括号, 却匹配到了右括号,false
			
			char left = stack.pop();
			if(left == '(' && c != ')') return false; // 左右括号不匹配
			if(left == '[' && c != ']') return false; // 左右括号不匹配
			if(left == '{' && c != '}') return false; // 左右括号不匹配
		}
	} // 扫描完毕
	return stack.isEmpty();
}
  • 解法3: 
static HashMap<Character, Character> map = new HashMap<>();
static {
	map.put('(', ')');
	map.put('[', ']');
	map.put('{', '}');
}
public boolean isValid(String s){
	Stack<Character> stack = new Stack<>();
	int len = s.length();
	for(int i = 0; i < len; i++){
		char c = s.charAt(i); 
		if(map.containsKey(c)){ // 左括号,哈希表中有该键则入栈
			stack.push(c);
		}else{ // 右括号
			if(stack.isEmpty()) return false;
			char left = stack.pop();
			if(c != map.get(left)) return false;
		}
	}
	return stack.isEmpty();
}

 

队列

队列是一种特殊的线性表,只能在头尾两端进行操作;

  • 队尾:只能从队尾添加元素,入队
  • 对头:只能从对头移除元素,出队
  • 先进先出的原则,First In First Out,FIFO

队列的接口设计

int size(); // 元素的数量
boolean isEmpty(); // 是否为空
void clear(); // 清空
void enQueue(E element); // 入队
E deQueue(); // 出队
E front(); // 获取队列的头元素

队列的内部实现是否可以直接利用以前学过的数据结构?

  • 动态数组、链表
  • 优先使用双向链表,因为队列主要是往头尾操作元素 

简单实现一个queue

import java.util.LinkedList;
import java.util.List;

public class Queue<E> {

    private List<E> linkedList= new LinkedList<>();

    public int size() {
        return linkedList.size();
    }

    public boolean isEmpty() {
        return linkedList.isEmpty();
    }

    // 入队列
    public void enQueue(E element){
        linkedList.add(element);
    }

    // 出队列
    public E deQueue(){
        return linkedList.remove(0);
    }

    // 获取队列的头元素
    public E front(){
        return linkedList.get(0);
    }


    public static void main(String[] args) {
        Queue<Integer> queue = new Queue();
        queue.enQueue(1);
        queue.enQueue(2);
        queue.enQueue(3);
        queue.enQueue(4);
        queue.enQueue(5);

        while (!queue.isEmpty()){
            System.out.println("Queue.main:"+queue.deQueue());
        }
    }
}

 练习 - 用栈实现队列

  • 准备2个栈:instack、outstack
  • 入队时,push到instack
  • 出队时: 如果outstack 为空 ,将instack所有元素逐一弹出,push到outstack,outstack弹出栈顶元素

                      如果outstack不为空,outstack弹出栈顶元素

        

 



/**
 * 使用栈实现队列
 */
public class QueueStack {

    private java.util.Stack<Integer> integerStack = new java.util.Stack<>();

    private java.util.Stack<Integer> outStack = new java.util.Stack<>();

    /**
     * 入队
     *
     * @param x
     */
    public void push(int x) {
        integerStack.push(x);
    }

    /**
     * 出队
     *
     * @return
     */
    public int pop() {
        if (outStack.empty()) {
            while (!integerStack.empty()) {
                outStack.push(integerStack.pop());
            }
        }
        return outStack.pop();
    }

    /**
     * 获取队头元素
     *
     * @return
     */
    public int peek() {
        if (outStack.empty()) {
            while (!integerStack.empty()) {
                outStack.push(integerStack.pop());
            }
        }
        return outStack.peek();
    }

    /**
     * 是否为空
     *
     * @return
     */
    public boolean empty() {
        return integerStack.isEmpty() && outStack.isEmpty();
    }

    public static void main(String[] args) {

    }
}

双端队列

双端队列是能在头尾两端添加、删除的队列;

  • 英文 deque 是 double ended queue 的简称;

双端队列接口设计

int size(); // 元素的数量
boolean isEmpty(); // 是否为空
void clear(); // 清空
void enQueueRear(E element); // 从队尾入队
E deQueueFront(); // 从队头出队
void enQueueFront(E element); // 从队头入队
E deQueueRear(); // 从队尾出队
E front(); // 获取队列的头元素
E rear(); // 获取队列的尾元素

双端队列源码 

import java.util.LinkedList;
import java.util.List;

/**
 * 双端队列
 */
public class Deque<E> {

    private List<E> list = new LinkedList<E>();

    // 入队列 队尾
    public void enQueueRear(E element) {
        list.add(element);
    }

    // 入队列 队头
    public void enQueueFront(E element) {
        list.add(0, element);
    }

    // 出队列 队头
    public E deQueueFront() {
        return list.remove(0);
    }

    // 出队列 队尾
    public E deQueueRear() {
        return list.remove(list.size() - 1);
    }

    public boolean isEmpty() {
        return list.isEmpty();
    }

    // 获取队列的头元素
    public E front() {
        return list.get(0);
    }

    // 获取队列的头元素
    public E rear() {
        return list.get(list.size() - 1);
    }

    public static void main(String[] args) {
        Deque<Integer> deque = new Deque<>();
        deque.enQueueFront(1);
        deque.enQueueFront(2);
        deque.enQueueRear(3);
        deque.enQueueRear(4);
        while (!deque.isEmpty()){
            System.out.println("Queue.main:"+deque.deQueueRear());
        }
    }
}

 

 循环队列 

 其实队列底层也可以使用动态数组实现,并且各项接口也可以优化到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];
    }

    public int size() {
        return size;
    }

    // 入队列
    public void enQueue(E element) {
        // 扩容
		ensureCapacity(size + 1);
        elements[(front + size) % elements.length] = element;
        size++;
    }


    // 出队列
    public E deQueue() {
        E frontElement = elements[front];
        elements[front] = null;
        front = (front + 1) & elements.length;
        size--;
        return frontElement;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    // 获取队列的头元素
    public E front() {
        return elements[front];
    }

    // 扩容
	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[(i + front) % elements.length];
			// newElements[i] = elements[index(i)];
		}
		System.out.println("从" + oldCapacity + "扩容到" + newCapacity);
		elements = newElements;
		front = 0; // 重置front
	}


    public static void main(String[] args) {

    }
}

入队列

  •    当我们往队列里添加元素时,通常往size位置添加元素; elements[size] = element;

                         

  •    但是我们发现循环队列 出队列时会把元素清除掉,队头位置后移,那么此时elements[size] = element; 就会覆盖44这个元素的值

                                  

  •   那么我们需要往队尾添加元素,其实就是  elements[front + size] = element; 但是如果此时 队尾添加元素已经到达数组最大长度,继续往队尾添加元素,那么将会数组越界。

                                    

  •   于是我们  elements[(front + size) % elements.length] = element; 对数组长度取余

                                   

出队列

  • 出队时,需要取出队头数据,并将front 前移,size减一 ;E frontElement = elements[front];front++;size--; 但是如果下面情况 front++ 为7

              

  • 那么我们将 front = (front + 1) % elements.length;

                             

循环双端队列

  • 可以进行两端添加、删除操作的循环队列

 

                                         

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值