LeetCode - 刷题记录(14)

这次练习的是LeetCode上标签为列表为热题Top100、难度为中等的几道题目。题解有官方题解也有个人题解,有的地方意思可能表达得不是很清楚也可能存在错误,有问题请提出,感谢❤

1.课程表

题目描述:

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]

给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?

示例 1:

输入: 2, [[1,0]]
输出: true

解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。

示例 2:

输入: 2, [[1,0],[0,1]]
输出: false

解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。

提示:

输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
1 <= numCourses <= 10^5

题解:

深度优先遍历,判断是否有环。

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        List<List<Integer>> adjacency = new ArrayList<>();
        for(int i = 0; i < numCourses; i++)
            adjacency.add(new ArrayList<>());
        int[] flags = new int[numCourses];
        for(int[] cp : prerequisites)
            adjacency.get(cp[1]).add(cp[0]);
        // 在这步之前的代码目的是将,题目提供的代表课程前后关系二维数组转换成集合,保存学习一门课后,可以学习的课程集合。(List<List<Integer>>)
        for(int i = 0; i < numCourses; i++)
            if(!dfs(adjacency, flags, i)) return false;
        return true;
    }
    // 用flags来判断路径有没有形成环,一旦有环证明此路径的课程不可能完成
    private boolean dfs(List<List<Integer>> adjacency, int[] flags, int i) {
    	// 遇到当前遍历中碰到的课程,证明有环
        if(flags[i] == 1) return false;
        // flag为-1,从当前结点开始的路径先前已经证实过无环,不必再遍历一遍
        if(flags[i] == -1) return true;
        flags[i] = 1;
        for(Integer j : adjacency.get(i))
            if(!dfs(adjacency, flags, j)) return false;
        // 无环的路径上的结点flag都置-1
        flags[i] = -1;
        return true;
    }
}

2.零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:

输入: coins = [2], amount = 3
输出: -1

说明:
你可以认为每种硬币的数量是无限的。

题解:
这道题一开始看题就感觉和刷题记录(13)中的“组合总和”差不多的套路,于是就采用回溯的方式的去做了。

class Solution {
    public int coinChange(int[] coins, int amount) {
        if(coins == null || coins.length <= 0) return -1;
        if(amount == 0) return 0;
        Arrays.sort(coins); 
        Map<String, Integer> result = new HashMap<>();
        result.put("result", Integer.MAX_VALUE);
        int res = getResult(coins, amount, 0, 0, result);
        if (res == Integer.MAX_VALUE) {
            return -1;
        }
        return res;
    }

    public int getResult(int[] coins, int nowAmount, int begin, int temp, Map<String, Integer> result) {
        if(nowAmount == 0) {
            result.put("result", Math.min(temp, result.get("result")));
            return result.get("result");
        }

        for(int i = begin; i<coins.length; i++) {
            if(nowAmount - coins[i] < 0) break;
            temp++;
            getResult(coins, nowAmount-coins[i],  i, temp, result);
            temp--;
        }

        return result.get("result");
    }
}

3.颜色分类

题目描述:
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

进阶:

一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?

题解:

采用多引用指向的方式来解,引用n_0指向放置元素0的位置、引用n_2指向放置元素2的位置(0和2位置正确了,1位置自然就正确了)。

class Solution {
    public void sortColors(int[] nums) {
    	// i用于数组的顺序遍历,
    	// 初始时n_0指向数组首元素,n_2指向数组尾元素
        int i = 0,n_0 = 0;
        int n_2 = nums.length - 1;
        // i引用位置小于等于n_2引用,表明各元素位置已正确
        while (i <= n_2) {
            int temp;
            switch (nums[i]) {
            // 当前元素为0. 交换n_0和i位置元素,同时n_0和i引用位置进1
                case 0:
                temp = nums[n_0];
                nums[n_0++] = nums[i];
                nums[i++] = temp;
                break;
                case 1:
              // 当前元素为1,i引用直接前进即可
                i++;
                break;
              // 当前元素为2,交换n_2和i位置元素,同时n_2引用位置退1
              // i引用位置不变,从数组后面交换过来的元素可能不属于当前位置,可能需要再做一次交换
                case 2:
                temp = nums[n_2];
                nums[n_2--] = nums[i];
                nums[i] = temp;
                break;
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值