队列和栈简析

1. 数据结构简介

1.1 队列

在这里插入图片描述

一句话来说便是“先入先出的数据结构”,队列是典型的 FIFO 数据结构,插入(insert)操作也称作入队(enqueue),新元素始终被添加在队列的末尾,删除(delete)操作也被称为出队(dequeue),你只能移除第一个元素

广度优先搜索算法(BFS)

广度优先搜索(BFS)的一个常见应用是找出从根结点到目标结点的最短路径,越是接近根结点的结点将越早地遍历,如果在第k轮中将结点 x 添加到队列中,则根结点与 x 之间的最短路径的长度恰好是 k ,也就是说,第一次找到目标结点时,你已经处于最短路径中

  • 模板
public int BFS(Node root, Node target) {
    Queue<Node> queue = new LinkedList<>();  // 存储所有待处理的元素
    Set<Node> used = new HashSet<>();     // 存储所有已经处理的元素
    int step = 0;       // 从头节点到目标节点的步数
    // 初始化
    queue.add(root);
    used.add(root);
    // 广度优先搜索主体
    while (!queue.isEmpty()) {
        step = step + 1;
        int size = queue.size();
        for (int i = 0; i < size; ++i) {
            Node cur = queue.element();
            if(cur == target){
                return step;
            }
            for (Node n : cur.next) {
                if (!used.contains(n)) {
                    queue.add(n);
    				used.add(n);
                }
            }
            queue.remove();
        }
    }
    return -1;          // 自始至终没找到返回-1
}
1.2 栈

在这里插入图片描述

一句话来说便是“后入先出的数据结构”,与队列不同,栈是一个 LIFO 数据结构。通常,插入操作在栈中被称作入栈 push ,与队列类似,总是在堆栈的末尾添加一个新元素,但是,删除操作,退栈 pop ,将始终删除队列中相对于它的最后一个元素

深度优先搜索算法(DFS)

深度优先搜索算法会从头节点开始一直“向下”遍历,在我们到达最深的结点之后,仅会回溯并尝试另一条路径(回溯时将从栈中弹出最深的结点,即推入到栈中的最后一个结点)

注意事项

  1. 与 BFS 不同,更早访问的结点可能不是更靠近根节点的节点
  2. 使用 DFS 进行递归时,没有显式创建 Stack ,但实际上,我们使用的是系统提供的隐式栈,称为 Call Stack
  3. 在大多数情况下,我们在能使用 BFS 时也可以使用 DFS
  • 模板一:隐式栈
boolean DFS(Node cur, Node target, Set<Node> visited) {
    if(target.equals(cur)){
        return true;
    }
    for (n : cur.next) {
        if (!visited.contains(cur)) {
            visited.add(n);
            return true if DFS(n, target, visited) == true;
        }
    }
    return false;
}
  • 模板二:显式栈
boolean DFS(int root, int target) {
    Set<Node> visited;
    Stack<Node> s;
    s.add(root);
    while (!s.isEmpty()) {
        Node cur = s.peek()
        if(target.equals(cur)){
        	return true;
    	}
        for (Node n : cur.next)
            if (!visited.contains(cur)) {
                s.add(n);
                visited.add(cur);
            }
        }
        s.remove();
    }
    return false;
}

2.队列和栈在Java中的实现

2.1 队列 Queue<E>

简介

位于 java.util.Queue 包中,与 Stack 不同,此处的 Queue 仅仅是一个接口(与 ListSet 同一级别),其常用的实现类有:

  1. 没有实现阻塞接口的
    • LinkedList
    • PriorityQueue
    • ConcurrentLinkedQueue
  2. 实现了阻塞接口的
    • ArrayBlockingQueue 一个由数组支持的有界队列
    • LinkedBlockingQueue 一个由链接节点支持的可选有界队列
    • PriorityBlockingQueue 一个由优先级堆支持的无界优先级队列
    • DelayQueue 一个由优先级堆支持的、基于时间的调度队列
    • SynchronousQueue 一个利用 BlockingQueue 接口的简单聚集机制

常用方法(Interface

  • public boolean add(E e) 增加一个元索
  • public boolean offer(E e) 添加一个元素并返回 true
  • public E remove() 移除并返回队列头部的元素
  • public E poll() 移除并返问队列头部的元素
  • public E element() 返回队列头部的元素
  • public E peek() 返回队列头部的元素
2.2 栈 Stack<E>

简介

位于 java.util.Stack 包中,是 Vector 的子类,故其大部分功能在 Vector 中已经实现,是一种对 Vector 的拓展

常用方法

  • public E push(E item) 把项压入堆栈顶部
  • public synchronized E pop() 移除堆栈顶部的对象,并作为此函数的值返回该对象
  • public synchronized E peek() 查看堆栈顶部的对象,但不从堆栈中移除它
  • public boolean empty() 检测堆栈是否为空
  • public synchronized int search(Object o) 返回对象在堆栈中的位置,1为起始位置

3. 例题剖析

题目:【墙与门】 https://leetcode-cn.com/problems/walls-and-gates/

解析: 由于需要房间到门的最短路径,我们可以使用 BFS 算法,每次向所选单位的四周移动,同时房间数量大于门的数量,如果以每个房间为单位进行查找,耗时较长,故此处可以以门为单位找房间,每个门都寻找一次所有的房间,将其值与当前门作对比并更新最小值,注意,若此处遇到障碍物不能继续走下去,下面的例子使用的是队列的方式,当然使用 DFS 的递归思路也可

class Solution {
    class location {
        int i;
        int j;

        public location(int i, int j) {
            this.i = i;
            this.j = j;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            location location = (location) o;
            return i == location.i && j == location.j;
        }

        @Override
        public int hashCode() {
            return Objects.hash(i, j);
        }
    }

    int INF = Integer.MAX_VALUE;
    int lenx;
    int leny;

    public void wallsAndGates(int[][] rooms) {
        leny = rooms.length;
        lenx = rooms[0].length;
        for (int i = 0; i < leny; i++) {
            for (int j = 0; j < lenx; j++) {
                if (rooms[i][j] == 0) {
                    BFS(rooms, i, j);
                }
            }
        }
    }

    public void BFS(int[][] rooms, int x, int y) {
        Queue<location> queue = new LinkedList<>();
        Set<location> set = new HashSet<>();
        int step = -1;
        location l0 = new location(x, y);
        queue.add(l0);
        while (!queue.isEmpty()) {
            step++;
            int size = queue.size();
            for (int k = 0; k < size; k++) {
                location a = queue.element();
                if (a != l0) {
                    if (rooms[a.i][a.j] == 0 || rooms[a.i][a.j] == -1) {
                        queue.remove();
                        continue;
                    } else {
                        rooms[a.i][a.j] = Math.min(step, rooms[a.i][a.j]);
                    }
                }
                // 此处可以化简为rooms[i][j] > 0
                if ((a.i - 1 > -1) && (rooms[a.i - 1][a.j] != -1) && (rooms[a.i - 1][a.j] != 0)) {
                    location l = new location(a.i - 1, a.j);
                    if (!set.contains(l)) {
                        queue.add(l);
                        set.add(l);
                    }
                }
                if ((a.j - 1 > -1) && (rooms[a.i][a.j - 1] != -1) && (rooms[a.i][a.j - 1] != 0)) {
                    location l = new location(a.i, a.j - 1);
                    if (!set.contains(l)) {
                        queue.add(l);
                        set.add(l);
                    }
                }
                if ((a.i + 1 < leny) && (rooms[a.i + 1][a.j] != -1) && (rooms[a.i + 1][a.j] != 0)) {
                    location l = new location(a.i + 1, a.j);
                    if (!set.contains(l)) {
                        queue.add(l);
                        set.add(l);
                    }
                }
                if ((a.j + 1 < lenx) && (rooms[a.i][a.j + 1] != -1) && rooms[a.i][a.j + 1] != 0) {
                    location l = new location(a.i, a.j + 1);
                    if (!set.contains(l)) {
                        queue.add(l);
                        set.add(l);
                    }
                }
                queue.remove();
            }
        }
    }
}

题目:【目标和】 https://leetcode-cn.com/problems/target-sum/

解析: 此题是经典的 DFS 算法题,思路较为简单,这里就不作剖析了

class Solution {
    private int a = 0;
    public int findTargetSumWays(int[] nums, int target) {
        DFS(nums, 0, 0, target);
        return a;
    }
    public void DFS(int[] nums, int i, int sum, int target) {
        if(i==nums.length) {
            if(sum==target){
                a++;
            }
            return;
        }
        DFS(nums, i + 1, sum + nums[i], target);
        DFS(nums, i + 1, sum - nums[i], target);
    }
}

本文图片来自:https://leetcode-cn.com/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值