劍指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! 輸出描述:
|
---|
解析
用兩個數small,big分別表示序列的最大值,最小值 初始化small=1,big=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;
}
}