FIFO:first in first out 先进先出 ——> 队列 首先处理数据结构的第一个元素
LIFO: last in first out 后进先出 ——>栈 首先处理数据结构中的最后一个元素

一、队列

1.关于队列的一些要点:

  • 新元素总是添加在队列的末尾。
  • 移除元素总是移除队列的第一个元素。

    2.基本队列的实现示例及其缺点:

    1.用动态数组和指向头部的索引实现(Java):
    ```java // “static void main” must be defined in a public class.

class MyQueue { // store elements private List data;
// a pointer to indicate the start position private int p_start;
public MyQueue() { data = new ArrayList(); p_start = 0; } / Insert an element into the queue. Return true if the operation is successful. */ public boolean enQueue(int x) { data.add(x); return true; };
/
Delete an element from the queue. Return true if the operation is successful. / public boolean deQueue() { if (isEmpty() == true) { return false; } p_start++; return true; } /** Get the front item from the queue. / public int Front() { return data.get(p_start); } /* Checks whether the queue is empty or not. / public boolean isEmpty() { return p_start >= data.size(); }
};

public class Main { public static void main(String[] args) { MyQueue q = new MyQueue(); q.enQueue(5); q.enQueue(3); if (q.isEmpty() == false) { System.out.println(q.Front()); } q.deQueue(); if (q.isEmpty() == false) { System.out.println(q.Front()); } q.deQueue(); if (q.isEmpty() == false) { System.out.println(q.Front()); } } }

  1. <a name="yN66v"></a>
  2. ##### 2.缺点:
  3. 随着起始指针的移动,浪费了越来越多的空间。
  4. <a name="AQSVr"></a>
  5. ##### 3.解决方案:
  6. 循环队列。
  7. <a name="cv4Xu"></a>
  8. #### 3.循环队列的实现:
  9. <a name="A740B"></a>
  10. ##### 1.链表法实现:
  11. ```java
  12. class Node {
  13. public int value;
  14. public Node nextNode;
  15. public Node(int value) {
  16. this.value = value;
  17. this.nextNode = null;
  18. }
  19. }
  20. class MyCircularQueue {
  21. private Node head, tail;
  22. private int count;
  23. private int capacity;
  24. /** Initialize your data structure here. Set the size of the queue to be k. */
  25. public MyCircularQueue(int k) {
  26. this.capacity = k;
  27. }
  28. /** Insert an element into the circular queue. Return true if the operation is successful. */
  29. public boolean enQueue(int value) {
  30. if (this.count == this.capacity)
  31. return false;
  32. Node newNode = new Node(value);
  33. if (this.count == 0) {
  34. head = tail = newNode;
  35. } else {
  36. tail.nextNode = newNode;
  37. tail = newNode;
  38. }
  39. this.count += 1;
  40. return true;
  41. }
  42. /** Delete an element from the circular queue. Return true if the operation is successful. */
  43. public boolean deQueue() {
  44. if (this.count == 0)
  45. return false;
  46. this.head = this.head.nextNode;
  47. this.count -= 1;
  48. return true;
  49. }
  50. /** Get the front item from the queue. */
  51. public int Front() {
  52. if (this.count == 0)
  53. return -1;
  54. else
  55. return this.head.value;
  56. }
  57. /** Get the last item from the queue. */
  58. public int Rear() {
  59. if (this.count == 0)
  60. return -1;
  61. else
  62. return this.tail.value;
  63. }
  64. /** Checks whether the circular queue is empty or not. */
  65. public boolean isEmpty() {
  66. return (this.count == 0);
  67. }
  68. /** Checks whether the circular queue is full or not. */
  69. public boolean isFull() {
  70. return (this.count == this.capacity);
  71. }
  72. }

2.数组法实现:
  1. class MyCircularQueue {
  2. private int[] queue;
  3. private int headIndex;
  4. private int count;
  5. private int capacity;
  6. /** Initialize your data structure here. Set the size of the queue to be k. */
  7. public MyCircularQueue(int k) {
  8. this.capacity = k;
  9. this.queue = new int[k];
  10. this.headIndex = 0;
  11. this.count = 0;
  12. }
  13. /** Insert an element into the circular queue. Return true if the operation is successful. */
  14. public boolean enQueue(int value) {
  15. if (this.count == this.capacity)
  16. return false;
  17. this.queue[(this.headIndex + this.count) % this.capacity] = value;
  18. this.count += 1;
  19. return true;
  20. }
  21. /** Delete an element from the circular queue. Return true if the operation is successful. */
  22. public boolean deQueue() {
  23. if (this.count == 0)
  24. return false;
  25. this.headIndex = (this.headIndex + 1) % this.capacity;
  26. this.count -= 1;
  27. return true;
  28. }
  29. /** Get the front item from the queue. */
  30. public int Front() {
  31. if (this.count == 0)
  32. return -1;
  33. return this.queue[this.headIndex];
  34. }
  35. /** Get the last item from the queue. */
  36. public int Rear() {
  37. if (this.count == 0)
  38. return -1;
  39. int tailIndex = (this.headIndex + this.count - 1) % this.capacity;
  40. return this.queue[tailIndex];
  41. }
  42. /** Checks whether the circular queue is empty or not. */
  43. public boolean isEmpty() {
  44. return (this.count == 0);
  45. }
  46. /** Checks whether the circular queue is full or not. */
  47. public boolean isFull() {
  48. return (this.count == this.capacity);
  49. }
  50. }

4.Java内置库的队列:

1.初始化:
  1. Queue<Integer> queue=new LinkedList<>();
  2. Deque<Integer> deque=new LinkedList<>();

2.常用方法:
  1. queue.push(1);
  2. int i=queue.peek();//i=1
  3. int j=queue.poll();//j=1
  4. boolean b=queue.isEmpty();//b=true
  5. deque.addLast(1);
  6. int i1=deque.getFirst();//i1=1
  7. int j1=deque.removeFirst();//j1=1;
  8. boolean bo=deque.isEmpty();//bo=true

5.队列的高级应用:广度优先搜索(BFS)

1.BFS : Breadth First Search

2.BFS的解释:

首先搜索和s距离为k的所有顶点,然后再去搜索和S距离为k+l的其他顶点。

3.BFS的应用:

最短路径

4.BFS的优化:

广度搜索的判断重复如果直接判断十分耗时,我们一般借助哈希表来优化时间复杂度

5.BFS一般框架实现代码(伪代码):
  1. // 计算从起点 start 到终点 target 的最近距离
  2. int BFS(Node start, Node target) {
  3. Queue<Node> q; // 核心数据结构
  4. Set<Node> visited; // 避免走回头路
  5. q.offer(start); // 将起点加入队列
  6. visited.add(start);
  7. int step = 0; // 记录扩散的步数
  8. while (q not empty) {
  9. int sz = q.size();
  10. /* 将当前队列中的所有节点向四周扩散 */
  11. for (int i = 0; i < sz; i++) {
  12. Node cur = q.poll();
  13. /* 划重点:这里判断是否到达终点 */
  14. if (cur is target)
  15. return step;
  16. /* 将 cur 的相邻节点加入队列 */
  17. for (Node x : cur.adj())
  18. if (x not in visited) {
  19. q.offer(x);
  20. visited.add(x);
  21. }
  22. }
  23. /* 划重点:更新步数在这里 */
  24. step++;
  25. }
  26. }

6.经典题举例:

(1)leetcode279 完全平方数:
image.png

  1. class Solution {
  2. public int numSquares(int n) {
  3. Queue<Integer> queue = new LinkedList<>();
  4. Set<Integer> visited = new HashSet<>();
  5. queue.add(0);
  6. visited.add(0);
  7. int distance = 0;
  8. while (!queue.isEmpty()) {
  9. distance++;
  10. int size = queue.size();
  11. for (int i = 0; i < size; i++) {
  12. int curr = queue.poll();
  13. for (int j = 1; j * j + curr <= n; j++) {
  14. int next = j * j + curr;
  15. if (next == n) return distance;
  16. if (next < n && !visited.contains(next)) {
  17. queue.add(next);
  18. visited.add(next);
  19. }
  20. }
  21. }
  22. }
  23. return distance;
  24. }
  25. }

(2)leetcode200 岛屿数量:
image.png

  1. class Solution {
  2. public int numIslands(char[][] grid) {
  3. int count = 0;
  4. for(int i = 0; i < grid.length; i++) {
  5. for(int j = 0; j < grid[0].length; j++) {
  6. if(grid[i][j] == '1'){
  7. bfs(grid, i, j);
  8. count++;
  9. }
  10. }
  11. }
  12. return count;
  13. }
  14. private void bfs(char[][] grid, int i, int j){
  15. Queue<int[]> list = new LinkedList<>();
  16. list.add(new int[] { i, j });
  17. while(!list.isEmpty()){
  18. int[] cur = list.remove();
  19. i = cur[0]; j = cur[1];
  20. if(0 <= i && i < grid.length && 0 <= j && j < grid[0].length && grid[i][j] == '1') {
  21. grid[i][j] = '0';
  22. list.add(new int[] { i + 1, j });
  23. list.add(new int[] { i - 1, j });
  24. list.add(new int[] { i, j + 1 });
  25. list.add(new int[] { i, j - 1 });
  26. }
  27. }
  28. }
  29. }

6.BFS升级:双向BFS

思想:从起始点和终点同时开始搜索,相遇时停止。

二、栈

1.关于栈的一些要点:

  • 在栈这种数据结构中,我们首先处理的是最新元素。
  • 删除时首先删除最新元素。
  • 插入时也直接插到末尾。

    2.基本栈的实现示例:

    ```java // “static void main” must be defined in a public class. class MyStack { private List data; // store elements public MyStack() {
    1. data = new ArrayList<>();
    } /* Insert an element into the stack. / public void push(int x) {
    1. data.add(x);
    } /* Checks whether the queue is empty or not. / public boolean isEmpty() {
    1. return data.isEmpty();
    } /* Get the top item from the queue. / public int top() {
    1. return data.get(data.size() - 1);
    } /* Delete an element from the queue. Return true if the operation is successful. / public boolean pop() {
    1. if (isEmpty()) {
    2. return false;
    3. }
    4. data.remove(data.size() - 1);
    5. return true;
    } };

public class Main { public static void main(String[] args) { MyStack s = new MyStack(); s.push(1); s.push(2); s.push(3); for (int i = 0; i < 4; ++i) { if (!s.isEmpty()) { System.out.println(s.top()); } System.out.println(s.pop()); } } }

  1. <a name="at0fv"></a>
  2. #### 3.Java内置库的栈:
  3. <a name="NPi3o"></a>
  4. ##### 1.初始化:
  5. ```java
  6. Stack<Integer> stack1=new LinkedList<>();
  7. Deque<Integer> stack2=new LinkedList<>();

2.常用方法:
  1. stack1.push(1);
  2. stack2.addFirst(1);
  3. int i=stack1.peek();//i=1
  4. int j=stack2.getFirst();//j=1
  5. int k=stack1.poll();//k=1
  6. int l=stack2.removeFirst();//l=1
  7. stack1.peek();//null
  8. stack2.getFirst();//null

4.栈的高级应用:深度优先搜索(DFS)

1.DFS:Depth First Search

2.DFS解释:

(1)访问顶点v;
(2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。

3.DFS应用:

全排列,迷宫问题,n皇后问题,可行路径问题

4.DFS的缺点:

DFS找到的结果不一定是最优结果。

5.DFS一般框架实现代码(伪代码):

(递归方案)

  1. /*
  2. * Return true if there is a path from cur to target.
  3. */
  4. boolean DFS(Node cur, Node target, Set<Node> visited) {
  5. return true if cur is target;
  6. for (next : each neighbor of cur) {
  7. if (next is not in visited) {
  8. add next to visted;
  9. return true if DFS(next, target, visited) == true;
  10. }
  11. }
  12. return false;
  13. }

(显式栈方案)

  1. /*
  2. * Return true if there is a path from cur to target.
  3. */
  4. boolean DFS(int root, int target) {
  5. Set<Node> visited;
  6. Stack<Node> s;
  7. add root to s;
  8. while (s is not empty) {
  9. Node cur = the top element in s;
  10. return true if cur is target;
  11. for (Node next : the neighbors of cur) {
  12. if (next is not in visited) {
  13. add next to s;
  14. add next to visited;
  15. }
  16. }
  17. remove cur from s;
  18. }
  19. return false;
  20. }

6.经典题举例:

leetcode 200 岛屿数量:
image.png

  1. class Solution {
  2. public int numIslands(char[][] grid) {
  3. int count = 0;
  4. for(int i = 0; i < grid.length; i++) {
  5. for(int j = 0; j < grid[0].length; j++) {
  6. if(grid[i][j] == '1'){
  7. dfs(grid, i, j);
  8. count++;
  9. }
  10. }
  11. }
  12. return count;
  13. }
  14. private void dfs(char[][] grid, int i, int j){
  15. if(i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') return;
  16. grid[i][j] = '0';
  17. dfs(grid, i + 1, j);
  18. dfs(grid, i, j + 1);
  19. dfs(grid, i - 1, j);
  20. dfs(grid, i, j - 1);
  21. }
  22. }

leetcode 133 克隆图(图的深拷贝)
image.png

  1. /*
  2. // Definition for a Node.
  3. class Node {
  4. public int val;
  5. public List<Node> neighbors;
  6. public Node() {
  7. val = 0;
  8. neighbors = new ArrayList<Node>();
  9. }
  10. public Node(int _val) {
  11. val = _val;
  12. neighbors = new ArrayList<Node>();
  13. }
  14. public Node(int _val, ArrayList<Node> _neighbors) {
  15. val = _val;
  16. neighbors = _neighbors;
  17. }
  18. }
  19. */
  20. class Solution {
  21. public Node cloneGraph(Node node) {
  22. Map<Node, Node> lookup = new HashMap<>();
  23. return dfs(node, lookup);
  24. }
  25. private Node dfs(Node node, Map<Node,Node> lookup) {
  26. if (node == null) return null;
  27. if (lookup.containsKey(node)) return lookup.get(node);
  28. Node clone = new Node(node.val, new ArrayList<>());
  29. lookup.put(node, clone);
  30. for (Node n : node.neighbors)clone.neighbors.add(dfs(n,lookup));
  31. return clone;
  32. }
  33. }

leetcode 494 目标和:
image.png

  1. class Solution {
  2. int count = 0;
  3. public int findTargetSumWays(int[] nums, int S) {
  4. dfs(nums,0,0,S);
  5. return count;
  6. }
  7. public void dfs(int[] nums,int i,int sum,int S){
  8. if(i==nums.length){
  9. if(sum==S){
  10. count++;
  11. }
  12. }else {
  13. dfs(nums,i+1,sum+nums[i],S);
  14. dfs(nums,i+1,sum-nums[i],S);
  15. }
  16. }
  17. }

三、BFS模板

1.如果不需要确定当前遍历到了哪一层,BFS 模板如下

  1. while queue 不空:
  2. cur = queue.pop()
  3. for 节点 in cur的所有相邻节点:
  4. if 该节点有效且未访问过:
  5. queue.push(该节点)

2.如果需要确定,如下

这里增加了 level 表示当前遍历到二叉树中的哪一层了,也可以理解为在一个图中,现在已经走了多少步了。size 表示在当前遍历层有多少个元素,也就是队列中的元素数,我们把这些元素一次性遍历完,即把当前层的所有元素都向外走了一步。

  1. level = 0
  2. while queue 不空:
  3. size = queue.size()
  4. while (size --) {
  5. cur = queue.pop()
  6. for 节点 in cur的所有相邻节点:
  7. if 该节点有效且未被访问过:
  8. queue.push(该节点)
  9. }
  10. level ++;