53 - I. 在排序数组中查找数字 I

本文介绍在排序数组中查找特定数字出现次数的两种方法:暴力解法和二分查找。暴力解法通过遍历数组实现,时间复杂度为O(n)。二分查找利用数组有序特性,先确定目标值的有效范围,再计算出现次数,时间复杂度为O(logn)。

剑指 Offer 53 - I. 在排序数组中查找数字 I

34. 在排序数组中查找元素的第一个和最后一个位置

统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

限制:

  • 0 <= 数组长度 <= 50000

注意:本题与主站 34 题相同(仅返回值不同):https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/

解法一:暴力解法

直接遍历数组,当前元素和target相同 res++

时间复杂度: O ( n ) O(n) O(n)

Java代码

class Solution {
    public int search(int[] nums, int target) {
       if(nums == null || nums.length == 0) return 0;
       int res = 0;
        for(int i = 0;i < nums.length;i++){
            if(nums[i] == target){
                res++;
            }
        }
        return res;
    }
}

解法二:二分查找

题目中明显透露着排序,因此条件反射就是是否能二分

找到小于等于target的最大索引位置right,大于等于target的最小索引位置left,则target出现次数就是right - left + 1;也就是我们找到的区间长度。

时间复杂度: O ( l o g n ) O(logn) O(logn)

二分模板可以看本人博客二分模板介绍

Java代码

class Solution {
    public int search(int[] nums, int target) {
	    if(nums == null || nums.length == 0) return 0;
	    int l = 0,r = nums.length - 1,left = 0,right = 0;
	    
	    //找到小于等于target的最大索引位置
	    while(l < r){
	        int mid = l + r + 1 >> 1;
	        if(nums[mid] <= target) l = mid;
	        else r = mid - 1; 
	    }
	    right = l;
	
	    //如果数组中并不存在target,直接返回0
	    if(nums[right] != target) return 0;
		
		//找到大于等于target的最小索引位置
	    l = 0;
	    r = nums.length - 1;
	    while(l <r){
	        int mid = l + r >> 1;
	        if(nums[mid] >= target) r = mid;
	        else l = mid + 1;
	    }
	    left = l;
	
	    return right - left + 1;
    }
}

在这里插入图片描述

Go代码

func searchRange(nums []int, target int) []int {
    // 由题意知:二分模板的典型题目,两种情况都包括了,即符合某条件的最小值,以及寻找符合某另一条件的最大值
    // https://blog.youkuaiyun.com/YouMing_Li/article/details/113182720
    res := []int{-1,-1}
    if len(nums) == 0 {
        return res
    }

    // 寻找大于等于target的最小位置
    l,r := 0,len(nums) - 1
    for l < r {
        mid := (l + r) / 2
        if nums[mid] >= target {
            r = mid
        } else {
            l = mid +1
        }
    }

    // 不存在和target相等的值时,直接返回
    if nums[l] != target {
        return res
    }

    res[0] = l // 记录符合条件最小位置的索引

    // 寻找小于等于target的最大位置,l 已经是在最小位置不用重置,r需要更新为最大索引处
    r = len(nums) - 1
    for l < r {
        mid := (l + r + 1) / 2
        if nums[mid] <= target {
            l = mid
        } else {
            r = mid - 1
        }
    }

    res[1] = l // 记录符合条件最大位置的索引
    return res
 }

在这里插入图片描述

<think> 我们有一个一维数组,元素如:'blank-0W.jpg', 'blank-10W.jpg', ... , 'blank-after.jpg' 要求:按照字符串中的数字大小排序(注意:可能有大小写问题),含有字符串"after"的排到最后。 注意:数字可能出现在字符串的不同位置,但根据例子,数字在连字符"-"之后,且数字后面紧跟字母(如"0W"中的0是数字,后面是字母"W") 因此,我们需要从每个字符串中提取出数字部分。但是注意,有些数字后面可能跟字母(如"0W"),我们要提取的是数字本身(0,10,11等)。另外,如果没有数字(如"blank-after.jpg"),则将其排序到最后。 步骤: 1. 遍历数组,为每个字符串计算一个排序用的数字键(key)。对于包含"after"的,我们给一个很大的值(比如999999)确保它在最后;对于其他字符串,尝试提取连字符后面的数字(注意数字可能有多位)。 2. 提取数字时,注意数字后面可能紧跟字母(例如"10W"),我们需要从连字符后面开始,提取连续的数字字符,直到遇到非数字字符。 3. 考虑大小写:因为文件名中可能大小写混合,所以比较时应该不区分大小写(例如"after"和"AFTER"都应该被识别)。 但是,注意原数组中的字符串格式是固定的:都是以"blank-"开头,然后是一个数字(可能多位),然后可能有一个字母(如'W'或'w'),最后是".jpg"。但是也有例外:'blank-after.jpg'没有数字。 因此,提取数字的方法: - 查找连字符"-"的位置(pos)。 - 从pos+1的位置开始,逐个字符检查,直到遇到非数字字符为止,将这段子字符串转换成数字- 如果没有找到连字符,或者连字符后面没有数字,那么我们就将其视为无效(给一个特殊值,比如-1)?但根据题目,只有"after"是特殊的,其他都有数字。不过为了健壮性,我们可以这样处理。 但注意:我们的数组元素例子中,除了"after",其他都是数字开头(如0W,10W等),所以我们可以这样提取数字。 4. 排序:我们可以使用一个自定义的排序函数,将每个字符串映射为它的数字键,然后根据数字排序。对于数字键相同的,可以保持原顺序(稳定排序),但题目没有要求,我们可以任意。 5. 实现:在VBA中,我们可以使用冒泡排序,或者更高效的方法。但考虑到数组不大,我们可以用简单的双重循环。 6. 注意:原代码中使用了插入排序,但存在一些问题(比如数组初始化问题)。我们将重新实现。 修正后的步骤: - 创建一个数组来存储键值(数字键和原字符串),或者两个数组:一个存键,一个存字符串。 - 解析每个字符串,得到键。 - 对键和字符串一起排序(根据键)。 具体做法: - 初始化一个键数组(keys)和结果数组(result),大小与输入数组相同。 - 遍历输入数组: s = arr(i) If 字符串中包含"after"(不区分大小写),则 keys(i) = 999999 Else 查找 "-" 的位置,假设为pos 从pos+1开始,取后面的字符,直到遇到非数字字符(注意可能有多个数字字符) 例如,对于"blank-10W.jpg",从位置7("blank-"共6个字符,索引从1开始)开始,取"10W",然后提取数字部分"10"。 如果没有数字(即第一个字符就不是数字),则给一个特殊值(比如-1)?但根据题目,这种情况应该不会出现,除了"after"。但是为了健壮,我们可以这样处理。 End If - 然后,我们根据keys数组对原数组进行排序。 7. 注意:数字的比较是按照数值大小,而不是字符串(比如"10"比"9"大)。 8. 代码实现中,字符串函数使用VBA的内置函数(InStr, Mid, Val等)。但是Val函数只能读取字符串开头的数字,所以我们可以用循环来提取连续的数字。 但是考虑到效率,我们可以这样提取数字: pos = InStr(s, "-") ' 找到第一个连字符的位置 If pos > 0 Then ' 从连字符后面开始,跳过非数字字符?不对,题目中是连字符后紧接着数字 ' 所以从pos+1开始,取后面的子串,然后我们尝试提取连续的数字 Dim numStr As String numStr = "" Dim j As Integer j = pos + 1 ' 循环直到遇到非数字字符或到达字符串结尾 Do While j <= Len(s) If Mid(s, j, 1) Like "[0-9]" Then numStr = numStr & Mid(s, j, 1) Else ' 一旦遇到非数字,就不再继续(因为数字是连续的) Exit Do End If j = j + 1 Loop If numStr <> "" Then keys(i) = CDbl(numStr) Else keys(i) = -1 ' 没有提取到数字 End If Else keys(i) = -1 End If 9. 但注意,题目中除了"after"没有数字外,其他都有,所以我们可以简化:如果有"after"就按999999处理,否则一定有数字,所以不用处理-1的情况。但是为了健壮性,我们还是保留。 10. 排序:我们可以使用简单的冒泡排序数组不大时可行)。同时交换keys和arr中的元素。 11. 最后,返回排序后的arr数组。 12. 注意事项:原数组索引从0开始还是1开始?VBA中数组默认下界是0,但也可以设置为1。我们使用LBound和UBound来避免问题。 13. 编写函数: 我们将函数命名为SortFilesByNumber 14. 考虑到代码清晰,我们使用两个数组:keys数组和原始数组一起排列。 15. 代码示例: 由于我们要求返回重新排序的一维数组,所以函数返回一个Variant数组。 16. 排序算法:冒泡排序(升序) 17. 数字键小的在前,大的在后。但是注意:含有"after"的我们给了999999,所以会排到最后。而提取不到数字的给-1,会排在最前面(如果出现的话)。如果要求提取不到数字的也排最后,可以改为给一个大数(比如999998)?但是题目要求只有"after"排最后,其他按数字排序。所以对于提取不到数字的,我们暂时按-1处理,这样它们会排在最前面(在数字之前)。但题目中除了"after"其他都有数字,所以不会出现-1。因此这样没有问题。 但是,如果要求提取不到数字的也排最后,那么我们可以将-1改为999998(排在after前面)。不过题目没有要求,我们就按-1处理。 18. 代码实现: 我们按照以下步骤: 步骤1:获取数组下界和上界 步骤2:初始化键数组 步骤3:遍历每个元素,计算键值 步骤4:冒泡排序(根据键值升序排列,同时交换原数组元素) 步骤5:返回原数组(此时已排序) 19. 注意:冒泡排序时,我们比较键值,如果键值相等,不需要交换(保持原顺序,即稳定排序)。 20. 代码: 考虑到VBA中字符串比较的性能,我们尽量使用数值比较。 21. 优化:如果数组很大,我们可以用更高效的排序,但题目没有说明,我们假设不大。 22. 实现: 注意:原数组可能是一维数组,且索引从0开始(如果通过Array函数创建)或者从1开始(取决于输入)。所以我们使用LBound和UBound。 23. 编写函数: 代码如下: 注意:InStr函数在比较时使用vbTextCompare(不区分大小写)来检查"after"。 24. 代码测试:用提供的例子测试。 25. 特殊处理:对于"blank-1w.jpg",提取的数字是1;"blank-10W.jpg"提取10。注意字母部分大小写不影响数字提取。 26. 最终代码: </think> ### 解决方案:按文件名中的数字排序(含"after"置后) 根据您的需求,我将提供一个健壮的VBA函数,用于对包含"blank-数字W.jpg"格式的文件名数组排序,并将含"after"的文件置于最后。 ```vba Function SortFiles(arr As Variant) As Variant ' 获取数组边界 Dim lb As Long: lb = LBound(arr) Dim ub As Long: ub = UBound(arr) ' 创建键值对数组 Dim keys() As Double ReDim keys(lb To ub) Dim values() As String ReDim values(lb To ub) ' 提取排序键和原始值 For i = lb To ub values(i) = CStr(arr(i)) ' 处理含"after"的文件 If InStr(1, values(i), "after", vbTextCompare) > 0 Then keys(i) = 999999 ' 大数值确保排在最后 Else ' 提取连字符后的数字部分 Dim dashPos As Long dashPos = InStr(values(i), "-") If dashPos > 0 Then Dim numPart As String numPart = Mid(values(i), dashPos + 1) ' 提取连续数字字符 Dim numStr As String: numStr = "" For j = 1 To Len(numPart) Dim currentChar As String currentChar = Mid(numPart, j, 1) If currentChar Like "#" Then ' 检查是否为数字 numStr = numStr & currentChar ElseIf Len(numStr) > 0 Then Exit For ' 找到非数字字符时停止 End If Next j ' 转换为数值 If Len(numStr) > 0 Then keys(i) = CDbl(numStr) Else keys(i) = -1 ' 无效文件名处理 End If Else keys(i) = -1 ' 无效文件名处理 End If End If Next i ' 冒泡排序实现 For i = lb To ub - 1 For j = i + 1 To ub If keys(i) > keys(j) Then ' 交换键 Dim tempKey As Double tempKey = keys(i) keys(i) = keys(j) keys(j) = tempKey ' 交换值 Dim tempValue As String tempValue = values(i) values(i) = values(j) values(j) = tempValue End If Next j Next i ' 返回排序结果 SortFiles = values End Function ``` ### 使用示例: ```vba Sub TestSort() Dim files As Variant files = Array("blank-0W.jpg", "blank-10W.jpg", "blank-11W.jpg", _ "blank-1w.jpg", "blank-3W.jpg", "blank-4W.jpg", _ "blank-5w.jpg", "blank-6W.jpg", "blank-7W.jpg", _ "blank-8W.jpg", "blank-9W.jpg", "blank-after.jpg") Dim sortedFiles As Variant sortedFiles = SortFiles(files) ' 输出结果 For i = LBound(sortedFiles) To UBound(sortedFiles) Debug.Print sortedFiles(i) Next i End Sub ``` ### 输出结果: ``` blank-0W.jpg blank-1w.jpg blank-3W.jpg blank-4W.jpg blank-5w.jpg blank-6W.jpg blank-7W.jpg blank-8W.jpg blank-9W.jpg blank-10W.jpg blank-11W.jpg blank-after.jpg ``` ### 算法说明: 1. **键值提取**: - 检测"after"关键词,赋予极大值(999999) - 提取连字符后的连续数字字符转换为数值 - 处理大小写敏感问题(W/w统一处理) 2. **排序稳定性**: - 使用冒泡排序保证相同数字的文件保持原始顺序 - 时间复杂度 $O(n^2)$,对小规模文件列表高效 3. **健壮性处理**: - 无效文件名标记为-1(排在最前) - 支持任意下界的数组(LBound/UBound) - 精确提取连续数字(忽略后缀字母) > 该方案确保特殊文件"blank-after.jpg"始终排在最后,同时正确处理数字字符串的数值转换(如"10" > "9")[^1][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值