阅读 94

程序员必备小知识:一文带你攻克搜索算法题目

1.二维数组中的查找

描述

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

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

给定 target = 7,返回 true。 
给定 target = 3,返回 false。 
数据范围:矩阵的长宽满足 0≤n,m≤5000,矩阵中的值满足 0≤val≤10^9
进阶:空间复杂度 O(1) ,时间复杂度 O(n+m)

示例1

输入:

7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]] 复制代码

返回值:

true 复制代码

说明:

存在7,返回true    复制代码

示例2

输入:

1,[[2]] 复制代码

返回值:

false 复制代码

示例3

输入:

3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]] 复制代码

返回值:

false 复制代码

说明:

不存在3,返回false    复制代码

题解1

暴力二分搜索:

针对于n行,每一行采用二分查找去寻找target,某一行找到直接返回true,如果都找不到,则返回false;

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

空间复杂度:O(1)

public class Solution {     public boolean Find(int target, int [][] array) {         if(array.length == 0){             return false;         }         for(int[] arr : array){             if(arr.length == 0){                 return false;             }             if(arr[0] <= target){                 boolean ans = binarySearch(target,arr);                 if(ans == true){                     return true;                 }             }         }         return false;     }          public boolean binarySearch(int target, int[] arr){         int left = 0;         int right = arr.length - 1;         while(left <= right){             int mid = left + (right-left)/2;             if(arr[mid] > target){                 right = mid - 1;             }else if(arr[mid] < target){                 left = mid+1;             }else{                 return true;             }         }         return false;     } } 复制代码

题解2 从左下角搜索(右上角搜索也是同理)

利用该二维数组的性质:

  • 每一行都按照从左到右递增的顺序排序, 

  • 每一列都按照从上到下递增的顺序排序 

改变个说法,即对于左下角的值 leftDown,leftDown是该行最小的数,是该列最大的数
每次将 leftDown 和目标值 target 比较:

  1. 当 leftDown < target,由于 m 已经是该行最大的元素,想要更大只有从列考虑,取值右移一位 

  2. 当 leftDown > target,由于 m 已经是该列最小的元素,想要更小只有从行考虑,取值上移一位 

  3. 当 leftDown = target,找到该值,返回 true 

用某行最小或某列最大与 target 比较,每次可剔除一整行或一整列

public class Solution {     public boolean Find(int target, int [][] array) {         int rows = array.length;         if(rows == 0){             return false;         }         int cols = array[0].length;         if(cols == 0){             return false;         }         int i = rows-1;         int j = 0;         while(i>=0 && j<=cols-1){             int leftDown = array[i][j]; // 左下角的值             if(leftDown > target){                 i -= 1;             }else if(leftDown < target){                 j += 1;             }else{                 return true;             }         }         return false;     } } 复制代码

总结

题解二主要是由于本题矩阵的特殊性,今后做题某步遇到搜索某值,可以像题解1一样用二分查找,复杂度O(logn)<线性查找O(n)。

2.旋转数组的最小数字

描述

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

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

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

示例1

输入:

[3,4,5,1,2] 复制代码

返回值:

1 复制代码

示例2

输入:

[3,100,200,3] 复制代码

返回值:

3 复制代码

题解

主要采用二分法,分以下几个情况:

  • [1,2,3,4,5] 依次递增,输出1

  • [3,4,5,1,2] 第一轮:low=0,high=4,mid=2指向5,5>2,low指针应右移至mid+1=3,指向1。第二轮,low=3,high=4,mid=3指向1,此时array[low=3] < array[high=4],令high=mid=3。第三轮,low=high,return array[low]=1。

  • [2,0,2,2,2] 第一轮:low=0,high=4,mid=2指向2,注意,这里特殊在array[mid]=array[high]=array[low],所以你就无法判断最小值究竟是在mid左还是右,我们做了一个low++ 的处理去缩小范围。第二轮,low=1,high=4,mid=2,此时注意,array[low]<array[high],所以直接return0。

import java.util.ArrayList; public class Solution {     public int minNumberInRotateArray(int [] array) {        int low = 0;        int high = array.length - 1;        while(low < high){            if(array[low] < array[high]){ // [1,2,3,4,5] 这种特殊情况                return array[low];            }            int mid = low + (high - low)/2;            if(array[mid] > array[high]){                low = mid + 1;            }else if (array[mid] < array[high]){                high = mid;            }else{                low++;            }        }        return array[low];     } } 复制代码

结束总结

基础的二分法模板如下,很多题都是在此基础上做得改动。

    public boolean binarySearch(int target, int[] arr){         int left = 0;         int right = arr.length - 1;         while(left <= right){             int mid = left + (right-left)/2;             if(arr[mid] > target){                 right = mid - 1;             }else if(arr[mid] < target){                 left = mid+1;             }else{                 return true;             }         }         return false;     }


作者:conghuhu
链接:https://juejin.cn/post/7019987346086952996


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐