Medium

题目描述

给你一个下标从 0 开始的正整数数组 nums

在一次操作中,你可以交换任意两个相邻元素,前提是它们有相同的 置位数。你可以执行这个操作任意次数(包括零次)。

如果你可以使数组按 升序 排列,返回 true;否则返回 false

示例 1:

输入:nums = [8,4,2,30,15]
输出:true
解释:让我们看每个元素的二进制表示。数字 2、4 和 8 分别有 1 个置位,二进制表示分别为 "10"、"100" 和 "1000"。数字 15 和 30 分别有 4 个置位,二进制表示分别为 "1111" 和 "11110"。
我们可以通过 4 次操作对数组进行排序:
- 交换 nums[0] 和 nums[1]。这个操作有效,因为 8 和 4 都有 1 个置位。数组变为 [4,8,2,30,15]。
- 交换 nums[1] 和 nums[2]。这个操作有效,因为 8 和 2 都有 1 个置位。数组变为 [4,2,8,30,15]。
- 交换 nums[0] 和 nums[1]。这个操作有效,因为 4 和 2 都有 1 个置位。数组变为 [2,4,8,30,15]。
- 交换 nums[3] 和 nums[4]。这个操作有效,因为 30 和 15 都有 4 个置位。数组变为 [2,4,8,15,30]。
数组已经排序,所以我们返回 true。

示例 2:

输入:nums = [1,2,3,4,5]
输出:true
解释:数组已经排序,所以我们返回 true。

示例 3:

输入:nums = [3,16,8,4,2]
输出:false
解释:可以证明无法使用任意次数的操作对输入数组进行排序。

提示:

  • 1 <= nums.length <= 100
  • 1 <= nums[i] <= 2^8

算法提示:

  • 将数组分成若干段,每段包含连续的具有相同置位数的元素。
  • 从左到右,前一段的最大元素应该小于当前段的最小元素。

解题思路

解题思路

这道题的核心是理解置位数相同的相邻元素可以自由交换这一规则。基于这个规则,我们可以推导出一个重要结论:

关键观察:由于只能交换相邻且置位数相同的元素,整个数组实际上被分割成若干个"段",每个段内的元素都有相同的置位数。段内的元素可以通过相邻交换达到任意顺序,但不同段之间的元素无法跨越。

算法思路

  1. 分段处理:将数组按置位数相同的连续元素分组
  2. 段内排序:每个段内部可以达到完全有序状态
  3. 段间约束:前一段的最大值必须 ≤ 后一段的最小值

具体步骤

  • 遍历数组,对于每个连续的相同置位数段,记录该段的最小值和最大值
  • 检查相邻段之间是否满足:前段最大值 ≤ 后段最小值
  • 如果所有段都满足此条件,则数组可以排序

这种方法的时间复杂度为O(n),空间复杂度为O(1),是最优解法。

代码实现

class Solution {
public:
    bool canSortArray(vector<int>& nums) {
        int prevMax = 0;
        int curMin = nums[0], curMax = nums[0];
        int prevBits = __builtin_popcount(nums[0]);
        
        for (int i = 1; i < nums.size(); i++) {
            int curBits = __builtin_popcount(nums[i]);
            
            if (curBits == prevBits) {
                curMin = min(curMin, nums[i]);
                curMax = max(curMax, nums[i]);
            } else {
                if (prevMax > curMin) return false;
                prevMax = curMax;
                curMin = curMax = nums[i];
                prevBits = curBits;
            }
        }
        
        return prevMax <= curMin;
    }
};
class Solution:
    def canSortArray(self, nums: List[int]) -> bool:
        prev_max = 0
        cur_min = cur_max = nums[0]
        prev_bits = bin(nums[0]).count('1')
        
        for i in range(1, len(nums)):
            cur_bits = bin(nums[i]).count('1')
            
            if cur_bits == prev_bits:
                cur_min = min(cur_min, nums[i])
                cur_max = max(cur_max, nums[i])
            else:
                if prev_max > cur_min:
                    return False
                prev_max = cur_max
                cur_min = cur_max = nums[i]
                prev_bits = cur_bits
        
        return prev_max <= cur_min
public class Solution {
    public bool CanSortArray(int[] nums) {
        int prevMax = 0;
        int curMin = nums[0], curMax = nums[0];
        int prevBits = CountBits(nums[0]);
        
        for (int i = 1; i < nums.Length; i++) {
            int curBits = CountBits(nums[i]);
            
            if (curBits == prevBits) {
                curMin = Math.Min(curMin, nums[i]);
                curMax = Math.Max(curMax, nums[i]);
            } else {
                if (prevMax > curMin) return false;
                prevMax = curMax;
                curMin = curMax = nums[i];
                prevBits = curBits;
            }
        }
        
        return prevMax <= curMin;
    }
    
    private int CountBits(int n) {
        int count = 0;
        while (n > 0) {
            count += n & 1;
            n >>= 1;
        }
        return count;
    }
}
var canSortArray = function(nums) {
    const countBits = (n) => {
        let count = 0;
        while (n) {
            count += n & 1;
            n >>= 1;
        }
        return count;
    };
    
    const groups = [];
    let currentGroup = [nums[0]];
    let currentBitCount = countBits(nums[0]);
    
    for (let i = 1; i < nums.length; i++) {
        const bitCount = countBits(nums[i]);
        if (bitCount === currentBitCount) {
            currentGroup.push(nums[i]);
        } else {
            groups.push(currentGroup.sort((a, b) => a - b));
            currentGroup = [nums[i]];
            currentBitCount = bitCount;
        }
    }
    groups.push(currentGroup.sort((a, b) => a - b));
    
    for (let i = 1; i < groups.length; i++) {
        if (groups[i-1][groups[i-1].length - 1] > groups[i][0]) {
            return false;
        }
    }
    
    return true;
};

复杂度分析

复杂度类型复杂度说明
时间复杂度O(n)只需要遍历数组一次,计算置位数的时间为常数
空间复杂度O(1)只使用了常数额外空间存储段的最值信息

相关题目