文章目录
- 剑指offer
- 3:数组中重复的数字
- 04:二维数组中的查找
- 05:替换空格
- 06:从尾到头打印链表
- 09:用两个栈实现队列
- 10 - I :斐波那契数列
- 10 - II :青蛙跳台阶
- 12:矩阵中的路径
- 14 - I :剪绳子
- 14 - II :剪绳子 II
- 15:二进制中1的个数
- 20:表示数值的字符串
- 22:链表中倒数第k个节点
- 24:反转链表
- 29:顺时针打印矩阵
- 39:数组中出现次数超过一半的元素
- 40:最小的k个数
- 42:连续子数组的最大和
- 46:二叉树的右侧视图
- 47:礼物的最大价值
- 50:第一个只出现一次的字母
- 53 - II : 0~n-1中缺失的数字
- 55 - I :二叉树的深度
- 55 - II :平衡二叉树
- 58 - I :翻转单词顺序
- 64:求1+2+...+n
- 68 - I :二叉搜索树的最近公共祖先
- 68 - II :二叉树的最近公共父节点
声明:题、图源自Leecode:https://leetcode-cn.com/
剑指offer
3:数组中重复的数字
题目:
解题①:使用额外的数组空间。执行用时:2 ms ;内存消耗:46.1 MB
class Solution {
public int findRepeatNumber(int[] nums) {
//创建一个长度为n的数组,初始化0
int[] sum=new int[nums.length];
//若数字出现,对应下标+1;
int j=0;
for(int i=0;i<nums.length;i++){
j=nums[i]; //当前下标nums的值
sum[j]++; //sum对应下标+1
if(sum[j]>1){ //输出第一个大于1的下标
return j;
}
}
return 0; //一定会有重复的数字,所以不会进行到这一步。
}
}
解题②:在原数组的基础上,进行。
思路参考《剑指offer》,如下:
代码如下:执行用时:0 ms ;内存消耗:45.7 MB
public int findRepeatNumber(int[] nums) {
if(nums.length==2) return nums[0];
int tmp=0;
for(int i=0;i<nums.length;){
if(nums[i]==i){ //若当前索引等于当前的值,则判断下一个
i++;
}else{ //若当前索引不等于当前的值,则与索引为当前值的数值进行比较
if(nums[i]==nums[nums[i]]){ //若相等则找到了重复的数字
// System.out.println("equal:"+nums[i]);
return nums[i];
}else{ //互换两个位置的数值,再重新比较
tmp=nums[i];
nums[i]=nums[tmp];
nums[tmp]=tmp;
// System.out.println(i+"===>change:"+nums[i]+"--"+nums[nums[i]]+"--"+tmp);
//此处不进行i++,继续比较当前位置,直到出现当前索引=当前值,或者发现了重复数字。
}
}
}
return 0;
}
04:二维数组中的查找
题目:在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
解答:执行用时 0 ms ; 内存消耗 44 MB。
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int xLen=matrix.length;
int yLen=matrix[0].length;
if(target<matrix[0][0] || target>matrix[xLen-1][yLen-1]) {
return false;
}
int row=0,col=yLen-1;
while(row<xLen && col>-1) {
if(matrix[row][col] == target) {
return true;
}
if(matrix[row][col] > target) {
col--;
}else{
row++;
}
}
return false;
}
05:替换空格
题目
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
解答1:利用库函数replace。执行用时 0ms;内存消耗36 MB。
class Solution {
public String replaceSpace(String s) {
return s.replace(" ","%20");
}
}
解答2:遍历。执行用时 0ms;内存消耗36.3 MB。
public String replaceSpace(String s) {
StringBuilder sb=new StringBuilder();
char c='a';
for(int i=0; i<s.length(); i++) {
c=s.charAt(i);
if(c == ' ') {
sb.append("%20");
}else {
sb.append(c);
}
}
return sb.toString();
}
06:从尾到头打印链表
题目:输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/
解答1:利用栈先入后出的特点。执行用时 1 ms ; 内存消耗 38.9 MB。
public int[] reversePrint(ListNode head) {
if(head == null) {
return new int[0];
}
// 栈:先进后出
Stack<Integer> stack=new Stack();
ListNode p1=head;
while(p1 != null) {
stack.push(p1.val);
p1=p1.next;
}
int[] res=new int[stack.size()];
for(int i=0;i<res.length;i++) {
res[i]=stack.pop();
}
return res;
}
解答2:空间复杂度O(1)。执行用时 0 ms ; 内存消耗 38.9 MB。
public int[] reversePrint(ListNode head) {
if(head == null) {
return new int[0];
}
// 计算长度
int len=0;
ListNode p1=head;
while(p1 != null) {
p1=p1.next;
len++;
}
int[] res=new int[len];
// 倒序存储在数组中
p1=head;
for(int i=len-1;i>=0;i--) {
res[i]=p1.val;
p1=p1.next;
}
return res;
}
09:用两个栈实现队列
题目:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof
解答:执行用时 47 ms ; 内存消耗 46.6 MB。
当将s1的内容从上到下压到s2的时候,s2出栈的元素永远是当前s1和s2中最久的,即头元素。当s2不为空的时候,不要把s1的内容压到s2中,避免打乱顺序,只有s2为空才移动s1内容。实现s1入队,s2出队。
class CQueue {
private Stack<Integer> s1;
private Stack<Integer> s2;
public CQueue() {
s1=new Stack();
s2=new Stack();
}
/**
* 在队列尾部插入整数
* @param value:整数值
*/
public void appendTail(int value) {
s1.push(value);
}
/**
* 在队列头部删除整数
* @return 返回删除的整数值
*/
public int deleteHead() {
if(s1.isEmpty() && s2.isEmpty()) {
return -1;
}
if(s2.isEmpty()) {
while(!s1.isEmpty()) {
s2.push(s1.pop());
}
}
return s2.pop();
}
}
10 - I :斐波那契数列
题目:写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof
解答1:暴力,超出时间限制
public int fib(int n) {
if(n == 0) return 0;
if(n == 1) return 1;
return (fib(n-1)+fib(n-2))%1000000007;
}
解答2:用数组存放记忆集。执行用时 0 ms ; 内存消耗 35.3 MB。
public int fib(int n) {
if(n <= 1) return n;
int[] res=new int[n+1];
res[0]=0;
res[1]=1;
for(int i=2;i<=n;i++) {
res[i]=res[i-1]+res[i-2];
res[i]=res[i]>=1000000007?res[i]-1000000007:res[i];
}
return res[n];
}
解答3:执行用时 0 ms ; 内存消耗 35.3 MB。
针对解答3不采用数组存放,实际中只会用到当前n的前两位n-1、n-2,因此只需存放这两个就行。
public int fib(int n) {
if(n <= 1) return n;
int cur=1,pre=0;
for(int i=2;i<=n;i++) {
// 对当前值来说:cur是上一个值,pre是上上一个的值,二者之和等于当前值.
cur=cur+pre;
// 更新上一值:pre=cur+pre-pre=cur,即上一值=上一个cur
pre=cur-pre;
// 结果取模
cur=cur>=1000000007?cur-=1000000007:cur;
}
return cur;
}
10 - II :青蛙跳台阶
题目:
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解答:执行用时:0 ms ;内存消耗:34.8 MB
public int numWays(int n) {
if(n<=1) {
return 1;
}
int[] num=new int[n+1];
num[0]=1;
num[1]=1;
for(int i=2; i<=n; i++) {
num[i]=(num[i-1]+num[i-2])%1000000007;
}
return num[n];
12:矩阵中的路径
题目:给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
解题:执行用时:6 ms ;内存消耗:40.1 MB
class Solution {
public boolean exist(char[][] board, String word) {
if(word.length() == 0){
return true;
}
// 用于存储当前单元格使用情况
boolean[][] use=new boolean[board.length][board[0].length];
char[] w=word.toCharArray();
// 表示当前遍历的是第几个元素,用于判断单元格是否使用
int num=1;
for(int i=0;i<board.length;i++) {
for(int j=0;j<board[0].length;j++) {
if(find(board,i,j,w,0,num,use)){
return true;
}
}
}
return false;
}
private boolean find(char[][] board,int i,int j,char[] c,int current,int num,boolean[][] use) {
// 匹配元素大于等于单词长度,表示全部匹配成功
if(current == c.length) {
return true;
}
// 索引越界 字符不匹配 路径已存在该节点
if(i >= board.length || j >= board[0].length || i < 0 || j < 0 || board[i][j] != c[current] || use[i][j]) {
return false;
}
// 匹配成功
use[i][j]=true;
num++;
current++;
// 遍历上下左右
boolean result= find(board,i-1,j,c,current,num,use) || find(board,i+1,j,c,current,num,use)
|| find(board,i,j-1,c,current,num,use) || find(board,i,j+1,c,current,num,use);
use[i][j]=false;
return result;
}
}
14 - I :剪绳子
题目:给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。2<=n<=58
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof
解答:执行用时 0 ms ; 内存消耗 35.3 MB。
public int cuttingRope(int n) {
if(n == 2) return 1;
if(n == 3) return 2;
int res=1;
while(n > 4) {
res *= 3;
n -= 3;
}
return res*n;
}
14 - II :剪绳子 II
题目:给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。2<=n<=1000(要考虑整型超过最大值的情况)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof
解答:执行用时 0 ms ; 内存消耗 35.2 MB。
public int cuttingRope(int n) {
if(n == 2) {
return 1;
}
if(n == 3) {
return 2;
}
long res=1; // 存储结果值.使用long型是为了避免后面乘3越界
// 大于4的数都要进行拆分才能得到最优解
while(n > 4) {
// 拆分成3比2更优
res *= 3;
// 若值大于1000000007:求余
res %= 1000000007;
// 减去拆分出来的3
n -= 3;
}
// 当n<=4时:无需再拆分(4本身等于2*2,拆分与否没有必要)
res *= n;
// 不能使用 res -= 1000000007 是因为*3后可能减一次还是超过1000000007
res %= 1000000007;
return (int)res;
}
15:二进制中1的个数
题目:编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。
链接:https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/
解答:执行用时:0 ms ;内存消耗:35.4 MB
public int hammingWeight(int n) {
int sum=0;
while(n != 0){
if((n&1) == 1){
sum++;
}
n>>>=1;
}
return sum;
}
20:表示数值的字符串
题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。规则可查看链接
链接:https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/
解答:执行用时 2 ms ; 内存消耗 38.2 MB。
太菜了,是参考别人的代码:https://leetcode-cn.com/u/ye-jin-tian-ming-2w/
public boolean isNumber(String s) {
if(s == null || s.length() == 0) {
return false;
}
// isNum-是否是数字,isE存在e,isPoint存在"."
// 结尾值可能是数字或者".",
boolean isNum=false,isE=false,isPoint=false;
// trim()能去除字符串头尾的空格
char[] str=s.trim().toCharArray();
for(int i=0;i<str.length;i++) {
if(str[i] >= '0' && str[i] <= '9') {
isNum=true;
}else if(str[i] == '.') {
// 小数点前面不能有e或者.
if(isPoint || isE) {
return false;
}
isPoint=true;
}else if(str[i] == 'e' || str[i] == 'E') {
// 前面不是数字,或者已经出现过一次e
if(!isNum || isE) {
return false;
}
isE=true;
// e后面必须跟着数字,清空isNum的值
isNum=false;
}else if(str[i] == '+' || str[i] == '-') {
// 符号只会出现在第一位,或者e后面
if(i != 0 && str[i-1] != 'e' && str[i-1] != 'E') {
// 符号不在第一位且上一位不为e
return false;
}
}else {
// 非法字符
return false;
}
}
return isNum;
}
22:链表中倒数第k个节点
题目:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
题解:执行用时:1 ms ;内存消耗:36.2 MB
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode p1=head;
ListNode p2=head;
int distance=k;
// 让p1和p2相距k个节点
while(p2 != null && distance != 0) {
p2=p2.next;
distance--;
}
// k大于链表的长度
if(distance != 0) {
return head;
}
// 保持p1和p2的距离,当p2为null时,倒数第k个即p1开始的链表
while(p2 != null) {
p1=p1.next;
p2=p2.next;
}
return p1;
}
24:反转链表
题目:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
解题:执行用时:0 ms ;内存消耗:38.3 MB
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode p1=head.next,p2=head,p3=null;
while(p1 != null) {
p2.next=p3;
p3=p2;
p2=p1;
p1=p1.next;
}
p2.next=p3;
return p2;
}
29:顺时针打印矩阵
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
解题:执行用时:116 ms ;内存消耗:40.5 MB
public int[] spiralOrder(int[][] matrix) {
if(matrix.length == 0) {
return new int[0];
}
int x=matrix.length;int y=matrix[0].length;
// 用一个boolean数组存储是否打印过
boolean[][] visited=new boolean[x][y];
int i=0,j=0,k=0;
int[] result=new int[x*y];
// 右->下->左->上
while(true) {
// 向右
while(k<result.length && j<y) {
if(!visited[i][j]){
result[k++]=matrix[i][j];
visited[i][j++]=true;
}else{
break;
}
}
j--;i++;
// 向下
while(k<result.length && i<x) {
if(!visited[i][j]){
result[k++]=matrix[i][j];
visited[i++][j]=true;
}else{
break;
}
}
i--;j--;
// 向左
while(k<result.length && j<y && i<x && i>=0 && j>=0) {
if(!visited[i][j]){
result[k++]=matrix[i][j];
visited[i][j--]=true;
}else{
break;
}
}
j++;i--;
// 向上
while(k<result.length && j<y && i<x && i>=0 && j>=0) {
if(!visited[i][j]){
result[k++]=matrix[i][j];
visited[i--][j]=true;
}else{
break;
}
}
if(k == result.length) {
break;
}
i++;j++;
}
return result;
}
39:数组中出现次数超过一半的元素
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
链接:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/
解题:执行用时:3 ms ;内存消耗:44.3 MB
public int majorityElement(int[] nums) {
if(nums.length<3) return nums[0];
Arrays.sort(nums);
int current=nums[0];
int sum=0;
for(int i=0;i<nums.length;i++){
if(current == nums[i]){
sum++;
if(sum>(nums.length/2)){
return current;
}
}else{
current=nums[i];
sum=1;
}
}
return -1;
}
40:最小的k个数
题目
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。(注意数字不用去重)
解法1:利用TreeMap的自动排序特点存储元素。执行用时:45 ms ;内存消耗:40 MB
public int[] getLeastNumbers(int[] arr, int k) {
if(arr.length ==0 || k == 0) {
return new int[0];
}
TreeMap<Integer,Integer> tm=new TreeMap<>();
for(int i=0; i<arr.length; i++) {
if(tm.containsKey(arr[i])){
tm.put(arr[i],tm.get(arr[i])+1);
} else {
tm.put(arr[i],1);
}
}
int[] result=new int[k];
int j=0;
for(Map.Entry<Integer, Integer> current:tm.entrySet()){
if(j == k){
break;
}
for(int i=0;i<current.getValue();i++){
if(j == k){
break;
}
result[j++]=current.getKey();
}
}
return result;
}
解法2:利用快速排序,先排序后截取。执行用时:7 ms ;内存消耗:39.7 MB
public int[] getLeastNumbers(int[] arr, int k) {
if(arr.length ==0 || k == 0) {
return new int[0];
}
quickSort(0,arr.length-1,arr);
return Arrays.copyOf(arr,k);
}
void quickSort(int low,int high,int[] arr) {
if(low>high) {
return;
}
int i=low;
int j=high;
int sign=arr[low];
while(i != j) {
while(arr[j] >= sign && i<j) {
j--;
}
if(j>i) {
arr[i]=arr[j];
i++;
}
while(arr[i] <= sign && i<j) {
i++;
}
if(i<j) {
arr[j]=arr[i];
j--;
}
}
arr[i]=sign;
quickSort(low,i-1,arr);
quickSort(i+1,high,arr);
}
42:连续子数组的最大和
题目:输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。
链接:https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/
解答:执行用时:1 ms ;内存消耗:45 MB
public int maxSubArray(int[] nums) {
int pre=0;
int max=nums[0];
for(int i=0;i<nums.length;i++){
int sum=0;
if(pre >= 0) {
sum=nums[i]+pre;
}else{
sum=nums[i];
}
max=Math.max(max,sum);
pre=sum;
}
return max;
}
46:二叉树的右侧视图
题目:给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
链接:https://leetcode-cn.com/problems/WNC0Lk/
解答:执行用时 1 ms ; 内存消耗 37.2 MB。
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res=new ArrayList<>();
if(root == null) {
return res;
}
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int size=queue.size();
res.add(queue.peek().val);
for(int i=0;i<size;i++) {
TreeNode cur=queue.poll();
// 先存右元素
if(cur.right != null) {
queue.offer(cur.right);
}
if(cur.left != null) {
queue.offer(cur.left);
}
}
}
return res;
}
47:礼物的最大价值
**题目:**在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
解法:执行用时:2 ms ;内存消耗:41 MB
public int maxValue(int[][] grid) {
int[] result=new int[grid[0].length+1];
for(int i=1;i<=grid.length;i++) {
for(int j=1;j<=grid[0].length;j++) {
result[j]=Math.max(result[j-1],result[j])+grid[i-1][j-1];
}
}
return result[grid[0].length];
}
50:第一个只出现一次的字母
题目
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
解答:执行用时:7 ms ;内存消耗:38.4 MB
public char firstUniqChar(String s) {
if(s.length() == 0) {
return ' ';
}
if(s.length() == 1) {
return s.charAt(0);
}
char[] letter=new char[26];
for(int i=0; i<s.length(); i++) {
letter[s.charAt(i)-'a']++;
}
for(int i=0; i<s.length(); i++) {
if(letter[s.charAt(i)-'a'] == 1) {
return s.charAt(i);
}
}
return ' ';
}
}
53 - II : 0~n-1中缺失的数字
题目:一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
解答:有序都采用二分法。运行时间 0 ms,内存消耗 38.7 MB
public int missingNumber(int[] nums) {
return getDefectNumber(nums,0,nums.length-1);
}
private int getDefectNumber(int[] nums,int low,int high) {
if((low+1) >= high) {
if(nums[low]+2 == nums[high]) {
return nums[low]+1;
} else if(nums[low] > low) {
return nums[low]-1;
} else {
return nums[high]+1;
}
}
// 判断左半部分和右半部分哪边少了一位
int mid=(low+high)/2;
if(nums[mid] != mid) {
//在左半部分
return getDefectNumber(nums,low,mid);
} else {
// 在右半部分
return getDefectNumber(nums,mid,high);
}
}
55 - I :二叉树的深度
题目:输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
链接:https://leetcode-cn.com/problems/er-cha-shu-de-shen-du-lcof/
解答1:执行用时 1 ms ; 内存消耗 38 MB。
public int maxDepth(TreeNode root) {
if(root == null) return 0;
int sum=0;
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
// 层次遍历
while(!queue.isEmpty()) {
sum++;
int size=queue.size();
for(int i=0;i<size;i++) {
TreeNode t=queue.poll();
if(t.left != null) queue.offer(t.left);
if(t.right != null) queue.offer(t.right);
}
}
return sum;
}
解答2:执行用时 0 ms ; 内存消耗 38.4 MB。
public int maxDepth(TreeNode root) {
if(root == null) {
return 0;
}
// 取左右子树大的那一边的值
return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
55 - II :平衡二叉树
题目:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
链接:https://leetcode-cn.com/problems/ping-heng-er-cha-shu-lcof/
解答1:执行用时 1 ms ; 内存消耗 38.6 MB。
前序遍历:自顶向下递归。
public boolean isBalanced(TreeNode root) {
if(root == null) {
return true;
}
// res返回的是当前root左右节点的平衡度
int res=Math.abs(treeSum(root.left)-treeSum(root.right));
// 还要计算每个节点的左右节点平衡度
if(res <= 1) {
return isBalanced(root.left) && isBalanced(root.right);
}
return false;
}
private int treeSum(TreeNode node) {
// 前序遍历
if(node == null) {
return 0;
}
// 左子树和右子树的最大值加上当前值,即为当前层。
return Math.max(treeSum(node.left),treeSum(node.right))+1;
}
解答2:执行用时 0 ms ; 内存消耗 36 MB。
后序遍历:自底向上。
public boolean isBalanced(TreeNode root) {
return treeSum(root) != -1;
}
private int treeSum(TreeNode node) {
// 后序遍历 左右根
if(node == null) {
return 0;
}
int left=treeSum(node.left);
// 剪枝:高度不可能为-1,若为-1,说明不平衡
if(left == -1) {
return -1;
}
int right=treeSum(node.right);
if(right == -1) {
return -1;
}
return Math.abs(left-right)>1?-1:Math.max(left,right)+1;
}
58 - I :翻转单词顺序
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。
题解:运行时间 2 ms,内存消耗 38.4 MB
public String reverseWords(String s) {
char[] word=s.toCharArray();
StringBuilder sb=new StringBuilder();
int j=0;int k=0;int before=0;
for(int i=word.length-1;i>=0;) {
while(i >= 0 && word[i] == ' ') {
i--;
}
if(i >= 0 && before == 1) {
sb.append(" ".toString());
}
j=i;
while(i >= 0 && word[i] != ' ') {
i--;
}
// 此时[i+1,j]是一个单词
if(i >= 0) {
sb.append(s.substring(i+1,j+1));
}else if(j >= 0){
sb.append(s.substring(0,j+1));
}
before=1;
}
return sb.toString();
}
64:求1+2+…+n
题目
求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
解答:运行时间 0 ms,内存消耗 35.1 MB
public int sumNums(int n) {
return n*(n+1)>>1;
}
68 - I :二叉搜索树的最近公共祖先
题目:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/
解答:执行用时 5 ms ; 内存消耗 39.1 MB。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == p || root == q) {
return root;
}
TreeNode res=root;
boolean left1=false,left2=false;
if(p.val < root.val) {
left1=true;
}
if(q.val < root.val) {
left2=true;
}
// 都在左子树
if(left1 && left2) {
res=lowestCommonAncestor(root.left,p,q);
}
// 都在右子树
if(!left1 && !left2) {
res=lowestCommonAncestor(root.right,p,q);
}
// 一个在左子树一个在右子树
return res;
}
68 - II :二叉树的最近公共父节点
题目:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
链接:https://leetcode-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/
解答1:运行时间 227 ms,内存消耗 39.4 MB
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
LinkedList<TreeNode> first=new LinkedList<>();
LinkedList<TreeNode> second=new LinkedList<>();
// 1.查询出从根节点到两个目标节点的路径
findPath(root,p,first);
findPath(root,q,second);
// 2.两个路径中最后一个公共节点就是最近的公共父节点
return lastCommon(first,second);
}
/**
* 查找根节点到goal的路径,存储到path中
*/
private void findPath(TreeNode root,TreeNode goal,LinkedList<TreeNode> path) {
// 前序遍历
if(root == null){
return;
}
path.add(root);
if(root == goal){
return; // 找到路径
}
if(path.peekLast() != goal) {
findPath(root.left,goal,path);
}
if(path.peekLast() != goal) {
findPath(root.right,goal,path);
}
if(path.peekLast() != goal) {
path.removeLast();
}
}
/**
* 查找path1和path2相等的节点,即为公共父节点
*/
private TreeNode lastCommon(LinkedList<TreeNode> path1,LinkedList<TreeNode> path2) {
TreeNode result=null;
int n=Math.min(path1.size(),path2.size());
for(int i=0;i<n;i++){
if(path1.get(i) == path2.get(i)) {
result=path1.get(i);
}
}
return result;
}
}
解答2:参照官方题解。运行时间 6 ms,内存消耗 40 MB
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 若root等于p或q,则返回root;若root为null,则返回null
if(root == null || root == p || root == q) {
return root;
}
// 递归左右子树,如果存在要寻找的节点,则返回
TreeNode left=lowestCommonAncestor(root.left,p,q);
TreeNode right=lowestCommonAncestor(root.right,p,q);
// 若left=null,则目标节点都在右子树
if(left == null) {
return right;
}
// 若right=null,则目标节点都在左子树
if(right == null) {
return left;
}
// 若left和right都不为null,则目标节点为当前根节点。
return root;
}
}