Top 100 Linked Question 修炼------第49题、第55题

49. Group Anagrams

题目链接

题目分析:给出一个list,里面包含各种字符串,尝试将同字母异形词分到同一类。下面是官方的例子介绍:

Input: ["eat", "tea", "tan", "ate", "nat", "bat"],
Output:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

随便选择两个词来进行解释,因为"eat","tea"都是由'e'、'a'、't'三个字母组成,那么这两个单词最后会被分在同一个list里面。同理可得,整个题目可以进行分类。

题目解答:在这里,我们使用python化一点的解法,就是建立一个字典,索引为tuple,tuple里面的元素就是单个字母,若两个单词所有的字母都相同,那么最后它们俩就是属于同字母异构词。

class Solution:
    def groupAnagrams(self, strs):
        ans={}
        for i in strs:
            key=tuple(sorted(i))
            if key in ans:
                ans[key].append(i)
            else:
                ans[key]=[i]
        return ans.values()

在上面的解答中,我们需要判断key是否存在,这个内容是可以简化的,我们可以采用collections类里面的defaultdict,这样就省去了我们判断的情况。

    def groupAnagrams(self,strs):
        import collections
        ans=collections.defaultdict(list)
        for i in strs:
            ans[tuple(sorted(i))].append(i)
        return ans.values()

55. Jump Game

题目链接

题目解释:给定一个list,里面是非负整数。现在我们要玩一个跳跃游戏,假设你是处在list的第一个位置(首部),list中的每个整数代表你能够跳的最远的距离,试问:在给定一个list的情况下,问你是否能够跳到list的最后一个位置。如

Input: [2,3,1,1,4]
Output: true

给出示例如上,假设你处在第一个位置,标记给出的list为nums,那么nums[0]=2代表你此时能够跳的最远的距离为2,我们观察后面的数据,发现跳一步,到下标为1这个位置,此时nums[1]=3,此时可以直接跳三步,那么可以直接跳到这个list的末尾,于是程序最终的结果就是可以跳到,最后的结果也就是True.

题目分析:首先我们能想到的一个基础的方法是回溯法,回溯法的思想和这个题目的思想完全吻合,都是不断的进行尝试的过程,从当前位置出发,一直走到当前的数据能够走的最远的距离,然后一次次的迭代,看是否存在一条路径能够到达,于是,笔者写出了这个基本的回溯法的代码:

class Solution:
    def canJump(self, nums):
        return self.canJumpFromPosition(0,nums)
    def canJumpFromPosition(self,position,nums):
        # 已经到达最后一个位置
        if position==len(nums)-1:
            return True
        furthest=min(position+nums[position],len(nums)-1)
        # 回溯判断,从[position+1:furthest+1]这个区间内,是否有路径可达
        for next_position in range(position+1,furthest+1):
            if self.canJumpFromPosition(next_position,nums):
                return True
        return False

于是,笔者开心的去leetcode上面进行提交,啊哦,出现了这样的提示: Time Limit Exceeded,缺失,采用这个最基础的递归算法,仔细的思考一下,确实会发现可能存在深度特别大的递归调用,那么就开始改进了呗。

结合我们自己的做题经验,一般这种递归的情况的题目我们可以尝试使用动态规划去解决,这样就可以避免后期一些不必要的递归操作,(下面的解答参考leetcode上面的解题方案,本人做题较少,还是难得想出来):

我们可以采用一张备忘录的表,来标记list中的数据的好坏(能够让我们从开头走到结尾的为好,不能的话则定义为坏),好了思想就是这么的简单,但是怎么确定给出的数据是好还是坏呢?以下内容翻译自官方的Solution:

Top-down Dynamic Programming can be thought of as optimized backtracking. It relies on the observation that once we determine that a certain index is good / bad, this result will never change. This means that we can store the result and not need to recompute it every time.

Therefore, for each position in the array, we remember whether the index is good or bad. Let's call this array memo and let its values be either one of: GOOD, BAD, UNKNOWN. This technique is called memoization[2].

自顶向上的动态规划方法可以被视为一种优化后的回溯法,它取决于我们观察的结果:一旦我们决定某一个确定的下标是好还是坏,那么这个结果就不会再改变。这就意味着我们可以存储相应的结果,不必重复的计算。

于是,对于数组中每个元素的位置,我们尝试记忆这个下标是好还是坏。尝试定义一个memo的枚举类,里面的元素要么是GOOD,BAD或者UNKNOW,这项技术叫做备忘录算法。

下面给出备忘录算法的表格:

Index	0	1	2	3	4	5	6
nums	2	4	2	1	0	2	0
memo	G	G	B	B	B	G	G

这个算法的步骤如下:

1.首先进行初始化工作,将memo中所有的内容都初始化为UNKNOW,但是最后一个元素初始化微GOOD,因为他自己可以到达自身。

2.修改回溯法使其在运行递归的时候首先检查下标的memo是什么状态,是好、坏还是UNKNOW

  2.1 如果是KNOW,那么返回True

  2.2 否则是继续运行回溯法

3.一旦我们确定了当前元素的状态(GOOD、BAD、或者是UNKNOW),那么我们将其存储在memo里面

// 定义枚举变量
enum Index {
    GOOD, BAD, UNKNOWN
}

public class Solution {
    Index[] memo;

    public boolean canJumpFromPosition(int position, int[] nums) {
        // 进行回溯法之前先进行判断
        if (memo[position] != Index.UNKNOWN) {
            return memo[position] == Index.GOOD ? true : false;
        }

        int furthestJump = Math.min(position + nums[position], nums.length - 1);
        for (int nextPosition = position + 1; nextPosition <= furthestJump; nextPosition++) {
            if (canJumpFromPosition(nextPosition, nums)) {
                memo[position] = Index.GOOD;
                return true;
            }
        }

        memo[position] = Index.BAD;
        return false;
    }

    public boolean canJump(int[] nums) {
        memo = new Index[nums.length];
        // 初始化所有的内容为UNKNOW
        for (int i = 0; i < memo.length; i++) {
            memo[i] = Index.UNKNOWN;
        }
        memo[memo.length - 1] = Index.GOOD;
        return canJumpFromPosition(0, nums);
    }
}

总结

当当前方法不行的时候,尝试着去优化吧,第一遍的时候做出一部分的结果吧......

猜你喜欢

转载自blog.csdn.net/sir_TI/article/details/88853702