The vast majority of my experience is in writing managed code. I'm putting myself through C++ boot camp right now and trying to really wrap my head around pointers and memory management in general. Right now I'm working out of Gayle Laakmann's Cracking the Coding Interview. I've used it before for other languages and am revisiting it for C++.
Here, I'm mostly concerned about memory management, but would also like to hear about any other C++ faux pas that might make me look bad in a whiteboarding session. I'll be moving on to other data structures after this, but this is my springboard, so I want to adjust as early as possible to write good code. The exercises I did are outlined in in main()
with their outputs. I think out of all of this, I'm most concerned about how I'm approaching deleteDuplicateEntries
for exercise 2.1.
Also, any positive comments about implementation would be much appreciated so I make sure I continue to do what's working well.
#include <iostream>
template <class Type>
class ListNode {
public:
ListNode(Type input): value(input), next(NULL) {}
void setValue(Type input) {
value = input;
}
Type getValue() {
return value;
}
void setNext(ListNode *nextTarget) {
if(nextTarget != NULL) {
next = nextTarget;
}
}
ListNode * getNext() {
return next;
}
private:
Type value;
ListNode<Type> *next;
};
// singly linked list
template <class Type>
class List {
public:
List(Type value) {
_head = new ListNode<Type>(value);
_tail = _head;
_size = 1;
}
~List() {
// second param deletes as it traverses
traverseHeadToTail(false, true);
}
// inserts at the end of the list
void append(Type value) {
ListNode<Type> *newNode = new ListNode<Type>(value);
_tail->setNext(newNode);
_tail = newNode;
_size++;
// if this makes two items, point head to tail
if(_size == 2) {
_head->setNext(_tail);
}
}
// inserts at the head of the list
void prepend(Type value) {
ListNode<Type> *newNode = new ListNode<Type>(value);
ListNode<Type> *formerHead = _head;
_head = newNode;
_head->setNext(formerHead);
_size++;
}
// note this inserts AFTER the position
void insertAtPosition(int pos, Type value) {
// validate, optimization
if(!isPosValid(pos)) { return; }
if(pos == 1) { prepend(value); return; }
if(pos == _size) { append(value); return; }
ListNode<Type> *newNode = new ListNode<Type>(value);
ListNode<Type> *targetNode = getNodeAtPos(pos);
ListNode<Type> *nextNode = targetNode->getNext();
targetNode->setNext(newNode);
newNode->setNext(nextNode);
_size++;
}
void deleteAtPosition(int pos) {
if(!isPosValid(pos)) { return; }
if(_size == 1) {
std::cout << "List must have at least one element to exist\n";
return;
}
ListNode<Type> *targetNode = getNodeAtPos(pos);
// handle for deletes at beginning and end
if(pos == 1) {
_head = getNodeAtPos(2);
} else if (pos == _size) {
_tail = getNodeAtPos(_size - 1);
} else {
// inside list delete
ListNode<Type> *prevNode = getNodeAtPos(pos - 1);
ListNode<Type> *nextNode = targetNode->getNext();
prevNode->setNext(nextNode);
}
delete targetNode;
_size--;
}
void writeAtPosition(int pos, Type value) {
if(!isPosValid(pos)) { return; }
getNodeAtPos(pos)->setValue(value);
}
void printList() {
std::cout << "Printing list contents...\n";
traverseHeadToTail(true, false);
}
int getSize(bool printSize) {
if (printSize) { std::cout << "The size is " << _size << "\n"; }
return _size;
}
void deleteDuplicateEntries() {
if(_size == 1) { return; }
Type firstValue = _head->getValue();
List<Type> *uniqueList = new List<Type>(firstValue);
int newSize = 1;
ListNode<Type> *curNode = _head;
while(curNode->getNext() != NULL) {
Type curNodeVal = curNode->getValue();
if(!valueExistsInList(uniqueList, curNodeVal)) {
uniqueList->append(curNodeVal);
newSize++;
}
curNode = curNode->getNext();
}
// delete all nodes and their children (old list)
traverseHeadToTail(false, true);
_head = uniqueList->getNodeAtPos(1);
_size = newSize;
}
bool valueExistsInList(List<Type> *listIn, Type value) {
if(listIn->getSize(false) == 1 && (listIn->getValueAtPosition(1) == value)) { return true; }
ListNode<Type> *curNode = listIn->getNodeAtPos(1);
while(curNode->getNext() != NULL) {
if(curNode->getValue() == value) {
return true;
}
curNode = curNode->getNext();
}
return false;
}
Type getValueAtPosition(int pos) {
if(!isPosValid(pos)) { return 0; }
ListNode<Type> *targetNode = getNodeAtPos(pos);
return targetNode->getValue();
}
Type getNthToLastValue(int n) {
if (n == 0) { std::cout << "Invalid position\n"; return NULL; }
int targetPos = (_size + 1) - n;
if(!isPosValid(targetPos)) { return NULL; }
return getValueAtPosition(targetPos);
}
ListNode<Type> * getNodeAtPos(int pos) {
if(!isPosValid(pos)) { return NULL; }
ListNode<Type> *curNode = _head;
for(int i = 1; i <= _size; i++) {
if(i == pos) { return curNode; break; }
curNode = curNode->getNext();
}
return NULL;
}
private:
ListNode<Type> *_head;
ListNode<Type> *_tail;
int _size;
ListNode<Type> * traverseHeadToTail(bool printList, bool destroyAll) {
ListNode<Type> *curNode = _head;
ListNode<Type> *nextNode = _head->getNext();
int count = 1;
// if we need to print and there's only one element
if(printList && nextNode == NULL) { std::cout << count << ". " << curNode->getValue() << "\n"; }
while(curNode->getNext() != NULL) {
if(printList) { std::cout << count << ". " << curNode->getValue() << "\n"; }
if(destroyAll) { delete curNode; }
curNode = nextNode;
nextNode = curNode->getNext();
count++;
}
// if the list was larger than one, print the last element's value
if(printList && count > 1) { std::cout << count << ". " << curNode->getValue() << "\n"; }
if(destroyAll) {
delete curNode;
return NULL;
} else {
return curNode;
}
}
bool isPosValid(int pos) {
bool result = true;
if(pos > _size || pos <= 0) {
std::cout << "Position " << pos << " is out of bounds\n";
result = false;
}
return result;
}
};
List<int> * addLinkedListNumbers(List<int> *inOne, List<int> *inTwo) {
// the sizes of each list need to match, if they don't: adjust
int oneSize = inOne->getSize(false);
int twoSize = inTwo->getSize(false);
while(oneSize != twoSize) {
if(oneSize < twoSize) {
inOne->append(0);
oneSize++;
} else {
inTwo->append(0);
twoSize++;
}
}
// now that they match, add them
List<int> *result = new List<int>(0);;
int place = 1;
bool carry = false;
while(oneSize > 0) {
int sum = inOne->getValueAtPosition(place) + inTwo->getValueAtPosition(place);
if(carry) { sum++; carry = false; }
if(sum > 9) {
sum = sum - 10;
carry = true;
}
if(oneSize == twoSize) {
result->writeAtPosition(1, sum);
} else {
result->append(sum);
}
oneSize--;
place++;
}
return result;
}
int main() {
// 2.1 - Write code to remove duplicates from an unsorted linked list.
// make a list full of unsorted duplicates
List<std::string> *myDuplicateList = new List<std::string>("a");
myDuplicateList->append("b");
myDuplicateList->append("a");
myDuplicateList->append("1");
myDuplicateList->append("a");
myDuplicateList->append("z");
myDuplicateList->append("a");
myDuplicateList->append("a");
myDuplicateList->append("1");
myDuplicateList->deleteDuplicateEntries();
myDuplicateList->printList();
// print size
myDuplicateList->getSize(true);
// output
// Printing list contents...
// 1. a
// 2. b
// 3. 1
// 4. z
// The size is 4
// 2.2 - Implement an algorithm to find the nth to last element of a singly linked list.
std::cout << "The third to last value is " << myDuplicateList->getNthToLastValue(3) << "\n";
// output
// The third to last value is b
delete myDuplicateList;
// 2.4
// You have two numbers represented by a linked list, where each node contains a single
// digit. The digits are stored in reverse order, such that the 1’s digit is at the head of
// the list. Write a function that adds the two numbers and returns the sum as a linked list.
// EXAMPLE
// Input: (3 -> 1 -> 5) + (5 -> 9 -> 2)
// Output: 8 -> 0 -> 8
// represents decimal 1513
List<int> *numListOne = new List<int>(3);
numListOne->append(1);
numListOne->append(5);
numListOne->append(1);
// 295
List<int> *numListTwo = new List<int>(5);
numListTwo->append(9);
numListTwo->append(2);
std::cout << "Adding 1513 to 295\n";
List<int> *result = addLinkedListNumbers(numListOne, numListTwo);
std::cout << "Sum is ";
for(int i = result->getSize(false); i > 0; i--) {
std::cout << result->getValueAtPosition(i);
}
std::cout << std::endl;
// output
// Adding 1513 to 295
// Result is 1808
delete numListOne;
delete numListTwo;
delete result;
}