刚刚写了bucket sort,就是利用下标来排序。而这里的一些问题,是用下标来实现别的功能的,比如“标记”,标记某个元素是否出现过。
注意一个区别,这类问题是:利用“下标”来记录“元素值本身”,而不是利用“下标”来记录“下标”(参考73.Set Matrix Zeroes,我原来以为73属于这一类其实不是)。
题目 | 简介 |
---|---|
442. Find All Duplicates in an Array | index-as-mark |
448. Find All Numbers Disappeared in an Array | index-as-mark |
41. First Missing Positive | cyclic swapping |
765. Couples Holding Hands | cyclic swapping(另外一篇写) |
442和448是使用下标标记,需要原数组有一个特性,比如“都大于零”,或者比如“都在-10~10范围内”。这样“标记”操作可以用“取负数”,或者“乘以11”来实现,被标记的元素可以完全和原来元素区别开来。
41和765都是cyclic swapping。41其实也是利用下标来交换元素,然而41和765被分类为一个神奇方法,于是另外开一篇写。
442. Find All Duplicates in an Array
一个数组,每个元素1 ≤ a[i] ≤ n (n = size of array), 有些出现两次,有些一次。求在 [1, n] 范围内哪些数没有再数组中出现。
Input:[4,3,2,7,8,2,3,1],Output:[2,3]
把每个数在“正数数列”中应该在的下标处的数翻转(取负数),不关心那个位置上的数本身的值,只是借用哪个下标而已。然而也不是完全不关心啦,在翻转之前先看看它是否已经被翻转过,如果已经被翻转过,则,bingo!找到一个duplicate!
原数组:[4,3,2,7,8,2,3,1]
正数数列:[1,2,3,4,5,6,7,8]
class Solution {
public List<Integer> findDuplicates(int[] nums) {
// when find a number i, flip the number at position i-1 to negative.
// if the number at position i-1 is already negative, i is the number that occurs twice.
List<Integer> res = new ArrayList<>();
for (int i = 0; i < nums.length; ++i) {
int index = Math.abs(nums[i]) - 1;//在正数数列中要翻转的下标
if (nums[index] < 0) {//之前标记过(这是第二次出现)
res.add(Math.abs(index + 1));
}
nums[index] = -nums[index];//翻转
}
return res;
}
}
448. Find All Numbers Disappeared in an Array
一个数组,每个元素1 ≤ a[i] ≤ n (n = size of array), 有些出现两次,有些一次。求在 [1, n] 范围内哪些数没有再数组中出现。
Input:[4,3,2,7,8,2,3,1],Output:[5,6]
基本思路和442一样,唯一区别是不能在第一遍扫的过程中就确定哪个disappear,而是要全扫完之后再来一遍,找到没被标记过的。
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> result = new ArrayList<>();
for (int i = 0; i < nums.length; ++i) {
int index = Math.abs(nums[i]) - 1;
if (nums[index] > 0) {
nums[index] = -nums[index];
}
}
for (int i = 0; i < nums.length; ++i) {
if (nums[i] > 0) {
result.add(i + 1);
}
}
return result;
}
}
41. First Missing Positive
Input: [3,4,-1,1] Output: 2。找到第一个缺失的正数。
举个🌰:
nums = [3,4,-1,1] -->现有的数组
nums = [1,2,3,4] -->连续正数数组
cyclic swapping的过程:
- i=0,nums[0]的数3不在正确的位置上,应该换到它应该待的位置:nums[2],[-1,4,3,1] ,换过来的新的-1不是正数,于是i++
- i=1,nums[1]的数4不在正确的位置上,应该换到它应该待的位置:nums[3],[-1,1,3,4] ,换过来的新数1是正数,且不在正确的位置上,于是i并不自增,下一轮还是i=1
- i=1, nums[1]的数1不在正确的位置上,应该换到它应该待的位置:nums[0],[1,-1,3,4] ,换过来的新的-1不是正数,于是i++
- i=2,nums[2]的数在正确的位置上
- i=3,nums[3]的数在正确的位置上。结束。
为什么这个过程可以保证不漏掉任何一位呢?因为我们的i是一位一位往后捋的,一定要保证当前位置的值或者1)是负数,或者2)在正确的位置上,否者不会i++的。
class Solution {
public int firstMissingPositive(int[] nums) {
int len = nums.length;
//swap each num to its correct position
for (int i = 0; i < len; i++) {
while (nums[i] >= 1 && nums[i] <= len && nums[nums[i]-1] != nums[i]) {
//swap nums[i] <==> nums[nums[i]-1]
int temp = nums[nums[i]-1]; //save nums[nums[i]-1] first!!
nums[nums[i]-1] = nums[i];
nums[i] = temp;
}
}
//check first missing num
for (int i = 0; i < len; i++) {
if (nums[i] != i+1) {
return i+1;
}
}
return len + 1;
}
}
765. Couples Holding Hands
Input: row = [0, 2, 1, 3],Output: 1
Explanation: We only need to swap the second (row[1]) and third (row[2]) person. 我们认为(0,1),(2,3),…是couple,求最少的swap使得每对couple都挨着。
本来我觉得41.first missing positive和前面442,448类似,都是用下标来标记,或者找与当前值val相对应的下标nums[val-1]的那个元素进行交换。总之我感觉差不多。
这个题765和41都被归类为cyclic swapping,貌似还挺复杂的,(为啥41这么简单呵呵)于是决定另外开一篇单独写cyclic swapping好啦。