LeetCode 每周算法 1(哈希、双指针、滑动窗口)

LeetCode 每周算法 1(哈希、双指针、滑动窗口)

哈希算法:

在这里插入图片描述

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        # 创建一个空字典来存储遍历过的元素及其索引
        num_dict = {}  
      
        # 使用for循环遍历nums数组,enumerate函数会同时返回索引(i)和值(num)  
        for i, num in enumerate(nums):  
            # 计算target减去当前元素的值,得到我们需要找的“补数”  
            complement = target - num  
            
            # 检查这个补数是否已经在字典num_dict中  
            if complement in num_dict:  
                # 如果在,说明我们找到了一个元素对,它们的和等于target  
                # num_dict[complement]是补数的索引,i是当前元素的索引  
                # 返回这两个索引的列表  
                return [num_dict[complement], i]  
            
            # 如果补数不在字典中,将当前元素及其索引添加到字典中  
            # 这样,在下一次迭代中,如果遇到了与当前元素相加等于target的元素,  
            # 我们就可以通过查找字典来快速找到它的索引  
            num_dict[num] = i  
        
        # 如果没有找到满足条件的两个数(但根据题目描述,这种情况不会发生),  
        # 理论上这里应该返回一个空列表。然而,由于题目保证每种输入只会对应一个答案,  
        # 所以这行代码实际上不会被执行到。  
        return []

在这里插入图片描述

class Solution(object):
    def groupAnagrams(self, strs):
        """
        :type strs: List[str]
        :rtype: List[List[str]]
        """
        # 创建一个空字典,用于存储排序后的字符串(作为键)和对应的原始字符串列表(作为值)  
        anagrams = {}  
        
        # 遍历输入字符串数组 strs 中的每个字符串 s  
        for s in strs:  
            # 对字符串 s 进行排序,得到排序后的字符串。注意,这里返回的是一个字符列表  
            sorted_chars = sorted(s)  
            
            # 将排序后的字符列表转换为字符串。
            # 这里使用 ''.join() 方法将列表中的字符拼接成一个新的字符串
            sorted_s = ''.join(sorted_chars)  
            
            # 检查排序后的字符串 sorted_s 是否已经作为键存在于字典 anagrams 中  
            if sorted_s in anagrams:  
                # 如果存在,则将当前字符串 s 添加到该键对应的列表中  
                anagrams[sorted_s].append(s)  
            else:  
                # 如果不存在,则在字典 anagrams 中创建一个新的键 sorted_s,
                # 并将其值设置为一个包含当前字符串 s 的新列表  
                anagrams[sorted_s] = [s]  
        
        # 字典 anagrams 的值(即所有字母异位词的列表)现在包含了所有分组的结果  
        # 使用 list() 函数将字典的 values 视图转换为列表,并返回这个列表  
        # 注意:这里的列表是包含多个列表的列表,每个内部列表代表一组字母异位词  
        return list(anagrams.values())

在这里插入图片描述

class Solution(object):
    def longestConsecutive(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # 创建一个空集合来存储数组中的所有元素  
        num_set = set(nums)  
        longest_streak = 0  
        
        # 遍历数组中的每个元素(或遍历可能的序列起始点,但直接遍历数组更简单)  
        for num in nums:  
            # 如果当前数字减1不在集合中,说明它可能是某个连续序列的起点  
            if num - 1 not in num_set:  
                # 初始化当前序列的长度为1  
                current_num = num  
                current_streak = 1  
                
                # 尝试扩展当前序列,只要num+1还在集合中  
                while current_num + 1 in num_set:  
                    current_num += 1  
                    current_streak += 1  
                
                # 更新最长序列的长度  
                longest_streak = max(longest_streak, current_streak)  
        
        return longest_streak

双指针算法:

在这里插入图片描述

class Solution(object):
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        # 初始化一个指针left,指向数组的开始位置  
        # 这个指针用来追踪当前应该放置非零元素的位置  
        left = 0  
        
        # 使用for循环遍历整个数组nums  
        # right指针是循环变量,它的值在每次迭代中都会自动增加,从而遍历数组中的每个元素  
        for right in range(len(nums)):  
            # 检查当前right指针所指的元素是否不等于0  
            if nums[right] != 0:  
                # 如果不等于0,那么将该元素与left指针所指的元素进行交换  
                # 这确保了所有非零元素都被放置在数组的前面部分  
                nums[left], nums[right] = nums[right], nums[left]  
                
                # 交换之后,将left指针向后移动一位  
                # 这样,left指针就指向了下一个应该放置非零元素的位置  
                left += 1

在这里插入图片描述

class Solution(object):
    def maxArea(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        # 初始化左指针指向数组的开始位置  
        left, right = 0, len(height) - 1  
        # 初始化最大面积为0  
        maxArea = 0  
        
        # 当左指针小于右指针时,继续循环  
        while left < right:  
            # 计算当前左右指针所构成容器的面积  
            # 容器面积 = 较小的高度 * 容器宽度(即左右指针之间的距离)  
            currentArea = min(height[left], height[right]) * (right - left)  
            # 更新最大面积  
            # 如果当前面积大于已知的最大面积,则更新maxArea  
            maxArea = max(maxArea, currentArea)  
            
            # 移动指向较短线的指针  
            # 这样做是因为容器的容量由较短的线决定,移动长线的指针并不能增加容器的容量  
            if height[left] < height[right]:  
                # 如果左线较短,则左指针右移  
                left += 1  
            else:  
                # 如果右线较短或两线等长(但按题意,我们总是先尝试移动左指针或右指针),
                # 则右指针左移  
                right -= 1  
        
        # 当循环结束时,maxArea存储了容器可以容纳的最大水量  
        return maxArea

在这里插入图片描述

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        # 对数组进行排序,方便后续使用双指针法并去除重复的三元组  
        nums.sort()  
        result = []  # 用来存储结果的三元组列表  
        n = len(nums)  
        
        # 遍历数组中的每个元素作为三元组的第一个数  
        for i in range(n):  
            # 跳过重复的元素,避免产生重复的三元组  
            if i > 0 and nums[i] == nums[i - 1]:  
                continue  
            
            # 初始化双指针,left指向i的下一个位置,right指向数组末尾  
            left, right = i + 1, n - 1  
            
            # 当left小于right时,执行循环  
            while left < right:  
                # 计算当前三数之和  
                total = nums[i] + nums[left] + nums[right]  
                
                # 如果和为0,则找到一个三元组  
                if total == 0:  
                    # 将找到的三元组添加到结果列表中  
                    result.append([nums[i], nums[left], nums[right]])  
                    
                    # 移动left和right指针,同时跳过重复的元素  
                    while left < right and nums[left] == nums[left + 1]:  
                        left += 1  
                    while left < right and nums[right] == nums[right - 1]:  
                        right -= 1  
                    
                    # 继续寻找下一个可能的三元组  
                    left += 1  
                    right -= 1  
                
                # 如果和小于0,说明需要增大总和,因此移动left指针向右  
                elif total < 0:  
                    left += 1  
                
                # 如果和大于0,说明需要减小总和,因此移动right指针向左  
                else:  
                    right -= 1  
        
        # 返回所有找到的三元组  
        return result

在这里插入图片描述

class Solution(object):
    def trap(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        # 如果高度图为空或包含的柱子数量少于3个,则无法形成积水,直接返回0  
        if not height or len(height) < 3:  
            return 0  
        
        # 初始化左右指针,分别指向高度图的开始和结束位置  
        left, right = 0, len(height) - 1  
        # 初始化左右两侧的最大高度,初始时分别为左右指针所在位置的高度  
        left_max, right_max = height[left], height[right]  
        # 初始化积水量为0  
        volume = 0  
        
        # 当左指针小于右指针时,继续遍历  
        while left < right:  
            # 如果左侧最大高度小于右侧最大高度,说明左侧边界较低,限制了左侧的水量  
            if left_max < right_max:  
                # 计算从left到left_max之间的水量
                # (注意:这里实际上是left_max - height[left],因为只考虑左侧边界)  
                # 并累加到总积水量中  
                volume += left_max - height[left]  
                # 将左指针向右移动一位  
                left += 1  
                # 更新左侧最大高度(如果新的高度更大)  
                left_max = max(left_max, height[left])  
            # 否则,如果右侧最大高度较小或相等,则处理右侧  
            else:  
                # 类似地,计算并累加右侧的水量  
                volume += right_max - height[right]  
                # 将右指针向左移动一位  
                right -= 1  
                # 更新右侧最大高度(如果新的高度更大)  
                right_max = max(right_max, height[right])  
        
        # 当左指针不再小于右指针时,说明已经遍历完所有可能形成积水的位置  
        # 返回总积水量  
        return volume

滑动窗口:

在这里插入图片描述

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        # 初始化最长无重复字符子串的长度为0  
        max_length = 0  
        # 初始化一个字典,用于存储字符最后出现的位置  
        char_map = {}  
        # 初始化窗口的左边界
        left = 0
        
        # 遍历字符串s中的每个字符  
        for right in range(len(s)):  
            # 如果当前字符已经存在于char_map中,并且它出现的位置在左边界的右侧  
            # 则说明这个字符破坏了当前窗口的唯一性,需要移动左边界  
            if s[right] in char_map and char_map[s[right]] >= left:  
                left = char_map[s[right]] + 1  # 更新左边界为重复字符的下一个位置  
            
            # 更新当前字符在char_map中的位置  
            char_map[s[right]] = right  
            
            # 计算当前窗口的长度,并更新最长长度  
            max_length = max(max_length, right - left + 1)  
        
        # 返回最长无重复字符子串的长度  
        return max_length

在这里插入图片描述

class Solution(object):
    def findAnagrams(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: List[int]
        """
        # 创建一个字典来存储字符串p中每个字符的出现次数  
        p_count = {}  
        for char in p:  
            p_count[char] = p_count.get(char, 0) + 1  
        
        # 初始化一个列表来存储所有满足条件的子串的起始索引  
        result = []  
        # 创建一个字典来记录当前窗口(子串)中每个字符的计数  
        current_count = {}  
        # left指针表示当前窗口的起始位置  
        left = 0  
        
        # 遍历字符串s  
        for right in range(len(s)):  
            # 如果当前字符在current_count中已存在,则增加其计数  
            # 否则,将当前字符添加到current_count中并初始化为1  
            if s[right] in current_count:  
                current_count[s[right]] += 1  
            else:  
                current_count[s[right]] = 1  
            
            # 当当前窗口的大小与p的长度相等时  
            if right - left + 1 == len(p):  
                # 检查current_count是否与p_count相等,即检查当前窗口是否是p的异位词  
                if current_count == p_count:  
                    # 如果是,则将当前窗口的起始索引left添加到结果列表中  
                    result.append(left)  
                # 准备移动窗口,首先减少窗口最左边字符的计数  
                current_count[s[left]] -= 1  
                # 如果最左边字符的计数变为0,则从current_count中删除该字符  
                if current_count[s[left]] == 0:  
                    del current_count[s[left]]  
                # 移动窗口的左边界  
                left += 1  
        
        # 返回所有满足条件的子串的起始索引列表  
        return result
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值