Since most of the ppl like puzzles I,ll start this question with a (bad spelling :))gotw like introduction, note that if you dont care about it you can skip the warmup(JG question) and read the G question since that is my "real SO question".
During review of the code samples provided by potential new employees
you stumbled upon a linked list whose implementation uses modern C++11
feature, an std::unique_ptr<>.
template <typename T>
struct Node {
T data;
std::unique_ptr<Node<T>> next;
Node () {}
Node(const T& data_): data(data_) {}
Node(Node& other) { std::static_assert(false,"OH NOES"); }
Node& operator= (const Node& other) {
std::static_assert(false,"OH NOES");
return *new Node();
}
public:
void addNext(const T& t) {
next.reset(new Node<T>(t));
}
};
template<typename T>
class FwdList
{
std::unique_ptr<Node<T>> head;
public:
void add(const T& t)
{
if (head == nullptr)
head.reset( new Node<T>(t));
else {
Node<T>* curr_node = head.get();
while (curr_node->next!=nullptr) {
curr_node = curr_node->next.get();
}
curr_node->addNext(t);
}
}
void clear() {
head.reset();
}
};
JG question:
Determine(ignoring the missing functionality) problem(s) with this
code.
G question: (added 2. based on answers)
1.
Is there a way to fix the problem(s) detected in JG part of the
question without the use of raw pointers?
2.
Does the fix work for the containers where node contain more than one pointer(for example binary tree has pointers to left and right child)
Answers:
JG :
stackoverflow :). Reason:recursion of the unique_ptr<> destructors
triggered by .clear() function.
G:
(???) I have no idea, my gut feeling is no, but I would like to check with
the experts.
So long story short: is there a way to use smart pointers in node based structures and not end up with SO problems? Please don't say that trees probably wont get too deep, or something like that, im looking for general solution.
You can clear it iteratively, making sure that each node's next pointer is empty before destroying the node:
while (head) {
head = std::move(head->next);
}
A binary tree is trickier; but you can flatten it into a list by iteratively cutting off right-hand branches and adding them to the bottom left, something like this:
node * find_bottom_left(node * head) {
while (head && head->left) {
head = head->left.get();
}
return head;
}
node * bottom = find_bottom_left(head.get());
while (head) {
bottom->left = std::move(head->right);
bottom = find_bottom_left(bottom);
head = std::move(head->left);
}
Related
I am trying to create an appendToTail function which will add a node to the end of a singly linked list.
I am having trouble in adding a node if the head is NULL(the linked list is empty)
class Node {
private:
Node* next;
int data;
public:
Node(int d, Node* n = NULL)
: data(d)
, next(n)
{
}
void appendToTail(int);
//other trivial functions(getters and setters etc. ) defined and
//declared
};
void Node::appendToTail(int d)
{
Node* end = new Node(d);
Node* n = this;
if (n == NULL)
n = end;
else {
while (n->next != NULL)
n = n->next;
n->next = end;
n->next->next = NULL;
}
end = NULL;
delete end;
}
int main()
{
Node* n = NULL;
n->appendToTail(5);
std::cout << n->getData(); //getData() is a function which
//retrieves the Data member variable
}
I am expecting to get 5 but I am getting an error which appears to be caused because my node remains null.
Now with modern C++ idioms we use smart pointers instead of raw pointers, it gives you the benefit of RAII (Resource acquisition is initialization) mechanism. In addition if you want an elegant solution to your problem you should introduce a List class with which you can express more clearly the concept of an empty list. It would give something like this:
#include <memory>
#include <iostream>
class List
{
public:
class Node
{
private:
std::shared_ptr<Node> next;
int data;
public:
Node(int d):next(nullptr),data(d){}
inline int getData() const {return data;}
inline std::shared_ptr<Node> getNext() const {return next;}
friend List;
};
List():head(nullptr),tail(nullptr){}
void appendToTail(int );
inline std::shared_ptr<Node> getHead() const {return head;}
inline std::shared_ptr<Node> getTail() const {return tail;}
private:
std::shared_ptr<Node> head;
std::shared_ptr<Node> tail;
};
void List::appendToTail(int d)
{
auto newTail = std::make_shared<Node>(d);
if (head == nullptr)
{
head = tail = newTail;
}
else
{
tail->next = newTail;
tail = newTail;
}
}
int main()
{
List l;
l.appendToTail(5);
std::cout<<l.getHead()->getData();
}
But you should definitely prefer std::list<T> or std::vector<T>.
Unfortunately there several errors with your approach. Semantic errors and a logical error with your interpretation of a linked list. Let's start with your initial misunderstanding. You cannot add a new tail to an empty list. Because it is emtpy. Meaning, not yet existing. Only if some object is existing/instantiated you can add a tail. You cannot add something to not existing stuff. So your idea to start with a Node* n = nullptr cannot work logically.
Additionally you are dereferencing a nullptr (major bug). That is also the main problem of your code. Nothing works. You need an instantiated object, before you can call it's member functions.
So before you can populate the list, you need to create/instantiate it initially. So you need to explicitly create the first node in your main function with
Node* n = new Node (5)
Then the list is existing and from now on you can add new members with calling appendToTail.
There are more semantic errors in your code which have luckily no side effects.
You must not delete the 'end' variable in your function. You want to keep the newly allocated memory for the new tail. But you introduced an additional sematic error by setting 'end' to nullptr and then call delete. Deleting a nullptr is a noOp and will do nothing. So, although you have a semantic error, this will not cause any trouble.
There is more:
For a pointer to Null you should always use nullptr.
And, your
if (n == NULL)
is always false. Before that, you assigned this to n. This is never NULL. You can delete the if else. Keep the statements from the else, except the
n->next->next = NULL;
That's not necessary. The constructor did that already for you. As explained, the next 2 statements should also be elimanted.
Additionally you may want to read a little more on the concept of linked lists.
I hope I could help a little
Every time I add comments inside the definitions of the operators, it starts giving me errors, but removing the comments immediately gets rid of the errors. I don't see why comments would have any effect at all on the code. Also just general advice on the overloading of operators in general would be appreciated.
Heres my class template:
template<class THING>
struct LLNode
{
THING data;
LLNode<THING> *next;
LLNode<THING> *prev;
};
template<class THING>
class LinkedList
{
private:
//use a doubly linked-list based implementation
//keep a head and tail pointer for efficiency
LLNode<THING> *Head;
LLNode<THING> *Tail;
int count;
public:
//setup initial conditions
LinkedList();
//delete all dynamic memory, etc.
~LinkedList();
//constant bracket operator to access specific element
const THING& operator[](int);
//Bracket operator to access specific element
THING& operator[](int);
//Equality operator to check if two lists are equal
bool operator==(const LinkedList<THING>&);
//Inequality operator to check if two lists are equal
bool operator!=(const LinkedList<THING>&);
//add x to front of list
void addFront(THING);
//add x to back of list
void addBack(THING);
//add x as the ith thing in the list
//if there are less than i things, add it to the back
void add(THING, int);
//remove and return front item from list
THING removeFront();
//remove and return back item from list
THING removeBack();
//return value of back item (but don't remove it)
THING getBack();
//return value of front item (but don't remove it)
THING getFront();
//return how many items are in the list
int length();
//print all elements in the linked list
void print();
};
And the operators I'm currently working on:
template<class THING>
THING& LinkedList<THING>::operator[](int index)
{
}
template<class THING>
bool LinkedList<THING>::operator==(const LinkedList<THING>& list_one, const LinkedList<THING>& list_two)
{
//checking for same size on both lists
//if both are same size, move on to checking for same data
if(list_one.count != list_two.count)
{
return false;
}
else
{
//boolean flag to hold truth of sameness
bool flag = true;
//two pointers to go through
LLNode<THING> *temp_one = list_one.Head;
LLNode<THING> *temp_two = list_two.Head;
while(temp_one != NULL && temp_two != NULL)
{
if(temp_one->data != temp_two->data)
{
flag = false;
break;
}
else
{
temp_one = temp_one->next;
temp_two = temp_two->next;
}
}
return flag;
}
}
These, as you've said, aren't compilation errors: they are Intellisense errors. These errors take a while to refresh in the extension and therefore aren't very indicative most of the time, and it's a known issue that Intellisense isn't great with adding comments, and is even worse when colliding with other extensions.
One way to get rid of the errors is to cut-paste all of the code (just go ctrl+a, ctrl+x, ctrl+v). This forces Intellisense to refresh.
Another way which is a personal favorite of mine is to shut down Intellisense :) you can see how to do that in here.
Part of my homework assignment is to implement a generic linked list.
So far I wrote this function:
template<class T>
void List<T>::insert(const T& data)
{
List<T>::Node* newNode = new List<T>::Node(data);
newNode->next = nullptr;
if (head == nullptr)
{
head = newNode;
tail = newNode;
}
else
{
tail->next = newNode;
tail = newNode;
}
size++;
}
As you can see I'm getting the data by reference but I could get it by value as well.
My question is what approach is better and why?
In C++98/03, what you have is generally the correct solution. In C++11, you can keep it the same, and you will be no worse off. But if you want to improve efficiency, you can make some modifications. There are two schools of thought. The most efficient solution requires a little code duplication. You need two functions.
template<class T>
void List<T>::insert(const T& data) // take a const reference
{
List<T>::Node* newNode = new List<T>::Node(data); // copy it in
...
template<class T>
void List<T>::insert(T&& data) // take an r-value reference
{
List<T>::Node* newNode
= new List<T>::Node(std::move(data)); // move it in
...
The other method is only slightly less efficient in most cases, and it avoids code duplication:
template<class T>
void List<T>::insert(T data) // take a value (copy)
{
List<T>::Node* newNode
= new List<T>::Node(std::move(data)); // move it in
...
Avoid unnecessary copy of data if passing by value, pass it by const reference as you did.
By reference is better.
The data is not copied over. Which can be significant we are talking about
class, structures, unions, etc.
By using "const" avoids the stupid programming error of storing values to parameters.
i.e.
void dumb_print_function(Type& d){
d=3; // bad programming practice.
}
x=LongCalc();
dumb_print_function(x); // output 3 not results of "LongCalc();".
I am making a tree of n children to store directories of computer. Now, concept is simply make a tree (that would not be a BT of course) and each node will have children as well. Consider the code below then I will explain the problem.
First Consider this:
C/users/DeadCoder/Movies/Batman.
Now In my main.cpp I have this all C, users, DeadCoder, Movies, Batman in a vector and then I send two pairs in insert Func. if root==NULL; it would just insert C. Next time C and users would go. It would find C and then insert users occordingly. Let's now see the code .
template <class T>
struct Node;
template <class T>
class tree
{
Node<T> *root;
public:
tree();
~tree();
int insert(T str, T str1);
Node<T> *getRoot();
Node<T> *search(T item, Node<T> *tempPtr);
};
template <class T>
struct Node{
T n;
Node<T> *sibling;
tree<T> children; // SEE my each node has children.
Node(T N){
this->n = N;
this->sibling = NULL;
}
};
// In .cpp FILE;
// Initilaizer
template <class T>
tree<T>::tree() // Constructor Initialization.
{
root=NULL;
}
// Insert Function.
template <class T>
int tree<T>::insert(T push, T find)
{
Node<T> *rPtr = root;
if (rPtr==NULL){
//ROOT is NULL. C needs to be inserted which is in find.
Node<T> *pusPtr = new Node<T>(find);
root = pushPtr;
root->sibling=NULL;
return 0;
}
else if(rPtr!=NULL){
Node<T> *pushPtr = new Node<T>(push);
Node<T> *temp2 = search(find, root);
Node<T> *temp = temp2->children.getRoot(); // say it LINE_40.
if (temp==NULL){
temp = pushPtr;
temp->sibling=NULL;
return 1;
}
// children are already present.
else if(temp!=NULL){
// You don't need to know code for this part.
}
}//if.
}
// Search Function.
template <class T>
Node<T> *tree<T>::search(T data, treeNode<T>* N)
{
if (N->n==data){ // where n represent directory.
return N; // data found.
}//if....
else{
Node<T> *child = N->children.getRoot();
// This is where i get Segmentation fault,
// because child is ==NULL; but you see in LINE_40 I did insert the child for C.
if(child!=NULL){ // say it line 80.
search(data, child);
}//if...
if(child->sibling!=NULL){
search(data, child->sibling);
}
}
}// search....
PROBLEM: C inserted. Users inserted. Now in search function at Line 80, it comes to find the child for C. and it should be Users as I have inserted it in LINE 40. BUT Instead it says child==NULL. I have been debugging for hours and I don't know why it says so. I hope Everybody gets the problem.
Now I really need to know why it is regarding C child to be NULL, It has to be users. Can anyOne see what is the problem???? HELP !!!!
Line 42 does nothing (I mean it has no side effect). It just puts a value in a temporary variable then leaves.
You probably want your temp to be a reference to the root. Something like: Node<T> *&temp =
Are you sure insert method actually inserted these elements?
It might be helpful to implement postconditions so to verify your methods actually fulfill their contract (design by contract).
This way you'll directly get what is wrong and debugging will be fast or unnecessary in some cases, since you'll get log messages saying "this method was supposed to do this but failed doing it", otherwise you'll look for hours where the problems comes from.
Here is code in which I am trying to implement a queue using linked list:
#include <iostream>
#include <cstdlib>
using namespace std;
template <class Item>
class Queue{
public:
struct node{
Item item;node *next;
node (Item x){
item=x; next=0;
}
};
typedef node* link;
link head, tail;
public:
Queue(int){ head=0;}
int empty() const { return head==0; }
void put(Item x){
node* t=tail;
tail=new node(x);
if (head==0) head=tail;
else t->next=tail;
}
Item get(){
Item v=head->item;link t=head->next;
delete head; head=tail return v;
}
};
int main(){
return 0;
}
but I have problems with pointers. For example, when I write Item v = head-> it should show me option to choose item but it does not show. Also in other place of code after -> this sign code does not give me possibility to choose item or next. Please help.
ON: The -> operator can be overloaded so the development environment cannot be sure what to do with it. You can do the following (temporarily or permanently) if you really want to have auto-completion.
// IMPORTANT. Make sure "head" is not null before you do it!
Node &headNode(*head); // Create a reference
headNode.next = tail; // Use TAB or CTRL+SPACE or whatever here after dot
OFF: I reviewed your code and made some corrections
template <class Item>
class Queue {
public:
Queue()
: head(0)
, tail(0)
{ }
bool empty() const { return head==0; }
void put(const Item& x)
{
Node* t = tail;
tail = new Node(x);
if (head==0)
head = tail;
else
t->next = tail;
}
Item get()
{
Item v = head->item;
Link t = head->next;
delete head;
head = t;
if(head==0)
tail = 0;
return v;
}
private:
struct Node {
Item item;
Node *next;
Node(const Item& x)
: item(x)
, next(0)
{}
};
typedef Node* Link;
Link head,tail;
};
Removed int typed nameless parameter from Queue constructor
Renamed node to Node and link to Link because Item is Item, not item. Just to make it somewhat standardized
Initializing tail at the constructor of Queue.
Using initializer list instead of code where possible.
Fixing Queue::get(), setting tail to zero if the queue become empty.
Using constant reference in parameter lists of Queue::put() and Queue::Node::Node()
Node, Link, head and tail is private from now.
Queue::empty() returns bool instead of int from now.
You would probably be better off reusing an existing container.
The STL explicitly contains, for example, a queue Container Adapter (based on deque by default, which is the most efficient choice).
If you don't need polymorphic behavior, a std::queue<Item> is what you're looking for, it's both extremely efficient (more than your custom list-based queue) and you will avoid memory management issues.
If you need polymorphic behavior, then use a std::queue< std::unique_ptr<Item> >.