java刷题笔记

Java刷题笔记

day1-二分查找

2021/11/30

34.在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

进阶:

你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109

解答:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        if(nums.length==0){
            return new int[]{-1,-1};
        }
        int[] ans = new int[2];
        ans[0] = findLeft(nums, target);
        if(ans[0]==-1){
            return new int[]{-1,-1};
        }
        ans[1] = findRight(nums, target);
        return ans;
    }

    public int findLeft(int[] nums, int target){
        int L = 0, R = nums.length-1;
        while(L<R){
            int mid = (L+R)>>>1;
            if(nums[mid]>target){
                R = mid-1;
            }else if(nums[mid]<target){
                L = mid+1;
            }else{
                R = mid;
            }
        }
        if(nums[L]==target){
            return L;
        }
        return -1;
    }
    public int findRight(int[] nums, int target){
        int L = 0, R = nums.length-1;
        while(L<R){
            int mid = (L+R+1)>>>1;
            if(nums[mid]>target){
                R = mid-1;
            }else if(nums[mid]<target){
                L = mid+1;
            }else{
                L = mid;
            }
        }
        return L;
    }
}

总结:

使用传统的二分法进行两次查找,需要注意的是,在findRight时,中点要向上取整。样例[5,7,7,8,8,10]中,当L=3,R=4时,如果使用向下取整,mid=L,就会出现L=L的死循环。

33.搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:

输入:nums = [1], target = 0
输出:-1

提示:

1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-10^4 <= target <= 10^4

进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?

解答:

class Solution {
    public int search(int[] nums, int target) {
        int L=0, R=nums.length-1;
        while(L<=R){
            int mid = (L+R)>>>1;
            if(nums[mid]==target){
                return mid;
            }
            if(nums[mid]>=nums[0]){
                if(nums[mid]>target&&target>=nums[0]){
                    R = mid-1;
                }else{
                    L = mid+1;
                }
            }else{
                if(nums[mid]<target&&nums[0]>target){
                    L = mid+1;
                }else{
                    R = mid-1;
                }
            }
        }
        return -1;
    }
}

总结:

可以把数组分成左右两段,两段数组都是递增的,且右段一定小于左段。

当nums[mid]>=nums[0]时,说明中值在左段。当target=nums[0],因为右段一定小于左段,所以为true时target在mid之前,false时target在右段。

当nums[mid]

74.搜索二维矩阵

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。

示例 1:

img

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true

示例 2:

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false

提示:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104

解答:

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        if(m==0) return false;
        int n = matrix[0].length;
        int L=0, R=m*n-1;
        while(L<=R){
            int mid = (L+R)>>>1;
            int midV = matrix[mid/n][mid%n];
            if(midV==target) return true;
            else{
                if(midV<target){
                    L = mid+1;
                }else{
                    R = mid-1;
                }
            }
        }
        return false;
    }
}

总结:

同一般二分法,将矩阵转化为数组即可。

day2-二分查找

2021/12/01

153.寻找旋转排序数组中的最小值

已知一个长度为

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

示例 3:

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。

提示:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1n 次旋转

解答:

class Solution {
    public int findMin(int[] nums) {
        int L=0, R=nums.length-1;
        while(L<R){
            int mid = L+(R-L)/2;
            if(nums[mid]<nums[R]){
                R=mid;
            }else{
                L=mid+1;
            }
        }
        return nums[L];
    }
}

总结:

与之前的旋转矩阵类似,只要理解右半段一定小于左半段即可。

162.寻找峰值

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞

你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

示例 1:

输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。

示例 2:

输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5 
解释:你的函数可以返回索引 1,其峰值元素为 2;
     或者返回索引 5, 其峰值元素为 6。

提示:

  • 1 <= nums.length <= 1000
  • -231 <= nums[i] <= 231 - 1
  • 对于所有有效的 i 都有 nums[i] != nums[i + 1]

解答:

class Solution {
    public int findPeakElement(int[] nums) {
        int L=0, R=nums.length-1;
        while(L<R){
            int mid = (L+R)>>>1;
            if(nums[mid]>nums[mid+1]){
                R = mid;
            }else{
                L = mid+1;
            }
        }
        return L;
    }
}

总结:

题中给出条件nums[-1] = nums[n] = -∞,说明数组中一定存在峰,且每个坡一定会有它的峰。所以直接用nums[mid]>nums[mid+1]判断它是上下坡即可。

day3-双指针

2021/12/02

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

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。

返回同样按升序排列的结果链表。

示例 1:

img

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]

示例 2:

img

输入:head = [1,1,1,2,3]
输出:[2,3]

提示:

  • 链表中节点数目在范围 [0, 300]
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序排列

解答:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode node = new ListNode(0,head);
        ListNode t = node;
        while(t.next!=null&&t.next.next!=null){
            if(t.next.val==t.next.next.val){
                int num = t.next.val;
                t.next = t.next.next;
                while(t.next!=null&&t.next.val==num){
                    t.next = t.next.next;
                }
            }else{
                t = t.next;
            }
        }
        return node.next;
    }
}

总结:

读懂题意,是要删除所有重复元素,而不是去重。

15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

提示:

  • 0 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

解答:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        int n = nums.length;
        if(n<=2){
            return ans;
        }
        Arrays.sort(nums);
        for(int i = 0; i < n-2; i++) {
            if(nums[i]>0) break;
            if(i>0&&nums[i]==nums[i-1]) continue;
            int L = i+1, R = n-1;
            while (L<R){
                int sum = nums[i]+nums[L]+nums[R];
                if(sum==0){
                    ans.add(new ArrayList<>(Arrays.asList(nums[i],nums[L],nums[R])));
                    while(L<R&&nums[L]==nums[++L]);
                    while(L<R&&nums[R]==nums[--R]);
                }else if(sum<0){
                    while(L<R&&nums[L]==nums[++L]);
                }else{
                    while(L<R&&nums[R]==nums[--R]);
                }
            }
        }
        return ans;
    }
}

day4-双指针

2021/12/03

844. 比较含退格的字符串

给定 st 两个字符串,当它们分别被输入到空白的文本编辑器后,请你判断二者是否相等。# 代表退格字符。

如果相等,返回 true ;否则,返回 false

**注意:**如果对空文本输入退格字符,文本继续为空。

示例 1:

输入:s = "ab#c", t = "ad#c"
输出:true
解释:S 和 T 都会变成 “ac”。

示例 2:

输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 “”。

示例 3:

输入:s = "a##c", t = "#a#c"
输出:true
解释:s 和 t 都会变成 “c”。

示例 4:

输入:s = "a#c", t = "b"
输出:false
解释:s 会变成 “c”,但 t 仍然是 “b”。

解答1:

//直接使用StringBuilder修改字符串
class Solution {
     public boolean backspaceCompare(String s, String t) {
        return fun(s).equals(fun(t));
    }
    public String fun(String s){
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(c=='#'){
                if(stringBuilder.length()>0){
                    stringBuilder.deleteCharAt(stringBuilder.length()-1);
                }
            }else{
                stringBuilder.append(c);
            }
        }
        return stringBuilder.toString();
    }
}

解答2:

//双指针逆序遍历
class Solution {
     public boolean backspaceCompare(String s, String t) {
        int t1 = s.length()-1, t2 = t.length()-1;
        int count1 = 0 , count2 = 0;
        while (t1>=0||t2>=0){
            while (t1>=0){
                if (s.charAt(t1)=='#'){
                    count1++;
                    t1--;
                }else if(count1>0){
                    count1--;
                    t1--;
                }else {
                    break;
                }
            }
            while (t2>=0){
                if (t.charAt(t2)=='#'){
                    count2++;
                    t2--;
                }else if(count2>0){
                    count2--;
                    t2--;
                }else {
                    break;
                }
            }
            if(t1>=0&&t2>=0){
                if(s.charAt(t1)!=t.charAt(t2)){
                    return false;
                }
            }else{
                if(t1>=0||t2>=0){
                    return false;
                }
            }
            t1--;
            t2--;
        }
        return true;
    }
}

986. 区间列表的交集

给定两个由一些 闭区间 组成的列表,firstListsecondList ,其中 firstList[i] = [starti, endi]secondList[j] = [startj, endj] 。每个区间列表都是成对 不相交 的,并且 已经排序

返回这 两个区间列表的交集

形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b

两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如,[1, 3][2, 4] 的交集为 [2, 3]

示例 1:

java刷题笔记_第1张图片

输入:firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
输出:[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]

示例 2:

输入:firstList = [[1,3],[5,9]], secondList = []
输出:[]

示例 3:

输入:firstList = [], secondList = [[4,8],[10,12]]
输出:[]

示例 4:

输入:firstList = [[1,7]], secondList = [[3,10]]
输出:[[3,7]]

提示:

  • 0 <= firstList.length, secondList.length <= 1000
  • firstList.length + secondList.length >= 1
  • 0 <= starti < endi <= 109
  • endi < starti+1
  • 0 <= startj < endj <= 109
  • endj < startj+1

解答:

class Solution {
    public int[][] intervalIntersection(int[][] firstList, int[][] secondList) {
        List<int[]> ans = new ArrayList<>();
        int i=0, j=0;
        while (i<firstList.length&&j<secondList.length){
            //取大的左边界,小的右边界,即为交集
            int L = Math.max(firstList[i][0], secondList[j][0]);
            int R = Math.min(firstList[i][1], secondList[j][1]);
            if(L<=R){
                ans.add(new int[]{L,R});
            }
            //右边界小的,前往下一区间
            if(firstList[i][1]<secondList[j][1]){
                i++;
            }else {
                j++;
            }
        }
        return ans.toArray(new int[ans.size()][]);
    }
}

11. 盛最多水的容器

给你 n 个非负整数 a1,a2,...,a``n,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai)(i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

**说明:**你不能倾斜容器。

示例 1:

java刷题笔记_第2张图片

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例 2:

输入:height = [1,1]
输出:1

示例 3:

输入:height = [4,3,2,1,4]
输出:16

示例 4:

输入:height = [1,2,1]
输出:2

提示:

  • n == height.length
  • 2 <= n <= 105
  • 0 <= height[i] <= 104

解答1:

class Solution {
    public int maxArea(int[] height) {
        int sum = 0,temp = 0;
        for(int i=0;i<height.length-1;i++){
            for(int j=i+1;j<height.length;j++){
                temp = Math.min(height[i],height[j]);
                temp *= j-i;
                if(temp>sum) sum = temp;
            }
        }
        return sum;
    }
}

解答2:

class Solution {
    public int maxArea(int[] height) {
        int L = 0, R = height.length-1;
        int ans = 0;
        while (L<R){
            int temp = Math.min(height[L],height[R])*(R-L);
            ans = Math.max(ans, temp);
            if(height[L]<height[R]){
                L++;
            }else{
                R--;
            }
        }
        return ans;
    }
}

总结:

​ 解答1中,遍历了有两两组合的情况,找到最大容器。而实际上,每次只需要让数字小的指针移动即可。因为数字大的指针如果进行移动,之后的容量一定会越来越小(高被另一边限制,宽逐渐缩短)。让数字小的指针移动,才可能找到更大的容量。这样无需遍历所有组合就能找到最大容量。

day5-滑动窗口

2021/12/04

438. 找到字符串中所有字母异位词

给定两个字符串 sp,找到 s 中所有 p异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

示例 2:

输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

提示:

  • 1 <= s.length, p.length <= 3 * 104
  • sp 仅包含小写字母

解答:

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> ans = new ArrayList<>();
        int n = s.length(), m = p.length();
        int[] a = new int[26];
        for (int i = 0; i < p.length(); i++) {
            a[p.charAt(i)-'a']++;
        }
        int t=0;
        for(int L=0, R=0; R<n; R++){
            if(--a[s.charAt(R)-'a']>=0){
                t++;
            }
            if(t==m){
                ans.add(L);
            }
            if(R-L+1==m){
                if(++a[s.charAt(L)-'a']>0){
                    t--;
                }
                L++;
            }
        }
        return ans;
    }
}

总结:

​ 思路:

  • 考虑用两个26数组存储字符出现频率,但每次都要进行26的循环,效率不高。
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> ans = new ArrayList<>();
        int n = s.length(), m = p.length();
        int[] a = new int[26];
        int[] b = new int[26];
        for (int i = 0; i < p.length(); i++) {
            a[p.charAt(i)-'a']++;
        }
        
        return ans;
    }
}
  • 只用一个数组,用一个计数器表示该段字符串中有几个合法字符。如下,但发现无法数组还原。
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> ans = new ArrayList<>();
        int n = s.length(), m = p.length();
        int[] a = new int[26];
        for (int i = 0; i < p.length(); i++) {
            a[p.charAt(i)-'a']++;
        }
        int t=0;
        for(int L=0, R=0; R<n; R++){
            if(a[s.charAt(R)-'a']>0){
                a[s.charAt(R)-'a']--;
                t++;
            }
            if(t==m){
                ans.add(L);
            }
            //无法还原数组
        }
        return ans;
    }
}
  • 每次查询数组时,不管他是不是目标字符,直接让其减一,还原时,直接让其加一。这样就可以维护数组不变。
if(--a[s.charAt(R)-'a']>=0){
	t++;
}
if(t==m){
	ans.add(L);
}
if(R-L+1==m){
    //还原后大于零,说明该字符是目标字符,移除滑动窗口,计数器减一。
	if(++a[s.charAt(L)-'a']>0){
		t--;
	}
	L++;
}

713. 乘积小于K的子数组

给定一个正整数数组 nums和整数 k

请找出该数组内乘积小于 k 的连续的子数组的个数。

示例 1:

输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。

示例 2:

输入: nums = [1,2,3], k = 0
输出: 0 

提示:

  • 1 <= nums.length <= 3 * 104
  • 1 <= nums[i] <= 1000
  • 0 <= k <= 106

解答1:

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        if(k<=1) return 0;
        int sum = 1, ans = 0;
        int n = nums.length;
        for(int L=0, R=0;;){
            sum*=nums[R];
            if(sum<k){
                ans++;
                R++;
            }else{
                L++;
                R=L;
                sum=1;
            }
            if(R==n){
                L++;
                R=L;
                sum=1;
            }
            if(L>=n){
                break;
            }
        }
        return ans;
    }
}

解答2:

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        if (k <= 1) return 0;
        int l = 0, r = 0, base = 1, ans = 0;
        int length = nums.length;
        while (r < length) {
            base *= nums[r++];
            while (base >= k) base /= nums[l++];
            ans += r - l;
        }
        return ans;
    }
}

总结:

解答1需要右指针左移,而实际上,如果开头到结尾的乘积可以满足条件,那么里面的每一个都可以满足条件。

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

进阶:

  • 如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

解答:

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int ans = Integer.MAX_VALUE;
        int L = 0, R = 0;
        int sum = 0;
        while (R < n) {
            sum += nums[R];
            while (sum >= target) {
                ans = Math.min(ans, R - L + 1);
                sum -= nums[L];
                L++;
            }
            R++;
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}

day6-广度优先搜索 / 深度优先搜索

2021/12/05

200. 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1

示例 2:

输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] 的值为 '0''1'

解答:

class Solution {
    int[] xx = {0,0,1,-1}, yy = {1,-1,0,0};
    public void dfs(char[][] grid, int i, int j){
        grid[i][j]='0';
        for (int t = 0; t < 4; t++) {
            int x = i+xx[t], y = j+yy[t];
            if(x>=0&&y>=0&&x<grid.length&&y<grid[0].length&&grid[x][y]=='1'){
                dfs(grid, x, y);
            }
        }
    }
    public int numIslands(char[][] grid) {
        int ans = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++){
                if(grid[i][j]=='1'){
                    ans++;
                    dfs(grid,i,j);
                }
            }
        }
        return ans;
    }
}

547. 省份数量

n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

示例 1:

java刷题笔记_第3张图片

输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2

示例 2:

java刷题笔记_第4张图片

输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3

提示:

  • 1 <= n <= 200
  • n == isConnected.length
  • n == isConnected[i].length
  • isConnected[i][j]10
  • isConnected[i][i] == 1
  • isConnected[i][j] == isConnected[j][i]

解答:

class Solution {
	public int findCircleNum(int[][] isConnected) {
        int n = isConnected.length;
        boolean[] v = new boolean[n];
        int ans = 0;
        for (int i=0;i<n;i++){
            if(!v[i]){
                v[i] = true;
                dfs(isConnected, v, n, i);
                ans++;
            }
        }
        return ans;
    }

    private void dfs(int[][] isConnected, boolean[] v, int n, int i) {
        for (int j=0;j<n;j++){
            if(isConnected[i][j]==1&&!v[j]&&i!=j){
                v[j] = true;
                dfs(isConnected, v, n, j);
            }
        }
    }
}

day7-广度优先搜索 / 深度优先搜索

2021/12/06

117. 填充每个节点的下一个右侧节点指针 II

给定一个二叉树

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

初始状态下,所有 next 指针都被设置为 NULL

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

示例:

java刷题笔记_第5张图片

输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。

提示:

  • 树中的节点数小于 6000
  • -100 <= node.val <= 100

解答:

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}
    
    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/

class Solution {
     public Node connect(Node root) {
         if(root==null) return null;
        Queue<Node> q = new LinkedList<>();
        q.add(root);
        while (!q.isEmpty()){
            int n = q.size();
            Node last = null;
            for (int i = 0; i < n; i++) {
                Node t = q.poll();
                if(t.left!=null){
                    q.add(t.left);
                }
                if(t.right!=null){
                    q.add(t.right);
                }
                if(i!=0){
                    last.next = t;
                }
                last = t;
            }
        }
        return root;
    }
}

572. 另一棵树的子树

给你两棵二叉树 rootsubRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

示例 1:

java刷题笔记_第6张图片

输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true

示例 2:

java刷题笔记_第7张图片

输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
输出:false

提示:

  • root 树上的节点数量范围是 [1, 2000]
  • subRoot 树上的节点数量范围是 [1, 1000]
  • -104 <= root.val <= 104
  • -104 <= subRoot.val <= 104

解答:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        return dfs(root, subRoot);
    }

    public boolean dfs(TreeNode root, TreeNode subRoot){
        if(root==null){
            return false;
        }
        return check(root,subRoot)||dfs(root.left,subRoot)||dfs(root.right,subRoot);
    }

    public boolean check(TreeNode root, TreeNode subRoot) {
        if(root==null&&subRoot==null){
            return true;
        }
        if(root==null||subRoot==null|| root.val!=subRoot.val){
            return false;
        }
        return check(root.left,subRoot.left)&&check(root.right,subRoot.right);
    }
}

day8-广度优先搜索 / 深度优先搜索

2021/12/07

1091. 二进制矩阵中的最短路径

难度中等143

给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1

二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:

  • 路径途经的所有单元格都的值都是 0
  • 路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。

畅通路径的长度 是该路径途经的单元格总数。

示例 1:

java刷题笔记_第8张图片

输入:grid = [[0,1],[1,0]]
输出:2

示例 2:

java刷题笔记_第9张图片

输入:grid = [[0,0,0],[1,1,0],[1,1,0]]
输出:4

示例 3:

输入:grid = [[1,0,0],[1,1,0],[1,1,0]]
输出:-1

提示:

  • n == grid.length
  • n == grid[i].length
  • 1 <= n <= 100
  • grid[i][j]01

解答:

class Solution {
    public int shortestPathBinaryMatrix(int[][] grid) {
        int n = grid.length;
        if(grid.length==0||grid[0][0]==1||grid[n-1][n-1]==1){
            return -1;
        }
        int xx[] = {0,0,1,1,1,-1,-1,-1}, yy[] = {1,-1,0,1,-1,0,1,-1};
        Queue<int[]> q = new LinkedList<>();
        q.add(new int[]{0,0});
        int ans = 1;
        while (!q.isEmpty()){
            int m = q.size();
            for (int i = 0; i < m; i++) {
                int t[] = q.poll();
                int a = t[0], b = t[1];
                if(a==n-1&&b==n-1){
                    return ans;
                }
                for(int j=0;j<8;j++){
                    int x = a+xx[j], y = b+yy[j];
                    if(x>=0&&y>=0&&x<n&&y<n&&grid[x][y]==0){
                        grid[x][y]=1;
                        q.add(new int[]{x,y});
                    }
                }
            }
            ans++;
        }
        return -1;
    }
}

总结:

找某一点是否存在,选择dfs;找最短,选择bfs。

130. 被围绕的区域

给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ ,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

示例 1:

java刷题笔记_第10张图片

输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

示例 2:

输入:board = [["X"]]
输出:[["X"]]

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 200
  • board[i][j] 为 'X' 或 'O'

解答:

class Solution {
    int xx[] = {0,0,1,-1}, yy[] = {1,-1,0,0};
    public void solve(char[][] board) {
        int n = board.length, m = board[0].length;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if((i==0||i==n-1||j==0||j==m-1)&&board[i][j]=='O'){
                    dfs(board,i,j);
                }
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if(board[i][j]=='O') {
                    board[i][j] = 'X';
                }
                if(board[i][j]=='*') {
                    board[i][j] = 'O';
                }
            }
        }
    }
    public void dfs(char[][] board, int i, int j){
        board[i][j] = '*';
        for (int k=0;k<4;k++){
            int x = i+xx[k], y = j+yy[k];
            if(x>=0&&x<board.length&&y>=0&&y<board[0].length&&board[x][y]=='O'){
                dfs(board,x,y);
            }
        }
    }
}

797. 所有可能的路径

给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序

二维数组的第 i 个数组中的单元都表示有向图中 i 号节点所能到达的下一些节点,空就是没有下一个结点了。

译者注:有向图是有方向的,即规定了 a→b 你就不能从 b→a 。

示例 1:

java刷题笔记_第11张图片

输入:graph = [[1,2],[3],[3],[]]
输出:[[0,1,3],[0,2,3]]
解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3

示例 2:

java刷题笔记_第12张图片

输入:graph = [[4,3,1],[3,2,4],[3],[4],[]]
输出:[[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]

示例 3:

输入:graph = [[1],[]]
输出:[[0,1]]

示例 4:

输入:graph = [[1,2,3],[2],[3],[]]
输出:[[0,1,2,3],[0,2,3],[0,3]]

示例 5:

输入:graph = [[1,3],[2],[3],[]]
输出:[[0,1,2,3],[0,3]]

提示:

  • n == graph.length
  • 2 <= n <= 15
  • 0 <= graph[i][j] < n
  • graph[i][j] != i(即,不存在自环)
  • graph[i] 中的所有元素 互不相同
  • 保证输入为 有向无环图(DAG)

解答:

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> temp = new ArrayList<>();
    public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
        temp.add(0);
        dfs(graph, 0);
        return ans;
    }
    public void dfs(int[][] graph, int t){
        if(t == graph.length-1){
            ans.add(new ArrayList<>(temp));
            return;
        }
        for(int next : graph[t]){
            temp.add(next);
            dfs(graph, next);
            temp.remove(temp.size()-1);
        }
    }
}

day9-递归 / 回溯

2021/12/08

78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

解答:

class Solution {
    List<Integer> temp = new ArrayList<Integer>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    public List<List<Integer>> subsets(int[] nums) {
        dfs(0,nums);
        return ans;
    }
    public void dfs(int n, int[] nums){
        if(n==nums.length){
            ans.add(new ArrayList<Integer>(temp));
            return;
        }

        temp.add(nums[n]);
        dfs(n+1,nums);
        temp.remove(temp.size()-1);
        dfs(n+1,nums);
    }
}

90. 子集 II

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10

解答1:

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        ans.add(new ArrayList<>());
        Arrays.sort(nums);
        int start = 1; 
        for (int i = 0; i < nums.length; i++) {
            List<List<Integer>> ans_tmp = new ArrayList<>();
            for (int j = 0; j < ans.size(); j++) {
                List<Integer> list = new ArrayList<>(ans.get(j));
                if (i > 0 && nums[i] == nums[i - 1] && j < start) {
                    continue;
                }
                list.add(nums[i]);
                ans_tmp.add(list);
            }

            start = ans.size();
            ans.addAll(ans_tmp);
        }
        return ans;
    }
}

解答2:

class Solution {
    List<Integer> t = new ArrayList<Integer>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        dfs(false, 0, nums);
        return ans;
    }

    public void dfs(boolean choosePre, int cur, int[] nums) {
        if (cur == nums.length) {
            ans.add(new ArrayList<Integer>(t));
            return;
        }
        dfs(false, cur + 1, nums);
        if (!choosePre && cur > 0 && nums[cur - 1] == nums[cur]) {
            return;
        }
        t.add(nums[cur]);
        dfs(true, cur + 1, nums);
        t.remove(t.size() - 1);
    }
}

day10-递归 / 回溯

2021/12/09

47. 全排列 II

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

提示:

  • 1 <= nums.length <= 8
  • -10 <= nums[i] <= 10

解答:

class Solution {
    boolean[] vis;
    public void fun(int[] nums, List<List<Integer>> ans, List<Integer> temp, int index){
        if(index==nums.length){
            ans.add(new ArrayList<Integer>(temp));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(vis[i]||(i>0&&nums[i]==nums[i-1]&&!vis[i-1])){
                continue;
            }
            temp.add(nums[i]);
            vis[i]=true;
            fun(nums,ans,temp,index+1);
            vis[i]=false;
            temp.remove(index);
        }
    }
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> temp = new ArrayList<>();
        vis = new boolean[nums.length];
        Arrays.sort(nums);
        fun(nums, ans, temp, 0);
        return ans;
    }
}

39. 组合总和

给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。

示例 1:

输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

示例 4:

输入: candidates = [1], target = 1
输出: [[1]]

示例 5:

输入: candidates = [1], target = 2
输出: [[1,1]]

提示:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每个元素都是独一无二的。
  • 1 <= target <= 500

解答:

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> temp = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        fun(candidates,0, target);
        return ans;
    }

    public void fun(int[] candidates, int index, int target){
        if(target==0){
            ans.add(new ArrayList<Integer>(temp));
            return;
        }
        if(target<0||index>=candidates.length){
            return;
        }
        temp.add(candidates[index]);
        fun(candidates,index, target-candidates[index]);
        temp.remove(temp.size()-1);
        fun(candidates,index+1, target);
    }
}

解答2:

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> temp = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        fun(candidates,0, target);
        return ans;
    }

    public void fun(int[] candidates, int index, int target){
        if(index>=candidates.length){
            return;
        }
        if(target==0){
            ans.add(new ArrayList<Integer>(temp));
            return;
        }
        fun(candidates,index+1, target);
        if(target-candidates[index]>=0){
            temp.add(candidates[index]);
            fun(candidates,index, target-candidates[index]);
            temp.remove(temp.size()-1);
        }
    }
}

总结:

简单的回溯,不做解释。解答2中,提前判断target-candidates[index]>=0,速度大大提升。

40. 组合总和 II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

**注意:**解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

解答:

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> temp = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        fun(candidates,0, target);
        return ans;
    }

    public void fun(int[] candidates, int index, int target){
        if(target==0){
            ans.add(new ArrayList<Integer>(temp));
            return;
        }
        for (int i = index; i < candidates.length; i++) {
            if(target-candidates[i]<0){
                break;
            }
            if(i>index&&candidates[i]==candidates[i-1]){
                continue;
            }
            temp.add(candidates[i]);
            fun(candidates, i+1, target-candidates[i]);
            temp.remove(temp.size()-1);
        }
    }
}

day12-动态规划

2021/12/11

213. 打家劫舍 II

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:

输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 3:

输入:nums = [0]
输出:0

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

解答:

class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if(n==1) return nums[0];
        if(n==2) return Math.max(nums[0], nums[1]);
        return Math.max(fun(nums, 0, n-1), fun(nums, 1, n));
    }
    public int fun(int[] nums, int idx, int n){
        int a = nums[idx], b = Math.max(nums[idx+1], nums[idx]);
        for(int i=idx+2;i<n;i++){
            int temp = b;
            b = Math.max(nums[i]+a, b);
            a = temp;
        }
        return b;
    }
}

55. 跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

提示:

  • 1 <= nums.length <= 3 * 104
  • 0 <= nums[i] <= 105

解答:

class Solution {
    public boolean canJump(int[] nums) {
        int n = nums.length, index = n-1;
        for(int i=n-2;i>=0;i--){
            if(nums[i]>=index-i){
                index = i;
            }
        }
        if(index==0) return true;
        return false;
    }
}

day13-动态规划

2021/12/12

45. 跳跃游戏 II

给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置。

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

示例 2:

输入: nums = [2,3,0,1,4]
输出: 2

提示:

  • 1 <= nums.length <= 104
  • 0 <= nums[i] <= 1000

解答:

class Solution {
    public int jump(int[] nums) {
        int ans=0, max=0, end=0;
        for(int i=0;i<nums.length-1;i++){
            max = Math.max(max, i+nums[i]);
            if(i == end){
                end = max;
                ans++;
            }
        }
        return ans;
    }
}

62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

示例 1:

java刷题笔记_第13张图片

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 109

解答:

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] num = new int[m][n];
        for(int i=0;i<m;i++){
            num[i][0] = 1;
        }
        for(int i=0;i<n;i++){
            num[0][i] = 1;
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                num[i][j] = num[i-1][j]+num[i][j-1];
            }
        }
        return num[m-1][n-1];
    }
}

day14-动态规划

2021/12/13

5. 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

示例 3:

输入:s = "a"
输出:"a"

示例 4:

输入:s = "ac"
输出:"a"

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母(大写和/或小写)组成

解答:

class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];
        String ans = "";
        for(int l=0;l<n;l++){
            for(int i=0;i+l<n;i++){
                int j=i+l;
                if(l==0) dp[i][j]=true;
                else if(l==1) dp[i][j]=(s.charAt(i)==s.charAt(j));
                else{
                    dp[i][j]=(s.charAt(i)==s.charAt(j)&&dp[i+1][j-1]);
                }
                if(dp[i][j]&&l+1>ans.length()){
                    ans = s.substring(i,i+l+1);
                }
            }
        }
        return ans;
    }
}

413. 等差数列划分

如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。

  • 例如,[1,3,5,7,9][7,7,7,7][3,-1,-5,-9] 都是等差数列。

给你一个整数数组 nums ,返回数组 nums 中所有为等差数组的 子数组 个数。

子数组 是数组中的一个连续序列。

示例 1:

输入:nums = [1,2,3,4]
输出:3
解释:nums 中有三个子等差数组:[1, 2, 3]、[2, 3, 4] 和 [1,2,3,4] 自身。

示例 2:

输入:nums = [1]
输出:0

提示:

  • 1 <= nums.length <= 5000
  • -1000 <= nums[i] <= 1000

解答:

class Solution {
    public int numberOfArithmeticSlices(int[] nums) {
        int n = nums.length;
        if (n == 1) {
            return 0;
        }
        int t = nums[0] - nums[1], count = 0;
        int ans = 0;
        for (int i = 2; i < n; ++i) {
            if (nums[i - 1] - nums[i] == t) {
                count++;
            } else {
                t = nums[i - 1] - nums[i];
                count = 0;
            }
            ans += count;
        }
        return ans;
    }
}

day15-动态规划

2021/12/14

91. 解码方法

一条包含字母 A-Z 的消息通过以下映射进行了 编码

'A' -> 1
'B' -> 2
...
'Z' -> 26

解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,"11106" 可以映射为:

  • "AAJF" ,将消息分组为 (1 1 10 6)
  • "KJF" ,将消息分组为 (11 10 6)

注意,消息不能分组为 (1 11 06) ,因为 "06" 不能映射为 "F" ,这是由于 "6""06" 在映射中并不等价。

给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数

题目数据保证答案肯定是一个 32 位 的整数。

示例 1:

输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。

示例 2:

输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。

示例 3:

输入:s = "0"
输出:0
解释:没有字符映射到以 0 开头的数字。
含有 0 的有效映射是 'J' -> "10" 和 'T'-> "20" 。
由于没有字符,因此没有有效的方法对此进行解码,因为所有数字都需要映射。

示例 4:

输入:s = "06"
输出:0
解释:"06" 不能映射到 "F" ,因为字符串含有前导 0("6" 和 "06" 在映射中并不等价)。

提示:

  • 1 <= s.length <= 100
  • s 只包含数字,并且可能包含前导零。

解答:

class Solution {
    public int numDecodings(String s) {
        int n = s.length();
        if(n==0) return 0;
        int[] dp = new int[n];
        char[] c = s.toCharArray();
        if(c[0]=='0') return 0;
        dp[0]=1;
        for(int i=1;i<n;i++){
            if(c[i]!='0'){
                dp[i]=dp[i-1];
            }
            int temp = (c[i]-'0')+(c[i-1]-'0')*10;
            if(temp>=10&&temp<=26){
                if(i==1){
                    dp[i]++;
                }else{
                    dp[i]+=dp[i-2];
                }
            }
        }
        return dp[n-1];
    }
}

139. 单词拆分

给你一个字符串 s 和一个字符串列表 wordDict 作为字典,判定 s 是否可以由空格拆分为一个或多个在字典中出现的单词。

**说明:**拆分时可以重复使用字典中的单词。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
     注意你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • swordDict[i] 仅有小写英文字母组成
  • wordDict 中的所有字符串 互不相同

解答:

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
		Set<String> set = new HashSet<>(wordDict);
        int n = s.length();
        boolean[] dp = new boolean[n+1];
        dp[0] = true;
        for(int i=1;i<=n;i++){
            for(int j=0;j<i;j++){
                if(dp[j]&&set.contains(s.substring(j,i))){
                    dp[i]=true;
                    break;
                }
            }
        }
        return dp[n];
    }
}

day16-动态规划

2021/12/15

300. 最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

提示:

  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104

进阶:

  • 你可以设计时间复杂度为 O(n2) 的解决方案吗?
  • 你能将算法的时间复杂度降低到 O(n log(n)) 吗?

解答:

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        if(n==0) return 0;
        int[] dp = new int[n];
        dp[0] = 1;
        int ans = 1;
        for(int i=1;i<n;i++){
            dp[i] = 1;
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    dp[i] = Math.max(dp[i], dp[j]+1);
                }
            }
            ans = Math.max(ans, dp[i]);
        }
        return ans;
    }
}

673. 最长递增子序列的个数

给定一个未排序的整数数组,找到最长递增子序列的个数。

示例 1:

输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。

示例 2:

输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。

注意: 给定的数组长度不超过 2000 并且结果一定是32位有符号整数。

解答:

class Solution {
    public int findNumberOfLIS(int[] nums) {
		int n = nums.length;
        if(n==0) return 0;
        int[] dp = new int[n];
        int[] count = new int[n];
        int max = 1;
        for(int i=0;i<n;i++){
            dp[i] = 1;
            count[i] = 1;
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    if(dp[i]<dp[j]+1){
                        dp[i] = dp[j]+1;
                        count[i] = count[j];
                    }else if(dp[i]==dp[j]+1){
                        count[i] += count[j];
                    }
                }
            }
            max = Math.max(max, dp[i]);
        }
        int ans = 0;
        for(int i=0;i<n;i++){
            if(dp[i]==max){
                ans += count[i];
            }
        }
        return ans;
    }
}

day17-动态规划

2021/12/16

1143. 最长公共子序列

给定两个字符串 text1text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1text2 仅由小写英文字符组成。

解答:

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length(), n = text2.length();
        int[][] dp = new int[m+1][n+1];
        for(int i=1;i<=m;i++){
            char c1 = text1.charAt(i-1);
            for(int j=1;j<=n;j++){
                char c2 = text2.charAt(j-1);
                if(c1==c2){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        return dp[m][n];
    }
}

583. 两个字符串的删除操作

给定两个单词 word1word2,找到使得 word1word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。

示例:

输入: "sea", "eat"
输出: 2
解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea  "

提示:

  1. 给定单词的长度不超过500。
  2. 给定单词中的字符只含有小写字母。

解答:

class Solution {
    public int minDistance(String text1, String text2) {
        int m = text1.length(), n = text2.length();
        int[][] dp = new int[m+1][n+1];
        for(int i=1;i<=m;i++){
            char c1 = text1.charAt(i-1);
            for(int j=1;j<=n;j++){
                char c2 = text2.charAt(j-1);
                if(c1==c2){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        int t = dp[m][n];
        return m-t+n-t;
    }
}

day18-动态规划

2021/12/17

72. 编辑距离

给你两个单词 word1word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例 2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

提示:

  • 0 <= word1.length, word2.length <= 500
  • word1word2 由小写英文字母组成

解答:

class Solution {
    public int minDistance(String word1, String word2) {
        int n = word1.length();
        int m = word2.length();

        // 有一个字符串为空串
        if (n * m == 0) {
            return n + m;
        }

        // DP 数组
        int[][] D = new int[n + 1][m + 1];

        // 边界状态初始化
        for (int i = 0; i < n + 1; i++) {
            D[i][0] = i;
        }
        for (int j = 0; j < m + 1; j++) {
            D[0][j] = j;
        }

        // 计算所有 DP 值
        for (int i = 1; i < n + 1; i++) {
            for (int j = 1; j < m + 1; j++) {
                int left = D[i - 1][j] + 1;
                int down = D[i][j - 1] + 1;
                int left_down = D[i - 1][j - 1];
                if (word1.charAt(i - 1) != word2.charAt(j - 1)) {
                    left_down += 1;
                }
                D[i][j] = Math.min(left, Math.min(down, left_down));
            }
        }
        return D[n][m];
    }
}

322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

示例 4:

输入:coins = [1], amount = 1
输出:1

示例 5:

输入:coins = [1], amount = 2
输出:2

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 231 - 1
  • 0 <= amount <= 104

解答:

class Solution {
    public int coinChange(int[] coins, int amount) {
        int n = coins.length;
        int[] dp = new int[amount+1];
        Arrays.fill(dp, amount+1);
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < n; j++) {
                if(i>=coins[j]){
                    dp[i] = Math.min(dp[i-coins[j]]+1,dp[i]);
                }
            }
        }
        return dp[amount]>amount ? -1:dp[amount];
    }
}

343. 整数拆分

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

说明: 你可以假设 n 不小于 2 且不大于 58。

解答:

class Solution {
    public int integerBreak(int n) {
		int[] dp = new int[n+1];
        for(int i=2;i<=n;i++){
            int t = 0;
            for(int j=1;j<i;j++){
                t = Math.max(t,Math.max(j*(i-j),j*dp[i-j]));
            }
            dp[i] = t;
        }
        return dp[n];
    }
}

day19-动态规划

2021/12/18

201. 数字范围按位与

给你两个整数 leftright ,表示区间 [left, right] ,返回此区间内所有数字 按位与 的结果(包含 leftright 端点)。

示例 1:

输入:left = 5, right = 7
输出:4

示例 2:

输入:left = 0, right = 0
输出:0

示例 3:

输入:left = 1, right = 2147483647
输出:0

提示:

  • 0 <= left <= right <= 231 - 1

解答:

class Solution {
    public int rangeBitwiseAnd(int left, int right) {
        while(left<right){
            right = right&(right-1);
        }
        return right;
    }
}

day20-其他

2021/12/19

384. 打乱数组

给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。

实现 Solution class:

  • Solution(int[] nums) 使用整数数组 nums 初始化对象
  • int[] reset() 重设数组到它的初始状态并返回
  • int[] shuffle() 返回数组随机打乱后的结果

示例:

输入
["Solution", "shuffle", "reset", "shuffle"]
[[[1, 2, 3]], [], [], []]
输出
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]

解释
Solution solution = new Solution([1, 2, 3]);
solution.shuffle();    // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2]
solution.reset();      // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3]
solution.shuffle();    // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]

提示:

  • 1 <= nums.length <= 200
  • -106 <= nums[i] <= 106
  • nums 中的所有元素都是 唯一的
  • 最多可以调用 5 * 104resetshuffle

解答:

class Solution {
    int[] nums;
    int n;
    Random random = new Random();
    public Solution(int[] nums) {
		this.nums = nums;
        n = nums.length;
    }
    
    public int[] reset() {
		return nums;
    }
    
    public int[] shuffle() {
		int[] ans = nums.clone();
        for(int i=0;i<n;i++){
            swap(ans, i, i+random.nextInt(n-i));
        }
        return ans;
    }
    public void swap(int[] arr, int i, int j){
        int c = arr[i];
        arr[i] = arr[j];
        arr[j] = c;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(nums);
 * int[] param_1 = obj.reset();
 * int[] param_2 = obj.shuffle();
 */

202. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果 可以变为 1,那么这个数就是快乐数。

如果 n 是快乐数就返回 true ;不是,则返回 false

示例 1:

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:

输入:n = 2
输出:false

提示:

  • 1 <= n <= 231 - 1

解答:

class Solution {
    private int getNext(int n){
        int sum=0;
        while(n>0){
            int t = n%10;
            n = n/10;
            sum += t*t;
        }
        return sum;
    }
    
    public boolean isHappy(int n) {
		Set<Integer> s = new HashSet<>();
        while(n!=1&&!s.contains(n)){
            s.add(n);
            n = getNext(n);
        }
        return n==1;
    }
}

149. 直线上最多的点数

给你一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。

示例 1:

java刷题笔记_第14张图片

输入:points = [[1,1],[2,2],[3,3]]
输出:3

示例 2:

java刷题笔记_第15张图片

输入:points = [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出:4

提示:

  • 1 <= points.length <= 300
  • points[i].length == 2
  • -104 <= xi, yi <= 104
  • points 中的所有点 互不相同

解答:

class Solution {
    public int maxPoints(int[][] points) {
        int n = points.length;
        int ans = 1;
        for(int i=0;i<n;i++){
            int[] x = points[i];
            for(int j=i+1;j<n;j++){
                int[] y = points[j];
                int temp = 2;
                for(int k=j+1;k<n;k++){
                    int[] z = points[k];
                    int s1 = (y[1]-x[1])*(z[0]-y[0]);
                    int s2 = (z[1]-y[1])*(y[0]-x[0]);
                    if(s1==s2) temp++;
                }
                ans = Math.max(ans,temp);
            }
        }
        return ans;
    }
}

day21-二分查找

2021/12/28

704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1
示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。

解答:

class Solution {
    public int search(int[] nums, int target) {
        int L = 0, R = nums.length-1;
        while(L<=R){
            int mid = (R+L)/2;
            if(nums[mid]>target){
                R = mid-1;
            }else if(nums[mid]<target){
                L = mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }
}

278. 第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

示例 1:

输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false 
调用 isBadVersion(5) -> true 
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。

示例 2:

输入:n = 1, bad = 1
输出:1

提示:

  • 1 <= bad <= n <= 231 - 1

解答:

/* The isBadVersion API is defined in the parent class VersionControl.
      boolean isBadVersion(int version); */

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int L = 1, R = n;
        while(L<R){
            int mid = L+(R-L)/2;//不能使用(L+R)/2,会溢出
            if(isBadVersion(mid)){
                R = mid;
            }else{
                L = mid+1;
            }
        }
        return L;
    }
}

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

示例 4:

输入: nums = [1,3,5,6], target = 0
输出: 0

示例 5:

输入: nums = [1], target = 0
输出: 0

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums无重复元素升序排列数组
  • -104 <= target <= 104

解答:

class Solution {
    public int searchInsert(int[] nums, int target) {
        int L = 0, R = nums.length-1, mid=0;
        while(L<=R){
            mid = L+(R-L)/2;
            int num = nums[mid];
            if(num>target){
                R = mid-1;
            }else if(num<target){
                L = mid+1;
            }else{
                return mid;
            }
        }
        //判断:L==R时,当前值小于target,L右移,L为插入位置;当前值大于target,当前位置为插入位置;
        return L>mid ? L : mid;
    }
}

解答2:

class Solution {
    public int searchInsert(int[] nums, int target) {
        int L = 0, R = nums.length-1;
        while(L<=R){
            int mid = L+(R-L)/2;
            if(nums[mid]<target){
                L = mid+1;
            }else{
                R = mid-1;
            }
        }
        //同1理,当前值大于target,当前位置L为插入位置。所以L始终为插入位置。
        return L;
    }
}

day22-双指针

2021/12/29

977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 已按 非递减顺序 排序

进阶:

  • 请你设计时间复杂度为 O(n) 的算法解决本问题

解答:

class Solution {
    public int[] sortedSquares(int[] nums) {
        int n = nums.length;
        int L=0, R=n-1, idx=n-1;
        int[] ans = new int[n];
        while(L<=R){
            int a=nums[L]*nums[L], b=nums[R]*nums[R];
            if(a>b){
                ans[idx--] = a;
                L++;
            }else{
                ans[idx--] = b;
                R--;
            }
        }
        return ans;
    }
}

189. 轮转数组

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

提示:

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1
  • 0 <= k <= 105

进阶:

  • 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
  • 你可以使用空间复杂度为 O(1)原地 算法解决这个问题吗?

解答:

class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        int m = n, i = 0, j = 0, temp = 0, num = nums[i];
        if(k%n == 0) return;
        while(m--!=0){
            i = (i+k)%n;
            temp = nums[i];
            nums[i] = num;
            num = temp;
            if (i == j) {
                i = ++j;
                num = nums[i];
                temp = nums[i];
            }
        }
    }
}

day23-双指针

2021/12/30

283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

解答1:

class Solution {
    public void moveZeroes(int[] nums) {
        int n=nums.length, L=0, R=0;
        //非零数向前移,最后补零
        while(R<n){
            if(nums[R]!=0){
                if(L!=R){
                    nums[L]=nums[R];
                    L++;
                	R++;
                }else{
                    L++;
                	R++;
                }
            }else{
                R++;
            }
        }
        while(L<n){
            nums[L++]=0;
        }
    }
}

优化:

class Solution {
    public void moveZeroes(int[] nums) {
        int n=nums.length, L=0, R=0;
        while(R<n){
            if(nums[R]!=0){
                if(L!=R){
                    nums[L]=nums[R];
                }
                L++;
            }
            R++;
        }
        while(L<n){
            nums[L++]=0;
        }
    }
}

167. 两数之和 II - 输入有序数组

给定一个已按照 非递减顺序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值*。*numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

示例 1:

输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

示例 2:

输入:numbers = [2,3,4], target = 6
输出:[1,3]

示例 3:

输入:numbers = [-1,0], target = -1
输出:[1,2]

提示:

  • 2 <= numbers.length <= 3 * 104
  • -1000 <= numbers[i] <= 1000
  • numbers非递减顺序 排列
  • -1000 <= target <= 1000
  • 仅存在一个有效答案

解答:

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] temp = new int[2];
        int L=0,R=numbers.length-1;
        while(numbers[L]+numbers[R]!=target){
            if(numbers[L]+numbers[R]<target){
                L++;
            }else{
                R--;
            }
        }
        temp[0]=L+1;
        temp[1]=R+1;
        return temp;
    }
}

day24-双指针

2021/12/31

344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。

示例 1:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:

输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

提示:

  • 1 <= s.length <= 105
  • s[i] 都是 ASCII 码表中的可打印字符

解答:

class Solution {
    public void reverseString(char[] s) {
        int L=0, R=s.length-1;
        while(L<R){
            char temp = s[L];
            s[L] = s[R];
            s[R] = temp;
            L++;R--;
        }
    }
}

557. 反转字符串中的单词 III

给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

示例:

输入:"Let's take LeetCode contest"
输出:"s'teL ekat edoCteeL tsetnoc"

提示:

  • 在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。

解答:

class Solution {
    public String reverseWords(String s) {
        StringBuffer ans = new StringBuffer();
        for(int i=0;i<s.length();i++){
            int j=i;
            while(i<s.length()&&s.charAt(i)!=' ') i++;
            int k=i-1;
            while(j<=k){
                ans.append(s.charAt(k));
                k--;
            }
            if(i!=s.length())ans.append(" ");
        }
        return ans.toString();
    }
}

day25-双指针

2021/01/01

876. 链表的中间结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

提示:

  • 给定链表的结点数介于 1100 之间。

解答:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode L = head, R = head;
        boolean flag = true;
        while(R.next!=null){
            R = R.next;
            if(flag){
                L = L.next;
            }
            flag = !flag;
        }
        return L;
    }
}

19. 删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

java刷题笔记_第16张图片

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

**进阶:**你能尝试使用一趟扫描实现吗?

解答:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode L = new ListNode(0,head), R = head;
        ListNode ans = L;
        while(n-->0){
            R = R.next;
        }
        while(R!=null){
            R = R.next;
            L = L.next;
        }
        L.next = L.next.next;
        return ans.next;
    }
}

day26-滑动窗口

2021/01/02

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

示例 4:

输入: s = ""
输出: 0

提示:

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

解答:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashMap<Character, Integer> m = new HashMap<>();
        int L=0, R=0, ans=0;
        while(R<s.length()){
            char c = s.charAt(R);
            if(m.containsKey(c)){
                L = Math.max(L,m.get(c)+1);
            }
            m.put(c,R);
            ans = Math.max(ans, R-L+1);
            R++;
        }
        return ans;
    }
}

567. 字符串的排列

给你两个字符串 s1s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false

换句话说,s1 的排列之一是 s2子串

示例 1:

输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").

示例 2:

输入:s1= "ab" s2 = "eidboaoo"
输出:false

提示:

  • 1 <= s1.length, s2.length <= 104
  • s1s2 仅包含小写字母

解答:

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        if (n > m) {
            return false;
        }
        int[] nums1 = new int[26];
        int[] nums2 = new int[26];
        for (int i = 0; i < n; ++i) {
            ++nums1[s1.charAt(i) - 'a'];
            ++nums2[s2.charAt(i) - 'a'];
        }
        if (Arrays.equals(nums1, nums2)) {
            return true;
        }
        for (int i = n; i < m; ++i) {
            nums2[s2.charAt(i) - 'a']++;
            nums2[s2.charAt(i - n) - 'a']--;
            if (Arrays.equals(nums1, nums2)) {
                return true;
            }
        }
        return false;
    }
}

day28-广度优先搜索 / 深度优先搜索

2021/01/04

617. 合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:

输入: 
	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
输出: 
合并后的树:
	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7

注意: 合并必须从两个树的根节点开始。

解答:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if (root1 == null) {
            return root2;
        }
        if (root2 == null) {
            return root1;
        }
        TreeNode t = new TreeNode(root1.val + root2.val);
        t.left = mergeTrees(root1.left, root2.left);
        t.right = mergeTrees(root1.right, root2.right);
        return t;
    }
}

116. 填充每个节点的下一个右侧节点指针

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

初始状态下,所有 next 指针都被设置为 NULL

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

示例:

java刷题笔记_第17张图片

输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。

提示:

  • 树中节点的数量少于 4096
  • -1000 <= node.val <= 1000

解答:

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}
    
    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/

class Solution {
    public Node connect(Node root) {
        if(root==null) return root;
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            int n = queue.size();
            while(n-->0){
                Node t = queue.poll();
                if(n!=0){
                    t.next = queue.peek();
                }
                if(t.left!=null){
                    queue.add(t.left);
                }
                if(t.right!=null){
                    queue.add(t.right);
                }
            }
        }
        return root;
    }
}

day29-广度优先搜索 / 深度优先搜索

2021/01/06

542. 01 矩阵

给定一个由 01 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。

两个相邻元素间的距离为 1

示例 1:

java刷题笔记_第18张图片

输入:mat = [[0,0,0],[0,1,0],[0,0,0]]
输出:[[0,0,0],[0,1,0],[0,0,0]]

示例 2:

java刷题笔记_第19张图片

输入:mat = [[0,0,0],[0,1,0],[1,1,1]]
输出:[[0,0,0],[0,1,0],[1,2,1]]

提示:

  • m == mat.length
  • n == mat[i].length
  • 1 <= m, n <= 104
  • 1 <= m * n <= 104
  • mat[i][j] is either 0 or 1.
  • mat 中至少有一个 0

解答1:

class Solution {
    int[] xx = {0,0,1,-1}, yy = {1,-1,0,0};
    public int[][] updateMatrix(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        Queue<int[]> queue = new LinkedList<>();
        //将所有的1替换为-1, 将所有0加入队列中
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == 1){
                    matrix[i][j] = -1;
                } else {
                    queue.add(new int[]{i,j});
                }
            }
        }
        while (!queue.isEmpty()){
            int[] point = queue.poll();
            int a = point[0];
            int b = point[1];
            for (int i = 0; i < 4; i++) {
                int x = a + xx[i];
                int y = b + yy[i];
                if (x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] == -1){
                    matrix[x][y] = matrix[a][b] + 1;
                    queue.add(new int[]{x,y});
                }
            }
        }
        return matrix;
    }
}

解答2:

class Solution {
    public int[][] updateMatrix(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        int[][] ans = new int[m][n];
        for(int i=0;i<m;i++){
            Arrays.fill(ans[i],Integer.MAX_VALUE/2);
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(matrix[i][j]==0){
                    ans[i][j]=0;
                }
            }
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(i-1>=0){
                    ans[i][j] = Math.min(ans[i][j], ans[i-1][j]+1);
                }
                if(j-1>=0){
                    ans[i][j] = Math.min(ans[i][j], ans[i][j-1]+1);
                }
            }
        }
        for(int i=m-1;i>=0;i--){
            for(int j=n-1;j>=0;j--){
                if(i+1<m){
                    ans[i][j] = Math.min(ans[i][j], ans[i+1][j]+1);
                }
                if(j+1<n){
                    ans[i][j] = Math.min(ans[i][j], ans[i][j+1]+1);
                }
            }
        }
        return ans;
    }
}

994. 腐烂的橘子

在给定的网格中,每个单元格可以有以下三个值之一:

  • 0 代表空单元格;
  • 1 代表新鲜橘子;
  • 2 代表腐烂的橘子。

每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。

返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1

示例 1:

java刷题笔记_第20张图片

输入:[[2,1,1],[1,1,0],[0,1,1]]
输出:4

示例 2:

输入:[[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。

示例 3:

输入:[[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。

提示:

  1. 1 <= grid.length <= 10
  2. 1 <= grid[0].length <= 10
  3. grid[i][j] 仅为 012

解答:

class Solution {
    public int orangesRotting(int[][] grid) {
        int M = grid.length;
        int N = grid[0].length;
        Queue<int[]> queue = new LinkedList<>();

        int count = 0; // count 表示新鲜橘子的数量
        for (int r = 0; r < M; r++) {
            for (int c = 0; c < N; c++) {
                if (grid[r][c] == 1) {
                    count++;
                } else if (grid[r][c] == 2) {
                    queue.add(new int[]{r, c});
                }
            }
        }

        int round = 0; // round 表示腐烂的轮数,或者分钟数
        while (count > 0 && !queue.isEmpty()) {
            round++;
            int n = queue.size();
            for (int i = 0; i < n; i++) {
                int[] orange = queue.poll();
                int r = orange[0];
                int c = orange[1];
                if (r-1 >= 0 && grid[r-1][c] == 1) {
                    grid[r-1][c] = 2;
                    count--;
                    queue.add(new int[]{r-1, c});
                }
                if (r+1 < M && grid[r+1][c] == 1) {
                    grid[r+1][c] = 2;
                    count--;
                    queue.add(new int[]{r+1, c});
                }
                if (c-1 >= 0 && grid[r][c-1] == 1) {
                    grid[r][c-1] = 2;
                    count--;
                    queue.add(new int[]{r, c-1});
                }
                if (c+1 < N && grid[r][c+1] == 1) {
                    grid[r][c+1] = 2;
                    count--;
                    queue.add(new int[]{r, c+1});
                }
            }
        }

        if (count > 0) {
            return -1;
        } else {
            return round;
        }
    }
}

day30-递归 / 回溯

2021/01/08

77. 组合

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

示例 2:

输入:n = 1, k = 1
输出:[[1]]

提示:

  • 1 <= n <= 20
  • 1 <= k <= n

解答:

class Solution {
   public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        for(int i=1;i<=n-k+1;i++){
            List<Integer> temp = new ArrayList<Integer>();
            temp.add(i);
            ans.add(temp);
        }
        for(int i=0;i<k-1;i++){
            int m = ans.size();
            for(int j=0;j<m;j++){
                List<Integer> temp = new ArrayList<>(ans.get(0));
                for(int p=temp.get(temp.size()-1)+1;n-p>=k-i-2;p++){
                    temp.add(p);
                    ans.add(new ArrayList<>(temp));
                    temp.remove(temp.size()-1);
                }
                ans.remove(0);
            }
        }
        return ans;
    }
}

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:

输入:nums = [1]
输出:[[1]]

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

解答:

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        List<Integer> temp = new ArrayList<Integer>();
        for(int num:nums){
            temp.add(num);
        }
        int n = nums.length;
        fun(n, ans, temp, 0);
        return ans;
    }
    public void fun(int n, List<List<Integer>> ans, List<Integer> temp, int first){
        if(first==n){
            ans.add(new ArrayList<Integer>(temp));
        }
        for(int i=first;i<n;i++){
            Collections.swap(temp,first,i);
            fun(n,ans,temp,first+1);
            Collections.swap(temp,first,i);
        }
    }
}

784. 字母大小写全排列

给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。

示例:
输入:S = "a1b2"
输出:["a1b2", "a1B2", "A1b2", "A1B2"]

输入:S = "3z4"
输出:["3z4", "3Z4"]

输入:S = "12345"
输出:["12345"]

提示:

  • S 的长度不超过12
  • S 仅由数字和字母组成。

解答:

class Solution {
    List<String> ans = new ArrayList();
    public List<String> letterCasePermutation(String s) {
        getStr(0, s, new StringBuffer());
        return ans;
    }

    public void getStr(int index, String s, StringBuffer sb){
        if(index == s.length()){
            ans.add(sb.toString());
            return;
        }
        char ch = s.charAt(index);
        sb.append(ch);
        getStr(index + 1, s, sb);
        sb.deleteCharAt(sb.length() - 1);
        
        if(!Character.isDigit(ch)){
            ch = (char)(ch - 'a' >= 0 ? ch - 32 : ch + 32);
            sb.append(ch);
            getStr(index + 1, s, sb);
            sb.deleteCharAt(sb.length() - 1);
        }
    }
}

day31-动态规划

2021/01/09

70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

**注意:**给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

解答:

class Solution {
    public int climbStairs(int n) {
        if(n==1) return 1;
        if(n==2) return 2;
        int[] sum = new int[n];
        sum[0] = 1;
        sum[1] = 2;
        for(int i=2;i<n;i++){
            sum[i] = sum[i-1]+sum[i-2];
        }
        return sum[n-1];
    }
}

198. 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

解答:

class Solution {
    public int rob(int[] nums) {
        if(nums.length==0||nums==null) return 0;
        int n = nums.length;
        if(n==1) return nums[0];
        int a = nums[0], b = Math.max(nums[0], nums[1]);
        for(int i=2;i<n;i++){
            int temp = b;
            b = Math.max(b, nums[i]+a);
            a = temp;
        }
        return b;
    }
}

120. 三角形最小路径和

给定一个三角形 triangle ,找出自顶向下的最小路径和。

每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 ii + 1

示例 1:

输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
   2
  3 4
 6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。

示例 2:

输入:triangle = [[-10]]
输出:-10

提示:

  • 1 <= triangle.length <= 200
  • triangle[0].length == 1
  • triangle[i].length == triangle[i - 1].length + 1
  • -104 <= triangle[i][j] <= 104

进阶:

  • 你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题吗?

解答:

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int n = triangle.size();
        int[] dp = new int[n + 1];
        for (int i = n - 1; i >= 0; i--) {
            for (int j = 0; j <= i; j++) {
                dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);
            }
        }
        return dp[0];
    }
}

day31-位运算

2021/01/10

231. 2 的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false

如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。

示例 1:

输入:n = 1
输出:true
解释:20 = 1

示例 2:

输入:n = 16
输出:true
解释:24 = 16

示例 3:

输入:n = 3
输出:false

示例 4:

输入:n = 4
输出:true

示例 5:

输入:n = 5
输出:false

提示:

  • -231 <= n <= 231 - 1

**进阶:**你能够不使用循环/递归解决此问题吗?

解答:

class Solution {
    public boolean isPowerOfTwo(int n) {
        return n>0 && (n&(n-1))==0;
    }
}

191. 位1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。

提示:

  • 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
  • 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。

示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

提示:

  • 输入必须是长度为 32二进制串

进阶

  • 如果多次调用这个函数,你将如何优化你的算法?

解答:

public class Solution {
    public int hammingWeight(int n) {
        int ans = 0;
        for(int i=0;i<32;i++){
            if((n&(1<<i))!=0) ans++;
        }
        return ans;
    }
}

解答2:

public class Solution {
    public int hammingWeight(int n) {
        int ans = 0;
        while(n!=0){
            n &= n-1;
            ans++;
        }
        return ans;
    }
}

你可能感兴趣的:(刷题,java,算法)