Understanding complexity of deleting duplicates from a linked-list - c++

I have written this program to delete duplicate nodes from an unsorted linked list:
#include<bits/stdc++.h>
using namespace std;
/* A linked list node */
struct Node
{
int data;
struct Node *next;
};
// Utility function to create a new Node
struct Node *newNode(int data)
{
Node *temp = new Node;
temp->data = data;
temp->next = NULL;
return temp;
}
/* Function to remove duplicates from a
unsorted linked list */
void removeDuplicates(struct Node *start)
{
// Hash to store seen values
unordered_set<int> seen;
/* Pick elements one by one */
struct Node *curr = start;
struct Node *prev = NULL;
while (curr != NULL)
{
// If current value is seen before
if (seen.find(curr->data) != seen.end())
{
prev->next = curr->next;
delete (curr);
}
else
{
seen.insert(curr->data);
prev = curr;
}
curr = prev->next;
}
}
/* Function to print nodes in a given linked list */
void printList(struct Node *node)
{
while (node != NULL)
{
printf("%d ", node->data);
node = node->next;
}
}
/* Driver program to test above function */
int main()
{
/* The constructed linked list is:
10->12->11->11->12->11->10*/
struct Node *start = newNode(10);
start->next = newNode(12);
start->next->next = newNode(11);
start->next->next->next = newNode(11);
start->next->next->next->next = newNode(12);
start->next->next->next->next->next =
newNode(11);
start->next->next->next->next->next->next =
newNode(10);
printf("Linked list before removing duplicates : \n");
printList(start);
removeDuplicates(start);
printf("\nLinked list after removing duplicates : \n");
printList(start);
return 0;
}
Does finding each element in the hash table affect the complexity? If yes what should be the time complexity of this algorithm considering that the set is implemented as a Binary Search tree where the cost of searching an element is O(logn) in worst case.
According to me T(n)=T(n-1)+log(n-1) ie. the nth element will perform log(n-1) comparisons (ie the height of tree with n-1 elements)
Please give a mathematical analysis.

Does finding each element in the hash table affect the complexity?
Well, in your code you are using unordered_set which has an average complexity of O(1), so the simple answer is - No.
...considering that the set is implemented as a Binary Search tree where the cost of searching an element is O(logn) in worst case.
Again, you have chosen unordered_set which is not a binary search. I believe some of the implementation of set use Red/Black trees and you would be looking at O(logN), but with the unordered_set it should be constant time. So now the only issue is the traversal of your linked list. Which, since you are just walking it in one direction while visiting each node, is an O(N) operation.

Related

Deletion of second largest element from a singly linked list

I'm currently trying to write a data structure framework for myself. Deletion of the second largest node from a singly linked list works flawlessly in ordinary cases. But fails in a particular one. Here's what I've already tried :
//node.h
typedef struct Node {
int value;
struct Node *nextNode;
} Node;
//linkedlist.h
typedef struct LinkedList{
Node *head;
int count;
} LinkedList;
//liblinkedlist.c
int deleteSecondLargest(LinkedList *list){
if(list->count==0)
return 1;
if(list->count==1)
return 2;
Node *temp = list->head;
Node *largest = temp;
Node *prev = NULL;
Node *prev1 = NULL;
Node *ptr = temp;
//finding the second largest node
while(temp!=NULL){
if(temp->value > largest->value){
largest = temp;
}
else if((temp->value!=largest->value) && (temp->value > ptr->value)){//here's the code failing
prev1 = prev;
ptr = temp;
}
prev = temp;
temp = temp->nextNode;
}
//deleting it
if(ptr==list->head)
list->head = list->head->nextNode;
else
prev1->nextNode = ptr->nextNode;
free(ptr);
list->count--;
return 0;
}
The code fails in the commented block whenever the items in the list are in the order of 1332->34->N.
I can understand why it is failing because both temp and ptr is holding 1332 and else if is returning false in the second iteration, but I can't find any solution to it. Also, the files in which the functions reside has been commented above the function definition.
Any help?
As far as I see, you have a problem with the first part of your code: finding the second-largest element in a single-linked list.
In fact, there're three problems in this code:
The ptr is initialized with first element, which may be too large to be the second maximum.
No node is ever demoted from largest to ptr. That means, for list 34 -> 1332 -> N your code also does not work.
If two maximums have equal values, second one is ignored. That means, for list 123 -> 123 -> N your code also does not work.
The algorithm of finding two maximums works as follows:
Initialization: initialize two current maximums with the lowest possible values or special "uninitialized" flag.
In loop over all the elements:
Update both maximums using the current value.
Implementation:
// Initialization
Node *largest = nullptr; // for maximum, nullptr means "not initialized"
Node *largest2 = nullptr; // for second maximum, nullptr means "not initialized"
Node *prev_largest = nullptr; // for previous node for maximum
Node *prev_largest2 = nullptr; // for previous node for second maximum
// Iterations
for (Node *cur = list->head, *prev = nullptr; // start of the loop: current node is head, prev is null
cur != nullptr; // end of the loop: current node is null
prev = cur, cur = cur->nextNode) { // loop iteration: move both current and prev nodes forward
if (largest == nullptr || cur->value > largest->value) { // check if we need to update maximum
// the node which was maximum is now second maximum
prev_largest2 = prev_largest;
largest2 = largest;
// current node is now maximum
prev_largest = prev;
largest = cur;
} else if (largest2 == nullptr || cur->value > largest2->value) { // check if we need to update second maximum
// current node is now second maximum
prev_largest2 = prev;
largest2 = cur;
}
}
// End of algorithm
// Second maximum is now in variable largest2
// Previous node for second maximum is now in variable prev_largest2
Also, please note this algorithm works even if your list contains less than 2 elements (in this case largest2 will be nullptr at the end).

bubble sort linked list not sorting

Why won't my bubble sort algorithm sort the linked list?
When given a list, and calling the method, it will output the same list. What's wrong with my current logic inside my for loop?
private:
IntNode *head, *tail;
node structure:
struct IntNode
{
int data;
IntNode * next;
};
bubble sort method:
void NodeSLList::SortList()
{
if (head == NULL || head->next == NULL)
return;
IntNode * current = head;
IntNode * nextElement = current->next;
IntNode * temp = NULL;
int changed = 1;
while (changed)
{
changed = 0;
for (current; (current != NULL) && (nextElement = NULL); )
{
if (current->data > nextElement->data)
{
temp = current->next;
current->next = nextElement->next;
nextElement->next = temp;
changed = 1;
}
current = current->next;
nextElement = nextElement->next;
}
}
}
The problem is caused by assigning in the for-loop, instead of comparing.
If you are implementing a linked list, may I suggest using a sentry, instead of a head
and NULL as end. This removes all 'corner-cases' during inserts and removes.
A sentry-node always exists, contains no data, points to the first item,
and the last item points to it.
May I also suggest using Mergesort, it works well for linked list, running in O(NlogN),
and having no space overhead. You can find an implementation here
Try running it through a debugger. If you look at the value of current on the second time round the changed loop, you will see that current is still null, so the second time round the changed loop it will not go through the current loop.

Reverse Traversing singly linked list in C++

Recently I have been asked this question on an interview. All I could do is traverse from 9 to 1 from a linked list starting from 0 to 9. Here is the code:
#include <iostream>
typedef struct node {
int data; // will store information
node *next; // the reference to the next node
};
node *head;
int printList(node *traverse) {
if (traverse->next == NULL) {
return -1;
}
traverse=traverse->next;
printList(traverse);
cout << traverse->data << endl;
return 0;
}
int main() {
node *temp = NULL;
node *begin = NULL;
for (int i = 0; i < 10; i++) {
temp = new node;
temp->data = i;
if (begin == NULL) {
begin = temp;
}
if (head != NULL) {
head->next = temp;
}
head = temp;
head->next = NULL;
}
head = begin;
printList(head);
return 0;
}
1) How can I print 0(the first element) with the printList() recursive function?
2) How can I replace printList() recursive function with while loop?
3) If asked in an interview, does the main() function has proper node initialisation and insertation?
They are four possible ways to achieve this, each of which having its own merits.
Recursion
void print_list(node* traverse)
{
if (traverse == NULL) return;
print_list(traverse->next);
std::cout << traverse->data << std::endl;
}
This is maybe the first answer to come in mind. However, the process stack size is limited, so recursion has a pretty low limit. A big list will provoke a stack overflow. This is not a very good design in C++.
Iteration
void print_list(node *n)
{
using namespace std;
deque<node*> res;
for(;n != NULL; n = n->next) res.push_back(n);
for_each(res.rbegin(), res.rend(), [](node* n){cout << n->data << endl;});
}
Of course, if you want to make it the iterative way, you will need to stack the node pointers yourself (on the process heap) and not delegate this job to the call stack. This method lets you print far bigger lists, and is O(n) in computations. It is, however O(n) in memory usage, but you already have a list which use O(n) memory. So this should not be an issue. However, you may really need to avoid memory consumption. Which brings us to the next idea.
Double iteration
void print_list(node *head)
{
node* last = NULL;
while(last != head)
{
node* current = head;
while(current->next != last)
current = current->next;
std::cout << current->data << std::endl;
last = current;
}
}
This may seem a dumb solution, as it has O(n^2) complexity, but that is computation-complexity. It has O(1) memory complexity and, depending on the actual context and exact problem, it may be the answer you need. But this O(n^2) complexity is a lot to pay. Especially if n is so big you wanted to avoid another O(n) allocation. Which brings us to the last idea.
Hack the container
void print_list(node *head)
{
node* last = NULL;
for(node* next; head != NULL; head = next)
{
next = head->next;
head->next = last;
last = head;
}
for(node* next; last != NULL; last = next)
{
next = last->next;
last->next = head;
head = last;
cout << last->data << endl;
}
}
You first modify the container, then iterate in your new order. On a single-linked list, you can just reverse the links, then reverse-iterate while reversing the links again. The beauty of it is it stays O(n) in computing, and O(1) in memory. The problem is that you invalidate the full container while doing this : your outputing operation does not leave the list constant : this is not exception-safe: if your operation fails in middle of iteration, the list is not valid anymore. This may or may not be an issue depending on the problem.
There's an old trick for traversing the list in reverse with a while loop.
You walk the loop in the forward direction, but as you leave each node, you reverse the link -- i.e., you get its current value (the pointer to the next node), but then set it so it contains a pointer to the previous node. When you reach the end of the list, you now have a singly-linked list that goes the opposite direction -- i.e., following the pointers will take you back to the beginning of the list.
So then you walk back to the beginning, printing each node's value out as you go, and (again) reversing the links so when you're done, the list is as it started, with links going from beginning to end.
Note, however, that this can lead to problems in (for one obvious example) multi-threaded code. The entire traversal of the list from beginning to end and back to the beginning has to be treated as a single, atomic operation -- if two threads try to traverse the list simultaneously, very bad things will happen. Likewise, making this exception-safe can be somewhat challenging as well.
IOW, these are rarely effective solutions to real problems (but the same is generally true of linked lists in general). They are, however, effective ways of showing an interviewer that you can play stupid linked-list tricks.
If your list is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:
1) You want to output the list reversed, your printList should look like this:
int printList(node* traverse)
{
if (!traverse)
return (-1);
printList(traverse->next);
std::cout << traverse->data << std::endl;
return (0);
}
2) It is not really possible with a while loop, unless you do really ugly things like concatenating every node's data to the beginning of the result string that you will print.
3) Your main seems very strange to me. I don't understand why your 'head' variable is global, why not in the main itself?
Last but not least, why don't using std::list?
I was asked a question containing this in the interview. It was intended to traverse the single list from both ends simultaneously. So reversing the single list was not an option. Also, memory space complexity should be O(1). I try to solve with nested loops with O(n^2) time complexity. However, more effective way is to use XOR linked list which was mentioned by #chuckcottrill and #jerry-coffin. Single list can be converted to XOR linked list by applying xor operation to prev and next pointers of node. XOR linked list based on the following property of XOR operation.
a^a^b = b (order of left side is not important)
Let's consider a node in single list and its neighboring nodes:
X: address of prev node , Y: address of next node
While converting to XOR list Y' = X^Y (Y': new value of Y)
While reverse traversing on XOR list Y^(Y') =Y^Y^X=X
So we can attain prev node (X) and do reverse traversal. The following code converts the single list to XOR linked list and does reverse traversal (forward traversal is also possible at the same time):
node* curr = head;
node* prev = NULL;
node* next= curr->next;
while (curr) {
next = curr->next;
// apply xor to prev and next
curr->next = (node*)((uintptr_t)(prev)^(uintptr_t)(next));
// move pointers forward
prev = curr;
curr = next;
}
// prev becomes tail node
// -- Reverse Traversal --
curr = prev ; // curr points to tail
prev = NULL;
node* temp;
while (curr) {
cout << curr->data << " ";
temp = curr;
// apply xor to prev and next
curr = (node*)((uintptr_t)(prev)^(uintptr_t)(curr->next));
prev = temp;
}
Please correct me if I am wrong.
This solution uses an Iterative approach. An extra "previous" pointer is used to maintain the node that will eventually follow the node in the reverse order of the list.
public static Nodes reverse(Nodes head){
Nodes temp;
Nodes previous=null;
while(head!=null)
{
temp=head.next;
head.next=previous;
previous=head;
head=temp;
}
return previous;
}
1) Change
if (traverse->next == NULL)
to
if (traverse == NULL)
2)
while(traverse != NULL) {
// print sth
traverse = traverse->next;
}
3) seems ok to me. Why do you declare head outside of main?
traverse->next = traverse;
A possible solution you could use. Another possibility,
traverse = (traverse->next)- (traverse);
but you must error check for over/underflow.

C++ Linked list implementation crashing

I am trying to implement a linked list for a data structures class and I am having some difficulty with the searching portion of the algorithm.
Below is the offending code, which I have tried to implement following the pseudo-code in the MIT introduction to algorithms text:
//
// Method searches and retrieves a specified node from the list
//
Node* List::getNode(unsigned position)
{
Node* current = m_listHead;
for(unsigned i = m_listSize-1; (current != 0) && (i != position); --i)
current = current->next;
return current;
}
The head at this point in the program is the 4th node, which contains the value of int 5. the problem appears to be in the body of the for-loop, where the pointer to the node object is assigned to the next node. But this is going beyond the head of the node, so it is essentially pointing at some random location in memory (this makes sense).
Shouldn't the algorithm be moving to the previous Node instead of the next Node in this case? Below is the pseudo-code:
LIST-SEARCH(L, k)
x <- head
while x != NIL and key != k
do x <- next[x]
return x
Also, here is the header file for my Linked list implementation. I haven't tried to implement it in Template form yet just to keep things simple:
#ifndef linkList_H
#define linkList_h
//
// Create an object to represent a Node in the linked list object
// (For now, the objects to be put in the list will be integers)
//
struct Node
{
// nodes of list will be integers
int number;
// pointer to the next node in the linked list
Node* next;
};
//
// Create an object to keep track of all parts in the list
//
class List
{
public:
// Contstructor intializes all member data
List() : m_listSize(0), m_listHead(0) {}
// methods to return size of list and list head
Node* getListHead() const { return m_listHead; }
unsigned getListSize() const { return m_listSize; }
// method for adding a new node to the linked list,
// retrieving and deleting a specified node in the list
void addNode(Node* newNode);
Node* getNode(unsigned position);
private:
// member data consists of an unsigned integer representing
// the list size and a pointer to a Node object representing head
Node* m_listHead;
unsigned m_listSize;
};
#endif
Implementation of addNode method:
//
// Method adds a new node to the linked list
//
void List::addNode(Node* newNode)
{
Node* theNode = new Node;
theNode = newNode;
theNode->next;
m_listHead = theNode;
++m_listSize;
}
Try this to construct the list:
void List::addNode(int number)
{
newNode = new Node;
newNode -> number = number;
newNode -> next = m_listHead ;
m_listHead = newNode;
++m_listSize;
}
It will add nodes to the head. Perhaps you may wish to store the pointer to the tail and insert the nodes there.
Unfortunately your code doesn't resemble the pseudo code you supply.
The pseudo-code is for searching a linked-list for a key, not a position.
The pseudo code reads as:
Assign head to (node) x.
while x isn't null and the key inside the current node (x) doesn't match k
assign x->next to x
return x
The returned value is either a pointer to the node that contains k or null
If you're trying to find the node at a given position your loop would be (note this is assuming you're going to use a zero-based index for accessing the list):
Assign head to (node) x
assign 0 to (int) pos
while x isn't null and pos not equal to given position
assign x->next to x
increment pos
return x
The result will either be a pointer to the node at the given position or null (if you hit the end of the list first)
Edit: Your code is very close to the latter if that's what you're trying to do ... can you see the difference?
Edit because I like homework where the OP asks the right questions :)
Node* List::getNodeContaining(int searchValue)
{
Node* current = m_listHead;
while (current != 0 && current->number != searchValue)
{
current = current->next;
}
return current;
}
Node* List::getNodeAtPos(int position)
{
Node* current = m_listHead;
int pos = 0;
while (current != 0 && pos != position)
{
current = current->next;
pos++;
}
return current;
}
You list is very different from what a normal list ADT looks like. Rather than returning nodes, which would require the client know about the list implementation, you return and accept the type you're making a list of.
In this case you're making a list of integers, sou you'd want
public:
void add(int num); //prepends an Item to the list
int get(int pos);
The implementations of both are simple. Add makes a new node, and links it in;
void List::add(int num)
{
Node *newNode = new Node;
newNode->number = num;
newNode->next = m_listHead;
m_listHead = newNode;
m_listSize++;
}
Then get is easy too:
int List::get(int pos)
{
if(pos>m_listSize)
;//throw an error somehow
Node *tmp = m_listHead;
while(pos-->0)
tmp=tmp->next;
return m->number
}

How can I use iteration instead of recursion to input values into a linked list?

Ok so let's say we have a linked list of characters with a head pointer. How can I create a loop to enter a string of characters into the linked list? My problem is when I think of head and head->next and head->next->next . . . it only seems natural to use a recursive function to set the characters at each node.
It's trivial to do it with iteration. You would just start at head, and use a loop to iterate over the list by doing current = current->next, until you hit a NULL.
Basically something like:
node* n = head;
while (n) {
// ... do something with n
n = n->next;
}
As you are using C++, then using std::list and an iterator would seem to be the way to go. Writing your own linked list is OK as a learning exercise, but please don't use such a thing in real code.
Assuming your linked list already has enough space in it:
node *n = head;
char *input = "hello list";
int len = strlen(input);
for (int i=0;i<len;i++)
{
n->data = input[i];
n=n->next;
}
Otherwise you need to check each time for a null value before adding the data:
node *n = head;
char *input = "hello list";
int len = strlen(input);
for (int i=0;i<len;i++)
{
if !(n)
{
break;
}
n->data = input[i];
n=n->next;
}
Another solution would involve adding a new element when you hit the end:
node *n = head;
char *input = "hello list";
int len = strlen(input);
for (int i=0;i<len;i++)
{
n->data = input[i];
if (!n->next && i < len-1)
{
n->next = new Node;
}
n = n->next;
}
First, it depends on whether it's a doubly-linked list, or a singly linked list -- but it sounds like you're dealing with a singly-linked list.
With a singly linked list it's easiest to add nodes to the beginning of the list. If that works for you, something like:
node *head; // head of the list
// insert node:
new_node->next = head;
head = new_node;
If you want to add to the end of the list, you walk through the list first:
// If the list is empty, just add the node at the head:
if (head == NULL) {
new_node->next = head;
head = new_node;
}
else {
// There's already data in the list, so walk to the end of the list:
node *pos;
for (pos=head; pos->next!=NULL; pos=pos->next)
;
new_node->next = NULL;
pos->next = new_node;
}
As a general rule, neither of these makes a lot of sense though. It's only really sensible to use a linked list when you plan on doing insertions and deletions somewhere in the middle of the list, and you normally save the position (i.e. a pointer to) where you're going to do the insertion or deletion.