《剑指Offer》搜索算法题篇——更易理解的思路~

本篇若要说难点,就是时间复杂度的把控,还有什么样的解法容易让大家看懂理解


JZ53 数字在升序数组中出现的次数

描述

给定一个长度为 n 的非降序数组和一个非负数整数 k ,要求统计 k 在数组中出现的次数

数据范围:0 \le n \le 1000 , 0 \le k \le 1000≤n≤1000,0≤k≤100,数组中每个元素的值满足 0 \le val \le 1000≤val≤100
要求:空间复杂度 O(1),时间复杂度 O(logn)

思路(二分法):由于所需时间复杂度为O(logn),所以可以先通过二分法找到所给的k值,并拿到k值的下标,因为是有序数组,所以由此展开——用left和right分别向左和向右遍历,若与k值相等,则给计数器+1,最后返回计数器即可;

    //计数器
    private int count = 0;
    public int GetNumberOfK(int [] array , int k) {
        //由于时间复杂度为O(logn),所以要利用二分查找先找到这个数
        int len = array.length;
        int left = 0;
        int right = len - 1;
        int keyIndex = binarySearch(array, left, right, k);
        if(count == -1){
            return 0;
        }
        //因为有序,所以分别向两边扩展找相同的元素,找到了,计数就加1
        //左扩展
        left = keyIndex - 1;
        while(left >= 0 && array[left] == k){
            count++;
            left--;
        }
        //右扩展
        right = keyIndex + 1;
        while(right < len && array[right] == k){
            count++;
            right++;
        }
        return count;
    }
    //二分查找
    private int binarySearch(int[] array, int left, int right, int k){
        while(left <= right){
            int mid = (left + right) / 2;
            if(k < array[mid]){
                right = mid - 1;
            }else if(array[mid] < k){
                left = mid + 1;
            }else{
                count++;
                return mid;
            }
        }
        return -1;
    }

JZ4 二维数组中的查找

描述

在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

[

[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]

]

给定 target = 7,返回 true。

给定 target = 3,返回 false。

数据范围:矩阵的长宽满足 0 \le n,m \le 5000≤n,m≤500 , 矩阵中的值满足 0 \le val \le 10^90≤val≤109
进阶:空间复杂度 O(1),时间复杂度 O(n+m)

思路(这题其实非常简单,但此题的标出的难度却是中等,通过率只有百分之二十几?想必大多数人都是因为时间复杂度超过了O(n + m)):为什么简单?二维数组,对每一行进行二分查找就ok~

    public boolean Find(int target, int [][] array) {
        //每一行进行二分查找
        for(int i = 0; i < array.length; i++){
            boolean flay = binarySearch(target, array[i]);
            if(flay){
                return true;
            }
        }
        return false;
    }
    public boolean binarySearch(int target, int[] array){
        int left = 0;
        int right = array.length - 1;
        while(left <= right){
            int mid = (left + right) / 2;
            if(target < array[mid]){
                right = mid - 1;
            }else if(target > array[mid]){
                left = mid + 1;
            }else{
                return true;
            }
        }
        return false;
    }

JZ11 旋转数组的最小数字

描述

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

数据范围:1 \le n \le 100001≤n≤10000,数组中任意元素的值: 0 \le val \le 100000≤val≤10000

要求:空间复杂度:O(1) ,时间复杂度:O(logn)

思路(二分法):因为数组为非降序数组,所以可以通过二分法,进行循环即可,但要注意以下情况:当mid指向元素大于right指向元素时,说明目标值一定在mid指向的右边;当mid指向元素小于right指向元素时,说明一定在mid指向的左边;若mid指向元素等于right指向元素时,不确定,可以用right缩小范围继续比较;

    public int minNumberInRotateArray(int [] array) {
        int left = 0;
        int right = array.length - 1;
        while(left < right){
            int mid = (left + right) / 2;
            //若中间值大于最右边,说明目标在中间值右边
            if(array[mid] > array[right]){
                left = mid + 1;
            }
            //此时不确定,比如 1 1 1 0 1,所以继续缩小范围
            else if(array[mid] == array[right]){
                right--;
            }
            //不在右边就在左边
            else{
                right = mid;
            }
        }
        return array[left];
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈亦康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值