剑指offer 题集


声明:题、图源自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;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值