20221008 - 870. Advantage Shuffle 优势洗牌(田忌赛马)

116 阅读3分钟

You are given two integer arrays nums1 and nums2 both of the same length. The advantage of nums1 with respect to nums2 is the number of indices i for which nums1[i] > nums2[i].

Return any permutation of nums1 that maximizes its advantage with respect to nums2.

Example 1

Input: nums1 = [2,7,11,15], nums2 = [1,10,4,11]
Output: [2,11,7,15]

Example 2

Input: nums1 = [12,24,8,32], nums2 = [13,25,32,11]
Output: [24,32,8,12]

Constraints

  • 1 <= nums1.length <= 10e5
  • nums2.length == nums1.length
  • 0 <= nums1[i], nums2[i] <= 10e9

Soution

一个超时的朴素田忌赛马:比较 nums1 和 nums2 的最小元素,nums1 大就对应;nums1 小就拿去和 nums2 最大的对应。

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int findMax(int* nums, int numsSize){
    int i, max;
    for (i = 0; i < numsSize; i++) {
        if (nums[i] != -1) {
            max = i;
            break;
        }
    }
    for (i = 0; i < numsSize; i++)
        if (nums[i] != -1 && nums[i] > nums[max]) max = i;
    return max;
}

int findMin(int* nums, int numsSize){
    int i, min;
    for (i = 0; i < numsSize; i++) {
        if (nums[i] != -1) {
            min = i;
            break;
        }
    }
    for (i = 0; i < numsSize; i++)
        if (nums[i] != -1 && nums[i] < nums[min]) min = i;
    return min;
}

int* advantageCount(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){
    int i, n, mina, minb, maxa, maxb;
    n = nums1Size;
    int *ans = (int*)malloc(sizeof(int) * n);
    int *a = (int*)malloc(sizeof(int) * n);
    int *b = (int*)malloc(sizeof(int) * n);
    for (i = 0; i < n; i++) {
        a[i] = nums1[i];
        b[i] = nums2[i];
    }
    for (i = 0; i < n; i++) {
        mina = findMin(a, n);
        minb = findMin(b, n);
        if (a[mina] > b[minb]) {
            ans[minb] = a[mina];
            a[mina] = -1;
            b[minb] = -1;
        } else {
            maxb = findMax(b, n);
            ans[maxb] = a[mina];
            a[mina] = -1;
            b[maxb] = -1;
        }
    }
    *returnSize = n;
    return ans;
}

先快速排序后的优化算法:

思路与算法

我们首先分别将数组 nums1 和 num2 进行排序,随后只需要不断考虑这两个数组的首个元素:

  • 如果 nums 1 的首个元素大于 nums2 的首个元素,那么就将它们在答案中对应起来,同时从数组中移除这两个元素,并增加一点「优势」;

  • 如果 nums1 的首个元素小于等于 nums2 的首个元素,那么移除 nums1 的首个元素。

当 nums1 中没有元素时,遍历结束。

这样做的正确性在于:

  • 对于第一种情况,由于 nums1 是有序的,那么 nums1的任意元素大于 nums2 的首个元素:

    • 如果我们不与 nums2 的首个元素配对,由于 nums2 是有序的,之后的元素会更大,这样并不划算;
    • 如果我们与 nums2 的首个元素配对,我们使用 nums1 的首个元素,可以使得剩余的元素尽可能大,之后可以获得更多「优势」。
  • 对于第二种情况,由于 nums2 是有序的,那么 nums1 的首个元素小于等于 nums2 中的任意元素,因此 nums1 的首个元素无法增加任何「优势」,可以直接移除。

在本题中,由于 num1 中的每一个元素都要与 nums2 中的元素配对,而我们是按照顺序考虑 nums2 中的元素的。因此在遍历结束后,nums2 中剩余的元素实际上是原先 nums2 的一个后缀。因此当 nums1 的首个元素无法配对时,我们给它配对一个 nums2 的尾元素即可,并将该尾元素移除。

在实际的代码编写中,我们无需真正地「移除」元素。对于 nums1,我们使用一个循环依次遍历其中的每个元素;对于 nums2 ,我们可以使用双指针 left 和 right 。如果 nums1 的首个元素可以增加「优势」,就配对 left 对应的元素并向右移动一个位置;如果无法配对,就配对 right 对应的元素并向左移动一个位置。

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
static int cmp(const void *pa, const void *pb){
    int *a = (int *)pa;
    int *b = (int *)pb;
    return a[1] - b[1];
}//nondecending order

int* advantageCount(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){
    int i, n, left, right;
    n = nums1Size;
    int *ans = (int*)malloc(sizeof(int) * n);
    int a[n][2], b[n][2];
    for (i = 0; i < n; i++) {
        a[i][0] = i; a[i][1] = nums1[i];
        b[i][0] = i; b[i][1] = nums2[i];
    }
    qsort(a, n, sizeof(a[0]), cmp);
    qsort(b, n, sizeof(b[0]), cmp);
    left = 0; right = n - 1;
    for (i = 0; i < n; i++) {
        if (a[i][1] > b[left][1]) {
            ans[b[left][0]] = a[i][1];
            left++;
        } else {
            ans[b[right][0]] = a[i][1];
            right--;
        }
    }
    *returnSize = n;
    return ans;
}

动态分配内存的方法注意 cmp 函数的写法。

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int cmp(const void *pa, const void *pb){
    int *a = *(int **)pa;
    int *b = *(int **)pb;
    return a[1] - b[1];
}//nondecending order

int* advantageCount(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){
    int i, n, left, right;
    n = nums1Size;
    int *ans = (int*)malloc(sizeof(int) * n);
    int **a = (int**)malloc(sizeof(int*) * n);
    int **b = (int**)malloc(sizeof(int*) * n);
    for (i = 0; i < n; i++) {
        a[i] = (int*)malloc(sizeof(int) * 2);
        b[i] = (int*)malloc(sizeof(int) * 2);
        a[i][0] = i; a[i][1] = nums1[i];
        b[i][0] = i; b[i][1] = nums2[i];
    }
    qsort(a, n, sizeof(a[0]), cmp);
    qsort(b, n, sizeof(b[0]), cmp);
    left = 0; right = n - 1;
    for (i = 0; i < n; i++) {
        if (a[i][1] > b[left][1]) {
            ans[b[left][0]] = a[i][1];
            left++;
        } else {
            ans[b[right][0]] = a[i][1];
            right--;
        }
    }
    for (i = 0; i < n; i++) {
        free(a[i]);
        free(b[i]);
    }
    free(a);
    free(b);
    *returnSize = n;
    return ans;
}

题目链接:870. 优势洗牌 - 力扣(LeetCode)