LeetCode算法日记

目录

一、两数之和

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

三、两两交换

四、旋转列表

五、斐波那契数列

六、中序遍历二叉树

七、子集II

七、丑数1

八、寻找旋转排序数组中的最小值

九、最大数

十、丑数2

十一、打劫

十二、存在重复数组3

十三、下一个更大元素I

十四、全排列(回溯)

十五、全排列II

十六、路径交叉(归纳法)

十七、分糖果

十八、键盘行

十九、只出现一次的数字|||

二十、搜索二维矩阵II

二十一、接雨水I

二十二、接雨水II

二十三、找最大子序列

二十四、丢失的数字

二十五、范围求和II

二十六、猜数字游戏

二十七、提莫

二十八、网格照明


一、两数之和

学习递归:三道题套路解决递归问题 | lyl's blog

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

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

来源:力扣(LeetCode)

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap();
        for(int i = 0;i<nums.length;i++){
            if(map.containsKey(target-nums[i])){
                return new int[]{map.get(target-nums[i]),i};
            }
            map.put(nums[i],i);
        }
        return new int []{-1,-1};
    }
}

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

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。

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

/**
 * 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) {
        //终止条件
        if(head==null||head.next==null){
            return head;
        }
        //缩短列表
        head.next = deleteDuplicates(head.next);
        //返回条件
        if(head.val==head.next.val){
            //向后移动
            head = head.next;
            return head;
        }
        return head;
    }
}

三、两两交换

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

/**
 * 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 swapPairs(ListNode head) {
        //终止条件
        if(head==null||head.next==null){
            return head;
        }
        //任务  交换head 和 head.next
        ListNode next = head.next;
        head.next = swapPairs(next.next);
        next.next = head;
        //返回条件
        return next;
    }
}

四、旋转列表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

/**
 * 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 rotateRight(ListNode head, int k) {
        int length = 1;
        ListNode tmp = head;
        if(head==null){
            return head;
        }
        while(tmp.next!=null){
            length++;
            tmp = tmp.next;
        }
        k = k%length;
        if(k==0){
            return head;
        }
        
        tmp.next = head;
        for(int i = 0;i<length-k;i++){
            tmp = tmp.next;
        }
        ListNode newNode = tmp.next;
        tmp.next = null;
        return newNode;
    }
}

五、斐波那契数列

public class Test03 {
    static int fbnq(int a){
        //终止条件
        if(a==1||a==2){
            return 1;
        }
//        递归 求前俩个数之和
        int num = fbnq(a-1)+fbnq(a-2);
//        返回
        return num;
    }
    public static void main(String[] args) {
        int i = 10;
//        1 1 2 3 5 8 13 21 34 55
        System.out.println("fbnq(i) = " + fbnq(i));

    }
}

六、中序遍历二叉树

实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:
BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
int next()将指针向右移动,然后返回指针处的数字。
注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。

你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。

/**
 * 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 BSTIterator {
    //存储左子树
    Deque<TreeNode> deque = new ArrayDeque<>();
    public BSTIterator(TreeNode root) {
        //左子树
        dfsLeft(root);
    }
    //递归 存储左子树
    public void dfsLeft(TreeNode tree){
        while(tree!=null){
            deque.addLast(tree);
            tree = tree.left;
        }
    }
    public int next() {
        //获取左子树
        TreeNode tNode = deque.pollLast();
        //右子树
        dfsLeft(tNode.right);
        //输出
        return tNode.val;
    }
    
    public boolean hasNext() {
        return !deque.isEmpty();
    }
}

/**
 * Your BSTIterator object will be instantiated and called as such:
 * BSTIterator obj = new BSTIterator(root);
 * int param_1 = obj.next();
 * boolean param_2 = obj.hasNext();
 */

七、子集II

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

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

class Solution {
    public static List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> retList = new ArrayList<>();
        retList.add(new ArrayList<>());
        //如果为空 返回
        if (nums == null || nums.length == 0) return retList;
        Arrays.sort(nums);
        //开始循环
        int yb = 1;
        for (int i = 0; i < nums.length; i++) {
            int size = retList.size();
            if (i > 0 && nums[i] != nums[i - 1]) {
                yb = size;
            }
            for (int j = size-yb; j < size; j++) {
                List<Integer> list = new ArrayList(retList.get(j));
                list.add(nums[i]);
                retList.add(list);
            }
        }
        return retList;
    }
}

七、丑数1

给你一个整数 n ,请你判断 n 是否为 丑数 。如果是,返回 true ;否则,返回 false 。

丑数 就是只包含质因数 2、3 和/或 5 的正整数。

class Solution {
    public boolean isUgly(int n) {
        int[] a = {2,3,5};
        if(n==1){
            return true;
        }
        if(n==0){
            return false;
        }
       
            for(int i:a){
                while(n%i==0){
                    n = n/i;
                }
            }
            return n==1;
        
    }
}

八、寻找旋转排序数组中的最小值

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
若旋转 7 次,则可以得到 [0,1,4,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 ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

class Solution {
    public int findMin(int[] nums) {
        int low = 0;
        int high = nums.length - 1;
        while (low < high) {
            int pivot = low + (high - low) / 2;
            if (nums[pivot] < nums[high]) {
                high = pivot;
            }
            else if (nums[pivot] > nums[high]) {
                low = pivot + 1;
            }
            else {
                high -= 1;
            }
        }
        return nums[low];
    }
}

九、最大数

给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。

注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。

class Solution {
    public String largestNumber(int[] nums) {
        String[] h=new String[nums.length];
        for(int i=0;i<nums.length;i++) h[i]=String.valueOf(nums[i]);
        Arrays.sort(h,new Comparator<String>(){
            @Override
            public int compare(String a,String b){
                if (a.charAt(0)!= b.charAt(0)) {
                    return b.charAt(0) - a.charAt(0);
                }
                String l1=a+b;
                String l2=b+a;
                return l2.compareTo(l1);
            }
        });
        if(h[0].charAt(0)=='0') return "0";
        StringBuilder sb=new StringBuilder();
        for(String ky:h) sb.append(ky);
        return sb.toString();
    }
}

十、丑数2

给你一个整数 n ,请你找出并返回第 n 个 丑数 。

丑数 就是只包含质因数 23 和/或 5 的正整数。

class Solution {
    public int nthUglyNumber(int n) {
        int[] dp = new int[n + 1];
        dp[1] = 1;
        int p2 = 1, p3 = 1, p5 = 1;
        for (int i = 2; i <= n; i++) {
            int num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5] * 5;
            dp[i] = Math.min(Math.min(num2, num3), num5);
            if (dp[i] == num2) {
                p2++;
            }
            if (dp[i] == num3) {
                p3++;
            }
            if (dp[i] == num5) {
                p5++;
            }
        }
        return dp[n];    
    }
}

十一、打劫

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

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

class Solution {
 public int rob(int[] nums) {
        int length = nums.length;
        if (length == 1) {
            return nums[0];
        } else if (length == 2) {
            return Math.max(nums[0], nums[1]);
        }
        return Math.max(robRange(nums, 0, length - 2), robRange(nums, 1, length - 1));
    }

    public int robRange(int[] nums, int start, int end) {
        int first = nums[start], second = Math.max(nums[start], nums[start + 1]);
        for (int i = start + 2; i <= end; i++) {
            int temp = second;
            second = Math.max(first + nums[i], second);
            first = temp;
        }
        return second;
    }
}

十二、存在重复数组3

class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
      TreeSet<Long> set = new TreeSet<Long>();
        for(int i = 0;i<nums.length;i++){
            Long ceiling = set.ceiling((long) nums[i]-(long)t);
            if(ceiling!=null&&ceiling<=(long) nums[i]+(long)t){
                return true;
            }
            set.add((long)nums[i]);
            if(i>=k){
                set.remove((long)nums[i-k]);
            }
        }
        return false;
    }
}

十三、下一个更大元素I

给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

例1

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
    对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
    对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
    对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

-----------

例2

输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
    对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
    对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/next-greater-element-i
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

单调栈法

理解:单调栈法使用map+队列来实现,由于是找后面一个最大值,所以采用倒序循环,队列用于存储循环游标当前的右边的最大值,当发现还有最大值时,pop比它小的元素(最后剩下的就是当前循环元素的右边第一个最大值)。

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        Map<Integer,Integer> map = new HashMap<>();
       Deque<Integer> deque = new ArrayDeque();
       //反向遍历
        for(int i = nums2.length-1;i>=0;i--){
            int num = nums2[i];
            while (!deque.isEmpty()&&deque.peek()<=num){
                deque.pop();
            }
            map.put(num,deque.isEmpty()?-1:deque.peek());
            deque.push(num);
        }
        int res[] = new int[nums1.length];
        for (int i = 0;i<nums1.length;i++){
            res[i] = map.get(nums1[i]);
        }
        return res;
    }
}

暴力法

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        for (int i = 0; i < nums1.length; i++) {
            int nex = 0;
            int num = nums1[i];
            for (int j = 0; j < nums2.length; j++) {
                if (nex == 1||nums1[i] == nums2[j]) {
                    nex = 1;
                }
                if (nex == 1) {
                    if (nums1[i] < nums2[j]) {
                        nums1[i] = nums2[j];
                        break;
                    }
                }
            }
            if(nums1[i]==num){
               nums1[i] = -1; 
            }
        }
        return nums1;
    }
}

十四、全排列(回溯)

给定一个不含重复数字的数组 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]]

class Solution {
    public List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> linkedList = new ArrayList<>();
    List<Integer> list = new LinkedList<>();
        for (Integer i :
                nums) {
            list.add(i);
        }
        suanFa(0,list,linkedList);
        return linkedList;
    }

    public void suanFa(Integer first,List<Integer> list,List<List<Integer>> LinkedList){
        if(first==list.size()){
            LinkedList.add(new ArrayList<>(list));
        }
        for (int i = first; i < list.size(); i++) {
            Collections.swap(list,first,i);
            suanFa(first+1,list,LinkedList);
            Collections.swap(list,first,i);
        }
    }


}

十五、全排列II

有重复数字

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> linkedList = new ArrayList<>();
        List<Integer> list = new LinkedList<>();
        for (Integer i :
                nums) {
            list.add(i);
        }
        HashSet<Integer> set = new HashSet<>();
        suanFa(0,list,linkedList,set);
        return linkedList;
    }
    public void suanFa(Integer first,List<Integer> list,List<List<Integer>> LinkedList,HashSet<Integer> set){
        if(first==list.size()){
            if (set.add(list.hashCode())) {
                LinkedList.add(new LinkedList<>(list));
            }
        }
        for (int i = first; i < list.size(); i++) {
           if(first!=i&&list.get(first).compareTo(list.get(i))==0){
                continue;
            }else {
                Collections.swap(list,first,i);
                suanFa(first+1,list,LinkedList,set);
                Collections.swap(list,first,i);
            }
        }
    }
}

十六、路径交叉(归纳法)

给你一个整数数组 distance 。

从 X-Y 平面上的点 (0,0) 开始,先向北移动 distance[0] 米,然后向西移动 distance[1] 米,向南移动 distance[2] 米,向东移动 distance[3] 米,持续移动。也就是说,每次移动后你的方位会发生逆时针变化。

判断你所经过的路径是否相交。如果相交,返回 true ;否则,返回 false 。

一共四种情况

class Solution {
    public static boolean isSelfCrossing(int[] distance) {
        int size = distance.length;
        if (size <= 3) {
            return false;
        }
        for (int i = 3; i < distance.length; i++) {
            if (distance[i - 3] >= distance[i - 1] && distance[i - 2] <= distance[i]) {
                return true;
            }
            if (i >= 4 && distance[i - 3] == distance[i - 1] && distance[i - 4] + distance[i] >= distance[i - 2]
            ) {
                return true;
            }
            if (i >= 4 && distance[i - 3] >= distance[i - 1] && distance[i] >= distance[i - 2]
            ) {
                return true;
            }
            if (i >= 5 && distance[i - 5] + distance[i - 1] >= distance[i - 3] && distance[i - 4] + distance[i] >= distance[i - 2]
                    && distance[i - 3] >= distance[i - 1] && distance[i - 2] >= distance[i - 4]
            ) {
                return true;
            }
        }
        return false;
    }
}

十七、分糖果

Alice 有 n 枚糖,其中第 i 枚糖的类型为 candyType[i] 。Alice 注意到她的体重正在增长,所以前去拜访了一位医生。

医生建议 Alice 要少摄入糖分,只吃掉她所有糖的 n / 2 即可(n 是一个偶数)。Alice 非常喜欢这些糖,她想要在遵循医生建议的情况下,尽可能吃到最多不同种类的糖。

给你一个长度为 n 的整数数组 candyType ,返回: Alice 在仅吃掉 n / 2 枚糖的情况下,可以吃到糖的最多种类数

示例 1:

输入:candyType = [1,1,2,2,3,3]
输出:3
解释:Alice 只能吃 6 / 2 = 3 枚糖,由于只有 3 种糖,她可以每种吃一枚。

示例 2:

输入:candyType = [1,1,2,3]
输出:2
解释:Alice 只能吃 4 / 2 = 2 枚糖,不管她选择吃的种类是 [1,2]、[1,3] 还是 [2,3],她只能吃到两种不同类的糖。

class Solution {
    public int distributeCandies(int[] candyType) {
      Set set = new HashSet();
        int size = candyType.length;
        for (int i:candyType) {
            if(set.size()>=size/2){
                break;
            }
            set.add(i);
        }
        size = size/2;
        if(set.size()==size){
            return size;
        }else {
            return set.size();
        }
    }
}

十八、键盘行

给你一个字符串数组 words ,只返回可以使用在 美式键盘 同一行的字母打印出来的单词。

美式键盘 中:

  • 第一行由字符 "qwertyuiop" 组成。
  • 第二行由字符 "asdfghjkl" 组成。
  • 第三行由字符 "zxcvbnm" 组成。
输入:words = ["Hello","Alaska","Dad","Peace"]
输出:["Alaska","Dad"]
输入:words = ["omk"]
输出:[]

方法1

    public Map<Character,Integer> initData(){
        Map<Character,Integer> map = new HashMap<>();
        String key1 = "zxcvbnm";
        String key2 = "asdfghjkl";
        String key3 = "qwertyuiop";
        String[] key = {key1,key2,key3};
        int i = 1;
        for (String c :
                key) {
            for (Character keys :
                    c.toCharArray()) {
                map.put(keys,i);
                map.put(keys.toString().toUpperCase().charAt(0),i);
            }
            i++;
        }
        return map;
    }

    public String[] findWords(String[] words) {
        Map<Character,Integer> map = initData();
        List<String> list = new ArrayList();
        for (String word :
                words) {
            if(word.equals("")){
                continue;
            }
            int num = map.get(word.charAt(0));
            boolean res = true;
            for (Character c :
                    word.toCharArray()) {
                if(map.get(c)!=num){
                    res = false;
                    break;
                }
            }
            if(res){
                list.add(word);
            }

        }
        return list.toArray(new String[list.size()]);
    }

方法2

12210...代表abcdefg字母的行号

class Solution {


    public  String[] findWords(String[] words) {
         String key = "12210111011122000010020202";
        List<String> list = new ArrayList();
        for (String word :
                words) {
            if(word.equals("")){
                continue;
            }
            boolean res = true;
            int i = key.charAt(Character.toLowerCase(word.charAt(0))-'a');
            for (Character c :
                    word.toCharArray()) {
                if(i!=(key.charAt(Character.toLowerCase(c)-'a'))){
                    res = false;
                    break;
                }
            }
            if(res){
                list.add(word);
            }
        }
        return list.toArray(new String[list.size()]);
    }
}

十九、只出现一次的数字|||

异或运算

class Solution {
    public int[] singleNumber(int[] nums) {
      int sum = 0;
        for (int i :
                nums) {
            sum ^= i;
        }
        int tmp = sum==Integer.MIN_VALUE?sum:sum&(-sum);
        int type1 = 0;
        int type2 = 0;
        for (int i :
                nums) {
            if((tmp&i)==0){
                type1^=i;
            }else {
                type2^=i;
            }
        }
        return new int[]{type1,type2};
    }
}

自己的思路

class Solution {
    public int[] singleNumber(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (Integer num :
                nums) {
            if(set.contains(num)){
                set.remove(num);
            }else {
                set.add(num);
            }
        }
        Object [] tmp = set.toArray();
        int[] res = new int[set.size()];
        int tp = 0;
       for (Object i :
                tmp) {
            res[tp] = (Integer) i;
            tp+=1;
        }
        return res;
    }
}

二十、搜索二维矩阵II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int l = matrix.length-1;
        int y = matrix[0].length-1;
        int x = 0;
        while (y>=0&&x<=l){
            if(matrix[x][y]==target){
                return true;
            }
            if(matrix[x][y]>target){
                y--;
            }else {
                x++;
            }
        }
        return false;
    }
}

二十一、接雨水I

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

思路:采用动态规划算法,两边往中间遍历,存储左、右边最大值,遍历时,如果左边最大值比右边大则计算右边可接雨水,右边游标向左移一位,反之。

class Solution {
    public int trap(int[] height) {
        int leftMax = height[0];
        int left = 0;
        int right = height.length-1;
        int rightMax = height[height.length-1];
        int sum = 0;
        while (left!=right){
            if(height[left]>leftMax){
                leftMax = height[left];
            }
            if(height[right]>rightMax){
                rightMax = height[right];
            }

            if(leftMax>=rightMax){
                sum+=rightMax-height[right];
                right--;
            }else {
                sum+=leftMax-height[left];
                left++;
            }
        }
        return sum;
    }
}

二十二、接雨水II

二十三、找最大子序列

给你一个整数数组 arr 和一个整数 difference,请你找出并返回 arr 中最长等差子序列的长度,该子序列中相邻元素之间的差等于 difference 。

子序列 是指在不改变其余元素顺序的情况下,通过删除一些元素或不删除任何元素而从 arr 派生出来的序列。

示例 1:

输入:arr = [1,2,3,4], difference = 1
输出:4
解释:最长的等差子序列是 [1,2,3,4]。
示例 2:

输入:arr = [1,3,5,7], difference = 1
输出:1
解释:最长的等差子序列是任意单个元素。
示例 3:

输入:arr = [1,5,7,8,5,3,4,2,1], difference = -2
输出:4
解释:最长的等差子序列是 [7,5,3,1]。

提示:

  • 1 <= arr.length <= 105
  • -104 <= arr[i], difference <= 104
class Solution {
     public static int longestSubsequence(int[] arr, int difference) {
        Map<Integer, Integer> max = new HashMap<>();
        int res = 1;
        for (int arri:arr) {
            int num = max.getOrDefault(arri - difference,0)+1;
            max.put(arri, num);
            res = res > num ? res : num;
        }
        return res;
//        return Collections.max(max.values());
    }
}

二十四、丢失的数字

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

示例 1:

输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 2:

输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。

解法1 公式

多次运行发现for循环比stream快

class Solution {
    public int missingNumber(int[] nums) {
        // int sum = Arrays.stream(nums).sum();
        int sum = 0;
        for (int i :
                nums) {
            sum+=i;
        }
        int length = nums.length;
        length = length*(length+1)/2;
        return length-sum;
    }
}

解法2   位运算  异或

class Solution {
    public int missingNumber(int[] nums) {
        int res = 0;
        for(int i :nums){
            res^=i;
        }
        for(int i = 0;i<=nums.length;i++){
            res^=i;
        }
        return res;
    }
}

二十五、范围求和II

给定一个初始元素全部为 0,大小为 m*n 的矩阵 M 以及在 M 上的一系列更新操作。

操作用二维数组表示,其中的每个操作用一个含有两个正整数 a 和 b 的数组表示,含义是将所有符合 0 <= i < a 以及 0 <= j < b 的元素 M[i][j] 的值都增加 1。

在执行给定的一系列操作后,你需要返回矩阵中含有最大整数的元素个数。

示例 1:

输入: 
m = 3, n = 3
operations = [[2,2],[3,3]]
输出: 4
解释: 
初始状态, M = 
[[0, 0, 0],
 [0, 0, 0],
 [0, 0, 0]]

执行完操作 [2,2] 后, M = 
[[1, 1, 0],
 [1, 1, 0],
 [0, 0, 0]]

执行完操作 [3,3] 后, M = 
[[2, 2, 1],
 [2, 2, 1],
 [1, 1, 1]]

M 中最大的整数是 2, 而且 M 中有4个值为2的元素。因此返回 4。

class Solution {
    public int maxCount(int m, int n, int[][] ops) {
        int minx = 999999;
        int miny = 999999;
        if(ops.length==0){
            return m*n;
        }
        for (int a[] : ops
        ) {
            minx = minx < a[0] ? minx : a[0];
            miny = miny < a[1] ? miny : a[1];
        }
        return minx*miny;
    }
}

二十六、猜数字游戏

你在和朋友一起玩 猜数字(Bulls and Cows)游戏,该游戏规则如下:

写出一个秘密数字,并请朋友猜这个数字是多少。朋友每猜测一次,你就会给他一个包含下述信息的提示:

猜测数字中有多少位属于数字和确切位置都猜对了(称为 "Bulls", 公牛),
有多少位属于数字猜对了但是位置不对(称为 "Cows", 奶牛)。也就是说,这次猜测中有多少位非公牛数字可以通过重新排列转换成公牛数字。
给你一个秘密数字 secret 和朋友猜测的数字 guess ,请你返回对朋友这次猜测的提示。

提示的格式为 "xAyB" ,x 是公牛个数, y 是奶牛个数,A 表示公牛,B 表示奶牛。

请注意秘密数字和朋友猜测的数字都可能含有重复数字。

示例 1:

输入: secret = "1807", guess = "7810"
输出: "1A3B"
解释: 数字和位置都对(公牛)用 '|' 连接,数字猜对位置不对(奶牛)的采用斜体加粗标识。
"1807"
  |
"7810"
示例 2:

输入: secret = "1123", guess = "0111"
输出: "1A1B"
解释: 数字和位置都对(公牛)用 '|' 连接,数字猜对位置不对(奶牛)的采用斜体加粗标识。
"1123"        "1123"
  |      or     |
"0111"        "0111"
注意,两个不匹配的 1 中,只有一个会算作奶牛(数字猜对位置不对)。通过重新排列非公牛数字,其中仅有一个 1 可以成为公牛数字。

class Solution {
    public String getHint(String secret, String guess) {
        int [] a = {0,0,0,0,0,0,0,0,0,0};
        int yb = 0;
        char [] b = guess.toCharArray();
        int res1 = 0;
        int res2 = 0;
        for (Character sec :
                secret.toCharArray()) {
            if(b[yb]==sec) {
                res1++;
                b[yb] = 'a';
            }else {
                a[sec-'0'] +=1;
            }
            yb++;
        }
    
        for (Character sec :
                b) {
            if(sec=='a'){
                continue;
            }
            if(a[sec-'0']>0){
                a[sec-'0'] -=1;
                res2++;
            }
        }
        return res1+"A"+res2+"B";
    }
}

二十七、提莫

在《英雄联盟》的世界中,有一个叫 “提莫” 的英雄。他的攻击可以让敌方英雄艾希(编者注:寒冰射手)进入中毒状态。

当提莫攻击艾希,艾希的中毒状态正好持续 duration 秒。

正式地讲,提莫在 t 发起发起攻击意味着艾希在时间区间 [t, t + duration - 1](含 t 和 t + duration - 1)处于中毒状态。如果提莫在中毒影响结束 前 再次攻击,中毒状态计时器将会 重置 ,在新的攻击之后,中毒影响将会在 duration 秒后结束。

给你一个 非递减 的整数数组 timeSeries ,其中 timeSeries[i] 表示提莫在 timeSeries[i] 秒时对艾希发起攻击,以及一个表示中毒持续时间的整数 duration 。

返回艾希处于中毒状态的 总 秒数。

 
示例 1:

输入:timeSeries = [1,4], duration = 2
输出:4
解释:提莫攻击对艾希的影响如下:
- 第 1 秒,提莫攻击艾希并使其立即中毒。中毒状态会维持 2 秒,即第 1 秒和第 2 秒。
- 第 4 秒,提莫再次攻击艾希,艾希中毒状态又持续 2 秒,即第 4 秒和第 5 秒。
艾希在第 1、2、4、5 秒处于中毒状态,所以总中毒秒数是 4 。

class Solution {
    public int findPoisonedDuration(int[] timeSeries, int duration) {
         int size = timeSeries.length;
        int zmTime = 0;
        int keep = 0;
        for (int i = 0; i < size - 1; i++) {
            if (timeSeries[i] + duration - 1 < timeSeries[i + 1]) {
                zmTime += duration;
            } else {
                keep = timeSeries[i + 1] - timeSeries[i];
                zmTime += keep;
                keep = duration - keep;
            }
        }
        return zmTime + duration;
    }
}

二十八、网格照明

在大小为 n x n 的网格 grid 上,每个单元格都有一盏灯,最初灯都处于 关闭 状态。

给你一个由灯的位置组成的二维数组 lamps ,其中 lamps[i] = [rowi, coli] 表示 打开 位于 grid[rowi][coli] 的灯。即便同一盏灯可能在 lamps 中多次列出,不会影响这盏灯处于 打开 状态。

当一盏灯处于打开状态,它将会照亮 自身所在单元格 以及同一 行 、同一 列 和两条 对角线 上的 所有其他单元格 。

另给你一个二维数组 queries ,其中 queries[j] = [rowj, colj] 。对于第 j 个查询,如果单元格 [rowj, colj] 是被照亮的,则查询结果为 1 ,否则为 0 。在第 j 次查询之后 [按照查询的顺序] ,关闭 位于单元格 grid[rowj][colj] 上及相邻 8 个方向上(与单元格 grid[rowi][coli] 共享角或边)的任何灯。

返回一个整数数组 ans 作为答案, ans[j] 应等于第 j 次查询 queries[j] 的结果,1 表示照亮,0 表示未照亮。

class Solution {
    /**
     * 对角线求法:
     * 截距 x-y x+y
     */
    public int[] gridIllumination(int n, int[][] lamps, int[][] queries) {

        Map<Integer, Integer> x = new HashMap<>();
        Map<Integer, Integer> y = new HashMap<>();
        //x-y
        Map<Integer, Integer> diagonalLineX = new HashMap<>();
        //x+y
        Map<Integer, Integer> diagonalLineY = new HashMap<>();
        Set<Long> pointSet = new HashSet<>();
        int[] res = new int[queries.length];
        //存灯 去重
        for (int[] coordinates : lamps) {
            if (pointSet.contains(hashAlgorithm(coordinates[0], coordinates[1]))) {
                continue;
            } else {
                pointSet.add(hashAlgorithm(coordinates[0], coordinates[1]));
                insert(coordinates[0], x);
                insert(coordinates[1], y);
                insert(coordinates[0] - coordinates[1], diagonalLineX);
                insert(coordinates[0] + coordinates[1], diagonalLineY);
            }
        }
        //去灯
        int[][] Path = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}, {1, -1}, {1, 1}, {-1, -1}, {-1, 1},{0,0}};
        int i = 0;
        for (int querie[] : queries) {
            //判断querie亮的没有
            if (check(querie[0], x)) {
                res[i] = 1;
            }
            if (check(querie[1], y)) {
                res[i] = 1;
            }
            if (check(querie[0] - querie[1], diagonalLineX)) {
                res[i] = 1;
            }
            if (check(querie[0] + querie[1], diagonalLineY)) {
                res[i] = 1;
            }
            i++;
            //遍历路径 找到要去掉的灯
            for (int j = 0; j < Path.length; j++) {
                Integer tmpX = querie[0] + Path[j][0];
                Integer tmpY = querie[1] + Path[j][1];
                Long value = hashAlgorithm(tmpX, tmpY);
                if (pointSet.contains(value)) {
                    pointSet.remove(value);
                    //去点 四个map value -1
                    delete(tmpX,x);
                    delete(tmpY,y);
                    delete(tmpX-tmpY,diagonalLineX);
                    delete(tmpX+tmpY,diagonalLineY);
                }
            }
        }
        return res;
    }

    public Long hashAlgorithm(Integer a, Integer b) {
        return (long) a + ((long) b << 32);
    }
    public void insert(Integer coordinates, Map<Integer, Integer> map) {
        map.put(coordinates, map.getOrDefault(coordinates, 0) + 1);
    }

    public void delete(Integer coordinates, Map<Integer, Integer> map) {
        if (map.get(coordinates) != null) {
            map.put(coordinates, map.get(coordinates) - 1);
        }
        if(map.get(coordinates)<=0){
            map.put(coordinates,null);
        }
    }

    public boolean check(Integer coordinates, Map<Integer, Integer> map) {
        return map.get(coordinates) != null;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值