I'm trying to delete the n'th element, which is a random number from 1 to n.
My code does this fine (correct element is deleted and surrounding elemnts are connected) but when it comes to being efficient, it is crashing when I un-comment the line delete (nodeToRemove); and I'm not sure why. Does anyone have any insight?
Assuming my struct looks like :
struct Node {
int data; // The data being stored at the node
Node *next; // Pointer to the next node
};
//------------------------------------------------------------------------------
void deleteNthElement (Node * & head, Node * &temp, int random)
{
temp = head;
Node *nodeToRemove;
if (random == 1)
{
nodeToRemove = temp;
head = head->next;
}
else
{
for (int i = 1; i < random - 1; i++)
temp = temp->next;
nodeToRemove = temp->next;
temp->next = temp->next->next;
}
// delete (nodeToRemove); <----- uncommenting this leads to crash,
}//end deleteNthElement()
//------------------------------------------
int main()
{
Node *head = NULL;
Node *temp;
Node *listarray[n[i]];
int n[] = {1000, 5000, 9000, 105000, 400000, 500000, 505000, 800000, 995000, 1000000};
for (int j = 0; j < n[i]; j++)
listarray[j] = new (Node);
//start timer
begin = clock();
//fill it
for (int j = 0; j < n[i]; j++)
{
listarray[j]->data = (rand() % n[i] + 1);
insertNodeInOrder (head, temp, listarray[j]);
}
//delete it
for (int j = 0; j < n[i]; j++)
deleteNthElement (head, temp, (rand() % (n[i] - j) + 1));
//deallocate
for (int j = 0; j < n[i]; j++)
delete listarray[j];
delete *listarray;
//end timer
}
You're picking the wrong Node, you want:
...
nodeToRemove = temp;
...
At least, you have to check for the end of the list, that is, you need to avoid access to null pointers (i really hope you set next to 0 at the end of the list). If you add the allocation parts, I will extend my answer.
void deleteNthElement (Node * & head, Node * &temp, int random)
{
temp = head;
Node *nodeToRemove;
if (random == 1)
{
nodeToRemove = temp;
if(head != 0)
head = head->next;
} else {
for (int i = 1; i < random - 1; i++)
if(temp != 0)
temp = temp->next;
else throw 1; // no such index; throw something more verbose :)
if(temp == 0)
throw 1; // same situation as above
nodeToRemove = temp->next;
if(nodeToRemove == 0)
throw 1; // same situation as above
temp->next = temp->next->next;
}
if(nodeToRemove == 0)
throw 1; //no such index; ... as the above 3
delete nodeToRemove;
}//end deleteNthElement()
Some clean-up first:
Why pass in two pointers here? do you need the value of temp out? If so why not return it?
Also why the node * & temp? (I can see why it is done for head).
int random should probably be called something like index as it describes the functionality better (as far as the function is concerned, there is nothing random about it).
The same with temp.
I propose:
void delete_element (Node* &head, int index)
{
Node* parent_node = head;
Node* node_to_remove;
//...
We don't need temp if we are removing the head. Also, we generally 0 index things, so we should reflect that too. Thus it becomes:
if (index== 0)
{
node_to_remove= head;
head = head->next;
}
Now we get to the main bit. The loop is just there to step through to the parent node of the node to delete, so we can simplify it a little and add checks to make sure we can't 'fall off' the end of the list.
We then have to make sure there is a node to remove, (so another check). We don't need to check for a node beyond as assigning nullptr isn't a problem (I am assuming that an invalid pointer is set to nullptr here):
{
while(--index && parent_node->next){ //pre-decrement means we stop before the one we want (parent)
parent_node = parent_node->next;}
if (parent_node->next){node_to_remove= parent_node->next;}
else {return;} //no point deleting it if it doesnt exist
parent_node->next = node_to_remove->next;//less indirection is always good. Ok if this is nullptr
}
Incidentally, this fixes a probable off by one error. Which is probably your problem (did it crash every time? only when deleting the last element? next to last?
Now we just need to delete it.
Putting it all together:
void delete_element (Node* &head, int index)
{
Node* parent_node = head;
Node* node_to_remove;
if (index== 0)
{
node_to_remove= head;
head = head->next;
}
else
{
while(--index && parent_node->next){ //pre-decrement means we stop before the one we want (parent)
parent_node = parent_node->next;}
if (parent_node->next){node_to_remove= parent_node->next;}
else {return;} //no point deleting it if it doesnt exist
parent_node->next = node_to_remove->next;//less indirection is always good. Ok if this is nullptr
}
delete node_to_remove;
return;
}
And that should work fine. The checks will prevent us dereferencing null pointers which was (probably) what caused you to crash. Can't tell without full code.
Related
Not sure if there is a simple and better way to implement this function?
void insert(Node* &head, int element, int position) {
Node* current = new Node;
current->data = element;
current->next = NULL;
if (position == 1) {
current->next = head;
head = current;
return;
}
else {
Node * current2 = head;
for (int i = 0; i < position - 2; i++) {
current2 = current2->next;
}
current2->next = current2->next;
current2->next = current;
}
}
A better way would be to make this function without null pointer access. You are missing all the necessary error checking.
But if you have to use this function you are already doing something wrong. The operation takes O(n) time. And if you build your list using only this function then you already have O(n^2) time. Using a balanced tree or heap would give you O(n * log n) time which makes a huge difference even for relatively small n. So think again why you need to insert at a given position and think about more suitable data structures.
A simpler implementation, and actually used in real code a lot, is to implement insert_before(before, data) or insert_after(after, data) with a doubly linked list. Both of which would get an item in the list and a new item to insert and place the new item before or after the old one on O(1) time.
Some boundary check is needed (please find the comments inline):
int listLen(Node *head)
{
int len = 0;
while (head != nullptr)
{
len++;
head = head->next;
}
return len;
}
void insert(Node* &head, int element, int position)
{
if (head == nullptr) // do nothing if head is nullptr
return;
if (position < 0) // insert to the begin if position is negative
position = 0;
else
{
int len = listLen(head);
if (len < position) // position is out of range, insert to the end
{
position = len;
}
}
if (position == 0)
{
Node *next = head;
head = new Node(element);
head->next = next;
}
else
{
int curPos = 0;
Node *curNode = head;
while (curPos < position - 1) // move to position
{
curNode = curNode->next;
curPos++;
}
Node *next = curNode->next; // do insertion
curNode->next = new Node(element);
curNode->next->next = next;
}
}
I have a problem, I am trying to create a list that deletes a highest value holding number, or all numbers with the same value if the value is highest in the list. Thank you for any kind of tips.
// n,n1,head,next - are pointers
int j = 0; //this number helps to put pointer forward by one place
while(n!=0){//should go through every digit of the list
if(head == 0){
cout << "list is empty" << endl;
}
else{
n = head;
n1=0; // n1 and n are pointers
while(n!=0){
if(n->sk == maxx){//searches for maximum digit in the list
break;
}
else{
n1=n;
n=n->next;
}
}
if(head == n){
head = head->next;
}
else{
n1->next = n->next;
}
delete n; // deletes the pointer holding the highest value
}
n = head; //problem is here or somewhere below
j++;
for(int i=0; i<j;i++){ // this loop should make the pointer point to the first
n = n->next; // number, then the second and so on until the end of list
} // and all the numbers inside the list with the value that
} // equals "maxx" should be deleted
You should dereference the pointers. Right now, you're pointing to their addresses. See if that helps resolve your problem.
Ok, the problem (the most of it) is the code:
while(n!=0){
if(n->sk == maxx){
break;
}
else{
n1=n;
n=n->next;
}
}
If you find the maxx value you should delete that node and continue to searching, don't break. This way you don't need so much code for this task.
while (n != 0){
if (n->sk == maxx){
node *prev = n->prev; // The previous node.
node *tmp = n; // this assume you have a class node.
// temporaly holds the pointer to n.
prev->next = n->next; // Connect the previous node with the following one.
n = n->next; // advance n to the next node in the list.
delete tmp; // delete the node.
}
}
If I understand correctly what you want, you can just iterate over your list and save the pointer for deletion:
it = head;
pos = nullptr;
while (it != nullptr) {
if(it -> sk == maxx) {
pos = it; // save ptr
it = it -> next;
delete pos; // delete saved ptr
pos = nullptr;
}
}
So I've got this insert function for a doubly linked list that is working for the most part just up until I try to insert a new node at a given index. I'm having trouble with linking it correctly to the nodes before and after it, if anyone could see why, I keep getting errors when I try to assign one of the points I'll point out in the code:
void insert(int index, ItemType& item)
{
int pos = 0;
Node* current = new Node;
Node* n = new Node;
n->info = item;
if (index >= 0 && index <= size)
{
if (size == 0)
{
head = n;
tail = n;
n->next = NULL;
n->prev = NULL;
size++;
return;
}
else if (index == 0)
{
n->prev = NULL;
n->next = head;
head->prev = n;
head = n;
size++;
return;
}
else if (index == size)
{
n->next = NULL;
n->prev = tail;
tail->next = n;
tail = n;
size++;
return;
}
else if (index < size/2)
{
current = head;
while(pos != index)
{
current = current->next;
pos++;
}
}
else if (index > size/2)
{
int endpos = size - 1;
current = tail;
while(endpos != index)
{
current = current->prev;
endpos--;
}
}
n->next = current;
current->prev->next = n; // HERE is where the code breaks, don't know why.
n->prev = current->prev;
current->prev = n;
size++;
}
}
So the code breaks at the current->prev->next = n statement stating there is an access violation writing location. So I'm not sure if that is coded right or if I've messed up in pointing assignments in earlier code. If anyone knows why its doing that and can point me in the right direction that would be awesome. Thanks.
From my observation,
Your code fails when index = size/2.
When there are two elements(size == 2) and when you try to insert at position 1, then current->prev->next = n; is meaningless
Do one of these changes else if (index <= size/2) or else if (index >= size/2)
If current is the first node in the list, then current->prev will be NULL, so current->prev->next will cause problems. You should check if current is the first item in the list before this line.
Also, your code leaks memory because you are allocating a new Node for current and you do not delete it. Since you are using current to move through the list rather than to create a new node, you should declare it as just
Node* current;
rather than
Node* current = new Node;
I'm trying to print the alphabet backwards using linked lists but I can't get the 'a' to show up. For some reason it skips it and I can't figure it out. Here is my code:
int _tmain(int argc, _TCHAR* argv[])
{
char s[]="abcdefghijklmnopqrstuvwxyz";
node *head;
node *temp;
node *current;
head = new node; // create the head of the linked list
head->data = s[25];
head->next = NULL;
temp = head; // get ready for the loop - save the head in temp - you are going to change temp in the loop
for(size_t i = 25; i >= 1; i--) // create the rest of the linked list
{
current = new node; // make a new node
current->data = s[i]; // set it's data member
current->next = NULL;
temp->next = current; // point to the new node
temp = current; // make temp point to current node (for next time through)
}
node *ptr = head; // set a ptr to head, then you are going to "increment" the pointer
while (ptr != NULL)
{
cout << ptr->data; // print out the linked list
ptr = ptr->next; // increment the linked list
}
cout << endl;
system("pause");
return 0;
}
does anyone know why it's happening? I think something is wrong in my for loop. Thank you!
The problem is that you are leaving out the case i=0 from your for loop.
Change your for loop to this:
size_t i = 25; // 'z' was already added
do
{
--i;
current = new node; // make a new node
current->data = s[i]; // set it's data member
current->next = NULL;
temp->next = current; // point to the new node
temp = current; // make temp point to current node (for next time through)
} while ( i != 0 );
The reason you can't simply do for(size_t i = 25; i >= 0; i--) is because i is unsigned, and so it will always be the case that i >= 0 and thus the loop will never terminate, or more likely you will get a segmentation fault.
for(size_t i = 25; i >= 0; i--)
'a' is the index 0 in your string, you never get to index 0.
I've created a Hash-table and I want to remove a node from the linked-list. The code works for removing the first node but not for removing others.
void intHashTable::remove(int num){
int location = ((unsigned)num) % size;
Node * runner = table[location];
int checker;
if(runner->next == NULL){
if(num == table[location]->num){
table[location] = NULL;
}
}else{
if(table[location]->num == num){
table[location] = table[location]->next;
}else{
//This part doesn't seem to be working.
Node *temp = runner->next;
while(temp != NULL){
if(temp->num == num){
runner->next = temp->next;
delete(temp);
break;
}
}
}
}
}
You haven't updated temp to point to the next item within the loop:
temp = temp->next;
You also appear to represent an empty row with a NULL pointer in your table, but you don't handle this case properly in your code - if runner is NULL then you'll crash when you try to access runner->next in the first check. Also, you're failing to delete the node in some cases.
To fix these issues, you can update your code to something like this:
void intHashTable::remove(int num)
{
int location = ((unsigned)num) % size;
Node * runner = table[location];
if (runner != NULL) {
if (runner->num == num) {
delete runner;
table[location] = NULL;
} else {
while (runner->next != NULL) {
if (runner->next->num == num) {
Node *temp = runner->next;
runner->next = runner->next->next;
delete temp;
break;
}
runner = runner->next;
}
}
}
}
Also note that I've removed the brackets from delete, which is a C++ keyword and not a function.
If you use doubly-linked lists (i.e. with a previous pointer as well as a next) then you can simplify this code a little, although for something like a hash table where you only tend to iterate through in one direction it's probably not worth the expense of the extra pointer (an extra 8 bytes per item on a 64-bit system).
You didn't updated temp and runner variables inside loop:
while(temp != NULL)
{
if(temp->num == num)
{
runner->next = temp->next;
delete temp;
break;
}
runner = temp; // Keep previous element to change its next pointer when num found
temp = temp->next; // Advance current pointer to next element
}