Leetcode之查找(一)

目录

1.search-insert-position

2.search-for-a-range

3.search-in-rotated-sorted-array

4.search-in-rotated-sorted-array-ii

5.word-search

6.combinations

7.combination-sum

8.combination-sum-ii


1.search-insert-position

题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。下面给出几个样例:[1,3,5,6], 5 → 2;[1,3,5,6], 2 → 1;[1,3,5,6], 7 → 4;[1,3,5,6], 0 → 0

分析:二分查找。注意while循环中“<=”以及是return low还是return  high,当low == high时,如果目标值比中间值更大,low会赋值为mid + 1,刚好是目标值该插入的位置;如果目标值比中间值更小,此时改变的是high值而low值不变,low刚好是目标值该插入的位置,所以应该返回low。

    public int searchInsert(int[] A, int target) {
        int low = 0,high = A.length - 1;
        while(low <= high){
            int mid = (low + high) >> 1;
            if(A[mid] == target)
                return mid;
            else if(target > A[mid])
                low = mid + 1;
            else
                high = mid - 1;
        }
        return low;
    }

2.search-for-a-range

题目:给出一个有序数组,请在数组中找出目标值的起始位置和结束位置,你的算法的时间复杂度应该在O(log n)之内,如果数组中不存在目标,返回[-1, -1].例如:给出的数组是[5, 7, 7, 8, 8, 10],目标值是8,返回[3, 4].

分析:先用二分查找找到其中一个目标值的位置,然后再往两边扩大范围即可。

   public int[] searchRange(int[] A, int target) {
        int[] range = new int[2];
        int low = 0,high = A.length - 1,mid = 0;
        while(low <= high){
            mid = (low + high) >> 1;
            if(A[mid] == target)
                break;
            if(A[mid] > target)
                high = mid - 1;
            else
                low = mid + 1;
        }
        if(low > high){//没有目标值
            range[0] = -1;range[1] = -1;
        }
        else{
            range[0] = mid;range[1] = mid;
            while(range[0] > 0 && A[range[0]] == A[range[0]-1])
                range[0]--;
            while(range[1] < A.length - 1 && A[range[1]] == A[range[1]+1])
                range[1]++;
        }
        return range;
    }

3.search-in-rotated-sorted-array

题目:给出一个转动过的有序数组,你事先不知道该数组转动了多少(例如,0 1 2 4 5 6 7可能变为4 5 6 7 0 1 2).在数组中搜索给出的目标值,如果能在数组中找到,返回它的索引,否则返回-1。假设数组中不存在重复项。

分析:因为数组中不存在重复项,将数组从中间分开必定有一边是有序的,我们可以根据目标值是否在有序的那边,来判断目标值在哪边,从而将原问题规模缩小成原来的一半,时间复杂度为O(logn).

    public int search(int[] nums, int target) {
        int low = 0,high = nums.length - 1;
        while(low <= high){
            int mid = (low + high) >> 1;
            if(nums[mid] == target)
                return mid;
            //前半部分有序
            // 注意'=',虽然没有重复元素,但有可能mid和low为同一个索引
            if(nums[mid] >= nums[low]){
                if(nums[low] <= target && nums[mid] > target)
                    high = mid - 1;
                else
                    low = mid + 1;
            }
            else{//后半部分有序
                if(nums[mid] < target && nums[high] >= target)
                    low = mid + 1;
                else
                    high = mid - 1;
            }
        }
        return -1;
    }

4.search-in-rotated-sorted-array-ii

题目:继续思考题目 "Search in Rotated Sorted Array":如果数组种允许有重复元素怎么办?会影响时间复杂度吗?是怎样影响时间复杂度的,为什么?编写一个函数判断给定目标值是否在数组中。

分析:数组中允许有重复元素,对于a[low] == a[mid]的情况,如数组[1,3,1,1,1]中查找目标值3,在这种情况下,是无法判断目标值在坐半边还是右半边,只能low++去掉一个干预项,在最差的情况下时间复杂度会退化成O(n).

类似题找旋转数组中的最小数字见剑指offer面试题11 https://blog.csdn.net/Nibaby9/article/details/104126765

   public int search(int[] A, int target) {
        if(A.length == 0)
            return -1;
        int low = 0,high = A.length - 1;
        while(low <= high){
            int mid = (low + high) / 2;
            if(A[mid] == target)
                return mid;
            if(A[mid] > A[low]){//左半部分有序
                if(target < A[mid] && target >= A[low])
                    high = mid - 1;
                else
                    low = mid + 1;
            }
            else if(A[mid] < A[low]){//右半部分有序
                if(target > A[mid] && target <= A[high])
                    low = mid + 1;
                else
                    high = mid - 1;
            }
            else
                low++;
        }
        return -1;
    }

5.word-search

题目:给出一个二维字符数组和一个单词,判断单词是否在数组中出现,单词由相邻单元格的字母连接而成,相邻单元指的是上下左右相邻。同一单元格的字母不能多次使用。例如:给出的字符数组=[↵ ["ABCE"],↵ ["SFCS"],↵ ["ADEE"]↵]单词 ="ABCCED", -> 返回 true,单词 ="SEE", ->返回 true,单词 ="ABCB", -> 返回 false.

分析:考查深度优先搜索和回溯。首先遍历 board 的所有元素,找到和 word 第一个字母相同的元素,然后从该位置开始深度遍历,依次看它的上下左右是否符合下一个元素的匹配。注意同一单元格的字母不能多次使用,所以我们需要对访问过的元素进行标记,另外还需要注意是否越边界。

    public boolean exist(char[][] board, String word) {
       if(board.length == 0)
            return false;
        if(word.length() == 0)
            return true;
        int row = board.length,col = board[0].length;
        boolean[][] visited = new boolean[row][col];
        //依次从每个点开始深度遍历
        for(int i = 0;i < row;i++){
            for(int j = 0;j < col;j++){
                if(board[i][j] == word.charAt(0))
                    if(DFS(board,word,i,j,1,visited))
                        return true;
            }
        }
        return false;
    }

    private boolean DFS(char[][] board, String word, int i, int j, int index, boolean[][] visited) {
        if(index == word.length())
            return true;
        visited[i][j] = true;//试探
        //往左
        if(i-1 >= 0 && !visited[i-1][j] && board[i-1][j] == word.charAt(index)){
            if(DFS(board,word,i-1,j,index+1,visited))
                return true;
        }
        //往右
        if(i+1 < board.length && !visited[i+1][j] && board[i+1][j] == word.charAt(index)){
            if(DFS(board,word,i+1,j,index+1,visited))
                return true;
        }
        //往上
        if(j-1 >= 0 && !visited[i][j-1] && board[i][j-1] == word.charAt(index)){
            if(DFS(board,word,i,j-1,index+1,visited))
                return true;
        }
        //往下
        if(j+1 < board[0].length && !visited[i][j+1] && board[i][j+1] == word.charAt(index)){
            if(DFS(board,word,i,j+1,index+1,visited))
                return true;
        }
        visited[i][j] = false;//回溯
        return false;
    }

6.combinations

题目:给出两个整数n和k,返回从1到n中取k个数字的所有可能的组合。例如:如果n=4,k=2,结果为[↵ [2,4],↵ [3,4],↵ [2,3],↵ [1,2],↵ [1,3],↵ [1,4],↵]

分析:回溯法。每次只考虑第n个数字取还是不取,如果取就把问题转化为从1到n-1中取k-1个数字,若不取则从1到n-1中取k个数字。所以只要遍历n到1,依次考虑是否取,然后当取0个数字时,就得到了其中一种组合,另外,取的数量不可能大于数字量本身,可根据这个进行剪枝。

    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> cur = new ArrayList<>();
        backtrack(result,cur,n,k);
        return result;
    }

    private void backtrack(List<List<Integer>> result, List<Integer> cur, int n, int k) {
        if(k == 0){
            result.add(new ArrayList<>(cur));
            return;
        }
        if(n < k)//剪枝
            return;
        for(int i = n;i >= 1;i--){
            cur.add(i);
            backtrack(result,cur,i-1,k-1);
            cur.remove(cur.size() - 1);
        }
    }

7.combination-sum

题目:给出一组候选数C和一个目标数T,找出候选数中加起来和等于T的所有组合。C中的数字在组合中可以被无限次使用。注意:题目中所有的数字(包括目标数T)都是正整数;你给出的组合中的数字 (a 1, a 2, … , a k) 要按非递增排序 (ie, a 1 ≤ a 2 ≤ … ≤ a k);结解集中不能包含重复的组合。例如:给定的候选数集是[2,3,6,7],目标数是7,解集是:[7],[2, 2, 3]

分析:回溯法。依次考虑候选数中的每个数是否加入到解集中,这样就把问题转化成了另一个子问题了,因为候选数中允许多次选择,所以递归还是考虑是否加入自己本身。另外,如果候选数中的数已经排好序,会方便我们剪枝,如果当前数已经大于目标数,再加入更大的数也不可能得到目标数。

   public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        if(candidates.length == 0)
            return result;
        List<Integer> cur = new ArrayList<>();
        Arrays.sort(candidates);
        trackback(candidates,target,result,cur,0);
        return result;
    }

    private void trackback(int[] candidates, int target, List<List<Integer>> result, List<Integer> cur,int index) {
        if(target == 0){
            result.add(new ArrayList<>(cur));
            return;
        }
        if(index == candidates.length || candidates[index] > target)//剪枝
            return;
        for(int i = index;i < candidates.length;i++){
            cur.add(candidates[i]);//试探
            trackback(candidates,target - candidates[i],result,cur,i);
            cur.remove(cur.size() - 1);//回溯
        }
    }

8.combination-sum-ii

题目:给出一组候选数C和一个目标数T,找出候选数中起来和等于T的所有组合。C中的每个数字在一个组合中只能使用一次。注意:题目中所有的数字(包括目标数T)都是正整数;组合中的数字 (a 1, a 2, … , a k) 要按非递增排序 (ie, a 1 ≤ a 2 ≤ … ≤ a k);结果中不能包含重复的组合。例如:给定的候选数集是[10,1,2,7,6,1,5],目标数是8,解集是:[1, 7],[1, 2, 5],[2, 6],[1, 1, 6]

分析:本题中候选数中每个数字只能使用一次,而且候选数集中可能会有重复的数字,所以我们只要在上题的基础上修改递归(递归考虑下一个数是否加入)和去重即可。

   public ArrayList<ArrayList<Integer>> combinationSum2(int[] num, int target) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<>();
        ArrayList<Integer> list = new ArrayList<>();
        Arrays.sort(num);
        combinationSum(num, result, list, 0, target);
        return result;
    }

    private void combinationSum(int[] num, ArrayList<ArrayList<Integer>> result, ArrayList<Integer> list, int index, int target) {
        if(target == 0){
            result.add(new ArrayList<>(list));
            return;
        }
        if(target < 0)
            return;
        for(int i = index;i < num.length;i++){
            if (i > index && num[i] == num[i - 1]) continue; // 去重
            list.add(num[i]);
            combinationSum(num,result,list,i+1,target - num[i]);
            list.remove(list.size()-1);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值