简体   繁体   English

C#中两个整数元组的总和

[英]Sum of two integers Tuple in C#

I am trying to solve this question just as a preparation for a jobb interview: Write a function that, given a list and a target sum, returns zero-based indices of any two distinct elements whose sum is equal to the target sum. 我正在尝试解决这个问题,就像为求职面试做准备一样:编写一个函数,给定一个列表和一个目标总和,返回任何两个不同元素的从零开始的索引,其总和等于目标总和。 If there are no such elements, the function should return null. 如果没有这样的元素,则该函数应返回null。

For example, FindTwoSum(new List() { 1, 3, 5, 7, 9 }, 12) should return any of the following tuples of indices: 例如,FindTwoSum(new List(){1,3,5,7,9},12)应该返回以下任何索引元组:

1, 4 (3 + 9 = 12) 1,4(3 + 9 = 12)

2, 3 (5 + 7 = 12) 2,3(5 + 7 = 12)

3, 2 (7 + 5 = 12) 3,2(7 + 5 = 12)

4, 1 (9 + 3 = 12) 4,1(9 + 3 = 12)

Here is what i have done so far 这是我到目前为止所做的

public static Tuple<int, int> FindTwoSum(List<int> list, int sum)
    {
        List<Tuple<int, int>> ListOfInt = new List<Tuple<int, int>>();
        for (int i=0; i<list.Count; i++)
        {
            for(int j=0; j<list.Count; j++)
            {
                if (list[i] + list[j] == sum)
                {
                    ListOfInt.Add(new Tuple<int, int>(i,j));
                }
            }
        }

        foreach ( var elemt in ListOfInt)
        {
            return elemt;
        }
        return null;

    }

The problem is that all results are found and saved in the Tuple: but i still cannot print out the result to the console. 问题是找到所有结果并保存在元组中:但我仍然无法将结果打印到控制台。 I think there is somthing wrong at the foreach statement. 我认为foreach陈述有些不对劲。 In the main method i am writing following to print the result to the console: 在我编写的主要方法中,将结果打印到控制台:

Console.WriteLine(FindTwoSum(new List<int>() { 1, 3, 5, 7, 9 }, 12));

Any recommendations please :)? 请问任何建议:)?

Well, if a + b = sum then b + a = sum , so you can cut the inner loop and return two pairs at once ; 好吧,如果a + b = sum然后b + a = sum ,那么你可以切割内部循环并同时返回两对 ; another issue and counter example for your current implementation is that a + a = sum doesn't count, eg 您当前实现的另一个问题和反例a + a = sum不计算,例如

  {3, 6, 9}, 12

should return only 应该只返回

  0, 2 // 3 + 9
  2, 0 // 9 + 3

and not 并不是

  1, 1 // 6 + 6 is wrong

I'd rather implement the solution returning IEnumerable<Tuple<int, int>> : 我宁愿实现返回IEnumerable<Tuple<int, int>>的解决方案:

  // Are you given List<int>? What about int[]? IEnumerable<int> is a much better choice 
  public static IEnumerable<Tuple<int, int>> FindTwoSum(IEnumerable<int> items, int sum) {
    // Validate Arguments (the method is public one!)
    if (null == items)
      throw new ArgumentNullException("items");

    var list = items.ToList();

    for (int i = 0; i < list.Count - 1; ++i)   // last line doesn't count
      for (int j = i + 1; j < list.Count; ++j) // note j = i + 1
        // if ((list[i] + list[j] == sum) && (list[i] != list[j])) { // distinct values and indexes
        if (list[i] + list[j] == sum) { // distinct indexes only
          yield return new Tuple<int, int>(i, j);
          yield return new Tuple<int, int>(j, i);                
        }
  }

In case of you want distinct values as well as distinct indexes the condition instead of 如果你想要不同的值以及不同的索引而不是

  if (list[i] + list[j] == sum)

should be 应该

  if ((list[i] + list[j] == sum) && (list[i] != list[j]))

Distinct values , but not indexes is not a case, since a[i] == a[i] so whenever indexes are not distinct neither the values. 不同的 ,但不是索引不是一种情况,因为a[i] == a[i]所以只要索引不区分值。 And we have the condition 我们有条件

  if ((list[i] + list[j] == sum) && (list[i] != list[j]))

Test: 测试:

  // note that I can pass an array (int[]) or list (List<int>) whatever collection
  String report = String.Join(Environment.NewLine, 
    FindTwoSum(new int[] { 1, 3, 5, 7, 9 }, 12));

  // (1, 4)
  // (4, 1)
  // (2, 3)
  // (3, 2)
  Console.Write(report);

Change the return value of your method to IEnumerable<Tuple<int, int>> , then use yield return to return the value. 将方法的返回值更改为IEnumerable<Tuple<int, int>> ,然后使用yield return返回值。 You'll get the value when you enumerate the result in a loop (in your Main() method). 当您在循环中枚举结果时(在Main()方法中),您将获得该值。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication13
{
    class Program
    {
        static void Main(string[] args)
        {
            var output = FindTwoSum(new List<int>() { 1, 3, 5, 7, 9 }, 12);
            foreach (var x in output)
                Console.WriteLine(x.Item1 + " " + x.Item2);

            Console.ReadKey(true);
        }

        public static IEnumerable<Tuple<int, int>> FindTwoSum(List<int> list, int sum)
        {
            List<Tuple<int, int>> ListOfInt = new List<Tuple<int, int>>();
            for (int i = 0; i < list.Count; i++)
            {
                for (int j = 0; j < list.Count; j++)
                {
                    if (list[i] + list[j] == sum)
                    {
                        ListOfInt.Add(new Tuple<int, int>(i, j));
                    }
                }
            }

            foreach (var elemt in ListOfInt)
            {
                yield return elemt;
            }

        }
    }
}

I noticed a potential problem in your example, given the wording of the question. 考虑到问题的措辞,我注意到你的例子中存在一个潜在的问题。 You mention distinct elements, but you seem to take no steps to ensure the values you are using are distinct. 您提到了不同的元素,但您似乎没有采取任何措施来确保您使用的值是不同的。 You also appear to be able to sum the same element (i ==j)!. 您似乎也能够对相同的元素求和(i == j)!

The solution below shows how you could avoid the problems above, as well as avoiding having to put elements in a list as you go: 下面的解决方案展示了如何避免上述问题,以及避免在出现时将元素放入列表中:

public static Tuple<int, int> FindTwoSum(int[] collection, int sum)
{
    int[] distinct = collection.Distinct().ToArray();
    for (int x = 0; x < distinct.Length; x++)
    {
        for (int y = 0; y < distinct.Length; y++)
        {
            if (y != x && distinct[x] + distinct[y] == sum)
                return Tuple.Create(Array.IndexOf(collection, distinct[x]), Array.IndexOf(collection, distinct[y]));
        }
    }
    return null;
}

You could then print this result using: 然后您可以使用以下方式打印此结果

Tuple<int, int> result = FindTwoSum(new[] { 1,3, 5, 7, 9}, 12);
if (result != null)
    Console.WriteLine(result.Item1 + "," + result.Item2);
else
    Console.WriteLine("No solution!");

If you want to get fancy, you can use Linq: 如果你想获得幻想,你可以使用Linq:

var numbers = new List<int> {1, 3, 5, 7, 9};

int target = 12;
var items = numbers.Select((value, index) => new {index, value});

var hits = 
    from a in items
    from b in items
    where (a.index != b.index) && (a.value + b.value == target)
    select new {index1 = a.index, index2 = b.index};

Console.WriteLine(string.Join("\n", hits.Select(x => 
    $"{x.index1}, {x.index2} ({numbers[x.index1]} + {numbers[x.index2]} = {target})")));

How it works 这个怎么运作

This firstly uses numbers.Select((value, index) => new {index, value}); 这首先使用数字。选择numbers.Select((value, index) => new {index, value}); to convert a list of numbers into a sequence of [value, index] tuples. 将数字列表转换为[value,index]元组的序列。

Then it effectively does a double-loop over that sequence of tuples by doing 然后它通过这样做有效地对该元组序列进行双循环

from a in items
from b in items

which produces all combinations of items from the sequence of tuples. 它产生元组序列中所有项目的组合。

Then it uses where to filter out all the items which have the same index (using (a.index != b.index) and also to determine which sums match the target value (using (a.value + b.value == target) ). 然后它使用where过滤掉所有具有相同索引的项目(使用(a.index != b.index)并确定哪些总和与目标值匹配(使用(a.value + b.value == target) )。

Next it selects the results into a new tuple with properties index1 and index2 . 接下来,它将结果选择为具有属性index1index2

Finally it uses string.Join() to output the results from that tuple. 最后,它使用string.Join()从该元组输出结果。

This section will return as soon as it finds the first element in the List. 一旦找到List中的第一个元素,此部分将立即返回。

    foreach ( var elemt in ListOfInt)
    {
        return elemt;
    }

You need to return List<Tuple<int, int>> and then in your consuming code loop through printing out Item1 and Item2 您需要返回List<Tuple<int, int>> ,然后在您的消费代码循环中打印出Item1Item2

I'm one of those LINQish fans. 我是那些LINQish粉丝之一。 So I just wrote the function with LINQ methods: 所以我刚用LINQ方法编写了函数:

private static IEnumerable<Tuple<int, int>> FindTwoSum(IEnumerable<int> candidates, int expectedSum)
{
    return candidates.SelectMany((Lhs, LhsIndex) => candidates.Select((Rhs, RhsIndex) => new { Rhs, RhsIndex }).Where(r => r.RhsIndex != LhsIndex).Select(r => new { Lhs, r.Rhs }))
                        .Where(pair => pair.Lhs + pair.Rhs == expectedSum)
                        .Select(pair => Tuple.Create(pair.Lhs, pair.Rhs));
}

The hard part is to create all pairings in the first step. 困难的部分是在第一步中创建所有配对。 I simply get for each value additionally the index and then create pairs for all combinations where the index are different. 我只是为索引获取每个值,然后为索引不同的所有组合创建对。 After that it gets quite easy to simply check if they sum up to the expected value and create the desired tuples for the output. 之后,可以很容易地简单地检查它们是否总结到预期值并为输出创建所需的元组。

To call this method you simply use: 要调用此方法,您只需使用:

var results = FindTwoSum(new[] { 1, 3, 5, 7, 9 }, 12);

foreach (var tuple in results)
{
    Console.WriteLine(tuple);
}

This loop: 这个循环:

    foreach ( var elemt in ListOfInt)
    {
        return elemt;
    }

will never "loop" back. 永远不会“循环”回来。 In the first iteration, if there is a first element in the list, it will return which exits and abandons the loop. 在第一次迭代中,如果列表中有第一个元素,它将return退出并放弃循环。

If you really just want the first good pair, get rid of the List<> : 如果你真的只想要第一个好的一对,那就去掉List<>

public static Tuple<int, int> FindTwoSum(List<int> list, int sum)
{
    for (int i=0; i<list.Count; i++)
    {
        for(int j=0; j<i; j++)
        {
            if (list[i] + list[j] == sum) // may want to append:   && list[i] != list[j]
            {
                return Tuple.Create(j, i);
            }
        }
    }
    return null;
}

Do you want to allow i == j in the returned tuple? 你想在返回的元组中允许i == j吗? (Edit: Changed my own code to disallow that.) You do not have to search both i > j and i < j , so choose one of them (as I did above). (编辑:改变我自己的代码以禁止它。)你不必同时搜索i > ji < j ,所以选择其中一个(如上所述)。

You can use Tuple.Create when you want to make a new Tuple<,> . 当你想要创建一个新的Tuple<,>时,可以使用Tuple.Create

To read the values form a Tuple, you call .Item1, .Item2, etc. 要从元组中读取值,可以调用.Item1,.Item2等。

var myTuple = new Tuple<int, string, string>(123, "Cat", "Dog");

var id = myTuple.Item1;
var pet1 = myTuple.Item2;
var pet2 = myTuple.Item3;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM