Datawale编程学习之哈希表和字符串(4)


任务4:7~8天

1.学习目标

1.1 散列表(哈希表)

实现一个基于链表法解决冲突问题的散列表
实现一个 LRU 缓存淘汰算法

1.2 字符串

实现一个字符集,只包含 a~z 这 26 个英文字母的 Trie 树
实现朴素的字符串匹配算法

1.3 对应的 LeetCode 练习题

哈希表
并完成leetcode上的两数之和(1)及Happy Number(202)!(要求全部用哈希思想实现!)
字符串
Reverse String (反转字符串)
英文版:Loading…
中文版:Loading…

Reverse Words in a String(翻转字符串里的单词)
英文版:Loading…
中文版:Loading…

String to Integer (atoi)(字符串转换整数 (atoi))
英文版:Loading…
中文版:Loading…

2. 学习内容

2.1 哈希表

定义:根据关键码值(key value)而进行访问的数据结构
通过把关键码值映射到表中的一个位置来访问记录,加快查找速度
散列函数/散列表
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后能得到包含该关键字的记录在表中的地址,则称M为哈希表,函数f(key)为哈希函数
哈希表查找的步骤
使用哈希函数:将被查找的键转化为数组的索引
除留余数法
直接地址法
数字分析法
平方取中法
折叠法
处理哈希碰撞冲突(开放寻址法、拉链法)
优点:时间和空间上的平衡
键值作为索引,查找的时间复杂度:O(1)
开放寻址法
如果有冲突发生,那么尝试选择另外的单元,直到找出空的单元为止
Hi=(H(key) + di) MOD m , i=1,2,…,k(k<=m-1)
di = 1,2,3,4…m-1,线性探测再散列
di = di = 12,-12,22,-22,32,…,±(k)^2,(k<=m/2),称二次探测再散列;
di = 伪随机数序列,称为随机探测再散列
拉链法(链表法)
2.1.1基于链表法解决冲突问题的散列表

class ListNode:
    def __init__(self, key):
        self.key = key
        self.next = None


class HashMap:
    def __init__(self, tableSize):
        self.table = [None] * tableSize
        self.n = 0  # n代表结点的数量

    def __len__(self):
        return self.n

    def hash(self, key):
        return abs(hash(key)) % len(self.table)

    def __getitem__(self, key):
        j = self.hash(key)
        node = self.table[j]
        while node is not None and node.key != key:
            node = node.next
        if node is None:
            raise KeyError('KeyError' + repr(key))
        return node

    def insert(self, key):
        try:
            self[key]
        except KeyError:
            j = self.hash(key)
            node = self.table[j]
            self.table[j] = ListNode(key)
            self.table[j].next = node 
            self.n += 1

    def __delitem__(self, key):
        j = self.hash(key)
        node = self.table[j]
        if node is not None:
            if node is not None:
                if node.key == key:
                    self.table[j] = node.next
                    self -= 1
                else:
                    while node.next is not None:
                        pre = node
                        node = node.next
                        if node.key == key:
                            pre = node
                            node = node.next
                            if node.key == key:
                                pre.next = node.next
                                self.n -= 1
                                break

2.1.2 LRU缓存淘汰算法
LRU:淘汰掉最不经常使用的
核心思想:如果数据最近被访问过,那么将来被访问的几率也就越高
设计一个LRU缓存,使得放入和移除都是O(1)
为什么散列表和链表经常一起使用
在这里插入图片描述

class LRUCache:
    # @param capacity, an integer
    def __init__(self, capacity):
        self.cache = {}
        self.used_list = []
        self.capacity = capacity
 
    # @return an integer
    def get(self, key):
        if key in self.cache:
            if key != self.used_list[-1]:
                self.used_list.remove(key)
                self.used_list.append(key)
            return self.cache[key]
        else:
            return -1
 
    # @param key, an integer
    # @param value, an integer
    # @return nothing
    def set(self, key, value):
        if key in self.cache:
            self.used_list.remove(key)
        elif len(self.cache) == self.capacity:
            self.cache.pop(self.used_list.pop(0))
        self.used_list.append(key)
        self.cache[key] = value

2.2 字符串

2.2.1 实现一个字符集,只包含 a~z 这 26 个英文字母的 Trie 树
Trie树:字典树(N叉树),一种特殊的前缀树结构
利用字符串公共前缀降低搜索空间,速度为O(k),k为输入的字符串长度
在这里插入图片描述
性质:
根节点不包含字符,除根结点以外的每个节点只包含一个字符
从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串
每个节点的所有子节点包含的字符串不相同
应用:
词频统计:比hash节省空间
搜索提示:输入前缀词的时候提示可以构成的词
作为辅助结构:如后缀树,AC自动机等辅助结构
只包含a~z的Trie树(字典树)

class Trie:
    # word_end = -1
 
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.root = {}
        self.word_end = -1
 
    def insert(self, word):
        """
        Inserts a word into the trie.
        :type word: str
        :rtype: void
        """
        curNode = self.root
        for c in word:
            if not c in curNode:
                curNode[c] = {}
            curNode = curNode[c]
          
        curNode[self.word_end] = True
 
    def search(self, word):
        """
        Returns if the word is in the trie.
        :type word: str
        :rtype: bool
        """
        curNode = self.root
        for c in word:
            if not c in curNode:
                return False
            curNode = curNode[c]
            
        # Doesn't end here
        if self.word_end not in curNode:
            return False
        
        return True
 
    def startsWith(self, prefix):
        """
        Returns if there is any word in the trie that starts with the given prefix.
        :type prefix: str
        :rtype: bool
        """
        curNode = self.root
        for c in prefix:
            if not c in curNode:
                return False
            curNode = curNode[c]
        
        return True
 
 
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
朴素的字符串匹配算法

2.2.2 实现朴素的字符串匹配算法
朴素的字符串匹配算法
字符串匹配:
输入:原字符串和子串
输出:子串在原字符串中首次出现的位置
朴素算法(暴力搜索法)
预处理时间:O(0)
匹配时间复杂度:O(N*M)
主要过程:从原字符串开始搜索,若出现不能匹配,则从原搜索位置+1继续
KMP算法

def strMatching(s1, s2):
    i, j = 0, 0
    n, m = len(s1), len(s2)
    while i < n and j < m:
        if s1[i] == s2[j]:
            i = i + 1
            j = j + 1
        else:
            i = i - j + 1
            j = 0
    if j == m:
        return i - j
    return -1

2.3 对应的 LeetCode 练习题

2.3.1哈希表
两数之和(1)

def twoSum(nums, target):
    map = {}
    for i, num in enumerate(nums):
        if target - num in map:
            return map[target - num], i
        else:
            map[num] = i

Happy Number(202)

class Solution:
    def isHappy(self, n):
        """
        :type n: int
        :rtype: bool
        """
        record = {}
        sq_sum = 0
        while n != 1:
            sq_sum = 0
            sub_num = n
            while sub_num > 0:
                sq_sum += (sub_num % 10) * (sub_num % 10)
                sub_num //= 10
            if sq_sum in record:
                return False
            else:
                record[sq_sum] = 1
            n = sq_sum
        return True

2.3.2 字符串
反转字符串

# 方法一:reverse()方法
s='helloword'
l=list(s)
l.reverse()
print("".join(l))   #把列表里的值拼接成一个字符串

# 方法二:使用栈
def func(s):
    l = list(s) #模拟全部入栈
    result = ""
    while len(l)>0:
        result += l.pop() #模拟出栈,pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
    return result
r = func(s)
print(r)
# 方法三:递归函数
def func(s):
    if len(s) <1:
        return s
    return func(s[1:])+s[0]

# 方法四 字符串切片的方法
s='helloword'
r=s[::-1]
print(r)

翻转字符串里的单词

def reverseWords(self, s: str) -> str:
    list= s.split()  # python 中字符串无法修改,要转为list
    return ' '.join(list[::-1])# 将反转后的列表转为 str

字符串转换整数(atoi)

class Solution:
    def myAtoi(self, str):
        import re        
        pattern = r"[\s]*[+-]?[\d]+"
        match = re.match(pattern, str)
        if match:
            res = int(match.group(0))
            if res > 2 ** 31 - 1:
                res = 2 ** 31 -1
            if res < - 2 ** 31:
                res = - 2 ** 31
        else:
            res = 0
        return res

3. 参考文献

https://shimo.im/docs/wiUMIdTR8Jsq7HO4/read

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值