1、判断平衡二叉树
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
思路一:对每个节点都要判断其左右子树的高度,递归从底层往上检测每个节点,避免在计算左右子树高度时重复遍历,所以递归的同时要返回高度信息
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if(root==null)return true;
return f(root)!=-1;
}
//具有一定程度的剪枝效果,当发现某个节点B不符合要求,向上递归时B是A的左节点,那A就不用计算右节点的高度了,直接返回-1。但若B是A的右节点,A还是会先计算其左子树的高度
int f(TreeNode t){
if(t==null)return 0;
int l=f(t.left);
if(l==-1)return -1;
int r=f(t.right);
if(r==-1)return -1;
if(l-r>1||r-l>1)return -1;
else return l<r?r+1:l+1;
}
}
为了既返回是不是平衡二叉树的Boolean值,又同时能保存每个结点的深度避免重复计算,C++里面基本类型有指针传递,Java没有,可以用一下几种方法。
来自牛客网@Aurora1
//对象成员变量
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
return f(root, new Holder());
}
private class Holder {
int n;
}
boolean f(TreeNode root, Holder h) {
if (root == null) {
h.n = 0;
return true;
}
Holder l = new Holder(), r = new Holder();
if (f(root.left, l) && f(root.right, r)) {
if (l.n - r.n > 1 || r.n - l.n > 1)
return false;
h.n += (l.n > r.n ? l.n : r.n) + 1;
return true;
}
return false;
}
}
//数组,这里只需要一个长为1的数组
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
return isBalance(root,new int[1]);
}
public boolean isBalance(TreeNode root,int []depth){
if(root==null){
depth[0]=0;
return true;
}
boolean left=isBalance(root.left,depth);
if(!left)return false;
int leftdepth=depth[0];
boolean right=isBalance(root.right,depth);
if(!right)return false;
int rightdepth=depth[0];
if(Math.abs(leftdepth-rightdepth)>1)return false;
depth[0]=Math.max(leftdepth+1,rightdepth+1);
return true;
}
}
//只用一个类变量就行
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
return f(root);
}
static int dep=0;//类变量,全局变量
boolean f(TreeNode t) {
if (t == null) {
dep=0;
return true;
}
int l=0,r=0;
if(f(t.left))l=dep;
else return false;
if(f(t.right))r=dep;
else return false;
if(l-r>1||r-l>1)return false;
dep=(l>r?l:r)+1;
return true;
}
}
2、二进制中1的个数
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
思路一:循环32次,验证每位上是0还是1
public class Solution {
public int NumberOf1(int n) {
int c=0;
for(int i=0;i<32;i++){
//if((n&1<<i)!=0)c++;//两种都可以
if((n>>>i&1)==1)c++;
}
return c;
}
}
思路二:循环到最高的为1的位就不循环,不必循环32位
public class Solution {
public int NumberOf1(int n) {
int count = 0;
while(n!= 0){
count++;
n = n & (n - 1);//把最低的1变成了0
}
return count;
}
}
思路三:利用Integer.bitCount(int i)原理
来自牛客网@Holiday_12138
public class Solution {
public int NumberOf1(int n) {
int temp = n;
temp = (temp & 0x55555555) + ((temp & 0xaaaaaaaa) >>> 1);//两位两位看
temp = (temp & 0x33333333) + ((temp & 0xcccccccc) >>> 2);//四位四位看
temp = (temp & 0x0f0f0f0f) + ((temp & 0xf0f0f0f0) >>> 4);
temp = (temp & 0x00ff00ff) + ((temp & 0xff00ff00) >>> 8);
temp = (temp & 0x0000ffff) + ((temp & 0xffff0000) >>> 16);
return temp;
}
}
3、矩形覆蓋
我们可以用2*1的小矩形横着或者竖着去覆蓋更大的矩形。请问用n个2*1的小矩形无重叠地覆蓋一个2*n的大矩形,总共有多少种方法?
思路一:设横着放的有a个,竖着放的有b个,它们的混合有 Caa+b=Cba+b C a + b a = C a + b b 种可能(等同于a个相同的球放入b+1个不同的盒子里,盒子可以为空)
public class Solution {
public int RectCover(int target) {
if(target==0)return 0;
int s=target/2;
int a,b,m,n,sum=0;
for(int i=0;i<=s;i++){//横着放的最多有s个
a=i;
b=target-2*a;
if(a==0||b==0){sum+=1;}
else{
m=a<b?a:b;
n=a+b;
sum+=C(n,m);
}
}
return sum;
}
int C(int n,int m){
if(m==1)return n;
long p=1,q=1;//n太大了可能会超出Integer范围
for(int i=n;i>n-m;i--){
p*=i;
}
for(int i=1;i<=m;i++){
q*=i;
}
return (int)(p/q);
}
}
思路二:f(1)=1,f(2)=2,f(n)=f(n-1)+f(n-2),斐波那契数列,神奇的发现!
4、连续子数组的最大和
例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和。
思路一:数组全为负数也没关系
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int len=array.length;
if(len==0)return 0;
int max=Integer.MIN_VALUE,sum=0;
for(int i=0;i<len;i++){
sum+=array[i];
max=sum<max?max:sum;
if(sum<0)sum=0;
}
return max;
}
}
思路二:DP
public int FindGreatestSumOfSubArray(int[] array) {
int res = array[0]; //记录当前所有子数组的和的最大值
int max=array[0]; //包含array[i]的连续数组最大值
for (int i = 1; i < array.length; i++) {
max=Math.max(max+array[i], array[i]);
res=Math.max(max, res);
}
return res;
}
5、逻辑符的短路特性求累加
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
思路一:不能循环那就递归,用逻辑符的短路特性来判断终止递归条件
public class Solution {
public int Sum_Solution(int n) {
return sum(n,n+1)>>1;
}
//快速幂的思想,不用乘法用递归求n(n+1)/2
int sum(int a,int b){
int result=0;
boolean f= (a&1)==0||(result+=b)>0;
a>>=1;b<<=1;
boolean f1= a==0||(result+=sum(a,b))>0;
return result;
}
}
//利用递归累加
public class Solution {
public int Sum_Solution(int n) {
int result=n;
boolean f= n==0||(result+=Sum_Solution(--n))>0;
return result;
}
}
思路二:Java的异常
来自牛客网@无极暴走夜之子
//用异常退出递归
public class Solution {
public int Sum_Solution(int n) {
return sum(n);
}
int sum(int n){
try{
int i = 1%n;
return n+sum(n-1);
}
catch(Exception e){
return 0;
}
}
}
6、构建乘积数组
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]*A1*…*A[i-1]*A[i+1]*…*A[n-1]。不能使用除法。
思路:结果很明显是两部分的乘积,先遍历一遍将这两部分保存起来,再相乘。线性时间复杂度。
来自牛客网@老石基
public class Solution {
public int[] multiply(int[] A) {
if(A==null||A.length==0)
return A;
int[] left = new int[A.length];//记录除了自己,左边的乘积
int[] right = new int[A.length];//记录除了自己,右边的乘积
right[A.length-1] = 1;
for(int i = A.length-2;i>=0;i--){
right[i] = right[i+1]*A[i+1];
}
left[0] = 1;
for(int i = 1;i<A.length;i++){
left[i] = left[i-1]*A[i-1];
}
int[] B = new int[A.length];
for(int i = 0;i<A.length;i++){
B[i] = left[i]*right[i];
}
return B;
}
}
实际上没必要构造两个新数组,利用B的空间就行
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int n=A.length;
int[] B=new int[n];
B[0]=1;
for(int i=1;i<n;i++){
B[i]=B[i-1]*A[i-1];
}
int temp=1;
for(int i=n-2;i>=0;i--){
temp*=A[i+1];
B[i]*=temp;
}
return B;
}
}
7、求二叉树的深度
思路一:递归
public class Solution {
public int TreeDepth(TreeNode root) {
if(root==null)return 0;
return (TreeDepth(root.left)>TreeDepth(root.right)?TreeDepth(root.left):TreeDepth(root.right))+1;
}
}
思路二:层序遍历
class Solution {
public:
int TreeDepth(TreeNode* pRoot) {
if (!pRoot) return 0;
queue<TreeNode*> que;
que.push(pRoot);int depth=0;
while (!que.empty()) {
int size=que.size();
depth++;
for (int i=0;i<size;i++) { //一次处理一层的数据
TreeNode *node=que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return depth;
}
};