算法刷题之二字典

Posted 金色旭光

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法刷题之二字典相关的知识,希望对你有一定的参考价值。

字典类算法

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

  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. 对元素分类操作

以上是关于算法刷题之二字典的主要内容,如果未能解决你的问题,请参考以下文章

二叉树经典题之二叉树最近公共祖先(LeetCode)

php面试题之二——数据结构和算法(高级部分)

leetcode刷题之数组NO.5

算法刷题之堆

力扣刷题之贪心算法(C++)

leetcode刷题之贪心算法(C++&Java)