1、最多能完成排序的块

1.1、描述:

给定一个长度为 n 的整数数组 arr ,它表示在 [0, n - 1] 范围内的整数的排列。
我们将 arr 分割成若干 块 (即分区),并对每个块单独排序。将它们连接起来后,使得连接的结果和按升序排序后的原数组相同。
返回数组能分成的最多块数量。

  • 示例: ```java 输入: arr = [4,3,2,1,0] 输出: 1 解释: 将数组分成2块或者更多块,都无法得到所需的结果。 例如,分成 [4, 3], [2, 1, 0] 的结果是 [3, 4, 0, 1, 2],这不是有序的数组。

输入: arr = [1,0,2,3,4] 输出: 4 解释: 我们可以把它分成两块,例如 [1, 0], [2, 3, 4]。 然而,分成 [1, 0], [2], [3], [4] 可以得到最多的块数。 对每个块单独排序后,结果为 [0, 1], [2], [3], [4]

  1. <a name="lxIms"></a>
  2. ## 1.2、个人思路:
  3. > 先将数组排序,然后从数组两边进行遍历,将排序后的结果和原数组从**两边**进行比较,如果相等则返回结果加1,最终返回min(n, max)
  4. - **代码**
  5. ```java
  6. public int maxChunksToSorted(int[] arr) {
  7. int n = arr.length;
  8. int[] b = Arrays.stream(arr).sorted().toArray();
  9. int max = 0;
  10. int front = 0;
  11. int rear = 0;
  12. for (int i = 0; i < n / 2; i++) {
  13. if (arr[i] == b[i]) {
  14. front++;
  15. }
  16. if (arr[n - i - 1] == b[n - i - 1]) {
  17. rear++;
  18. }
  19. }
  20. if ((front == n / 2 || rear == n / 2) ) {
  21. if (n % 2 != 0 && n>1){
  22. if (arr[n / 2 ] == b[n / 2]){
  23. max++;
  24. }
  25. }
  26. }
  27. max = max+front + rear + 1;
  28. return Math.min(max,n);
  29. }
  • 存在的问题:没有注意数字和下标的关系,没有找到应有的数学关系,直接从两边进行计算,不能通过一些特殊实例。

    1.3、官方

  • 思路

定理一:对于某个块 AA,它由块 BB 和块 CC 组成,即 A = B + CA=B+C。如果块 BB 和块 CC 分别排序后都与原数组排序后的结果一致,那么块 AA 排序后与原数组排序后的结果一致。

证明:因为块 BB 和块 CC 分别排序后都与原数组排序后的结果一致,所以块 BB 的元素和块 CC 的元素的并集为原数组排序后在对应区间的所有元素。而 A = B + CA=B+C,因此将块 AA 排序后与原数组排序后的结果一致。

定理二:对于某个块 AA,它由块 BB 和块 CC 组成,即 A = B + CA=B+C。如果块 AA 和块 BB 分别排序后都与原数组排序后的结果一致,那么块 CC 排序后与原数组排序后的结果一致。

证明:如果块 CC 排序后与原数组排序后的结果不一致,那么块 BB 的元素和块 CC 的元素的并集不等同于原数组排序后在对应区间的所有元素。而块 AA 排序后与原数组排序后的结果一致,说明块 AA 的元素等同于原数组排序后在对应区间的所有元素。这与块 AA 的元素由块 BB 的元素和 块 CC 的元素组成矛盾,因此块 CC 排序后与原数组排序后的结果一致。

  • 代码 ```java class Solution { public int maxChunksToSorted(int[] arr) {
    1. int m = 0, res = 0;
    2. //当遍历到第i个位置时,如果可以切分为块,那前i个位置的最大值一定等于i。
    3. //否则,一定有比i小的数划分到后面的块,那块排序后,一定不满足升序。
    4. for (int i = 0; i < arr.length; i++) {
    5. m = Math.max(m, arr[i]);
    6. if (m == i) {
    7. res++;
    8. }
    9. }
    10. return res;
    } }
  1. <a name="sxqpr"></a>
  2. ## 1.4、总结
  3. - 在数组相关的题目时,需要看数组下标和数组值的关系
  4. - 特定题目的数学关系需要注意
  5. <a name="wQL5l"></a>
  6. # 2、使数组唯一的最小增量
  7. <a name="mWmAD"></a>
  8. ## 1.1、描述:
  9. 给你一个整数数组 nums 。每次 move 操作将会选择任意一个满足 0 <= i < nums.length 的下标 i,并将 nums[i] 递增 1。
  10. 返回使 nums 中的每个值都变成唯一的所需要的最少操作次数。
  11. - **示例:**
  12. ```java
  13. 输入:nums = [1,2,2]
  14. 输出:1
  15. 解释:经过一次 move 操作,数组将变为 [1, 2, 3]。
  16. 输入:nums = [3,2,1,2,1,7]
  17. 输出:6
  18. 解释:经过 6 次 move 操作,数组将变为 [3, 4, 1, 2, 5, 7]。
  19. 可以看出 5 次或 5 次以下的 move 操作是不能让数组的每个值唯一的。
  20. 来源:力扣(LeetCode)
  21. 链接:https://leetcode.cn/problems/minimum-increment-to-make-array-unique
  22. 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

1.2、个人思路:

1、创建一个新的数组用以记录每个数字出现的次数 2、遍历这个数组,对出现次数大于0的数字往前探测,然后将这个探测的数字设为一次,并使得记录减一,i—

  • 代码

    1. public int minIncrementForUnique(int[] nums) {
    2. int res=0;
    3. int len= nums.length;
    4. int temp[] = new int[nums.length*3];
    5. int tempLen=temp.length;
    6. for (int i = 0; i < len*3; i++) {
    7. temp[i] = -1;
    8. }
    9. for (int i = 0; i < len; i++) {
    10. temp[nums[i]]++;
    11. }
    12. for (int i = 0; i < tempLen; i++) {
    13. if (temp[i]>0){
    14. for (int j = 1; j < tempLen-i; j++) {
    15. if (temp[i+j]==-1){
    16. res=res+j;
    17. temp[i+j]=0;
    18. temp[i]--;
    19. i--;
    20. break;
    21. }
    22. }
    23. }
    24. }
    25. return res;
    26. }
  • 存在的问题:空间和时间复杂度都太高!!!

    1.3、官方

    力扣

    尤其第三种方法

  • 代码 ```java class Solution { int[] pos = new int [80000]; public int minIncrementForUnique(int[] A) {

    1. Arrays.fill(pos, -1); // -1表示空位
    2. int move = 0;
    3. // 遍历每个数字a对其寻地址得到位置b, b比a的增量就是操作数。
    4. for (int a: A) {
    5. int b = findPos(a);
    6. move += b - a;
    7. }
    8. return move;

    }

    // 线性探测寻址(含路径压缩) private int findPos(int a) {

    1. int b = pos[a];
    2. // 如果a对应的位置pos[a]是空位,直接放入即可。
    3. if (b == -1) {
    4. pos[a] = a;
    5. return a;
    6. }
    7. // 否则向后寻址
    8. // 因为pos[a]中标记了上次寻址得到的空位,因此从pos[a]+1开始寻址就行了(不需要从a+1开始)。
    9. b = findPos(b + 1);
    10. pos[a] = b; // 寻址后的新空位要重新赋值给pos[a]哦,路径压缩就是体现在这里。
    11. return b;

    } }

作者:sweetiee 链接:https://leetcode.cn/problems/minimum-increment-to-make-array-unique/solution/ji-shu-onxian-xing-tan-ce-fa-onpai-xu-onlogn-yi-ya/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 ```

1.4、总结

  • 个人思路和线性探测一致,但线性探测之后的处理不一致,标准解法是直接算出路径,而我的是生成更新数组然后重新遍历。 :::info Arrays.fill(pos, -1); // -1表示空位 :::