【LeetCode】分块查找(Python版)

本文详细介绍了分块查找算法的基本原理和实现过程。通过将数列分块并确保块间有序,分块查找能在未完全排序的数列中高效搜索目标元素。文章还提供了Python代码示例,展示了如何构建分块并进行搜索。

一、分块查找

分块查找部需要进行整理排序,首先按照一定的取值范围将数列分成数块,块内的元素是可以无序的,但块必须是有序的,其中块的有序表示为处于后面位置中的块的最小元素都要比前面位置块中的最大元素大。

二、输出结果

在这里插入图片描述

三、代码实现

import random
import timeit
def randomList(n):
    iList = []
    for i in range(n):
        iList.append(random.randrange(0,1000))
    return iList


def divideBlock():
    global iList,indexList
    sort = []
    for key in indexList:
        # 第一次运算,key = [250,0]
        # 从iList里面获取数据,一个个判断是否在0-250之间
        # 若满足条件则将数据i存储到sub字典当中
        sub = [i for i in iList if i < key[0]]
        # 将0-250区间的个数赋值给indexiList中key[1]
        key[1] = len(sub)
        sort += sub
        # 删除已经分块的数据,为方便未分块数据进行分块
        iList = list(set(iList)-set(sort))
    iList = sort
    print()
    return indexList
def blockSearch(iList,key):
    right = 0
    left = 0
    for num in indexList:
        # 如果未找到分块的数据,会不断往后累加字典分块个数
        left += right
        right += num[1] 
        # 一个区间一个区间判断,属于该区间则跳出该for循环,继续往下执行
        if key< num[0]:
            break
    for i in range(left,right):
        if key == iList[i]:
            return i
    return -1

if __name__ == "__main__":
    ######创建20个1000以内的列表########
    iList = randomList(20)
    print(iList)
    #分块的字典列表,第二列数据为这个区域内的个数
    indexList = [[250,0],[500,0],[750,0],[1000,0]]
    divideBlock()
    print(iList)
    keys=[random.choice(iList),random.randrange(min(iList),max(iList))]
    for key in keys:
        num = blockSearch(iList,key)
        if(num>=0):
            print("%d number is %d\n"%(key,num))
        else:
            print("%d the key number is not find\n"%key)

<think>好的,我现在需要帮助用户解决如何在Python中使用函数实现字母的区间划分的问题。用户还提到了参考之前的引用内容,特别是引用[2]中的LeetCode问题763的解决方案。让我仔细分析这个问题。 首先,用户的问题是关于如何将字符串中的字母划分为尽可能多的区间,使得每个字母只出现在一个区间中。例如,字符串“ababcbacadefegdehijhklij”应该被划分为[9,7,8]这样的区间长度,对应的划分是"ababcbaca", "defegde", "hijhklij"。每个区间里的所有字母都不会出现在其他区间里。这类似于合并重叠区间的思路。 接下来,我需要回忆引用[2]中的解决方案。该解决方案是LeetCode 763题的标准解法,主要思路是先记录每个字母的首次和最后一次出现的位置,然后将这些区间按起始位置排序,最后合并重叠的区间。用户提供的代码示例中,遍历了26个字母,找到每个字母在字符串中的起始和结束位置,生成区间列表,然后合并这些区间。合并的方法和LeetCode 56题合并区间的方法类似,即维护当前区间的左右边界,遇到重叠的区间就扩展右边界,否则将当前区间加入结果,并更新左右边界为下一个区间。 现在,用户希望将这个逻辑封装成一个Python函数。我需要考虑如何将这个过程分解为可重用的函数,同时确保代码的可读性和效率。可能的步骤包括: 1. **记录每个字符的最后出现位置**:这一步可以优化,不需要遍历每个字母,而是遍历整个字符串,记录每个字符的最后出现的索引。这样避免检查26个字母中的每一个是否存在于字符串中,提高效率。 2. **生成区间列表**:根据每个字符的起始和结束位置生成区间,但为了避免重复,其实只需要记录每个字符的最后出现位置即可。因为当遍历字符串时,当前区间的结束位置是当前字符的最后出现位置的最大值,直到遍历到结束位置,形成一个区间。 3. **合并区间**:这一步可以通过单次遍历字符串来完成。维护当前区间的开始和结束,当遍历到当前结束位置时,确定一个区间,并更新开始位置到下一个字符的位置。 可能需要注意的是,用户提供的代码可能有些冗余,比如生成所有字母的区间然后合并,但实际上更高效的方式是直接记录每个字符的最后出现位置,然后遍历字符串时动态调整区间的结束位置。这种方法的时间复杂度是O(n),比生成所有区间再排序合并更优。 因此,正确的实现步骤应该是: - 创建一个字典,记录每个字符的最后出现位置。 - 遍历字符串,更新当前区间的结束位置为当前字符最后出现位置的最大值。 - 当遍历到当前结束位置时,记录区间长度,并重置开始位置到下一个字符的位置。 这样,不需要显式生成区间列表,也不需要排序和合并,因为通过维护当前区间的结束位置,可以实时确定每个不重叠的区间。 现在,我需要将这个逻辑转化为Python函数。可能需要两个主要函数:一个负责生成字符的最后出现位置,另一个负责遍历字符串并划分区间。 此外,用户提供的引用[2]中的代码使用的是首尾查找每个字符的位置,这可能对于长字符串来说效率较低,因为多次调用str.find和str.rfind。更好的方法是一次遍历记录最后出现的位置,这样时间复杂度更低。 因此,优化后的函数应该: 1. 遍历一次字符串,记录每个字符的最后出现索引。 2. 再次遍历字符串,维护当前区间的start和end,当i到达end时,划分一个区间。 这样,两次遍历的时间复杂度都是O(n),整体效率较高。 现在,我需要验证这个思路是否正确。例如,对于字符串"ababcbacadefegdehijhklij",每个字符的最后出现位置为: a:8, b:5, c:7, d:14, e:15, f:11, g:13, h:19, i:22, j:23, k:20, l:21. 遍历字符串时: - start=0, end=8(因为a的最后位置是8)。在i=8时,记录区间长度9(8-0+1),然后start=9。 - 下一个字符是d,最后位置是14。在遍历到14时,记录长度15-9+1=7,start=15。 - 下一个字符是h,最后位置是19,遍历到19时记录长度5,start=20。依此类推。 这样得到的区间长度列表应该是[9,7,8],与原题示例一致。 因此,正确的代码实现应该基于这种方法。现在,我需要将其写成函数,并测试是否正确。 可能的实现步骤: 函数定义:def partition_labels(s: str) -> List[int]: 步骤: 1. 创建last_occurrence字典,记录每个字符的最后出现索引。 2. 遍历字符串,记录每个字符的最后位置。 3. 初始化start和end为0,结果列表为空。 4. 再次遍历字符串,对于每个字符i: a. 更新end为max(end, last_occurrence[s[i]]) b. 如果i == end,说明当前区间结束,记录长度,并更新start到i+1 5. 返回结果列表。 这样,代码会更高效,且不需要生成和合并区间列表。 现在,我需要检查用户提供的引用[2]中的代码是否和这个思路一致。用户提供的代码先生成每个字母的起始和结束位置,然后合并这些区间。而优化后的方法更高效,不需要处理所有26个字母,而是直接遍历字符串,因此更适合。 因此,正确的实现应该采用第二种方法,即两次遍历的方法,而不是生成区间再合并。这可能是在用户提供的引用[2]中的代码的一个优化点。 综上所述,我将编写一个函数partition_labels,采用两次遍历的方法,更高效地解决问题,并解释其步骤。</think>### 基于Python的字母区间划分实现 #### 一、算法原理 字母区间划分问题的核心在于找到每个字符的最后出现位置,通过**贪心算法**动态合并区间。该算法LeetCode 763题[^2]的解决方案一致,具体步骤为: 1. **记录字符最后位置**:遍历字符串,建立字符→最后索引的映射表 2. **动态划分区间**:二次遍历时维护区间边界,当遍历指针到达当前区间右边界时完成一个区间的划分 #### 二、代码实现 ```python def partition_labels(s): # 记录每个字符的最后出现位置 last_occurrence = {char: idx for idx, char in enumerate(s)} result = [] start = end = 0 for i, char in enumerate(s): end = max(end, last_occurrence[char]) if i == end: # 到达当前区间终点 result.append(end - start + 1) start = i + 1 # 重置区间起点 return result ``` #### 三、算法分析 1. **时间复杂度**:$O(n)$ - 两次线性遍历(记录最后位置+划分区间) - 比暴力合并区间方案($O(n log n)$)更高效 2. **空间复杂度**:$O(1)$ - 字符集大小固定(ASCII字符最多128个键值对) #### 四、测试验证 ```python print(partition_labels("ababcbacadefegdehijhklij")) # 输出: [9,7,8](对应"ababcbaca", "defegde", "hijhklij") print(partition_labels("abcda")) # 输出: [5](字符a跨越整个字符串) ``` #### 五、应用场景 1. 文本分段处理(如排引擎) 2. 基因序列分析(定位重复片段) 3. 数据压缩(识别重复模式) --- ### 相关问题 1. **如何优化大规模字符串的区间划分?** 可采用并行处理分块统计字符位置,再合并各块边界数据[^4] 2. **如何处理包含Unicode字符的字符串?** 改用字典记录字符时无需修改核心逻辑,Python 3默认支持Unicode字符串 3. **该算法能否用于非连续字符检测?** 可通过检查区间长度是否为1来判断孤立字符(如`partition_labels("abc") → [1,1,1]`) 4. **如何逆向还原划分后的子字符串?** 根据长度数组重建切片范围: ```python def reconstruct_partitions(s, lengths): partitions = [] pos = 0 for l in lengths: partitions.append(s[pos:pos+l]) pos += l return partitions ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值