【数组、字符串】算法例题

该博客围绕数组和字符串相关算法例题展开,包含合并有序数组、移除元素、删除重复项等24道题目。详细给出各题的题目描述、示例及提示,还介绍了多种解题方法,如合并有序数组有合并后排序、双指针比较等方法,部分题解还分析了复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 每个题的【方法1】是自己的思路,【其他方法】是力扣上更优的解题思路

目录

一、数组、字符串

1. 合并两个有序数组 ①

2. 移除元素 ①

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

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

5. 多数元素 ①

6. 轮转数组 ②

7. 买卖股票的最佳时机 ①

8. 买卖股票的最佳时机 II ②

9. 跳跃游戏 ②

10. 跳跃游戏II  ②

11. H指数 ②

12. O(1)时间插入、删除和获取随机元素 ②

13. 除自身以外数组的乘积 ②

14. 加油站 ②

15. 分发糖果 ③

16. 接雨水 ③

17. 罗马数字转整数 ①

18. 整数转罗马数字 ②

19. 最后一个单词的长度 ①

20. 最长公共前缀 ①

21. 反转字符串中的单词 ②

22. Z字形变换 ②

23. 找出字符串中第一个匹配项的下标 ①

24. 文本左右对齐 ③


一、数组、字符串

1. 合并两个有序数组 ①

 给你两个按 非递减顺序 排列的整数数组 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 中的元素。
示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。
示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。


提示:

nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[j] <= 109

    方法1:数组合并后再调用内置函数进行排序(1ms)

        public static void merge(int[] nums1, int m, int[] nums2, int n) {
            for(int i = 0; i < n; i++){
                nums1[m + i] = nums2[i];
            }
            Arrays.sort(nums1);
        }

    方法2:两个指针对两个数组中元素一一比较(0ms)

    public static void merge(int[] nums1, int m, int[] nums2, int n) {
            int i = m - 1;
            int j = n - 1;
            for (int k = m + n - 1; k >= 0; k--) {
                if (i < 0){
                    nums1[k] = nums2[j];
                    j--;
                    continue;
                }
                if (j < 0){
                    nums1[k] = nums1[i];
                    i--;
                    continue;
                }
                if (nums1[i] > nums2[j]){
                    nums1[k] = nums1[i];
                    i--;
                }else {
                    nums1[k] = nums2[j];
                    j--;
                }
    
            }
        }

    方法3:类似于方法2

        public void merge(int[] nums1, int m, int[] nums2, int n) {
            if(n==0) return;
            int i = m-1,j=n-1,k = m+n-1;
            while(i>=0&&j>=0){
                if(nums1[i]>nums2[j]){
                    nums1[k--] = nums1[i--];
                }else{
                    nums1[k--] = nums2[j--];
                }
            }
            while(j>=0){
                nums1[k--] = nums2[j--];
            }
          
        }

    2. 移除元素 ①

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

    不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

    元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

    说明:

    为什么返回数值是整数,但输出的答案是数组呢?

    请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

    你可以想象内部操作如下:

    // nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
    int len = removeElement(nums, val);
    
    // 在函数里修改输入数组对于调用者是可见的。
    // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
    for (int i = 0; i < len; i++) {
        print(nums[i]);
    }
    

      示例 1:

      输入:nums = [3,2,2,3], val = 3
      输出:2, nums = [2,2]
      解释:函数应该返回新的长度 2,并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
      示例 2:

      输入:nums = [0,1,2,2,3,0,4,2], val = 2
      输出:5, nums = [0,1,3,0,4]
      解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0,1,3,0,4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。


      提示:

      0 <= nums.length <= 100
      0 <= nums[i] <= 50
      0 <= val <= 100

      方法1:

          public int removeElement(int[] nums, int val) {
              ArrayList<Integer> list = new ArrayList<>();
              int cnt = 0;
              for (int i = 0; i < nums.length; i++) {
                  if (nums[i] != val){
                      list.add(nums[i]);
                  }
              }
              for (Integer integer : list) {
                  nums[cnt++] = integer;
              }
              return list.size();
          }

      方法2:

          public int removeElement(int[] nums, int val) {
              int left = 0;
              int right = 0;
              while(right < nums.length) {
                  if(nums[right] == val) {
                      right++;
                  } else{
                      nums[left++] = nums[right++];
                  }
              }
              return left;
          }

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

       给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

      考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

      • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
      • 返回 k 。

      判题标准:

      系统会用下面的代码来测试你的题解:

      int[] nums = [...]; // 输入数组
      int[] expectedNums = [...]; // 长度正确的期望答案

      int k = removeDuplicates(nums); // 调用

      assert k == expectedNums.length;
      for (int i = 0; i < k; i++) {
          assert nums[i] == expectedNums[i];
      }
      如果所有断言都通过,那么您的题解将被 通过。

      示例 1:

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

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

      提示:

      • 1 <= nums.length <= 3 * 104
      • -104 <= nums[i] <= 104
      • nums 已按 非严格递增 排列

      方法1:

          public static int removeDuplicates(int[] nums) {
              int i = 1;
              int cnt = 1;
              while (i < nums.length){
                  if (nums[i] == nums[i - 1]){
                      i++;
                      continue;
                  }else {
                      nums[cnt++] = nums[i++];
                  }
              }
              return cnt;
          }

      20250225:

      public static int removeDuplicates(int[] nums) {
      	int left = 0;
      	int right = left + 1;
      	int index = 0;
      	while (right < nums.length){
      		if (nums[left] == nums[right]){
      			right++;
      		} else {
      			nums[index++] = nums[left];
      			left = right;
      			right++;
      		}
      		if (left == nums.length - 1 || right == nums.length){
      			nums[index++] = nums[left];
      		}
      	}
      	return index == 0? 1 : index;
      }

      方法2:

          public int removeDuplicates1(int[] nums) {
              int slow =0;
              for(int fast =1; fast < nums.length; fast++){
                  if(nums[fast] != nums[slow]){
                      slow++;
                      nums[slow] = nums[fast];
                  }
              }
              return slow+1;
          }

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

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

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

      说明:

      为什么返回数值是整数,但输出的答案是数组呢?

      请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

      你可以想象内部操作如下:

      // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
      int len = removeDuplicates(nums);
      
      // 在函数里修改输入数组对于调用者是可见的。
      // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
      for (int i = 0; i < len; i++) {
          print(nums[i]);
      }
      

      示例 1:

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

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


      提示:

      1 <= nums.length <= 3 * 104
      -104 <= nums[i] <= 104
      nums 已按升序排列

        方法1:

            public static int removeDuplicates(int[] nums) {
                int index = 0;
                int count = 1;
                for (int i = 1; i < nums.length; i++) {
                    if (nums[i] == nums[index] && count == 1){
                        index++;
                        nums[index] = nums[i];
                        count++;
                    }
                    if (nums[i] != nums[index]){
                        index++;
                        nums[index] = nums[i];
                        count = 1;
                    }
                }
                return index + 1;
            }

        方法2:

            public static int removeDuplicates1(int[] nums) {
                if(nums.length<=2) return nums.length;
                int slow = 2;
                for(int fast = 2;fast<nums.length;fast++)
                {
                    if(nums[fast]!=nums[slow-2])
                    {
                        nums[slow++] = nums[fast];
                    }
                }
                return slow;
            }

        方法3:

        class Solution {
            public int removeDuplicates(int[] nums) {   
                return process(nums, 2);
            }
            int process(int[] nums, int k) {
                int u = 0; 
                for (int x : nums) {
                    if (u < k || nums[u - k] != x) nums[u++] = x;
                }
                return u;
            }
        }
        
        作者:宫水三叶
        链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/
        来源:力扣(LeetCode)
        

        5. 多数元素 ①

         给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 

        ⌊ n/2 ⌋ 的元素。

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

        示例 1:

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

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

        提示:

        • n == nums.length
        • 1 <= n <= 5 * 104
        • -109 <= nums[i] <= 109

        方法一:

            public static int majorityElement(int[] nums) {
                HashMap<Integer, Integer> map = new HashMap<>();
                for (int num : nums) {
                    map.put(num, map.getOrDefault(num, 0) + 1);
                }
                int length = nums.length / 2;
                Integer result = 0;
                Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
                for (Map.Entry<Integer, Integer> entry : entries) {
                    if (entry.getValue() > length){
                        result = entry.getKey();
                    }
                }
                return result;
            }

        方法2:  0秒

            private int quickSort(int[] nums, int l, int r, int k) {
                if (l >= r) return nums[l];
                int x = nums[l + r >> 1];
                int i = l - 1;
                int j = r + 1;
                while (i < j) {
                    while (nums[++i] < x) ;
                    while (nums[--j] > x) ;
                    if (i < j) {
                        int tmp = nums[i];
                        nums[i] = nums[j];
                        nums[j] = tmp;
                    }
                }
                return k <= j ? quickSort(nums, l, j, k) : quickSort(nums, j + 1, r, k);
            }
        
            public int majorityElement(int[] nums) {
                return quickSort(nums, 0, nums.length - 1, nums.length / 2);
            }

        方法3:1秒

            public int majorityElement(int[] nums) {
                int res = nums[0];
                int count = 1;
                for(int i = 1; i < nums.length; i++){
                    if(nums[i] == res){
                        count++;
                    }
                    else if(count > 1){
                        count--;
                    }
                    else{
                        res = nums[i];
                    }
                }
                return res;
            }

        6. 轮转数组 ②

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

        示例 1:

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

        解释:
        向右轮转 1 步: [7,1,2,3,4,5,6]
        向右轮转 2 步: [6,7,1,2,3,4,5]
        向右轮转 3 步: [5,6,7,1,2,3,4]

        示例 2:

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


        提示:

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

          方法1:

              public static void rotate(int[] nums, int k) {
                  int length = nums.length;
                  int[] newArr = new int[length];
                  k = k > length? k % length : k;
                  for (int i = 0; i < nums.length; i++) {
                      newArr[i] = nums[(length + i - k) % length];
                  }
                  System.arraycopy(newArr, 0, nums, 0, length);
              }

          方法2:

              public void rotate(int[] nums, int k) {
                  //翻转数组
                  k = k % nums.length;
                  reverse(nums,0,nums.length-1);
                  reverse(nums,0,k-1);
                  reverse(nums,k,nums.length-1);
          
                  
              }
              public void reverse(int[] nums, int start,int end){
                  while(start<end){
                      int temp = nums[start];
                      nums[start++] = nums[end];
                      nums[end--] = temp;
                  }
              }

          7. 买卖股票的最佳时机 ①

           给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

          你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

          返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

            示例 1:

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

            输入:prices = [7,6,4,3,1]
            输出:0
            解释:在这种情况下, 没有交易完成, 所以最大利润为 0。


            提示:

            1 <= prices.length <= 105
            0 <= prices[i] <= 104

            方法一:

                public static int maxProfit(int[] prices) {
                    int max = 0;
                    int min = Integer.MAX_VALUE;
                    for (int i = 0; i < prices.length; i++) {
                        if (prices[i] < min){
                            min = prices[i];
                        }else {
                            max = prices[i] - min > max? prices[i] - min:max;
                        }
                    }
                    return max;
                }

            方法二:

                public int maxProfit(int[] prices) {
                    int cost = Integer.MAX_VALUE, profit = 0;
                    for (int price : prices) {
                        cost = Math.min(cost, price);
                        profit = Math.max(profit, price - cost);
                    }
                    return profit;
                }
            
            作者:Krahets
            链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
            

            8. 买卖股票的最佳时机 II ②

             给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

            在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

            返回 你能获得的 最大 利润 。

            示例 1:

            输入:prices = [7,1,5,3,6,4]
            输出:7
            解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
                 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
                 总利润为 4 + 3 = 7 。
            示例 2:

            输入:prices = [1,2,3,4,5]
            输出:4
            解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
                 总利润为 4 。
            示例 3:

            输入:prices = [7,6,4,3,1]
            输出:0
            解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。

            提示:

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

            方法1:

                public static int maxProfit(int[] prices) {
                    int max = 0;
                    int left = 0;
                    for (int i = 1; i < prices.length; i++) {
                       if (prices[i] > prices[i - 1]){
                           left++;
                           if (i == prices.length - 1){
                               max += prices[i] - prices[i - left];
                           }
                       }else {
                           max += prices[i - 1] - prices[i - 1 - left];
                           left = 0;
                       }
                    }
                    return max;
                }

            0ms,打败100%

            public static int maxProfit(int[] prices) {
            	int left = 0;
            	int max = 0;
            	for (int i = 1; i < prices.length; i++) {
            		if (prices[i] < prices[i - 1]){
            			max += (prices[i - 1] - prices[left]);
            			left = i;
            		}
            		if (left < prices.length - 1 && i == prices.length - 1){
            			max += prices[i] - prices[left];
            		}
            	}
            	return max;
            }

            方法2:

                public int maxProfit(int[] prices) {
                    int profit = 0;
                    for (int i = 1; i < prices.length; i++) {
                        int tmp = prices[i] - prices[i - 1];
                        if (tmp > 0) profit += tmp;
                    }
                    return profit;
                }
            
            作者:Krahets
            链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/
            

            9. 跳跃游戏 ②

             给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

            判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。

            示例 1:

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

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


            提示:

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

              方法二:

                  public static boolean canJump(int[] nums) {
                      if (nums == null) {
                          return false;
                      }
                      //前n-1个元素能够跳到的最远距离
                      int k = 0;
                      for (int i = 0; i <= k; i++) {
                          //第i个元素能够跳到的最远距离
                          int temp = i + nums[i];
                          //更新最远距离
                          k = Math.max(k, temp);
                          //如果最远距离已经大于或等于最后一个元素的下标,则说明能跳过去,退出. 减少循环
                          if (k >= nums.length - 1) {
                              return true;
                          }
                      }
                      //最远距离k不再改变,且没有到末尾元素
                      return false;
                  }

              10. 跳跃游戏II  ②

               给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]

              每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

              • 0 <= j <= nums[i] 
              • i + j < n

              返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

                示例 1:

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

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


                提示:

                1 <= nums.length <= 104
                0 <= nums[i] <= 1000
                题目保证可以到达 nums[n-1]

                方法二:

                    public int jump(int[] nums) {
                        if (nums == null || nums.length <= 1) return 0;
                
                        int currJumpMax = 0,  //以当前跳跃步数,能到的最远位置,比如: jump=1跳一次时,最远能到下标currJumpMax=2
                            currPostMax = 0,  //当前位置能到的最远位置
                            jump = 0,
                            i = 0;
                
                        while (i < nums.length - 1) { //不需要检查最后一个位置是因为,最后一个位置我们不用跳了已经
                            currPostMax = Math.max(currPostMax, i + nums[i]);
                            if (i == currJumpMax) { //已经走到了当前跳跃步数的边界,
                                jump++; //我们不得不再跳一次
                                currJumpMax = currPostMax; //并记录当前跳跃步数能到的最远位置
                            }
                            i++;
                        }
                
                        return jump;
                    }

                11. H指数 ②

                 给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数

                根据维基百科上 h 指数的定义h 代表“高引用次数” ,一名科研人员的 h 指数 是指他(她)至少发表了 h 篇论文,并且 至少 有 h 篇论文被引用次数大于等于 h 。如果 h 有多种可能的值,h 指数 是其中最大的那个。

                  示例 1:

                  输入:citations = [3,0,6,1,5]
                  输出:3 
                  解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。     由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3。
                  示例 2:

                  输入:citations = [1,3,1]
                  输出:1


                  提示:

                  n == citations.length
                  1 <= n <= 5000
                  0 <= citations[i] <= 1000

                  方法2:

                      public int hIndex(int[] cs) {
                          int n = cs.length;
                          int l = 0, r = n;
                          while (l < r) {
                              int mid = l + r + 1 >> 1;
                              if (check(cs, mid)) l = mid;
                              else r = mid - 1;
                          }
                          return r;
                      }
                      boolean check(int[] cs, int mid) {
                          int ans = 0;
                          for (int i : cs) if (i >= mid) ans++;
                          return ans >= mid;
                      }
                  
                  作者:宫水三叶
                  链接:https://leetcode.cn/problems/h-index/
                  

                  方法3:

                      public int hIndex(int[] citations) {
                          Arrays.sort(citations);
                          int hIndex = 0;
                          int n = citations.length-1;
                  
                          for (int i = n; i >= 0; i--) {
                              if(citations[i] > hIndex ) {
                                  hIndex++;
                              }
                          }
                          return citations[n] == 0?0:hIndex;
                      }

                  12. O(1)时间插入、删除和获取随机元素 ②

                  13. 除自身以外数组的乘积 ②

                   给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

                  题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内。

                  请 不要使用除法,且在 O(n) 时间复杂度内完成此题。

                  示例 1:

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

                  输出: [24,12,8,6]

                  示例 2:

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


                  提示:

                  2 <= nums.length <= 105
                  -30 <= nums[i] <= 30
                  保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内


                  进阶:你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组 不被视为 额外空间。)

                  方法1:(超出时间限制,通过18/22)

                      public static int[] productExceptSelf(int[] nums) {
                          int[] answer = new int[nums.length];
                          Arrays.fill(answer, 1);
                          for (int i = 0; i < nums.length; i++) {
                              int index = 0;
                              while (index <nums.length){
                                  if (index != i){
                                      answer[i] *= nums[index];
                                  }
                                  index++;
                              }
                          }
                          return answer;
                      }

                  方法2:(0ms)

                      public int[] productExceptSelf(int[] nums) {
                          int[] result = new int[nums.length];
                          int initial = 1;
                          for(int i = 0; i < nums.length; i++){
                              result[i] = initial;
                              initial *= nums[i];
                          }
                          initial = 1;
                          for(int j = nums.length - 1; j >= 0; j--){
                              result[j] *= initial;
                              initial *= nums[j];
                          }
                  
                          return result;
                      }

                  14. 加油站 ②

                   在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

                  你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

                  给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

                  示例 1:

                  输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
                  输出: 3
                  解释:
                  从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
                  开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
                  开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
                  开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
                  开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
                  开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
                  因此,3 可为起始索引。
                  示例 2:

                  输入: gas = [2,3,4], cost = [3,4,3]
                  输出: -1
                  解释:
                  你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
                  我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
                  开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
                  开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
                  你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
                  因此,无论怎样,你都不可能绕环路行驶一周。


                  提示:

                  gas.length == n
                  cost.length == n
                  1 <= n <= 105
                  0 <= gas[i], cost[i] <= 104

                    方法1:(34/40通过)

                        public static int canCompleteCircuit(int[] gas, int[] cost) {
                            for (int i = 0; i < gas.length; i++) {
                                if (gas[i] < cost[i]){
                                    continue;
                                }else {
                                    int index = 0;
                                    int res = gas[i];
                                    int result = cost[(i - 1 + gas.length) % gas.length];
                                    while (index < gas.length - 1){
                                        res -= cost[(i + index) % gas.length];
                                        if (res < 0){
                                            break;
                                        }else {
                                            res = res + gas[(i + 1 + index) % gas.length];
                                            index++;
                                        }
                                    }
                                    if (res == result){
                                        return i;
                                    }
                    
                                }
                            }
                            return -1;
                        }

                    方法2:(0ms)

                        public int canCompleteCircuit(int[] gas, int[] cost) {
                            int res = 0;
                            int tank = 0;
                            int need = 0;
                            
                            for (int i = 0; i < gas.length; i++) {
                                tank += (gas[i] - cost[i]);
                                if (tank < 0) {
                                    res = i + 1;
                                    need += tank;
                                    tank = 0;
                                }
                            }
                            if (tank + need >= 0) {
                                return res;
                            } 
                            return -1;
                        }

                    方法3:(1ms)

                        public int canCompleteCircuit(int[] gas, int[] cost) {
                            int n = gas.length;
                            int sum = 0;
                            int minSum = Integer.MAX_VALUE, minSumIndex = 0;
                    
                            for (int i = 0; i < n; i++){
                                sum += gas[i] - cost[i]; 
                    
                                if (sum < minSum){
                                    minSumIndex = i;
                                    minSum = sum;
                                }
                            }
                            if (sum < 0) return -1;
                            else if (minSum >= 0) return 0;
                            else return (minSumIndex + 1) % n;
                        }
                    

                    15. 分发糖果 ③ ×

                    n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

                    你需要按照以下要求,给这些孩子分发糖果:

                    • 每个孩子至少分配到 1 个糖果。
                    • 相邻两个孩子评分更高的孩子会获得更多的糖果。

                    请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

                    示例 1:

                    输入:ratings = [1,0,2]
                    输出:5
                    解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
                    示例 2:

                    输入:ratings = [1,2,2]
                    输出:4
                    解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
                         第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。


                    提示:

                    n == ratings.length
                    1 <= n <= 2 * 104
                    0 <= ratings[i] <= 2 * 104

                      . - 力扣(LeetCode)

                      3ms:

                      class Solution {
                          public int candy(int[] ratings) {
                              int[] left = new int[ratings.length];
                              int[] right = new int[ratings.length];
                              Arrays.fill(left, 1);
                              Arrays.fill(right, 1);
                              for(int i = 1; i < ratings.length; i++)
                                  if(ratings[i] > ratings[i - 1]) left[i] = left[i - 1] + 1;
                              int count = left[ratings.length - 1];
                              for(int i = ratings.length - 2; i >= 0; i--) {
                                  if(ratings[i] > ratings[i + 1]) right[i] = right[i + 1] + 1;
                                  count += Math.max(left[i], right[i]);
                              }
                              return count;
                          }
                      }
                      
                      作者:Krahets
                      链接:https://leetcode.cn/problems/candy/solutions/17847/candy-cong-zuo-zhi-you-cong-you-zhi-zuo-qu-zui-da-/
                      

                      官方题解:

                      class Solution {
                          public int candy(int[] ratings) {
                              int n = ratings.length;
                              int[] left = new int[n];
                              for (int i = 0; i < n; i++) {
                                  if (i > 0 && ratings[i] > ratings[i - 1]) {
                                      left[i] = left[i - 1] + 1;
                                  } else {
                                      left[i] = 1;
                                  }
                              }
                              int right = 0, ret = 0;
                              for (int i = n - 1; i >= 0; i--) {
                                  if (i < n - 1 && ratings[i] > ratings[i + 1]) {
                                      right++;
                                  } else {
                                      right = 1;
                                  }
                                  ret += Math.max(left[i], right);
                              }
                              return ret;
                          }
                      }
                      
                      作者:力扣官方题解
                      链接:https://leetcode.cn/problems/candy/solutions/533150/fen-fa-tang-guo-by-leetcode-solution-f01p/
                      来源:力扣(LeetCode)
                      著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

                      16. 接雨水 ③

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

                      示例 1:

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

                        输入:height = [4,2,0,3,2,5]
                        输出:9


                        提示:

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

                        双指针解法:(0ms)  时间O(n),空间O(1)

                        
                        public int trap(int[] height) {
                        	int n = height.length;
                        	int res = 0;
                        	// 左右指针:分别指向左右两边界的列
                        	int left = 0, right = n - 1;
                        	// 左指针的左边最大高度、右指针的右边最大高度
                        	int leftMax = height[left], rightMax = height[right];
                        	// 最两边的列存不了水
                        	left++;
                        	right--;
                        	// 向中间靠拢
                        	while(left <= right){
                        		leftMax = Math.max(leftMax, height[left]);
                        		rightMax = Math.max(rightMax, height[right]);
                        		if(leftMax < rightMax){
                        			// 左指针的leftMax比右指针的rightMax矮
                        			// 说明:左指针的右边至少有一个板子 > 左指针左边所有板子
                        			// 根据水桶效应,保证了左指针当前列的水量决定权在左边
                        			// 那么可以计算左指针当前列的水量:左边最大高度-当前列高度
                        			res += leftMax - height[left];
                        			left++;
                        		}else{
                        			// 右边同理
                        			res += rightMax - height[right];
                        			right--;
                        		}
                        	}
                        	return res;
                        作者:windliang
                        链接:https://leetcode.cn/problems/trapping-rain-water/solutions/9112/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
                        来源:力扣(LeetCode)
                        著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
                        }
                        

                        双指针(1ms)

                        可算看懂了,原来双指针同时开两个柱子接水。大家题解没说清楚,害得我也没看懂。 对于每一个柱子接的水,那么它能接的水=min(左右两边最高柱子)-当前柱子高度,这个公式没有问题。同样的,两根柱子要一起求接水,同样要知道它们左右两边最大值的较小值。

                        问题就在这,假设两柱子分别为 i,j。那么就有 iLeftMax,iRightMax,jLeftMx,jRightMax 这个变量。由于 j>i ,故 jLeftMax>=iLeftMax,iRigthMax>=jRightMax.

                        那么,如果 iLeftMax>jRightMax,则必有 jLeftMax >= jRightMax,所有我们能接 j 点的水。

                        如果 jRightMax>iLeftMax,则必有 iRightMax >= iLeftMax,所以我们能接 i 点的水。

                        而上面我们实际上只用到了 iLeftMax,jRightMax 两个变量,故我们维护这两个即可。(题解都没说清楚,就说个 LeftMax,RightMax,谁知道为什么就可以这么做了。)

                        public int trap(int[] height) {
                            int sum = 0;
                            int max_left = 0;
                            int max_right = 0;
                            int left = 1;
                            int right = height.length - 2; // 加右指针进去
                            for (int i = 1; i < height.length - 1; i++) {
                                //从左到右更
                                if (height[left - 1] < height[right + 1]) {
                                    max_left = Math.max(max_left, height[left - 1]);
                                    int min = max_left;
                                    if (min > height[left]) {
                                        sum = sum + (min - height[left]);
                                    }
                                    left++;
                                //从右到左更
                                } else {
                                    max_right = Math.max(max_right, height[right + 1]);
                                    int min = max_right;
                                    if (min > height[right]) {
                                        sum = sum + (min - height[right]);
                                    }
                                    right--;
                                }
                            }
                            return sum;
                        }
                        
                        作者:windliang
                        链接:https://leetcode.cn/problems/trapping-rain-water/solutions/9112/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
                        来源:力扣(LeetCode)
                        著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

                        动态规划:(1ms)

                        public int trap(int[] height) {
                            int sum = 0;
                            int[] max_left = new int[height.length];
                            int[] max_right = new int[height.length];
                            
                            for (int i = 1; i < height.length - 1; i++) {
                                max_left[i] = Math.max(max_left[i - 1], height[i - 1]);
                            }
                            for (int i = height.length - 2; i >= 0; i--) {
                                max_right[i] = Math.max(max_right[i + 1], height[i + 1]);
                            }
                            for (int i = 1; i < height.length - 1; i++) {
                                int min = Math.min(max_left[i], max_right[i]);
                                if (min > height[i]) {
                                    sum = sum + (min - height[i]);
                                }
                            }
                            return sum;
                        }
                        
                        作者:windliang
                        链接:https://leetcode.cn/problems/trapping-rain-water/solutions/9112/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
                        来源:力扣(LeetCode)
                        著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

                        栈(18ms):

                        public int trap6(int[] height) {
                            int sum = 0;
                            Stack<Integer> stack = new Stack<>();
                            int current = 0;
                            while (current < height.length) {
                                //如果栈不空并且当前指向的高度大于栈顶高度就一直循环
                                while (!stack.empty() && height[current] > height[stack.peek()]) {
                                    int h = height[stack.peek()]; //取出要出栈的元素
                                    stack.pop(); //出栈
                                    if (stack.empty()) { // 栈空就出去
                                        break; 
                                    }
                                    int distance = current - stack.peek() - 1; //两堵墙之前的距离。
                                    int min = Math.min(height[stack.peek()], height[current]);
                                    sum = sum + distance * (min - h);
                                }
                                stack.push(current); //当前指向的墙入栈
                                current++; //指针后移
                            }
                            return sum;
                        }
                        
                        作者:windliang
                        链接:https://leetcode.cn/problems/trapping-rain-water/solutions/9112/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
                        来源:力扣(LeetCode)
                        著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

                        17. 罗马数字转整数 ①

                         罗马数字包含以下七种字符: I, V, X, LCD 和 M

                        字符          数值
                        I             1
                        V             5
                        X             10
                        L             50
                        C             100
                        D             500
                        M             1000

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

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

                        • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
                        • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 
                        • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

                        给定一个罗马数字,将其转换成整数。


                        示例 1:

                        输入: s = "III"
                        输出: 3
                        示例 2:

                        输入: s = "IV"
                        输出: 4
                        示例 3:

                        输入: s = "IX"
                        输出: 9
                        示例 4:

                        输入: s = "LVIII"
                        输出: 58
                        解释: L = 50, V= 5, III = 3.
                        示例 5:

                        输入: s = "MCMXCIV"
                        输出: 1994
                        解释: M = 1000, CM = 900, XC = 90, IV = 4.
                        提示:

                        1 <= s.length <= 15
                        s 仅含字符 ('I', 'V', 'X', 'L', 'C', 'D', 'M')
                        题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内
                        题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
                        IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
                        关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。

                          方法一:

                              public int romanToInt(String s) {
                                  Stack<Integer> stack = new Stack<>();
                                  for (int i = 0; i < s.length(); i++) {
                                      char c = s.charAt(i);
                                      int num = charToInt(c);
                                      if (stack.size() == 0 || num <= stack.peek()) {
                                          stack.push(num);
                                      }else {
                                          Integer pop = stack.pop();
                                          stack.push(num - pop);
                                      }
                                  }
                                  int sum = 0;
                                  while (stack.size() > 0){
                                      sum += stack.pop();
                                  }
                                  return sum;
                              }
                              public static int charToInt(char c){
                                  int symbol = 0;
                                  switch (c){
                                      case 'I':
                                          symbol = 1;
                                          break;
                                      case 'V':
                                          symbol = 5;
                                          break;
                                      case 'X':
                                          symbol = 10;
                                          break;
                                      case 'L':
                                          symbol = 50;
                                          break;
                                      case 'C':
                                          symbol = 100;
                                          break;
                                      case 'D':
                                          symbol = 500;
                                          break;
                                      case 'M':
                                          symbol = 1000;
                                          break;
                                  }
                                  return symbol;
                              }

                          方法1(6ms)20250321

                          public static int romanToInt(String s) {
                          	HashMap<String, 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 sum = map.get(s.charAt(0) + "");
                          	char[] charArray = s.toCharArray();
                          	for (int i = 1; i < charArray.length; i++) {
                          		Integer num = map.get(charArray[i] + "");
                          		sum += num;
                          		if ((num / 5 == 1 || num / 5 == 2) && charArray[i - 1] == 'I'){
                          			sum -= 2;
                          		}else if ((num / 50 == 1 || num / 50 == 2) && charArray[i - 1] == 'X'){
                          			sum -= 20;
                          		}else if ((num / 500 == 1 || num / 500 == 2) && charArray[i - 1] == 'C'){
                          			sum -= 200;
                          		}
                          	}
                          	return sum;
                          }
                          

                          方法2:

                              public int romanToInt(String s) {
                                  int sum = 0;
                                  int preNum = getValue(s.charAt(0));
                                  for(int i = 1;i < s.length(); i ++) {
                                      int num = getValue(s.charAt(i));
                                      if(preNum < num) {
                                          sum -= preNum;
                                      } else {
                                          sum += preNum;
                                      }
                                      preNum = num;
                                  }
                                  sum += preNum;
                                  return sum;
                              }
                              
                              private int getValue(char ch) {
                                  switch(ch) {
                                      case 'I': return 1;
                                      case 'V': return 5;
                                      case 'X': return 10;
                                      case 'L': return 50;
                                      case 'C': return 100;
                                      case 'D': return 500;
                                      case 'M': return 1000;
                                      default: return 0;
                                  }
                              }

                          18. 整数转罗马数字 ②

                           罗马数字包含以下七种字符: I, V, X, LCD 和 M

                          字符          数值
                          I             1
                          V             5
                          X             10
                          L             50
                          C             100
                          D             500
                          M             1000

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

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

                          • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
                          • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 
                          • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

                          给你一个整数,将其转为罗马数字。

                          示例 1:

                          输入: num = 3
                          输出: "III"
                          示例 2:

                          输入: num = 4
                          输出: "IV"
                          示例 3:

                          输入: num = 9
                          输出: "IX"
                          示例 4:

                          输入: num = 58
                          输出: "LVIII"
                          解释: L = 50, V = 5, III = 3.
                          示例 5:

                          输入: num = 1994
                          输出: "MCMXCIV"
                          解释: M = 1000, CM = 900, XC = 90, IV = 4.

                          提示:

                          • 1 <= num <= 3999

                          方法1:

                              public static String intToRoman(int num) {
                                  int[] nums = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
                                  StringBuilder stringBuilder = new StringBuilder();
                                  while (num > 0){
                                      for (int i = 0; i < nums.length; ) {
                                          while (num >= nums[i]){
                                              stringBuilder.append(toString(nums[i]));
                                              num -= nums[i];
                                          }
                                          i++;
                                      }
                                  }
                                  return stringBuilder.toString();
                              }
                              public static String toString(int num){
                                  switch (num){
                                      case 1000:
                                          return "M";
                                      case 900:
                                          return "CM";
                                      case 500:
                                          return "D";
                                      case 400:
                                          return "CD";
                                      case 100:
                                          return "C";
                                      case 90:
                                          return "XC";
                                      case 50:
                                          return "L";
                                      case 40:
                                          return "XL";
                                      case 10:
                                          return "X";
                                      case 9:
                                          return "IX";
                                      case 5:
                                          return "V";
                                      case 4:
                                          return "IV";
                                      case 1:
                                          return "I";
                                  }
                                  return "";
                              }

                          方法2:

                              public String intToRoman(int num) {
                                  StringBuilder sb = new StringBuilder();
                                  int remains = num;
                                  while (remains > 0) {
                                      if (remains >= 1000) {
                                          sb.append('M');
                                          remains -= 1000;
                                      } else if (remains >= 100) {
                                          if (remains >= 900) {
                                              sb.append("CM");
                                              remains -= 900;
                                          } else if (remains >= 500) {
                                              sb.append('D');
                                              remains -= 500;
                                          } else if (remains >= 400) {
                                              sb.append("CD");
                                              remains -= 400;
                                          } else {
                                              sb.append('C');
                                              remains -= 100;
                                          }
                                      } else if (remains >= 10) {
                                          if (remains >= 90) {
                                              sb.append("XC");
                                              remains -= 90;
                                          } else if (remains >= 50) {
                                              sb.append('L');
                                              remains -= 50;
                                          } else if (remains >= 40) {
                                              sb.append("XL");
                                              remains -= 40;
                                          } else {
                                              sb.append('X');
                                              remains -= 10;
                                          }
                                      } else {
                                          if (remains >= 9) {
                                              sb.append("IX");
                                              remains -= 9;
                                          } else if (remains >= 5) {
                                              sb.append('V');
                                              remains -= 5;
                                          } else if (remains >= 4) {
                                              sb.append("IV");
                                              remains -= 4;
                                          } else {
                                              sb.append('I');
                                              remains -= 1;
                                          }
                                      }
                                  }
                                  return sb.toString();
                              }
                          

                          方法3:

                              int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
                              String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
                          
                              public String intToRoman(int num) {
                                  StringBuffer roman = new StringBuffer();
                                  for (int i = 0; i < values.length; ++i) {
                                      int value = values[i];
                                      String symbol = symbols[i];
                                      while (num >= value) {
                                          num -= value;
                                          roman.append(symbol);
                                      }
                                      if (num == 0) {
                                          break;
                                      }
                                  }
                                  return roman.toString();
                              }

                          19. 最后一个单词的长度 ①

                           给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。

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

                            示例 1:

                            输入:s = "Hello World"
                            输出:5
                            解释:最后一个单词是“World”,长度为5。
                            示例 2:

                            输入:s = "   fly me   to   the moon  "
                            输出:4
                            解释:最后一个单词是“moon”,长度为4。
                            示例 3:

                            输入:s = "luffy is still joyboy"
                            输出:6
                            解释:最后一个单词是长度为6的“joyboy”。


                            提示:

                            1 <= s.length <= 104
                            s 仅有英文字母和空格 ' ' 组成
                            s 中至少存在一个单词

                            方法1:(0ms)

                                public static int lengthOfLastWord(String s) {
                                    int right = s.length() - 1;
                                    while (s.charAt(right) == ' '){
                                        right--;
                                    }
                                    int index = right;
                                    while (index >= 0 && s.charAt(index) != ' '){
                                        index--;
                                    }
                                    return right - index;
                                }

                            20. 最长公共前缀 ①

                             编写一个函数来查找字符串数组中的最长公共前缀。

                            如果不存在公共前缀,返回空字符串 ""

                            示例 1:

                            输入:strs = ["flower","flow","flight"]
                            输出:"fl"
                            示例 2:

                            输入:strs = ["dog","racecar","car"]
                            输出:""
                            解释:输入不存在公共前缀。


                            提示:

                            1 <= strs.length <= 200
                            0 <= strs[i].length <= 200
                            strs[i] 仅由小写英文字母组成

                              方法1:(1ms)

                                  public static String longestCommonPrefix(String[] strs) {
                                      if (strs.length == 1){
                                          return strs[0];
                                      }
                                      int index = 0;
                                      String str = strs[0];
                                      while (index < str.length()) {
                                          char c = str.charAt(index);
                                          for (int i = 1; i < strs.length; i++) {
                                              if (strs[i].length() <= index){
                                                  return str.substring(0, index);
                                              }
                                              char c1 = strs[i].charAt(index);
                                              if (c1 != c){
                                                  return str.substring(0, index);
                                              }
                                          }
                                          index++;
                                      }
                                      return str.substring(0, index);
                                  }

                              方法2:(0ms)

                                  public String longestCommonPrefix(String[] strs) {
                                      if (strs == null || strs.length == 0) {
                                          return "";
                                      }
                                      String prefix = strs[0];
                                      int count = strs.length;
                                      for (int i = 1; i < count; i++) {
                                          prefix = longestCommonPrefix(prefix, strs[i]);
                                          if (prefix.length() == 0) {
                                              break;
                                          }
                                      }
                                      return prefix;
                                  }
                              
                                  public String longestCommonPrefix(String str1, String str2) {
                                      int length = Math.min(str1.length(), str2.length());
                                      int index = 0;
                                      while (index < length && str1.charAt(index) == str2.charAt(index)) {
                                          index++;
                                      }
                                      return str1.substring(0, index);
                                  }

                              21. 反转字符串中的单词 ②

                               给你一个字符串 s ,请你反转字符串中 单词 的顺序。

                              单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

                              返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

                              注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

                              示例 1:

                              输入:s = "the sky is blue"
                              输出:"blue is sky the"
                              示例 2:

                              输入:s = "  hello world  "
                              输出:"world hello"
                              解释:反转后的字符串中不能存在前导空格和尾随空格。
                              示例 3:

                              输入:s = "a good   example"
                              输出:"example good a"
                              解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。


                              提示:

                              1 <= s.length <= 104
                              s 包含英文大小写字母、数字和空格 ' '
                              s 中 至少存在一个 单词

                                进阶:如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。

                                方法1:(6ms)

                                    public static String reverseWords(String s) {
                                        Stack<String> stack = new Stack<>();
                                        StringBuilder stringBuilder = new StringBuilder();
                                        String[] s1 = s.split(" ");
                                        for (String s2 : s1) {
                                            if (!"".equals(s2)){
                                                stack.push(s2);
                                            }
                                        }
                                        while (stack.size() > 0){
                                            stringBuilder.append(stack.pop()).append(" ");
                                        }
                                        return stringBuilder.toString().substring(0, stringBuilder.length() - 1);
                                    }

                                方法1:(7ms)

                                public static String reverseWords(String s) {
                                	StringBuilder stringBuilder = new StringBuilder();
                                	int left = 0;
                                	int right = 0;
                                	while (left < s.length() && right < s.length()){
                                		while (left < s.length() && s.charAt(left) == ' '){
                                			left++;
                                		}
                                		right = left + 1;
                                		while (right < s.length() && s.charAt(right) != ' '){
                                			right++;
                                		}
                                		if (left == s.length()){
                                			break;
                                		}
                                		String word = s.substring(left, right);
                                		StringBuilder mid = new StringBuilder(word);
                                		stringBuilder = mid.append(" ").append(stringBuilder);
                                		left = right + 1;
                                	}
                                	String res = stringBuilder.toString();
                                	return res.length() > 0? res.substring(0, res.length() - 1): " ";
                                }
                                

                                22. Z字形变换 ②

                                 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

                                比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

                                P   A   H   N
                                A P L S I I G
                                Y   I   R

                                之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"

                                请你实现这个将字符串进行指定行数变换的函数:

                                string convert(string s, int numRows);

                                示例 1:

                                输入:s = "PAYPALISHIRING", numRows = 3
                                输出:"PAHNAPLSIIGYIR"
                                示例 2:

                                输入:s = "PAYPALISHIRING", numRows = 4
                                输出:"PINALSIGYAHRPI"
                                解释:
                                P      I       N
                                A   L S    I G
                                Y A   H R
                                P      I
                                示例 3:

                                输入:s = "A", numRows = 1
                                输出:"A"


                                提示:

                                1 <= s.length <= 1000
                                s 由英文字母(小写和大写)、',' 和 '.' 组成
                                1 <= numRows <= 1000

                                  方法1:

                                      public static String convert(String s, int numRows) {
                                          if (numRows == 1){
                                              return s;
                                          }
                                          StringBuilder[] stringBuilders = new StringBuilder[numRows];
                                          for (int i = 0; i < numRows; i++) {
                                              stringBuilders[i] = new StringBuilder();
                                          }
                                          boolean flag = true;
                                          int index = 0;
                                          int[] indexArr = new int[s.length()];
                                          for (int i = 0; i < s.length(); i++) {
                                              indexArr[i] = index;
                                              if (index == 0){
                                                  flag = true;
                                              }
                                              if (index == numRows - 1){
                                                  flag = false;
                                              }
                                              if (flag){
                                                  index++;
                                              }else {
                                                  index--;
                                              }
                                  
                                          }
                                          for (int i = 0; i < s.length(); i++) {
                                              stringBuilders[indexArr[i]].append(s.charAt(i));
                                          }
                                          for (int i = 1; i < numRows; i++) {
                                              stringBuilders[0].append(stringBuilders[i]);
                                          }
                                          return stringBuilders[0].toString();
                                      }

                                  23. 找出字符串中第一个匹配项的下标 ①

                                   给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回  -1 

                                  示例 1:

                                  输入:haystack = "sadbutsad", needle = "sad"
                                  输出:0
                                  解释:"sad" 在下标 0 和 6 处匹配。
                                  第一个匹配项的下标是 0 ,所以返回 0 。
                                  示例 2:

                                  输入:haystack = "leetcode", needle = "leeto"
                                  输出:-1
                                  解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。


                                  提示:

                                  1 <= haystack.length, needle.length <= 104
                                  haystack 和 needle 仅由小写英文字符组成

                                    方法1:(0ms)

                                        public static int strStr(String haystack, String needle) {
                                            for (int i = 0; i < haystack.length() - needle.length() + 1; i++) {
                                                String substring = haystack.substring(i, i + needle.length());
                                                if (substring.equals(needle)){
                                                    return i;
                                                }
                                            }
                                            return -1;
                                        }

                                    方法1:(1ms)(21min)

                                    public static int strStr(String haystack, String needle) {
                                    	if (haystack.length() < needle.length()){
                                    		return -1;
                                    	}
                                    	int b = 0;
                                    	int index = -1;
                                    	char c = needle.charAt(b);
                                    	int i = haystack.indexOf(c, b);
                                    	while (b < needle.length() && i < haystack.length()){
                                    		index = i;
                                    		if (i < 0){
                                    			return -1;
                                    		}else {
                                    			b++;
                                    			i++;
                                    			while (b < needle.length() && i < haystack.length() && haystack.charAt(i) == needle.charAt(b)){
                                    					i++;
                                    					b++;
                                    			}
                                    		}
                                    		if (b == needle.length()){
                                    			return index;
                                    		}else {
                                    			b = 0;
                                    			i = haystack.indexOf(c, index + 1);
                                    		}
                                    	}
                                    	return index;
                                    }
                                    

                                    方法2:(0ms)

                                    public static int strStr(String haystack, String needle) {
                                    	int n = haystack.length();
                                    	int m = needle.length();
                                    	for (int i = 0; i+m <= n; i++) {
                                    		boolean flag = true;
                                    		for (int j = 0; j < m; j++) {
                                    			if (haystack.charAt(i+j) != needle.charAt(j)){
                                    				flag = false;
                                    				break;
                                    			}
                                    		}
                                    		if (flag){
                                    			return i;
                                    		}
                                    	}
                                    	return  -1;
                                    }
                                    

                                    24. 文本左右对齐 ③

                                    给定一个单词数组 words 和一个长度 maxWidth ,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。

                                    你应该使用 “贪心算法” 来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ' ' 填充,使得每行恰好有 maxWidth 个字符。

                                    要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。

                                    文本的最后一行应为左对齐,且单词之间不插入额外的空格。

                                    注意:

                                    • 单词是指由非空格字符组成的字符序列。
                                    • 每个单词的长度大于 0,小于等于 maxWidth
                                    • 输入单词数组 words 至少包含一个单词。

                                    示例 1:

                                    输入: words = ["This", "is", "an", "example", "of", "text", "justification."], maxWidth = 16
                                    输出:

                                    [
                                       "This    is    an",
                                       "example  of text",
                                       "justification.  "
                                    ]
                                    

                                    示例 2:

                                    输入:words = ["What","must","be","acknowledgment","shall","be"], maxWidth = 16
                                    输出:
                                    [
                                      "What   must   be",
                                      "acknowledgment  ",
                                      "shall be        "
                                    ]
                                    解释: 注意最后一行的格式应为 "shall be    " 而不是 "shall     be",
                                         因为最后一行应为左对齐,而不是左右两端对齐。       
                                         第二行同样为左对齐,这是因为这行只包含一个单词。
                                    

                                    示例 3:

                                    输入:words = ["Science","is","what","we","understand","well","enough","to","explain","to","a","computer.","Art","is","everything","else","we","do"],maxWidth = 20
                                    输出:
                                    [
                                      "Science  is  what we",
                                      "understand      well",
                                      "enough to explain to",
                                      "a  computer.  Art is",
                                      "everything  else  we",
                                      "do                  "
                                    ]
                                    

                                    提示:

                                    • 1 <= words.length <= 300
                                    • 1 <= words[i].length <= 20
                                    • words[i] 由小写英文字母和符号组成
                                    • 1 <= maxWidth <= 100
                                    • words[i].length <= maxWidth

                                    题解:68. 文本左右对齐 - 力扣(LeetCode)

                                    贪心算法:(0ms)

                                    class Solution {
                                    	public List<String> fullJustify(String[] words, int maxWidth) {
                                    		List<String> res = new ArrayList<>();
                                    		int cnt = 0, bg = 0;
                                    		for (int i = 0; i < words.length; i++) {
                                    			cnt += words[i].length() + 1;
                                    			// 如果是最后一个单词,或者加上下一个词就超过长度了,即可凑成一行
                                    			if (i + 1 == words.length || cnt + words[i + 1].length() > maxWidth) {
                                    				// 对每行单词进行空格平均划分
                                    				res.add(fillWords(words, bg, i, maxWidth, i + 1 == words.length));
                                    				bg = i + 1;
                                    				cnt = 0;
                                    			}
                                    		}
                                    		return res;
                                    	}
                                    
                                    	/**
                                    	 * 对每行单词进行空格平均划分
                                    	 */
                                    	private String fillWords(String[] words, int bg, int ed, int maxWidth, boolean lastLine) {
                                    		int wordCount = ed - bg + 1;
                                    		// 除去每个单词尾部空格, + 1 是最后一个单词的尾部空格的特殊处理
                                    		int spaceCount = maxWidth + 1 - wordCount;
                                    		for (int i = bg; i <= ed; i++) {
                                    			// 除去所有单词的长度
                                    			spaceCount -= words[i].length();
                                    		}
                                    		// 词尾空格
                                    		int spaceSuffix = 1;
                                    		// 额外空格的平均值 = 总空格数/间隙数
                                    		int spaceAvg = (wordCount == 1) ? 1 : spaceCount / (wordCount - 1);
                                    		// 额外空格的余数
                                    		int spaceExtra = (wordCount == 1) ? 0 : spaceCount % (wordCount - 1);
                                    		// 填入单词
                                    		StringBuilder sb = new StringBuilder();
                                    		for (int i = bg; i < ed; i++) {
                                    			sb.append(words[i]);
                                    			if (lastLine) {
                                    				sb.append(" ");
                                    				continue;
                                    			}
                                    			int n = spaceSuffix + spaceAvg + (((i - bg) < spaceExtra) ? 1 : 0);
                                    			while (n-- > 0) {
                                    				sb.append(" ");
                                    			}
                                    		}
                                    		// 填入最后一个单词
                                    		sb.append(words[ed]);
                                    		// 补上这一行最后的空格
                                    		int lastSpaceCnt = maxWidth - sb.length();
                                    		while (lastSpaceCnt -- > 0){
                                    			sb.append(" ");
                                    		}
                                    		return sb.toString();
                                    	}
                                    }
                                    public List<String> fullJustify(String[] words, int maxWidth) {
                                        List<String> ans = new ArrayList<>();
                                        dfs(0, 0, 0, words, maxWidth, ans);
                                        return ans;
                                    }
                                    
                                    public void dfs(int left, int right, int strLen, String[] words, int maxWidth, List<String> ans){
                                        if (right == words.length) {
                                            StringBuilder line = new StringBuilder();
                                            for (int i = left; i < right; i++) {
                                                if (i == left) {
                                                    line.append(words[i]);
                                                } else {
                                                    line.append(" ");
                                                    line.append(words[i]);
                                                }
                                            }
                                    
                                            int leftSpace = maxWidth - line.length();
                                            while (leftSpace > 0) {
                                                line.append(" ");
                                                leftSpace--;
                                            }
                                            ans.add(line.toString());
                                            return;
                                        }
                                    
                                        if (right == left) {
                                            strLen = words[right].length();
                                        } else {
                                            strLen += words[right].length() + 1;
                                        }
                                    
                                        if (strLen <= maxWidth) {
                                            right += 1;
                                            dfs(left, right, strLen, words, maxWidth, ans);
                                        } else {
                                            int spaceNum = maxWidth - (strLen - words[right].length()) + right - left;
                                            StringBuilder sb = new StringBuilder();
                                            for (int i = left; i < right; i++) {
                                                if (i == left) {
                                                    sb.append(words[i]);
                                                    if (right - left == 1) {
                                                        int leftSpace = maxWidth - sb.length();
                                                        while (leftSpace > 0) {
                                                            sb.append(" ");
                                                            leftSpace--;
                                                        }
                                                    }
                                                } else {
                                                    int spaceN = (int) Math.ceil(spaceNum * 1.0 / (right - i));
                                                    while (spaceN > 0) {
                                                        sb.append(" ");
                                                        spaceN--;
                                                        spaceNum--;
                                                    }
                                                    sb.append(words[i]);
                                                }
                                            }
                                            ans.add(sb.toString());
                                            left = right;
                                            dfs(left, right, 0, words, maxWidth, ans);
                                        }
                                    
                                    }

                                    评论
                                    添加红包

                                    请填写红包祝福语或标题

                                    红包个数最小为10个

                                    红包金额最低5元

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

                                    抵扣说明:

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

                                    余额充值