【问题标题】:I need help to optimize my code speed for Sum 3 problem我需要帮助来优化 Sum 3 问题的代码速度
【发布时间】:2021-01-14 21:49:05
【问题描述】:

谜题说明

*给定一个包含 n 个整数的数组 nums,在 nums 中是否存在元素 a、b、c 使得 a + b + c = 0?在数组中找到所有唯一的三元组,其总和为零。 请注意,解集不得包含重复的三元组。

示例 1: 输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]]

示例 2: 输入:nums = [] 输出:[]

示例 3: 输入:nums = [0] 输出:[]

约束: 0

描述结束

我在通过 leetcode 解决这个难题时遇到了一些问题,我的运行时间超过了时间限制,因此我的代码需要优化。

我知道可以修复的两个位置是双循环条目和检查任何列表是否匹配的循环。

一个额外的问题并没有阻止我通过,但在某些情况下可能会导致此失败是检查列表以查看它是否包含距离并且不等于自 IndexOf 以来的第一次或第二次迭代将返回找到的第一个索引。

至于循环检查,我在 python 中找到了一个更快的解决方案,但我不确定 c# equivilent

Python代码查找是否是一个列表

存在于列表列表中。

# Input List Initialization 
Input = [[1, 1, 1, 2], [2, 3, 4], [1, 2, 3], [4, 5, 6]] 
  
# List to be searched 
list_search = [1, 1, 1, 2] 
  
# Using in to find whether  
# list exists or not 
if list_search in Input: 
    print("True") 
else: 
    print("False") 

这是我当前的代码解决方案(TLDR:它可以工作,但太慢了)

public class Solution {
    public IList<IList<int>> ThreeSum(int[] nums) {
        
        /*total must always = 0
        ideas to use a sort on each list to check if there are
        multiple arrays that are the same combination of values
        
        the last sum can be assumed and pulled via Arraylist since there can only be one answer assumed
        values can be quickly ignored if there is no corrisponding 3rd value that sums to 0 
        ex -1,-1, do we have a value of 2 if not than we can ignore this combination
        also if we do than we will want to temporarly store it than sort the list and compare to our already existing
        list
        
        Something to think about if the numbs array is sorted ahead of time can we ignore using the sort operation
        in some conditions to save cpu time
        
        If the size of the nums array is less than 3 than return a blank array
        also if the size of the array = 3 we can assume the solution and return nums (maybe)
        
        A possible way to make this faster would be to find a way to assume all possible values have been found or use a Addoku/Arrow-Sudoku method of solving and see if for example a value in the array is 9 and there are no negative values in a set of 2 that can = -9 than 9 can be instantly ignored and removed since the combination is not possible.
        
        I dont think the index check is perfect since indexof checks for the first value but it has been passing all of my test cases so I will leave it for now but I will need to find a better way to remove I and Y elements from the index check to avoid duplicates.
        */
        
        IList<IList<int>> mainLs = new List<IList<int>>();
        if(nums.Length < 3){
            return mainLs;
        }
        
        
        for(int i = 0; i< nums.Length; i++){
            int currentVal = nums[i];
           for(int y = 0; y< nums.Length; y++){
               //is there an easier way to skip the matching index
               if(i == y){
                   continue;
               }else{
                   /*
                   We will sum the two elements together and check the distance
                   between the sum and 0. Than if we have the distance in our index and the value to sum to 0
                   is not the index we are on since we need to avoid duplicates than add that value to the array
                   */
                   ArrayList tempNumls = new ArrayList(nums);
                 
                  
                   int sum = currentVal + nums[y];
                   int distance = Math.Abs(sum - 0);
                   //Math abs always produces a postive value which will work for us if the sum is negative but if the sum is positive it will return a positive value so we multiply by -1 to produce the proper result.
                   if(sum > 0){
                       distance = distance * -1;
                   }
                   //output the results found
                   // Console.WriteLine("distance: "+distance + " X & Y = "+ currentVal + " & " + nums[y]);
                   
                   if(tempNumls.Contains(distance) && tempNumls.IndexOf(distance) != y && tempNumls.IndexOf(distance) != i){
                   
                       ArrayList tempIls = new ArrayList() {currentVal, nums[y], distance};
                       //sorting and comparing will be the easiest way to make sure we do not have matching lists
                       tempIls.Sort();
                       var list = tempIls.Cast<int>().ToList();
                       bool match = false;
                       //best to find a better way to traverse and compare the list of list without 
                       //needing to loop over them all
                       foreach (List<int> listname in mainLs)
                    {
                          if (listname.SequenceEqual(list))
                             {
                                 match = true; 
                                break;
                             }
                    }
                       if(!match){
                       mainLs.Add(list);
                       }
                       
                   }
               }
           }
        }
        
        return mainLs;
    }
}

【问题讨论】:

  • 您知道ContainsIndexOfSequenceEqualToList 都包含自己的循环吗?
  • 旁注:请避免使用ArrayList 将代码发布到其他人看到的位置,例如 SO - 如果不偏离问题,它没有任何用处。见stackoverflow.com/questions/2309694/…
  • 代码中也没有DictionaryHashSet...因此,虽然起点很好,但无法接受此代码作为答案。但在进入竞争之前先让它运行。
  • @AlexeiLevenkov 谢谢,我刚刚回到 c# 我不确定要使用什么容器我只是想要一些可以排序并检查包含的东西我会切换它们。另外,您如何建议我在我的解决方案中加入 Dictionary 和 Hashset?
  • @madreflection 好点,这对我来说可能是最好的做法,看看我可以在哪里删除或替换其中的一些。我试试看,你还有什么建议吗?

标签: python c# loops optimization indexof


【解决方案1】:

“谢谢大家的建议,代码速度大大加快,通过了 315/318 个测试用例。看了一些关于这个问题的视频后,我注意到主要问题是我做了太多的检查并接近这个问题以错误的方式解决问题,因此除非我对其进行重组,否则它没有机会通过。我将附上我走过的示例解决方案。“

    public class Solution {
    public IList<IList<int>> ThreeSum(int[] nums) {

        
        HashSet<IList<int>> mainLs = new HashSet<IList<int>>();
        IList<IList<int>> returnedList = new List<IList<int>>();
        if(nums.Length < 3){
            return returnedList;
        }
        
        Array.Sort(nums);
       
        for(int i = 0; i< nums.Length -2; i++){
            if(i == 0 || (i> 0 && nums[i] != nums[i-1])){
                int low = i + 1;
                int size = nums.Length;
                int high = size - 1;
                int sum = 0 - nums[i];
                while (low < high){
                    if (nums[low] + nums[high] == sum){
                        mainLs.Add(new List<int>(){nums[i],nums[low], nums[high]});
                         while (low < high && nums[low] == nums[low+1]) {low++;}
                        while (low < high && nums[high] == nums[high-1]) {high--;}
                      
                        
                        low++;
                        high --;
                    }else if(nums[low] + nums[high] > sum){
                        high--;
                    }else{
                        low++;
                    }
                }
            }
        }
        returnedList = new List<IList<int>>(mainLs.ToList());
        return returnedList;
    }
}

【讨论】:

    【解决方案2】:

    您可以使用集合中的 Counter 作为索引机制以及覆盖特殊情况(即零、重复数字)的方法。

    索引数字的好处是允许快速验证数字是否存在以完成给定的总和。您可以使用它来查找与零组合的正数的负数版本。当您有一个在列表中出现两次的数字时,您也可以使用它来查找 -2n。

    对于剩余的(不同的)三元组,将每个正数与每个其他数字组合并检查是否存在相反的值。

    from collections import Counter
    def sum3(A):
        cA = Counter(A)
        if cA[0]>3: yield [0,0,0]  # triple zeros, then [-x,0,x]
        if cA[0]>0: yield from ( [-p,0,p] for p in cA if p>0 and -p in cA )
        del cA[0] # all triplets with zero covered
        for N,c in cA.items():
            if N == 0: continue
            if c>1 and -2*N in cA: yield [-2*N,N,N] # cover repeated numbers
            if N<1: continue                        # then distinct numbers ...
            yield from ([a,-a-N,N] for a in cA if -a-N in cA and len({a,-a-N,N})==3)
    

    输出:

    result = list(sum3([-1,0,1,2,-1,-4]))
    # [[-1, 0, 1], [2, -1, -1]]        
    

    【讨论】:

      【解决方案3】:

      就像对Tanner上面发布的算法提出两个优化:

      1. 在外循环中,没有必要一直走到“nums.Length-2”。我们知道“和零三元组”不能包含 3 个正数。必须至少有 1 个负数。所以一旦 nums[i]>0 我们可以从外部循环中中断。
      2. 在内循环中,不需要一直一直到“nums.Length-1”。假设 nums[i]=-10 和 nums[i+1]=-9 。为了平衡它们,第三个数字不能大于+19。在内部循环期间,我们可以相信“低”会增加并且需要比 +19 更低的值来进行平衡。因此,初始“高”值可能远低于 nums.Length-1。我们可以使用二分查找找到最接近 nums[i]+nums[i+1] 的值。

      【讨论】:

        猜你喜欢
        • 2014-12-23
        • 1970-01-01
        • 1970-01-01
        • 2023-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-27
        相关资源
        最近更新 更多