剑指offer(六):查找算法

剑指offer(五):查找算法

题目一:数组中重复的数字

方法一:哈希表 / Set,自己也是这么出来的,但是没有答案简便
class Solution {
    public int findRepeatNumber(int[] nums) {
        Set<Integer> dic = new HashSet<>();
        for(int num : nums) {
            if(dic.contains(num)) return num;
            dic.add(num);
        }
        return -1;
    }
}
方法二:原地交换

遍历数组并通过交换操作,使元素的 索引 与 值 一一对应,就能通过索引映射对应的值,起到与字典等价的作用。

class Solution {
    public int findRepeatNumber(int[] nums) {
        int i = 0;
        while(i < nums.length) {
            //遍历到索引与对应的值相等是,跳过
            if(nums[i] == i) {
                i++;
                continue;
            }
            //如果遍历到某一个值时,下式相等时nums[i]处和索引i处的元素值都为nums[i],即找到一组重复值,返回此值
            if(nums[nums[i]] == nums[i]) return nums[i];
            int tmp = nums[i];
            nums[i] = nums[tmp];
            nums[tmp] = tmp;
        }
        return -1;
    }
}

题目二: 二维数组中的查找

在排好序的二维数组进行查找某一个数字,笨方法使用对每一行进行二分查找,时间复杂度为NlogN,相比于暴力方案的N^2要好不少;

class Solution {
    int[][] matrix;
    int n,target;
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        this.matrix = matrix;
        int m = matrix.length;
        if (m==0)return false;
        this.n = matrix[0].length;
        if (n==0)return false;
        this.target = target;
        for (int i = 0; i < m; i++) {
            if (matrix[i][n-1]<target)continue;
            else {
                //对每一行进行判断
                if (recursionBinarySearch(matrix[i],target,0,matrix[i].length-1)>=0)return true;
            }
        }
        return false;
    }
    //进行二分查找
    int recursionBinarySearch(int[] arr,int key,int low,int high){
        if(key < arr[low] || key > arr[high] || low > high){
            return -1;
        }
        int middle = (low + high) / 2;			//初始中间位置
        if(arr[middle] > key){
            //比关键字大则关键字在左区域
            return recursionBinarySearch(arr, key, low, middle - 1);
        }else if(arr[middle] < key){
            //比关键字小则关键字在右区域
            return recursionBinarySearch(arr, key, middle + 1, high);
        }else {
            return middle;
        }
    }
}
方法二:类似于树的访问方式,直呼高级

在这里插入图片描述

思路:从 “根节点” 开始搜索,遇到比 target 大的元素就向左,反之向右,即可找到目标值 target

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        int i = matrix.length - 1, j = 0;
        //i最多减小到0,j最多为行长度减一
        while(i >= 0 && j < matrix[0].length)
        {
            if(matrix[i][j] > target) i--;
            else if(matrix[i][j] < target) j++;
            else return true;
        }
        return false;
    }
}

题目三:旋转数组的最小数字

使用暴力法竟然跑通了,时间复杂度也挺好,只是这样做不合适,正确解答使用二分查找

方法一:暴力法
class Solution {
    public int minArray(int[] numbers) {
    if (numbers.length==0)return 0;
    int res=0;
    for (int i = 0; i < numbers.length-1; i++) {
        if (numbers[i+1]-numbers[i]<0){
            res = i+1;
            break;
        }
    }
    return numbers[res];
    }
}
方法二:二分查找、

关键点

  • 每次使用二分法的中点与右边界进行比较
  • 当比较值相同时的缩小区间安全性
    在这里插入图片描述
class Solution {
    public int minArray(int[] numbers) {
        int i = 0, j = numbers.length - 1;
        while (i < j) {
            int m = (i + j) / 2;
            if (numbers[m] > numbers[j]) i = m + 1;
            else if (numbers[m] < numbers[j]) j = m;
            else {
                int x = i;
                for(int k = i + 1; k < j; k++) {
                    if(numbers[k] < numbers[x]) x = k;
                }
                return numbers[x];
            }
        }
        return numbers[i];
    }
}

题目四: 第一个只出现一次的字符

方法一:使用两个set和一个字符数组
class Solution {
    public char firstUniqChar(String s) {
    char reschar = ' ';
    if (s.length()==0)return reschar;
    char[] tem = new char[s.length()];
    Set<Character> res = new HashSet<>();
    Set<Character> res1 = new HashSet<>();
    for (int i = 0; i < s.length(); i++) {
        char cur = s.charAt(i);
        if (res.contains(cur)){
            res1.add(cur);
            }
        res.add(cur);
        tem[i] = cur;
        }
        for (int j = 0; j < tem.length; j++) {
            if (!res1.contains(tem[j])){
                reschar = tem[j];
                break;
            }
        }
        return reschar;
    }
}
方法二:哈希表–>HashMap<Character, Boolean> 一个存字符,一个存是否出现一次,太精妙了

在这里插入图片描述

class Solution {
    public char firstUniqChar(String s) {
        HashMap<Character, Boolean> dic = new HashMap<>();
        char[] sc = s.toCharArray();
        for(char c : sc)
            dic.put(c, !dic.containsKey(c));
        for(char c : sc)
            if(dic.get(c)) return c;
        return ' ';
    }
}
方法三:有序哈希表–>LinkedHashMap

有序哈希表中的键值对是 按照插入顺序排序 的。基于此,可通过遍历有序哈希表,实现搜索首个 “数量为 1的字符”。

class Solution {
    public char firstUniqChar(String s) {
        //LinkedHashMap是有顺序的哈希表
        Map<Character, Boolean> dic = new LinkedHashMap<>();
        char[] sc = s.toCharArray();
        for(char c : sc)
            dic.put(c, !dic.containsKey(c));
        //哈希表的访问方式值得学习
        for(Map.Entry<Character, Boolean> d : dic.entrySet()){
           if(d.getValue()) return d.getKey();
        }
        return ' ';
    }
}

题目五:在排序数组中查找数字 I

使用二分查找的方式来操作来找到左右边界。关键怎么找到左右边界

class Solution {
    public int search(int[] nums, int target) {
        // 搜索右边界 right
        int i = 0, j = nums.length - 1;
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] <= target) i = m + 1;
            else j = m - 1;
        }
        int right = i;
        // 若数组中无 target ,则提前返回
        if(j >= 0 && nums[j] != target) return 0;
        // 搜索左边界 right
        i = 0; j = nums.length - 1;
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] < target) i = m + 1;
            else j = m - 1;
        }
        int left = j;
        return right - left - 1;
    }
}
//上述方法比较臃肿
class Solution {
    public int search(int[] nums, int target) {
        return helper(nums, target) - helper(nums, target - 1);
    }
    int helper(int[] nums, int tar) {
        int i = 0, j = nums.length - 1;
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] <= tar) i = m + 1;
            else j = m - 1;
        }
        return i;
    }
}

题目六:0~n-1 中缺失的数字

class Solution {
    public int missingNumber(int[] nums) {
        int i = 0, j = nums.length-1;
        while (i<=j){
            int m = (i+j)/2;
            if (nums[m]==m)i=m+1;
            else j=m-1;
        }
        return i;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值