LeetCode经典算法题:打家劫舍java详解

简介: LeetCode经典算法题:打家劫舍java详解

题目描述

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

输入:[1,2,3,1] 输出:4

输入:[2,7,9,3,1] 输出:12

解题思路与代码

    static int maxMoney(int[] nums,int index){
        if (nums == null || index < 0) {
            return 0;
        }
        if (index == 0) {
            return nums[0];
        }
        return Math.max(maxMoney(nums,index - 2) + nums[index], maxMoney(nums,index
                - 1));
    }
    static int maxMoney(int[] nums){
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int length = nums.length;
        if (length == 1) {
            return nums[0];
        }


        /*
        int[] dp = new int[length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < length; i++) {
            dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[length - 1];
        */

        int first = nums[0], second = Math.max(nums[0], nums[1]);
        for (int i = 2; i < length; i++) {
            int temp = second;
            second = Math.max(first + nums[i], second);
            first = temp;
        }
        return second;
    }
如果房子首尾相连:
    public int rob(int[] nums) {
        int length = nums.length;
        if (length == 1) {
            return nums[0];
        } else if (length == 2) {
            return Math.max(nums[0], nums[1]);
        }
        return Math.max(robRange(nums, 0, length - 2), robRange(nums, 1, length -
                1));
    }
    public int robRange(int[] nums, int start, int end) {
        int first = nums[start], second = Math.max(nums[start], nums[start + 1]);
        for (int i = start + 2; i <= end; i++) {
            int temp = second;
            second = Math.max(first + nums[i], second);
            first = temp;
        }
        return second;
    }
    public int rob(TreeNode root) {
        int[] rootStatus = dfs(root);
        return Math.max(rootStatus[0], rootStatus[1]);
    }
    public int[] dfs(TreeNode node) {
        if (node == null) {
            return new int[]{0, 0};
        }
        int[] l = dfs(node.left);
        int[] r = dfs(node.right);
        int selected = node.val + l[1] + r[1];
        int notSelected = Math.max(l[0], l[1]) + Math.max(r[0], r[1]);
        return new int[]{selected, notSelected};
    }

预测赢家

题目描述

给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。

每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。

解题思路与代码

给定一个表示分数的数组,预测玩家1是否会成为赢家。可以假设每个玩家的玩法都会使他的分数最大化。

两个值的时候必然是取较大的,三个值,取一个能使自己分数和最大的,后手必然留较小的给先手,因此先手选一个值加上该较小值最大化

 static int maxScore(int[] nums, int l, int r) {
        //剩下一个值,只能取该值
        if (l == r) {
            return nums[l];
        }
        int selectLeft =0, selectRight=nums.length-1;
        //剩下大于两个值,先手选一边(使自己得分最高的一边),后手则选使对手得分最低的一边
        if ((r - l) >= 2) {
            selectLeft = nums[l] +Math.min(maxScore(nums, l+2, r),maxScore(nums,
                    l+1, r-1));
            selectRight = nums[r] +Math.min(maxScore(nums, l+1, r-1),maxScore(nums,
                    l, r-2));
        }
        //剩下两个值,取较大的
        if ((r - l) == 1) {
            selectLeft = nums[l];
            selectRight = nums[r];
        }
        return Math.max(selectLeft, selectRight);
    }
    int getScore(int[] nums, int start, int end) {
        int selectLeft, selectRight;
        int gap = end - start;
        if (gap == 0) {
            return nums[start];
        } else if (gap == 1) { // 此时直接取左右的值就可以
            selectLeft = nums[start];
            selectRight = nums[end];
        } else if (gap >= 2) { // 如果gap大于2,递归计算selectLeft和selectRight
            // 计算的过程为什么用min,因为要按照对手也是最聪明的来计算。
            int num = getScore(nums, start + 1, end - 1);
            selectLeft = nums[start] + min(getScore(nums, start + 2, end), num);
            selectRight = nums[end] + min(num, getScore(nums, start, end - 2));
        }
        return max(selectLeft, selectRight);
    }
    bool PredictTheWinner(int[] nums) {
        int sum = 0;
        for (int i : nums) {
            sum += i;
            int player1 = getScore(nums, 0, nums.size() - 1);
            int player2 = sum - player1;
            // 如果最终两个玩家的分数相等,那么玩家 1 仍为赢家,所以是大于等于。
            return player1 >= player2;
            //return getScore(nums, 0, nums.size() - 1) >=0 ;
        }
        //差值
        int getScore(int[] nums, int start, int end) {
            if (end == start) {
                return nums[start];
            }
            int selectLeft = nums[start] - getScore(nums, start + 1, end);
            int selectRight = nums[end] - getScore(nums, start, end - 1);
            return max(selectLeft, selectRight);
        }      

动态规划:使用二维数组存储差值

    public boolean PredictTheWinner(int[] nums) {
        int length = nums.length;
        int[][] dp = new int[length][length];
        for (int i = 0; i < length; i++) {
            dp[i][i] = nums[i];
        }
        for (int i = length - 2; i >= 0; i--) {
            for (int j = i + 1; j < length; j++) {
                //j = i +1 因此可以优化为一维数组,下标位置相同才有值,据此推导其他的值
                //Math.max(nums[i] - dp[j][j], nums[j] - dp[j - 1][j - 1]);
                dp[i][j] = Math.max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);
            }
        }
        return dp[0][length - 1] >= 0;
    }

省份数量

题目描述

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

解题思路与代码

解法一:深度优先

获取一个城市,通过递归找到离该城市最远的城市,标记为已访问,然后逐个向内进行标记

  public int findCircleNum(int[][] isConnected) {
        int provinces = isConnected.length;
        boolean[] visited = new boolean[provinces];
        int circles = 0;
        for (int i = 0; i < provinces; i++) {
            if (!visited[i]) {
                dfs(isConnected, visited, provinces, i);
                circles++;
            }
        }
        return circles;
    }
    public void dfs(int[][] isConnected, boolean[] visited, int provinces, int i) {
        for (int j = 0; j < provinces; j++) {
            if (isConnected[i][j] == 1 && !visited[j]) {
                visited[j] = true;
                dfs(isConnected, visited, provinces, j);
            }
        }
    }

解法二:广度优先

获取一个城市,先标记与该城市直连的城市(最近的),然后逐步向外扩散寻找

    public int bfs(int[][] isConnected) {
        int provinces = isConnected.length;
        boolean[] visited = new boolean[provinces];
        int circles = 0;
        Queue<Integer> queue = new LinkedList<Integer>();
        for (int i = 0; i < provinces; i++) {
            if (!visited[i]) {
                queue.offer(i);
                while (!queue.isEmpty()) {
                    int j = queue.poll();
                    visited[j] = true;
                    for (int k = 0; k < provinces; k++) {
                        if (isConnected[j][k] == 1 && !visited[k]) {
                            queue.offer(k);
                        }
                    }
                }
                circles++;
            }
        }
        return circles;
    }


解法三:并查集

将每个城市看成一个节点,如果两个城市相连,则建立树关系,选出其中一个为head,如果两个树中的节点也相连,则将其中一个head设置为另一个树的head

两个方法 :一个寻找head节点,一个合并树

    static int mergeFind(int[][] isConnected){
        int provinces = isConnected.length;
        int[] head = new int[provinces];
        int[] level = new int[provinces];
        for (int i = 0; i < provinces; i++) {
            head[i] = i;
            level[i] = 1;
        }
        for (int i = 0; i < provinces; i++) {
            for (int j = i + 1; j < provinces; j++) {
                if (isConnected[i][j] == 1) {
                    merge(i, j,head,level);
                }
            }
        }
        int count = 0;
        //找出所有的head
        for (int i = 0; i < provinces; i++) {
            if (head[i] == i) {
                count++;
            }
        }
        return count;
    }
    //查找head节点
    static int find(int x, int[] arr) {
        if(arr[x] == x)
            return x;
        else
            arr[x] = find(arr[x],arr);//路径压缩,每一个节点直接能找到head
        return arr[x];
    }
    static void merge(int x, int y,int[] arr,int[] level) {
        int i = find(x,arr);
        int j = find(y,arr);
        //深度比较短的树的head往深度大的树上挂,使合并后的深度尽量小
        if(i == j){
            return;
        }
        if(level[i] <= level[j]){
            arr[i] = j;
        }else{
            arr[j] = i;
        }
        //深度加1
        level[j]++;
    }

三角形的最大周长

题目描述

给定由一些正数(代表长度)组成的数组 A ,返回由其中三个长度组成的、面积不为零的三角形的最大周长。

如果不能形成任何面积不为零的三角形,返回 0 。

解题思路与代码

贪心算法:

先小到大排序,假设最长边是最后下标,另外两条边是倒数第二和第三下标,则此时三角形周长最大

n < (n-1) + (n-2),如果不成立,意味着该数组中不可能有另外两个值之和大于n,此时将n左移,重新计算

 public int largestPerimeter(int[] A) {
        Arrays.sort(A);
        for (int i = A.length - 1; i >= 2; --i) {
            if (A[i - 2] + A[i - 1] > A[i]) {
                return A[i - 2] + A[i - 1] + A[i];
            }
        }
        return 0;
    }


目录
相关文章
|
8天前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
34 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
18天前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
36 0
|
2月前
|
负载均衡 NoSQL 算法
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
这篇文章是关于Java面试中Redis相关问题的笔记,包括Redis事务实现、集群方案、主从复制原理、CAP和BASE理论以及负载均衡算法和类型。
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
|
2月前
|
搜索推荐 算法 Java
手写快排:教你用Java写出高效排序算法!
快速排序(QuickSort)是经典的排序算法之一,基于分治思想,平均时间复杂度为O(n log n),广泛应用于各种场合。在这篇文章中,我们将手写一个Java版本的快速排序,从基础实现到优化策略,并逐步解析代码背后的逻辑。
110 1
|
10天前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
30 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
13天前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
33 2
|
10天前
|
算法 Java Linux
java制作海报一:java使用Graphics2D 在图片上写字,文字换行算法详解
这篇文章介绍了如何在Java中使用Graphics2D在图片上绘制文字,并实现自动换行的功能。
28 0
|
13天前
|
算法 Java
LeetCode(一)Java
LeetCode(一)Java
|
18天前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
12 0
|
2月前
|
算法
测试工程师的技能升级:LeetCode算法挑战与职业成长
这篇文章通过作者亲身体验LeetCode算法题的过程,探讨了测试工程师学习算法的重要性,并强调了算法技能对于测试职业成长的必要性。
57 1
测试工程师的技能升级:LeetCode算法挑战与职业成长