数据结构课程:必知必会的数据结构(队栈、哈希表、布隆过滤器)

栈和队列基础:

  • 定义:存放数据的线性表。
  • 操作:入栈/队列、出栈/队列、判断满/空。
  • 空间复杂度:O(n)
  • 单次操作时间复杂度:O(1)
  • 区别:栈是先进后出(FILO, First In Last Out),队列是先进先出(FIFO, First In First Out),具体如下图:

栈和队列的实现

  • 数组和链表皆可(线性表)
  • 指针:(辅助变量),①栈顶/底指针,②队头/尾指针
  • 关键:出入元素的同时移动指针

栈的应用:括号匹配检测:

  • 括号、引号等符号是成对出现的,必须相互匹配,设计一个算法,自动检测输入的字符串中的括号是否匹配,比如:

  • 从左向右扫描字符串:

  • 不匹配进栈:

  • 匹配后出栈:

参考:https://www.cnblogs.com/qiaojushuang/p/9048675.html

LeetCode例题:

394. 字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例:

s = "3[a]2[bc]", 返回 "aaabcbc".
s = "3[a2[c]]", 返回 "accaccacc".
s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef"

解法

  • 思路:使用两个栈分别存储数字和字母

  • 注意1: 数字是多位的话,要处理后入数字栈

  • 注意2: 出栈时过程中产生的组合后的字符串要继续入字母栈

  • 注意3: 记得字母出栈的时候字符要逆序组合成字符串

  • 注意4: 不用字符串而用字母栈的原因是字符串的 join 效率会比字符串加法高一些
class Solution(object):
    def decodeString( s):
        """
        :type s: str
        :rtype: str
        """
        nums, chars = [], []        
        i, length = 0, len(s)
        while i < length:
            j = i + 1
            if s[i].isdigit():
                num = int(s[i])
                while j < length:
                    if s[j].isdigit():   #是否只由数字构成,是返回True
                        num = num * 10 + int(s[j])
                        j += 1
                    else:
                        break
                nums.append(num)
            elif s[i] == '[' or s[i].isalpha():
                chars.append(s[i])
            else:
                t, tmpc = chars.pop(), []
                while t != '[':
                    tmpc.append(t)
                    #默认移除列表最后一个元素,并返回最后一个元素,这里是从栈中弹出最后一个字符并删除,直至’[‘
                    t = chars.pop()  
                tchars = nums.pop() * ''.join(tmpc[::-1])
                chars.append(tchars)
            i = j
    return ''.join(chars)

另一种解法:

class Solution:
    def decodeString(self, s):
        """
        :type s: str
        :rtype: str
        """
        stack = []
        num = 0
        string = ''
        for i in s:
            if i == '[':
                stack.append(string)
                stack.append(num)
                string = ''
                num = 0
            elif i == ']':
                n = stack.pop()
                prestring = stack.pop()
                string = prestring+n*string
            elif i.isdigit():
                num = 10*num + int(i)
            else:
                string += i
        return string

155. 最小栈

设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

  • push(x) -- 将元素 x 推入栈中。
  • pop() -- 删除栈顶的元素。
  • top() -- 获取栈顶元素。
  • getMin() -- 检索栈中的最小元素。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2

解法:采用双栈的方式,一个用于记录数值,一个用于记录最小值,当x与最小值相等的情况也要考虑进去

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.l = []
        self.min = []

    def push(self, x):
        """
        :type x: int
        :rtype: void
        """
        self.l.append(x)
        if self.min == []:
            self.min.append(x)
        else:
            if self.min[-1]>x:
                self.min.append(x)
            else:
                self.min.append(self.min[-1])
                
    def pop(self):
        """
        :rtype: void
        """
        self.l.pop()
        self.min.pop()

    def top(self):
        """
        :rtype: int
        """
        return self.l[-1]

    def getMin(self):
        """
        :rtype: int
        """
        return self.min[-1]


# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

哈希表基础:

  • 定义:存放数据的集合
  • 操作:根据(Key, Value)进行,插入,查找,删除(可以没有)
  • 空间复杂度:O(m)
  • 单次操作时间复杂度:O(1)
  • 本质:Key的索引

例题

  • 给出n个[0, m)范围内的整数,去重
  • 快速排序:期望时间复杂度O(nlogn),附加空间复杂度O(1)
  • 计数(基数)排序:时间复杂度O(n + m)   超越比较排序下限,附加空间复杂度O(m)

哈希表实现

  • 处理冲突(Key, Value):开放地址法(数组)和拉链法(数组+链表)
  • 负载率= 已有元素大小/ 存储散列大小

应用

  • 字符串匹配:设字符串A=‘12314123’,求‘123’在A中出现的次数?不会写KMP又想要O(n)肿么办?

    ①Key相等时Value有可能不同;
    ②每次比较Value也是不小的开销,特别是Value可能很大;
    ③不考虑Value将产生错误率(错误率换时间);
    ④多重哈希(降低错误率);

哈希表删除问题

  • 能否直接删除哈希表中的元素?
  • 考虑两种不同的实现方式
  • 硬删除vs 软删除

LeetCode例题:

128. 最长连续序列

给定一个未排序的整数数组,找出最长连续序列的长度。

要求算法的时间复杂度为 O(n)

示例:

输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。

解法:利用哈希表解决,题目要求 O(n) 复杂度。

  • 用哈希表存储每个端点值对应连续区间的长度
  • 若数已在哈希表中:跳过不做处理
  • 若是新数加入:
    • 取出其左右相邻数已有的连续区间长度 left 和 right
    • 计算当前数的区间长度为:cur_length = left + right + 1
    • 根据 cur_length 更新最大长度 max_length 的值
    • 更新区间两端点的长度值
class Solution:
    def longestConsecutive(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        d = {}
        max_len = 0
        for num in nums:
            if num not in d:
                left = d.get(num-1,0)
                right = d.get(num+1,0)
                
                cur_len = 1+left+right
                if cur_len > max_len:
                    max_len = cur_len
                    
                d[num]=cur_len
                d[num-left]=cur_len
                d[num+right]=cur_len
        return max_len

布隆过滤器基础:

定义:是判断一个字符串是否出现过的数据结构,相比哈希表是空间换时间,布隆过滤器是错误率换空间。

实现

  • 由01的数字序列构成
  • 插入:多个不同hash函数计算Key,置1
  • 查找:有一个为0不可能存在,全为1可能存在
  • 空间:

优缺点

  • 优点:时间和空间、多个hash函数可以并行、交差并(位运算)
  • 缺点:错误率随负载率上升而上升、无法删除

 


To be continue......

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值