1. 程式人生 > >劍指offer-每日6題之第七天(java版)

劍指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;
    }
}