LeetCode 面试题 01.02. 判定是否互为字符重排 01

本篇博客深入探讨了链表处理的各种高级技巧,包括如何合并零间节点、从链表中移除非零和序列、以及通过前缀和进行特定操作。同时,提供了详细算法实现,如利用哈希表优化查找过程、采用双指针技巧移动零,以及通过递归和迭代方法解决链表问题。
摘要由CSDN通过智能技术生成

599. 两个列表的最小索引总和

添加链接描述
假设 Andy 和 Doris 想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示。

你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅。 如果答案不止一个,则输出所有答案并且不考虑顺序。 你可以假设答案总是存在。

示例 1:
输入: list1 = [“Shogun”, “Tapioca Express”, “Burger King”, “KFC”],list2 = [“Piatti”, “The Grill at Torrey Pines”, “Hungry Hunter Steakhouse”, “Shogun”]
输出: [“Shogun”]
解释: 他们唯一共同喜爱的餐厅是“Shogun”。

class Solution {
    public String[] findRestaurant(String[] list1, String[] list2) {
        Map<String, Integer> map = new HashMap<>();
        for (int i = 0; i < list1.length; i++) {
            map.put(list1[i], i);
        }
        int minIndexSum = Integer.MAX_VALUE;
        List<String> list = new ArrayList<>();
        for (int i = 0; i < list2.length; i++) {
            if(map.containsKey(list2[i])) {
                int j = map.get(list2[i]);
                if(i + j < minIndexSum) {
                    list.clear();
                    list.add(list2[i]);
                    minIndexSum = i + j;
                } else if (i + j == minIndexSum) {
                    list.add(list2[i]);
                }
            }
        }
        return list.toArray(new String[0]);
    }
}

2181. 合并零之间的节点

添加链接描述
给你一个链表的头节点 head ,该链表包含由 0 分隔开的一连串整数。链表的 开端 和 末尾 的节点都满足 Node.val == 0 。

对于每两个相邻的 0 ,请你将它们之间的所有节点合并成一个节点,其值是所有已合并节点的值之和。然后将所有 0 移除,修改后的链表不应该含有任何 0 。

返回修改后链表的头节点 head 。

示例 1:
在这里插入图片描述

输入:head = [0,3,1,0,4,5,2,0]
输出:[4,11]
解释:
上图表示输入的链表。修改后的链表包含:

  • 标记为绿色的节点之和:3 + 1 = 4
  • 标记为红色的节点之和:4 + 5 + 2 = 11
class Solution {
    public ListNode mergeNodes(ListNode head) {
        ListNode newHead = new ListNode(-1);
        ListNode tmp = newHead;

        ListNode cur = head;
        int val = 0;
        while(cur != null) {
            if(cur != head && cur.val == 0) { // 第一个头节点不加入
                tmp.next = new ListNode(val);
                tmp = tmp.next;
                val = 0; // 下一个
            } else {
                val += cur.val;
            }
            cur = cur.next;
        }

        return newHead.next;
    }
}

69. x 的平方根

添加链接描述
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

示例 1:
输入:x = 4
输出:2

class Solution {
    // 二分
    public int mySqrt(int x) {
        if(x == 1) return 1;
        int left = 1;
        int right = x / 2; //
        while(left <= right) {
            int mid = left + (right - left) / 2;
            if(x / mid > mid) { //不用x > mid * mid 防溢出
                left = mid + 1;
            } else if (x / mid < mid) {
                right = mid - 1;
            } else {
                return mid;
            }
        }
        return right; // -1->-1   0->1
    }
    
    // 2、根据平方数的性质——连续n个奇数相加的结果一定是平方数
    public int mySqrt2(int x) {
        if(x < 0) return 0;
        int i = 1;
        int ans = 0;
        while(x >= 0) {
            x -= i;
            i += 2;
            ans++;
        }
        return ans - 1;
    }

    public int mySqrt1(int x) {
        double mul = Math.sqrt(x);
        return (int)Math.floor(mul);
    }
}

917. 仅仅反转字母

添加链接描述
给你一个字符串 s ,根据下述规则反转字符串:

所有非英文字母保留在原有位置。
所有英文字母(小写或大写)位置反转。
返回反转后的 s 。

示例 1:
输入:s = “ab-cd”
输出:“dc-ba”

class Solution {
    public String reverseOnlyLetters(String s) {
        char[] ret = s.toCharArray();
        int left = 0;
        int right = ret.length - 1; // 
        while(left < right) {
            while(left < right && !Character.isLetter(ret[left])) {
                left++;
            }
            while(left < right && !Character.isLetter(ret[right])) {
                right--;
            }
            if(left < right) {
                char tmp = ret[left];
                ret[left] = ret[right];
                ret[right] = tmp;
            }
            left++; //
            right--; //
        }
        return new String(ret);
    }
}

剑指 Offer 54. 二叉搜索树的第k大节点

添加链接描述
给定一棵二叉搜索树,请找出其中第 k 大的节点的值。
示例 1:

输入: root = [3,1,4,null,2], k = 1
3
/
1 4

2
输出: 4

class Solution {
    // 中序遍历 保存的list中是从小到大排序的
    public void inorder(TreeNode root, List<Integer> list) {
        if(root == null) {
            return;
        }
        inorder(root.left, list);
        list.add(root.val);
        inorder(root.right, list);
    }

    public int kthLargest(TreeNode root, int k) {
        List<Integer> list = new ArrayList<>();
        inorder(root, list);
        return list.get(list.size() - k); // 获取
    }
}

496. 下一个更大元素 I

添加链接描述
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

示例 1:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:

  • 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
  • 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
  • 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
class Solution {
    // 单调栈 + map
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        Deque<Integer> stack = new ArrayDeque<>(); // 从小到大
        Map<Integer, Integer> map = new HashMap<>(); // 记录映射关系
        for (int i = nums2.length - 1; i >= 0; i--) { // 从后往前 记录nums2中向左第一个最大值
            int num = nums2[i];
            while(!stack.isEmpty() && num >= stack.peek()) {
                stack.pop(); // 比栈顶大或等于 出栈 判断下一个
            }
            // 空-> 右边无比之大的数 存储-1    不为空 -> 存储
            map.put(num, stack.isEmpty() ? -1 : stack.peek());
            stack.push(num); // 入栈
        }
        int[] ans = new int[nums1.length];
        for (int i = 0; i < nums1.length; i++) {
            ans[i] = map.get(nums1[i]); // 数组元素不重复 通过map找到当前数据对应的第一个最大值
        }
        return ans;
    }
    
    // 暴力算法 O(n*m)
    // 找在nums2中的位置->j  往后找第一个大的k  判断是否<n  若存在加入数组
    public int[] nextGreaterElement1(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int[] ans = new int[m];
        for (int i = 0; i < m; i++) {
            int j = 0;
            while(j < n && nums2[j] != nums1[i]) {
                j++;
            }
            int k = j + 1;
            while(k < n && nums2[k] < nums1[i]) {
                k++;
            }
            ans[i] = k < n ? nums2[k] : -1;
        }
        return ans;
    }
}

1441. 用栈操作构建数组

添加链接描述
给你一个目标数组 target 和一个整数 n。每次迭代,需要从 list = {1,2,3…, n} 中依序读取一个数字。

请使用下述操作来构建目标数组 target :

Push:从 list 中读取一个新元素, 并将其推入数组中。
Pop:删除数组中的最后一个元素。
如果目标数组构建完成,就停止读取更多元素。
题目数据保证目标数组严格递增,并且只包含 1 到 n 之间的数字。

请返回构建目标数组所用的操作序列。

题目数据保证答案是唯一的。

示例 1:
输入:target = [1,3], n = 3
输出:[“Push”,“Push”,“Pop”,“Push”]
解释:
读取 1 并自动推入数组 -> [1]
读取 2 并自动推入数组,然后删除它 -> [1]
读取 3 并自动推入数组 -> [1,3]

class Solution {
    public List<String> buildArray(int[] target, int n) {
        List<String> list = new ArrayList<>();
        int index = 0;
        for(int j = 1; j <= n && index < target.length; j++) {
            if(j == target[index]) { // 一个匹配 到 下一个元素
                list.add("Push");
                index++;
            } else {
                list.add("Push");
                list.add("Pop");
            }
        }
        return list;
    }
}

328. 奇偶链表

添加链接描述
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。

第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。

请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。

你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

class Solution {
    // 分离节点后合并
    public ListNode oddEvenList(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode evenHead = head.next; // 连接单节点的尾
        
        ListNode odd = head; // 单数链表的头节点
        ListNode even = head.next; // 偶数链表的头节点
        while(even != null && even.next != null) {
            odd.next = even.next;
            odd = odd.next;

            even.next = odd.next;
            even = even.next;
        }
        odd.next = evenHead; // 
        return head;
    }
}

2. 两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

class Solution {
    // 正确做法:与67题类似
    // https://leetcode-cn.com/problems/add-binary/
    // 由于输入的两个链表都是逆序存储数字的位数的,因此两个链表中同一位置的数字可以直接相加。
    // 我们同时遍历两个链表,逐位计算它们的和,并与当前位置的进位值相加。
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode newHead = new ListNode(-1);
        ListNode tmp = newHead;

        int carry = 0; // 进位
        while(l1 != null || l2 != null || carry != 0) {
            int val1 = l1 != null ? l1.val : 0;
            int val2 = l2 != null ? l2.val : 0;
            int val = val1 + val2 + carry; // 还需加入的总值
            carry = val / 10; // 如果是10,此次节点val为0,进位1

            tmp.next = new ListNode(val % 10);
            tmp = tmp.next; // 不要忘了往后走

            if(l1 != null) {
                l1 = l1.next;
            }
            if(l2 != null) {
                l2 = l2.next;
            }
        }

        return newHead.next;
    }



    // 修改为Long 还是有超出范围的情况
    public Long getSumOfVal(ListNode cur) {
        StringBuilder sb = new StringBuilder();
        while(cur != null) {
            sb.append(cur.val);
            cur = cur.next;
        }
        String str = sb.reverse().toString();
        return Long.valueOf(str);
    }

    public ListNode addTwoNumbers1(ListNode l1, ListNode l2) {
        Long num1 = getSumOfVal(l1);
        Long num2 = getSumOfVal(l2);
        Long sum = num1 + num2; // 两链表 val 之和
        // 逆置和
        String str = String.valueOf(sum);
        StringBuilder sb = new StringBuilder(str).reverse();

        ListNode newHead = new ListNode(-1);
        ListNode cur = newHead; // 连接每一个节点
        for (int i = 0; i < str.length(); i++) {
            ListNode node = new ListNode(sb.charAt(i) - '0');
            cur.next = node;
            cur = cur.next;
        }
        
        return newHead.next;
    }
}

67. 二进制求和

添加链接描述
给你两个二进制字符串,返回它们的和(用二进制表示)。
输入为 非空 字符串且只包含数字 1 和 0。

示例 1:
输入: a = “11”, b = “1”
输出: “100”

class Solution {
    public String addBinary(String a, String b) {
        int i = a.length() - 1;
        int j = b.length() - 1;
        StringBuilder sb = new StringBuilder();
        int carry = 0;
        while(i >= 0 || j >= 0 || carry != 0) { // 进位不为0
            int sum = carry;
            if(i >= 0) {
                sum += a.charAt(i--) - '0';
            }
            if(j >= 0) {
                sum += b.charAt(j--) - '0';
            }
            sb.append(sum % 2);
            carry = sum / 2;
        }
        /*if() { // 进位 不为0 放个位数
            sb.append(carry);
        }*/
        return sb.reverse().toString();
    }

    // 先将 a 和 b 转化成十进制数,求和后再转化为二进制数
    // 如果字符串超过 33 位,不能转化为 Integer
    // 如果字符串超过 65 位,不能转化为 Long
    // 如果字符串超过 500000001 位,不能转化为 BigInteger
    /*public String addBinary1(String a, String b) {
        return Integer.toBinaryString(
             Integer.parseInt(a, 2) + Integer.parseInt(b, 2)
        );
    }*/
}

1171. 从链表中删去总和值为零的连续节点

给你一个链表的头节点 head,请你编写代码,反复删去链表中由 总和 值为 0 的连续节点组成的序列,直到不存在这样的序列为止。

删除完毕后,请你返回最终结果链表的头节点。

你可以返回任何满足题目要求的答案。

(注意,下面示例中的所有序列,都是对 ListNode 对象序列化的表示。)

示例 1:
输入:head = [1,2,-3,3,1]
输出:[3,1]
提示:答案 [1,2,1] 也是正确的。

class Solution {
    // 前缀和
    public ListNode removeZeroSumSublists(ListNode head) {
        ListNode newHead = new ListNode(0);
        newHead.next = head;

        int sum = 0; // 和
        ListNode cur = newHead;
        Map<Integer, ListNode> map = new HashMap<>();
        // 首次遍历建立 节点处链表和<->节点 哈希表
        // 若同一和出现多次会覆盖,即记录该sum出现的最后一次节点
        while(cur != null) {
            sum += cur.val;
            map.put(sum, cur);
            cur = cur.next;
        }

        // 第二遍遍历 若当前节点处sum在下一处出现了则表明两结点之间所有节点和为0 直接删除区间所有节点
        //(从前面的一个sum处的节点指向后面的另一个sum处的节点的next)
        sum = 0;
        cur = newHead;
        while(cur != null) {
            sum += cur.val;
            cur.next = map.get(sum).next;
            cur = cur.next;
        }

        return newHead.next;
    }
}

717. 1比特与2比特字符

有两种特殊字符:
第一种字符可以用一个比特 0 来表示
第二种字符可以用两个比特(10 或 11)来表示、
给定一个以 0 结尾的二进制数组 bits ,如果最后一个字符必须是一位字符,则返回 true 。

示例 1:
输入: bits = [1, 0, 0]
输出: true
解释: 唯一的编码方式是一个两比特字符和一个一比特字符。
所以最后一个字符是一比特字符。

class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        int start = 0;
        while(start < bits.length - 1) {
            if(bits[start] == 0) {
                start++;
            } else {
                start += 2;
            }
        }
        return start == bits.length - 1;
    }
}

1791. 找出星型图的中心节点

有一个无向的 星型 图,由 n 个编号从 1 到 n 的节点组成。星型图有一个 中心 节点,并且恰有 n - 1 条边将中心节点与其他每个节点连接起来。

给你一个二维整数数组 edges ,其中 edges[i] = [ui, vi] 表示在节点 ui 和 vi 之间存在一条边。请你找出并返回 edges 所表示星型图的中心节点。

class Solution {
    // 顶点数目为 N,中心顶点的度为 N-1
    public int findCenter(int[][] edges) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int[] e : edges) {
            map.put(e[0], map.getOrDefault(e[0], 0) + 1);
            map.put(e[1], map.getOrDefault(e[1], 0) + 1);
        }
        int size = map.size();
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if(entry.getValue() == size - 1) {
                return entry.getKey();
            }
        }
        /*for (int key : map.keySet()) {
            if(map.get(key) == size - 1) { // 根据k找v
                return key;
            }
        }*/
        return -1;
    }

    public int findCenter1(int[][] edges) {
        return edges[0][0] == edges[1][0] || edges[0][0] == edges[1][1] ? edges[0][0] : edges[0][1];
    }
}

1046. 最后一块石头的重量

添加链接描述
有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

示例:
输入:[2,7,4,1,8,1]
输出:1
解释:
先选出 7 和 8,得到 1,所以数组转换为 [2,4,1,1,1],
再选出 2 和 4,得到 2,所以数组转换为 [2,1,1,1],
接着是 2 和 1,得到 1,所以数组转换为 [1,1,1],
最后选出 1 和 1,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。

class Solution {
    // 优先级队列
    public int lastStoneWeight(int[] stones) {
        // 大根堆
        PriorityQueue<Integer> pr = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        for (int n : stones) {
            pr.offer(n);
        }
        // 拿出两个 不相等y-x入队列
        while(pr.size() > 1) {
            int num1 = pr.poll();
            int num2 = pr.poll();
            if(num1 != num2) {
                pr.offer(num1 - num2);
            }
        }
        return pr.isEmpty() ? 0 : pr.poll();
    }

    // 排序
    public int lastStoneWeight1(int[] stones) {
        int n = stones.length;
        if(n <= 1) {
            return stones[0];
        }
        while(stones[n - 2] != 0) {
            Arrays.sort(stones);
            if(stones[n - 1] != stones[n - 2]) {
                stones[n - 1] = stones[n - 1] - stones[n - 2];
                stones[n - 2] = 0;
            } else {
                stones[n - 1] = 0;
                stones[n - 2] = 0;
            }
            Arrays.sort(stones); // stones[n - 2] != 0
        }
        return stones[n - 1];
    }
}

1380. 矩阵中的幸运数

添加链接描述
给你一个 m * n 的矩阵,矩阵中的数字 各不相同 。请你按 任意 顺序返回矩阵中的所有幸运数。

幸运数是指矩阵中满足同时下列两个条件的元素:

在同一行的所有元素中最小
在同一列的所有元素中最大

示例 1:
输入:matrix = [[3,7,8],[9,11,13],[15,16,17]]
输出:[15]
解释:15 是唯一的幸运数,因为它是其所在行中的最小值,也是所在列中的最大值。

class Solution {
    public List<Integer> luckyNumbers1 (int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        int[] minRow = new int[m]; // 第 i 行的最小值
        Arrays.fill(minRow, Integer.MAX_VALUE);
        int[] maxCol = new int[n]; // 第 j 列的最大值
        // 遍历   记录行最小值,列最大值
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                minRow[i] = Math.min(minRow[i], matrix[i][j]);
                maxCol[j] = Math.max(maxCol[j], matrix[i][j]);
            }
        }
        // 再次遍历 同时满足即为答案
        List<Integer> list = new ArrayList<>();
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(matrix[i][j] == minRow[i] && matrix[i][j] == maxCol[j]) {
                    list.add(matrix[i][j]);
                }
            }
        }
        return list;
    }


    // 遍历矩阵,判断 matrix[i][j] 是否是它所在行的最小值和所在列的最大值
    public List<Integer> luckyNumbers (int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        List<Integer> list = new ArrayList<>();
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                boolean isMin = true;
                boolean isMax = true;
                // 判断行最小值
                for (int k = 0; k < n; k++) {
                    if(matrix[i][k] < matrix[i][j]) {
                        isMin = false;
                        break;
                    }
                }
                if(!isMin) {
                    continue;
                }
                // 判断列最大值
                for (int k = 0; k < m; k++) {
                    if(matrix[k][j] > matrix[i][j]) {
                        isMax = false;
                        break;
                    }
                }
                if(isMax) {
                    list.add(matrix[i][j]);
                }
            }
        }
        return list;
    }
}

540. 有序数组中的单一元素

添加链接描述
给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。

请你找出并返回只出现一次的那个数。

你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。

示例 1:
输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2

class Solution {
    // O(logn) 二分查找
    public int singleNonDuplicate2(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = (right - left) / 2 + left;
            if(mid % 2 == 0) { // 偶数
                if(nums[mid] == nums[mid + 1]) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            } else { // 奇数
                if(nums[mid] == nums[mid - 1]) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }
        }
        return nums[right];
    }

    // 用异或来进行统一,因为 偶数异或1 等于 加1,奇数异或1 等于 减1
    public int singleNonDuplicate(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = (right - left) / 2 + left;
            if(nums[mid] == nums[mid ^ 1]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return nums[right];
    }

    // O(n) 每两个遍历
    public int singleNonDuplicate1(int[] nums) {
        for(int i = 0; i < nums.length - 1; i += 2) {
            if(nums[i] != nums[i + 1]) {
                return nums[i];
            }
        }
        return nums[nums.length - 1];
    }
}

1189. “气球” 的最大数量

添加链接描述
给你一个字符串 text,你需要使用 text 中的字母来拼凑尽可能多的单词 “balloon”(气球)。
字符串 text 中的每个字母最多只能被使用一次。请你返回最多可以拼凑出多少个单词 “balloon”。

示例 1:
输入:text = “nlaebolko”
输出:1

class Solution {
    public int maxNumberOfBalloons(String text) {
        // 此单词每个字符次数
        int[] times = new int[5];
        for(int i = 0; i < text.length(); i++) {
            char ch = text.charAt(i);
            if(ch == 'b') {
                times[0]++;
            } else if (ch == 'a') {
                times[1]++;
            } else if (ch == 'l') {
                times[2]++;
            } else if (ch == 'o') {
                times[3]++;
            } else if(ch == 'n'){
                times[4]++;
            }
        }
        // 'l' 'o' 有两次
        times[2] /= 2;
        times[3] /= 2;
        
        int min = times[0];
        for (int i = 0; i < 5; i++) {
            min = Math.min(min, times[i]);
        }
        return min;
    }
}

面试题 01.02. 判定是否互为字符重排

添加链接描述
给定两个字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。

示例 1:
输入: s1 = “abc”, s2 = “bca”
输出: true

class Solution {
    // 3、哈希表
    public boolean CheckPermutation(String s1, String s2) {
        if(s1.length() != s2.length()) {
            return false;
        }
        HashMap<Character, Integer> map = new HashMap<>();
        // 如果相等,抵消为0
        for(int i = 0; i < s1.length(); i++) {
            map.put(s1.charAt(i), map.getOrDefault(s1.charAt(i), 0) + 1);
            map.put(s2.charAt(i), map.getOrDefault(s2.charAt(i), 0) - 1);
        }
        // 判断
        for(HashMap.Entry<Character, Integer> entry : map.entrySet()) {
            if(entry.getValue() != 0) {
                return false;
            }
        }
        return true;
    }


    // 2、数组统计次数
    public boolean CheckPermutation2(String s1, String s2) {
        if(s1.length() != s2.length()) {
            return false;
        }
        // 数组记录两字符串的字符次数、
        int[] cnts1 = cntLetters(s1);
        int[] cnts2 = cntLetters(s2);
        for(int i = 0; i < 26; i++) {
            if(cnts1[i] != cnts2[i]) {
                return false;
            }
        }
        return true;
    }

    public int[] cntLetters(String str) {
        int[] cnts = new int[26];
        for(int i = 0; i < str.length(); i++) {
            cnts[str.charAt(i) - 'a']++;
        }
        return cnts;
    }


    // 1、转字符数组 排序
    public boolean CheckPermutation1(String s1, String s2) {
        if(s1.length() != s2.length()) {
            return false;
        }
        char[] chars1 = s1.toCharArray();
        Arrays.sort(chars1);
        char[] chars2 = s2.toCharArray();
        Arrays.sort(chars2);
        // 字符串比较
        return new String(chars1).equals(new String(chars2));
    }
}

1984. 学生分数的最小差值

添加链接描述
给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数。另给你一个整数 k 。
从数组中选出任意 k 名学生的分数,使这 k 个分数间 最高分 和 最低分 的 差值 达到 最小化 。
返回可能的 最小差值 。

示例 1:
输入:nums = [90], k = 1
输出:0
解释:选出 1 名学生的分数,仅有 1 种方法:

  • [90] 最高分和最低分之间的差值是 90 - 90 = 0
    可能的最小差值是 0
// 滑动窗口最右边的值就是窗口内的最大值,滑动窗口最左边的值就是窗口内的最小值
// 因此,我们要寻找的就是已经排序的数组中,所有大小为 k 的滑动窗口中,最右端数字 - 最左端数字 的最小结果
class Solution {
    public int minimumDifference(int[] nums, int k) {
        Arrays.sort(nums);
        int min = Integer.MAX_VALUE;
        for(int i = 0; i + k - 1 < nums.length; i++) {
            min = Math.min(min, nums[i + k - 1] - nums[i]);
        }
        return min;
    }

    // 滑动窗口:通过两个指针截取固定长度的数组
    public int minimumDifference1(int[] nums, int k) {
        //   先将数组进行排序
        Arrays.sort(nums);
        // 截取一定长度的数组
        int left=0;
        int right=k-1;
        // 创建一个变量保存最小的差值
        int min = Integer.MAX_VALUE;
        while(right < nums.length) {
              min = Math.min(min, nums[right] - nums[left]);
              left++;
              right = left + k - 1;
        }
        return min;
    }
}

1447. 最简分数

添加链接描述
给你一个整数 n ,请你返回所有 0 到 1 之间(不包括 0 和 1)满足分母小于等于 n 的 最简 分数 。分数可以以 任意 顺序返回。

示例 1:
输入:n = 2
输出:[“1/2”]
解释:“1/2” 是唯一一个分母小于等于 2 的最简分数。

class Solution {
    // 更相减损法
    int gcd(int a, int b) {
        while(true) {
            if(a > b) {
                a -= b;
            } else if (a < b) {
                b -= a;
            } else {
                return a;
            }
        }
    }

    // 欧几里得算法
    int gcd1(int a, int b) {
        return b == 0 ? a : gcd(b, a % b);
        // int c = a % b;
        // while(c != 0) {
        //     a = b;
        //     b = c;
        //     c = a % b;
        // }
        // return b;
    }

    public List<String> simplifiedFractions(int n) {
        List<String> list = new ArrayList<>();
        for(int i = 1; i < n; i++) {
            for(int j = i + 1; j <= n; j++) {
                if(gcd(i, j) == 1) {
                    list.add(i + "/" + j);
                }
            }
        }
        return list;
    }
}

2006. 差的绝对值为 K 的数对数目

添加链接描述
给你一个整数数组 nums 和一个整数 k ,请你返回数对 (i, j) 的数目,满足 i < j 且 |nums[i] - nums[j]| == k 。
|x| 的值定义为:
如果 x >= 0 ,那么值为 x 。
如果 x < 0 ,那么值为 -x 。

示例 1:
输入:nums = [1,2,2,1], k = 1
输出:4
解释:差的绝对值为 1 的数对为:

  • [1,2,2,1]
  • [1,2,2,1]
  • [1,2,2,1]
  • [1,2,2,1]
class Solution {
    public int countKDifference(int[] nums, int k) {
        int ans = 0;
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int n : nums) {
            ans += map.getOrDefault(n + k, 0) + map.getOrDefault(n - k, 0);
            map.put(n, map.getOrDefault(n, 0) + 1);
        }
        return ans;
    }

    public int countKDifference1(int[] nums, int k) {
        int ans = 0;
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if(nums[i] - nums[j] == k || nums[j] - nums[i] == k) {
                    ans++;
                }
            }
        }
        return ans;
    }
}

1748. 唯一元素的和

添加链接描述
给你一个整数数组 nums 。数组中唯一元素是那些只出现 恰好一次 的元素。
请你返回 nums 中唯一元素的 和 。

示例 1:
输入:nums = [1,2,3,2]
输出:4
解释:唯一元素为 [1,3] ,和为 4 。

class Solution {
    // 哈希表记录次数
    public int sumOfUnique1(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int n : nums) {
            map.put(n, map.getOrDefault(n, 0) + 1);
        }
        int ans = 0;
        for(HashMap.Entry<Integer, Integer> entry : map.entrySet()) {
            if(entry.getValue() == 1) {
                ans += entry.getKey();
            }
        }
        return ans;
    }

    // 一次遍历  没有出现加起来 出现过就减 同时设为2
    public int sumOfUnique2(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int ans = 0;
        for(int n : nums) {
            if(!map.containsKey(n)) {
                ans += n; // 没有此值
                map.put(n, 1); // 同时加入哈希表
            } else if(map.get(n) == 1) {
                ans -= n; // 出现过一次 表示加过了 需要减掉
                map.put(n, 2); // 同时设为2
            }
        }
        return ans;
    }

    // 数组
    public int sumOfUnique(int[] nums) {
        int[] cnts = new int[101];
        int ans = 0;
        for(int n : nums) {
            if(++cnts[n] == 1) {
                ans += n;
            } else if (cnts[n] == 2) {
                ans -= n;
            }
        }
        return ans;
    }
}

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 是第一个错误的版本。

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int left = 1;
        int right = n;
        while(left < right) {
            int mid = left + ((right - left) >> 1);
            if(isBadVersion(mid)) {
                right = mid;  // 答案在区间 [left, mid] 中
            } else {
                left = mid + 1; // 答案在区间 [mid+1, right] 中
            }
        }
        return left;
    }
}

202. 快乐数

添加链接描述
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

示例 1:
输入:n = 19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1

class Solution {
    // 1、用哈希集合检测循环
    /* 算法分为两部分,我们需要设计和编写代码:
    - 给一个数字 n,它的下一个数字是什么?
    - 按照一系列的数字来判断我们是否进入了一个循环。
       第 1 部分我们按照题目的要求做数位分离,求平方和。
       第 2 部分可以使用哈希集合完成。每次生成链中的下一个数字时,我们都会检查它是否已经在哈希集合中。
        如果它不在哈希集合中,我们应该添加它。
        如果它在哈希集合中,这意味着我们处于一个循环中,因此应该返回 false。*/

    public int getNextNum(int n) {
        int num = 0;
        while(n > 0) {
            num += Math.pow(n % 10, 2);
            n /= 10;
        }
        return num;
    }

    public boolean isHappy1(int n) {
        Set<Integer> set = new HashSet<>();
        while(n != 1 && !set.contains(n)) {
            set.add(n);
            n = getNextNum(n);
        }
        return n == 1;
    }

    // 2、快慢指针法判断是否有环  龟兔赛跑
    public boolean isHappy(int n) {
        int slow = n;
        int fast = getNextNum(n);
        while(fast != 1 && slow != fast) {
            slow = getNextNum(slow);
            fast = getNextNum(getNextNum(fast));
        }
        return fast == 1;
    }
}

1816. 截断句子

添加链接描述
句子 是一个单词列表,列表中的单词之间用单个空格隔开,且不存在前导或尾随空格。每个单词仅由大小写英文字母组成(不含标点符号)。

例如,“Hello World”、“HELLO” 和 “hello world hello world” 都是句子。
给你一个句子 s 和一个整数 k ,请你将 s 截断 ,使截断后的句子仅含 前 k 个单词。返回 截断 s 后得到的句子。

示例 1:
输入:s = “Hello how are you Contestant”, k = 4
输出:“Hello how are you”
解释:
s 中的单词为 [“Hello”, “how” “are”, “you”, “Contestant”]
前 4 个单词为 [“Hello”, “how”, “are”, “you”]
因此,应当返回 “Hello how are you”

class Solution {
    public String truncateSentence(String s, int k) {
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < s.length(); i++) {
            if(s.charAt(i) == ' ') {
                k--;
            }
            if(k == 0) {
                break;
            }

            sb.append(s.charAt(i));
        }
        return sb.toString();
    }
}

1725. 可以形成最大正方形的矩形数目

添加链接描述
给你一个数组 rectangles ,其中 rectangles[i] = [li, wi] 表示第 i 个矩形的长度为 li 、宽度为 wi 。

如果存在 k 同时满足 k <= li 和 k <= wi ,就可以将第 i 个矩形切成边长为 k 的正方形。例如,矩形 [4,6] 可以切成边长最大为 4 的正方形。

设 maxLen 为可以从矩形数组 rectangles 切分得到的 最大正方形 的边长。

请你统计有多少个矩形能够切出边长为 maxLen 的正方形,并返回矩形 数目 。

示例 1:
输入:rectangles = [[5,8],[3,9],[5,12],[16,5]]
输出:3
解释:能从每个矩形中切出的最大正方形边长分别是 [5,3,5,5] 。
最大正方形的边长为 5 ,可以由 3 个矩形切分得到。

class Solution {
    public int countGoodRectangles(int[][] rectangles) {
        int maxLen = 0; // maxLen
        int cnt = 0;
        for(int x[] : rectangles) {
            int min = Math.min(x[0], x[1]); // 每组中的最小边长

            if(min > maxLen) {
                cnt = 1; // 更新最大边长数
            }
            if(min == maxLen) {
                cnt++;
            }
            
            maxLen = Math.max(min, maxLen);
        }
        return cnt;
    }
}

1414. 和为 K 的最少斐波那契数字数目

添加链接描述
给你数字 k ,请你返回和为 k 的斐波那契数字的最少数目,其中,每个斐波那契数字都可以被使用多次。

斐波那契数字定义为:
F1 = 1
F2 = 1
Fn = Fn-1 + Fn-2 , 其中 n > 2 。
数据保证对于给定的 k ,一定能找到可行解。

示例 1:
输入:k = 7
输出:2
解释:斐波那契数字为:1,1,2,3,5,8,13,……
对于 k = 7 ,我们可以得到 2 + 5 = 7 。

class Solution {
    // 获取1-k的数列  从大到小找
    public int findMinFibonacciNumbers(int k) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        int f1 = 1;
        int f2 = 1;
        while (f1 + f2 <= k) {
            int f3 = f1 + f2;
            list.add(f3);
            f1 = f2;
            f2 = f3;
        }

        int ans = 0;
        for (int i = list.size() - 1; i >= 0 && k > 0; i--) {
            int num = list.get(i);
            
            // ans += k / num;
            // k %= num;

            // 斐波那契数字为:1,1,2,3,5,8,13
            // k = 19
            // 输出:3 
            // 解释:对于 k = 19 ,我们可以得到 1 + 5 + 13 = 19
            if(k >= num) {
                k -= num;
                ans++;
            }
        }
        return ans;
    }
}

1688. 比赛中的配对次数

添加链接描述

class Solution {
    // 每一场比赛中,输的队伍无法晋级,且不会再参加后续的比赛。
    // 由于最后只决出一个获胜队伍,因此就有 n-1 个无法晋级的队伍,也就是会有 n−1 场比赛。
    public int numberOfMatches(int n) {
        return n - 1;
    }

    public int numberOfMatches1(int n) {
        int ans = 0;
        while(n > 1) {
            ans += n / 2;
            if(n % 2 != 0) {
                n = n / 2 + 1;
            }else {
                n = n / 2;
            }
        }
        return ans;
    }
}

884. 两句话中的不常见单词

添加链接描述

class Solution {
    public String[] uncommonFromSentences(String s1, String s2) {
        String[] words1 = s1.split(" ");
        String[] words2 = s2.split(" ");
         HashMap<String, Integer> map = new HashMap<>();
        for (int i = 0; i < words1.length; i++) {
            map.put(words1[i], map.getOrDefault(words1[i], 0) + 1);
        }
        for (int i = 0; i < words2.length; i++) {
            map.put(words2[i], map.getOrDefault(words2[i], 0) + 1);
        }

        List<String> list = new ArrayList<>();
        for(Map.Entry<String, Integer> ret : map.entrySet ()) {
            if(ret.getValue() == 1) {
                list.add(ret.getKey());
            }
        }
        return list.toArray(new String[0]);
    }
}

1332. 删除回文子序列

添加链接描述

class Solution {
    // 如果该字符串本身为回文串,此时只需删除1次即可,删除次数为1
    // 如果该字符串本身不是回文串,此时只需删除2次即可,比如可以先删除所有的'a',再删除所有的'b',删除次数为2
    public int removePalindromeSub(String s) {
        int len = s.length();
        for(int i = 0; i < len / 2; i++) {
            if(s.charAt(i) != s.charAt(len - 1 - i)) {
                return 2;
            }
        }
        return 1;
    }
}

1512. 好数对的数目

添加链接描述
给你一个整数数组 nums 。
如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组 好数对 。
返回好数对的数目。

示例 1:
输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始

class Solution {
    // 哈希表  x(x-1)/2
    public int numIdenticalPairs(int[] nums) {
        int cnt = 0;
        Map<Integer, Integer> map = new HashMap<>();
        for (int n : nums) {
            map.put(n, map.getOrDefault(n, 0) + 1);
        }
        for(Map.Entry<Integer, Integer> entry : map.entrySet()) {
            int x = entry.getValue();
            cnt += x * (x - 1) / 2;
        }
        return cnt;
    }

    // 数组下标 相当于排列求和
    // 假设nums = [1,1,1,1]
    // 第一遍   temp是[0,0,0,0]   ans+=0   temp[0]++
    // 第二遍   temp是[1,0,0,0]   ans+=1   temp[0]++
    // 第三遍   temp=[2,0,0,0]    ans+=2   temp[0]++
    // 第四遍   temp=[3,0,0,0]   ans+=3    temp[0]++
    public int numIdenticalPairs2(int[] nums) {
        int cnt = 0;
        int[] ret = new int[101];
        for(int x : nums) {
            cnt += ret[x];
            ret[x]++;
        }
        return cnt;
    }

    // 暴力
    public int numIdenticalPairs1(int[] nums) {
        int cnt = 0;
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if(nums[i] == nums[j]) {
                    cnt++;
                }
            }
        }
        return cnt;
    }
}

617. 合并二叉树

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

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

    // 非递归
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1 == null) return root2;
        if(root2 == null) return root1;
        TreeNode merged = new TreeNode(root1.val + root2.val);
        Queue<TreeNode> queue = new LinkedList<>(); // 合并的队列
        Queue<TreeNode> queue1 = new LinkedList<>();
        Queue<TreeNode> queue2 = new LinkedList<>();
        queue.offer(merged);
        queue1.offer(root1);
        queue2.offer(root2);
        while(!queue2.isEmpty() && !queue2.isEmpty()) {
            TreeNode node = queue.poll(), node1 = queue1.poll(), node2 = queue2.poll();
            TreeNode left1 = node1.left, left2 = node2.left;
            TreeNode right1 = node1.right, right2 = node2.right;

            if(left1 != null || left2 != null) {
                if(left1 != null && left2 != null) {
                    TreeNode left = new TreeNode(left1.val + left2.val);
                    node.left = left;
                    queue.offer(left);
                    queue1.offer(left1);
                    queue2.offer(left2);
                } else if (left1 != null) {
                    node.left = left1;
                } else if (left2 != null) {
                    node.left = left2;
                }
            }

            if(right1 != null || right2 != null) {
                if(right1 != null && right2 != null) {
                    TreeNode right = new TreeNode(right1.val + right2.val);
                    node.right = right;
                    queue.offer(right);
                    queue1.offer(right1);
                    queue2.offer(right2);
                } else if (right1 != null) {
                    node.right = right1;
                } else if (right2 != null) {
                    node.right = right2;
                }
            }
        }

        return merged;
    }

    // 递归
    public TreeNode mergeTrees1(TreeNode root1, TreeNode root2) {
        if(root1 == null) {
            return root2;
        }
        if(root2 == null) {
            return root1;
        }
        TreeNode root = new TreeNode(root1.val + root2.val);
        root.left = mergeTrees(root1.left, root2.left);
        root.right = mergeTrees(root1.right, root2.right);
        return root;
    }
}

844. 比较含退格的字符串

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

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

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

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

class Solution {
    // 指针 逆序遍历
    // 定义 skip 意为需要跳过的字符数量,skip != 0 跳过当前字符
    // 直到两字符串能够各自确定一个字符,然后将这两个字符进行比较
    public static boolean backspaceCompare(String s, String t) {
        int i = s.length() - 1;
        int j = t.length() - 1;
        int skip1 = 0;
        int skip2 = 0;
        while(i >= 0 || j >= 0) {
            while(i >= 0) {
                if(s.charAt(i) == '#') { // 需要跳过的字符+1
                    skip1++;
                    i--;
                } else if(skip1 > 0) { // 跳过
                    skip1--;
                    i--;
                } else {
                    break; // 不需要删的字符 出去比较是否相等
                }
            }
            while(j >= 0) {
                if(t.charAt(j) == '#') {
                    skip2++;
                    j--;
                } else if(skip2 > 0) {
                    skip2--;
                    j--;
                } else {
                    break;
                }
            }
            if(i >= 0 && j >= 0) {
                if(s.charAt(i) != t.charAt(j)) { // 字符不相等
                    return false;
                }
            } else {
                if(i >= 0 || j >= 0) { // 有一个遍历完
                    return false;
                }
            }
            i--;
            j--;
        }
        return true;
    }

    // 重构字符串
    // 退格符和应当被删除的字符都去除 比较两字符串是否相等
    public static boolean backspaceCompare2(String s, String t) {
        return build(s).equals(build(t));
    }

    public static String build(String str) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            if(str.charAt(i) != '#') {
                sb.append(str.charAt(i));
            }  else {
                if(sb.length() > 0) {
                    sb.deleteCharAt(sb.length() - 1); // 删除最后一个下标的元素
                }
            }
        }
        return sb.toString();
    }

    // 栈
    public boolean backspaceCompare1(String s, String t) {
        Stack<Character> stack1 = new Stack<>();
        Stack<Character> stack2 = new Stack<>();
        int len = Math.max(s.length(), t.length());
        for (int i = 0; i < len; i++) {
            if(i < s.length()) {
                if(s.charAt(i) != '#') {
                    stack1.push(s.charAt(i));
                } else if (s.charAt(i) == '#' && !stack1.empty()) { // 退格 栈不为空
                    stack1.pop();
                }
            }
            if(i < t.length()) {
                if(t.charAt(i) != '#') {
                    stack2.push(t.charAt(i));
                } else if (t.charAt(i) == '#' && !stack2.empty()){
                    stack2.pop();
                }
            }
        }
        if(stack1.size() != stack2.size()) {
            return false;
        }
        int size = stack1.size(); // 
        for (int i = 0; i < size; i++) {
            if(stack1.pop() != stack2.pop()) {
                return false;
            }
        }
        return true;
    }
}

933. 最近的请求次数

添加链接描述

class RecentCounter {
    // 二分查找
    List<Integer> list;

    public RecentCounter() {
        list = new ArrayList<>();
    }
    
    public int ping(int t) {
        list.add(t);
        int index = binarySearch(list, t - 3000); // 查找的是 t - 3000
        return list.size() - index;
    }

    private int binarySearch(List<Integer> list, int k) {
        int left = 0;
        int right = list.size() - 1;
        while (left < right) {
            int mid = (right - left) / 2 + left;
            if (list.get(mid) >= k) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }



    // 存3002的时候删除1,因为1不在[2,3002]之间,而100,3001,3002都是在[2,3002]之间,返回siz为3
    // 当前请求时间入队列,最早发生的请求会最先被删除,超过 3000 毫秒的请求删除
    // Queue<Integer> que;

    // public RecentCounter1() {
    //     que = new LinkedList();
    // }

    // public int ping1(int t) {
    //     que.offer(t);
    //     while(que.peek() < t - 3000) {
    //         que.poll();
    //     }
    //     return que.size();
    // }
}

682. 棒球比赛

添加链接描述

你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。

比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:

整数 x - 表示本回合新获得分数 x
“+” - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
“D” - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
“C” - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。
请你返回记录中所有得分的总和。

class Solution {
    public int calPoints(String[] ops) {
        int ans = 0;
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < ops.length; i++) {
            String str = ops[i];
            if(str.equals("+")) {
                int num1 = stack.pop();
                int num2 = stack.peek(); // 前第二个
                stack.push(num1); // 重新加入
                stack.push(num1 + num2);
                ans += stack.peek();

            } else if (str.equals("D")) {
                stack.push(stack.peek() * 2);              
                ans += stack.peek();
                
            } else if (str.equals("C")) {
                int ret = stack.pop();             
                ans -= ret;

            } else {
                // 是数字 -> 转数字 入栈
                stack.push(Integer.parseInt(str));
                ans += stack.peek();
            }
        }
        return ans;
    }
}

1716. 计算力扣银行的钱

添加链接描述

Hercy 想要为购买第一辆车存钱。他 每天 都往力扣银行里存钱。
最开始,他在周一的时候存入 1 块钱。从周二到周日,他每天都比前一天多存入 1 块钱。在接下来每一个周一,他都会比 前一个周一 多存入 1 块钱。
给你 n ,请你返回在第 n 天结束的时候他在力扣银行总共存了多少块钱。

示例 1:
输入:n = 4
输出:10
解释:第 4 天后,总额为 1 + 2 + 3 + 4 = 10 。

class Solution {
    /*每周多 7 块,因此每周的钱之和的序列是一个等差数列,等差数列求和公式求所有完整周
    剩下的天数里,每天存的钱也是一个等差数列,可以用相同的公式进行求和。两者相加*/
    public int totalMoney(int n) {
        // 所有完整周
        int week = n / 7;
        int firstWeek = (1 + 7) * 7 / 2; // 每周多7
        int lastWeek = firstWeek + 7 * (week - 1);
        int weekMoney = (firstWeek + lastWeek) * week / 2;
        // 剩下不足一周
        int day = n % 7;
        int firstDay = 1 + week; // 最后一周周一
        int lastDay = firstDay + day - 1;
        int dayMoney = (firstDay + lastDay) * day / 2;

        return weekMoney + dayMoney;
    }

    public int totalMoney1(int n) {
        int week = 0;
        int day = 1;
        int ans = 0;
        for(int i = 0; i < n; i++) {
            ans += week + day;
            day++; // 每天
            if(day == 8) { // 每一周
                day = 1;
                week++;
            }
        }
        return ans;
    }
}

747. 至少是其他数字两倍的最大数

添加链接描述

class Solution {
    // 最大值 m1,和次大值 m2,如果 m1 >= m2*2,同时记录 m1 下标
    public int dominantIndex(int[] nums) {
        int m1 = -1;
        int m2 = -1;
        int index = -1;
        for (int i = 0; i < nums.length; i++) {
            if(nums[i] > m1) {
                m2 = m1;
                m1 = nums[i];
                index = i;
            } else if(nums[i] > m2) {
                m2 = nums[i];
            }
        }
        return m1 >= m2 * 2 ? index : -1;
    }

    public int dominantIndex1(int[] nums) {
        // 用array找max
        int[] array = new int[nums.length];
        for(int i = 0; i < nums.length; i++) {
            array[i] = nums[i];
        }
        Arrays.sort(array);
        int max = array[array.length - 1];

        int index = 0; // 记录下标
        int flag = 0;
        for (int i = 0; i < nums.length; i++) {            
            if(nums[i] == max) {
                flag++;
                index = i;
            }else if(nums[i] * 2 > max) {
                return -1;
            }
            if(flag > 1) { // 不止一个最大数
                return -1;
            }
        }
        return index;
    }
}

334. 递增的三元子序列

添加链接描述
给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。
如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。

示例 1:
输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意

class Solution {
    public boolean increasingTriplet1(int[] nums) {
        int n = nums.length;
        if(n < 3) {
            return false;
        }
        // leftMin[i] 表示 nums[0] 到 nums[i] 中的最小值,
        int[] leftMin = new int[n];
        leftMin[0] = nums[0];
        for (int i = 1; i < n; i++) {
            leftMin[i] = Math.min(leftMin[i - 1], nums[i]);
        }
        // rightMax[i] 表示 nums[i] 到 nums[n−1] 中的最大值。
        int[] rightMax = new int[n];
        rightMax[n - 1] = nums[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            rightMax[i] = Math.max(rightMax[i + 1], nums[i]);
        }
        // 查找是否存在一个下标 i 满足 leftMin[i−1] < nums[i] < rightMax[i + 1]
        for (int i = 1; i < n - 1; i++) {
            if(leftMin[i - 1] < nums[i] && nums[i] < rightMax[i + 1]) {
                return true;
            }
        }
        return false;
    }

    // 赋初始值的时候,已经满足second > first了,现在找第三个数third
    // (1) 如果third比second大,那就是找到了,直接返回true
    // (2) 如果third比second小,但是比first大,那就把second的值设为third,然后继续遍历找thrid
    // (3) 如果third比first还小,那就把first的值设为third,然后继续遍历找thrid
    // (这样的话first会跑到second的后边,但是不要紧,因为在second的前边,老first还是满足的)
    public static boolean increasingTriplet(int[] nums) {
        int n = nums.length;
        if(n < 3) {
            return false;
        }
        int first = nums[0];
        int second = Integer.MAX_VALUE;
        for (int i = 1; i < n; i++) {
            int num = nums[i];
            if(num > second) {
                return true;
            } else if (num >first) { // 比second小 比first大
                second  = num;
            } else {
                first = num; // 比first小
            }
        }
        return false;
    }
}

1920. 基于排列构建数组

添加链接描述
给你一个 从 0 开始的排列 nums(下标也从 0 开始)。请你构建一个 同样长度 的数组 ans ,其中,对于每个 i(0 <= i < nums.length),都满足 ans[i] = nums[nums[i]] 。返回构建好的数组 ans 。
从 0 开始的排列 nums 是一个由 0 到 nums.length - 1(0 和 nums.length - 1 也包含在内)的不同整数组成的数组。

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

class Solution {
    // 在原数组更改
    public int[] buildArray(int[] nums) {
        // 范围 [0, 999]  用%1000取原值
        // 最终值 原数值
        for(int i = 0; i < nums.length; i++) {
            nums[i] += 1000 * (nums[nums[i]] % 1000);
        }
        for(int i = 0; i < nums.length; i++) {
            nums[i] /= 1000;
        }
        return nums;
    }

    public int[] buildArray1(int[] nums) {
        int[] ans = new int[nums.length];
        for(int i = 0; i < nums.length; i++) {
            ans[i] = nums[nums[i]];
        }
        return ans;
    }
}

在这里插入图片描述

1629. 按键持续时间最长的键

添加链接描述

class Solution {
    public char slowestKey(int[] releaseTimes, String keysPressed) {
        // 字符 时间 都初识为第一个
        char ans = keysPressed.charAt(0);
        int time = releaseTimes[0];
        for(int i = 1; i < releaseTimes.length; i++) {
             int nextTime = releaseTimes[i] - releaseTimes[i - 1]; // 下一个持续时间
             if(time < nextTime) {
                 // 下一个持续时间更长 更新字符与时间
                 ans = keysPressed.charAt(i);
                 time = nextTime;
             }else if(time == nextTime && ans < keysPressed.charAt(i)) {
                 // 时间相同 比较字符大小是否需要更新 c>b
                 ans = keysPressed.charAt(i);
             }
        }
        return ans;
    }
}

89. 格雷编码

添加链接描述

class Solution {
    // 如果二进制码字的第i位和i+1位相同,则对应的格雷码的第i位为0,否则为1
    public List<Integer> grayCode(int n) {
        List<Integer> list = new ArrayList<>();
        for(int i = 0; i < 1 << n; i++) { // 2^n
            list.add((i >> 1) ^ i);
        }
        return list;
    }

    /*
    1位格雷码有两个码字
    (n+1)位格雷码中的前2^n个码字等于n位格雷码的码字,按顺序书写,加前缀0
    (n+1)位格雷码中的后2^n个码字等于n位格雷码的码字,按逆序书写,加前缀1
    n+1位格雷码的集合 = n位格雷码集合(顺序)加前缀0 + n位格雷码集合(逆序)加前缀1
     */
    // n 位的格雷码序列 等于 (n-1) 位的格雷码序列 并上 (n-1) 位的格雷码序列倒序前面加1的结果
    // 3位的格雷码 [000, 001, 011, 010] | [110, 111, 101, 100]
    public List<Integer> grayCode1(int n) {
        List<Integer> list = new ArrayList<>();
        list.add(0);
        if(n < 1) {
            return list;
        }
        int head = 1;
        for (int i = 0; i < n; i++) {
            // 逆序 每一层
            for (int j = list.size() - 1; j >= 0; j--) {
                list.add(head + list.get(j));
            }
            // 每一层 左移一位
            head <<= 1;
        }
        return list;
    }
}

976. 三角形的最大周长

添加链接描述
给定由一些正数(代表长度)组成的数组 A,返回由其中三个长度组成的、面积不为零的三角形的最大周长。
如果不能形成任何面积不为零的三角形,返回 0。

示例 1:
输入:[2,1,2]
输出:5

class Solution {
    // 排序 
    // a + b > c  找 c 前面符合条件的a b最大值
    public int largestPerimeter(int[] nums) {
        Arrays.sort(nums);
        for(int i = nums.length - 1; i >= 2; --i) {
            if(nums[i - 2] + nums[i - 1] > nums[i]) {
                return nums[i - 2] + nums[i - 1] + nums[i];
            }
        }
        return 0;
    }
}

495. 提莫攻击

添加链接描述
当提莫攻击艾希,艾希的中毒状态正好持续 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 {
    // 未中毒状态,加duration,更新中毒结束时间expired,本次中毒结束时间timeSeries[i]+duration
    // 中毒状态,本次中毒结束时间 - 上次中毒时间expired
    public int findPoisonedDuration(int[] timeSeries, int duration) {
        int anx = 0; // 每次中毒时间总和
        int expired = 0; // 中毒结束时间
        for(int i = 0; i < timeSeries.length; i++) {
            if(timeSeries[i] >= expired) { // 未中毒
                anx += duration;
            }else { // 中毒
                anx += timeSeries[i] + duration - expired;
            }
            expired = timeSeries[i] + duration; // 加每次中毒时间
        }
        return anx;
    }
}

剑指 Offer 61. 扑克牌中的顺子

从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
示例 1:
输入: [1,2,3,4,5]
输出: True

class Solution {
    // 排序
    // 相邻判断重复 末位为max 最后一个0后为min
    public boolean isStraight(int[] nums) {
        int joker = 0;
        Arrays.sort(nums);
        for(int i = 0; i < 4; i++) {
            if(nums[i] == 0) { // 大小王的数量
                joker++;
            } else if(nums[i] == nums[i + 1]) { // 有重复
                return false;
            }
        }
        return nums[4] - nums[joker] < 5;
    }

    // 除大小王 没有重复牌
    // max - min < 5
    public boolean isStraight1(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        int max = 0; // 
        int min = 14; // 
        for(int i = 0; i < 5; i++) {
            if(nums[i] == 0) { // 跳过大小王
                continue;
            }
            if(set.contains(nums[i])) { // 有重复
                return false;
            }
            set.add(nums[i]);
            max = Math.max(max, nums[i]); // 最大
            min = Math.min(min, nums[i]); // 最小
        }
        return max - min < 5;
    }
}

1646. 获取生成数组中的最大值

添加链接描述

class Solution {
    public int getMaximumGenerated(int n) {
        if(n < 2) {
            return n;
        }
        int max = 1;
        int[] nums = new int[n + 1];
        // nums[0] = 0;
        nums[1] = 1;
        for (int i = 2; i <= n; i++) {
            nums[i] = nums[i / 2] +i % 2 * nums[i / 2 + 1]; // 奇数判断为0
            /*if(i % 2 == 0) { // 偶数->[i/2]
                nums[i] = nums[i / 2];
            }else { // 奇数->[i/2] + [i/2+1]
                nums[i] = nums[i / 2] + nums[i / 2 + 1];
            }*/
            max = Math.max(max, nums[i]);
        }
        return max;
    }
}

1614. 括号的最大嵌套深度

添加链接描述

class Solution {
    public int getMaximumGenerated(int n) {
        if(n < 2) {
            return n;
        }
        int max = 1;
        int[] nums = new int[n + 1];
        // nums[0] = 0;
        nums[1] = 1;
        for (int i = 2; i <= n; i++) {
            nums[i] = nums[i / 2] +i % 2 * nums[i / 2 + 1]; // 奇数判断为0
            /*if(i % 2 == 0) { // 偶数->[i/2]
                nums[i] = nums[i / 2];
            }else { // 奇数->[i/2] + [i/2+1]
                nums[i] = nums[i / 2] + nums[i / 2 + 1];
            }*/
            max = Math.max(max, nums[i]);
        }
        return max;
    }
}

119. 杨辉三角 II

添加链接描述

class Solution {
    /*
    递推:
    一行一行地计算,计算到需要的那一行,返回
    第一行只有一个数:1
    首尾:都是1。中间的数:获取上一行的两个数相加
    计算完一行加入C,计算到rowIndex,返回C的rowIndex
     */
    public List<Integer> getRow1(int rowIndex) {
        List<List<Integer>> C  = new ArrayList<>();
        for (int i = 0; i <= rowIndex; i++) {
            List<Integer> row = new ArrayList<>();
            for (int j = 0; j <= i; j++) {
                if(j == 0 || j == i) {
                    row.add(1);
                } else {
                    row.add(C.get(i - 1).get(j - 1) + C.get(i - 1).get(j));
                }
            }
            C.add(row);
        }
        return C.get(rowIndex);
    }

    // 只需要上一行 每次更新即可
    public List<Integer> getRow2(int rowIndex) {
        List<Integer> prev = new ArrayList<>();
        for(int i = 0; i <= rowIndex; i++) {
            List<Integer> cur = new ArrayList<>();
            for(int j = 0; j <= i; j++) {
                if(j == 0 || j == i) {
                    cur.add(1);
                } else {
                    cur.add(prev.get(j - 1) + prev.get(j));
                }
            }
            prev = cur;
        }
        return prev;
    }

    // 从后往前加 每次加给j
    public List<Integer> getRow(int rowIndex) {
        List<Integer> row = new ArrayList<>();
        row.add(1);
        for(int i = 1; i <= rowIndex; i++) {
            row.add(0);
            for(int j = i; j > 0; j--) {
                row.set(j, row.get(j - 1) + row.get(j)); // j的位置更新
            }
        }
        return row;
    }
}

在这里插入图片描述

1576. 替换所有的问号

添加链接描述
给你一个仅包含小写英文字母和 ‘?’ 字符的字符串 s,请你将所有的 ‘?’ 转换为若干小写字母,使最终的字符串不包含任何 连续重复 的字符。

示例 1:
输入:s = “?zs”
输出:“azs”
解释:该示例共有 25 种解决方案,从 “azs” 到 “yzs” 都是符合题目要求的。只有 “z” 是无效的修改,因为字符串 “zzs” 中有连续重复的两个 ‘z’ 。

class Solution {
    public String modifyString(String s) {
        char[] array = s.toCharArray();
        int len = array.length;
        for (int i = 0; i < len; i++) {
            // if(!Character.isLetter(array[i])) { // 不是字母
            if(array[i] == '?') {
                for (char ch = 'a'; ch <= 'c'; ch++) { // 用 a b c 判断
                    if((i > 0 && array[i - 1] == ch) || (i < len - 1 && array[i + 1] == ch)) {
                        continue; // 判断前 判断后
                    }
                    array[i] = ch;
                    break;
                }
            }
        }
        return new String(array);
    }
}

1185. 一周中的第几天

添加链接描述
给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。
输入为三个整数:day、month 和 year,分别表示日、月、年。
您返回的结果必须是这几个值中的一个 {“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday”}。

示例 1:
输入:day = 31, month = 8, year = 2019
输出:“Saturday”

class Solution {
    public String dayOfTheWeek(int day, int month, int year) {
        String[] weeks = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
        int[] monthDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        int days = 0;
        for(int i = 1971; i < year; i++) {
            days += (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) ? 366 : 365;
        }
        if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
            monthDays[1] = 29;
        }
        for(int i = 0; i < month - 1; i++) {
            days += monthDays[i];
        }
        days += day;
        return weeks[(days + 3) % 7];
    }

    public String dayOfTheWeek2(int day, int month, int year) {
        String[] weeks = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
        int[] monthDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        // 当前年份之前的天数
        int days = 0;
        for(int i = 1971; i < year; i++) {
            days += isRun(i) ? 366 : 365; // 闰年
        }
        // 当前年为闰年 -> 二月加一天
        if(isRun(year)) {
            monthDays[1] = 29;
        }
        // 当前月份之前的天数
        for(int i = 0; i < month - 1; i++) {
            days += monthDays[i];
        }
        // 当前月份的天数
        days += day;
        return weeks[(days + 3) % 7];
    }

    public static boolean isRun(int year) {
        return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
    }

    // 1970年12月31日->星期四,算出输入的日期距离1970年12月31日有几天,再加上3后对7求余
    /* 求输入的日期距离 19701970 年 1212 月 3131 日的天数,可以分为三部分分别计算后求和:
    (1)输入年份之前的年份的天数贡献;
    (2)输入年份中,输入月份之前的月份的天数贡献;
    (3)输入月份中的天数贡献。
        例如,如果输入是 21002100 年 1212 月 3131 日,即可分为三部分分别计算后求和:
    (1)19711971 年 11 月 11 到 20992099 年 1212 月 3131 日之间所有的天数;
    (2)21002100 年 11 月 11 日到 21002100 年 1111 月 3131 日之间所有的天数;
    (3)21002100 年 1212 月 11 日到 21002100 年 1212 月 3131 日之间所有的天数。*/
    public String dayOfTheWeek1(int day, int month, int year) {
        String[] weeks = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
        int[] monthDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        // 当前年份之前的天数
        int days  = 365 * (year - 1971) + (year - 1969) / 4; // 加闰年
        // 当前月份之前的天数
        for(int i = 0; i < month - 1; i++) {
            days += monthDays[i];
        }
        if((year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) && month >= 3) {
            days += 1;
        }
        // 当前月份的天数
        days += day;
        return weeks[(days + 3) % 7];
    }
}

930. 和相同的二元子数组

添加链接描述
给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。
子数组 是数组的一段连续部分。

示例 1:
输入:nums = [1,0,1,0,1], goal = 2
输出:4
解释:
有 4 个满足题目要求的子数组:[1,0,1]、[1,0,1,0]、[0,1,0,1]、[1,0,1]

class Solution {
    // 滑动窗口
    public int numSubarraysWithSum(int[] nums, int goal) {
        int cnt = 0;
        int len = nums.length;
        int left1 = 0, left2 = 0, right = 0, sum1 = 0, sum2 = 0;
        while(right < len) {
            sum1 += nums[right];
            while(left1 <= right && sum1 > goal) {
                sum1 -= nums[left1];
                left1++;
            }
            sum2 += nums[right];
            while(left2 <= right && sum2 >= goal) {
                sum2 -= nums[left2];
                left2++;
            }
            cnt += left2 - left1;
            right++;
        }
        return cnt;
    }

    // 滑动窗口  本题只有0/1
    public int numSubarraysWithSum4(int[] nums, int goal) {
        int cnt = 0;
        int len = nums.length;
        for(int left = 0; left < len; left++) {
            if(nums[left] > goal) {
                continue;
            }
            int sum = nums[left];
            if(sum == goal) { // 单个数
                cnt++;
            }
            int right = left + 1;
            while(right < len && sum <= goal) {
                sum += nums[right];
                if(sum == goal) {
                    cnt++;
                    if(right + 1 < len && nums[right + 1] != 0) {
                        break; // 下一个为1 大于goal 提前结束本次循环
                    }
                }
                right++; // 
            }
        }
        return cnt;
    }

    // 数组
    public int numSubarraysWithSum3(int[] nums, int goal) {
        int cnt = 0;
        int sum = 0;
        int[] array = new int[nums.length + 1];
        for(int i : nums) {
            array[sum]++;
            sum += i;
            if(sum - goal >= 0) {
                cnt += array[sum - goal];
            }
        }
        return cnt;
    }

    // 哈希表 前缀和
    public int numSubarraysWithSum2(int[] nums, int goal) {
        int cnt = 0;
        int prevsum = 0;
        HashMap<Integer, Integer> map = new HashMap<>();
        /*map.put(0, 1); // 前缀和为0
        for(int num : nums) {
            prevsum += num; // [0,0,0,0,0] 0 -->15
            cnt += map.getOrDefault(prevsum - goal, 0);
            map.put(prevsum, map.getOrDefault(prevsum, 0) + 1);
        }*/
        for(int num : nums) {
            map.put(prevsum, map.getOrDefault(prevsum, 0) + 1);
            prevsum += num;
            cnt += map.getOrDefault(prevsum - goal, 0);
        }
        return cnt;
    }

    // 双循环
    public int numSubarraysWithSum1(int[] nums, int goal) {
        int cnt = 0;
        int n = nums.length;
        for(int i = 0; i < n; i++) {
            int sum = nums[i];
            if(sum == goal) {
                cnt++;
            }
            for(int j = i + 1; j < n; j++) {
                sum += nums[j];
                if(sum == goal) {
                    cnt++;
                }
            }
        }
        return cnt;
    }
}

390. 消除游戏

添加链接描述
列表 arr 由在范围 [1, n] 中的所有整数组成,并按严格递增排序。请你对 arr 应用下述算法:

从左到右,删除第一个数字,然后每隔一个数字删除一个,直到到达列表末尾。
重复上面的步骤,但这次是从右到左。也就是,删除最右侧的数字,然后剩下的数字每隔一个删除一个。
不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
给你整数 n ,返回 arr 最后剩下的数字。

示例 1:
输入:n = 9
输出:6
解释:
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr = [2, 4, 6, 8]
arr = [2, 6]
arr = [6]

class Solution {
    // https://leetcode.com/problems/elimination-game/discuss/87119/JAVA%3A-Easiest-solution-O(logN)-with-explanation
    public int lastRemaining(int n) {
        int head = 1; // n为1时 head就是最后一个数
        int step = 1;
        boolean left = true;
        while(n > 1) {
            // 从左移除(奇数->从右移除)
            if(left || n % 2 == 1) {
                head += step;
            }
            step *= 2; // 步长
            left = !left; // 反向
            n /= 2; // 减少一半
        }
        return head;
    }
}

461. 汉明距离

添加链接描述
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。

给你两个整数 x 和 y,计算并返回它们之间的汉明距离。

class Solution {
    public int hammingDistance(int x, int y) {
        return Integer.bitCount(x ^ y);
    }

    public int hammingDistance3(int x, int y) {
        int cnt = 0;
        int s = x ^ y;
        while(s != 0) {
            s &= s - 1; // 取出最低位的 1
            cnt++;
        }
        return cnt;
    }

    public int hammingDistance2(int x, int y) {
        int cnt = 0;
        int s = x ^ y; // 统计不同位个数
        while(s != 0) {
            cnt += s & 1;
            s >>= 1;
        }
        return cnt;
    }

    public int hammingDistance1(int x, int y) {
        int cnt = 0;
        for(int i = 0; i < 32; i++) {
            cnt += (x & 1) ^ (y & 1); // x y 最低位判断相等
            x >>>= 1;
            y >>>= 1;
        }
        return cnt;
    }
}

405. 数字转换为十六进制数

添加链接描述
示例:
输入:
26
输出:
“1a”

class Solution {
    // 分组 4个8组
    public String toHex(int num) {
        if(num == 0) {
            return "0";
        }
        StringBuilder sb = new StringBuilder();
        
        for(int i = 7; i >= 0; --i) {
            int ret = (num >> (4 * i)) & 15; // 正向转
            if(sb.length() > 0 || ret > 0) { // 零和正整数,可能出现前导零
                char c = ret < 10 ? (char)(ret + '0') : (char)(ret - 10 + 'a');
                sb.append(c);
            }
        }
        return sb.toString();
        /*while(num != 0) {
            int ret = num & 15; // 每4位
            char c = (char)(ret + '0');
            if(ret >= 10) {
                c = (char)(ret - 10 + 'a');
            }
            sb.append(c);
            num >>>= 4; // 无符号右移
        }
        return sb.reverse().toString();*/
    }
    
    // 位运算 %16 /16
    public String toHex1(int num2) {
        if(num2 == 0) {
            return "0";
        }
        long num = num2;
        if(num < 0) {
            num = (long)(Math.pow(2, 32) + num); // 负数 加2^32偏移量
        }
        StringBuilder sb = new StringBuilder();
        
        while(num != 0) {
            long ret = num % 16;
            char c  = (char)(ret + '0');
            if(ret >= 10) {
                c = (char)(ret - 10 + 'a'); // abcdef
            }
            sb.append(c);
            num /=  16;
        }
        /*String letter = "abcdef";
        while(num != 0) {
            int ret = (int)(num % 16);
            if(ret >= 10) {
                sb.append(letter.charAt(ret-10));
            } else {
                sb.append(ret);
            }
            num /= 16;
        }*/
        return sb.reverse().toString();
    }
}

2022. 将一维数组转变成二维数组

添加链接描述

class Solution {
    public int[][] construct2DArray(int[] original, int m, int n) {
        if(original.length != m * n) {
            return new int[0][];
        }
        int[][] ans = new int[m][n];
        for(int i = 0; i < original.length; i += n) {
            System.arraycopy(original, i, ans[i / n], 0, n);
        }

        /*for(int i = 0; i < original.length; i++) {
            ans[i / n][i % n] = original[i];
        }*/

        /*int k = 0;
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                ans[i][j] = original[k++];
            }
        }*/
        return ans;
    }
}

1160. 拼写单词

添加链接描述
给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。
假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),那么我们就认为你掌握了这个单词。
注意:每次拼写(指拼写词汇表中的一个单词)时,chars 中的每个字母都只能用一次。
返回词汇表 words 中你掌握的所有单词的 长度之和。

示例 1:
输入:words = [“cat”,“bt”,“hat”,“tree”], chars = “atach”
输出:6
解释:
可以形成字符串 “cat” 和 “hat”,所以答案是 3 + 3 = 6。

class Solution {
    // 哈希表  统计chars字符个数 与 words每个单词字符个数 小于 加长度
    public int countCharacters(String[] words, String chars) {
        HashMap<Character, Integer> charsCnt = new HashMap<>();
        int len = chars.length();
        for (int i = 0; i < len; i++) { // 1、chars字符次数
            char c = chars.charAt(i);
            charsCnt.put(c, charsCnt.getOrDefault(c, 0) + 1);
        }
        int ans = 0;
        for (String word : words) { // 2、遍历words单词
            HashMap<Character, Integer> wordCnt = new HashMap<>();
            int wordLength = word.length(); // 每个单词长度
            for (int i = 0; i < wordLength; i++) {
                char c = word.charAt(i);
                wordCnt.put(c, wordCnt.getOrDefault(c, 0) + 1); // 统计每个单词的字符次数
            }
            boolean isAns = true;
            for (int i = 0; i < wordLength; i++) {
                char c = word.charAt(i);
                if(charsCnt.getOrDefault(c, 0) < wordCnt.getOrDefault(c, 0)) { // 小于
                    isAns = false;
                    break;
                }
            }
            if(isAns) {
                ans += word.length();
            }
        }
        /*for (int i = 0; i < words.length; i++) { // 2、遍历words单词
            HashMap<Character, Integer> wordCnt = new HashMap<>();
            for (int j = 0; j < words[i].length(); j++) { // 加入每个字符
                char c = words[i].charAt(j);
                wordCnt.put(c, wordCnt.getOrDefault(c, 0) + 1);
            }
            boolean isAns = true;
            for (int j = 0; j < words[i].length(); j++) {
                char c = words[i].charAt(j);
                if(charsCnt.getOrDefault(c, 0) < wordCnt.getOrDefault(c, 0)) { // 字符小于
                    isAns = false;
                    break;
                }
            }
            if(isAns) {
                ans += words[i].length();
            }
        }*/
        return ans;
    }
}

1995. 统计特殊四元组

添加链接描述
给你一个 下标从 0 开始 的整数数组 nums ,返回满足下述条件的 不同 四元组 (a, b, c, d) 的 数目 :
nums[a] + nums[b] + nums[c] == nums[d] ,且
a < b < c < d

示例 1:
输入:nums = [1,2,3,6]
输出:1
解释:满足要求的唯一一个四元组是 (0, 1, 2, 3) 因为 1 + 2 + 3 == 6 。

class Solution {
    // 4、数组
    public int countQuadruplets(int[] nums) {
        int[] cache = new int[201];
        int cnt = 0;
        for (int i = 2; i < nums.length; i++) {
            for (int j = 0; j < i - 1; j++) {
                cache[nums[i - 1] + nums[j]]++;
            }
            for (int j = i + 1; j < nums.length; j++) {
                int diff = nums[j] - nums[i];
                cnt += diff >= 0 ? cache[diff] : 0;
            }
        }
        return cnt;
    }

    // 3、d-c
    // nums[a]+nums[b]=nums[d]−nums[c]
    public int countQuadruplets3(int[] nums) {
        int cnt = 0;
        int n = nums.length;
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int b = n - 3; b >= 1; --b) {
            // nums[d]−nums[c] 出现的次数
            for (int d = b + 2; d < n; d++) {
                map.put(nums[d] - nums[b + 1], map.getOrDefault(nums[d] - nums[b + 1], 0) + 1);
            }
            for (int a = 0; a < b; a++) {
                cnt += map.getOrDefault(nums[a] + nums[b], 0);
            }
        }
        return cnt;
    }
    
    // 2、哈希表
    public int countQuadruplets2(int[] nums) {
        int cnt = 0;
        int n = nums.length;
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = n - 2; i >= 2; --i) {
            map.put(nums[i + 1], map.getOrDefault(nums[i + 1],0) + 1);
            for (int a = 0; a < i; a++) {
                for (int b = a + 1; b < i; b++) {
                    cnt += map.getOrDefault(nums[a] + nums[b] + nums[i], 0);
                }
            }
        }
        return cnt;
    }

    //  1、暴力
    public int countQuadruplets1(int[] nums) {
        int cnt = 0;
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                for (int k = j + 1; k < n; k++) {
                    for (int l = k + 1; l < n; l++) {
                        if(nums[i] + nums[j] + nums[k] == nums[l]) {
                            cnt++;
                        }
                    }
                }
            }
        }
        return cnt;
    }
}

290. 单词规律

添加链接描述
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。

这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。

示例1:

输入: pattern = “abba”, str = “dog cat cat dog”
输出: true

class Solution {
    // 循环取字符串
    public static boolean wordPattern(String pattern, String str) {
        Map<String, Character> str2ch = new HashMap<>(); // 单词
        Map<Character, String> ch2str = new HashMap<>(); // 字符
        int i = 0; // i j 取str字符串
        for (int p = 0; p < pattern.length(); p++) {
            char ch = pattern.charAt(p); // 遍历pattern字符
            if(i >= str.length()) {
                return false;
            }
            int j = i;
            while(j < str.length() && str.charAt(j) != ' ') { // 跳过空白字符
                j++;
            }
            String tmp = str.substring(i, j); // 返回子字符串 [i, j) 取str一个单词
            if(str2ch.containsKey(tmp) && str2ch.get(tmp) != ch) {
                return false;
            }
            if(ch2str.containsKey(ch) && !tmp.equals(ch2str.get(ch))) {
                return false;
            }
            str2ch.put(tmp, ch);
            ch2str.put(ch, tmp);
            i = j + 1;
        }
        return i >= str.length();
    }

    // 哈希表  记录字符对应字符串 字符串对应字符
    public static boolean wordPattern2(String pattern, String str) {
        Map<String, Character> str2ch = new HashMap<>(); // 单词
        Map<Character, String> ch2str = new HashMap<>(); // 字符
        String[] strings = str.split(" "); // 分割
        if(strings.length != pattern.length()) { // 长度不想相等
            return false;
        }
        for (int i = 0; i < pattern.length(); i++) {
            char ch = pattern.charAt(i);
            if(str2ch.containsKey(strings[i]) && str2ch.get(strings[i]) != ch) { // 判断字符串对应的字符
                return false;
            }
            if(ch2str.containsKey(ch) && !strings[i].equals(ch2str.get(ch))) { // 判断字符对应的字符串
                return false;
            }
            str2ch.put(strings[i], ch);
            ch2str.put(ch, strings[i]);
        }
        return true;
    }


    // int i -- > java 自动装箱,转成 Integer
    // put 返回的是一个 Integer
    // 判断对象相等的话,相当于判断的是引用的地址,即使 Integer 包含的值相等,也不会相等

    // 如果key不存在,插入成功,返回null;如果key存在,返回之前对应的value
    public boolean wordPattern1(String pattern, String str) {
        String[] words = str.split(" ");
        if(pattern.length() != words.length) {
            return false;
        }
        Map<Object, Integer> map = new HashMap<>();
        for (Integer i = 0; i < words.length; i++) {
            if(map.put(words[i], i) != map.put(pattern.charAt(i), i)) {
                return false;
            }
        }
        return true;
    }
}

242. 有效的字母异位词

添加链接描述
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true

class Solution {
    // Unicode 哈希表
    public boolean isAnagram(String s, String t) {
        if(s.length() != t.length()) {
            return false;
        }
        HashMap<Character, Integer> map = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
        }
        for (int i = 0; i < t.length(); i++) {
            map.put(t.charAt(i), map.getOrDefault(t.charAt(i), 0) - 1);
            if(map.get(t.charAt(i)) < 0) {
                return false;
            }
        }
        return true;
    }

    // 记录字母次数
    public boolean isAnagram2(String s, String t) {
        if(s.length() != t.length()) {
            return false;
        }
        int[] table = new int[26];
        for (int i = 0; i < s.length(); i++) {
            table[s.charAt(i) - 'a']++;
        }
        for (int i = 0; i < t.length(); i++) {
            table[t.charAt(i) - 'a']--;
            if(table[t.charAt(i) - 'a'] < 0) { // 为负
                return false;
            }
        }
        return true;
    }

    // 转字符数组 排序
    public boolean isAnagram1(String s, String t) {
        if(s.length() != t.length()) {
            return false;
        }
        char[] ret1 = s.toCharArray();
        char[] ret2 = t.toCharArray();
        Arrays.sort(ret1);
        Arrays.sort(ret2);
        return Arrays.equals(ret1, ret2);
    }
}

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 。

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

    // 双指针
    public int[] twoSum3(int[] numbers, int target) {
        for (int i = 0; i < numbers.length; i++) { // 一个
            int left = i + 1;
            int right = numbers.length - 1;
            while(left <= right) {
                int mid = (right - left) / 2 + left;
                if(numbers[mid] == target - numbers[i]) { // 另一个
                    return new int[]{i+1, mid+1};
                }else if (numbers[mid] < target- numbers[i]) {
                    left = mid + 1;
                }else {
                    right = mid - 1;
                }
            }
        }
        return new int[]{-1, -1};
    }

    // 暴力
    public int[] twoSum1(int[] numbers, int target) {
        int ret = 0;
        int[] ans = new int[2];
        for (int i = 0; i < numbers.length; i++) {
            for (int j = i + 1; j < numbers.length; j++) {
                ret = numbers[i] + numbers[j];
                if(ret == target) {
                    ans[0] = i+1;
                    ans[1] = j+1;
                    break;
                }
            }
        }
        return ans;
    }
}

125. 验证回文串

添加链接描述
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true
解释:“amanaplanacanalpanama” 是回文串

class Solution {
    // 原字符串 指针 比较
    public boolean isPalindrome(String s) {
        int left = 0;
        int right = s.length() - 1;
        while(left < right) {
            while(left < right && !Character.isLetterOrDigit(s.charAt(left))) {
                left++;
            }
            while(left < right && !Character.isLetterOrDigit(s.charAt(right))) {
                right--;
            }
            if(left < right) {
                if(Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                    return false;
                }
                left++;
                right--;
            }
        }
        return true;
    }

    // 字符数字拿出 1、函数判断
    public boolean isPalindrome1(String s) {
        StringBuilder ret = new StringBuilder();
        for(int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if(Character.isLetterOrDigit(ch)) { // 同时判断字符数字
                ret.append(Character.toLowerCase(ch));
            }
        }
        StringBuilder retReverse = new StringBuilder(ret).reverse();
        return retReverse.toString().equals(ret.toString());
    }

    // 字符数字拿出 1、双指针判断
    public boolean isPalindrome2(String s) {
        StringBuilder ret = new StringBuilder();
        for(int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if(Character.isLetterOrDigit(ch)) { // 同时判断字符数字
                ret.append(Character.toLowerCase(ch));
            }
        }
        int len = ret.length();
        int left = 0;
        int right = len - 1;
        while(left < right) {
            if(ret.charAt(left++) != ret.charAt(right--)) {
                return false;
            }
        }
        return true;
    }
}

229. 求众数 II

添加链接描述
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

示例 1:
输入:[3,2,3]
输出:[3]

class Solution {
    // 3、摩尔投票算法
    // 找3/n,最多只有两个。相同->加一,不相同->抵消
    public List<Integer> majorityElement(int[] nums) {
        // 初始化两个候选人和机票
        int cand1 = 0;
        int cand2 = 0;
        int vote1 = 0;
        int vote2 = 0;
        //
        for(int num : nums) {
            if(vote1 > 0 && num == cand1) { // 该元素是第一个元素 机票加一
                vote1++;
            } else if (vote2 > 0 && num == cand2) { // 该元素是第二个元素 机票加一
                vote2++;
            } else if (vote1 == 0) { // 第一个元素
                cand1 = num;
                vote1++;
            } else if (vote2 == 0) { // 第二个元素
                cand2 = num;
                vote2++;
            } else { // 三个都不相等 都抵消
                vote1--;
                vote2--;
            }
        }
        // 判断机票是否 > len/3
        int cnt1 = 0 ;
        int cnt2 = 0 ;
        for(int num : nums) {
            if(vote1 > 0 && num == cand1) {
                cnt1++;
            }
            if(vote2 > 0 && num == cand2) {
                cnt2++;
            }
        }
        List<Integer> list = new ArrayList<>();
        if(vote1 > 0 && cnt1 > nums.length / 3) {
            list.add(cand1);
        }
        if(vote2 > 0 && cnt2 > nums.length / 3) {
            list.add(cand2);
        }
        return list;
    }

    // 2、哈希表 记录次数
    public List<Integer> majorityElement2(int[] nums) {
        List<Integer> list = new ArrayList<>();
        HashMap<Integer, Integer> set = new HashMap<>();
        for(int n : nums) {
            set.put(n, set.getOrDefault(n, 0) + 1);
        }
        for (int n : set.keySet()) {
            if(set.get(n) > nums.length / 3) {
                list.add(n);
            }
        }
        return list;
    }

    // 1、循环遍历次数 哈希去重
    public List<Integer> majorityElement1(int[] nums) {
        List<Integer> list = new ArrayList<>();
        HashSet<Integer> set = new HashSet<>();
        if(nums.length <= 2) {
            list.add(nums[0]);
            if(nums.length == 2) {
                if(nums[0] != nums[1]) { // 避免重复
                    list.add(nums[1]);
                }
            }
            return list;
        }
        Arrays.sort(nums);
        int cnt = 1;
        for (int i = 0; i < nums.length - 1; i++) {
            if(nums[i] == nums[i+1]) {
                cnt++;
            } else {
                cnt = 1;
            }
            if(cnt > nums.length / 3) { // >
                if(set.add(nums[i])) {
                    list.add(nums[i]); // 避免重复
                }
                cnt = 1;
            }
        }
        return list;
    }
}

169. 多数元素

添加链接描述
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:
输入:[3,2,3]
输出:3

class Solution {
    // 摩尔投票算法
    // 多数元素的次数>n/2 其余<n/2 相减>=1
    /*
    候选人(cand_num)初始化为nums[0],票数count初始化为1。
	当遇到与cand_num相同的数,则票数count = count + 1,否则票数count = count - 1。
	当票数count为0时,更换候选人,并将票数count重置为1。
	遍历完数组后,cand_num即为最终答案。*/
    public int majorityElement(int[] nums) {
        int cand = nums[0];
        int cnt = 0;
        for(int n : nums) {
            if(cnt == 0) {
                cand = n;
            }
            if(n == cand) {
                cnt++;
            }else {
                cnt--;
            }
        }
        // int cnt = 1;
        // for(int i = 1; i < nums.length; i++) {
        //     if(nums[i] == cand) {
        //         cnt++;
        //    } else if(--cnt == 0) { // 更新
        //         cand = nums[i];
        //         cnt = 1;
        //     }
        // }
        return cand;
    }

    // 排序 后半位置
    public int majorityElement2(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length / 2];
    }

    // 遍历 次数超过len
    public int majorityElement1(int[] nums) {
        Arrays.sort(nums);
        int cnt = 0;
        for(int i = 0; i < nums.length - 1; i++) {
            if(nums[i] == nums[i+1]) {
                cnt++;
            }else {
                cnt = 0;
            }
            if(cnt >= (nums.length / 2)) {
                return nums[i];
            }
        }
        return nums[0];
    }
}

260. 只出现一次的数字 III

添加链接描述
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。

class Solution {
    // 位运算
    public int[] singleNumber(int[] nums) {
        int sum = nums[0];
        for(int i = 1; i < nums.length; i++) {
            sum ^= nums[i];
        }
        // 取最低位 1  防溢出
        int lsb = sum == Integer.MIN_VALUE ? sum : sum & (-sum);
        int type1 = 0;
        int type2 = 0;
        // 为 0 为 1 分成两组
        for(int n : nums) {
            if((lsb & n) != 0) {
                type1 ^= n;
            }else {
                type2 ^= n;
            }
        }
        return new int[]{type1, type2};
    }
    
    // 异或 暴力查找
    public int[] singleNumber1(int[] nums) {
        Arrays.sort(nums);
        int ret = nums[0];
        int len = nums.length;
        for(int i = 1; i < len; i++) {
            ret ^= nums[i];
        }
        for(int i = 0; i < len; i++) {
            for(int j = i + 1; j < len; j++) {
                if((nums[i] ^ nums[j]) == ret) {
                    return new int[]{nums[i], nums[j]};
                }
            }
        }
        return new int[]{-1, -1};
    }
}

136. 只出现一次的数字

添加链接描述
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
在这里插入图片描述

class Solution {
    // list  没有-加入 有-删除
    public int singleNumber(int[] nums) {
        ArrayList<Integer> list = new ArrayList<>();
        for(int n : nums) {
            if(list.contains(n)) {
                list.remove(Integer.valueOf(n)); // 
            }else {
                list.add(n);
            }
        }
        return list.get(0);
    }

    // 哈希 存储次数
    public int singleNumber2(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int n : nums)  { 
            int cnt = map.getOrDefault(n, 0) + 1;
            map.put(n, cnt);
        }
        for(int i : map.keySet()) {
            if(map.get(i) == 1) {
                return i;
            }
        }
        return -1;
    }

    // 按位异或
    public int singleNumber1(int[] nums) {
        int single = nums[0];
        for(int i = 1; i < nums.length; i++) {
            single ^= nums[i];
        }
        return single;
    }
}

58. 最后一个单词的长度

添加链接描述
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中最后一个单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。

示例 1:
输入:s = “Hello World”
输出:5

class Solution {
    // 删除头尾空白字符串
    // 最后一次" "前  lastIndexOf 从后向前找子串
    public int lengthOfLastWord(String s) {
        return s.trim().length() - s.trim().lastIndexOf(" ") - 1;
    }

    // 最后一个单词 到前面的空格长度
    public int lengthOfLastWord1(String s) {
        int index = s.length() - 1;
        while(s.charAt(index) == ' ') {
            index--;
        }
        int wordLength = 0;
        while(index >= 0 &&s.charAt(index) != ' ') {
            wordLength++;
            index--;
        }
        return wordLength;
    }
}

13. 罗马数字转整数

添加链接描述
例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

class Solution {
    public int romanToInt(String s) {
        HashMap<Character, Integer> map = new HashMap<>();
        map.put('I', 1);
        map.put('V', 5);
        map.put('X', 10);
        map.put('L', 50);
        map.put('C', 100);
        map.put('D', 500);
        map.put('M', 1000);
        int ans = 0;
        int n = s.length();
        // 若一个数字右侧的数字比它大,则将该数字的符号取反。
        for(int i = 0; i < n; i++) {
            int val = map.get(s.charAt(i));
            if(i < n - 1 && val < map.get(s.charAt(i + 1))) {
                ans -= val;
            }else {
                ans += val;
            }
        }
        return ans;
    }
}

448. 找到所有数组中消失的数字

添加链接描述
给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。

示例 1:
输入:nums = [4,3,2,7,8,2,3,1]
输出:[5,6]

class Solution {
    // 加 n 找小于n的
    public List<Integer> findDisappearedNumbers(int[] nums) {
        int len = nums.length;
        for (int n : nums) {
            int k = (n - 1) % len;
            nums[k] += len;
        }
        ArrayList<Integer> list = new ArrayList<>();
        for(int i = 0; i < len; i++) {
            if(nums[i] <= len) {
                list.add(i+1);
            }
        }
        return list;
    }

    // 用哈希表检查 1-n 中的每一个数是否出现
    public List<Integer> findDisappearedNumbers1(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for(int n : nums) {
            set.add(n);
        }
        ArrayList<Integer> list = new ArrayList<>();
        for(int i = 0; i < nums.length; i++) {
            if(!set.contains(i+1)) {
                list.add(i+1);
            }
        }
        return list;
    }
}

88. 合并两个有序数组

添加链接描述
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

class Solution {
    // 从后放
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = m - 1;
        int p2 = n - 1;
        int cur = 0;
        int tail = m + n - 1; // 尾巴
        while(p1 >= 0 || p2 >= 0) {
            if(p1 == -1) {
                cur = nums2[p2--];
            } else if (p2 == -1) {
                cur = nums1[p1--];
            } else if (nums1[p1] > nums2[p2]) {
                cur = nums1[p1--];
            } else {
                cur = nums2[p2--];
            }
            nums1[tail--] = cur;
        }
    }

    // 双指针
    public void merge2(int[] nums1, int m, int[] nums2, int n) {
        int p1 = 0;
        int p2 = 0;
        int[] sorted = new int[m+n];
        int i = 0;
        while(p1 < m || p2 < n) {
            if(p1 == m) {
                sorted[i++] = nums2[p2++];
            } else if(p2 == n) {
                sorted[i++] = nums1[p1++];
            } else if(nums1[p1] < nums2[p2]) {
                sorted[i++] = nums1[p1++];
            } else {
                sorted[i++] = nums2[p2++];
            }
        }
        i = 0;
        for(int k : sorted) {
            nums1[i++] = k;
        }
    }

    // 合并 排序
    public void merge1(int[] nums1, int m, int[] nums2, int n) {
        for(int i = 0; i < n; i++) {
            nums1[m + i] = nums2[i];
        }
        Arrays.sort(nums1);
    }
}

80. 删除有序数组中的重复项 II

添加链接描述
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

输入
[0,0,1,1,1,1,2,3,3]
输出
[0,0,1,1,2,3,3]
预期结果
[0,0,1,1,2,3,3]

class Solution {
    public int removeDuplicates(int[] nums) {
        int fast = 2;
        int slow = 2;
        while(fast < nums.length) {
            if(nums[slow-2] != nums[fast]) {
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
}

441. 排列硬币

添加链接描述

你总共有 n 枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i 行必须正好有 i 枚硬币。阶梯的最后一行 可能 是不完整的。
给你一个数字 n ,计算并返回可形成 完整阶梯行 的总行数。

输入:n = 5
输出:2
解释:因为第三行不完整,所以返回 2 。

class Solution {
    public int arrangeCoins(int n) {
        long left = 1;
        long right = n;
        while(left <= right) {
            long mid = left + (right - left) / 2;
            long total = mid * (mid + 1) / 2;
            if(total == n) {
                return (int)mid;
            }
            if(total > n) {
                right = mid - 1;
            }else {
                left = mid + 1;
            }
        }
        return (int)right;
    }
}

434. 字符串中的单词数

添加链接描述

统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。
请注意,你可以假定字符串里不包括任何不可打印的字符。
示例:
输入: “Hello, my name is John”
输出: 5
解释: 这里的单词是指连续的不是空格的字符,所以 “Hello,” 算作 1 个单词。

class Solution {
    // 该下标对应的字符不为空格;
    // 该下标为初始下标或者该下标的前下标对应的字符为空格;
    public int countSegments1(String s) {
        int cnt = 0;
        for(int i = 0; i < s.length(); i++) {
            if((i == 0 || s.charAt(i-1) == ' ') && s.charAt(i) != ' ') {
                cnt++;
            }
        }
        return cnt;
    }

    public int countSegments(String s) {
        int cnt = 0;
        String[] strings = s.split(" ");
        for (int i = 0; i < strings.length; i++) {
            if(!strings[i].equals("")) {
                cnt++;
            }
        }
        return cnt;
    }
}

415. 字符串相加

添加链接描述
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例 1:
输入:num1 = “11”, num2 = “123”
输出:“134”

class Solution {
    public String addStrings(String num1, String num2) {
        int i = num1.length() - 1;
        int j = num2.length() - 1;
        int carry = 0;
        StringBuilder sb = new StringBuilder();
        while(i >= 0 || j >= 0 || carry != 0) {
            int x = i >= 0 ? num1.charAt(i) - '0' : 0;
            int y = j >= 0 ? num2.charAt(j) - '0' : 0;
            int result = x + y + carry;
            sb.append(result % 10);
            carry = result / 10; // 进位
            i--;
            j--;
        }
        sb.reverse(); // 逆置
        return sb.toString();
    }
}

1154. 一年中的第几天

添加链接描述
给你一个字符串 date ,按 YYYY-MM-DD 格式表示一个 现行公元纪年法 日期。请你计算并返回该日期是当年的第几天。
通常情况下,我们认为 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 2 天,依此类推。每个月的天数与现行公元纪年法(格里高利历)一致。

示例 1:
输入:date = “2019-01-09”
输出:9

class Solution {
    public int dayOfYear(String date) {
        String[] strings = date.split("-");
        int year = Integer.parseInt(strings[0]);
        int month = Integer.parseInt(strings[1]);
        int day = Integer.parseInt(strings[2]);
        int[] amount = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        // 闰年 2月变29天
        if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
            amount[1]++;
        }
        int ans = 0;
        for (int i = 0; i < month - 1; i++) {
            ans += amount[i];
        }
        return ans + day;
    }
}

414. 第三大的数

添加链接描述
给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。

示例 1:
输入:[3, 2, 1]
输出:1
解释:第三大的数是 1 。

示例 2:
输入:[1, 2]
输出:2
解释:第三大的数不存在, 所以返回最大的数 2 。

示例 3:
输入:[2, 2, 3, 1]
输出:1
解释:注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。
此例中存在两个值为 2 的数,它们都排第二。在所有不同数字中排第三大的数为 1 。

class Solution {
    // a b c 最大值、次大值和第三大值
    // 若c仍为−2^63,则不存在三个或以上的不同元素,即第三大值不存在,返回a,否则返回c
    public int thirdMax(int[] nums) {
        long a = Long.MIN_VALUE;
        long b = Long.MIN_VALUE;
        long c = Long.MIN_VALUE;
        for (long n : nums) {
            if(n > a) {
                c = b;
                b = a;
                a = n;
            } else if (a > n && n > b) {
                c = b;
                b = n;
            } else if (b > n && n > c) {
                c = n;
            }
        }
        return c == Long.MIN_VALUE ? (int)a : (int)c;
    }

    public int thirdMax2(int[] nums) {
        TreeSet<Integer> set = new TreeSet<>();
        for (int n : nums) {
            set.add(n);
            if(set.size() > 3) { // 超过3个 删除最小
                set.remove(set.first());
            }
        }
        // 有序集合大小为3,最小值就是第三大的数;不足3,返回最大值。
        return set.size() == 3 ? set.first() : set.last();
    }

    // 排序 逆置 找第三
    public int thirdMax1(int[] nums) {
        Arrays.sort(nums);
        reverse(nums);
        for (int i = 0, diff =  1; i < nums.length - 1; i++) {
            if(nums[i] != nums[i+1] && ++diff == 3) {
                return nums[i+1];
            }
        }
        return nums[0];
    }

    public void reverse(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while(left < right) {
            int tmp = nums[left];
            nums[left++] = nums[right];
            nums[right--] = tmp;
        }
    }
}

412. Fizz Buzz

添加链接描述
给你一个整数 n ,找出从 1 到 n 各个整数的 Fizz Buzz 表示,并用字符串数组 answer(下标从 1 开始)返回结果,其中:

answer[i] == “FizzBuzz” 如果 i 同时是 3 和 5 的倍数。
answer[i] == “Fizz” 如果 i 是 3 的倍数。
answer[i] == “Buzz” 如果 i 是 5 的倍数。
answer[i] == i (以字符串形式)如果上述条件全不满足。

示例 1:
输入:n = 3
输出:[“1”,“2”,“Fizz”]

class Solution {
    public List<String> fizzBuzz(int n) {
        List<String> answer = new ArrayList<>();
        for(int i = 1; i <= n; i++) {
            if(i % 15 == 0) {
                answer.add("FizzBuzz");
            }else if(i % 3 == 0) {
                answer.add("Fizz");
            }else if(i % 5 == 0) {
                answer.add("Buzz");
            }else {
                answer.add(i+"");
            }
        }
        return answer;
    }
}

389. 找不同

添加链接描述
给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。

示例 1:
输入:s = “abcd”, t = “abcde”
输出:“e”
解释:‘e’ 是那个被添加的字母。

class Solution {
    public char findTheDifference(String s, String t) {
        int[] cnt = new int[26];
        for(int i = 0; i < s.length(); i++) {
            cnt[s.charAt(i) - 'a']++;
        }
        for(int i = 0; i < t.length(); i++) {
            char ch = t.charAt(i);
            cnt[ch - 'a']--;
            if(cnt[ch - 'a'] < 0) {
                return ch;
            }
        }
        return ' ';    
    }

    // 位运算
    public char findTheDifference2(String s, String t) {
        int ret = 0;
        for(int i = 0; i < s.length(); i++) {
            ret ^= s.charAt(i);
            ret ^= t.charAt(i);
        }
        ret ^= t.charAt(t.length()-1);
        return (char)ret;    
    }

    // 排序 遍历 
    public char findTheDifference1(String s, String t) {
        char[] ss = s.toCharArray();
        char[] tt = t.toCharArray();
        Arrays.sort(ss);
        Arrays.sort(tt);
        for (int i = 0; i < ss.length; i++) {
            if(ss[i] != tt[i]) {
                return tt[i];
            }
        }
        return tt[tt.length-1];
    }
}

387. 字符串中的第一个唯一字符

添加链接描述
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。

示例:
s = “leetcode”
返回 0

class Solution {
    public int firstUniqChar(String s) {
        int[] ret = new int[26];
        for(int i = 0; i < s.length(); i++) {
            ret[s.charAt(i) - 'a']++;
        }
        for(int i = 0; i < s.length(); i++) {
            if(ret[s.charAt(i) - 'a'] == 1) { // 按顺序找 只出现一次的字符
                return i;
            }
        }
        return -1;
    }

    public int firstUniqChar1(String s) {
        HashMap<Character, Integer> map = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            int cnt = map.getOrDefault(s.charAt(i), 0) + 1;
            map.put(s.charAt(i), cnt);
        }
        for (int i = 0; i < s.length(); i++) {
            if(map.get(s.charAt(i)) == 1) { // 找次数为1
                return i;
            }
        }
        return -1;
    }
}

383. 赎金信

添加链接描述

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:
输入:ransomNote = “a”, magazine = “b”
输出:false

class Solution {
    // 字符数
    public boolean canConstruct(String ransomNote, String magazine) {
        if(ransomNote.length() > magazine.length()) {
            return false;
        }
        int[] cnt = new int[26];
        for(char c : magazine.toCharArray()) {
            cnt[c - 'a']++;
        }
        for(char c : ransomNote.toCharArray()) {
            cnt[c - 'a']--;
            if(cnt[c - 'a'] < 0) {
                return false;
            }
        }
        return true;
    }

    // 哈希
    public boolean canConstruct1(String ransomNote, String magazine) {
        if(ransomNote.length() > magazine.length()) { // 不够构成ransomNote
            return false;
        }
        HashMap<Character, Integer> map = new HashMap<>();
        for (int i = 0; i < magazine.length(); i++) { // 记录字符 和次数
            int cnt = map.getOrDefault(magazine.charAt(i), 0) + 1;
            map.put(magazine.charAt(i), cnt);
        }
        for (int i = 0; i < ransomNote.length(); i++) {
            int cnt = 0;
            if(!map.containsKey(ransomNote.charAt(i))) {
                return false; // 找不到字符
            }else {
                cnt = map.get(ransomNote.charAt(i)) - 1;
                map.put(ransomNote.charAt(i), cnt); // 次数减一
            }
            if(cnt == 0) { // 没有次数 移除该字符
                map.remove(ransomNote.charAt(i), 0);
            }
        }
        return true; // 遍历完 都能在magazine里找到
    }
}

374. 猜数字大小

添加链接描述
We are playing the Guess Game. The game is as follows:
I pick a number from 1 to n. You have to guess which number I picked.
Every time you guess wrong, I will tell you whether the number I picked is higher or lower than your guess.

You call a pre-defined API int guess(int num), which returns 3 possible results:
-1: The number I picked is lower than your guess (i.e. pick < num).
1: The number I picked is higher than your guess (i.e. pick > num).
0: The number I picked is equal to your guess (i.e. pick == num).
Return the number that I picked.

Example 1:
Input: n = 10, pick = 6
Output: 6

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        int left = 1;
        int right = n;
        while(left < right) {
            int pick = left + (right - left) / 2; // 防止mid加法溢出
            // The number I picked is higher than your guess ...
            if(guess(pick) > 0) {
                left = pick + 1;
            }else if (guess(pick) < 0) {
                right = pick - 1;
            }else {
                return pick;
            }
        }
        return left;
    }
}

367. 有效的完全平方数

添加链接描述
给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

示例 1:
输入:num = 16
输出:true

class Solution {
    // 二分
    public boolean isPerfectSquare(int num) {
        long left = 0;
        long right = num / 2;
        long mid = 0;
        while(left <= right) {
            mid = (left + right) / 2;
            if(mid * mid == num) {
                return true;
            } else if (mid * mid < num) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left * left == num; // 1
    }

    // 暴力
    public boolean isPerfectSquare2(int num) {
        for(long i = 1; i <= num; i++) {
            long x = i * i;
            if(x == num) {
                return true;
            }else if (x > num) { // i*i > num 
                return false;
            }
        }
        return false;
    }

    // 减
    public boolean isPerfectSquare1(int num) {
        // 平方数 =1+3+5+...+(2∗n−1)
        int i = 1;
        while(num > 0) {
            num -= i;
            i += 2;
        }
        return num == 0;
    }
}

24. 两两交换链表中的节点

添加链接描述

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例:
输入:head = [1,2,3,4]
输出:[2,1,4,3]

class Solution {
    // 迭代
    public ListNode swapPairs(ListNode head) {
        ListNode newHead = new ListNode(-1);
        ListNode tmp = newHead;
        tmp.next = head;
        while(tmp.next != null && tmp.next.next != null) {
            ListNode node1 = tmp.next; // 1
            ListNode node2 = tmp.next.next; // 2
            tmp.next = node2; // newHead: 2
            node1.next = node2.next; // 1-3
            node2.next = node1; // 2-1-3
            tmp = node1; // 下一组
        }
        return newHead.next;
    }

    // 递归
    public ListNode swapPairs1(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode newHead = head.next; // 1 2 3 4  保存2
        head.next = swapPairs(newHead.next); // 交换下一组4-3  把head指向4:1-4
        newHead.next = head; // 2指向1
        return newHead;
    }
}

997. 找到小镇的法官

添加链接描述

在一个小镇里,按从 1 到 n 为 n 个人进行编号。传言称,这些人中有一个是小镇上的秘密法官。

如果小镇的法官真的存在,那么:

  • 小镇的法官不相信任何人。
  • 每个人(除了小镇法官外)都信任小镇的法官。
  • 只有一个人同时满足条件 1 和条件 2 。
    给定数组 trust,该数组由信任对 trust[i] = [a, b] 组成,表示编号为 a 的人信任编号为 b 的人。

如果小镇存在秘密法官并且可以确定他的身份,请返回该法官的编号。否则,返回 -1。

示例 1:
输入:n = 2, trust = [[1,2]]
输出:2

class Solution {
    // 法官:被信任 n-1 && 信任别人 0
    public int findJudge1(int n, int[][] trust) {
        int[][] array = new int[n+1][2];
        for (int[] k : trust) {
            array[k[0]][0]++; // k[0] 信任别人
            array[k[1]][1]++; // k[1] 被信任
        }
        for (int i = 1; i <= n; i++) {
            if(array[i][1] == n - 1 && array[i][0] == 0) {
                return i;
            }
        }
        return -1;
    }

    public int findJudge(int n, int[][] trust) {
        int[] array = new int[n+1];
        for (int[] k : trust) {
            array[k[0]]--; // k[0] 信任别人
            array[k[1]]++; // k[1] 被信任
        }
        for (int i = 1; i <= n; i++) {
            if(array[i] == n - 1) {
                return i;
            }
        }
        return -1;
    }
}

350. 两个数组的交集 II

添加链接描述

给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。

示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]

class Solution {
    // 哈希  数字的个数
    public int[] intersect(int[] nums1, int[] nums2) {
        // 较短数组放到哈希表 较长数组遍历取交集
        if(nums1.length > nums2.length) {
            intersect(nums2, nums1);
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int n : nums1) { // 放nums1 数和个数
            int cnt = map.getOrDefault(n, 0) + 1;
            map.put(n, cnt);
        }
        int[] array = new int[nums1.length];
        int index = 0;
        for (int n : nums2) {
            int cnt = map.getOrDefault(n, 0);
            if(cnt > 0) { // 有重复
                array[index++] = n;
                cnt--; // 减少一次
                if(cnt > 0) {
                    map.put(n, cnt); // 修改key
                }else {
                    map.remove(n); // 次数为0 没有n的交集了
                }
            }
        }
        return Arrays.copyOfRange(array, 0, index);
    }

    // 指针
    public int[] intersect1(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int index = 0; // 要返回的数组的范围
        int index1 = 0; // nums1
        int index2 = 0; // nums2
        int[] array = new int[nums1.length];
        while(index1 < nums1.length && index2 < nums2.length) {
            int x = nums1[index1];
            int y = nums2[index2];
            if(x == y) {
                array[index++] = x;
                index1++;
                index2++;
            } else if (x < y) { // x != y 指向较小数的指针右移
                index1++;
            } else {
                index2++;
            }
        }
        return Arrays.copyOfRange(array, 0, index);
    }
}

349. 两个数组的交集

添加链接描述

给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

class Solution {
    // 排序 双指针
    public int[] intersection(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int index = 0; // 要返回的数组的范围
        int index1 = 0; // nums1
        int index2 = 0; // nums2
        int[] array = new int[nums1.length];
        while(index1 < nums1.length && index2 < nums2.length) { // 一个遍历完 停止
            int x = nums1[index1];
            int y = nums2[index2];
            if(x == y) {
                if(index == 0 || x != array[index - 1]) { // 保证唯一性
                    array[index++] = x;
                }
                index1++;
                index2++;
            } else if (x < y) { // x != y 指向较小数的指针右移
                index1++;
            } else {
                index2++;
            }
        }
        return Arrays.copyOfRange(array, 0, index);
    }

    // 哈希
    public int[] intersection1(int[] nums1, int[] nums2) {
        int[] array = new int[nums1.length];
        HashSet<Integer> set = new HashSet<>(); // 放nums1
        for (int i = 0; i < nums1.length; i++) {
            set.add(nums1[i]);
        }
        int k = 0; // 记录要返回的范围
        for (int i = 0; i < nums2.length; i++) {
            if(set.contains(nums2[i])) { // 有重复 放到array 删除
                array[k++] = nums2[i];
                set.remove(nums2[i]); 
            }
        }
        return Arrays.copyOfRange(array, 0, k);
    }
}

419. 甲板上的战舰

添加链接描述
在这里插入图片描述

class Solution {
    // 左边为空位 board[i][j-1]
    // 上方为空位 board[i-1][j]
    public int countBattleships(char[][] board) {
        int cnt = 0;
        int row = board.length; // 行
        int col = board[0].length; // 列
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if(board[i][j] == 'X') {
                    if(i > 0 && board[i-1][j] == 'X') {
                        continue;
                    }
                    if(j > 0 && board[i][j-1] == 'X') {
                        continue;
                    }
                    cnt++;
                }
            }
        }
        return cnt;
    }

    // 如果是'X' 就设为' ' 判断行列
    public int countBattleships1(char[][] board) {
        int cnt = 0;
        int row = board.length; // 行
        int col = board[0].length; // 列
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if(board[i][j] == 'X') {
                    board[i][j] = ' ';
                    for (int k = i+1; k < row && board[k][j] == 'X'; k++) {
                        board[k][j] = ' ';
                    }
                    for (int k = j+1; k < col && board[i][k] == 'X'; k++) {
                        board[i][k] = ' ';
                    }
                    cnt++;
                }
            }
        }
        return cnt;
    }
}

345. 反转字符串中的元音字母

添加链接描述

给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。
元音字母包括 ‘a’、‘e’、‘i’、‘o’、‘u’,且可能以大小写两种形式出现。

示例 1:
输入:s = “hello”
输出:“holle”

class Solution {
    public String reverseVowels(String s) {
        int l = 0;
        int r = s.length() - 1;
        String str = "aeiouAEIOU";
        char[] newS = s.toCharArray();
        while(l < r) {
            if(str.contains(s.charAt(l)+"")) {
                if(str.contains(s.charAt(r)+"")) {
                    char ch = newS[l];
                    newS[l] = newS[r];
                    newS[r] = ch;
                    ++l;
                    --r;
                }else {
                    r--;
                }
            }else {
                l++;
            }
        }
        return new String(newS);
    }
    /*
    public String reverseVowels1(String s) {
        int l = 0;
        int r = s.length() - 1;
        String str = "aeiouAEIOU";
        char[] newS = s.toCharArray();
        while(l < r) {
            if(str.contains(s.charAt(l)+"") && str.contains(s.charAt(r)+"")) {
                char ch = newS[l];
                newS[l] = newS[r];
                newS[r] = ch;
                ++l;
                --r;
            }
            if(!str.contains(s.charAt(l)+"")) {
                ++l;
            }
            if(!str.contains(s.charAt(r)+"")) {
                --r;
            }
        }
        return new String(newS);
    }
    */
}

268. 丢失的数字

添加链接描述

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

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

class Solution {
    // 数学求和 n(n+1)/2
    public int missingNumber(int[] nums) {
        int n = nums.length;
        int sumN = (1+n)*n/2; // 1-N 的和
        int sumNums = 0; // 数组的和
        for(int i = 0; i < n; ++i) {
            sumNums += nums[i];
        }
        return sumN - sumNums;
    }

    // 哈希
    public int missingNumber3(int[] nums) {
        Arrays.sort(nums); // 排序后判断加入的nums[i]是否是i
        HashSet<Integer> set = new HashSet<>();
        for(int i = 0; i < nums.length; i++) {
            set.add(nums[i]);
            if(!set.contains(i)) {
                return i;
            }
        }
        return nums.length;
    }

    // 排序
    public int missingNumber2(int[] nums) {
        Arrays.sort(nums);
        for(int i = 0; i < nums.length; i++) {
            if(nums[i] != i) {
                return i;
            }
        }
        return nums.length;
    }

    // 按位异或 同为0 异为1
    public int missingNumber1(int[] nums) {
        int len = nums.length; // 长度就是最大值
        int ret = nums[0];
        for (int i = 1; i < len; i++) {
            ret ^= nums[i]; // 数组的每一位
            ret ^= i; // i
        }
        ret ^= len;
        return ret;
    }
}

219. 存在重复元素 II

添加链接描述

给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

示例 1:
输入: nums = [1,2,3,1], k = 3
输出: true

class Solution {
    // 记录下标
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < nums.length; i++) {
            if(map.containsKey(nums[i]) && i - map.get(nums[i]) <= k) {
                return true;
            }
            map.put(nums[i], i);
        }
        return false;
    }

    public boolean containsNearbyDuplicate1(int[] nums, int k) {
        // 哈希表  限定范围 只需要判断有重复直接返回true
        HashSet<Integer> set = new HashSet<>();
        for(int i = 0; i < nums.length; i++) {
            if(set.contains(nums[i])) {
                return true;
            }
            set.add(nums[i]);
            if(set.size() > k) {
                set.remove(nums[i-k]); // 移除最前面的数字
            }
        }
        return false;
    }
}

217. 存在重复元素

添加链接描述

给定一个整数数组,判断是否存在重复元素。
如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

示例 1:
输入: [1,2,3,1]
输出: true

class Solution {
    // 排序 相邻比较相等
    public boolean containsDuplicate(int[] nums) {
        Arrays.sort(nums);
        for(int i = 0; i < nums.length - 1; i++) {
            if(nums[i] == nums[i+1]) {
                return true;
            }
        }
        return false;
    }

    // 哈希
    public boolean containsDuplicate1(int[] nums) {
        HashSet<Integer> map = new HashSet<>();
        for (int n : nums) {
            /*if(map.contains(n)) {
                return true;
            }
            map.add(n);*/

            // 如果 e 已经存在于 set 中,那么 add() 方法就会返回 false
            if(!map.add(n)) {
                return true;
            }
        }
        return false;
    }
}

70. 爬楼梯

添加链接描述

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

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

class Solution {
    public int climbStairs(int n) {
        int n1 = 1;
        int n2 = 1;
        int n3 = 1;
        for(int i = 2; i <= n; i++) {
            n3 = n1 + n2;
            n1 = n2;
            n2 = n3;
        }
        return n3;
        /*if(n <= 2) {
            return n;
        }
        int n1 = 1;
        int n2 = 2;
        int n3 = 0;
        for(int i = 3; i <= n; i++) {
            n3 = n1 + n2;
            n1 = n2;
            n2 = n3;
        }
        return n3;*/
    }
}

1518. 换酒问题

添加链接描述

小区便利店正在促销,用 numExchange 个空酒瓶可以兑换一瓶新酒。你购入
了 numBottles 瓶酒。
如果喝掉了酒瓶中的酒,那么酒瓶就会变成空的。
请你计算 最多 能喝到多少瓶酒。

输入:numBottles = 9, numExchange = 3
输出:13
解释:你可以用 3 个空酒瓶兑换 1 瓶酒。
所以最多能喝到 9 + 3 + 1 = 13 瓶酒。

class Solution {
    public int numWaterBottles(int numBottles, int numExchange) {
        return numBottles + (numBottles-1)/(numExchange-1);
    }

    public int numWaterBottles1(int numBottles, int numExchange) {
        int total = numBottles;
        while(numBottles >= numExchange) {
            total += numBottles / numExchange;
            // 加不够换剩下的
            numBottles = numBottles/numExchange + numBottles%numExchange; 
        }
        return total;
    }
}

258. 各位相加

添加链接描述

给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。

示例:
输入: 38
输出: 2
解释: 各位相加的过程为:3 + 8 = 11, 1 + 1 = 2。 由于 2 是一位数,所以返回 2。

class Solution {
    // O(1)
    public int addDigits(int num) {
        if(num > 0 && num % 9 == 0) { // 防止0
            return 9;
        }
        return num % 9; // 如果是个位数也可以直接输出
    }

    // 循环
    public int addDigits2(int num) {
        while(num >= 10) {
            int sum = 0;
            while(num != 0) {
                sum += num % 10;
                num /= 10;
            }
            num = sum;
        }
        return num;
    }

    // 递归
    public int addDigits1(int num) {
        if(num < 10) {
            return num;
        }
        int sum = 0;
        while(num != 0) {
            sum += num % 10;
            num /= 10;
        }
        return addDigits(sum);
    }
}

292. Nim 游戏

添加链接描述
示例 1:
输入:n = 4
输出:false
解释:如果堆中有 4 块石头,那么你永远不会赢得比赛;
因为无论你拿走 1 块、2 块 还是 3 块石头,最后一块石头总是会被你的朋友拿走。

class Solution {
    public boolean canWinNim(int n) {
        if(n % 4 == 0) {
            return false;
        }
        return true;
    }
}

巴什博奕(Bash Game):

只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个.最后取光者得胜

结论:如果 n % (m + 1) == 0,则先手必败,否则先手必胜

你和你的朋友,两个人一起玩 Nim 游戏:

桌子上有一堆石头。
你们轮流进行自己的回合,你作为先手。
每一回合,轮到的人拿掉 1 - 3 块石头。
拿掉最后一块石头的人就是获胜者。
假设你们每一步都是最优解。请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true;否则,返回 false 。


190. 颠倒二进制位

添加链接描述
颠倒给定的 32 位无符号整数的二进制位。

  • 输入:n = 00000010100101000001111010011100
  • 输出:964176192 (00111001011110000010100101000000)
  • 解释:输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
    因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。
public class Solution {
    // you need treat n as an unsigned value
    public int reverseBits(int n) {
        int num = 0;
        for (int i = 1; i <= 32 && n != 0; i++) {
            // 如果最低位是1 就可以颠倒或给num
            num |= (n & 1) << (32 - i);
            n >>>= 1;
        }
        return num;
    }
}

191. 位1的个数

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

public class Solution {
    // you need to treat n as an unsigned value
    // 每次去一个1
    public int hammingWeight(int n) {
        int cnt = 0;
        while(n != 0) {
            n &= (n - 1);
            cnt++;
        }
        return cnt;
    }

    public int hammingWeight1(int n) {
        int cnt = 0;
        for (int i = 0; i < 32; i++) {
            if((n & (1 << i)) != 0) { // 用1<<i 判断每一位
                cnt++;
            }
        }
        return cnt;
    }
}

231. 2 的幂

添加链接描述

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。
如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。

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

class Solution {
    // 判断是否为最大 22 的幂的约数
    public boolean isPowerOfTwo(int n) {
        return n > 0 && (1 << 30) % n == 0;
    }

    // 一个数 n 是 2 的幂,当且仅当 n 是正整数,并且 n 的二进制表示中仅包含 1 个 1
    // 将 n 的二进制表示中最低位的那个 1 提取出来,再判断剩余的数值是否为 0
    public boolean isPowerOfTwo1(int n) {
        return n > 0 && (n & (n-1)) == 0;
    }
}

263. 丑数

添加链接描述

给你一个整数 n ,请你判断 n 是否为 丑数 。如果是,返回 true ;否则,返回 false 。
丑数 就是只包含质因数 2、3 和/或 5 的正整数。

示例 1:
输入:n = 6
输出:true
解释:6 = 2 × 3

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

283. 移动零

添加链接描述

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

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

在这里插入图片描述

class Solution {
    // 2、双指针
    public void moveZeroes(int[] nums) {
        int left = 0;
        int right = 0;
        while (right < nums.length) {
            if(nums[right] != 0) {
                swap(nums, left, right);
                left++;
            }
            right++;
        }
    }

    public void swap(int[] nums, int left, int right) {
        int tmp = nums[right];
        nums[right] = nums[left];
        nums[left] = tmp;
    }

    // 1、快慢指针
    public void moveZeroes1(int[] nums) {
        int slow = 0;
        for(int fast = 0; fast < nums.length; fast++) {
            if(nums[fast] != 0) {
                nums[slow++] = nums[fast];
            }
        }
        // slow-len都置为0
        while(slow < nums.length) {
            nums[slow++] = 0;
        }
    }
}

面试题 01.01. 判定字符是否唯一

添加链接描述

实现一个算法,确定一个字符串 s 的所有字符是否全都不同。

示例 1:
输入: s = “leetcode”
输出: false

class Solution {
	// ASCII码的字符个数为128个,用两个64位,8字节的long变量,
    // 二进制位 1 表示出现过,0 表示没有出现过
    public boolean isUnique(String astr) {
        long low = 0;
        long high = 0;
        for (char c : astr.toCharArray()) {
            if (c > 64) {
                long index = 1L << (c - 64);
                if ((high & index) != 0) { // 出现过
                    return false;
                }
                high |= index; // 标记为出现
            } else {
                long index = 1L << c;
                if ((low & index) != 0) {
                    return false;
                }
                low |= index;
            }
        }
        return true;
    }
    
    public boolean isUnique2(String astr) {
        int[] array = new int[128];
        for (int i = 0; i < astr.length(); i++) {
            if (array[astr.charAt(i)] != 0) {
                return false;
            }
            array[astr.charAt(i)]++;
        }
        return true;
    }

    public boolean isUnique1(String astr) {
        HashMap<Character, Integer> map = new HashMap();
        for (int i = 0; i < astr.length(); i++) {
            if(map.containsKey(astr.charAt(i))) {
                return false;
            }
            map.put(astr.charAt(i), i);
        }
        return true;
    }
}

面试题 01.02. 判定是否互为字符重排

添加链接描述
给定两个字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。

示例 1:
输入: s1 = “abc”, s2 = “bca”
输出: true

class Solution {
    public boolean CheckPermutation(String s1, String s2) {
        if(s1.length() != s2.length()) {
            return false;
        }
        // 转字符数组 排序
        char[] chars1 = s1.toCharArray();
        Arrays.sort(chars1);
        char[] chars2 = s2.toCharArray();
        Arrays.sort(chars2);
        // 字符串比较
        return new String(chars1).equals(new String(chars2));
    }
}

面试题 01.09. 字符串轮转

添加链接描述
字符串轮转。给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成(比如,waterbottle是erbottlewat旋转后的字符串)。

示例1:
输入:s1 = “waterbottle”, s2 = “erbottlewat”
输出:True

class Solution {
    public boolean isFlipedString(String s1, String s2) {
        // helloworld
        // rldhellowo
        // helloworldhelloworld
        return s1.length() == s2.length() && (s1 + s1).contains(s2);
    }
}

344. 反转字符串

添加链接描述

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

示例 1:
输入:s = [“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]

class Solution {
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length - 1;
        while(left < right) {
            char tmp = s[left];
            s[left++] = s[right];
            s[right--] = tmp;
        }
        System.out.println(Arrays.toString(s));
    }
}

338. 比特位计数

添加链接描述

给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。

示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10

class Solution {
    public int[] countBits(int n) {
        int[] ans = new int[n+1];
        for (int i = 0; i <= n; i++) {
            ans[i] = cntEveryBIt(i);
        }
        return ans;
    }

    public int cntEveryBIt(int n) {
        int cnt = 0;
        while(n > 0) {
            /*if((n & 1) == 1) {
                cnt++;
            }
            n >>= 1;*/
            n &= (n - 1);
            cnt++;
        }
        return cnt;
    }
}

326. 3 的幂

添加链接描述
给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。
整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == 3x

示例 1:
输入:n = 27
输出:true

示例 2:
输入:n = 0
输出:false

class Solution {
    public boolean isPowerOfThree(int n) {
        // 32位有符号整数的范围内,最大的3的幂为3^19 = 1162261467
        // 判断 n 是否是 3^19的约数
        return n > 0 && 1162261467 % n == 0;
    }

    public boolean isPowerOfThree1(int n) {
        while(n > 0 && n % 3 == 0) {
            n /= 3;
        }
        return n == 1;
    }
}

66. 加一

添加链接描述

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

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

class Solution {
    public int[] plusOne(int[] digits) {
        for(int i = digits.length - 1; i >= 0; --i) {
            digits[i] = (digits[i] + 1) % 10;
            if(digits[i] != 0) {
                return digits;
            }
        }
        // 全是9
        digits = new int[digits.length+1];
        digits[0] = 1;
        return digits;
    }
}

35. 搜索插入位置

添加链接描述

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。

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

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

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

28. 实现 strStr()

添加链接描述

实现 strStr() 函数。

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

class Solution {
    public int strStr(String haystack, String needle) {
        if(haystack == null || needle == null) {
            return -1;
        }
        int i = 0;
        int j = 0;
        int lenHay  = haystack.length();
        int lenNee = needle.length();
        while(i < lenHay && j < lenNee) {
            if(haystack.charAt(i) == needle.charAt(j)) {
                i++;
                j++;
            }else {
                i = i - j + 1;
                j = 0;
            }
        }
        if(j >= lenNee) {
            return i-j;
        }
        return -1;
    }
}

27. 移除元素

添加链接描述
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

class Solution {
    public int removeElement(int[] nums, int val) {
        int fast = 0;
        int slow = 0;
        while(fast < nums.length) {
            if(nums[fast] != val) {
                nums[slow++] = nums[fast];
            }
            fast++;
        }
        return slow;
    }
}

26. 删除有序数组中的重复项

添加链接描述

有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

class Solution {
    // 快慢指针
    public int removeDuplicates(int[] nums) {
        int len = nums.length;
        if(len == 0) {
            return 0;
        }
        int fast = 1;
        int slow = 1;
        while(fast < len) {
            if(nums[fast] != nums[fast-1]) {
                nums[slow++] = nums[fast];
            }
            fast++;
        }
        return slow;
    }

    // 两个循环遍历
    public int removeDuplicates1(int[] nums) {
        // Arrays.sort(nums); 已经是有序数组
        int len = nums.length -1;
        for (int i = 0; i < len; i++) {
            if(nums[i] == nums[i+1]) { // 一样
                // 往前放
                for (int j = i; j < len; j++) {
                    nums[j] = nums[j+1];
                }
                i--;
                len--;
            }
        }
        return len+1;
    }
}

709. 转换成小写字母

添加链接描述

给你一个字符串 s ,将该字符串中的大写字母转换成相同的小写字母,返回新的字符串。

示例 1:
输入:s = “Hello”
输出:“hello”

在这里插入图片描述

Java

class Solution {
    public String toLowerCase1(String s) {
        return s.toLowerCase();
    }

	public String toLowerCase(String s) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if(ch >= 65 && ch <= 90) {
                ch |= 32;
            }
            sb.append(ch);
        }
        return sb.toString();
    }
}

C

char* toLowerCase1(char* s) {
    int i = 0;
    int len = strlen(s);
    for (i = 0; i < len; i++)
    {
        s[i] = tolower(s[i]);
    }
    return s;
}

char* toLowerCase(char* s) {
    int i = 0;
    int len = strlen(s);
    for (i = 0; i < len; i++)
    {
        if (s[i] >= 65 && s[i] <= 90) {
            s[i] |= 32;
        }
    }
    return s;
}

9. 回文数

添加链接描述

class Solution {
    public boolean isPalindrome(int x) {
        if(x < 0) {
            return false;
        }
        int tmp = x;
        int res = 0; // 逆转的x
        while(tmp != 0) {
            res = res*10 + tmp % 10;
            tmp /= 10;
        }
        if(x == res) {
            return true;
        }
        return false;
    }
    
    public boolean isPalindrome1(int x) {
        StringBuilder sb = new StringBuilder(x+"").reverse();
        if(sb.toString().equals(x+"")) {
            return true;
        }
        return false;
    }
}

7. 整数反转

添加链接描述

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。

示例 1:
输入:x = 123
输出:321

class Solution {
    public int reverse(int x) {
        int res = 0;
        while(x != 0) {
            // 判断未来 下一次的范围 否则x==0 直接返回超范围的res
            if(res < Integer.MIN_VALUE/10 || res > Integer.MAX_VALUE/10) {
                return 0;
            }
            res = res*10 + x%10;
            x /= 10;
        }
        return res;
    }
}

1. 两数之和

添加链接描述

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

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

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

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

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

    public int[] twoSum2(int[] nums, int target) {
        int len = nums.length;
        for (int i = 0; i < len; i++) {
            for (int j = i + 1; j < len; j++) {
                if(nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[0];
    }

    public int[] twoSum1(int[] nums, int target) {
        int[] array = new int[2];
        for (int i = 0; i < nums.length; i++) {
            for (int j = i; j < nums.length; j++) {
                if(i == j) continue;
                if(nums[i] + nums[j] == target) {
                    array[0] = i;
                    array[1] = j;
                }
            }
        }
        return array;
    }

    /* public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[] nums = new int[4];
        for (int i = 0; i < nums.length; i++) {
            nums[i] = scanner.nextInt();
        }
        int target = scanner.nextInt();
        int[] ret = twoSum(nums, target);
        System.out.println(Arrays.toString(ret));
    } */
}

189. 轮转数组

添加链接描述
在这里插入图片描述

class Solution {
    // 2、逆置
    public void reverse(int[] nums, int left, int right) {
        while(left < right) {
            int tmp = nums[left];
            nums[left++] = nums[right];
            nums[right--] = tmp;
        }
    }

    public void rotate(int[] nums, int k) {
        k %= nums.length;
        reverse(nums, nums.length-k, nums.length-1); // 后
        reverse(nums, 0, nums.length-k-1); // 前
        reverse(nums, 0, nums.length-1); // 整体
    }

    // 1、超出时间限制
    public void rotate1(int[] nums, int k) {
        k %= nums.length;
        while(k != 0) {
            int tmp = nums[nums.length-1];
            for (int i = nums.length - 2; i >= 0; i--) {
                nums[i+1] = nums[i];
            }
            nums[0] = tmp;
            k--;
        }
    }
}

面试题 17.04. 消失的数字

添加链接描述
在这里插入图片描述

class Solution {
    public int missingNumber(int[] nums) {
        int x = 0;
        for(int i = 0; i < nums.length; i++) {
            x ^= nums[i];
        }
        for(int i = 0; i < nums.length + 1; i++) {
            x ^= i;
        }
        return x;
    }
    
    public int missingNumber1(int[] nums) {
        int sum1 = 0;
        for(int i = 0; i < nums.length; i++) {
            sum1 += nums[i];
        }
        int sum2 = 0;
        for(int i = 0; i < nums.length + 1; i++) {
            sum2 += i;
        }
        return sum2 -sum1;
    }
}

剑指 Offer 18. 删除链表的节点

class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(head == null) {
            return null;
        }
        if(head.val == val) {
            return head.next; // 头节点是
        }
        ListNode prev = head; // 前一个
        ListNode cur = head.next;
        while(cur != null) {
            if(cur.val == val) {
                prev.next = cur.next;
                return head;
            }
            prev = prev.next;
            cur = cur.next;
        }
        return head;
    }
}

面试题 02.02. 返回倒数第 k 个节点

添加链接描述

class Solution {
    public int kthToLast(ListNode head, int k) {
        ListNode fast = head;
        ListNode slow = head;
        while(k != 0) {
            fast = fast.next;
            k--;
        }
        while(fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow.val;
    }
}

面试题 02.01. 移除未排序重复节点

题目链接

class Solution {
    public ListNode removeDuplicateNodes(ListNode head) {
        if(head == null) {
            return null;
        }
        HashSet<Integer> set = new HashSet<>();
        ListNode cur = head;
        // set.add(cur.val); // 
        // while(cur != null && cur.next != null) {
        //     if(set.contains(cur.next.val)) { // 用下一个查找
        //         cur.next = cur.next.next;
        //     } else {
        //         set.add(cur.next.val); // 
        //         cur = cur.next;
        //     }
        // }

        // 双指针
        ListNode pre = null;
        while(cur != null) {
            if(set.contains(cur.val)) {
                pre.next  = cur.next;
            } else {
                set.add(cur.val);
                pre = cur;
            }
            cur = cur.next;
        }
        return head;
    }
}

剑指 Offer 06. 从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

class Solution {
    // 3、栈
    public int[] reversePrint(ListNode head) {
        Stack<Integer> stack = new Stack<>();
        ListNode cur = head;
        while(cur != null) {
            stack.push(cur.val);
            cur = cur.next;
        }
        int i = 0;
        int[] ans =  new int[stack.size()];
        while(!stack.isEmpty()) {
            ans[i++] = stack.pop();
        }
        return ans;
    }

    // 2、反转链表 遍历
    public int[] reversePrint2(ListNode head) {
        ListNode prev = null;
        ListNode cur = head;
        int size = 0; // 计算链表长度
        while(cur != null) {
            ListNode curNext = cur.next;
            cur.next = prev;
            prev = cur;
            cur = curNext;
            size++;
        }
        int[] array = new int[size];
        cur = prev;
        for (int i = 0; i < size; i++) {
            array[i] = cur.val;
            cur = cur.next;
        }
        return array;
    }

    // 1、ArrayList
    public int[] reversePrint1(ListNode head) {
        ArrayList<Integer> list = new ArrayList<>();
        ListNode cur = head;
        while(cur != null) {
            list.add(cur.val);
            cur = cur.next;
        }
        // 获取链表长度
        int size = list.size();
        // 遍历放到数组里
        int[] array = new int[size];
        for (int i = 0; i < size; i++) {
            array[i] = list.get(size-i-1); // 减一
        }
        return array;
    }
}

1290. 二进制链表转整数

题目链接

每到下一个,乘2,就不用计算长度

class Solution {
    public int getDecimalValue(ListNode head) {
        if(head == null) {
            return 0;
        }
        ListNode cur = head;
        int sum = 0;
        while(cur != null) {
            sum = sum * 2 + cur.val;
            cur = cur.next;
        }
        return sum;
    }
}

237. 删除链表中的节点

【题目链接】

class Solution {
    public void deleteNode(ListNode node) {
        // 把node变成它的下一个
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

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

【题目链接】

和牛客删除链表重复节点一样,只是需要保留一个

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode prev = head;
        ListNode cur = head.next;
        while(cur != null) {
            // 相同 删除
            if(cur.next != null && cur.val == cur.next.val) {
                while(cur.next != null && cur.val == cur.next.val) {
                    cur = cur.next;
                }
                prev.next = cur;
                prev = prev.next; // 
                cur = cur.next;
            } else {
                prev = cur;
                cur = cur.next;
            }
        }
        // 如果头节点需要删除
        if(head.val == head.next.val) {
            head = head.next;
        }
        return head;
    }
}

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

【题目链接】

在这里插入图片描述

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(n < 0 || head == null) {
            return null;
        }
        ListNode fast = head.next;
        ListNode slow = head;
        // 找要删的前一个节点
        while(n-1 != 0) {
            fast = fast.next;
            if(fast == null) {
                break;
            }
            n--;
        }
        // 要删的是head
        if(fast == null) {
            head = head.next;
            return head;
        }
        // 要删的是头节点的下一个
        if(fast.next == null) {
            head.next = head.next.next;
            return head;
        }
        //
        while(fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        // 删
        slow.next = slow.next.next;
        return head;
    }
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

三春去后诸芳尽

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

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

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

打赏作者

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

抵扣说明:

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

余额充值