寻找具有给定和的数组中的有序对,但数组中有重复值。

huangapple go评论76阅读模式
英文:

find ordered pairs is array with given sum but array has duplicate values

问题

You are trying to find ordered pairs in an array where A[i] + A[j] = q for a given q, without using additional data structures, in O(N) time (excluding sort time) and O(1) space. Your current code works for arrays with unique elements. However, for arrays with duplicate elements like [1,3,3,3], your code won't return the correct result.

To handle arrays with duplicate elements and achieve the desired result, you can modify your code as follows:

function findTuples(arr, q) {
    arr.sort((a, b) => a - b);

    let result = [];
    let i = 0;
    let j = arr.length - 1;

    while (i < j) {
        let sum = arr[i] + arr[j];

        if (sum === q) {
            result.push([arr[i], arr[j]]);
            i++;
            j--;

            // Skip duplicate elements
            while (i < j && arr[i] === arr[i - 1]) {
                i++;
            }

            while (i < j && arr[j] === arr[j + 1]) {
                j--;
            }
        } else if (sum < q) {
            i++;
        } else {
            j--;
        }
    }

    return result;
}

This modified code should correctly handle arrays with duplicate elements and find the ordered pairs in O(N) time and O(1) space, as requested.

英文:

I want to solve the problem: find all ordered pairs in an array where
A[i] + A[j] = q , i != j

for example: array = [1,3,3,3] q = 6
output = [[3,3], [3,3], [3,3], [3,3], [3,3], [3,3]]

I can't use any other data-structure
in O(N) time (not including sort time) and O(1) space.

I have been able to come up with a algorithm for array with unique elements though! here is the code:

function findTuples(arr: number[], q: number): Array&lt;[number, number]&gt; {
    // Sort the array in ascending order
    arr.sort((a, b) =&gt; a - b);

    let low = 0;
    let high = arr.length - 1;
    let result: Array&lt;[number, number]&gt; = [];

    // While low pointer is less than high pointer
    while (low &lt; high) {
        let sum = arr[low] + arr[high];

        // If sum of two elements is equal to q
        if (sum === q) {
            result.push([arr[low], arr[high]]);
            result.push([arr[high], arr[low]]);

            low++;
            high--;
        } else if (sum &lt; q) {
            // If sum is less than q, move low pointer up
            low++;
        } else {
            // If sum is greater than q, move high pointer down
            high--;
        }
    }

    return result;
}

I am trying to solve this using two pointers method but I can't come up with the right algorithm.

is it even possible?!

答案1

得分: 2

以下是翻译好的内容:

要输出的成对数量可能在O(n²)的数量级。不可能设计一个时间复杂度为O(n)的算法来生成大小为O(n²)的输出,除非我们排除用于输出的空间。

然而,如果我们允许输出只列出一个不同的成对组合,但标明它的出现次数,那么O(n²)大小的输出可以减少到O(n)。对于您的示例,这意味着我们可以生成以下输出:

[[[3,3], 6]]

...这意味着[3,3]是一个有6次出现的解决方案。如果我们允许这种(压缩的)输出格式,那么就有可能实现O(n)的时间复杂度。

我们可以使用双指针算法来实现这一点,但在这里我将使用一个哈希映射(`Map`),假设它的(平摊)时间复杂度为O(1)用于获取和设置操作。优点是输入不需要排序就可以工作:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function findTuples(arr, q) {
    const count = new Map;
    const result = [];
    // 收集每个不同值的频率
    for (const value of arr) {
        count.set(value, (count.get(value) ?? 0) + 1);
    }

    for (const [a, countA] of count) {
        // 获取到达总和所需的补充值
        const b = q - a;
        if (a === b) { // 特殊情况
            result.push([[a, a], countA * (countA - 1)]);
        } else { // 所有其他情况
            const countB = count.get(b);
            if (countB) {
                result.push([[a, b], countA * countB]);
            }
        }
    }
    return result;
}

// 问题中的示例输入
const result = findTuples([1,3,3,3], 6);
console.log(result);

// 另一个输入:
const result2 = findTuples([1,2,2,4,4], 6);
console.log(result2);

<!-- end snippet -->

希望这对您有所帮助。

英文:

The number of pairs to output could be in the order of O(n²). It is not possible to design an algorithm with time complexity O(n) that can produce output with size O(n²). And O(1) space complexity isn't possible for the same reasons, unless we exclude the space used for the output.

However, the O(n²) sized output could be reduced to O(n) if we allow the output to list a distinct pair only once, but with an indication how many occurrences there would be for it. For your example this would mean we could produce this output:

[ [[3,3], 6] ]

...meaning that [3,3] is a solution that has 6 occurrences. If we allow for that (compressed) output format, then it is possible to have a time complexity of O(n).

We could use the 2 pointer algorithm for this, but here I will use a hashmap (Map) instead, assuming that it has an (amortised) time complexity of O(1) for get and set operations. The advantage is that the input does not have to be sorted for this to work:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function findTuples(arr, q) {
    const count = new Map;
    const result = [];
    // Collect the frequency for each distinct value
    for (const value of arr) {
        count.set(value, (count.get(value) ?? 0) + 1);
    }

    for (const [a, countA] of count) {
        // Get the complementary value needed to arrive at the sum
        const b = q - a;
        if (a === b) { // Special case
            result.push([[a, a], countA * (countA - 1)]);
        } else { // All other cases
            const countB = count.get(b);
            if (countB) {
                result.push([[a, b], countA * countB]);
            }
        }
    }
    return result;
}

// The example input from the question
const result = findTuples([1,3,3,3], 6);
console.log(result);

// Another input:
const result2 = findTuples([1,2,2,4,4], 6);
console.log(result2);

<!-- end snippet -->

答案2

得分: 1

    function findTuples(arr, q) {
      let result = [];
      for (let i = 1; i < arr.length; i++) {
        if (arr[i - 1] + arr[i] === q)
          result.push([arr[i - 1], arr[i]]);
      }
      return result;
    }

然而问题的要求并不清晰如果需要找到起始数组中所有值的配对即从数组中任意位置获取2个值的组合那么这将不是线性的在这种情况下O(n) 的要求就没有意义

还有这可能是值得讨论的但因为输出是一个新的存储至少在JavaScript中是这样),所以我不确定你能够称其为在空间使用方面是O(1)
英文:

The word "ordered" is a little vague, but if there really is an O(n) solution then the goal must be to step through the array starting at 1 and add that value to the previous value and perform the comparison. If it succeeds, then that pair is added to the list, and the index is incremented.

function findTuples(arr, q) {
  let result = [];
  for (let i = 1; i &lt; arr.length; i++) {
    if (arr[i - 1] + arr[i] === q)
      result.push([arr[i - 1], arr[i]]);
  }
  return result;
}

However the problem requirement is not clear. If it's required to find all pairs of values from the start array, like combinations of 2 values taken from anywhere in the array, then that won't be linear. So in that case the O(n) requirement does not make sense.

Oh, also, and this is probably debatable, but because the output is a new lot of storage (at least in JavaScript), I'm not sure you could call this O(1) in terms of space used.

答案3

得分: 1

使用两个指针。将左指针设置为索引0。使用二分查找放置右指针,找到与值相关联的最大索引,使得arr[left] + arr[right] ≤ target。

维护输出,可以是索引对的列表或计数。

重复执行以下操作:

如果 arr[left] != arr[right]
  如果 arr[left] + arr[right] < target
    增加左指针
  否则,如果 arr[left] + arr[right] > target
    减小右指针
  否则 # arr[left] + arr[right] == target
    计算与 arr[left] 和 arr[right] 相同元素的数量。
    称这些数量为 left_count 和 right_count。至少都为1。
    增加计数 left_count * right_count 或更新索引对的列表以包括所有这样的索引对。
    最后,增加左指针并减小右指针,以便关联值都变化(即每个值的相同数量都会增加)。
否则
  如果 arr[left] + arr[right] == target
    计数 += choose(right - left + 1, 2) # 或更新索引对的列表以反映 left 到 right 之间的所有 2 个索引对
  返回

当 left == right 或者达到 return 语句时停止。

示例:对于目标值 3 的数组 [1, 1, 1, 2, 2]:我们从 left=0 和 right=4 开始。我们在第一个 if 的最后一个 else 语句中,因为 arr[left] + arr[right] == target。由于 left_count = 3,right_count = 2,我们找到了 3*2 = 6 个新的满足目标的索引对,之后我们完成了。如果我们在构建索引对列表,那么它是 [0,1,2] 中的每个元素与 [3,4] 中的每个元素成对。

示例:对于目标值 4 的数组 [1, 2, 2, 2, 3]:我们再次从 left=0 和 right=4 开始,同样在第一个 if 的最后一个 else 语句中。这里 left_count = right_count = 1,所以我们通过 1 增加计数(或添加 (0,4) 到我们的列表),然后 left=1,right=3。现在我们在最后一个 else 语句中,所以将 choose(3,2) 或每对来自 [1,2,3] 的 2 个索引添加到我们的列表。

英文:

Use two pointers. Set the left pointer to index 0. Use binary search to place the right pointer, finding the largest index associated with a value such that arr[left] + arr[right] <= target.

Maintain your output, either a list of pairs of indices, or a count.

Repeatedly do the following:

if arr[left] != arr[right]
  if arr[left] + arr[right] &lt; target
    increment left
  else if arr[left] + arr[right] &gt; target
    decrement right
  else # arr[left] + arr[right] == target
    count the number of identical elts to arr[left] and arr[right]. 
    Call these left_count and right_count. At a minimum these are both 1.
    Increment count by left_count * right_count or update the list of pairs of indices for all such pairs. 
    Finally, increment left and decrement right so both associated values change (i.e. each increments per number of identical values).
else
  if arr[left] + arr[right] == target
    count += choose(right - left + 1, 2) # or update the list of pairs of indices to reflect all pairs of 2 indices between left &amp; right inclusive
  return

Stop when left == right or if we reach the return statement.

Example: [1, 1, 1, 2, 2] for target 3: We start with left=0 and right=4. We're in the final else of the first if, since arr[left] + arr[right] == target. Since left_count = 3 and right_count = 2, we've found 3*2 = 6 new pairs that meet our target, after which we're done. If we're building the list of pairs, it's each of [0,1,2] paired with each of [3,4].

Example: 1, 2, 2, 2, 3 for target 4: We again start with left=0 and right=4, again in the final else of the first iff. Here left_count = right_count = 1, so we increment count by 1 (or add (0,4) to our list) and now left=1 and right=3. Now we're in the final else statement, so add choose(3,2) or every pair of 2 indices from [1,2,3] to our list.

答案4

得分: 1

使用两个指针,在找到 arr[left] + arr[right] == q 时,移动 left 和 right 来找出有多少个相同的数字等于 arr[left] 和 arr[right],然后将这对数字放入结果中。

public List<int[]> findTuples(int[] arr, int q)
{
    List<int[]> result = new LinkedList<int[]>();

    int left = 0;
    int right = arr.length - 1;

    int tempLeft;
    int tempRight;

    while(left <= right && left < arr.length && right >= 0)
    {
        if (arr[left] + arr[right] < q)
        {
            left++;
        }
        else if (arr[left] + arr[right] > q)
        {
            right--;
        }
        else
        {
            tempLeft = left;

            tempRight = right;

            //如果 [left, right] 区间内的数字都相同
            if (arr[left] == arr[right])
            {
                int count = (tempRight - tempLeft + 1) * (tempRight - tempLeft);

                for (int i = 0; i < count; i++)
                {
                    result.add(new int[]{arr[tempLeft], arr[tempRight]});
                }

                break;
            }
            else
            {
                //在左侧和右侧找到相同的数字
                while(left < arr.length && arr[left] == arr[tempLeft]) 
                {
                    left++;
                }

                while(right >= 0 && arr[right] == arr[tempRight])
                {
                    right--;
                }

                //将这些对数字添加到结果中
                for (int i = 0; i < left - tempLeft; i++)
                {
                    for (int j = 0; j < tempRight - right; j++)
                    {
                        result.add(new int[]{arr[tempLeft], arr[tempRight]});
                        result.add(new int[]{arr[tempRight], arr[tempLeft]});
                    }
                }
            }
        }
    }

    return result;
}
英文:

Using two points, when you find arr[left] + arr[right] == q, move left and right to find out how many same number equals arr[left] and arr[right], then put the pair in result.

public List&lt;int[]&gt; findTuples(int[] arr, int q)
{
List&lt;int[]&gt; result = new LinkedList&lt;int[]&gt;();
int left = 0;
int right = arr.length - 1;
int tempLeft;
int tempRight;
while(left &lt;= right &amp;&amp; left &lt; arr.length &amp;&amp; right &gt;= 0)
{
if (arr[left] + arr[right] &lt; q)
{
left++;
}
else if (arr[left] + arr[right] &gt; q)
{
right--;
}
else
{
tempLeft = left;
tempRight = right;
//if [left, right] is all same numbers
if (arr[left] == arr[right])
{
int count = (tempRight- tempLeft + 1)*(tempRight - tempLeft);
for (int i = 0; i &lt; count; i++)
{
result.add(new int[]{arr[tempLeft], arr[tempRight]});
}
break;
}
else
{
//find same number in left and right side
while(left &lt; arr.length &amp;&amp; arr[left] == arr[tempLeft]) 
{
left++;
}
while(right &gt;= 0 &amp;&amp; arr[right] == arr[tempRight])
{
right--;
}
//add the pairs into result
for (int i = 0; i &lt; left - tempLeft; i++)
{
for (int j = 0; j &lt; tempRight - right; j++)
{
result.add(new int[]{arr[tempLeft], arr[tempRight]});
result.add(new int[]{arr[tempRight], arr[tempLeft]});
}
}
}
}
}
return result;
}

答案5

得分: 0

以下是您要翻译的代码部分:

function findOrderedPairs(arr: number[], sum: number) {
    const result: number [][] = [];
    arr.sort((a ,b) => a - b);

    let low = 0, high = arr.length - 1;

    while (low < high) {
        if (arr[low] + arr[high] < sum) {
            low++;
        } else if (arr[low] + arr[high] > sum) {
            high--;
        } else {
            result.push([arr[low], arr[high]]);
            result.push([arr[high], arr[low]]);

            let runner = low + 1;
            while (runner < high && arr[runner] === arr[low]) {
                result.push([arr[runner], arr[high]]);
                result.push([arr[high], arr[runner]]);
                runner++;
            }
            high--;
        }
    }
    return result;
}

console.log(findOrderedPairs([1, 3, 3, 3], 6));
// output: [ [ 3, 3 ], [ 3, 3 ], [ 3, 3 ], [ 3, 3 ], [ 3, 3 ], [ 3, 3 ] ]

希望这对您有所帮助!

英文:

thanks for all the answers! really valuable!
here is the solution I came up with

function findOrderedPairs(arr: number[], sum: number) {
const result: number [][] = [];
arr.sort((a ,b) =&gt; a - b);
let low = 0, high = arr.length - 1;
while (low &lt; high) {
if (arr[low] + arr[high] &lt; sum) {
low++;
} else if (arr[low] + arr[high] &gt; sum) {
high--;
} else {
result.push([arr[low], arr[high]]);
result.push([arr[high], arr[low]]);
let runner = low + 1;
while (runner &lt; high &amp;&amp; arr[runner] === arr[low]) {
result.push([arr[runner], arr[high]]);
result.push([arr[high], arr[runner]]);
runner++;
}
high--;
}
}
return result;
}
console.log(findOrderedPairs([1, 3, 3, 3], 6));
// output: [ [ 3, 3 ], [ 3, 3 ], [ 3, 3 ], [ 3, 3 ], [ 3, 3 ], [ 3, 3 ] ]
</details>

huangapple
  • 本文由 发表于 2023年7月3日 05:04:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/76600801.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定