1

I need a structure to keep track of presence of some items. I just wanted to take an array a0....aN and mark the elements as a[0]=0,a[1]=0,a[2]=1........(a[i]=1 if the element is present,a[i]=0 if element is not present). But the items range from -1000 to +1000. It can be done by putting the negative range from 1001 to 2000. I needed to know if there is any other data structure in c++ that can work like array and with negative indexes. Thank you for your time.

rd10
  • 1,599
  • 2
  • 10
  • 27
  • 3
    No, there isn't. But instead of mapping *only* the negative values, I'd shift *all* of them, so you don't need to differentiate, but just add the offset (i. e. -1000 is mapped to 0, 1000 is mapped to 2000). Of course, you can wrap this into your own container class... – Aconcagua Apr 12 '18 at 10:05
  • Related: https://stackoverflow.com/q/4306/1025391 – moooeeeep Apr 12 '18 at 10:23
  • 3
    Instead of using array, and forcing to use somehow the same value both for the index of the array element and for the item identifier, consider using other types of data structure. `std::map` or `std::set` seem to be the right tools for the job – Gian Paolo Apr 12 '18 at 10:32
  • @GianPaolo We should consider the `unordered` counterparts, too. On the other hand, just shifting the indices has guaranteed `O(1)` runtime vs. O(log(n)) with the ordered containers and does not require hashing and collision handling as with the unordered ones. – Aconcagua Apr 12 '18 at 10:55
  • @Aconcagua If what you want is set semantics, I'd consider any approach that requires you to rewrite the set functionality for the specific application premature optimisation. – moooeeeep Apr 12 '18 at 11:05
  • @moooeeeep Are we tied already to a set? Array of bool is out already? std::vector? I'd consider selecting the most appropriate data structure for a specific task a basic design decision rather than optimisation... – Aconcagua Apr 12 '18 at 11:16
  • 1
    @Aconcagua You can replace a `std::set` with a data structure that you wrote yourself on the basis of an array of bits anytime, once you find it's a performance critical optimization. If it's not, doing so might turn out to be an unnecessary source of unexpected errors and a waste of time. – moooeeeep Apr 12 '18 at 11:24
  • @moooeeeep On second thought, need to agree... – Aconcagua Apr 12 '18 at 11:40

5 Answers5

3

map is used for this only, to have key/index of any basic/user-defined data type. See - http://www.cplusplus.com/reference/map/map/

Example for your case:

#include <iostream>
#include <map>
#include <string>

int main ()
{
  std::map<int, int> mymap;

  mymap[-1]=1;
  mymap[-2]=0;
  mymap[-3]=1;

  std::cout << mymap[-1] << '\n';
  std::cout << mymap[-2] << '\n';
  std::cout << mymap[-3] << '\n';

  return 0;
}

Example for char:

#include <iostream>
#include <map>
#include <string>

int main ()
{
  std::map<char,std::string> mymap;

  mymap['a']="an element";
  mymap['b']="another element";
  mymap['c']=mymap['b'];

  std::cout << "mymap['a'] is " << mymap['a'] << '\n';
  std::cout << "mymap['b'] is " << mymap['b'] << '\n';
  std::cout << "mymap['c'] is " << mymap['c'] << '\n';
  std::cout << "mymap['d'] is " << mymap['d'] << '\n';

  std::cout << "mymap now contains " << mymap.size() << " elements.\n";

  return 0;
}
Abhishek Keshri
  • 3,074
  • 14
  • 31
1

You an create your own data structure which supports -ve indexes. Just add an offset to the indexs while storing them in an array.

class MyArray {
    int *arr;
    public:
    MyArray(int offset) {
        arr = new int[2*offset]; // size must to double the offset
    }
    ~MyArray(){
        delete arr;
    }
    void add(int index, int val) {
        arr[index + offset] = val;
    }
    void get(int index) {
        return arr[index + offset];
    }
}

Then you can just use your class to add and get elements with any index.

MyArray arr = MyArray(1000); // pass max -ve index as offset
arr.add(10, -150);
cout << arr.get(100);
schorsch312
  • 5,553
  • 5
  • 28
  • 57
Rajeev Singh
  • 3,292
  • 2
  • 19
  • 30
  • 1
    As far as I understood the question, range shall include both -offset and +offset, so we'd need size of 2*offset + 1... – Aconcagua Apr 12 '18 at 10:22
  • Instead of custom add/get, I'd rather provide standard index operators (const and non-const one), returning references. – Aconcagua Apr 12 '18 at 10:24
  • 4
    Why introduce more issues such as memory leaks by using `new[]`? Just use `std::vector`. Your `MyArray` class leaks memory and is not safely copyable or assignable. – PaulMcKenzie Apr 12 '18 at 10:30
  • 1
    in this case a `std::array` might be more appropriate – doron Apr 12 '18 at 10:43
  • 1
    In this case, I'd suggest to use `std::bitset` instead of an integer array of any sort. – moooeeeep Apr 12 '18 at 11:02
1

I need a structure to keep track of presence of some items.

If what you want is set semantics, use a set data structure. No need to implement a custom array wrapper. You can use a std::set (or std::unordered_set) for that. Remember that "premature optimization is the root of all evil".

Insert the values that are there, leave out the values that are missing. No need to worry about negative indices. You can use the methods std::set::find() or std::set::count() to check the presence of an item. Have a look at the documentation to find some example code.

If you later find it's a performance critical optimization, you can replace a std::set<int> with a data structure that you wrote yourself on the basis of an array of bits anytime. If it's not, doing so prematurely might turn out to be an unnecessary source of unexpected errors and a waste of time.

For reference:

moooeeeep
  • 31,622
  • 22
  • 98
  • 187
  • Actually, we have two options: Storing the present elements or the missing ones in the set, both being equally complex, so which one would you select (especially in respect to premature optimisation...)? – Aconcagua Apr 12 '18 at 12:00
  • Provided we know *in advance* that one or the other case always is the exception, selecting the variant that keeps the set smaller shouldn't be PO any more. Would you agree? – Aconcagua Apr 12 '18 at 12:00
  • @Aconcagua I guess the default should be to keep track of present items. It seems easier to start with an empty set, that is then populated given the data, than it is to start with a set containing all possible items and then remove the ones actually present. But as you say, relative performance may differ depending on the actual data (and query patterns). – moooeeeep Apr 12 '18 at 12:34
0

Most efficient approach will be just shifting your array indexes so all of them are non-negative. In your case just use a[i+1000] and it will be sufficient.

If you really need to use negative indexes it is also possible. C / C++ calculates memory address of array element using address of table and then adding index value to it. Using negative numbers just points to memory area placed before your table (which is not you normally want).

int a[2001];
int *b = &a[1000];
int x = b[-1000]; // This points to 1000 places before b which translates to a[0] (valid place)

Another approach will be using containers. Then any number can be translated to string and stored in proper container.

DevilaN
  • 1,317
  • 10
  • 21
0

I think that the answer of @Rajev is almost fine. I have just replaced a plain array with a std::vector. Thus, the memory management is secure and copying and moving is easy.

template <typname T>
class MyArray {
  private:
    std::vector<T> arr;
  public:
    MyArray(int offset) {
        arr.resize(2*offset); // size must to double the offset
    }

    void set(int index, int val) {
        arr[index + offset] = val;
    }

    void get(int index) {
        return arr[index + offset];
    }
}

You can expand this further by overloading the operator [] of MyArray.

schorsch312
  • 5,553
  • 5
  • 28
  • 57