Java入门编程练习题

题目来源:LeetCode



杨辉三角

1、第n行有n个数字
2、每一行的开始和结尾数字都为1
3、第n+1行的第i个数字等于第n行的i-1个数字加上第n行的i个数字

public class Main {
    private void print(int[][] arr){
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j <= i; j++) {
                System.out.print(arr[i][j]+"\t");
            }
            System.out.println();
        }
    }

    private void rect(int rows){
        int[][] arr = new int[rows][rows];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j <= i; j++) {
                if (j==0 || i==j) {
                    arr[i][j] = 1;
                }else {
                    arr[i][j] = arr[i-1][j-1] + arr[i-1][j];
                }
            }
        }
        print(arr);
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.rect(10);
    }
}

在这里插入图片描述


删除排序数组中的重复项

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

示例 1: 给定数组 nums = [1,1,2], 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。

示例 2: 给定 nums = [0,0,1,1,1,2,2,3,3,4],函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。

解答: 由于已经排序,只需一次遍历,将数字往前移,后面部分即使乱序也不用管

class Solution {
    public int removeDuplicates(int[] nums) {
        if (nums.length == 0) return 1;
        int max = nums[0];
        int index = 1;
        int temp = 0;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i]>max){
                max = nums[i];
                temp = nums[i];
                nums[i] = nums[index];
                nums[index] = temp;
                index ++;
            }
        }
        return index;
    }
}

在这里插入图片描述
在这里插入图片描述

最后一个单词的长度

给定一个仅包含大小写字母和空格 ’ ’ 的字符串 s,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。 如果不存在最后一个单词,请返回 0 。

说明:一个单词是指仅由字母组成、不包含任何空格字符的 最大子字符串。

示例: 输入: “Hello World” ,输出: 5

解答:从后往前,先过滤无用空格

注意: 最后有多个空格的情况

class Solution {
    public int lengthOfLastWord(String s) {
        int count = 0;
        for (int i = s.length()-1; i >= 0; i--) {
            if (s.charAt(i) == ' '){
                if (count==0) continue;
                break;
            }else {
                count ++;
            }
        }
        return count;
    }
}

在这里插入图片描述

加一

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 : 输入: [1,2,3] ,输出: [1,2,4],解释: 输入数组表示数字 123。

解答:从后往前,逢10进1

注意: 首位是9的情况

class Solution {
    public int[] plusOne(int[] digits) {
        for (int i = digits.length-1; i >= 0; i--) {
            digits[i] = (digits[i]+1)%10;
            if (digits[i] != 0) {
                break;
            }
        }
        if(digits[0] == 0){
            //int[] arr = new int[digits.length+1];
            //arr[0] = 1;
            //System.arraycopy(arr,0,digits,0,digits.length);
            //digits = arr;
            digits = new int[digits.length+1];
            digits[0] = 1;
        }
        return digits;
    }
}

在这里插入图片描述

爬梯子

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数。

示例 1: 输入: 2,输出: 2;解释: 有两种方法可以爬到楼顶。
示例 2: 输入: 3,输出: 3;解释: 有三种方法可以爬到楼顶。

解答: 爬到第 xx 级台阶的方案数是爬到第 x - 1x−1 级台阶的方案数和爬到第 x - 2x−2 级台阶的方案数的和:f(x)=f(x−1)+f(x−2)

class Solution {
    public int climbStairs(int n) {
        int p = 0;
        int q = 1;
        int res = 0;
        for (int i = 0; i < n; i++) {
            res = p + q;
            p = q;
            q = res;
        }
        return res;
    }
}

在这里插入图片描述

合并两个有序数组

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

说明: 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

示例:
输入:nums1 = [1,2,3,0,0,0], m = 3;nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]

解答:三指针从后往前
注意:如[4,5,6,0,0,0]、[1,2,3]的情况
在这里插入图片描述

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = m - 1;
        int p2 = n - 1;
        int p = m + n - 1;
        while (p1>=0 && p2>=0){
            if (nums1[p1] >= nums2[p2]){
                nums1[p] = nums1[p1];
                p1 --;
            }else {
                nums1[p] = nums2[p2];
                p2 --;
            }
            p --;
        }
        System.arraycopy(nums2,0,nums1,0,p2+1);
    }
}

删除排序链表中的重复元素

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 : 输入: 1->1->2->3->3;输出: 1->2->3

解答:由于已排序,可以只考虑比较相邻节点的值

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode node = head;
        while (node != null && node.next != null){
            if (node.val == node.next.val){
                node.next = node.next.next;
            }else {
                node = node.next;
            }
        }
        return head;
    }
}

解答成功:执行耗时:1 ms,击败了73.94% 的Java用户;内存消耗:39.5 MB,击败了42.35% 的Java用户

相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 :
在这里插入图片描述
解答
递归地判断两个二叉树是否相同。
1、如果两个二叉树都为空,则两个二叉树相同。
2、如果两个二叉树中有且只有一个为空,则两个二叉树一定不相同。
3、如果两个二叉树都不为空,那么首先判断它们的根节点的值是否相同,若不相同则两个二叉树一定不同,若相同,再分别判断两个二叉树的左子树是否相同以及右子树是否相同。

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p==null && q==null){
            return true;
        }else if (p==null || q==null){
            return false;
        }else if (p.val != q.val){
            return false;
        }else {
            return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
        }
    }
}

解答成功: 执行耗时:0 ms,击败了100.00% 的Java用户;内存消耗:37.3 MB,击败了39.50% 的Java用户

对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

实例:二叉树 [1,2,2,3,4,4,3] 是对称的。
在这里插入图片描述
解答
通过「同步移动」两个指针的方法来遍历这棵树,pp 指针和 qq 指针一开始都指向这棵树的根,随后 pp 右移时,qq 左移,pp 左移时,qq 右移。每次检查当前 pp 和 qq 节点的值是否相等,如果相等再判断左右子树是否对称。

class Solution {
    public boolean check(TreeNode p, TreeNode q){
        if (p==null && q==null) return true;
        else if (p==null || q==null) return false;
        else return p.val==q.val && check(p.left, q.right) && check(p.right, q.left);
    }

    public boolean isSymmetric(TreeNode root) {
        return check(root, root);
    }
}

解答成功: 执行耗时:1 ms,击败了29.43% 的Java用户 内存消耗:38.2 MB,击败了26.57% 的Java用户

二叉树的层次遍历-正/反序

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其自底向上的层次遍历为: [[15, 7], [9, 20], [3]]

解答
广度优先遍历,核心是用一个queue队列存节点信息。

class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        // 用于存最终结果
        List<List<Integer>> ret = new ArrayList<>();
        // 队列,用于存储节点信息
        Queue<TreeNode> queue = new LinkedList<>();
        if (root == null){
            return ret;
        }
        // 先将head节点添加入队列,然后开始while循环。
        queue.offer(root);
        // 辅助作用的节点,用于指向出队的节点信息
        TreeNode node;
        while (!queue.isEmpty()){
            // 临时的列表,存某一层的所有节点。
            ArrayList<Integer> temp = new ArrayList<>();
            // 得到队列的长度,此时队列的长度=这一层的节点数(后面可以发现)
            int len = queue.size();
            // 由于输出结果是二维数组结构,第二维的数组内数量就是该层的全部节点。
            // 所以可以通过for循环填入某一层的所以节点。
            for (int i = 0; i < len; i++) {
                // 先出队第一个节点
                node = queue.poll();
                // 将该节点的值加入
                temp.add(node.val);
                // 如果左边子节点存在,则加入队列
                if (node.left != null){
                    queue.offer(node.left);
                }
                // 如果右边子节点存在,则加入队列
                if (node.right != null){
                    queue.offer(node.right);
                }
            }
            // for循环完成,表示这一层的节点都已获取到,将此接入到结果列表中
            ret.add(temp);
        }
        // 如果是倒序层次遍历,反转一下即可
        Collections.reverse(ret);
        return ret;
    }
}

有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
例如
给定有序数组: [-10,-3,0,5,9],一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

解答

class Solution {
    public TreeNode tempFunc(int[] nums, int left, int right){
        // left=right时,区间内只有一个元素
        // left>right时,说明已没有元素,返回null节点
        if (left > right) {
            return null;
        }
        // 选择中间位置左边的元素作为根节点(因为整除:5/2=2)
        int mid = (left + right)/2;
        // 将元素的值作为中间节点
        TreeNode root = new TreeNode(nums[mid]);
        // 左子节点通过递归调用获得
        // 返回的是中间节点左边区间里,中间元素的节点,让root.left指向它
        // 递归里相当于不断把大区间二分成小区间
        // 简单的情况如“1,2,3,4,5”,
        // 首次进来1:left=0,mid=2,right=4;左边“1,2”,根节点“3”,右边“4,5”。
        // left递归1:left=0,mid=0,right=1;左边“”,根节点“1”,右边“2”。
        // left递归2:left=0,right=-1;不符合,返回null。
        // right递归1:left=3,mid=3,right=4;左边“”,根节点“4”,右边“5”。
        // right递归2:left=4,mid=4,right=4;左边“”,根节点“5”,右边“”。
        // right递归3:left=5,right=4;不符合,返回null。
        // 最终:(二叉搜索树的中序遍历是升序序列)
        //     3
        //    / \
        //    1  4
        //    \   \
        //     2   5
        root.left = tempFunc(nums, left, mid-1);
        // 同上
        root.right = tempFunc(nums, mid+1, right);
        // 返回根节点(相对于当前节点而言是根节点)
        return root;
    }

    public TreeNode sortedArrayToBST(int[] nums) {
        TreeNode node = tempFunc(nums, 0, nums.length-1);
        return node;
    }
}

平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。 一棵高度平衡二叉树定义为: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例:
给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
  15   7 

返回 true

解答

class Solution {
    // 将大的树拆成若干个深度<=1的子树
    // 每次判断当前父节点的左右子树的高度差是否>1
    // 每次递归,如果不平衡,返回-1;如果平衡,返回子树的深度(1或0<空节点>)
    // 叠加递归的结果,就是该父节点的深度
    public int tempFunc(TreeNode root){
        if (root == null){
            return 0;
        }
        // 自底而上,先递归到最底层的左子树
        int leftHeight = tempFunc(root.left);
        // 左子树完,递归右子树
        int rightHeight = tempFunc(root.right);
        // 如果左右子树的高度差>1,则说明不平衡
        // 如果左或右子树的高度=-1,则说明已经有子树不平衡了
        if (leftHeight==-1 || rightHeight==-1 || Math.abs(leftHeight-rightHeight)>1){
            return -1;
        }
        // 否则,一切正常,返回左右子树的最大值+1
        else{
            return Math.max(leftHeight, rightHeight) + 1;
        }
    }

    // 返回结果如果=-1,则表示其中某一个节点的子树不平衡,则总的也不平衡
    // 返回结果如果>=0,则表示全部平衡,则总的也平衡
    public boolean isBalanced(TreeNode root) {
        return tempFunc(root)>=0;
    }
}

二叉树的最小深度

给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
示例: 给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7 

返回它的最小深度: 2.

解答



买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。 注意:你不能在买入股票前卖出股票。

示例 : 输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

解答:
在这里插入图片描述

一次遍历,寻找最小值,作为买入点。若不是最小值,则比较利润是否最大。

class Solution {
    public int maxProfit(int prices[]) {
        // 预先设置一个最低价格
        int minprice = Integer.MAX_VALUE;
        // 预先设置利润=0
        int maxprofit = 0;
        for (int i = 0; i < prices.length; i++) {
            // 判断当前价格是否小于最低价格
            if (prices[i] < minprice) {
                // 更新最低价格
                minprice = prices[i];
            // 否则判断是否利润更大
            } else if (prices[i] - minprice > maxprofit) {
                // 更新最大利润
                maxprofit = prices[i] - minprice;
            }
        }
        return maxprofit;
    }
}

本来以为这种解法在“最低价格在后面的情况下会出错,如[7,6,5,3,1,4]”,后来想了想发现不会。因为是一次遍历,当找到最低价格后,前面的已经抛弃了,从之后再开始比较if(prices[i] - minprice > maxprofit)。

两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
解答:

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    ListNode listNode = new ListNode(0);
    ListNode node = listNode;
    // 存放进位
    int valH = 0;
    // 只要有一边不为空,就循环相加
    while (l1!=null && l2!=null) {
        // 两数以及进位相加
        int val = l1.val + l2.val + valH;
        // 取当前和的进位,为下次用
        valH = val/10;
        // 取低位
        int valL = val%10;  // 15%10=5
        // 创建节点
        node.next = new ListNode(valL);
        node = node.next;
        l1 = l1.next;
        l2 = l2.next;
    }
    // 若l1剩余,则全部添加进来
    while (l1!=null) {
        int val = l1.val + valH;
        valH = val/10;
        int valL = val%10;  // 15%10=5
        node.next = new ListNode(valL);
        node = node.next;
        l1 = l1.next;
    }
    // 若l2剩余,则全部添加进来
    while (l2!=null) {
        int val = l2.val + valH;
        valH = val/10;
        int valL = val%10;  // 15%10=5
        node.next = new ListNode(valL);
        node = node.next;
        l2 = l2.next;
    }
    // 若全部添加完还有进位,则额外添加一个“1”
    if (valH==1) {
        node.next = new ListNode(valH);
    }
    // 返回创建的根节点的next节点,因为根节点的val是无效的空值
    return listNode.next;
}

无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解答:

public int lengthOfLongestSubstring(String s) {
    // 空字符串直接返回0
    if (s.length() == 0) {
        return 0;
    }
    // 存遍历过的字符,key=字符,value=该字符的下标+1(这样方便后面max()时候不用再判断)
    Map<Character, Integer> map = new HashMap<>();
    int maxLength = 0;
    int start = 0;
    int end = 0;
    // 遍历字符串
    for (end = 0; end < s.length(); end++) {
        // 取出一个字符
        char key = s.charAt(end);
        // 判断是否在map中存在
        // 1.若存在,且value>=start,说明当前序列中有重复了,start要移动到value
        // 2.若存在,但value<start,说明这个存在的key不在当前序列中,start不用移动
        // =>合并以上两种情况,不管>还是<,start都取他跟value的最大值即可
        if (map.containsKey(key)) {
            start = Math.max(start, map.get(key));
        }
        // 计算当前序列的长度,取最大值
        maxLength = Math.max(maxLength, end-start+1);
        // 将当前字符的下标放入map
        // 注意1,下标+1:end+1
        // 注意2,多次put相同的key时,value会被覆盖
        map.put(key, end+1);
    }
    return maxLength;
}

盛最多水的容器

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
**说明:**你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
在这里插入图片描述

// 在初始时,左右指针分别指向数组的左右两端,它们可以容纳的水量为 min(1, 7) * 8 = 8
// 此时我们需要移动一个指针。直觉告诉我们,应该移动对应数字较小的那个指针(即此时的左指针)。
// 如果我们移动数字较大的那个指针,那么前者「两个指针指向的数字中较小值」不会增加,
// 后者「指针之间的距离」会减小,那么这个乘积会减小。因此,我们移动数字较大的那个指针是不合理的。
// 因此,我们移动 数字较小的那个指针。
// 所以,我们将左指针向右移动:
// 此时可以容纳的水量为 min(8,7)∗7=49。由于右指针对应的数字较小,我们移动右指针
// 此时可以容纳的水量为 min(8,3)∗6=18。
// 重复,直到两个指针重合。
public int maxArea(int[] height) {
    // 左指针
    int l = 0;
    // 右指针
    int r = height.length-1;
    // 存最大值
    int max = 0;
    // 循环执行,直至左右指针重合
    while (l<r) {
        // 以短板为准,计算一下容量
        int temp = Math.min(height[l], height[r]) * (r-l);
        // 比较最大值
        max = Math.max(temp, max);
        // 移动更小的一边
        if (height[l]<height[r]) {
            l++;
        }else {
            r--;
        }
    }
    System.out.println(max);
    return max;
}
  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小锋学长生活大爆炸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值