Да, вы правы. Равнина
elements = new LinkedList[N];
достаточно, чтобы выделить их. Вы можете получить к ним доступ
elements[i]->push(....);
и удалите их в своем деструкторе, как вы показали:
delete[] elements;
Компилятор запомнит, сколько элементов было выделено, и правильно вызовет деструктор для каждого списка. Точка перегрузки операторов new и delete заключается в предоставлении пользовательской стратегии выделения памяти. Например, вы можете предварительно выделить память и затем извлечь из этого пула, вместо того, чтобы каждый раз снова выделять память из ОС.
Но обратите внимание, вы должны написать конструктор копирования и оператор копирования. Поскольку, если кто-то копирует вашу хэш-карту, необходимо скопировать связанный список, а не только указатель. Или вы можете сделать конструктор копирования и оператор присваивания копий приватными и не определять их, запретив копии вашей хэш-карты:
....
private:
MyHashMap(MyHashMap const& rhs);
MyHashMap & operator=(MyHashMap const& rhs);
....