Finding an element in an array that isn't repeated a multiple of three times?

前端 未结 3 1832
南笙
南笙 2020-12-14 03:04

After reading this interesting question I was reminded of a tricky interview question I had once that I never satisfactorily answered:

You are given a

3条回答
  •  感动是毒
    2020-12-14 03:44

    You're looking for an item with a rep-count that's non-zero (mod 3). I think I'd do it recursively:

    1. split the array in half
    2. find items with rep count that's non-zero (mod 3) in each half
    3. merge the halves, keeping counts for unequal keys, and adding the counts for equal keys
    4. strike out any with count = 0 (mod 3)
    5. return that as the set of values for the current part of the input.

    Without even trying to optimize things beyond the basic algorithm (e.g., not worrying about storing only two bits per count), this seems to do pretty well. I've included code to generate a reasonably large test case (e.g., 1500+ items) and print out the sizes of the maps it's creating. At any given time, it seems to have a maximum of around 50 items in the maps it creates (i.e., it only uses two maps at a time, and the largest I've seen is around 25 items). Technically, as it stands I believe this is currently something like O(N log N), but if you switched to a hash-based container, I believe you'd expect O(N).

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    class zero_mod { 
        unsigned base;
    public:
        zero_mod(unsigned b) : base(b) {}
    
        bool operator()(std::pair const &v) { 
            return v.second % base == 0; 
        }
    };
    
    // merge two maps together -- all keys from both maps, and the sums 
    // of equal values.
    // Then remove any items with a value congruent to 0 (mod 3)
    //
    std::map 
    merge(std::map const &left, std::map const &right) { 
        std::map::const_iterator p, pos;
        std::map temp, ret;
    
        std::copy(left.begin(), left.end(), std::inserter(temp, temp.end()));
        for (p=right.begin(); p!=right.end(); ++p) 
            temp[p->first] += p->second;
        std::remove_copy_if(temp.begin(), temp.end(), 
                            std::inserter(ret, ret.end()), 
                            zero_mod(3));
        return ret;
    }   
    
    // Recursively find items with counts != 0 (mod 3):    
    std::map 
    do_count(std::vector const &input, size_t left, size_t right) { 
        std::map left_counts, right_counts, temp, ret;
    
        if (right - left <= 2) {
            for (size_t i=left; i!=right; ++i) 
                ++ret[input[i]];
            return ret;
        }
        size_t middle = left + (right-left)/2;
        left_counts = do_count(input, left, middle);
        right_counts = do_count(input, middle, right);
        ret = merge(left_counts, right_counts);
    
        // show the size of the map to get an idea of how much storage we're using.
        std::cerr << "Size: " << ret.size() << "\t";
        return ret;
    }
    
    std::map count(std::vector const &input) { 
        return do_count(input, 0, input.size());
    }
    
    namespace std { 
        ostream &operator<<(ostream &os, pair const &p) { 
            return os << p.first;
        }
    }
    
    int main() {
        srand(time(NULL));
        std::vector test;
    
        // generate a bunch of data by inserting packets of 3 items    
        for (int i=0; i<100; i++) {
            int packets = std::rand() % 10;
            int value = rand() % 50;
            for (int i=0; i results = count(test);
    
        // show the results. Should match the value shown as removed above:
        std::copy(results.begin(), results.end(), 
            std::ostream_iterator >(std::cout, "\n"));
        return 0;
    }
    

提交回复
热议问题