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 <= 1001 <= nums[i] <= 2^8
算法提示:
- 将数组分成若干段,每段包含连续的具有相同置位数的元素。
- 从左到右,前一段的最大元素应该小于当前段的最小元素。
解题思路
解题思路
这道题的核心是理解置位数相同的相邻元素可以自由交换这一规则。基于这个规则,我们可以推导出一个重要结论:
关键观察:由于只能交换相邻且置位数相同的元素,整个数组实际上被分割成若干个"段",每个段内的元素都有相同的置位数。段内的元素可以通过相邻交换达到任意顺序,但不同段之间的元素无法跨越。
算法思路:
- 分段处理:将数组按置位数相同的连续元素分组
- 段内排序:每个段内部可以达到完全有序状态
- 段间约束:前一段的最大值必须 ≤ 后一段的最小值
具体步骤:
- 遍历数组,对于每个连续的相同置位数段,记录该段的最小值和最大值
- 检查相邻段之间是否满足:前段最大值 ≤ 后段最小值
- 如果所有段都满足此条件,则数组可以排序
这种方法的时间复杂度为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) | 只使用了常数额外空间存储段的最值信息 |