0

This is the problem i am trying to solve, along with the solution testing the test case in which i am having trouble in.

Leetcode problem: 128. Longest Consecutive Sequence

#include <iostream>
#include <vector>
#include <unordered_map>

class Solution {
public:
    int longestConsecutive(std::vector<int>& nums)
    {
        std::unordered_map<int, int> seq;
        for (auto i : nums)
        {
            ++seq[i];
        }
        int max{ 0 };
        for (auto i : seq)
        {
            int ans{ 0 };
            int x{ i.first };
            if (seq[x] > 0)
            {
                ans = 1;
                int p{ x };
                int q{ x };
                while (seq[p - 1] > 0)
                {
                    --p;
                    ++ans;
                }
                while (seq[q + 1] > 0)
                {
                    ++q;
                    ++ans;
                }
            }
            if (max < ans)
                max = ans;
        }
        return max;
    }
};

int main()
{
    Solution s;
    std::vector<int> nums{ 4,0,-4,-2,2,5,2,0,-8,-8,-8,-8,-1,7,4,5,5,-4,6,6,-3 };
    std::cout << s.longestConsecutive(nums);
    return 0;
}

When i tested this test case in my offline compiler(microsoft c++ compiler in visualt studio), it is showing the test case ans as 5(which is absolutely correct).

microsoft visual studio debug console screenshot

However, upon submitting the same solution in leetcode it is showing the test case ans as 4(which is wrong). I tried testing this case in online gdb compiler also and there also this test case ans is showing as 4.

Leetcode failed test case screenshot

My solution has passed 55/72 cases so i am sure there is not any issue with my algorithm or my answer output format. I believe it to be an issue with leetcode or online

genpfault
  • 51,148
  • 11
  • 85
  • 139
  • 2
    what is your specific question? How to find the bug? -> [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) – 463035818_is_not_an_ai Mar 27 '23 at 14:48
  • Umm, but if you can reproduce your problem with the other compiler (e.g. online godbolt), then just debug it there? – pptaszni Mar 27 '23 at 14:49
  • 5
    The issue is your code, not the compiler. Let's not be so arrogant. Differing results means you've (very likely) invoked Undefined Behavior. I wouldn't consider this the best way to solve this problem either. – sweenish Mar 27 '23 at 14:49
  • probably not the problem, but why do you use `seq[x]` in the loop rather than `i.second` ? – 463035818_is_not_an_ai Mar 27 '23 at 14:53
  • 6
    You are modifying the map while looping over it. This invalidates the iterators. – ChrisMM Mar 27 '23 at 14:54
  • 2
    These test are designed (1) to break (time/memory exceeded) if you try the obvious algorithm and (2) test your program with edge-test-cases ie completely within the spec but not usual data eg the empty test case, maximum and minimum integers ... The example test-cases are usually easy to pass using the obvious approach and the hidden test-cases are usually not. – Richard Critten Mar 27 '23 at 14:55
  • 1
    https://meta.stackoverflow.com/questions/285551/why-not-upload-images-of-code-errors-when-asking-a-question – Marek R Mar 27 '23 at 15:02
  • 3
    @OP *so i am sure there is not any issue with my algorithm* -- A program that you write is *not* an algorithm. An algorithm is a set of steps that will *always* give the correct answer. A program that you write that implements the algorithm can have bugs. That's the difference -- your program has bugs. – PaulMcKenzie Mar 27 '23 at 15:06
  • 1
    I could not answer, because again the question has been closed too quick. Not good. Solution is here: https://godbolt.org/z/WaM6ebxq5 Why is ist necessary to close a question after 30min? – A M Mar 27 '23 at 15:13
  • Thinking that the fault is anywhere but with yourself is not an efficient way to find bugs. – john Mar 27 '23 at 15:25
  • 1
    I am new to data structures and thought i have understood maps quite well. It wasn't though. Thanks everyone for providing useful input. – electric fan Mar 27 '23 at 16:57

2 Answers2

6

Consider your code (shortened for brevity)

    for (auto i : seq)
    {
        int x{ i.first }; 
        if (seq[x] > 0) // okay, will be in the sequence
        {
            int p{ x };
            int q{ x };
            while (seq[p - 1] > 0) // p-1 might not be in the map, so insert
            {}
            while (seq[q + 1] > 0) // q+1 might not be in the map, so insert
            {}
        }
    }

Both these while loops can cause insertions, which can invalidate the iterators used by for (auto i : seq). Thus, you have Undefined Behaviour, and getting anywhere near the correct result is more by accident.

ChrisMM
  • 8,448
  • 13
  • 29
  • 48
  • 1
    sadly address sanitizer is unable to catch that: https://godbolt.org/z/hb8deeb8b – Marek R Mar 27 '23 at 16:12
  • Thanks a lot. I am new to maps and hence didn't knew that i was unknowingly inserting new keys in the map. I have now successfully found an alternate way to do this by using condition (seq.find(p - 1) != seq.end(). – electric fan Mar 27 '23 at 16:55
1

With your issue pointed out (invaliding iterators due to inserts), here's a working solution. It beats 80.33% on runtime and 60.17% on memory:

class Solution {
 public:
  int longestConsecutive(vector<int>& nums) {
    std::unordered_set<int> uniqueVals;

    // Fill set
    for (auto& i : nums) {
      uniqueVals.insert(i);
    }

    int maxSequence = 0;
    // Iterate the set instead of the vector to avoid re-checking
    // repeated values in the vector.
    for (auto& i : uniqueVals) {
      // Is number the start of a sequence?
      if (uniqueVals.find(i - 1) != uniqueVals.end()) {
        continue;  // It was not, we move on immediately
      }

      // Getting here means our value is the start of a sequence
      int sequenceLength = 1;
      for (int val = i + 1; uniqueVals.find(val) != uniqueVals.end();
           ++val, ++sequenceLength) {
      }
      if (sequenceLength > maxSequence) {
        maxSequence = sequenceLength;
      }
    }

    return maxSequence;
  }
};

This answer was accepted by leetcode, so it passes all tests.

The big idea is identifying the start of a sequence. I use std::unordered_set because I don't care how many times a value has occurred, and I get O(1) insertion. I only care if a value is in the set or not.

More importantly, I don't modify the set with insertions as I'm iterating it (read-only), which keeps my loop well-defined.

sweenish
  • 4,793
  • 3
  • 12
  • 23