剑指offer
来源
牛客网上的题目与牛友解析评论
入门
斐波那契数列
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
n≤39
示例1
输入
4
返回值
3
描述
此题是非常经典的入门题了。我记得第一次遇到此题是在课堂上,老师拿来讲“递归”的(哈哈哈)。同样的类型的题还有兔子繁殖的问题。大同小异。此题将用三个方法来解决,从入门到会做。
考察知识:递归,记忆化搜索,动态规划和动态规划的空间优化。
难度:一星
题解
方法一:递归
题目分析,斐波那契数列公式为:f[n] = f[n-1] + f[n-2], 初始值f[0]=0, f[1]=1,目标求f[n]
看到公式很亲切,代码秒秒钟写完。
int Fibonacci(int n) {
if (n==0 || n==1) return n;
return Fibonacci(n-1) + Fibonacci(n-2);
}
优点,代码简单好写,缺点:慢,会超时
时间复杂度:O(2^n)
空间复杂度:递归栈的空间
方法二:记忆化搜索
拿求f[5] 举例
通过图会发现,方法一中,存在很多重复计算,因为为了改进,就把计算过的保存下来。
那么用什么保存呢?一般会想到map, 但是此处不用牛刀,此处用数组就好了。
int Fib(int n, vector<int>& dp) {
if (n==0 || n==1) return n;
if (dp[n] != -1) return dp[n];
return dp[n] = Fib(n-1) + Fib(n-2);
}
int Fibonacci(int n) {
vector<int> dp(45, -1); // 因为答案都是>=0 的, 所以初始为-1,表示没计算过
return Fib(n, dp);
}
时间复杂度:O(n), 没有重复的计算
空间复杂度:O(n)和递归栈的空间
方法三:动态规划
虽然方法二可以解决此题了,但是如果想让空间继续优化,那就用动态规划,优化掉递归栈空间。
方法二是从上往下递归的然后再从下往上回溯的,最后回溯的时候来合并子树从而求得答案。
那么动态规划不同的是,不用递归的过程,直接从子树求得答案。过程是从下往上。
int Fibonacci(int n) {
vector<int> dp(n+1, 0);
dp[1] = 1;
for (int i=2; i<=n; ++i) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
时间复杂度:O(n)
空间复杂度:O(n)
继续优化
发现计算f[5]的时候只用到了f[4]和f[3], 没有用到f[2]…f[0],所以保存f[2]…f[0]是浪费了空间。
只需要用3个变量即可。
int Fibonacci(int n) {
if (n == 0 || n == 1) return n;
int a = 0, b = 1, c;
for (int i=2; i<=n; ++i) {
c = a + b;
a = b;
b = c;
}
return c;
}
时间复杂度:O(n)
空间复杂度:O(1)
完美!
通过的代码
public class Solution {
public int Fibonacci(int n) {
int a=1;
int b=1;
int c=1;
if(n==0){
return 0;
}
if(n<3){
return 1;
}
for(int i=2;i<=n;i++){
b=c;
a=c-a;
c=a+b;
}
return c;
}
}
public class Solution {
public int Fibonacci(int n) {
int[] array = new int[n+1];
if (n == 0 || n == 1) return n;
if (n > 39) return 0;
array[0] = 0;
array[1] = 1;
for (int i = 2; i <= n; i++) {
array[i] = array[i-1] + array[i-2];
}
return array[n];
}
}
import java.util.Scanner;
public class Solution {
public int Fibonacci(int n) {
int a[] = new int[n+1];
if (n==1 || n==2){
a[n] = 1;
}
if (n>2){
a[1] = a[2] = 1;
for (int i = 3; i <= n ;i++){
a[i] = a[i-1] + a[i-2];
a[n] = a[i];
}
}
return a[n];
}
public static void main(String[] s){
Solution t = new Solution();
Scanner scanner = new Scanner(System.in);
int fibonacci = t.Fibonacci(scanner.nextInt());
System.out.println(fibonacci);
}
}
public class Solution {
public int Fibonacci(int n) {
switch(n){
case 0:
return 0;
case 1:
return 1;
case 2:
return 1;
default:
int [] a=new int[n+1];
a[0]=0;
a[1]=1;
for(int i=2;i<n+1;i++){
a[i]=a[i-1]+a[i-2];
}
return a[n];
}
}
}
public class Solution {
public int Fibonacci(int n) {
int[] array = new int[40];
array[0] = 0;
array[1] = 1;
for(int i=2;i<40;i++){
array[i]=array[i-1]+array[i-2];
}
return array[n];
}
}
public class Solution {
public int Fibonacci(int n) {
if(n == 2 || n == 1){
return 1;
}
int q1 = 1;
int q2 = 1;
int temp = 0;
int val = 0;
for(int i = 2; i < n; i++){
val = q1 + q2;
temp = q1;
q1 = val;
q2 = temp;
}
return val;
}
}
public class Solution {
public int Fibonacci(int n) {
int[] array=new int[40];
array[0]=0;
array[1]=1;
for(int i=2;i<=n;i++){
array[i]=array[i-1]+array[i-2];
}
return array[n];
}
}
public class Solution {
public int Fibonacci(int n) {
if(n == 0){
return 0;
}else if(n == 1){
return 1;
}
int sum = 1;
int one = 0;
for(int i=2;i<=n;i++){
sum = sum + one;
one = sum - one;
}
return sum;
}
}
public class Solution {
int[] dp = new int[40];
public int Fibonacci(int n) {
dp[1] = 1;
dp[2] = 1;
for(int i = 3; i <=n ; i++){
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
}
简单
用两个栈实现队列
题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
题解
描述
这是一道对栈和队列之间灵活转化的题目。
难度:一星
考察知识:队列,栈
题解
方法:模拟
如果我知道队列是FIFO,栈是FILO,但是这道题我还是不知道怎么写怎么办?
对于这种感觉不难,但是又不会写的,方法就是模拟。
比如有如下操作:(pop操作确保栈中有元素)
push(1);push(2);pop(3);push(4);
根据队列的特性,只能pop(1),pop(2),pop之后的结果
上述是队列的操作。
当push的时候,我们必须要用一个stack来存,假设用stack1来存。
那么push操作解决了。那么pop操作怎么办呢?
如果pop(1),但是此时在stack1的栈底,如果要pop,必须再将stack1中的数据push到stack2中,然后在pop,如图
这样直接弹出stack2的栈顶就可以了。
如果要继续pop,那就继续弹出stack2就可以了
但是现在总感觉哪里还是有点问题。如果是这样就继续测试几个例子。
如果push(5),
所以最后总结一下:push操作就直接往stack1中push, pop操作需要分类一下:如果stack2为空,那么需要将stack1中的数据转移到stack2中,然后在对stack2进行pop,如果stack2不为空,直接pop就ok。
复杂度分析
时间复杂度:push操作为O(1),pop操作为O(1)
空间复杂度:需要stack来存,O(n)
代码
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if (stack2.empty()) {
while (!stack1.empty()) {
stack2.push(stack1.top());
stack1.pop();
}
}
int ret = stack2.top();
stack2.pop();
return ret;
}
private:
stack<int> stack1;
stack<int> stack2;
};
题解
分析
队列的特性是:“先入先出”,栈的特性是:“先入后出”
当我们向模拟的队列插入数 a,b,c 时,假设插入的是 stack1,此时的栈情况为:
- 栈 stack1:{a,b,c}
- 栈 stack2:{}
当需要弹出一个数,根据队列的"先进先出"原则,a 先进入,则 a 应该先弹出。但是此时 a 在 stack1 的最下面,将 stack1 中全部元素逐个弹出压入 stack2,现在可以正确的从 stack2 中弹出 a,此时的栈情况为:
- 栈 stack1:{}
- 栈 stack2:{c,b}
继续弹出一个数,b 比 c 先进入"队列",b 弹出,注意此时 b 在 stack2 的栈顶,可直接弹出,此时的栈情况为:
- 栈 stack1:{}
- 栈 stack2:{c}
此时向模拟队列插入一个数 d,还是插入 stack1,此时的栈情况为:
- 栈 stack1:{d}
- 栈 stack2:{c}
弹出一个数,c 比 d 先进入,c 弹出,注意此时 c 在 stack2 的栈顶,可直接弹出,此时的栈情况为:
- 栈 stack1:{d}
- 栈 stack2:{c}
根据上述栗子可得出结论:
- 当插入时,直接插入 stack1
- 当弹出时,当 stack2 不为空,弹出 stack2 栈顶元素,如果 stack2 为空,将stack1 中的全部数逐个出栈入栈stack2,再弹出 stack2 栈顶元素
代码
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (stack2.size() <= 0) {
while (stack1.size() != 0) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
复杂度
push时间复杂度:
pop空间复杂度:
通过的代码
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
return stack2.pop();
}else{
return stack2.pop();
}
}
}
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (stack2.size() <= 0) {
while (stack1.size() != 0) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
return stack2.pop();
}else{
return stack2.pop();
}
}
}
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.isEmpty()){//注意:栈2为空时,才能将栈1中的所有元素弹出到栈2
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
// if(stack2.isEmpty()){//此时栈2依旧为空,则返回-1
// return -1;
// }else{
// return stack2.pop();//删除队首元素就是将此时的栈2栈顶元素弹出即可
// }
}
}
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (!stack2.isEmpty()){
return stack2.pop();
}else {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
//只改变出即可
public int pop() throws Exception{
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
if(stack2.isEmpty()){
throw new Exception("queue is Empty!");
}
return stack2.pop();
}
}
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack1.isEmpty() && stack2.isEmpty()){
throw new RuntimeException("The queue is enpty");
}
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
import java.util.Stack;
public class Solution {
//思路:s1 作为出队的栈 s2作为入队的栈
//入队:入到s2里
//出队:出s1,如果s1为空,则将s2元素导入到s1中
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack2.push(node);
}
public int pop() {
if(stack1.isEmpty()){
while(!stack2.isEmpty()){
stack1.push(stack2.pop());
}
}
if(stack1.isEmpty()){//该情况说明在没有数据的情况下调用pop,应该抛异常
return -1;
}
return stack1.pop();
}
}
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.add(node);
}
public int pop() {
if (stack2.size() == 0 && stack1.size() == 0){
throw new RuntimeException("queue is empty!");
}
if (stack2.size() == 0) {
while (stack1.size() != 0){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
旋转数组的最小数字
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
示例1
输入
[3,4,5,1,2]
返回值
1
题解
描述
这是一道对二分查找算法灵活运用的一道题目。
二分查找算法不限于运用在有序数组上。如果能够明确二分之后,答案存在于二分的某一侧,就可以使用二分。本题就是如此。
难度:二星
考察知识:数组,二分查找
题解
方法一:暴力方法:
直接遍历一遍数组,即可找到最小值。但是本题的附加条件就没有用上。肯定不是面试官所期望的答案。
方法二:二分查找
这种二分查找难就难在,arr[mid]跟谁比.
我们的目的是:当进行一次比较时,一定能够确定答案在mid的某一侧。一次比较为 arr[mid]跟谁比的问题。
一般的比较原则有:
- 如果有目标值target,那么直接让arr[mid] 和 target 比较即可。
- 如果没有目标值,一般可以考虑端点
这里我们把target 看作是右端点,来进行分析,那就要分析以下三种情况,看是否可以达到上述的目标。
- 情况1,
arr[mid] > target:4 5 6 1 2 3
arr[mid] 为 6, target为右端点 3,arr[mid] > target
, 说明[first … mid] 都是 >= target的,因为原始数组是非递减,所以可以确定答案为 [mid+1…last]区间,所以first = mid + 1
- 情况2,
arr[mid] < target:5 6 1 2 3 4
arr[mid] 为 1, target为右端点 4,arr[mid] < target
, 说明答案肯定不在[mid+1…last],但是arr[mid]有可能是答案,所以答案在[first, mid]区间,所以last = mid
; - 情况3,
arr[mid] == target
:
如果是1 0 1 1 1, arr[mid] = target = 1, 显然答案在左边
如果是 1 1 1 0 1, arr[mid] =target = 1, 显然答案在右边 所以这种情况,不能确定答案在左边还是右边,那么就让last = last -1;慢慢缩少区间,同时也不会错过答案。
接下来我们用个例子来说明一下:
误区:那我们肯定在想,能不能把左端点看成target, 答案是不能。
原因:
情况1 :1 2 3 4 5 , arr[mid] = 3. target = 1, arr[mid] > target, 答案在mid 的左侧
情况2 :3 4 5 1 2 , arr[mid] = 5, target = 3, arr[mid] > target, 答案却在mid 的右侧
所以不能把左端点当做target
复杂度分析
时间复杂度:二分,所以为O(longN), 但是如果是[1, 1, 1, 1],会退化到O(n)
空间复杂度:没有开辟额外空间,为O(1)
代码
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if (rotateArray.size() == 0) return 0;
int first = 0, last = rotateArray.size() - 1;
while (first < last) { // 最后剩下一个元素,即为答案
if (rotateArray[first] < rotateArray[last]) { // 提前退出
return rotateArray[first];
}
int mid = first + ((last - first) >> 1);
if (rotateArray[mid] > rotateArray[last]) { // 情况1
first = mid + 1;
}
else if (rotateArray[mid] < rotateArray[last]) { //情况2
last = mid;
}
else { // 情况3
--last;
}
}
return rotateArray[first];
}
};
通过的代码
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if (array.length == 0) {
return 0 ;
}
int l = 0 ;
int r = array.length - 1 ;
int m = 0 ;
while (array[l] >= array[r]) {
if (r - l == 1) {
m = r ;
break ;
}
m = l + (r - l) / 2 ;
if (array[l] <= array[m]) {
l = m ;
}
if (array[r] >= array[m]) {
r = m ;
}
}
return array[m] ;
}
}
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
int length=array.length;
if (length==0){
return 0;
}
if (length==1){
return array[0];
}
int left=0;
int right=length-1;
while (left<right){
int midIndex=left+(right-left)/2;
if (array[midIndex]>array[right]){
left=midIndex+1;
}else if (array[midIndex]==array[right]){
right--;
}else {
right=midIndex;
}
}
return array[left];
}
}
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
//可以这样做,但是没有优化,拿不到offer
/*if (array.length == 0 || array == null)
return 0;
int a=array[0];
for (int i=0; i<array.length; i++) {
if (a > array[i])
a = array[i];
}
return a;*/
//用二分查找
//注意:非递减表示可以递增或相等
if (array.length == 0 || array == null)
return 0;
int l=0, r=array.length-1;
while(l < r) {
if (array[l] < array[r]) //防止出现 1 0 1 1 1这种情况
return array[l];
int mid=(l+r)/2;
if (array[l] < array[mid]) //说明最小元素一定在右边
l=mid+1;
else if (array[r] > array[mid]) //说明最小元素一定在左边
r=mid;
else
l++; //否则往后遍历继续二分
}
return array[l];
}
}
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array.length==0) return 0;
int right=array.length-1;
int target=array[right];
int left=0;
while(left<right){
if(array[left]<array[right]) return array[left];
int mid=(left+right)/2;
if(array[mid]>target){
left=mid+1;
}
else if(array[mid]<target){
right=mid;
}
else{
left++;
}
}
return array[left];
}
}
import java.util.ArrayList;
public class Solution {
// 1, 2,3, 4, 5
public int minNumberInRotateArray(int [] array) {
if (array.length == 0) {
return 0;
}
int left =0, right = array.length-1;
while (left < right) {
if (right - left < 2) {
return Math.min(array[left], array[right]);
}
int mid = (left + right) / 2;
if (array[left] < array[right]) { // 有序
right = mid;
}else { //旋转
if (array[mid] < array[right]) {
right = mid;
}else {
left = mid;
}
}
}
return array[left];
}
}
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array.length == 0) return 0;
int f = 0;
int e = array.length - 1;
while(f < e) {
if(array[f] < array[e]) return array[f];
int m = (f+e)/2;
if(array[f] < array[m]) {
f = m + 1;
} else if(array[e] > array[m]) {
e = m;
} else {
f++;
}
}
return array[f];
}
}
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if (array.length == 0) {
return 0;
}
int minValue = array[0];
int l = 0, r = array.length - 1;
while (l <= r) {
final int mid = l + (r - l) / 2;
if (array[mid] < array[l]) { // 至少右边是有序的
minValue = array[mid];
r = mid - 1;
}
else if (array[mid] > array[r]) {// 至少左边是有序的
minValue = array[l];
l = mid + 1;
}
else { // 无法判断到底哪边有序,比如 [0, 0, 0, 0, 0, 0, 1, 0]
if (array[r] < minValue) {
minValue = array[r];
}
r --;
}
}
return minValue;
}
}
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array.length==0||array==null)return 0;
int low=0,high=array.length-1;
while(low<high){//二者相等就跳出循环
if(array[low]<array[high])return array[low];//防止出现 1 0 1 1 1这种情况
int mid=(high+low)/2;
if(array[mid] > array[low])
low = mid + 1;
else if(array[mid] < array[high])
high = mid;
else low++;//其余情况 无法判定左右的有序情况
}
return array[low];
}
}
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
int p1 = 0;
int p2 = array.length-1;
/*while(array[p1]==array[p2]){
p1++;
p2--;
}*/
while(p1<p2){
int mid = (p2-p1)/2+p1;
if (array[p1] < array[p2]) {
return array[p1];
}
if(array[mid]<array[p1]){
p2=mid;
}else if(array[mid]>array[p1]) {
p1 = mid+1;
}else{
p1++;
}
}
return array[p1];
}
}
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array.length==0) return 0;
if(array.length==1) return array[0];
int a=array[0];
int l=0,r=array.length-1,mid=(l+r)/2;
while(l!=r){
if(array[mid]>=a){l=mid+1;mid=(l+r)/2;}
else if(array[mid]<a){r=mid;mid=(l+r)/2;}
}
return Math.min(array[l],a);
}
}
跳台阶扩展问题
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
示例1
输入
3
返回值
4
题解
描述:
这是一道可以递归,记忆化递归,动态规划,递推思想的题目。
知识点:递归,动态规划,递推
难度:一星
题解:
方法一:暴力方法
设f[i] 表示 当前跳道第 i 个台阶的方法数。那么f[n]就是所求答案。
假设现在已经跳到了第 n 个台阶,那么前一步可以从哪些台阶到达呢?
如果上一步跳 1 步到达第 n 个台阶,说明上一步在第 n-1 个台阶。已知跳到第n-1个台阶的方法数为f[n-1]
如果上一步跳 2 步到达第 n 个台阶,说明上一步在第 n-2 个台阶。已知跳到第n-2个台阶的方法数为f[n-2]
。。。
如果上一步跳 n 步到达第 n 个台阶,说明上一步在第 0 个台阶。已知跳到 第0个台阶的方法数为f[0]
那么总的方法数就是所有可能的和。也就是f[n] = f[n-1] + f[n-2] + … + f[0]
显然初始条件f[0] = f[1] = 1
所以我们就可以先求f[2],然后f[3]…f[n-1], 最后f[n]
代码:
int jumpFloorII(int n) {
if (n==0 || n==1) return 1;
vector f(n+1, 0);
f[0] = f[1] = 1;
for (int i=2; i<=n; ++i) {
for (int j=0; j<i; ++j) {
f[i] += f[j];
}
}
return f[n];
}
复杂度分析:
时间复杂度:O(n2)
空间复杂度:O(n)
方法二:继续优化
对于方法一中的:f[n] = f[n-1] + f[n-2] + … + f[0]
那么f[n-1] 为多少呢?
f[n-1] = f[n-2] + f[n-3] + … + f[0]
所以一合并,f[n] = 2*f[n-1],初始条件f[0] = f[1] = 1
所以可以采用递归,记忆化递归,动态规划,递推。具体详细过程,可查看青蛙跳台阶。
这里直接贴出递推的代码。
代码:
int jumpFloorII(int n) {
if (n==0 || n==1) return 1;
int a = 1, b;
for (int i=2; i<=n; ++i) {
b = a << 1; // 口诀:左移乘2,右移除2
a = b;
}
return b;
}
当然,你会发现一个规律:
f[0] = f[1] = 1
f[2] = 2 = 21
f[3] = 4 = 22
f[4] = 8 = 23
…
f[n] = 2n-1
所以,针对本题还可以写出更加简单的代码。
int jumpFloorII(int n) {
if (n == 0 || n == 1) return 1;
return static_cast(pow(2, n-1));
}
复杂度分析:
时间复杂度:O(n)
空间复杂度:O(1)
变态跳台阶
易知 f(n)=f(n-1)+f(n-2)+……f(1)
f(n-1)=f(n-2)+……f(1)
两式相减得f(n)=2f(n-1)
复制代码
-*- coding:utf-8 -*-
class Solution:
def jumpFloorII(self, number):
# write code here
n=1
for i in range(2,number+1):
n=2*n
return n
通过的代码
public class Solution {
public int jumpFloorII(int target) {
int res = 0;
int sum = 0;
int i = 1;
while(i <= target) {
res = sum + 1;
sum += res;
i++;
}
return res;
}
}
public class Solution {
public int jumpFloorII(int target) {
if(target<=1) {
return 1;
}
return 2*jumpFloorII(target-1);
}
}
public class Solution {
public int jumpFloorII(int target) {
if(target<=2) {
return target;
}
return 2*jumpFloorII(target-1);
}
}
public class Solution {
public int jumpFloorII(int target) {
int sum=1;
for(int i=2;i<=target;i++){
sum*=2;
}
return sum;
}
}
public class Solution {
public int jumpFloorII(int target) {
if(target==1||target==2){
return target;
}
int a=1;
int b=2;
for(int i=2;i<target;i++){
a=b;
b=b<<1;
}
return b;
}
}
public class Solution {
public int jumpFloorII(int target) {
int a = 1;
// int b = 1;
for(int i=2;i<=target;i++){
a = a<<1;
}
return a;
}
}
public class Solution {
public int jumpFloorII(int target) {
if(target <= 0){
return -1;
}else if(target == 1){
return 1;
}else{
return 2 * jumpFloorII(target - 1);
}
}
}
public class Solution {
public int jumpFloorII(int target) {
if(target == 0|| target == 1) return 1;
int x = 1;
for(int i = 2;i<=target;i++){
x = x * 2;
}
return x;
}
}
public class Solution {
public int jumpFloorII(int target) {
if(target < 2) return 1;
return (int)Math.pow(2,target-1);
}
}
public class Solution {
public int jumpFloorII(int target) {
if(target<=0)return 0;
if(target==1)return 1;
int b=2;
int result=2;
for(int i=2;i<target;i++){
result=result*b;
}
return result;
}
合并两个排序的链表
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
示例1
输入
{1,3,5},{2,4,6}
返回值
{1,2,3,4,5,6}
说明:本题目包含复杂数据结构ListNode
题解
描述
这是一篇针对初学者的题解,共用2种方法解决。
知识点:单链表,递归
难度:一星
题解:
题目要求:给两个非递减单链表l1, l2,合并为一个非递减的单链表。
方法一:迭代版本求解
初始化:定义cur指向新链表的头结点
操作:
- 如果l1指向的结点值小于等于l2指向的结点值,则将l1指向的结点值链接到cur的next指针,然后l1指向下一个结点值
- 否则,让l2指向下一个结点值
- 循环步骤1,2,直到l1或者l2为nullptr
- 将l1或者l2剩下的部分链接到cur的后面
技巧
一般创建单链表,都会设一个虚拟头结点,也叫哨兵,因为这样每一个结点都有一个前驱结点。
代码
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
ListNode *vhead = new ListNode(-1);
ListNode *cur = vhead;
while (pHead1 && pHead2) {
if (pHead1->val <= pHead2->val) {
cur->next = pHead1;
pHead1 = pHead1->next;
}
else {
cur->next = pHead2;
pHead2 = pHead2->next;
}
cur = cur->next;
}
cur->next = pHead1 ? pHead1 : pHead2;
return vhead->next;
}
};
时间复杂度:O(m+n),m,n分别为两个单链表的长度
空间复杂度:O(1)
方法二:递归版本
方法一的迭代版本,很好理解,代码也好写。但是有必要介绍一下递归版本,可以练习递归代码。
写递归代码,最重要的要明白递归函数的功能。可以不必关心递归函数的具体实现。
比如这个ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
函数功能:合并两个单链表,返回两个单链表头结点值小的那个节点。
如果知道了这个函数功能,那么接下来需要考虑2个问题:
- 递归函数结束的条件是什么?
- 递归函数一定是缩小递归区间的,那么下一步的递归区间是什么?
对于问题1.对于链表就是,如果为空,返回什么
对于问题2,跟迭代方法中的一样,如果PHead1的所指节点值小于等于pHead2所指的结点值,那么phead1后续节点和pHead节点继续递归
代码
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if (!pHead1) return pHead2;
if (!pHead2) return pHead1;
if (pHead1->val <= pHead2->val) {
pHead1->next = Merge(pHead1->next, pHead2);
return pHead1;
}
else {
pHead2->next = Merge(pHead1, pHead2->next);
return pHead2;
}
}
};
时间复杂度:O(m+n)
空间复杂度:O(m+n),每一次递归,递归栈都会保存一个变量,最差情况会保存(m+n)个变量
通过的代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode newHeadNode = new ListNode(0);
ListNode tempNode = newHeadNode;
while(null!=list1&&null!=list2){
if(list1.val>=list2.val){
tempNode.next = list2;
tempNode = tempNode.next;
list2 = list2.next;
}else{
tempNode.next = list1;
tempNode = tempNode.next;
list1 = list1.next;
}
}
if(null == list1){
tempNode.next = list2;
}else if(null == list2){
tempNode.next = list1;
}
return newHeadNode.next;
}
}
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode ans=null;
if(list1==null) return list2;
if(list2==null) return list1;
if(list1.val<list2.val){
ans=list1;
list1=list1.next;
}else{
ans=list2;
list2=list2.next;
}
ListNode t=ans;
while(list1!=null && list2!=null){
if(list1.val<=list2.val){
ans.next=list1;
ans=ans.next;
list1=list1.next;
}else{
ans.next=list2;
ans=ans.next;
list2=list2.next;
}
}
while(list1!=null){
ans.next=list1;
ans=ans.next;
list1=list1.next;
}
while(list2!=null){
ans.next=list2;
ans=ans.next;
list2=list2.next;}
return t;
}
}
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode head = new ListNode(0);
ListNode p = head;
while(list1!=null && list2!=null){
if(list1.val<list2.val){
p.next = list1;
list1=list1.next;
}else{
p.next = list2;
list2=list2.next;
}
p=p.next;
}
if(list1==null) p.next = list2;
if(list2==null) p.next = list1;
return head.next;
}
}
public class Solution {
public ListNode Merge(ListNode listA, ListNode listB) {
// 非空判断
if(listA == null) {
return listB;
}
if(listB == null) {
return listA;
}
// 创建新链表的头,尾指针
ListNode newHead = null, newTail = null, tempNode;
while(listA != null && listB != null) {
if(listA.val <= listB.val) {
tempNode = listA;
listA = listA.next;
} else {
tempNode = listB;
listB = listB.next;
}
// 第一次 newHead 和 newTail 都为空
if(newHead == null) {
newHead = tempNode;
newTail = newHead;
} else {
newTail.next = tempNode;
newTail = newTail.next;
}
}
newTail.next = listA != null ? listA : listB;
return newHead;
}
}
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode head = new ListNode(-1);
ListNode cur = head;
while(list1 != null && list2 != null){
if(list1.val <= list2.val){
cur.next = list1;
list1 = list1.next;
} else {
cur.next = list2;
list2 = list2.next;
}
cur = cur.next;
}
if(list1 == null){
cur.next = list2;
} else {
cur.next = list1;
}
return head.next;
}
}
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null) return list2;
if(list2==null) return list1;
ListNode Merge_=null;
if(list1.val<list2.val){
Merge_=list1;
Merge_.next=Merge(list1.next,list2);
}else{
Merge_=list2;
Merge_.next=Merge(list1,list2.next);
}
return Merge_;
}
}
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null) return list2;
if(list2==null) return list1;
if(list1.val<list2.val){
list1.next = Merge(list1.next,list2);
return list1;
}else{
list2.next = Merge(list1,list2.next);
return list2;
}
}
}
//递归
//找到res和res.next即可
public class Solution {
public static ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
ListNode res=null;
if(list1.val< list2.val){
res=list1;
res.next=Merge(list1.next,list2);
}else{
res=list2;
res.next=Merge(list1,list2.next);
}
return res;
}
}
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if (list1 == null || list2 == null)
return list1 == null ? list2 : list1;
if (list1.val < list2.val) {
ListNode second = Merge(list1.next, list2);
list1.next = second;
return list1;
}
else {
ListNode second = Merge(list1, list2.next);
list2.next = second;
return list2;
}
}
}
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode rst= new ListNode(0), p;
p = rst;
while(list1!=null && list2!=null){
if(list1.val<list2.val){
p.next = list1;
list1 = list1.next;
p = p.next;
}
else{
p.next = list2;
list2 = list2.next;
p=p.next;
}
}
p.next = list2==null ? list1:list2;
return rst.next;
}
}
二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
比如: 源二叉树
8
/
6 10
/ \ /
5 7 9 11
镜像二叉树
8
/
10 6
/ \ /
11 9 7 5
示例1
输入
{8,6,10,5,7,9,11}
返回值
{8,10,6,11,9,7,5}
说明:本题目包含复杂数据结构TreeNode
题解
使用递归思想:
先镜像当前root左右节点,然后再递归镜像左孩子和右孩子即可。
代码如下:
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
// write code here
if(pRoot == null) return pRoot;
TreeNode temp= pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = temp;
Mirror(pRoot.left);
Mirror(pRoot.right);
return pRoot;
}
}
【数据结构和算法】BFS,DFS,递归等多种实现方式,图文详解
1,BFS解决
之前讲373,数据结构-6,树的时候,提到过二叉树的广度优先搜索,就是一层一层的访问,像下面这样
二叉树的BFS代码如下
public static void treeBFS(TreeNode root) {
//如果为空直接返回
if (root == null)
return;
//队列
Queue<TreeNode> queue = new LinkedList<>();
//首先把根节点加入到队列中
queue.add(root);
//如果队列不为空就继续循环
while (!queue.isEmpty()) {
//poll方法相当于移除队列头部的元素
TreeNode node = queue.poll();
//打印当前节点
System.out.println(node.val);
//如果当前节点的左子树不为空,就把左子树
//节点加入到队列中
if (node.left != null)
queue.add(node.left);
//如果当前节点的右子树不为空,就把右子树
//节点加入到队列中
if (node.right != null)
queue.add(node.right);
}
}
这题要求的是输出二叉树的镜像,就是每一个节点的左右子节点进行交换,随便画个二叉树看一下
我们需要遍历每一个节点,然后交换他的两个子节点,一直循环下去,直到所有的节点都遍历完为止,代码如下
public TreeNode Mirror(TreeNode root) {
//如果为空直接返回
if (root == null)
return null;
//队列
final Queue<TreeNode> queue = new LinkedList<>();
//首先把根节点加入到队列中
queue.add(root);
while (!queue.isEmpty()) {
//poll方法相当于移除队列头部的元素
TreeNode node = queue.poll();
//交换node节点的两个子节点
TreeNode left = node.left;
node.left = node.right;
node.right = left;
//如果当前节点的左子树不为空,就把左子树
//节点加入到队列中
if (node.left != null) {
queue.add(node.left);
}
//如果当前节点的右子树不为空,就把右子树
//节点加入到队列中
if (node.right != null) {
queue.add(node.right);
}
}
return root;
}
2,DFS解决
无论是BFS还是DFS都会访问到每一个节点,访问每个节点的时候交换他的左右子节点,直到所有的节点都访问完为止,代码如下
public TreeNode Mirror(TreeNode root) {//DFS
//如果为空直接返回
if (root == null)
return null;
//栈
Stack<TreeNode> stack = new Stack<>();
//根节点压栈
stack.push(root);
//如果栈不为空就继续循环
while (!stack.empty()) {
//出栈
TreeNode node = stack.pop();
//子节点交换
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
//左子节点不为空入栈
if (node.left != null)
stack.push(node.left);
//右子节点不为空入栈
if (node.right != null)
stack.push(node.right);
}
return root;
}
3,中序遍历解决
这题其实解法比较多,只要访问他的每一个节点,然后交换子节点即可,我们知道二叉树不光有BFS和DFS访问顺序,而且还有前序遍历,中序遍历和后续遍历等,不管哪种访问方式,只要能把所有节点都能访问一遍然后交换子节点就能解决,我们这里就以中序遍历来看下,前序和后序就不在看了。在373,数据结构-6,树中,提到二叉树中序遍历的非递归写法如下
public static void inOrderTraversal(TreeNode tree) {
Stack<TreeNode> stack = new Stack<>();
while (tree != null || !stack.isEmpty()) {
while (tree != null) {
stack.push(tree);
tree = tree.left;
}
if (!stack.isEmpty()) {
tree = stack.pop();
System.out.println(tree.val);
tree = tree.right;
}
}
}
我们来对他改造一下,就是在访问每个节点的时候交换,代码如下
public static TreeNode Mirror(TreeNode root) {
//如果为空直接返回
if (root == null)
return null;
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while (node != null || !stack.isEmpty()) {
while (node != null) {
stack.push(node);
node = node.left;
}
if (!stack.isEmpty()) {
node = stack.pop();
//子节点交换
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
//注意这里以前是node.right,因为上面已经交换了
//,所以这里要改为node.left
node = node.left;
}
}
return root;
}
4,递归方式解决
二叉树中序遍历的递归代码如下
public void inOrderTraversal(TreeNode node) {
if (node == null)
return;
inOrderTraversal(node.left);
System.out.println(node.val);
inOrderTraversal(node.right);
}
上面说了,只要能访问二叉树的每一个节点,然后交换左右子节点就行了,这里就以二叉树中序遍历递归的方式来看下
public TreeNode Mirror(TreeNode root) {
if (root == null)
return null;
Mirror(root.left);
//子节点交换
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
//上面交换过了,这里root.right要变成root.left
Mirror(root.left);
return root;
}
再来看一个后续遍历的
public TreeNode Mirror(TreeNode root) {
if (root == null)
return null;
TreeNode left = Mirror(root.left);
TreeNode right = Mirror(root.right);
root.left = right;
root.right = left;
return root;
}
5,总结
这题没什么难度,但解法比较多,主要是因为二叉树的遍历方式比较多,如果每一种方式递归和非递归都写的话就更多了,这里不在一直写下去了。
通过的代码
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
// write code here
TreeNode tmp;
if(pRoot == null) return pRoot;
tmp = pRoot.right;
pRoot.right = pRoot.left;
pRoot.left = tmp;
Mirror(pRoot.left);
Mirror(pRoot.right);
return pRoot;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
if(pRoot == null){
return null;
}
if(pRoot != null){
TreeNode temp = pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = temp;
if(pRoot.left != null){
Mirror(pRoot.left);
}
if(pRoot.right != null){
Mirror(pRoot.right);
}
}
return pRoot;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
// write code here
if(pRoot==null)
return null;
if(pRoot.left!=null){
Mirror(pRoot.left);
}
if(pRoot.right!=null){
Mirror(pRoot.right);
}
TreeNode temp;
temp=pRoot.left;
pRoot.left=pRoot.right;
pRoot.right=temp;
return pRoot;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
if (pRoot == null)
return null;
// write code here
Mirror(pRoot.left);
Mirror(pRoot.right);
TreeNode temp = pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = temp;
return pRoot;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
// Queue<TreeNode> q=new Queue<>();
public TreeNode Mirror (TreeNode pRoot) {
// write code here
if(pRoot==null) return pRoot;
TreeNode temp=pRoot.left;
pRoot.left=pRoot.right;
pRoot.right=temp;
Mirror(pRoot.left);
Mirror(pRoot.right);
return pRoot;
}
}
public class Solution {
public TreeNode Mirror (TreeNode pRoot) {
if (pRoot == null) return null;
if (pRoot.left == null && pRoot.right == null) return pRoot;
TreeNode temp = pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = temp;
Mirror(pRoot.left);
Mirror(pRoot.right);
return pRoot;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
TreeNode temp;
public TreeNode Mirror (TreeNode pRoot) {
// write code here
if (pRoot == null) {
return pRoot;
}
temp = pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = temp;
Mirror(pRoot.left);
Mirror(pRoot.right);
return pRoot;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
if(pRoot == null) {
return null;
}
TreeNode temp = pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = temp;
Mirror(pRoot.left);
Mirror(pRoot.right);
return pRoot;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
// write code here
if (pRoot == null) return pRoot;
if(pRoot!=null){
TreeNode temp = pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = temp;
}
if(pRoot.left != null){
Mirror(pRoot.left);
}
if(pRoot.right != null){
Mirror(pRoot.right);
}
return pRoot;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
// write code here
if(pRoot == null) return pRoot;
TreeNode temp = pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = temp;
Mirror(pRoot.left);
Mirror(pRoot.right);
return pRoot;
}
}
数组中出现次数超过一半的数字
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
示例1
输入
[1,2,3,2,2,2,5,4,2]
返回值
2
题解
描述
这是一篇针对初学者的题解。共用三种方法解决。
知识点:数组,排序,哈希
难度:一星
题解
题目抽象:给定一个数组,找出数组中的众数,若有,返回众数,若没有,返回0
众数定义:数组中出现次数大于数组一般的元素
方法一:哈希法
根据题目意思,显然可以先遍历一遍数组,在map中存每个元素出现的次数,然后再遍历一次数组,找出众数。
代码
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
unordered_map<int,int> mp;
for (const int val : numbers) ++mp[val];
for (const int val : numbers) {
if (mp[val] > numbers.size() / 2 ) return val;
}
return 0;
}
};
时间复杂度:O(n)
空间复杂度:O(n)
方法二:排序法
可以先将数组排序,然后可能的众数肯定在数组中间,然后判断一下。
代码
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
sort(numbers.begin(), numbers.end());
int cond = numbers[numbers.size() / 2];
int cnt = 0;
for (const int k :numbers) {
if (cond == k) ++cnt;
}
if (cnt > numbers.size() / 2) return cond;
return 0;
}
};
时间复杂度:O(nlongn)
空间复杂度:O(1)
方法三:候选法(最优解)
加入数组中存在众数,那么众数一定大于数组的长度的一半。
思想就是:如果两个数不相等,就消去这两个数,最坏情况下,每次消去一个众数和一个非众数,那么如果存在众数,最后留下的数肯定是众数。
具体做法:
- 初始化:候选人cond = -1, 候选人的投票次数cnt = 0
- 遍历数组,如果cnt=0, 表示没有候选人,则选取当前数为候选人,++cnt
- 否则,如果cnt > 0, 表示有候选人,如果当前数=cond,则++cnt,否则–cnt
- 直到数组遍历完毕,最后检查cond是否为众数
代码
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
int cond = -1;
int cnt = 0;
for (int i=0; i<numbers.size(); ++i) {
if (cnt == 0) {
cond = numbers[i];
++cnt;
}
else {
if (cond == numbers[i]) ++cnt;
else --cnt;
}
}
cnt = 0;
for (const int k :numbers) {
if (cond == k) ++cnt;
}
if (cnt > numbers.size() / 2) return cond;
return 0;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
通过的代码
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if (array.length == 0) {
return 0;
}
int temp = array[0];
int count = 1;
for (int i = 1; i < array.length; i++) {
if (array[i] == temp) {
count++;
} else {
count--;
}
if (count == 0) {
temp = array[i];
count = 1;
}
}
count = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] == temp) {
count++;
}
}
return count > array.length / 2 ? temp : 0;
}
}
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int len=array.length;
if(len==0) return 0;
int ret=0,res=array[0],count=1;
for(int i=1;i<len;i++){
if(count==0){
res=array[i];
count++;
continue;
}
if(array[i]!=res){
count--;
}else count++;
}
for(int num:array){
if(num==res) ret++;
}
return ret>(len>>1)?res:0;
}
}
import java.util.HashMap;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
HashMap<Integer, Integer> hashMap = new HashMap();
for (int i = 0; i < array.length; ++i){
hashMap.put(array[i], hashMap.getOrDefault(array[i], 0) + 1);
if (hashMap.get(array[i]) > array.length / 2){
return array[i];
}
}
return 0;
}
}
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if (array.length == 0) {
return 0;
}
int temp = array[0];
int count = 1;
for(int num : array){
if(count == 0) temp = num;
count += num == temp ? 1 : -1;
}
count = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] == temp) {
count++;
}
}
return count > array.length / 2 ? temp : 0;
}
}
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null||array.length==0)return 0;
int val = array[0];
int count = 1;
for(int i = 1;i<array.length;i++){
if(array[i]==val){
count++;
}else{
count--;
if(count==0){
val = array[i];
count=1;
}
}
}
int num = 0;
for(int i = 0;i<array.length;i++){
if(array[i]==val){
num++;
}
}
return num>array.length/2?val:0;
}
}
import java.util.HashMap;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if (array.length == 0 || array == null) return 0;
int index = array[0];
int count = 1;
for (int i = 1; i < array.length; i++) {
if (array[i] == index) count ++;
else {
count --;
if (count == 0) {
index = array[i];
count = 1;
}
}
}
count = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] == index) count++;
}
if (count > array.length / 2) return index;
return 0;
}
}
public class Solution {
private boolean slove(int key, int[] array, int length) {
boolean flag = false;
int count = 0;
for (int i = 0 ;i<length;i++){
if(key==array[i]){
count++;
}
}
if (count>length/2){
flag=true;
}
return flag;
}
public int MoreThanHalfNum_Solution(int [] array) {
//一 一对比,计数,只有一个
//想到一对一的map数据结构
int length = array.length;
for (int i = 0 ;i<length;i++){
if(slove(array[i],array,length)){
return array[i];
};
}
return 0;
}
}
import java.util.HashMap;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length==0) return 0;
int pre = array[0];
int count=1;
for(int i=1; i<array.length; i++){
if(pre==array[i]){
++count;
}else if(--count<0) {
pre = array[i];
count = 1;
}
}
count = 0;
for(int i=0; i<array.length; i++){
if(array[i]==pre) count++;
}
return count>array.length/2? pre: 0;
}
}
import java.util.HashMap;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if (array.length == 0) return 0;
if (array.length == 1) return array[0];
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < array.length; i++) {
int key = array[i];
if (map.containsKey(key)) {
if (map.get(key) + 1 > array.length / 2) {
return key;
} else {
map.replace(key, map.get(key) + 1);
}
} else {
map.put(key, 1);
}
}
return 0;
}
}
import java.util.Arrays;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
Arrays.sort(array);
int count=0;
int num=array[array.length/2];
for(int n:array){
if(n==num){
count++;
}else if(n>num)
break;
}
return count>array.length/2?num:0;
}
}
连续子数组的最大和
题目描述
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为 O(n).
示例1
输入
[1,-2,3,10,-4,7,2,-5]
返回值
18
说明
输入的数组为{1,-2,3,10,—4,7,2,一5},和最大的子数组为{3,10,一4,7,2},因此输出为该子数组的和 18。
题解
描述
这是一篇针对初学者的题解,共用两种方法解决。
知识点:数组,动态规划
难度:一星
题解
题目抽象:给定一个数组,求连续子数组的最大和。
方法一:动态规划
状态定义:dp[i]表示以i结尾的连续子数组的最大和。所以最终要求dp[n-1]
状态转移方程:dp[i] = max(array[i], dp[i-1]+array[i])
解释:如果当前元素为整数,并且dp[i-1]为负数,那么当然结果就是只选当前元素
技巧:这里为了统一代码的书写,定义dp[i]表示前i个元素的连续子数组的最大和,结尾元素为array[i-1]
代码
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
int sz = array.size();
vector<int> dp(sz+1, 1);
dp[0] = 0; // 表示没有元素
int ret = array[0];
for (int i=1; i<=sz; ++i) {
dp[i] = max(array[i-1], dp[i-1]+array[i-1]);
ret = max(ret, dp[i]);
}
return ret;
}
};
时间复杂度:O(n)
空间复杂度:O(n)
方法二:空间复杂度O(1)解法
思想很简单,就是对下标为i的元素array[i],先试探的加上array[i], 如果和为负数,显然,以i结尾的元素对整个结果不作贡献。
具体过程:
- 初始化:维护一个变量tmp = 0
- 如果tmp+array[i] < 0, 说明以i结尾的不作贡献,重新赋值tmp = 0
- 否则更新tmp = tmp + array[i]
- 最后判断tmp是否等于0, 如果等于0, 说明数组都是负数,选取一个最大值为答案。
代码
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
int ret = array[0];
int tmp = 0;
for (const int k : array) {
if (tmp + k < 0) {
tmp = 0;
}
else {
tmp += k;
}
ret = max(ret, tmp);
}
if (tmp != 0)
return ret;
return *max_element(array.begin(), array.end());
}
};
时间复杂度:O(n)
空间复杂度:O(1)
通过的代码
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int len=array.length;
if(len<=0){
return 0;
}
int dp=array[0];
//int[] dp=new int[len];
//dp[0]=array[0];
int max=dp;
for(int i=1;i<len;i++){
if(dp>0){
dp=dp+array[i];
}
else{
dp=array[i];
}
max=Math.max(max,dp);
}
return max;
}
}
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int maxVal = 0;
int curSum = 0;
int len = array.length;
for(int i=0; i<len; i++){
curSum = curSum+array[i];
maxVal = Math.max(maxVal, curSum);
if(curSum<0) {
if (array[i]>0){
curSum = array[i];
} else {
curSum = 0;
}
}
}
return maxVal>0?maxVal:-1;
}
}
import java.lang.*;
import java.util.*;
import java.io.*;
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
//int[] dp = new int[array.length];
int sum=array[0];
//int res=Integer.MIN_VALUE();
//int res=-100;
for (int i=1;i<array.length;i++) {
array[i] += Math.max(array[i-1], 0);
sum= Math.max(array[i], sum);
//sum=0;
}
return sum;
}
}
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if(array.length==0) return 0;
int dp[]=new int[array.length];
int max=array[0];
dp[0]=array[0];
for(int i=1;i<array.length;i++){
if(dp[i-1]+array[i]>array[i]) dp[i]=dp[i-1]+array[i];
else dp[i]=array[i];
if(max<dp[i]) max=dp[i];
}
return max;
}
}
public class Solution {
public int FindGreatestSumOfSubArray(int[] arr) {
// 排异
if (arr == null || arr.length < 1) {
System.out.println("数组为空!");
}
// max用来记录最大的子数组和,因为有负数存在,所以设置为MIN_VALUE而不能为0
int max = Integer.MIN_VALUE;
// tmpMax用来记录当前的和,初始值给0
int tmpMax = 0;
// 遍历数组arr来找最大的子数组和
for (int i : arr) {
// 如果当前的和tmpMax小于等于0,就重新设置当前和;如果当前和大于0,累加当前和
//这样做就可以简化逻辑,因为如果遍历到i号元素时的和tmpMax(假设从0开始遍历,那么这里的tmpMax就是0至i-1的和)是非正数。就可以将tmpMax的值直接给成i,为什么呢?
//因为如果此时的i为正数,那再好不过,正零负三者相比,高下立判;
// 如果为负数,我们也不慌,因为max变量记录了tmpMax的值,如果i作为负数比tmpMax还小,虽然本次i依旧会赋值给tmpMax,
// 但是根据下面if (max < tmpMax)的判断,最终max还是上一轮tmpMax的值,所以逻辑是通的。
if (tmpMax <= 0) {
tmpMax = i;
}
else {
tmpMax += i;
}
// 用变量max记录tmpMax的值
if (max < tmpMax) {
max = tmpMax;
}
}
//返回最大的子数组和
return max;
}
}
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
//dp状态转移方程为dp[i] = max(array[i],dp[i-1]+array[i]);
int[] dp = new int[array.length+1];
int ret = array[0];
for(int i = 1;i <= array.length ;i++){
dp[i] = Math.max(array[i-1],dp[i-1]+array[i-1]);
ret = Math.max(ret,dp[i]);
}
return ret;
}
}
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if (array.length == 0) return 0;
int ret = Integer.MIN_VALUE;;
int crt = 0;
for (int i=0; i<array.length; i++) {
if (crt >= 0) crt += array[i];
else crt = array[i];
if (crt > ret) ret = crt;
}
return ret;
}
}
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int ret = array[0];
int temp = 0;
for (int i : array) {
if (temp + i < 0){
temp = 0;
}else {
temp += i;
}
ret = Math.max(temp,ret);
}
if (temp != 0){
return ret;
}
int max = array[0];
for (int i : array) {
if (max < i){
max = i;
}
}
return max;
}
}
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int len=array.length;
int pre=array[0];
int max=pre;
for(int i=1;i<len;i++){
pre=Math.max(pre+array[i],array[i]);
if(pre>max)max=pre;
}
return max;
}
}
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int max=Integer.MIN_VALUE;
int sum=0;
for(int num:array){
sum+=num;
max=Math.max(max,sum);
if(sum<0){
sum=0;
}
}
return max;
}
}
第一个只出现一次的字符
题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
示例1
输入
“google”
返回值
4
题解
描述
这是一篇针对初学者的题解。共用两种方法解决。
知识点:哈希,bitset
难度:一星
题解
方法一:哈希法
很显然,遍历一遍字符串,统计每个字符出现的次数。然后再遍历一遍字符串,找出答案。
代码一:用map实现
class Solution {
public:
int FirstNotRepeatingChar(string str) {
unordered_map<char, int> mp;
for (const char ch : str) {
++mp[ch];
}
for (int i=0; i<str.length(); ++i) {
if (mp[str[i]] == 1) return i;
}
return -1;
}
};
代码二:用数组代替map
class Solution {
public:
int FirstNotRepeatingChar(string str) {
int mp[128] = {0};
for (const char ch : str) {
++mp[ch];
}
for (int i=0; i<str.length(); ++i) {
if (mp[str[i]] == 1) return i;
}
return -1;
}
};
时间复杂度:O(2n), 需要遍历两次字符串
空间复杂度:O(n)
方法二:使用bitset
其实思想还是哈希,主要是这里bitset更节省空间,每个位置的值为0或1, 同时可以练习下bitset的使用。
具体过程:
- 初始化:bitset<128> b1表示只出现1次, b2表示出现2次以上
- 遍历字符串,第一次出现,b1[ch] = 1
- 如果第二次出现,b2[ch] = 1
- 最后,找出第一个b1[ch] == 1 && b2[ch] == 0的字符
代码
class Solution {
public:
int FirstNotRepeatingChar(string str) {
bitset<128> b1, b2;
for (const char ch : str) {
if (!b1[ch] && !b2[ch]) {
b1[ch] = 1;
}
else if (b1[ch] && !b2[ch]) {
b2[ch] = 1;
}
}
for (int i=0; i<str.length(); ++i) {
if (b1[str[i]] && !b2[str[i]]) {
return i;
}
}
return -1;
}
};
时间复杂度:O(2n), 需要遍历两次字符串
空间复杂度:O(n)
通过的代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String str ;
while ((str = bufferedReader.readLine()) != null){
int i = FirstNotRepeatingChar(str);
System.out.println(i);
}
}
public static int FirstNotRepeatingChar(String str) {
int [] mp = new int[128];
for (int i = 0; i < str.length(); i++) {
mp[str.charAt(i)]++;
}
for (int i = 0; i < str.length(); i++) {
if( mp[str.charAt(i)]==1){
return i;
}
}
return -1;
}
}
import java.util.*;
public class Solution {
public int FirstNotRepeatingChar(String str) {
// HashMap<Character,Integer> res = new HashMap();
// char[] arr = str.toCharArray();
// for(char c:arr){
// if(res.containsKey(c)){
// res.put(c,res.get(c)+1);
// }else{
// res.put(c,1);
// }
// }
// for(int i=0;i<str.length();i++){
// if(res.get(str.charAt(i))==1){
// return i;
// }
// }
// return -1;
if(str == null || str.length()==0){
return -1;
}
int[] res = new int[60];
char[] arr = str.toCharArray();
for(char c:arr){
res[c-'A']++;
}
for(int i=0;i<arr.length;i++){
if(res[arr[i]-'A']==1){
return i;
}
}
return -1;
}
}
import java.util.*;
public class Solution {
public int FirstNotRepeatingChar(String str) {
int [] mp = new int[128];
for (int i = 0; i < str.length(); i++) {
mp[str.charAt(i)]++;
}
for (int i = 0; i < str.length(); i++) {
if( mp[str.charAt(i)]==1){
return i;
}
}
return -1;
}
}
import java.util.HashMap;
public class Solution {
public int FirstNotRepeatingChar(String str) {
//异常,str为空,长度0,返回-1
if(str==null||str.isEmpty()){
return -1;
}
int[]arr=new int[128];
for(int i=0;i<str.length();i++){
arr[str.charAt(i)]++;
}
for(int i=0;i<str.length();i++){
if(arr[str.charAt(i)]==1){
return i;
}
}
return -1;
}
}
public class Solution {
public int FirstNotRepeatingChar(String str) {
if (str == null || str.length() == 0) {
return -1;
}
int[] arr = new int[58];
for (int i = 0; i < str.length(); i++) {
arr[str.charAt(i) - 'A'] += 1;
}
for (int i = 0; i < str.length(); i++) {
if (arr[str.charAt(i) - 'A'] == 1) {
return i;
}
}
return -1;
}
}
import java.util.HashMap;
public class Solution {
public int FirstNotRepeatingChar(String str) {
if(str==null||str.length()==0)
return -1;
int[] count=new int[58];
for(int i=0;i<str.length();i++)
{
char tmp=str.charAt(i);
count[tmp-'A']++;
}
for(int i=0;i<str.length();i++){
char c=str.charAt(i);
if(count[c-'A']==1)
return i;
}
return -1;
}
}
import java.util.*;
public class Solution {
public int FirstNotRepeatingChar(String str) {
for(int i =0;i<str.length();i++){
if(str.indexOf(str.charAt(i)) == i && str.indexOf(str.charAt(i),i+1) == -1) return i;
}
return -1;
}
}
public class Solution {
public int FirstNotRepeatingChar(String str) {
int[] counts = new int[58];
for(int i=0;i<str.length();i++){
counts[str.charAt(i)-'A'] += 1;
}
for(int i=0;i<str.length();i++){
if(counts[str.charAt(i)-'A'] == 1){
return i;
}
}
return -1;
}
}
import java.util.HashMap;
import java.util.Map;
public class Solution {
public int FirstNotRepeatingChar(String str) {
if(str==null || str.length()<=0){
return -1;
}
int[] letter = new int[58];
char[] cc = str.toCharArray();
for(int i=0; i<cc.length; i++){
int index=cc[i]-'A';
letter[index]++;
}
for(int i=0; i<cc.length; i++){
int index=cc[i]-'A';
if(letter[index]==1){
return i;
}
}
return -1;
}
}
public class Solution {
public int FirstNotRepeatingChar(String str) {
int[] a = new int[58];
for(int i=0;i<str.length();i++){
a[(int)str.charAt(i)-65]+=1;
}
for(int i=0;i<str.length();i++){
if(a[(int)str.charAt(i)-65]==1)
return i;
}
// for (int i = 0; i < str.length(); i++) {
// a[((int) str.charAt(i)) - 65] += 1;
// }
// for (int i = 0; i < str.length(); i++) {
// if (a[((int) str.charAt(i)) - 65] == 1)
// return i;
// }
return -1;
}
}
二叉树的深度
题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
示例1
输入
{1,2,3,4,5,#,6,#,#,7}
返回值
4
说明:本题目包含复杂数据结构TreeNode
题解
描述
这是一篇指针初学者的题解。这里用2种方法。
知识点:二叉树,队列,树的层次遍历, 分治法
难度:一星
题解
题目抽象:给出一颗二叉树,求树的最大深度,也就是从根节点到所有叶子节点中的最大值。
方法一:分治法
分治法简介:求一个规模为n的问题,先求左边规模大约为n/2的问题,再求右边规模大约为n/2的问题,然后合并左边,右边的解,从而求得最终解。具体可参考归并排序。
步骤:
- 求 pro(left, rigth) -> int
- 先求pro(left, (left+right)/2) -> lval
- 再求pro((left+right)/2 + 1, right) -> rval
- merge(lval, rval) -> result
这里以本题为具体例子:
函数是求二叉树的最大深度,我们不必管函数具体是怎么实现的。
所以最终结果为 max( 头结点左子树的最大深度, 头结点右子树的最大深度)+1
- 求
TreeDepth(TreeNode* pRoot)->int
- 先求
TreeDepth(pRoot->left) ->lval
- 再求
TreeDepth(pRoot->right) ->rval
return max(lval, rval) + 1
代码
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if (!pRoot) return 0;
int lval = TreeDepth(pRoot->left);
int rval = TreeDepth(pRoot->right);
return max(lval, rval) + 1;
}
};
时间复杂度:O(n)
空间复杂度:O(n),当退化到链表时
方法二:层次遍历
求最大深度,可用队列。因为要满足先进先出的特性。
- 初始化:一个队列queue<TreeNode*> q, 将root节点入队列q
- 如果队列不空,做如下操作:
- 弹出队列头,保存为node,将node的左右非空孩子加入队列
- 做2,3步骤,知道队列为空
如果不需要确定当前遍历到了哪一层,模板如下:
void bfs() {
vis[] = 0;
queue<int> pq(start_val);
while (!pq.empty()) {
int cur = pq.front(); pq.pop();
for (遍历cur所有的相邻节点nex) {
if (nex节点有效 && vis[nex]==0){
vis[nex] = 1;
pq.push(nex)
}
}
}
}
如果需要确定遍历到哪一层,模板如下;
void bfs() {
int level = 0;
vis[] = 0; // or set
queue<int> pq(original_val);
while (!pq.empty()) {
int sz = pq.size();
while (sz--) {
int cur = pq.front(); pq.pop();
for (遍历cur所有的相邻节点nex) {
if (nex节点有效 && vis[nex] == 0) {
vis[nex] = 1;
pq.push(nex)
}
} // end for
} // end inner while
level++;
} // end outer while
}
所以本题直接套模板即可:
代码如下:
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if (!pRoot) return 0;
queue<TreeNode*> q;
q.push(pRoot);
int leve1 = 0;
while (!q.empty()) {
int sz = q.size();
while (sz--) {
auto node = q.front(); q.pop();
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
leve1 += 1;
}
return level;
}
};
时间复杂度:O(n),二叉树的每个节点遍历一次
空间复杂度:O(n),最差情况下,树平衡时,队列最多存储n/2个节点。
通过的代码
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
private int ret = 0;
public int TreeDepth(TreeNode root) {
dfs(root,0);
return ret;
}
public void dfs(TreeNode root,int depth){
if(root != null){
ret = Math.max(ret,++depth);
dfs(root.left,depth);
dfs(root.right,depth);
}
}
}
public class Solution {
public int TreeDepth(TreeNode root) {
if(root == null){
return 0;
}
TreeDepth(root.left);
TreeDepth(root.right);
return Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;
}
}
public class Solution {
public int TreeDepth(TreeNode root) {
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
return 1 + Math.max(TreeDepth(root.left),TreeDepth(root.right));
}
}
public class Solution {
public int TreeDepth(TreeNode root) {
if (root == null) return 0;
return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;
}
}
public class Solution {
public int TreeDepth(TreeNode root) {
if(root == null)
return 0;
int depth1 = 1 + TreeDepth(root.left);
int depth2 = 1 + TreeDepth(root.right);
return depth1 > depth2 ? depth1 : depth2;
}
}
public class Solution {
public int TreeDepth(TreeNode root) {
if(root == null) return 0;
int l = TreeDepth(root.left);
int r = TreeDepth(root.right);
return l > r ?l + 1:r + 1;
}
}
public class Solution {
public int TreeDepth(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(TreeDepth(root.left),TreeDepth(root.right)) + 1;
}
}
public class Solution {
public int TreeDepth(TreeNode root) {
if(root==null) return 0;
return Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;
}
}
public class Solution {
public int TreeDepth(TreeNode root) {
int n=0,m=0;
if(root==null) return 0;
else{
n=TreeDepth(root.left);
m=TreeDepth(root.right);
if(n>m) return n+1;
else return m+1;
}
}
}
import java.util.*;
public class Solution {
public int TreeDepth(TreeNode root) {
if (root == null) return 0;
int left = TreeDepth(root.left);
int right = TreeDepth(root.right);
return Math.max(left, right) + 1;
}
}
平衡二叉树
题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
示例1
输入
{1,2,3,4,5,6,7}
返回值
true
说明:本题目包含复杂数据结构TreeNode
题解
这是一篇针对初学者的题解,用两种方法解决。
知识点:树,递归
难度:一星
题解
方法一:自顶向下
判断一个数是否为平衡二叉树。平衡二叉树是左子树的高度与右子树的高度差的绝对值小于等于1,同样左子树是平衡二叉树,右子树为平衡二叉树。
根据定义,如果我们能够求出以每个结点为根的树的高度,然后再根据左右子树高度差绝对值小于等于1,,就可以判断以每个结点为根的树是否满足定义。
我们可以用hash<TreeNode*, int>
来存以每个结点的树的高度。
代码如下:
map<TreeNode*, int> hs;
int depth(TreeNode *root) {
if (!root) return 0;
if (hs.find(root) != hs.end()) return hs[root];
int ldep = depth(root->left);
int rdep = depth(root->right);
return hs[root] = max(ldep, rdep) + 1;
}
然后再用先序遍历:根节点、左子树、右子树
来判断以每个结点为根的树是否满足条件。
代码如下:
bool judge(TreeNode *root) {
if (!root) return true;
return abs(hs[root->left] - hs[root->right]) <= 1 &&
judge(root->left) && judge(root->right);
}
最后的代码为:
class Solution {
public:
map<TreeNode*, int> hs;
int depth(TreeNode *root) {
if (!root) return 0;
if (hs.find(root) != hs.end()) return hs[root];
int ldep = depth(root->left);
int rdep = depth(root->right);
return hs[root] = max(ldep, rdep) + 1;
}
bool judge(TreeNode *root) {
if (!root) return true;
return abs(hs[root->left] - hs[root->right]) <= 1 &&
judge(root->left) && judge(root->right);
}
bool IsBalanced_Solution(TreeNode* root) {
depth(root);
return judge(root);
}
};
时间复杂度:O(N)
空间复杂度:O(N)
方法二:自底向上
方法一是先求出以每个结点为根的树的高度,然后再判断,其实可以直接再求高度的同时,直接判断即可。
利用后序遍历:左子树、右子树、根节点
,可以先递归到叶子节点,然后在回溯的过程中来判断是否满足条件。
求树的高度的代码为:
int depth(TreeNode *root) {
if (!root) return 0;
int ldep = depth(root->left);
int rdep = depth(root->right);
return max(ldep, rdep) + 1;
}
然后对上述代码加以改造,如果不满足平衡二叉树的定义,则返回-1,并且如果左子树不满足条件了,直接返回-1,右子树也是如此,相当于剪枝,加速结束递归。
代码如下:
int depth(TreeNode *root) {
if (!root) return 0;
int ldep = depth(root->left);
if (ldep == -1) return -1;
int rdep = depth(root->right);
if (rdep == -1) return -1;
int sub = abs(ldep - rdep);
if (sub > 1) return -1;
return max(ldep, rdep) + 1;
}
最后只需要判断depth(root)返回的是否为-1,如果是-1,则不是,否则,则是。
代码如下:
class Solution {
public:
int depth(TreeNode *root) {
if (!root) return 0;
int ldep = depth(root->left);
if (ldep == -1) return -1;
int rdep = depth(root->right);
if (rdep == -1) return -1;
int sub = abs(ldep - rdep);
if (sub > 1) return -1;
return max(ldep, rdep) + 1;
}
bool IsBalanced_Solution(TreeNode* root) {
return depth(root) != -1;
}
};
时间复杂度:O(N)
空间复杂度:O(N)
通过的代码
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if(root == null) {
return true;
}
return Math.abs(maxDepth(root.left) - maxDepth(root.right)) <= 1 &&
IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
}
private int maxDepth(TreeNode root) {
if(root == null) {
return 0;
}
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
}
public class Solution {
boolean flag = true ;
public boolean IsBalanced_Solution(TreeNode root) {
judge(root);
return flag ;
}
public int judge(TreeNode r){
if(r == null) return 0 ;
int left = judge(r.left) ;
int right = judge(r.right) ;
if((left-right)>1 || (right-left)>1){
flag = false ;
return -1 ;
}
return left>right?left+1:right+1;
}
}
public class Solution {
public int maxDepth (TreeNode root) {
// write code here
if (root ==null){
return 0;
}
int left = maxDepth(root.left);
int right=maxDepth(root.right);
return Math.max(left,right)+1;
}
public boolean IsBalanced_Solution(TreeNode root) {
if (root ==null){
return true;
}
int left = maxDepth(root.left);
int right=maxDepth(root.right);
int diff = Math.abs(left-right);
if (diff>1){
return false;
}
return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
}
}
public class Solution {
public int depth(TreeNode root){
if(root == null)return 0;
int left = depth(root.left);
if(left == -1)return -1; //如果发现子树不平衡之后就没有必要进行下面的高度的求解了
int right = depth(root.right);
if(right == -1)return -1;//如果发现子树不平衡之后就没有必要进行下面的高度的求解了
if(left - right <(-1) || left - right > 1)
return -1;
else
return 1+(left > right?left:right);
}
public boolean IsBalanced_Solution(TreeNode root) {
return depth(root) != -1;
}
}
public class Solution {
public int depth(TreeNode root){
if(root == null)return 0;
int left = depth(root.left);
if(left == -1)return -1;
int right = depth(root.right);
if(right == -1)return -1;
if(left - right <(-1) || left - right > 1)
return -1;
else
return 1+(left > right?left:right);
}
public boolean IsBalanced_Solution(TreeNode root) {
return depth(root) != -1;
}
}
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if(root==null)return true;
boolean a=Math.abs(depth(root.left)-depth(root.right))<=1;
boolean b = IsBalanced_Solution(root.left);
boolean c= IsBalanced_Solution(root.right);
return a&&b&&c;
}
public int depth(TreeNode root){
if(root==null)return 0;
return Math.max(depth(root.left), depth(root.right))+1;
}
}
public class Solution {
boolean flag = true;
public boolean IsBalanced_Solution(TreeNode root) {
if(root == null) {
return true;
}
treeHeight(root);
return flag;
}
public int treeHeight(TreeNode root) {
if(root == null) {
return 0;
}
int leftH = treeHeight(root.left);
// if (leftH == -1) {
// return -1;
// }
int rightH = treeHeight(root.right);
// if(rightH == -1) {
// return -1;
// }
int temp = Math.abs(leftH - rightH);
if(temp > 1) {
flag = false;
return -1;
}
return Math.max(leftH,rightH) + 1;
}
}
import java.util.*;
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if(root == null){
return true;
}
int m = depth(root.left);
int n = depth(root.right);
if(Math.abs(m-n) > 1){
return false;
}
return true;
}
public int depth(TreeNode root){
if(root == null){
return 0;
}else{
int m = depth(root.left);
int n = depth(root.right);
return m>n? m+1 : n+1;
}
}
}
// 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);
// }
// }
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
return height2(root) >= 0 ;
}
private int height2(TreeNode root ){ // 1 2
if(root == null) return 0;
int left = height2(root.left);
int right = height2(root.right);
if( left== -1 || right ==-1 || Math.abs(left-right)>1 )
return -1 ;
return Math.max(left,right) +1 ;
}
}
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
return depth(root) != -1;
}
private int depth(TreeNode root){
if(root == null) return 0;
int left = depth(root.left);
if(left == -1) return -1;
int right = depth(root.right);
if(right == -1) return -1;
if(left - right < (-1) || left - right > 1){
return -1;
}else{
return 1 + (left > right ? left : right);
}
}
}
不用加减乘除做加法
题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
示例1
输入
1,2
返回值
3
题解
描述
这是一篇针对初学者的题解
知识点:位运算
难度:二星
题解
题目描述:不使用加减乘除来实现两数加法、
方法:位运算
知识补充:
按位与&,按位或|, 按位异或^
补码
计算机中存整数n是用补码存的。
- 如果n为正数,则原码=反码=补码
- 如果n为负数,则补码=反码+1
本题是考察对位运算的运用,使用位运算来实现两数的加法。
设两数字的二进制形式 a,b ,其求和 s = a + b ,a(i) 代表 a 的二进制第 i 位,则分为以下四种情况:
观察发现,无进位和运算就是按位异或结果,进位就是与运算结果但是需要左移一位,因为进位影响下一位的运算。
所以s = a + b,其实就是无进位和+进位的结果。
算法步骤:
- 计算a和b的无进位和,和进位
- 如果进位不为0,则说明a+b的结果等于无进位和+进位,此时,把无进位和作为a,进位作为b,继续计算
- 如果进位等于0, 说明此时a+b的结果就等于无进位和,返回无进位和即可。
如图:
Q:你可能有疑问,如果是一个数为负数或者两个数都为负数怎么办?
A:上述补码的介绍,补码就是解决减法的问题,计算机把减法看做加法来运算。
所以代码如下:
class Solution {
public:
int Add(int num1, int num2)
{
while (num2 != 0) {
// 负数左移会在低位补1,所以转化为无符号整数
int c = ((unsigned int)(num1 & num2)) << 1;
num1 ^= num2;
num2 = c;
}
return num1;
}
};
时间复杂度:O(1)
空间复杂度:O(1)
通过的代码
public class Main {
public int add(int num1,int num2) {
int result;
int ans;
do{
result = num1 ^ num2;
ans = (num1 & num2) << 1;
num1 = result;
num2 = ans;
}while(ans!=0);
return result;
}
public static void main(String[] args){
System.out.println(new Main().add(1,2));
System.out.println(new Main().add(111,899));
System.out.println(new Main().add(-1,2));
System.out.println(new Main().add(1,-2));
System.out.println(new Main().add(3,0));
System.out.println(new Main().add(0,-4));
System.out.println(new Main().add(-2,-8));
}
}
public class Solution {
public int Add(int num1,int num2) {
return num2 == 0 ? num1 : Add(num1 ^ num2, (num1 & num2) << 1);
}
}
public class Solution {
public int Add(int num1,int num2) {
int result = 0;
int carry = 0;
do{
result = num1 ^ num2; //不带进位的加法
carry = (num1 & num2) << 1; //进位
num1 = result;
num2 = carry;
}while(carry != 0); // 进位不为0则继续执行加法处理进位
return result;
}
}
public class Solution {
public int Add(int num1,int num2) {
while (num2 != 0){
int m = (num1 & num2) << 1;
num1 ^= num2;
num2 = m;
}
return num1;
}
}
数组中重复的数字
题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1
示例1
输入
[2,3,1,0,2,5,3]
返回值
2或3
题解
方法1:
对于重复性问题可以想到set,遍历数组依次加入集合,若集合中存在该元素则直接返回该元素,否则将该元素加入集合:
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] numbers) {
// write code here
Set<Integer> set = new HashSet<Integer>();
for(int i : numbers){
if(set.contains(i)){
return i;
}else{
set.add(i);
}
}
return -1;
}
}
方法2:
对于题目所给的条件,可以开辟一个与给定数组等长的新数组,用于记录给定数组中出现的数字的个数(新数组的下标即表示给定数组中的数,数组存放的值,即为该数字出现的次数)一旦新数组中某个记录的数大于1,则返回新数组的下标
代码如下:
public static int duplicate (int[] numbers) {
// write code here
int[] countarray=new int[numbers.length];
for(int i=0;i<numbers.length;i++) {
countarray[numbers[i]]++;
if(countarray[numbers[i]]>1) {
return numbers[i];
}
}
return -1;
}
数组中重复的数字-替换法(O(n),O(1))
//解题思路
/*替换法(O(n),O(1))
数组存放原则:numbers[i] = i
遍历数组所有元素,交换不符合数组存放原则的元素:
例如[2,3,1,0,2]
遍历0位元素2:(交换0位元素2和2位元素1)->[1,3,2,0,2]
遍历0位元素1:(交换0位元素1和1位元素3)->[3,1,2,0,2]
遍历0位元素3:(交换0位元素3和3位元素0)->[0,1,2,3,2]
依次遍历0、1、2、3位置元素,都符合存放原则numbers[i] = i,不做任何操作
遍历末位元素2,此时末位元素2和2位元素2相等,出现了两次,即数字2位重复元素
*/
public int duplicate (int[] numbers) {
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] != i){
if (numbers[i] == numbers[numbers[i]]) return numbers[i];
int temp = numbers[numbers[i]];
numbers[numbers[i]] = numbers[i];
numbers[i] = temp;
i--;//遍历完0位元素以及交换完数字后,如果0位元素仍不符合数组存放原则:numbers[i] = i,那么还要重新遍历0位元素
}
}
return -1;
}
剑指Offer 03. 数组中重复的数字-巧解:set
利用set的不重复特性。把数组中的元素一个个添加到set中,如果某一元素添加过后,set的长度没有改变,说明该元素重复!
let mySet = new Set();
for (let i = 0; i < numbers.length; i++){
let setLength = mySet.size;
mySet.add(numbers[i]);
if(setLength === mySet.size){
return numbers[i];
}
}
return -1;
通过的代码
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] numbers) {
int[] res = new int[numbers.length];
for(int i = 0; i < numbers.length; i++){
res[numbers[i]]++;
if(res[numbers[i]]==2){
return numbers[i];
}
}
return -1;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] numbers) {
// write code here
if(numbers.length<=0)
return -1;
int len = numbers.length;
int a[] = new int[len];
for(int i = 0;i<len;i++){
++a[numbers[i]];
if(a[numbers[i]]>1)
return numbers[i];
}
return -1;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] numbers) {
if(numbers.length==0){
return -1;
}
int index=0;
while(index<numbers.length){
if(numbers[index]==index){
index++;
}else{
int tmp=numbers[index];
if(numbers[index]==numbers[tmp]){
return numbers[index];
}
numbers[index]=numbers[tmp];
numbers[tmp]=tmp;
}
}
return -1;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
//public int duplicate (int[] numbers) {
// HashSet dict = new HashSet();
// for(int i = 0; i< numbers.length;i++)
// {
// if(dict.contains(numbers[i])){return numbers[i];}
// else{dict.add(numbers[i]);}
// }
// return -1;
// // write code here
//}
public int duplicate(int[] numbers)
{
int temp;
for(int i = 0; i< numbers.length; i++)
{
while(i != numbers[i])
{
if(numbers[i] == numbers[numbers[i]])
{
return numbers[i];
}
temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
return -1;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] numbers) {
int[] res=new int[numbers.length];
for(int i=0;i<numbers.length;i++){
res[numbers[i]]++;
if(res[numbers[i]]==2){
return numbers[i];
}
}
return -1;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] numbers) {
// write code here
if(numbers == null || numbers.length == 0){
return -1;
}
int[] nums = new int[numbers.length];
for(int i = 0; i < numbers.length; i++){
if(nums[numbers[i]] > 0){
return numbers[i];
}else{
nums[numbers[i]]++;
}
}
return -1;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] numbers) {
// write code here
int[] count = new int[numbers.length];
for(int n:numbers){
if(++count[n]>1) return n;
}
return -1;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] numbers) {
// write code here
/*if(numbers==null||numbers.length==0)
return -1;
HashSet set =new HashSet();
for(int i=0;i<numbers.length;i++){
if(numbers[i]>=numbers.length)
return -1;
else {
if(!set.add(numbers[i])){
return numbers[i];
}
}
}
return -1;*/
int[] countarray=new int[numbers.length];
for(int i=0;i<numbers.length;i++) {
countarray[numbers[i]]++;
if(countarray[numbers[i]]>1) {
return numbers[i];
}
}
return -1;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] nums) {
// write code here
int[] count = new int[nums.length];
for(int i : nums){
if(count[i] == 1){
return i;
}
count[i] = 1;
}
return -1;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param numbers int整型一维数组
* @return int整型
*/
//把数字放到下标为该数字的地方,如果该位置的数字和下标已经对应,那么找到一个重复的数字
public static int duplicate (int[] numbers) {
// wri[te code here
int n = numbers.length;
for(int i=0;i<n;i++){
while(i != numbers[i]){
if(numbers[i] == numbers[numbers[i]]) return numbers[i];
swap(numbers,i,numbers[i]);
}
}
return -1;
}
public static void swap(int[] nums,int index1,int index2){
int tmp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = tmp;
}
}
构建乘积数组
题目描述
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)
对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在。
示例1
输入
[1,2,3,4,5]
返回值
[120,60,40,30,24]
描述
这是一篇针对初学者的题解。
知识点:数组
难度:一星
题解
题目描述:给定一个长度为n的数组A,求数组B,B[i] = A[0]A[1]…A[i-1]*A[i+1]…A[n-1]。
要求不能使用除法。
方法:
根据题目描述,如果可以使用除法,就很简单。但是要求不能使用。
假设:
left[i] = A[0]*...*A[i-1]
right[i] = A[i+1]*...*A[n-1]
所以:
B[i] = left[i] * right[i]
这样就避免使用了除法。但是如果对每个B[i], 0<=i<n,都这么求,显然时间复杂度太高。
我们把整个结果画到下面图:
可知:
left[i+1] = A[0]*...A[i-1]*A[i]
right[i+1] = A{i+2]*...*A[n-1]
于是,
left[i+1] = left[i] * A[i]
right[i] = right[i+1] * A[i+1]
所以,我们可以先把所有的left[i]求出,right[i]求出。
代码如下:
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
vector<int> B(A.size(), 1);
for (int i=1; i<A.size(); ++i) {
B[i] = B[i-1] * A[i-1]; // left[i]用B[i]代替
}
int tmp = 1;
for (int j=A.size()-2; j>=0; --j) {
tmp *= A[j+1]; // right[i]用tmp代替
B[j] *= tmp;
}
return B;
}
};
时间复杂度:O(N)
空间复杂度: O(1)
通过的代码
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
/**暴力解法
int[] B=new int[A.length];
for(int i=0;i<A.length;i++){
int tmp=1;
for(int j=0;j<A.length;j++){
if(j==i){
continue;
}
tmp=tmp*A[j];
}
B[i]=tmp;
}
return B;**/
//elegant solution
int[] B=new int[A.length];
B[0]=1;
for(int i=1;i<A.length;i++){
B[i]=B[i-1]*A[i-1];
}
int temp=1;
for(int i=A.length-2;i>=0;i--){
temp=temp*A[i+1];
B[i]=B[i]*temp;
}
return B;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] a) {
int[] left = new int[a.length];
int[] right = new int[a.length];
int[] ret = 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;ret[0]=right[0];
for(int i=1;i<a.length;i++){
left[i] = left[i-1] * a[i-1];
ret[i] = left[i] * right[i];
}
return ret;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int n = A.length;
int[] b = new int[n];
int l = 1;
for(int i = 0 ; i < n ; i++){
int right = 1;
for(int j = i+1 ; j < n ;j++){
right *= A[j];
}
b[i] = right*l;
l *= A[i];
}
return b;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
if(A.length == 0) return new int[2];
int[] b = new int[A.length];
b[0] = 1;
for(int i=1;i<A.length;i++)
{
b[i] = A[i-1] * b[i-1];
}
int t = 1;
for(int i=A.length-2;i>=0;i--)
{
t *= A[i+1];
b[i] *= t;
}
return b;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int[] B = new int[A.length];
B[0]=1;
B[1]=A[0];
for(int i=2;i<A.length;i++){
B[i] = B[i-1]*A[i-1];
}
int temp=1;
for(int i=A.length-2;i>=0;i--){
temp = temp*A[i+1];
B[i] = temp*B[i];
}
return B;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int len = A.length;
int[] B = new int[len];
int[] a = new int[len];
int[] b = new int[len];
a[0] = 1;
b[0] = 1;
for (int i = 1;i<len;i++){
a[i] = a[i-1]*A[i-1];
b[i] = b[i-1]*A[len-i];
}
for (int i = 0;i<len;i++){
B[i] = a[i]*b[len-i-1];
}
return B;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int len = A.length;
int[] m = new int[len];
int[] B = new int[len];
m[0] = 1;
B[len - 1] = 1;
for(int i = 1; i < len; i++){
m[i] = m[i - 1] * A[i - 1];
}
for(int j = len - 2; j >= 0; j--){
B[j] = B[j + 1] * A[j + 1];
}
for(int i = 0; i < len; i++){
B[i] = m[i] * B[i];
}
return B;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int[] B = new int[A.length];
int num = 1;
for(int i = 0 ; i < A.length ; i++){
num = 1;
for(int j = 0 ; j < A.length ; j++){
if(j != i){
num *= A[j];
}
}
B[i] = num;
}
return B;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
if(A.length <= 1){
return null;
}
int[] B = new int[A.length];
for(int i = 0; i < A.length; i++){
int result = 1;
for(int j = 0; j < A.length; j++){
if(j != i){
result *= A[j];
}
}
B[i] = result;
}
return B;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int[] B = new int[A.length];
int temp = 0;
for (int i = 0; i < A.length; i++) {
temp = A[i];
A[i] = 1;
B[i] = 1;
for (int j = 0; j < A.length; j++) {
B[i] *= A[j];
}
A[i] = temp;
}
return B;
}
}
二叉搜索树的第k个结点
题目描述
给定一棵二叉搜索树,请找出其中的第k小的TreeNode结点。
示例1
输入
{5,3,7,2,4,6,8},3
返回值
{4}
说明
按结点数值大小顺序第三小结点的值为4
说明:本题目包含复杂数据结构TreeNode
《剑指offer》 第54题 二叉搜索树的第k个节点
题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)中,按结点数值大小顺序第三小结点的值为4。
题目给出的是该二叉树的层序遍历结果。实际如图
可以看到,4是按大小排序时,第3个节点。如果K=3,即4为所求的值。而对应大小排序的就是中序遍历,中序遍历结果是{2,3,4,5,6,7,8},所以本题核心需要使用到中序遍历。然后对中序遍历的结果中,取第K个元素即可。这里可以使用数字或者集合存储遍历结果,然后取第k个,但是会产生额外的空间复杂度,当然也可以使用一个变量记录遍历的节点数,当遍历到K个节点时即为所求。
写法1:
使用数组存储,代码看起来非常的清晰。这个代码还有优化空间,因为完成了所有的遍历,事实上可以在遍历到K时,让遍历退出。
import java.util.ArrayList;
public class Solution {
ArrayList<TreeNode> list = new ArrayList<>();
TreeNode KthNode(TreeNode pRoot, int k) {
OrderTraversal(pRoot);
if(k<1 || list.size()<k)
return null;
return list.get(k-1);
}
// 中序遍历
public void OrderTraversal(TreeNode node) {
if(node != null) {
OrderTraversal(node.left);
list.add(node);
OrderTraversal(node.right);
}
}
}
//优化:传入参数K,并在每次遍历前都判断下k是否符合要求。
// public void OrderTraversal(TreeNode node,int k) {
// if(node != null) {
// if ( list.size() >= k ) //提前退出
// return ;
// OrderTraversal(node.left,k);
// list.add(node);
// OrderTraversal(node.right,k);
// }
// }
写法2:
使用递归。
public class Solution {
int index=0;//记录当前遍历的节点数量
TreeNode KthNode(TreeNode pRoot, int k) {
TreeNode pNode = null;
if(pRoot==null || k<=0)//这里就进行了根节点为null的判断
return pNode;
pNode = getKthNode(pRoot,k);
return pNode;
}
public TreeNode getKthNode(TreeNode pRoot, int k){
TreeNode node = null;
if(pRoot.left != null)
node = getKthNode(pRoot.left,k);
//pRoot的某个子节点为null时进入下面代码
index++;
if(k == index)
node = pRoot; //满足条件就将pRoot赋值给node
//当node赋值时已经找到时,不需要再遍历了,赋值后就不为null,所以要加上这个判断条件
//写node == null或者k<index都是可以的。
if(node == null && pRoot.right!=null)
node = getKthNode(pRoot.right,k);
return node;
}
}
写法3:
使用栈。
import java.util.Stack;
public class Solution {
TreeNode KthNode(TreeNode pRoot, int k)
{
int count = 0;
if(pRoot == null || k <= 0){
return null;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode node = pRoot;
//中序遍历
while(!stack.isEmpty() || node != null){
if(node != null){
stack.push(node); //当前节点不为null,应该寻找左子节点,准备让左子节点入栈
node = node.left;
}else{
node = stack.pop();//当前节点null则弹出栈内元素,相当于按顺序输出最小值。
count++;//弹出时,才应该计数
if(count == k){ //相等时就返回
return node;
}
node = node.right;//左边遍历完,还没到K,就得找右子节点
}
}
return null;
}
}
通过的代码
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
int cout=0;
TreeNode node;
TreeNode KthNode(TreeNode pRoot, int k) {
if (k==0) return null;
NodePring(pRoot,k);
return node;
}
void NodePring(TreeNode pRoot, int k){
if(pRoot==null) return;
if (k==cout) return;
KthNode(pRoot.left,k);
cout++;
if(cout==k) node =pRoot;
KthNode(pRoot.right,k);
}
}
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList<TreeNode> list = new ArrayList<>();
TreeNode KthNode(TreeNode pRoot, int k) {
InOrder(pRoot);
if(list.size()>=k&&k>0){
return list.get(k-1);
}
return null;
}
public void InOrder(TreeNode pRoot){
if(pRoot!=null){
InOrder(pRoot.left);
list.add(pRoot);
InOrder(pRoot.right);
}
}
}
public class Solution {
private TreeNode target = null;
private int k1 = 0;
TreeNode KthNode(TreeNode pRoot, int k)
{
k1 = k;
getKthNode(pRoot);
return target;
}
private void getKthNode(TreeNode pRoot){
if(pRoot == null || k1 <= 0){
return;
}
getKthNode(pRoot.left);
k1--;
if(k1 == 0){
target = pRoot;
return;
}
getKthNode(pRoot.right);
}
}
public class Solution {
int cnt=0;
TreeNode res;
TreeNode KthNode(TreeNode pRoot, int k) {
if(pRoot==null||k<1)return null;
preOrder(pRoot,k);
return res;
}
private void preOrder(TreeNode pRoot,int k){
if(pRoot==null)return;
preOrder(pRoot.left,k);
cnt++;
if(cnt==k){
res=pRoot;
return;
}
preOrder(pRoot.right,k);
}
}
public class Solution {
int index = 1;
TreeNode ans = null;
TreeNode KthNode(TreeNode pRoot, int k) {
dfs(pRoot, k);
return ans;
}
public void dfs(TreeNode pRoot, int k){
if(pRoot == null){
return;
}
dfs(pRoot.left, k);
if(index > k){
return;
}
if(index == k){
ans = new TreeNode(pRoot.val);
}
index++;
dfs(pRoot.right, k);
}
}
import java.util.*;
public class Solution {
ArrayList<TreeNode> nodeList=new ArrayList<>();
TreeNode KthNode(TreeNode pRoot, int k) {
inOrder(pRoot);
if(k<=0||k>nodeList.size()){
return null;
}
else{
return(nodeList.get(k-1));
}
}
public void inOrder(TreeNode pRoot){
if(pRoot==null){
return;
}
inOrder(pRoot.left);
nodeList.add(pRoot);
inOrder(pRoot.right);
}
}
public class Solution {
int count = 0;
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot != null){
TreeNode node = KthNode(pRoot.left, k);
if(node != null){
return node;
}
count++;
if(count == k){
return pRoot;
}
node = KthNode(pRoot.right, k);
if(node != null){
return node;
}
}
return null;
}
}
public class Solution {
TreeNode result;
int count=0;
TreeNode KthNode(TreeNode pRoot, int k)
{
//二叉搜索树的中序遍历就是从小到大的
if(pRoot==null){
return null;
}
count=k;
deal(pRoot);
return result;
}
void deal(TreeNode pRoot)
{
//中序遍历
if(pRoot.left!=null){
deal(pRoot.left);
}
count--;
if(count==0){
result=pRoot;
}
if(pRoot.right!=null){
deal(pRoot.right);
}
}
}
import java.util.*;
public class Solution {
TreeNode KthNode(TreeNode pRoot, int k) {
if(k <= 0 || pRoot == null) {
return null;
}
Stack<TreeNode> stack = new Stack<> ();
int count = 0;
while(pRoot != null || !stack.isEmpty()) {
if(pRoot != null){
stack.push(pRoot);
pRoot = pRoot.left;
}else{
TreeNode node = stack.pop();
count++;
if(count == k){
return node;
}
node = node.right;
pRoot = node;
}
}
return null;
}
}
import java.util.*;
public class Solution {
ArrayList<TreeNode> arr;
TreeNode KthNode(TreeNode pRoot, int k) {
if(k==0){
return null;
}
arr=new ArrayList<TreeNode>();
processM(pRoot);
if(k<=arr.size()){
return arr.get(k-1);
}else{
return null;
}
}
void processM(TreeNode pRoot){
if(pRoot==null){
return;
}
processM(pRoot.left);
arr.add(pRoot);
processM(pRoot.right);
}
}