图的深度优先遍历和广度优先遍历

图的表示

邻接矩阵

利用两个数组来表示图: 一维数组存储图中顶点信息,二维数组(邻接矩阵)存储图中的边或弧的信息

  •  无向图的边数组是一个对称矩阵,有n个顶点的话,则邻接矩阵就是一个n*n的方针
  • 某个顶点的度,其实就是这个顶点v_i在邻接矩阵中第i行或(第i列)的元素之和;
  • 顶点v_i的所有邻接点就是将矩阵中第行元素扫描i一遍,arc[i][j]为1就是邻接点;
  • 有向图讲究入度和出度,顶点v_i的的入度为1,正好是第i列各数之和。顶点v_i的出度为2,即第i行的各数之和

邻接表

  • 邻接矩阵是不错的一种图存储结构,但是,对于边数相对顶点较少的图,这种结构存在对存储空间的极大浪费
  • 找到一种数组与链表相结合的存储方法称为邻接表。
  • 图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
  • 图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点的边表,有向图则称为顶点v_i作为弧尾的出边表

  • 顶点表的各个节点由data和firstedge两个节点域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点
  •  边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针

二者区别

  • 对于一个具有n个顶点的e条边的无向图, 它的邻接表表示有n个顶点表结点2e个边表结点
  • 对于一个具有n个顶点e条边的有向图, 它的邻接表表示有n个顶点表结点e个边表结点
  • 如果图中边的数目远远小于n^2称作稀疏图,这是用邻接表表示比用邻接矩阵表示节省空间;
  • 如果图中边的数目接近于n^2,对于无向图接近于n*(n-1)称作稠密图,考虑到邻接表中要附加链域,采用邻接矩阵表示法为宜。

 

BFS遍历

算法过程

  • 首先将跟节点放入队列中
  • 从对列中取出第一个节点,进行访问,并将其所有未访问过的邻居加入队列中
  • 如果队列为空,则算法结束

时间复杂度:不确定!

  • |V|代表节点的个数, |E|代表边的数量
  • 邻接表表示时,查找所有顶点的邻接点所需时间为O(|E|), 访问顶点的邻接点所花时间为O(|V|),总的时间复杂度为O(|V| + O|E|)
  • 邻接矩阵表示时,查找每个顶点的邻接点所需时间为O(|V|), 要查找整个矩阵,故总的时间复杂度为(O(|V|^2))

完全平方数

解题思路

1. 递归

class Solution {
    public int numSquares(int n) {
        if(n <= 2){
            return n;
        }
        int[] dp = new int[n+1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        for(int i = 1; i <= n; i++){
            for(int j = 1; i - j * j >= 0; j++){
                dp[i] = Math.min(dp[i], dp[i - j *j] + 1);
            }
        }
        return dp[n];
    }
}

2. 利用DFS

对问题建模

  • 整个问题转化为一个图论问题
  • 从n到0,每个数字表示一个节点
  • 如果两个数字x到y相差一个完全平方数,则连接一条边
  • 可以得到一个无权图
  • 原问题转化成,求这个无权图中从n到0的最短路径

比如

图的深度优先遍历和广度优先遍历_第1张图片

class Solution {
    class Node{
        public Node(int val, int passed){
            this.val = val;
            this.passed = passed;
        }
        private int val;
        private int passed;
    }
    public int numSquares(int n) {
        if(n <= 2){
            return n;
        }
        Node node = new Node(n, 0);
        Queue queue = new ArrayDeque<>();
        queue.offer(node);
        while(!queue.isEmpty()){
            Node tmp = queue.poll();
            if(tmp.val == 0){
                return tmp.passed;
            }
            for(int i = 1; tmp.val - i * i >= 0; i ++){
                queue.offer(new Node(tmp.val - i * i, tmp.passed + 1));
            }
        }
        return n;
    }
}

DFS遍历

非递归

  • 1. 首先从根节点放入stack中
  • 2. 从stack中取出第一个节点进行访问,将它某一个未访问过的邻居加入stack中。
  • 3. 重复步骤2
  • 4. 如果不存在未访问过的节点,将上一级节点加入stack中,重复步骤2
  • 5. 若stack为空,表示遍历结束

递归

  • 递归终止条件: 所有邻居都被访问过或者找到需要寻找的节点
  • 递归过程:对当前节点的所有未访问过的邻居进行递归。

岛屿的最大面积

  • 图中的每块陆地(标志为1的地方) 都可以当作一个节点
  • 两个1上下或者所有相连视为一条边
  • 那么二维数组可以建模成很多个图
  • 对二维数组进行遍历,如果是1, 则执行图的基础遍历算法DFS(或者BFS),并记录访问标志
  • 注意:在DFS或者BFS中都要注意,当一个子图遍历完后检查是否还有未访问过节点,并随机选择未访问节点进行遍历
class Solution {
    private int area = 0;
    public int maxAreaOfIsland(int[][] grid) {
        if(grid == null || grid.length == 0){
            return 0;
        }
        int m = grid.length;
        int n = grid[0].length;
        boolean [][] visited = new boolean[m][n];
        int ans = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(!visited[i][j] && grid[i][j] == 1){
                    area = 0;
                    dfs(grid, visited, i, j);
                    ans = Math.max(ans, area);
                }
            }
        }
        return ans;
    }

    public void dfs(int[][] grid, boolean[][]visited, Integer x, int y){
        int m = grid.length;
        int n = grid[0].length;
        if(x < 0 || x >= m || y < 0 || y >= n){
            return;
        }
        if(visited[x][y] || grid[x][y] == 0){
            return;
        }
        area += 1;
        visited[x][y] = true;
        dfs(grid, visited, x - 1, y); 
        dfs(grid, visited, x + 1, y);
        dfs(grid, visited, x, y - 1);
        dfs(grid, visited, x, y + 1);
    }
}

 

 

你可能感兴趣的:(算法)