剑指offer-每日6题之第七天(java版)

原题链接:

 第一题:数字在排序数组中出现的次数
 第二题:二叉树的深度
 第三题:平衡二叉树
 第四题:数组中只出现一次的数字
 第五题:和为S的连续正数序列
 第六题:和为S的两个数字

第一题:数字在排序数组中出现的次数

题目描述

统计一个数字在排序数组中出现的次数。

解析

已知数组有序,所以用二分法,找到第一个k和最后一个k的位置,最后利用下标计算重复数组个数

public class Solution {
   public int GetNumberOfK(int [] array , int k) {
        int firstK=firstK(array,k,0,array.length-1);
        int lastK=lastK(array,k,0,array.length-1);
        if(lastK!=-1 && firstK!=-1)
            return lastK-firstK+1;
        return 0;
    }
    public int  firstK(int [] arr , int k,int start,int end){
        //if(start>end)return -1;
        //int mid=(start+end)/2;
        //if(arr[mid]>k)return firstK(arr,k,start,mid-1);
        //else if(arr[mid]<k) return firstK(arr,k,mid+1,end);
        //if( mid-1>=start && arr[mid-1]==k)return firstK(arr,k,start,mid-1);
        //else return mid;
       //递归
        int  mid=(start+end)/2;
        while(start<=end){
           if(arr[mid]>k)end=mid-1;
            else if(arr[mid]<k)start=mid+1;
            else if(mid-1>=start && arr[mid-1]==k)
               end=mid-1;
            else return mid;
            mid=(start+end)/2;
        }
        return -1;

    }
    public int  lastK(int [] arr , int k,int start,int end){
        //递归
        //if(start>end)return -1;
        //int mid=(start+end)/2;
        //if(arr[mid]>k)return lastK(arr,k,start,mid-1);
        //else if(arr[mid]<k)return lastK(arr,k,mid+1,end);
        // else if(mid+1<=end && arr[mid+1]==k)
        //   return lastK(arr,k,mid+1,end);
        //else return mid;
        //循环
        int  mid=(start+end)/2;
        while(start<=end){
            if(arr[mid]>k)end=mid-1;
            else if(arr[mid]<k)start=mid+1;
            else if(mid+1<=end && arr[mid+1]==k)
               start=mid+1;
            else return mid;
            mid=(start+end)/2;
        }
        return -1;
    }
}

第二题:二叉树的深度

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

解析

      递归:先判断根节点,在找左子树和右子树的长度,最后返回左子树深度和右子树深度点中较大的

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root==null)return 0;
        int left=TreeDepth(root.left);
        int right=TreeDepth(root.right);
        return left>right?left+1:right+1;
    }
}

第三题:平衡二叉树

题目描述

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

解析

方法一:

平衡二叉树:对于任意的节点,左右子树深度不超过1

对二叉树上的每个节点进行判断,如果当前节点不符合要求,直接返回false,否则继续判断其他的节点,直到所有的节点都符合条件,即为平衡二叉树

public class Solution {
    //要调用求二叉树深度的函数
    public boolean IsBalanced_Solution(TreeNode root) {
        //先判断当前根节点是否符合
        if(root==null)return true;
        int left=TreeDepth(root.left);
        int right=TreeDepth(root.right);
        int diff=Math.abs(left-right);
        if(diff>1)return false;
        return IsBalanced_Solution(root.left)&&IsBalanced_Solution(root.right);
    }
    public int TreeDepth(TreeNode root) {
        if(root==null)return 0;
        int left=TreeDepth(root.left);
        int right=TreeDepth(root.right);
        return left>right?left+1:right+1;
    }
}

解析

方法二:

       方法一有很明显的问题,在判断上层结点的时候,会多次重复遍历下层结点,增加了不必要的开销。如果改为从下往上遍历,如果子树是平衡二叉树,则返回子树的高度;如果发现子树不是平衡二叉树,则直接停止遍历,这样至多只对每个结点访问一次。

public class Solution {
   public boolean IsBalanced_Solution(TreeNode root) {
        return getDepth(root) != -1;
    }
    private int getDepth(TreeNode root) {
        if (root == null) return 0;
        int left = getDepth(root.left);
        if (left == -1) return -1;
        int right = getDepth(root.right);
        if (right == -1) return -1;
        return Math.abs(left - right) > 1 ? -1 : 1 + Math.max(left, right);
    }
}

第四题:数组中只出现一次的数字

题目描述

一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。

解析

      如果两个数字相同,那么将它们进行异或运算得到的二进制结果每一位一定为0,否则,得到的结果中至少有一个位上的数字为1,即其中的一个数字这个位上的数字为0,另一个不相同的数字这一位上的数字为1

       所以,要求数组中不相同的这两个数字,我们先将所有的数字进行异或,会得到一个二进制数,然后找出不是1的那一位数字,然后将所有的数组在遍历一遍,将这一位上为0的和这一位上为1的分为两组,进行异或,得到的结果就是最后要找的两个不想等的数字

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if(array==null||array.length<2)return;
        int number=0;
        for(int i=0;i<array.length;i++){
            number^=array[i];
        }
        int indexOf1=findFirstBitIs1(number);
        num1[0]=0;num2[0]=0;
        for(int i=0;i<array.length;i++){
         if (isBit1(array[i],indexOf1)){
             num1[0]^=array[i];
         }else{
             num2[0]^=array[i];
         }
        }
        
    }
    public int findFirstBitIs1(int number){
        int indexBit=0;
        while((number&1)==0){
            number=number>>1;
            ++indexBit;
        }
        return indexBit;
    }
    public boolean isBit1(int num,int indexBit){
        num=num>>indexBit;
        return (num&1)==1;
    }
}

第五题:和为S的连续正数序列

题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

输出描述:

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

解析

用两个数small,big分别表示序列的最大值,最小值

初始化small=1,big=2;
small到big序列和小于sum,big++;大于sum,small++;
当small增加到(1+sum)/2,停止

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> lists=new ArrayList<ArrayList<Integer>>();
        if(sum<=1){return lists;}
        int small=1;
        int big=2;
        while(small!=(1+sum)/2){          //到达数组中间的位置时候停止
            int curSum=sumOfList(small,big);
            if(curSum==sum){
                ArrayList<Integer> list=new ArrayList<Integer>();
                for(int i=small;i<=big;i++){
                    list.add(i);
                }
                lists.add(list);
                small++;big++;
            }else if(curSum<sum){
                big++;
            }else{
                small++;
            }
        }
        return lists;
    }
     
    public int sumOfList(int head,int leap){        //计算当前序列的和
        int sum=head;
        for(int i=head+1;i<=leap;i++){
            sum+=i;
        }
        return sum;
    }
}

解析

方法二:数学方法(大佬的思路)

1)由于我们要找的是和为S的连续正数序列,因此这个序列是个公差为1的等差数列,而这个序列的中间值代表了平均值的大小。假设序列长度为n,那么这个序列的中间值可以通过(S / n)得到,知道序列的中间值和长度,也就不难求出这段序列了。

2)满足条件的n分两种情况:

n为奇数时,序列中间的数正好是序列的平均值,所以条件为:(n & 1) == 1 && sum % n == 0;

n为偶数时,序列中间两个数的平均值是序列的平均值,而这个平均值的小数部分为0.5,所以条件为:(sum % n) * 2 == n.

3)由题可知n >= 2,那么n的最大值是多少呢?我们完全可以将n从2到S全部遍历一次,但是大部分遍历是不必要的。为了让n尽可能大,我们让序列从1开始,

根据等差数列的求和公式:S = (1 + n) * n / 2,得到.

eg:假设输入sum = 100,我们只需遍历n = 13~2的情况(按题意应从大到小遍历),n = 8时,得到序列[9, 10, 11, 12, 13, 14, 15, 16];n  = 5时,得到序列[18, 19, 20, 21, 22]。

时间复杂度为

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> ans = new ArrayList<>();
        for (int n = (int) Math.sqrt(2 * sum); n >= 2; n--) {
            if ((n & 1) == 1 && sum % n == 0 || (sum % n) * 2 == n) {//sum=1,输出结果为空
                ArrayList<Integer> list = new ArrayList<>();
                for (int j = 0, k = (sum / n) - (n - 1) / 2; j < n; j++, k++) {
                    list.add(k);
                }
                ans.add(list);
            }
        }
        return ans;
    }
}

六题:和为S的两个数字

题目描述

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

输出描述:

对应每个测试案例,输出两个数,小的先输出。

解析

双指针法:如果两个下标对应的值相加<sum,小的指针自增,如果相等,添加到list中,否则较大的指针自减

如果有多组输出,肯定是第一组的乘积最小,遵循差大积小

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (array == null || array.length < 2) {
            return list;
        }
        int i=0,j=array.length-1;
        while(i<j){
            if(array[i]+array[j]==sum){
            list.add(array[i]);
            list.add(array[j]);
                return list;
           }else if(array[i]+array[j]>sum){
                j--;
            }else{
                i++;
            }
             
        }
        return list;
    }
}

猜你喜欢

转载自blog.csdn.net/ccccc1997/article/details/81836208