二分搜索

1局部最小值位置

定义局部最小的概念。arr长度为1时,arr[0]是局部最小。arr的长度为N(N>1)时,如果arr[0]<arr[1],那么arr[0]是局部最小;如果arr[N-1]<arr[N-2],那么arr[N-1]是局部最小;如果0<i<N-1,既有arr[i]<arr[i-1]又有arr[i]<arr[i+1],那么arr[i]是局部最小。 给定无序数组arr,已知arr中任意两个相邻的数都不相等,写一个函数,只需返回arr中任意一个局部最小出现的位置即可。

public class Solution {
    public int getLessIndex(int[] arr){
        if(arr.length==0)
            return -1;
        if(arr.length==1)
            return 0;
        if(arr[0]<arr[1])
            return 0;
        if(arr[arr.length-1]<arr[arr.length-2])
            return arr.length-1;
        for(int i=1;i<arr.length-2;i++){
            if(arr[i]<arr[i-1]&&arr[i]<arr[i+1]){
                return i;
            }
        }
        return -1;
    }
}

采用二分搜索做法,时间复杂度为O(lgn)

1.arr长度为0,返回-1,表示局部最小位置不存在

2.如果长度为1,返回0,此时0是局部最小位置

3.如果arr长度大于1,先判断arr[0]、arr[1]大小,arr[n-2]、arr[n-1]大小

public class Solution {
    public int getLessIndex(int[] arr) {
        if(arr==null||arr.length==0){
            return -1;
        }
        if(arr.length ==1||arr[0]<arr[1]){
            return 0;
        }
        if(arr[arr.length-1]<arr[arr.length-2]){
            return arr.length-1;
        }
        int left = 1;
        int right = arr.length-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 left;
 
    }
}

另一种二分法

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

2元素最左出现

对于一个有序数组arr,再给定一个整数num,请在arr中找到num这个数出现的最左边的位置。

给定一个数组arr及它的大小n,同时给定num。请返回所求位置。若该元素在数组中未出现,请返回-1。

测试样例:
[1,2,3,3,4],5,3
返回:2

自己写的代码
import java.util.*;

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

牛客网上代码

import java.util.*;
 
public class LeftMostAppearance {
    public int findPos(int[] arr, int n, int num) {
        // write code here
        if(arr==null || n==0) return -1;
        int res=-1;
        int left=0;
        int right=n-1;
        int mid;
        while(left<=right){
            mid=left+(right-left)/2;
            if(arr[mid]<num){
                left=mid+1;
            }else if(arr[mid]>num){
                right=mid-1;
            }else{
                right=mid-1;
 
                res=mid;
            }
        }
        return res;
         
    }
}

3循环有序数组最小值

对于一个有序循环数组arr,返回arr中的最小值。有序循环数组是指,有序数组左边任意长度的部分放到右边去,右边的部分拿到左边来。比如数组[1,2,3,3,4],是有序循环数组,[4,1,2,3,3]也是。

给定数组arr及它的大小n,请返回最小值。

测试样例:
[4,1,2,3,3],5
返回:1


思路:arr[L]<arr[R] 有序,min=arr[L]
arr[L]>=arr[R] arr[L]>arr[M] L-M
arr[M]>arr[R] M-R
arr[L]<=arr[M]&&arr[M]<=arr[R] =>arr[L]=arr[M]=arr[R] 遍历数组
自己写的代码
import java.util.*;

public class MinValue {
    public int getMin(int[] arr, int n) {
        // write code here
        int left=0;
        int right=n-1;
        int min=arr[0];
        if(arr[left]<arr[right]){
            min=arr[left];
        }else{
            while(left<=right){
                int mid=(left+right)/2;
                if(arr[left]>arr[mid]){
                    min=arr[mid];
                    right=mid-1;
                }else if(arr[mid]>arr[right]){
                    min=arr[mid];
                    left=mid+1;
                }else{
                    for(int i=1;i<n;i++){
                        if(arr[i]<min){
                            min=arr[i];
                        }
                    }
                    break;
                }
            }
        }
        return min;
    }
}

牛客网上代码

1left、mid、right

arr[left]>arr[mid] right=mid

arr[mid]>arr[left] left=mid

else mid++;

break;

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

2

import java.util.*;
 
public class MinValue {
    public int getMin(int[] arr, int n) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        int left = 0;
        int right = n - 1;
        int mid = 0;
        while (left<right){
            //当数组只有两个值的时候
            if (left==right-1){
                break;
            }
            //左边小于右边,left到right范围内是有序的
            if (arr[left]<arr[right]){
                return arr[left];
            }
            mid=(left+right)/2;
            if (arr[left]>arr[mid]){ //证明最小值在Left和mid之间
                right=mid;
                continue;
            }
            if (arr[mid]>arr[right]){//证明最小值在mid和right之间
                left=mid;
                continue;
            }
            //当出现等值的情况
            while (left<right){
                if (arr[left]==arr[mid]){
                    left++;
                }else if (arr[left]<arr[mid]){
                    return arr[left];
                }
            

//else{
// right=mid;
// break;
// }


} } } return Math.min(arr[left],arr[right]); } }

4最左原位

有一个有序数组arr,其中不含有重复元素,请找到满足arr[i]==i条件的最左的位置。如果所有位置上的数都不满足条件,返回-1。

给定有序数组arr及它的大小n,请返回所求值。

测试样例:
[-1,0,2,3],4
返回:2

自己写的代码1
import java.util.*;

public class Find {
    public int findPos(int[] arr, int n) {
        // write code here
        for(int i=0;i<n;i++){
            if(arr[i]==i)
                return i;
        }
        return -1;
    }
}
自己写的代码2
import java.util.*;

public class Find {
    public int findPos(int[] arr, int n) {
        // write code here
        if(arr[0]>n||arr[n-1]<0)
            return -1;
        int res=-1;
        int left=0;
        int right=n-1;
        int mid=0;
        while(left<=right){
            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;
    }
}

牛客网上的代码

import java.util.*;
 
public class Find {
    public int findPos(int[] arr, int n) {
        if (arr == null || n == 0) {
            return -1;
        }
        if (arr[0] > (n - 1)) { // 数组本身有序,第一个数都比最后一个大,肯定不存在
            return -1;
        }
        if (arr[n - 1] < 0) {// 最后一个数都小于0,则前面的数更不可能出现 arr[i] == i的情况
            return -1;
        }
        int left = 0;
        int right = n - 1;
        int mid = 0;
        int res = -1;
        while (left <= right) {
            mid = (left + right) / 2;
            if (arr[mid] > mid) { // 下标是按1进行递增,中间的值比中间的下标都大,则右边不会存在arr[i]==i的情况
                right = mid - 1;
            }
            if (arr[mid] < mid) { // 左边不存在arr[i]==i 的情况
                left = mid + 1;
            }
            if (arr[mid] == mid) {
                res = mid; // 但仍需要在左边进行寻找
                right = mid - 1;
            }
        }
        return res;
    }
}

5完全二叉树计数

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

给定树的根结点root,请返回树的大小。

牛客网上的代码1

import java.util.*;
 
/*
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) {
        //1.先找到完全二叉树左子树最左边节点的层数。然后再找出右子树的左节点能到达的层数
        //2.看左右子树的左节点能到的层数相等,则左子树必定是一个满二叉树,那使用满二叉树的性质求出左子树的节点数,右子树的节点数使用递归方式求出
        //3.如果左右子树的左节点能到的层数不相等,则右子树必定是一颗少一层的满二叉树。然后左子树使用递归方法求出节点数
        return bsCount(root,1,mostLeftlevel(root,1));
         
    }
     
    public int bsCount(TreeNode node,int start,int height){//start表示从头节点开始位置,height表示左子树左节点能到达的层数
        if(start == height){
            return 1;//只有一个头结点
        }
         
        if(mostLeftlevel(node.right,start+1) == height){//说明左子树是一颗满二叉树:start+1是因为右子树没包含跟节点
             return (1<<(height - start))+bsCount(node.right,start+1,height);
        }else{//说明右子树是比层数少一的满二叉树
            return (1<<(height - start - 1))+bsCount(node.left,start+1,height);
        }
    }
     
    public int mostLeftlevel(TreeNode node,int level){
        while(node != null){
            level++;//包括了头结点
            node = node.left;
        }
        return level-1;
    }
}

牛客网上的代码2

import java.util.*;
 
/*
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) {
        if (root == null) {
            return 0;
        }
        int count = 1;// 默认加上头结点
        // 获得层数
        TreeNode temp = root.left;
//      int zuoCenShu = temp == null ? 0 : 1;
        int zuoCenShu =  0;
        while (temp != null) {
            temp = temp.left;
            zuoCenShu++;
        }
 
        // 检测右子树
        temp = root.right;
        int youCenShu = 0;
        while (temp != null) {
            temp = temp.left;
            youCenShu++;
        }
        // 判断层数是否相等
        if (youCenShu == zuoCenShu && zuoCenShu != 0) {
            // 说明左子树为满二叉树
            count = (int)  Math.pow(2, zuoCenShu) + count(root.right);
        } else {
            // 说明右子树为满
            count = (int) Math.pow(2, youCenShu) + count(root.left);
        }
        return count;
 
    }
}

6快速N次方

如果更快的求一个整数k的n次方。如果两个整数相乘并得到结果的时间复杂度为O(1),得到整数k的N次方的过程请实现时间复杂度为O(logN)的方法。

给定kn,请返回k的n次方,为了防止溢出,请返回结果Mod 1000000007的值。

测试样例:
2,3
返回:8
import java.util.*;
  
public class QuickPower {
    public int getPower(int k, int N) {
        // write code here
        if(N == 0){
           return 1;
        } 
        if(N == 1){
           return k; 
        } 
        if(N % 2 == 0) {
            long tmp = getPower(k, N / 2);
            tmp = (tmp * tmp) % 1000000007;
            return (int)tmp;
        }else {
            long tmp = getPower(k, (N - 1) / 2);
            tmp = (tmp * tmp) % 1000000007;
            return (int)((tmp * k) % 1000000007);
        }
          
    }
}

 

import java.util.*;
 
public class QuickPower {
    public int getPower(int k, int N) {
        // write code here
        if(k==0){
            return 0;
        }
        if(k==1){
            return 1;
        }
        if(N==0){
            return 1;
        }
        long modNum=1000000007;
        long res=1;
        long tmp=k;
         
        for(;N>0;N>>=1){
            if((N&1)!=0){
                res*=tmp;
            }
            tmp=(tmp*tmp)%modNum;
            res=res%modNum;
             
        }
      
        return (int)res;
    }
}

 

import java.util.*;
 
public class QuickPower {
    public int getPower(int k, int N) {
        long con = 1000000007;
        // write code here
        int factor = N;
        long res = 1;
        long tmp = k;
        while(factor>0){
            if((factor&1)!=0){
                res=(res*tmp)%con;
            }
            tmp = (tmp*tmp)%con;
            factor=factor>>1;
        }
        return (int)res;
    }
}

 



posted @ 2017-05-10 17:07  临江仙zhe  阅读(76)  评论(0)    收藏  举报