Readability of for-loops without counters - c++

I recently wrote an implementation for a simple linked list, and at several points in my code it looks like
Node* current_node = head;
while (current_node != nullptr) {
if (current_node->data == query) {
// perform some action
break;
}
current_node = current_node->next;
}
And I just recently thought I could re-implement this as
for (Node* current_node = head; current_node != nullptr; current_node = current_node->next) {
if (current_node->data == query) {
// perform some action
break;
}
}
I know that both are syntactically correct, and that any performance differences should be negligible, but I was wondering if having an equality condition in the check is generally implemented in a for-loop? Previously I have only used inequalities (ex: >, <, etc.) in for-loops. Which version is more conventional/readable?

It's not a bad practice to loop through a linked list via for loop, but you can improve it to:
std::list<type> list;
auto it = std::find(begin(list), end(list), query);
if (it != end(list))
// perform some action

Related

Difference in a C++ pointer behavior when incremented in the for loop definition vs inside for loop

I'm onto a leetcode problem (Floyd’s Cycle Detection Algorithm for Linked List) wherein I noticed a strange behavior on the pointer states.
When I change the pointer state inside the for loop, the program executes correctly (pointer states move to the right states):
ListNode *detectCycle(ListNode *head) {
if(!head or !head->next)
return nullptr;
ListNode *slow, *fast;
for(slow = head, fast = head; fast && fast->next;)
{
slow = slow->next, fast = fast->next->next; // This hops the pointers correctly
if(slow == fast)
{
slow = head;
while(slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
}
return nullptr;
}
But when I state change slow & fast in the for loop definition, the state change is wrong and the program doesnt give the right output.
ListNode *detectCycle(ListNode *head) {
if(!head or !head->next)
return nullptr;
ListNode* slow, *fast;
for(slow = head, fast = head; fast && fast->next; slow = slow->next, fast=fast->next->next) // Pointers dont hop correctly
{
if(slow == fast)
{
slow = head;
while(slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
}
return nullptr;
}
I dont know whats causing this. In my head, incrementing the pointers in the for loop definition vs immediately in the for loop should be the same thing. Can someone throw an insight as to why incrementing pointers within the loop vs in the for loop signature leads to different behavior?
A
for (init-statement; condition; iteration-expression)
{
dostuff();
}
maps to
{
init-statement
while ( condition )
{
dostuff();
iteration-expression ;
}
}
so we get
{
slow = head, fast = head;
while (fast && fast->next)
{
slow = slow->next, fast = fast->next->next;
dostuff(); // for example purposes only. Not really replacible with a function
}
}
and
{
slow = head, fast = head;
while (fast && fast->next)
{
dostuff();
slow = slow->next, fast=fast->next->next;
}
}
In the first, slow and fast are always updated before dostuff().
In the second, dostuff happens before slow and fast are updated, so the values of slow and fast used in dostuff will be different on the first loop iteration.

Why am I unable to enter elements in the linked list while the function is working otherwise?

I wrote a program to merge two sorted linked list into one and this function was the one I used to do it but it's not working. The code of the function is as follows is as follows:
void combine(Node **temp, Node *temp_1, Node *temp_2){
while(temp_1 != NULL || temp_2 != NULL){
if(temp_1->data > temp_2->data){
push(temp, temp_2->data);
temp_2 = temp_2->next;
}
else{
push(temp, temp_1->data);
temp_1 = temp_1->next;
}
}
while(temp_1 != NULL){
push(temp, temp_1->data);
temp_1 = temp_1->next;
}
while(temp_2 != NULL){
push(temp, temp_2->data);
temp_2 = temp_2->next;
}
}
Now, this code doesn't add anything to the final linked list. If I write something like
push(temp, temp_1->data);
it will add elements just fine so the problem isn't definitely with the push function. Can someone tell me what is the problem with the above code?
The full code is in the following URL:
https://ide.geeksforgeeks.org/FZ8IS4PADE
The issue is the while condition:
while(temp_1 != NULL || temp_2 != NULL){
This will allow the execution of the body of the loop when just one of those two pointers is null, and this will result in undefined behaviour on the first statement in that body:
if(temp_1->data > temp_2->data){
The || should be an &&. This will fix your issue.
Other remarks on your code
Don't use NULL for comparing your pointer variables against, but nullptr
The use of push makes your code inefficient: at every push, your code is starting an iteration through the whole list to find the end of it. Since you actually know what is the last node (since it was created in the previous iteration of the loop) this is a waste of time. Instead, keep a reference to the tail of the list that is being created. As there is no tail at the start of the combine process, it might be useful to create a "sentinel" node that comes before the real list that will be returned.
Use better variable names. temp is not temporary at all. It is the result that the caller wants to get: this name is misleading.
Avoid code repetition. The last two loops are the same except for the list that is copied from, and this code is again similar to the parts in the main loop. So create a function that does this job of copying a node from a source list to the end of another list, and that advances both pointers.
Here is what that would look like:
void copyNode(Node **source, Node **targetTail) {
*targetTail = (*targetTail)->next = new Node((*source)->data);
*source = (*source)->next;
}
void combine(Node **result, Node *head_1, Node *head_2){
Node *sentinel = new Node(0); // Dummy
Node *current = sentinel;
while(head_1 != nullptr && head_2 != nullptr){
if(head_1->data > head_2->data){
copyNode(&head_2, &current);
}
else{
copyNode(&head_1, &current);
}
}
if (head_1 == nullptr) {
head_1 = head_2;
}
while (head_1 != NULL) {
copyNode(&head_1, &current);
}
*result = sentinel->next;
delete sentinel;
}

Finding out if a Linked List has a Cycle [Code Wars Problem]

bool hasCycle(Node * head)
{
Node *p = NULL;
Node *q = head;
while(q)
{
q = q->next;
if(q->next != NULL) q = q->next;
p = p->next;
}
return p == q ? true : false;
}
My code works for most of the solutions, but it fails one test case for some reason. Can someone take a look at my code and explain to me what is wrong with my logic. Heres the link to the Code Wars https://www.codewars.com/kata/5af9a4b2de4c7fdab30000e5/train/cpp
The logic behind this problem is to use two pointers: a fast one and a slow one. If there is a loop, the fast will catch up with the slow. If there is no loop, the fast will reach the end first.
Your code didn't exihibit the logic like this. I don't know what you are trying to do. Here is my code:
bool hasCycle(Node * head)
{
Node *fast = head, *slow = head;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
if (fast == slow) return true; // here the fast catches the slow
}
return false; // here the fast reaches the end of the linked list
}

Equality operator for linked lists C++

I am trying to create a linked list class and I'm having trouble determining how to check the equality of two lists using the operator== (equality operator). How would I go about going through each node and checking if elements within them are equal in their respective positions?
bool List::operator==(const List& list2) const {
if(mySize != list2.mySize){
return false;
}
if(myFirst == list2.myFirst){
if(myFirst == NULL){
return true;
}
Node * nPtr1 = myFirst;
Node * nPtr2 = list2.myFirst;
while(nPtr1 != NULL){
//what can I do here to check the equality of each element in both lists?
}
}
}
According to your code, myFirst is a pointer, so the following is wrong:
if(myFirst == list2.myFirst)
Unless a node is equal to another node ONLY if it is the same node (pointer wise).
You have a special case when the lists are empty which you kind of captured:
if(myFirst == nullptr && list2.myFirst == nullptr)
{
return true;
}
That would be the empty case.
Otherwise, you got the while properly, and if your items (Node) can simple be compared you would do:
p = myFirst;
q = list2.myFirst;
while(p != nullptr)
{
if(*p != *q) // this is what you're asking about, right?
{
return false;
}
p = p->next; // not too sure how you have a Node separated from the List
q = q->next; // and check next/previous items...
}
return true;
Note that if nodes can only be equal if they have the same pointer then the compare becomes:
if(p != q) // this is a test of pointers instead of objects
P.S. Someone mentioned using a recursive algorithm. That's an idea and conceptually it's great. When using such in the real world, though, you notice that it can be (much) slower. It has to very heavily use the stack and with very large lists, it could break your software.
while(nPtr1 != NULL){
if(nPtr1 != nPtr2){
return false;
}
nPtr1=nPtr1->next;
nPtr2=nPtr2->next;
}
return true;
But this is the way to check if the two lists are identical (nPtr1 and nPtr2 are pointing to the same list). If you really want to compare lists by content you have to compare content like:
if(nPtr1->content != nPtr2->content)
and also change your first pointer check:
if(myFirst->content == list.myFirst->content)

LinkedList used in an interview's test

[EDIT]Fixed my code. Is while(temp != NULL), not while(temp->next != NULL). Sorry to insert wrong code.
Today I've participated an online programming test. The interviewer used Codility to evaluate my code and the other interviewees.
At some moment a question about Linked list was made. It's about to count how many items a linked list has.
I did the only possible approach to do this, AFAIK:
//This is struct declaration
struct SomeStruct
{
int value;
SomeStruct* next;
}
int elementCount(SomeStruct* list)
{
int count = 0;
if(list != NULL)
{
SomeStruct* temp = list;
while(temp != NULL)
{
count++;
temp = temp->next;
}
}
return count;
}
I remember when I send this code as answer for this question, Codility points me out that this solution is wrong because its consume too much time to execute the task.
In my head and in this thread on SO there's no other way to get size of linked list without traversing it, not in a simple way.
Is there a problem with Codility when it says this solution is wrong? Or there are another approaches?
PS: the test allowed using of STL
Your solution is incorrect, since it returns 1 less than the actual count. Just try applying it to a list with 1 element.
Why did you come up with this strange two-tiered structure with an if and and a cycle that checks temp->next? Why not just
unsigned elementCount(const SomeStruct *list)
{
unsigned count = 0;
for (const SomeStruct *temp = list; temp != NULL; temp = temp->next)
++count;
return count;
}
I suspect that you decided to treat the element pointed by the list as the unused and reserved "header" element. Indeed, sometimes it might make sense to do implement lists that way. But I see nothing like that stated in your post. Did they tell you to treat it that way specifically?
well you don't have to evaluate the indirection temp->next twice for each iteration.
you can simply do
int count( SomeStruct const* pNode )
{
int result = 0;
while( pNode != 0 )
{
++result;
pNode = pNode->next;
}
return result;
}
Also, as WhozCraig notes, your code was logically wrong (yielding an off by one result), not just potentially inefficient.
Codility may be using a circularly linked list to check, in this case, your code will never end.
Using STL trivilailzes this though, as it has a List<> with a size method.