LeetCode刷题笔记(Java)---第441-460题

文章目录

      • 前言
      • 笔记导航
      • 441. 排列硬币
      • 442. 数组中重复的数据
      • 443. 压缩字符串
      • 445. 两数相加 II
      • 446. 等差数列划分 II - 子序列
      • 447. 回旋镖的数量
      • 448. 找到所有数组中消失的数字
      • 449. 序列化和反序列化二叉搜索树
      • 450. 删除二叉搜索树中的节点
      • 451. 根据字符出现频率排序
      • 452. 用最少数量的箭引爆气球
      • 453. 最小移动次数使数组元素相等
      • 454. 四数相加 II
      • 455. 分发饼干
      • 456. 132模式
      • 457. 环形数组循环
      • 458. 可怜的小猪
      • 459. 重复的子字符串

前言

需要开通vip的题目暂时跳过

笔记导航

点击链接可跳转到所有刷题笔记的导航链接

441. 排列硬币

你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。

给定一个数字 n,找出可形成完整阶梯行的总行数。

n 是一个非负整数,并且在32位有符号整型的范围内。

LeetCode刷题笔记(Java)---第441-460题_第1张图片

  • 解答

    public int arrangeCoins(int n) {
            int low = 1;
            int high = n;
            long mid,sum;//使用long类型是为了应对 输入:1804289383 时,计算sum值超出int的取值范围的情况
            while(low<=high)
            {
                mid = low + (high - low )/2;
                sum = mid*(mid+1)/2;
                if(sum == n)
                {
                    return (int)mid;//强制类型转换,将long类型转换为int类型
                }
                else if(n > sum)
                {
                    low = (int)mid + 1;
                }
                else
                {
                    high = (int)mid - 1;
                }
            }
            return high;//return low - 1;是同样的结果。因为最后high
        }
    
  • 分析

    1. long 来存储 为了避免越界
    2. 二分查找 利用等差数列求和公式 来判断二分查找两个指针的移动。
    3. 若当前的结果 正好等于n 那么返回mid
    4. 若大于n high = mid - 1
    5. 若小于n low = mid + 1
  • 提交结果

    LeetCode刷题笔记(Java)---第441-460题_第2张图片

442. 数组中重复的数据

给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。

找到所有出现两次的元素。

你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?

LeetCode刷题笔记(Java)---第441-460题_第3张图片

  • 解答

    //方法一
      public List<Integer> findDuplicates(int[] nums) {
            List<Integer> res = new ArrayList<>();
            if(nums.length == 0)return res;
            Arrays.sort(nums);
            int cur = nums[0];
            for(int i = 1;i < nums.length;i++){
                if(cur == nums[i])res.add(nums[i]);
                cur = nums[i];
            }
            return res;
        }
    //方法二
      public List<Integer> findDuplicates(int[] nums) {
            List<Integer> res = new ArrayList<>();
            int[] sum = new int[nums.length];
            for(int i = 0;i < nums.length;i++){
                sum[nums[i] - 1]++;
                if(sum[nums[i] - 1] == 2)res.add(nums[i]);
            }
            return res;
        }
    //方法三
    public List<Integer> findDuplicates(int[] nums) {
            List<Integer> res = new ArrayList<>();
            for (int i = 0; i < nums.length; ++i) {
                int index = Math.abs(nums[i])-1;
                if (nums[index] < 0)
                    res.add(Math.abs(index+1));
                nums[index] = -nums[index];
            }
            return res;
        }
    
  • 分析

    1. 方法1 排序,相邻重复的数字加入到答案中
    2. 方法2 利用额外空间,因为nums中的数字不会超过数组的长度。所以可以设置一个统计个数的数组。来统计数字出现的次数,出现两次的话 加入到答案中
    3. 方法三 O(n)时间复杂度 且无额外空间。遍历数组,将数字a对应的索引位置的值取反。若那个位置本身就是负数,那么说明a重复出现,加入到答案中。
  • 提交结果

    方法一LeetCode刷题笔记(Java)---第441-460题_第4张图片

    方法二LeetCode刷题笔记(Java)---第441-460题_第5张图片

    方法三LeetCode刷题笔记(Java)---第441-460题_第6张图片

443. 压缩字符串

给定一组字符,使用原地算法将其压缩。

压缩后的长度必须始终小于或等于原数组长度。

数组的每个元素应该是长度为1 的字符(不是 int 整数类型)。

在完成原地修改输入数组后,返回数组的新长度。

进阶:

  • 你能否仅使用O(1) 空间解决问题?LeetCode刷题笔记(Java)---第441-460题_第7张图片

LeetCode刷题笔记(Java)---第441-460题_第8张图片

LeetCode刷题笔记(Java)---第441-460题_第9张图片

  • 解答

    public int compress(char[] chars) {
            char cur = chars[0];
            int curIndex = 0;
            int count = 1;
            int removeCount = 0;
            for (int i = 1; i < chars.length - removeCount; i++) {
                if (chars[i] == cur) {
                    count++;
                } else {
                    if (count >= 2) {
                        char[] counts = String.valueOf(count).toCharArray();
                        for (int j = 0; j < counts.length; j++) {
                            chars[curIndex + 1 + j] = counts[j];
                        }
                        modify(chars, curIndex + counts.length + 1, count - counts.length - 1);
                        removeCount += count - counts.length - 1;
                        i = curIndex + 2;
                    }
                    cur = chars[i];
                    curIndex = i;
                    count = 1;
                }
            }
            if (count >= 2) {
                char[] counts = String.valueOf(count).toCharArray();
                for (int j = 0; j < counts.length; j++) {
                    chars[curIndex + 1 + j] = counts[j];
                }
                return curIndex + 1 + counts.length;
            }
            return curIndex + 1;
        }
    
        public void modify(char[] chars, int index, int step) {
            for (int i = index; i < chars.length - step; i++) {
                chars[i] = chars[i + step];
            }
        }
    
  • 分析

    1. 一次遍历,需要记录移除了多少个字符,当前正在遍历的字符 以及个数,还有当前数字的第一次出现的位置。
    2. for循环的结束条件是 数组长度 减去 移除字符的个数。
    3. 当前遍历的字符等于cur 计数 + 1
    4. 不想等的时候。如果count 大于等于2
    5. 那么将count转换成字符。
    6. 根据count字符的长度 以及需要修改数组的起始位置,来修改数组中的值。
    7. 重置索引i
    8. 更新cur 和curIndex 以及count
    9. for循环结束,若count大于等于2,和上面一样,来修改数组。
    10. 返回curIndex + 1 + counts.length 表示修改后的数组的长度。
    11. 若count < 2 返回curIndex + 1 表示数组的长度。
  • 提交结果LeetCode刷题笔记(Java)---第441-460题_第10张图片

445. 两数相加 II

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

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

进阶:

  • 如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。

LeetCode刷题笔记(Java)---第441-460题_第11张图片

  • 解答

    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            Stack<ListNode> stack1 = new Stack<>();
            Stack<ListNode> stack2 = new Stack<>();
            ListNode p = l1;
            while(p!=null){
                stack1.push(p);
                p = p.next;
            }
            p = l2;
            while(p != null){
                stack2.push(p);
                p = p.next;
            }
            ListNode head = new ListNode(0);
            int flag = 0;
            while(!stack1.isEmpty() || !stack2.isEmpty()){
                ListNode p1 = null;
                if(!stack1.isEmpty()){
                    p1 = stack1.pop();
                }
                ListNode p2 = null;
                if(!stack2.isEmpty()){
                    p2 = stack2.pop();
                }
                int number1 = p1 != null? p1.val:0;
                int number2 = p2 != null? p2.val:0;
                int sum = number1 + number2 + flag;
                if(sum >= 10){
                    sum -= 10;
                    flag = 1;
                }else flag = 0;
                ListNode next = head.next;
                head.next = new ListNode(sum);
                head.next.next = next;
            }
            if(flag == 1){
                ListNode next = head.next;
                head.next = new ListNode(1);
                head.next.next = next;
            }
            return head.next;
        }
    
  • 分析

    1. 利用栈来实现。
    2. while循环,只要一个栈不为空,就继续。
    3. 取出栈顶。若栈顶为空 则数字为0.
    4. 两个数字相加 再加上一位的进位数等到这一位的和。
    5. 若大于10 则需要%10 进位标记为1.
    6. 否则进位标记为0
    7. 结束循环的时候,若进位标记是1 则需要在最前面加上1
  • 提交结果

    LeetCode刷题笔记(Java)---第441-460题_第12张图片

446. 等差数列划分 II - 子序列

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

LeetCode刷题笔记(Java)---第441-460题_第13张图片

数组 A 包含 N 个数,且索引从 0 开始。该数组子序列将划分为整数序列 (P0, P1, …, Pk),满足 0 ≤ P0 < P1 < … < Pk < N。

如果序列 A[P0],A[P1],…,A[Pk-1],A[Pk] 是等差的,那么数组 A 的子序列 (P0,P1,…,PK) 称为等差序列。值得注意的是,这意味着 k ≥ 2。

函数要返回数组 A 中所有等差子序列的个数。

输入包含 N 个整数。每个整数都在 -231 和 231-1 之间,另外 0 ≤ N ≤ 1000。保证输出小于 2的31次方-1。

LeetCode刷题笔记(Java)---第441-460题_第14张图片

  • 解答

    public int numberOfArithmeticSlices(int[] A) {
            int n = A.length;
            long ans = 0;
            Map<Integer, Integer>[] cnt = new Map[n];
            for (int i = 0; i < n; i++) {
                cnt[i] = new HashMap<>(i);
                for (int j = 0; j < i; j++) {
                    long delta = (long)A[i] - (long)A[j];
                    if (delta < Integer.MIN_VALUE || delta > Integer.MAX_VALUE) {
                        continue;
                    }
                    int diff = (int)delta;//公差
                    int sum = cnt[j].getOrDefault(diff, 0);// 以A[j]结束 公差为diff的个数
                    int origin = cnt[i].getOrDefault(diff, 0); // 以A[i]结束 公差为diff的个数
                    cnt[i].put(diff, origin + sum + 1);//更新 以A[i]结束 公差为diff的个数
                    ans += sum;
                }
            }
            return (int)ans;
        }
    
  • 分析
    LeetCode刷题笔记(Java)---第441-460题_第15张图片

  • 提交结果

    LeetCode刷题笔记(Java)---第441-460题_第16张图片

447. 回旋镖的数量

给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。

找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。

LeetCode刷题笔记(Java)---第441-460题_第17张图片

  • 解答

    public int numberOfBoomerangs(int[][] points) {
            Map<Pair<Pair<Integer,Integer>,Integer>,Integer> map = new HashMap<>();
            int res = 0;
            for(int i = 0;i < points.length;i++){
                int[] ori = points[i];
                for(int j = 0;j < points.length;j++){
                    if(i == j)continue;
                    int[] cur = points[j];
                    int dis = (int)(Math.pow(cur[0] - ori[0],2) + Math.pow(cur[1] - ori[1],2));
                    Pair<Pair<Integer,Integer>,Integer> pair = new Pair(new Pair(ori[0],ori[1]),dis);
                    map.put(pair,map.getOrDefault(pair,0) + 1);
                }
            }
            for(Pair<Pair<Integer,Integer>,Integer> pair : map.keySet()){
                int n = map.get(pair);
                if(n >= 2){
                    res += n * (n-1);
                }
            }
            return res;
        }
    
        public int cal(int n){
            return n * (n - 1);
        }
    
  • 分析

    1. 暴力解决
    2. 以一个点为轴,计算其他点到这个点的距离,map的key 表示 以一个点为轴,+ 距离,value表示个数。
    3. 两层循环计算完所有点到其他点的距离,统计出距离的个数。
    4. 遍历map 若某个点为轴 且距离相等的个数大于等于2 表示有回旋镖。
    5. 回旋镖的计算公式是 n * (n-1),n表示轴相同 距离相同的个数。
  • 提交结果

    LeetCode刷题笔记(Java)---第441-460题_第18张图片

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

给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。

找到所有在 [1, n] 范围之间没有出现在数组中的数字。

您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。

LeetCode刷题笔记(Java)---第441-460题_第19张图片

  • 解答

    //方法1
    public List<Integer> findDisappearedNumbers(int[] nums) {
            List<Integer> res = new ArrayList<>();
            res.add(0);
            for(int i = 0;i < nums.length;i++){
                res.add(0);
            }
            for(int i = 0;i < nums.length;i++){
                res.set(nums[i],1);
            }
            for(int i = 1;i <= nums.length;i++){
                if(res.get(i) == 0){
                    res.add(i);
                }
            }
            res = res.subList(nums.length + 1,res.size());
            return res;
        }
    //方法2
    public List<Integer> findDisappearedNumbers(int[] nums) {
            
            for (int i = 0; i < nums.length; i++) {
                int newIndex = Math.abs(nums[i]) - 1;
                if (nums[newIndex] > 0) {
                    nums[newIndex] *= -1;
                }
            }
            
            List<Integer> result = new LinkedList<Integer>();
            
            for (int i = 1; i <= nums.length; i++) {
                
                if (nums[i - 1] > 0) {
                    result.add(i);
                }
            }
            
            return result;
        }
    
  • 分析

    1. 方法1
    2. O(n)的时间复杂度
    3. 第一次遍历,添加n个0到res当中。
    4. 第二次遍历,对应数字的索引位置的值设置为1.
    5. 第三次遍历,找到仍然是0的位置,表示缺少这个数字,添加到res末尾。
    6. 最后截取res的后面新加上的那一段即为答案。
    7. 方法2
    8. 原地修改数组
    9. 第一次遍历,当前数字 - 1 对应的位置的数字取成负数
    10. 第二次遍历,还是正数的位置,表示没有出现过这个数字。则添加到答案中
  • 提交结果

    方法1LeetCode刷题笔记(Java)---第441-460题_第20张图片
    方法2LeetCode刷题笔记(Java)---第441-460题_第21张图片

449. 序列化和反序列化二叉搜索树

序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。

设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。

编码的字符串应尽可能紧凑。

LeetCode刷题笔记(Java)---第441-460题_第22张图片

  • 解答

    public StringBuilder postorder(TreeNode root, StringBuilder sb) {
            if (root == null)
                return sb;
            postorder(root.left, sb);
            postorder(root.right, sb);
            sb.append(root.val);
            sb.append(' ');
            return sb;
        }
    
        // Encodes a tree to a single string.
        public String serialize(TreeNode root) {
            StringBuilder sb = postorder(root, new StringBuilder());
            if (sb.length() > 0)
                sb.deleteCharAt(sb.length() - 1);
            return sb.toString();
        }
    
        public TreeNode helper(Integer lower, Integer upper, ArrayDeque<Integer> nums) {
            if (nums.isEmpty())
                return null;
            int val = nums.getLast();
            if (val < lower || val > upper)
                return null;
    
            nums.removeLast();
            TreeNode root = new TreeNode(val);
            root.right = helper(val, upper, nums);
            root.left = helper(lower, val, nums);
            return root;
        }
    
        // Decodes your encoded data to tree.
        public TreeNode deserialize(String data) {
            if (data.isEmpty())
                return null;
            ArrayDeque<Integer> nums = new ArrayDeque<Integer>();
            for (String s : data.split("\\s+"))
                nums.add(Integer.valueOf(s));
            return helper(Integer.MIN_VALUE, Integer.MAX_VALUE, nums);
        }
    
  • 分析

    1. 因为是一颗二叉搜索树,所以可以根据前序遍历或者后序遍历来还原一颗二叉搜索树。
    2. 选择后序遍历的原因是 还原的过程更加的简单。
  • 提交结果

    LeetCode刷题笔记(Java)---第441-460题_第23张图片

450. 删除二叉搜索树中的节点

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。

说明: 要求算法时间复杂度为 O(h),h 为树的高度。

  • 解答

    public int successor(TreeNode root) {
        root = root.right;
        while (root.left != null) root = root.left;
        return root.val;
      }
    
      public int predecessor(TreeNode root) {
        root = root.left;
        while (root.right != null) root = root.right;
        return root.val;
      }
    
      public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) return null;
    
        if (key > root.val) root.right = deleteNode(root.right, key);
        else if (key < root.val) root.left = deleteNode(root.left, key);
        else {
          if (root.left == null && root.right == null) root = null;
          else if (root.right != null) {
            root.val = successor(root);//当前结点的值替换成右子树中的最小值
            root.right = deleteNode(root.right, root.val);//右子树中 删掉 替换的值的结点。
          }
          else {
            root.val = predecessor(root);//当前结点的值替换成 左子树中的最大值。
            root.left = deleteNode(root.left, root.val);//左子树中 删掉 替换值的结点。
          }
        }
        return root;
      }
    
  • 分析

    1. 递归
    2. 首先通过递归找到要删除的结点。
    3. 若当前结点没有孩子,则直接删除。
    4. 若当前结点有右孩子。则找到右子树中的最小值。该结点替换成右子树中的最小值。
    5. 再将找到的最小值的结点删除。
    6. 若当前结点有左孩子,则找到左子树中的最大值。该结点替换成左子树中的最大值。
    7. 再将找到的最大值的结点删除。
  • 提交结果LeetCode刷题笔记(Java)---第441-460题_第24张图片

451. 根据字符出现频率排序

给定一个字符串,请将字符串里的字符按照出现的频率降序排列。

LeetCode刷题笔记(Java)---第441-460题_第25张图片

LeetCode刷题笔记(Java)---第441-460题_第26张图片

  • 解答

    public String frequencySort(String s) {
            Map<Character,Integer> map = new HashMap<>();
            char[] chars = s.toCharArray();
            for(int i = 0;i < chars.length;i++){
                map.put(chars[i],map.getOrDefault(chars[i],0) + 1);
            }
            PriorityQueue<Pair<Character,Integer>> queue = new PriorityQueue<>(new Comparator<Pair<Character,Integer>>(){
                public int compare(Pair<Character,Integer> p1,Pair<Character,Integer> p2){
                    return p2.getValue() - p1.getValue();
                }
            });
            for(Character ch : map.keySet()){
                queue.add(new Pair(ch,map.get(ch)));
            }
            StringBuilder sb = new StringBuilder();
            while(!queue.isEmpty()){
                Pair<Character,Integer> p = queue.poll();
                char ch = p.getKey();
                for(int i = 0;i < p.getValue();i++){
                    sb.append(ch);
                }
            }
            return sb.toString();
        }
    
  • 分析

    1. 利用hashMap统计字符出现的次数。
    2. 利用优先级队列,存储字符及对应的次数。
    3. while出队,每次出队必定是当前队伍这次数最多的字符。将出队的字符根据它对应的次数 添加到sb当中。
  • 提交结果LeetCode刷题笔记(Java)---第441-460题_第27张图片

452. 用最少数量的箭引爆气球

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。

一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。

LeetCode刷题笔记(Java)---第441-460题_第28张图片

提示:

  • 0 <= points.length <= 104

  • points[i].length == 2

  • -231 <= xstart < xend <= 231 - 1

  • 解答

    public int findMinArrowShots(int[][] points) {
            Arrays.sort(points,new Comparator<int[]>(){
                public int compare(int[] o1,int[] o2){
                    if(o1[0] < o2[0]) return -1;
                    else return 1;
                }
            });
            int res = 0;
            if(points.length == 0)return res;
            int[] temp = points[0];
            for(int i = 1;i < points.length;i++){
                int[] cur = points[i];
                if(cur[0] <= temp[1]){
                    temp[0] = cur[0];
                    temp[1] = Math.min(cur[1],temp[1]);
                }else{
                    res++;
                    temp = cur;
                }
            }
            return res + 1;
        }
    
  • 分析

    1. 根据开始起点的大小升序的排序原始数组。
    2. 遍历区间
    3. 若当前区间的起始位置小于记录区间的结束位置
    4. 则更新记录区间的起始位置为 当前区间的起始位置,更新记录区间的结束位置为 当前区间和记录区间的结束位置的较小者。简单点说就是找到区间重合的部分。记录下来。
    5. 若当前区间和记录区间不重合,说明无法使用一支箭同时射破。res++ 更新记录区间为当前区间。
    6. 最后结果+1,是因为当前记录的区间还没有使用气球射破。
  • 提交结果LeetCode刷题笔记(Java)---第441-460题_第29张图片

453. 最小移动次数使数组元素相等

给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动将会使 n - 1 个元素增加 1。

LeetCode刷题笔记(Java)---第441-460题_第30张图片

  • 解答

    public int minMoves(int[] nums) {
            Arrays.sort(nums);
            int count = 0;
            for (int i = nums.length - 1; i > 0; i--) {
                count += nums[i] - nums[0];
            }
            return count;
        }
    
    public int minMoves(int[] nums) {
            int moves = 0, min = Integer.MAX_VALUE;
            for (int i = 0; i < nums.length; i++) {
                min = Math.min(min, nums[i]);
            }
            for (int i = 0; i < nums.length; i++) {
                moves += nums[i] - min;
            }
            return moves;
        }
    
  • 分析

    1. 对于每次将n-1个元素加1 等价于每次将某一个元素减1。
    2. 只考虑他们的相对大小。相同即可。
    3. 所以起始就是每一位减去最小值即可。
    4. 遍历 当前位置和最小位置的差值 累加
    5. 方法1和方法2 的差别就是时间复杂度的不同。
  • 提交结果

    方法1LeetCode刷题笔记(Java)---第441-460题_第31张图片
    方法2LeetCode刷题笔记(Java)---第441-460题_第32张图片

454. 四数相加 II

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。

LeetCode刷题笔记(Java)---第441-460题_第33张图片

  • 解答

    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
            Map<Integer, Integer> map = new HashMap<>() ;
            int count = 0 ;
            for(int i : A)
            {
                for(int j : B)
                {
                    map.put((i+j), map.getOrDefault(i+j, 0)+1) ;
                }
            }
            for(int i: C)
            {
                for(int j : D)
                {
                    if(map.containsKey(0-(i+j)))
                    {
                        count += map.get(0-(i+j)) ;
                    }
                }
            }
            return count;
        }
    
  • 分析

    1. 遍历A和B中的数字,进行组合相加。
    2. 相加的结果放入map中,如果已经存在,则计数+1
    3. 遍历C和D中的数字,进行组合相加。
    4. 如果map中存在 CD组合相加后的相反数。说明4个数字相加为0.
    5. res 加上 对应和的次数。
  • 提交结果LeetCode刷题笔记(Java)---第441-460题_第34张图片

455. 分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

LeetCode刷题笔记(Java)---第441-460题_第35张图片

  • 解答

    public int findContentChildren(int[] g, int[] s) {
            Arrays.sort(g);
            Arrays.sort(s);
            int index = 0;
            int res = 0;
            for(int i = 0;i < g.length;i++){
                while(index < s.length && s[index] < g[i]){
                    index++;
                }
                if(index < s.length && s[index++] >= g[i])res++;
            }
            return res;
        }
    
  • 分析

    1. 将g和s都按照升序排序。
    2. 遍历每一个孩子。
    3. 根据孩子的胃口从index开始往后找第一块大于等于孩子胃口的饼干。计数+1
  • 提交结果LeetCode刷题笔记(Java)---第441-460题_第36张图片

456. 132模式

给定一个整数序列:a1, a2, …, an,一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。

注意:n 的值小于15000。
LeetCode刷题笔记(Java)---第441-460题_第37张图片

  • 解答

    public boolean find132pattern(int[] nums) {
            if (nums.length < 3)
                return false;
            Stack < Integer > stack = new Stack < > ();
            int[] min = new int[nums.length];
            min[0] = nums[0];
            for (int i = 1; i < nums.length; i++)
                min[i] = Math.min(min[i - 1], nums[i]);
                
            for (int j = nums.length - 1; j >= 0; j--) {
                if (nums[j] > min[j]) {
                    while (!stack.isEmpty() && stack.peek() <= min[j])
                        stack.pop();
                    if (!stack.isEmpty() && stack.peek() < nums[j])
                        return true;
                    stack.push(nums[j]);
                }
            }
            return false;
        }
    
  • 分析

    1. 用数组记录前缀最小值。表示当前位置之前出现的最小值。
    2. 逆序的遍历数组。使用栈
    3. 若当前位置的值大于当前位置的前缀最小值,则看栈中的元素。是否在这两者中间。
    4. 若栈顶小于等于前缀最小值,则栈顶出栈,直到大于或为空
    5. 如果当前栈顶的元素小于当前遍历的元素。说明找到了132模式
    6. 否则将当前遍历的元素入栈。
  • 提交结果LeetCode刷题笔记(Java)---第441-460题_第38张图片

457. 环形数组循环

给定一个含有正整数和负整数的环形数组 nums。 如果某个索引中的数 k 为正数,则向前移动 k 个索引。相反,如果是负数 (-k),则向后移动 k 个索引。因为数组是环形的,所以可以假设最后一个元素的下一个元素是第一个元素,而第一个元素的前一个元素是最后一个元素。

确定 nums 中是否存在循环(或周期)。循环必须在相同的索引处开始和结束并且循环长度 > 1。此外,一个循环中的所有运动都必须沿着同一方向进行。换句话说,一个循环中不能同时包括向前的运动和向后的运动。

LeetCode刷题笔记(Java)---第441-460题_第39张图片

  • 解答

    //方法1
    public boolean circularArrayLoop(int[] nums) {
            int len = nums.length;
            for (int i = 0; i < len; i++) {
                int[] visited = new int[len];
                int curStep = nums[i];
                boolean before = curStep > 0;
                int temp = i;
                int last = i;
                while (visited[temp] == 0) {
                    last = temp;
                    visited[temp] = 1;
                    if (before) {
                        int nextIndex = (temp + nums[temp]) % len;
                        if (nums[nextIndex] < 0) break;
                        else if (visited[nextIndex] == 1 && nextIndex != last) return true;
                        else temp = nextIndex;
                    } else {
                        int nextIndex = temp + nums[temp];
                        if (nextIndex < 0) nextIndex = (5000*len + nextIndex) % len;
                        if (nums[nextIndex] > 0) break;
                        else if (visited[nextIndex] == 1 && nextIndex != last) return true;
                        else temp = nextIndex;
                    }
                }
            }
            return false;
        }
    //方法2
      private void setZero(int[] nums, int i){
            int j;
            while (true) { // !(nums[j] == 0 || nums[i]*nums[j]<0)
                j = (i + nums[i] + 5000*nums.length) % nums.length;
                if (nums[j] == 0 || nums[i]*nums[j]<0) {
                    nums[i] = 0;
                    break;
                }
                nums[i] = 0;
                i = j;
            }
        }
    
        public boolean circularArrayLoop(int[] nums) {
            if (nums.length == 0) return false;
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] == 0) continue;
                int lastJ, lastK;
                int j=i, k=i;
    
                while (true) {
                    lastJ = j;
                    j = (j + nums[j] + 5000*nums.length) % nums.length;
                    if (nums[lastJ]*nums[j] < 0 || nums[j] == 0 || lastJ == j) {
                        setZero(nums, i);
                        break;
                    }
                    lastK = k;
                    k = (k + nums[k] + 5000*nums.length) % nums.length;
                    if (nums[lastK]*nums[k] < 0 || nums[k] == 0 || lastK == k){
                        setZero(nums, i);
                        break;
                    }
                    lastK = k;
                    k = (k + nums[k] + 5000*nums.length) % nums.length;
                    if (nums[lastK]*nums[k] < 0 || nums[k] == 0 || lastK == k){
                        setZero(nums, i);
                        break;
                    }
                    if (j == k)
                        return true;
                }
            }
            return false;
        }
    
  • 分析

    1. 方法1遍历数组,以每个数字为起点开始寻找
    2. visited数组用于记录有没有访问过
    3. 首先判断当前位置的数字大于0还是小于0.记录下它是向前还是向后。
    4. while循环,visited[temp] == 0 表示当前位置没有访问过。
    5. 当前位置记录下last,visited设为1。last用于判断循环长度是否大于1
    6. 若是向前的 则计算下一个索引位置。如果下一个索引位置的值表示向后 则break
    7. 若下一个索引位置被访问过了,且不等于last。说明构成了环形,返回true
    8. 否则更新temp 为nextIndex;
    9. 若向后 同理。
    10. 方法2 快慢指针
    11. 快指针走两步,慢指针走一步。若两指针相遇 则说明有环
    12. 若出现无法构成环的地方,则将当前位置以及可以到达的位置设置为0.
    13. 每次计算两个指针的下一步。若下一步和当前步的值为相反数 则无法构成环,若下一步的值为0,则无法构成环 剪枝。若下一步等于当前步 则构成的环长度等于1 无效的环。
  • 提交结果

    方法1LeetCode刷题笔记(Java)---第441-460题_第40张图片

    方法2LeetCode刷题笔记(Java)---第441-460题_第41张图片

458. 可怜的小猪

有 1000 只水桶,其中有且只有一桶装的含有毒药,其余装的都是水。它们从外观看起来都一样。如果小猪喝了毒药,它会在 15 分钟内死去。

问题来了,如果需要你在一小时内,弄清楚哪只水桶含有毒药,你最少需要多少只猪?

回答这个问题,并为下列的进阶问题编写一个通用算法。

LeetCode刷题笔记(Java)---第441-460题_第42张图片

  • 解答

    public int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
            int states = minutesToTest / minutesToDie + 1;
            return (int) Math.ceil(Math.log(buckets) / Math.log(states));
        }
    
  • 分析

    1. 猪猪的状态的分为 第一次喝水死亡…第n次喝水死亡 ,存活
    2. 也就是说在p分钟内 猪猪喝水m分钟死亡的情况下,有 p /m + 1种状态
    3. 找规律可以发现
    4. x 只猪 s种状态 可以检测 s的x次方只水桶中 含有毒药的那个水桶。
  • 提交结果LeetCode刷题笔记(Java)---第441-460题_第43张图片

459. 重复的子字符串

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

LeetCode刷题笔记(Java)---第441-460题_第44张图片

  • 解答

    public boolean repeatedSubstringPattern(String s) {
            int len = s.length();
            for(int i = 1;i <= len/2;i++){
                if(len % i == 0){
                    int num = len / i;
                    StringBuilder sb = new StringBuilder();
                    String sub = s.substring(0,i);
                    while(num > 0){
                        sb.append(sub);
                        num--;
                    }
                    if(sb.toString().equals(s))return true;
                }
            }
            return false;
        }
    
  • 分析

    1. 根据字符串的长度得到可以平等划分的段长度。
    2. 根据子串构建等长的字符串 和原字符串进行比较是否相等。
  • 提交结果LeetCode刷题笔记(Java)---第441-460题_第45张图片

你可能感兴趣的:(#,LeetCode刷题笔记,算法,leetcode,数据结构,java)