原题链接:
- 第一题 : 数字在排序数组中出现的次数
- 第二题 : 二叉树的深度
- 第三题 : 平衡二叉树
- 第四题 : 数组中只出现一次的数字
- 第五题 : 和为S的连续正数序列
- 第六题 : 和为S的两个数字
第一题 : 数字在排序数组中出现的次数
题目 :
统计一个数字在排序数组中出现的次数。
解析 :
很容易想到要找到两个端点的下标,然后通过下标之间的关系确定出现的次数,通过两个函数来确定起始下标和结束下标,拿找起始下标来说,我们可以利用二分的思想,不过找起始下标的时候要判断一下是不是最左边的下标即可.具体实现看代码注意细节,可以有递归和非递归实现.
public int GetNumberOfK(int[] array, int k) {
int sum = 0;
if(array.length != 0 || array != null) {
int l = getFirst(array,k,0,array.length-1);
int r = getLast(array,k,0,array.length-1);
if(l != -1 && r != -1)sum = r - l + 1;
}
return sum;
}
public static int getFirst(int[] array,int k,int l,int r) {
if(l > r) return -1;
int mid = l + (r - l)/2;
if(array[mid] == k){
if(( mid > 0 && array[mid-1] != k) || (mid == 0))return mid;
else r = mid-1;
}
else if(array[mid] < k)l = mid+1;
else r = mid-1;
return getFirst(array,k,l,r);
}
public static int getLast(int[] array,int k,int l,int r) {
if(l > r) return -1;
int mid = l + (r - l)/2;
if(array[mid] == k){
if((mid < array.length-1 && array[mid+1] != k) || (mid == array.length-1))return mid;
else l = mid + 1;
}
else if(array[mid] > k)r = mid-1;
else l = mid+1;
return getLast(array,k,l,r);
}
非递归
//非递归的写法
public int GetNumberOfK2(int[] array, int k) {
int sum = 0;
if(array.length != 0 || array != null) {
int l = getHead(array,k,0,array.length-1);
int r = getRear(array,k,0,array.length-1);
if(l != -1 && r != -1)sum = r - l + 1;
}
return sum;
}
public static int getHead(int[] array,int k,int l,int r) {
while(l <= r) {
int mid = l + (r - l)/2;
if(array[mid] == k) {
if((mid > 0 && array[mid-1] != k ) || (mid == 0))return mid; //注意这里一定要先判断下标,不然会越界访问
else r = mid - 1;
}
else if(array[mid] < k)l = mid + 1;
else r = mid - 1;
}
return -1;
}
public static int getRear(int[] array,int k,int l,int r) {
while(l <= r) {
int mid = l + (r - l)/2;
if(array[mid] == k) {
if((mid < array.length-1 && array[mid+1] != k ) || (mid == array.length-1 ))return mid;
else l = mid + 1;
}
else if(array[mid] < k)l = mid + 1;
else r = mid - 1;
}
return -1;
}
第二题 : 二叉树的深度
题目 :
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
解析 :
每个一某个节点为根节点的树的高度是左右子树中比较高的一颗的高度+1,根据从上到下递归求出子树的高度即可.
public class TestTreeDepth {
private static class TreeNode{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
left = null;
right = null;
}
}
public static void main(String[] args) {
TreeNode root = new TreeNode(2);
root.left = new TreeNode(3);
System.out.println(new TestTreeDepth().TreeDepth(root));
}
public int TreeDepth(TreeNode root) {
if(root == null)return 0;
int llen = TreeDepth(root.left);
int rlen = TreeDepth(root.right);
return llen < rlen ? rlen+1: llen+1;
}
}
第三题 : 平衡二叉树
题目 :
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
解析 :
首先,要弄清平衡二叉树的判断条件,平衡二叉树是一颗左右子树的高度之差不超过1的树(包括任意的子树的左右子树),所以我们递归的判断每棵树的左右子树的高度之差是否小于等于1即可.
public boolean IsBalanced_Solution(TreeNode root) {
if(root == null)return true;
if(Math.abs(Height(root.left) - Height(root.right)) > 1){
return false;
}
return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
}
public static int Height(TreeNode root){
if(root == null)return 0;
return Math.max(Height(root.left),Height(root.right)) + 1;
}
第四题 :数组中只出现一次的数字
题目 :
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
解析 :
这个题目要用到异或的思想,首先我们要知道,一个数异或自己本身得到的是0,所以这一组整形数组的异或的值就是那两个只出现一次的数异或的结果(这两个数不相等,所以异或的结果不等于0),我们要求得这两个数,我们这样想,如果只有一个数出现一次,其余数出现两次,那么我们很容易求得结果,结果就是所有的数异或起来得到的结果. 但是这组数中有两个只出现一次的数,所以我们要想办法把这些数分成两组.
这里的分法是先把原数组中的所有的数异或一遍得到一个结果(就是那两个数异或的结果), 然后我们判断这个数的二进制从哪一位开始是1(这个数不是0,所以一定有某个位置是1) , 然后我们按照这个标准分为两组,所有数中对应的这个位置是1的分为一组,对应是0的分为另一组(为什么要这样分呢,这样一定可以把那两个数分开(异或的性质),而且其他相同的数没有被分开).
然后我们就可以很轻松的求解了.
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array.length < 2)return ;
int res = 0;
for(int i = 0; i < array.length; i++)res ^= array[i];
int digit = 1;
while((digit&res) == 0)digit *= 2;
for(int i = 0; i < array.length; i++){
if((array[i] & digit) == 0)num1[0] ^= array[i];
else num2[0] ^= array[i];
}
}
第五题 : 和为S的连续正数序列
题目 :
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述 : 输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序.
解析 :
首先要知道,我们要求的是一个连续递增的公差为1的递增等差数列,如果我们知道了这个序列的长度,那么我们就能知道这个序列的中间值 = S/n, 这里满足条件的n有两种情况,一种是n为奇数的时候,这个时候要满足sum % n == 0 && n%2 == 1(n&1 = 1) 第二种情况是n为偶数的情况,这种情况下序列的平均值是中间两个数的和/2,这是我们可以推到关系(sum % n)*2 == n,所以只要判断这个关系即可.
还有一个问题就是枚举的n的范围 由等差公式 S = (1 + n) * n / 2,得到 n <= sqrt (2*S); 所以枚举的范围变小.
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
for(int n = (int)Math.sqrt(2*sum); n >= 2; n--){
if((sum%n == 0 && (n&1) == 1)|| ((sum%n)*2 == n)){
ArrayList<Integer>list = new ArrayList<Integer>();
for(int j = 0,k = sum/n- (n-1)/2; j < n; k++,j++)list.add(k);
ans.add(list);
}
}
return ans;
}
第六题 : 和为S的两个数字
题目 :
输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述 : 对应每个测试案例,输出两个数,小的先输出。
解析 :
因为数列满足递增,我们可以设两个指针分别只想头部和尾部i,j
若a[i]+ a[j] == sum,得到答案(相差越远乘积越小);
若a[i]+ a[j]> sum,a[j] 肯定不会是答案(前面已得出 i 前面的数已不行),j -= 1;
若a[i] + a[j] < sum,a[i] 肯定不会是答案(前面已得出 j 后面的数已不行),i += 1;
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
int i = 0,j = array.length-1;
ArrayList<Integer>ans = new ArrayList<Integer>();
while(i < j){
if(array[i] + array[j] == sum){
ans.add(array[i]);
ans.add(array[j]);
break;
}
if(array[i] + array[j] > sum)j--;
if(array[i] + array[j] < sum)i++;
}
return ans;
}