查找算法(中等)【剑指offer】

剑指 Offer 04. 二维数组中的查找

解题思路1:遍历二分查找

class Solution:
    def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        n = len(matrix)
        if n == 0: return False
        m = len(matrix[0])
        if m == 0: return False
        for i in range(n):
            res = matrix[i]
            left, right = 0, m-1
            while left <= right:
                mid = (left+right)//2
                if res[mid] == target: return True
                elif res[mid] > target: right = mid - 1
                else : left = mid + 1
        return False    

解题思路2:

 若使用暴力法遍历矩阵 matrix ,则时间复杂度为 O(NM)。暴力法未利用矩阵 “从上到下递增、从左到右递增” 的特点,显然不是最优解法。
 如下图所示在这里插入图片描述
我们将矩阵逆时针旋转 45° ,并将其转化为图形式,发现其类似于 二叉搜索树 ,即对于每个元素,其左分支元素更小、右分支元素更大。因此,通过从 “根节点” 开始搜索,遇到比 target 大的元素就向左,反之向右,即可找到目标值 target 。
 “根节点” 对应的是矩阵的 “左下角” 和 “右上角” 元素,本文称之为 标志数 ,以 matrix 中的 左下角元素 为标志数 flag ,则有:
  1.若 flag > target ,则 target 一定在 flag 所在行的上方 ,即 flag 所在行可被消去
  2.若 flag < target ,则 target 一定在 flag 所在列的右方 ,即 flag 所在列可被消去

算法流程:
1、从矩阵 matrix 左下角元素(索引设为 (i, j) )开始遍历,并与目标值对比:
  当 matrix[ i ][ j ] > target 时,执行 i-- ,即消去第 i 行元素;
  当 matrix[ i ] [j ] < target 时,执行 j++ ,即消去第 j 列元素;
  当 matrix[ i ][ j ] = target 时,返回 true ,代表找到目标值。
2、若行索引或列索引越界,则代表矩阵中无目标值,返回 false
(每轮 i 或 j 移动后,相当于生成了“消去一行(列)的新矩阵”, 索引(i,j) 指向新矩阵的左下角元素(标志数),因此可重复使用以上性质消去行(列))
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution:
    def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        i, j = len(matrix) - 1, 0
        while i >= 0 and j < len(matrix[0]):
            if matrix[i][j] == target: return True
            elif matrix[i][j] > target: i -= 1
            else: j += 1
        return False

剑指 Offer 11. 旋转数组的最小数字

解题思路1:最简单容易想到的思路:历遍

class Solution:
    def minArray(self, numbers: List[int]) -> int:
        i = 0
        while i < len(numbers) - 1:
            if numbers[i] > numbers[i+1]: return numbers[i+1]
            else: i += 1
        return numbers[0]

解题思路2:二分查找

如下图所示,寻找旋转数组的最小元素即为寻找右排序数组的首个元素 nums[x] ,称 x 为 旋转点 。
在这里插入图片描述

算法流程:

1.初始化: 声明 i, j 双指针分别指向 nums 数组左右两端
。。。。。。。。。。。。。。。。。。。。。。。。。
2.循环二分: 设 m = (i + j) / 2 为每次二分的中点( “/” 代表向下取整除法,因此恒有 i ≤ m < j ),可分为以下三种情况:
  当 nums[m] > nums[j]时: m 一定在左排序数组中,即旋转点 x 一定在 [m + 1, j] 闭区间内,因此执行 i = m + 1
  当 nums[m] < nums[j] 时: m 一定在右排序数组中,即旋转点 x 一定在[i, m] 闭区间内,因此执行 j = m
  当 nums[m] = nums[j] 时: 无法判断 mm 在哪个排序数组中,即无法判断旋转点 x 在 [i, m] 还是 [m + 1, j] 区间中。解决方案: 执行 j = j - 1 缩小判断范围
。。。。。。。。。。。。。。。。。。。。。。。。
3.返回值: 当 i = j 时跳出二分循环,并返回旋转点的值 nums[i]

注:左端点往右区间第一位即旋转点靠,右端点往右区间最左侧靠即也是往旋转点靠,最终两个端点重合,循环停止

class Solution:
    def minArray(self, numbers: List[int]) -> int:
        left, right = 0, len(numbers)-1
        while left < right:
            m = left + (right-left)//2
            if numbers[m] > numbers[right]:
                left = m + 1
            elif numbers[m] < numbers[right]:
                right = m
            else:
                right -= 1
        return numbers[left]

剑指 Offer 50. 第一个只出现一次的字符

解题思路:哈希表

1.遍历字符串 s ,使用哈希表统计各字符数量是否 > 1
2.再遍历字符串 s ,在哈希表中找到首个数量为 1 的字符并返回

算法流程:

1.初始化,字典记为dic
。。。。。。。。。。。。。。。。。。。。。。。。。。
2.字符统计: 遍历字符串 s 中的每个字符 i:
 a.若 dic 中不包含键(key) i :则向 dic 中添加键值对 (i, True) ,代表字符 i 的数量为 1
 b.若 dic 中包含键(key) i :则修改键 i 的键值对为 (i, False) ,代表字符 i 的数量 > 1
。。。。。。。。。。。。。。。。。。。。。。。。。。
3.查找数量为 1 的字符: 遍历字符串 s 中的每个字符 i:
若 dic中键 i 对应的值为 True ,则返回 i
4.若算法没结束,返回 ’ ’ ,代表字符串无数量为 1 的字符

class Solution:
    def firstUniqChar(self, s: str) -> str:
        dic = {}
        for i in s:
            dic[i] = i not in dic
        for j in s:
            if dic[j]:
                return j
        return ' '
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值