美文网首页算法
算法05-堆和二叉堆、图

算法05-堆和二叉堆、图

作者: 一亩三分甜 | 来源:发表于2021-02-21 16:46 被阅读0次

    《算法练习-文章汇总》

    堆:Heap

    可以迅速找到一堆数中最大或者最小值的数据结构
    将根节点最大的堆叫做大顶堆或大根堆,根节点最小的堆叫做小顶堆或小根堆
    常见的堆有二叉堆或斐波那契堆

    假设是大顶堆,则常见操作(API):
    find-max:O(1)
    delete-max:O(logN)
    insert(create):O(logN)或O(1)

    二叉堆实现细节

    1.二叉堆一般都通过"数组"来实现
    2.假设"第一个元素"在数组中的索引为0的话,则父节点和子节点的关系如下:

    I.索引为i的左孩子的索引是2i+1
    II.索引为i的有孩子的索引为2
    i+2
    III.索引为i的父节点的索引是floor((i-1)/2)

    insert插入操作

    1.新元素一律先插入到堆的尾部
    2.依次向上调整整个堆的结构(一直到根即可)

    Delete Max删除堆顶操作

    1.将堆尾元素替换到顶部(即堆顶被替代删除掉)
    2.依次从根部向下调整整个堆的结构(一直到堆尾即可)

    二叉堆是堆的一种常见且简单的实现;但是并不是最优的实现。

    最小的k个数(字节)
    输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

    class Solution {
        public int[] getLeastNumbers(int[] arr, int k) {
            PriorityQueue<Integer> heap = new PriorityQueue();
            for(int i =0;i<arr.length;i++){
                heap.add(arr[i]);
            }
            int[] list = new int[k];
            for (int j=0;j<k;j++){
                list[j] = heap.poll();
            }
            return list;
        }
    }
    

    滑动窗口最大值(亚马逊)
    给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

    返回滑动窗口中的最大值。

    示例 1:

    输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
    输出:[3,3,5,5,6,7]
    解释:
    滑动窗口的位置 最大值


    [1 3 -1] -3 5 3 6 7 3
    1 [3 -1 -3] 5 3 6 7 3
    1 3 [-1 -3 5] 3 6 7 5
    1 3 -1 [-3 5 3] 6 7 5
    1 3 -1 -3 [5 3 6] 7 6
    1 3 -1 -3 5 [3 6 7] 7
    示例 2:

    输入:nums = [1], k = 1
    输出:[1]

    暴力法

    class Solution {
        public int[] maxSlidingWindow(int[] nums, int k) {
            int n = nums.length;
            if(n*k == 0) return new int[0];
            int[] output = new int[n - k +1];
            for(int i = 0;i< n - k + 1;i++){
                int maxValue = Integer.MIN_VALUE;
                for(int j = i; j < k+i;j++){
                    maxValue = Math.max(maxValue,nums[j]);
                }
                 output[i] = maxValue;
            }
            return output;
        }
    }
    

    大顶堆法

       @Test
        public int[] maxSlidingWindow(int[] nums, int k) {
    
            //若数组和窗口大小为空,直接返回空数组
            if (nums.length == 0 || k == 0)
                return new int[0];
    
            int n = nums.length;
    
            int[] result = new int[n-k+1]; //window的大小
    
            PriorityQueue<Integer> maxPQ = new PriorityQueue<>((o1, o2) -> (o2-o1));//大顶堆
    
            for (int i=0;i<n;i++){
    
                int start =  i - k;
    
                if (start>=0){//如果有元素漂移到窗口外了,移出这个元素
                    maxPQ.remove(nums[start]);
                }
    
                maxPQ.offer(nums[i]);
    
                if (maxPQ.size() == k) {//窗口为k已经满员了,取一次值
                    result[i - k + 1] = maxPQ.peek();//取大顶堆最顶部的值放入result中
                }
            }
            return result;
        }
    }
    

    前 K 个高频元素
    给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
    示例 1:
    输入: nums = [1,1,1,2,2,3], k = 2
    输出: [1,2]
    示例 2:
    输入: nums = [1], k = 1
    输出: [1]

        public int[] topKFrequent(int[] nums, int k) {
            Map<Integer,Integer> map = new HashMap<>();
            for (int n:nums) {//数组中每个元素出现的次数,n作为key 次数作为value
                map.put(n,map.getOrDefault(n,0)+1);
            }
            //大顶堆
            PriorityQueue<Map.Entry<Integer,Integer>> maxPQ = new PriorityQueue<>((a,b)->(b.getValue()-a.getValue()));
            for (Map.Entry<Integer,Integer> entry:map.entrySet()) {
                maxPQ.add(entry);//将出现次数从大到小的排列组合map,放入大顶堆中
            }
            List<Integer> list = new ArrayList<>();
            while (list.size()<k){//一次取出大顶堆中的前k个Map,Map中的key也就是元素放入到list中
                Map.Entry<Integer,Integer> ent = maxPQ.poll();
                list.add(ent.getKey());
            }
            int[] res = list.stream().mapToInt(Integer::valueOf).toArray();//list转int[]
            return res;
        }
    }
    

    丑数(字节)
    我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
    示例:
    输入: n = 10
    输出: 12
    解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

        public int nthUglyNumber(int n) {
            int[] uglyNumbers = {2,3,5};//丑数都是2或3或5相乘的结果
            Set<Long> set = new HashSet<>();
            PriorityQueue<Long> queue = new PriorityQueue<>();//小根堆
            queue.add(1L);
            int count = 0;
            while (!queue.isEmpty()){
                Long cue = (Long) queue.poll();//取出小根堆顶部最小值
    
                if (++count>=n){//记录取出的次数,到第n个数时即第n个丑数
                    return cue.intValue();
                }
                for (int num : uglyNumbers) {
                    if (!set.contains(num*cue)){//在集合中过滤重复的丑数
                        queue.add(num*cue);
                        set.add(num*cue);
                    }
                }
            }
            return -1;
        }
    

    图的属性和分类

    图的属性

    Graph(V,E)
    V-vertex:点
    1.度-入度和出度
    2.点和点之间:连通与否
    E-edge:边
    1.有向和无向(单行线)
    2.权重(边长)

    图的表示和分类

    邻接矩阵和邻接表
    无向无权图
    邻接矩阵
    0:表示两点之间没有直接的边相连
    1:表示两点之间有直接的边相连
    有向无权图
    边有方向
    无权:所有的边不是0即是1
    无向有权图
    边没有方向,矩阵对称,边的长度不再是0和1了,有边长
    有向有权图
    有方向,右边长

    DFS和BFS

    visited = set()
    树可以保证没有环路,图要写visited,有环路避免重复
    连通图个数: https://leetcode-cn.com/problems/number-of-islands/
    给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

    岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

    此外,你可以假设该网格的四条边均被水包围。

    示例 1:

    输入:grid = [
    ["1","1","1","1","0"],
    ["1","1","0","1","0"],
    ["1","1","0","0","0"],
    ["0","0","0","0","0"]
    ]
    输出:1
    示例 2:

    输入:grid = [
    ["1","1","0","0","0"],
    ["1","1","0","0","0"],
    ["0","0","1","0","0"],
    ["0","0","0","1","1"]
    ]
    输出:3

        void dfs(char[][] grid, int r, int c) {
            int nr = grid.length;//获取数组的行数
            int nc = grid[0].length;//获取数组的列数
    
            if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') {
                return;
            }
    
            grid[r][c] = '0';//当前格的值置为0
            //遍历上下左右四个
            dfs(grid, r - 1, c);
            dfs(grid, r + 1, c);
            dfs(grid, r, c - 1);
            dfs(grid, r, c + 1);
        }
    
        public int numIslands(char[][] grid) {
            if (grid == null || grid.length == 0) {
                return 0;
            }
            //获取数组的行数和列数
            int nr = grid.length;
            int nc = grid[0].length;
            int num_islands = 0;
            //遍历数组中每一个元素
            for (int r = 0; r < nr; ++r) {
                for (int c = 0; c < nc; ++c) {
                    if (grid[r][c] == '1') {
                        ++num_islands;//若出现元素值为1,则岛屿数量加1
                        dfs(grid, r, c);//使用深度优先遍历,将此岛屿所有的元素变为0
                    }
                }
            }
            return num_islands;
        }
    

    拓扑排序(Topological Sorting): https://zhuanlan.zhihu.com/p/34871092
    最短路径(Shortest Path):Dijkstra https://www.bilibili.com/video/av25829980?from=search&seid=13391343514095937158
    最小生成树(Minimum Spanning Tree): https://www.bilibili.com/video/av84820276?from=search&seid=17476598104352152051

    相关文章

      网友评论

        本文标题:算法05-堆和二叉堆、图

        本文链接:https://www.haomeiwen.com/subject/sduqfltx.html