原题链接:
第一题:数字在排序数组中出现的次数
第二题:二叉树的深度
第三题:平衡二叉树
第四题:数组中只出现一次的数字
第五题:和为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;
}
}