• 算法刷题之二字典


    字典类算法

    字典所涉及到的题目有如下:

    1. 两数之和
    2. 字母异位词分组
    3. 数独
    4. 设计LRU

    字典使用技巧:
    字典在某些场景下很方便:

    1. 判断重复,某一个元素是否重复出现
    2. in操作的时间复杂度为常数
    3. 需要使用复杂数据结构,key-->[]
    4. 消灭判断,使用key直接获取value

    两数之和

    题目:
    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
    示例:

    给定 nums = [2, 7, 11, 15], target = 9
    因为 nums[0] + nums[1] = 2 + 7 = 9
    所以返回 [0, 1]
    

    方法:
    常规思路是可以两次循环找到数组中任意两个不重复元素的和,时间复杂度为O(2)。
    高效的做法是将两个数相加等于target,变成判断b --> target-a 在不在字典中。如果在就说明有a + b 等于 target。同时,判断b在不在字典中是常数级别的操作,所以将两层循环变成一层循环。
    知识点:使用了字典的查找高效这一特点

    def twoSum(nums, target):
        hashmap={}
        for i,num in enumerate(nums):
            if hashmap.get(target - num) is not None:
                return [i,hashmap.get(target - num)]
            hashmap[num] = i #这句不能放在if语句之前,解决list中有重复值或target-num=num的情况
    

    字母异位词分组

    题目:
    给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

    示例:
    输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
    输出:

    [
      ["ate","eat","tea"],
      ["nat","tan"],
      ["bat"]
    ]
    

    说明:
    所有输入均为小写字母。
    不考虑答案输出的顺序。

    方法:给出的字符串数组,将每一个字符串重新排列之后可以分成三类。进一步说就是将排序之后相同的字符串加入到一个数组中。使用字典的key:value。value为数组,这样的结构能够完成,key就是排序后的字符串。因为排序之后的字符串是数组,所以不能直接作为key值,将其合并成字符串。
    知识点: 字典的复杂格式 key:[]的使用。要熟练使用collections中的基础数据增强功能

    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        from collections import defaultdict  # 构造一个key --> []的数据结构
        strs_dict = defaultdict(list)
        for i in strs:
            strs_dict[''.join(sorted(i))].append(i)
    
        return list(strs_dict.values())
    

    有效的数独

    题目:

    判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

    数字 1-9 在每一行只能出现一次。
    数字 1-9 在每一列只能出现一次。
    数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

    上图是一个部分填充的有效的数独。
    数独部分空格内已填入了数字,空白格用 '.' 表示。

    示例 1:
    输入:

    [
      ["5","3",".",".","7",".",".",".","."],
      ["6",".",".","1","9","5",".",".","."],
      [".","9","8",".",".",".",".","6","."],
      ["8",".",".",".","6",".",".",".","3"],
      ["4",".",".","8",".","3",".",".","1"],
      ["7",".",".",".","2",".",".",".","6"],
      [".","6",".",".",".",".","2","8","."],
      [".",".",".","4","1","9",".",".","5"],
      [".",".",".",".","8",".",".","7","9"]
    ]
    
    输出: true
    

    方法:关键点在于判断当前的数字在同行、同列、同一个区域有没有重复。将每一个行中数字保存为一个字典,key是数字,value是出现次数。如果有重复,则value会大于1。同样,每一个列,每一个区域都需要以数字为key,建立字典。最后判断行、列、区域中哪一块有value大于1,则表明数独不存在

    知识点:使用字典判断重复,比起循环查找要高效很多。同时字典不断添加还有一个先后循序,有些场合可以避免重复(两数之和)

    def isValidSudoku(self, board):
            """
            :type board: List[List[str]]
            :rtype: bool
            """
            # init data
            rows = [{} for i in range(9)]
            columns = [{} for i in range(9)]
            boxes = [{} for i in range(9)]
    
            # validate a board
            for i in range(9):
                for j in range(9):
                    num = board[i][j]
                    if num != '.':
                        num = int(num)
                        box_index = (i // 3 ) * 3 + j // 3
                        
                        # keep the current cell value
                        rows[i][num] = rows[i].get(num, 0) + 1
                        columns[j][num] = columns[j].get(num, 0) + 1
                        boxes[box_index][num] = boxes[box_index].get(num, 0) + 1
                        
                        # check if this value has been already seen before
                        if rows[i][num] > 1 or columns[j][num] > 1 or boxes[box_index][num] > 1:
                            return False         
            return True
    

    设计LRU

    题目:
    设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
    set(key, value):将记录(key, value)插入该结构
    get(key):返回key对应的value值

    要求:

    1. set和get方法的时间复杂度为O(1)
    2. 某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
    3. 当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。

    若opt=1,接下来两个整数x, y,表示set(x, y)
    若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1

    对于每个操作2,输出一个答案

    方法:题目中要求set或get的时间复杂度是O(1),这就是意味着添加或者查询元素是不能使用循环,所以必须是字典的数据结构。而set或get某一个元素同时还要让该元素热点最大,结合时间复杂度来考虑就不能用循环解决,只能用插入的顺序。但是总所周知通常字典是一个无序的,所以这里要使用有序字典。从collections中导入OrderedDict这种有序字典。有序字典顾名思义就是能够按照插入的顺序保存元素。
    知识点:collections中增强数据结构,结合对时间复杂的要求

    #
    # lru design
    # @param operators int整型二维数组 the ops
    # @param k int整型 the k
    # @return int整型一维数组
    #
    class Solution:
        def LRU(self , operators , k ):
            from collections import OrderedDict
            res = []
            dic = OrderedDict()
             
            for item in operators:
                oper = item[0]
                if oper == 1:
                    key = item[1]
                    value = item[2]
                    if key in dic:
                        dic.pop(key)
                    else:
                        if k > 0:
                            k -= 1
                        else:
                            dic.popitem(False)
                    dic[key] = value
                elif oper == 2:
                    key = item[1]
                    res.append(dic.get(key, -1))
                    if key in dic:
                        dic.move_to_end(key)
            return res
    

    总结

    字典在算法中出镜的频率很高,这和字典本身的高效性能有关。通常来说一个题如果有一下特征,可以考虑使用字典来解决:

    1. 相加之和等于多少
    2. 判断元素是否重复出现
    3. 对元素分类操作
  • 相关阅读:
    钉钉C# 使用数据接口要注意的问题
    两个比较快的DNS
    钉钉考勤组信息
    Sqlserver直接附加数据库和设置sa密码
    SQL Server 2016 附加数据库提示创建文件失败如何解决
    FastJson用法
    钉钉开发平台文档
    Sqlserver脚本创建登录名密码
    string 与 stringbuilder效率相差很大
    C# RichTextBox插入带颜色数据
  • 原文地址:https://www.cnblogs.com/goldsunshine/p/14921298.html
Copyright © 2020-2023  润新知