算法总结之二分搜索

文章目录

    • 二分搜索常见算法题目
      • 局部最小值
      • 元素最左出现位置
      • 循环有序数组最小值
      • 最左原位
      • 完全二叉树计数
      • 快速N次方


二分搜索常见算法题目

局部最小值

定义局部最小的概念。arr长度为1时,arr[0]是局部最小。arr的长度为N(N>1)时,如果arr[0] 思路
(1)判断左右两端的值是否为局部最小值
(2)二分搜索,判断mid的左右两边,比mid小的一边一定存在局部最小值;否则返回mid

		public class Solution {
		    public int getLessIndex(int[] arr) {
		        if(arr==null||arr.length==0)
		            return -1;
		        int len = arr.length;
		        if(len==1||arr[0]<arr[1])
		            return 0;
		        if(arr[len-1]<arr[len-2])
		            return len-1;
		        int left = 1;
		        int right = len-2;
		        int mid = 0;
		        while(left<=right){
		            mid = (left+right)/2;
		            if(arr[mid]>arr[mid-1])
		                right = mid-1;
		            else if(arr[mid]>arr[mid+1])
		                left = mid+1;
		            else
		                return mid;
		        }
		        return -1;
		    }
		}

元素最左出现位置

对于一个有序数组arr,再给定一个整数num,请在arr中找到num这个数出现的最左边的位置。
给定一个数组arr及它的大小n,同时给定num。请返回所求位置。若该元素在数组中未出现,请返回-1。
测试样例:
[1,2,3,3,4],5,3
返回:2
思路:二分搜索,mid 位置的值把大于等于 num 的右边去掉 ,在剩余部分继续二分搜索,直到最后一个

		public class LeftMostAppearance {
		    public int findPos(int[] arr, int n, int num) {
		        int left = 0;
		        int right = n-1;
		        int res = -1;
		        while(left<=right){
		            int mid = (left+right)/2;
		            if(arr[mid]>num)
		                right = mid-1;
		            else if(arr[mid]<num)
		                left = mid+1;
		            else{
		                right = mid-1;
		                res = mid;
		            }
		        }
		        return res;
		    }
		}

循环有序数组最小值

对于一个有序循环数组arr,返回arr中的最小值。有序循环数组是指,有序数组左边任意长度的部分放到右边去,右边的部分拿到左边来。比如数组[1,2,3,3,4],是有序循环数组,[4,1,2,3,3]也是。
给定数组arr及它的大小n,请返回最小值。
测试样例:
[4,1,2,3,3],5
返回:1
思路
(1)判断首尾,如果 a[0] < a[n-1],因为有序所以返回 a[0]
(2)否则,二分搜索 mid ,a[0]>a[mid]则在mid左边二分搜索,a[0] (3)若 a[0] == a[mid] ,则需要遍历数组

		public class MinValue {
		    public int getMin(int[] arr, int n) {
		        if(arr==null||n==0)
		            return -1;
		        int left = 0;
		        int right = n-1;
		        int mid = 0;
		        if(n==1||arr[left]<arr[right])
		            return arr[left];
		        if(n==2)
		            return Math.min(arr[0],arr[1]);
		        while(left<right){
		            mid = (left+right)/2;
		            if(arr[left]>arr[mid]){
		                right = mid;
		            }else if(arr[left]<arr[mid]){
		                left = mid;
		            }else{
		                while(left<right){
		                    if(arr[left]==arr[mid]){
		                        left++;
		                    }else if(arr[left]<arr[mid]){
		                        return arr[left];
		                    }else{
		                        right = mid;
		                        break;
		                    }
		                }
		            }
		        }
		        return arr[left];
		    }
		}

最左原位

有一个有序数组arr,其中不含有重复元素,请找到满足arr[i]==i条件的最左的位置。如果所有位置上的数都不满足条件,返回-1。
给定有序数组arr及它的大小n,请返回所求值。
测试样例:
[-1,0,2,3],4
返回:2
思路
(1)先判断数组两端,因为是有序数组,如果arr[0]>0或arr[n-1] (2)二分搜索,判断arr[mid]与mid的大小关系,大于则左边二分搜索,小于则右边。
(3)当arr[mid]==mid时,接着二分搜索其左边。

		public class Find {
		    public int findPos(int[] arr, int n) {
		        if(arr==null||n==0)
		            return -1;
		        int left = 0;
		        int right = n-1;
		        int res = -1;
		        while(left<=right){
		            if(arr[left]>left||arr[right]<right){
		                break;
		            }
		            int mid = (left+right)/2;
		            if(arr[mid]>mid){
		                right = mid-1;
		            }else if(arr[mid]<mid){
		                left = mid+1;
		            }else{
		                res = mid;
		                right = mid-1;
		            }
		        }
		        return res;
		    }
		}

完全二叉树计数

给定一棵完全二叉树的根节点root,返回这棵树的节点个数。如果完全二叉树的节点数为N,请实现时间复杂度低于O(N)的解法。
给定树的根结点root,请返回树的大小。

		/*
		public class TreeNode {
		    int val = 0;
		    TreeNode left = null;
		    TreeNode right = null;
		    public TreeNode(int val) {
		        this.val = val;
		    }
		}*/
		public class CountNodes {
		    public int count(TreeNode root) {
		        int res = 0;
		        if(root == null)
		            return 0;
		        int left = getDepth(root.left);
		        int right = getDepth(root.right);
		        if(left==right){
		            res = (int)Math.pow(2,left);
		            res = res + count(root.right);
		        }else{
		            res = (int)Math.pow(2,right);
		            res = res + count(root.left);
		        }
		        return res;
		    }
		    //获取深度
		    private int getDepth(TreeNode tree){
		        int temp = 0;
		        while(tree!=null){
		            temp++;
		            tree = tree.left;
		        }
		        return temp;
		    }
		}

快速N次方

如果更快的求一个整数k的n次方。如果两个整数相乘并得到结果的时间复杂度为O(1),得到整数k的N次方的过程请实现时间复杂度为O(logN)的方法。
给定k和n,请返回k的n次方,为了防止溢出,请返回结果Mod 1000000007的值。
测试样例:
2,3
返回:8
思路
(1)10^75,75的二进制为1001011
(2)10^75 = 10^64 * 10^8 * 10^2 * 10^1, 由此可见,只需求出10,然后依据10求出10^2 ,依据10^2 求出10^4…
(3)而循环的次数为二进制数的位数,且只有为1时才乘到结果中

		public class QuickPower {
		    public int getPower(int k, int N) {
		        if(k==1||N==0)
		            return 1;
		        long res = 1;
		        long temp = k;
		        int m = 1000000007;
		        while(N>0){
		            if((N&1)!=0)
		                res *= temp;
		            temp = (temp*temp)%m;//保证值在int的范围内
		            res = res%m;
		            N = N>>1;
		        }
		        return (int)res;
		    }
		}

你可能感兴趣的:(数据结构和算法)