Reversing a linked list using nested while loops - c

I need some help.
I have this homework assignment due on Wednesday and the only thing I haven't been able to do is reverse my linked list. I have literally been working on this all day.
My professor hasn't told us any way to do this and doesn't allow us to email him questions so I'm kind of stuck.
He wants us to accomplish this by using nested while loops and pointers called front & back, and newhead.
Below is my code. Let me know if you have any questions and thanks in advance.
NOTE: Code is compiled in Code::Blocks using the GNU compiler.
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int random_number;
struct node *next;
} Node;
typedef Node *Nodeptr;
void printout(Nodeptr);
void sum(Nodeptr);
void reverse(Nodeptr);
int main() {
Nodeptr head = NULL;
if ((head = malloc(sizeof(Node))) == NULL)
return 0;
head->random_number = rand() % 50 + 50;
head->next = NULL;
Nodeptr here = head;
Nodeptr newnode = NULL;
int n;
for (n = 0; n < 10; n++) {
if ((newnode = malloc(sizeof(Node))) == NULL)
return 0;
newnode->random_number = rand() % 50 + 50;
newnode->next = NULL;
here->next = newnode;
here = here->next;
}
printout(head);
sum(head);
reverse(&head);
printout(head);
return 0;
}
void printout(Nodeptr head) {
Nodeptr aux = head;
int n = 0;
while (aux != NULL) {
printf("The value of node no. %d is %d \n", n, aux->random_number);
aux = aux->next;
n++;
}
}
void sum(Nodeptr head) {
Nodeptr aux = head;
int n = 0, sum = 0;
while (aux != NULL) {
sum += aux->random_number;
aux = aux->next;
n++;
}
printf("The sum total of all nodes in this list is %d\n", sum);
}
void reverse(Nodeptr head) {
Nodeptr newhead = head;
Nodeptr back = NULL;
Nodeptr front = NULL;
while (back != NULL) {
front->next = back;
back = NULL;
front = head->next;
back = head;
while (front != NULL) {
front = newhead->next;
newhead->next = back;
back = newhead;
}
newhead = head;
}
}

If you reverse a linked list:
A -> B -> C -> D -> E (odd numbered case)
E -> D -> C -> B -> A
A -> B -> C -> D (even numbered case)
D -> C -> B -> A
Do you see how the items have changed place? In the odd numbered case, A swapped places with E, B with D and C didn't move.
Now do you have some idea how to do it via a loop? The outer loop goes from the start of the list to the half-way point, the inner loop locates the matching item to swap them.
Definitely not the most efficient way to reverse a linked list, but I guess it's just a way to get you think more about the data structure.
The reverse() you posted didn't make sense, because the pointer values are incorrect. With back initialized to NULL, the while loop will not execute at all.

I am not sure what API you teacher want you to use for reverse:
The simplest one would take a Node * and return the new head pointer. You would invoke it from main as head = reverse(head);
Here is a simple implementation of this approach:
Node *reverse(Node *head) {
Node *front = head;
Node *newhead = NULL;
while (front) {
Node *back = front->next;
front->next = newhead;
newhead = front;
front = back;
}
return newhead;
}
An alternative API would have reverse take a pointer to the head pointer, hence a Node **, and update that to point to the reversed list.
Here is how it works:
void reverse(Node **headp) {
Node *front = *headp;
Node *newhead = NULL;
while (front) {
Node *back = front->next;
front->next = newhead;
newhead = front;
front = back;
}
*headp = newhead;
}
Note that you do not need nested loops.
Note also that you should not hide pointers behind typedefs, it is bad practice, error prone and creates confusion for both the next reader and the original author.

Related

Bubble Sort on Linked List Shortening List

typedef struct Node{
int data;
struct Node* next;
}Node;
Node* initNode(){
Node* newN = malloc(sizeof(Node));
newN->data = rand() % (1000 -0);
newN->next = NULL;
}
void addNodes(int amount, Node* first){
Node* cur = first;
while(cur->next != NULL){
cur = cur->next;
}
for(int i = 0; i <= amount; i++){
cur->next = initNode();
cur = cur->next;
}
}
void printNodes(Node* first){
Node* cur = first;
int loopC = 0;
while(cur->next != NULL){
printf("%d-", cur->data);
loopC++;
cur = cur->next;
}
}
beginning of areas causing problem
I think that it is something wrong with the sort or swap functions causing the shortening of the linked list, not sure though.
void swap(Node* prev, Node* cur, Node* first){
Node* next = cur->next;
if(next==NULL){return;}
Node* nextO = next->next;
cur->next = nextO;
if(prev != NULL){prev->next = next;}
else{first = next;}
next->next = cur;
}
void bubbleSort(Node* first){
int switchC = -1;
while(switchC != 0){
switchC = 0;
Node* cur = first;
Node* prev = NULL;
Node* next = NULL;
while(cur->next != NULL){
next = cur->next;
if(next->data <= cur->data){
swap(prev, cur, first);
switchC++;
}
prev = cur;
cur = next;
}
}
}
end of areas causing problem
void main(){
Node* first = initNode();
addNodes(10, first);
printNodes(first);
bubbleSort(first);
printNodes(first);
}
inputed linked list: Node 0: value:383-Node 1: value:886-Node 2: value:777-Node 3: value:915-Node 4: value:793-Node 5: value:335-Node 6: value:386-Node 7: value:492-Node 8: value:649-Node 9: value:421-Node 10: value:362
Outputed Linked List: Node 0: value:383-Node 1: value:386-Node 2: value:421-Node 3: value:492-Node 4: value:649-Node 5: value:777-Node 6: value:793-Node 7: value:886
I don't see why you need a prev pointer in your bubbleSort function.
Also, you don't have to change pointers in your swap function. Since the size of the list won't change, you can merely swap the data field.
Be careful no to squeeze your code this much. It's hard to follow.
Start with a known data (such as the vector 100,99,...,89 below).
Will be much easier to debug.
Here's a possible fix with minimum changes to your code:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
int data;
struct Node* next;
}Node;
int count = 100;
Node* initNode(){
Node* newN = malloc(sizeof(Node));
newN->data = count--;
newN->next = NULL;
}
void addNodes(int amount, Node* first){
Node* cur = first;
while(cur->next != NULL){
cur = cur->next;
}
for(int i = 0; i <= amount; i++){
cur->next = initNode();
cur = cur->next;
}
}
void printNodes(Node* first){
Node* cur = first;
int loopC = 0;
while(cur != NULL){
printf("%d-", cur->data);
loopC++;
cur = cur->next;
}
printf("\n");
}
void swap(Node* node1, Node* node2)
{
int tmp = node2->data;
node2->data = node1->data;
node1->data = tmp;
}
void bubbleSort(Node* first)
{
int switchC = -1;
while(switchC != 0) {
switchC = 0;
Node* cur = first;
Node* next = NULL;
while ((next = cur->next) != NULL) {
if(next->data <= cur->data) {
swap(cur, next);
switchC++;
}
cur = next;
}
}
}
void main()
{
Node* first = initNode();
addNodes(10, first);
printNodes(first);
bubbleSort(first);
printNodes(first);
}
Output:
$ gcc main.c && ./a.out
100-99-98-97-96-95-94-93-92-91-90-89-
89-90-91-92-93-94-95-96-97-98-99-100-
You claim that the problem is in your Bubblesort implementation, but there is at least a problem in printNodes():
void printNodes(Node* first){
Node* cur = first;
int loopC = 0;
while(cur->next != NULL){
printf("%d-", cur->data);
loopC++;
cur = cur->next;
}
}
Observe that the last node in the list will not satisfy the condition cur->next != NULL, and therefore its value will not be printed. It is worth also mentioning at this point that the representations of the input and output linked lists presented in the OP were not produced by the version of printNodes() presented there, which begs the question of where they did come from.
It turns out that you are right about there being a bug in your sort, however. Both your swap() and your bubbleSort() are flawed. They accept the head pointer by value and return nothing, so when one of these functions tries to assign a different node to be the head node, that change does not propagate to the function's caller.
There are two possible solutions for that in the bubbleSort() function:
Pass a pointer to the head pointer:
void bubbleSort(Node **first) // ...
Erstwhile uses of first would need to be adjusted to *first.
Return the new value of first, making it the caller's responsibility to update its head-node pointer.
Node *bubbleSort(Node *first) {
// ...
return first;
}
The same applies to your swap() function, but there you have an additional option: restructure bubbleSort so that the prev argument to swap() is never null. You can do this by temporarily inserting a dummy head node in front of the true head, and structuring the sort passes so that the dummy is never evaluated for exchange with its successor. This permits you to make the head node an ordinary case instead of a special one. I leave the details to you to work out if you wish, but here's a start:
void bubbleSort(Node **first) {
Node dummy = { .next = *first };
// ...
*first = dummy.next;
}
If you pursue this then you should find that swap() does not need the third argument, as its prev will always be non-NULL (sometimes it may be &dummy). The simplification may even be enough that you decide you don't need a separate swap() function in the first place.

Delete first element in a linked sorted list

I'm new here and this is actually my first question.
I wrote this code and I can't delete the first element of the list, somehow I can delete other elements.
I have also a problem with the function reverse_list and I believe it is because I did not pass the argument as a reference.
Can I use the & in the argument in C programming?
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node * next;
};
struct Node * build_sorted_list () {
struct Node * head = NULL, * temp, * rear, * front;
int num;
printf ("please enter a number:");
scanf ("%d", &num);
while (num != 0) {
temp = (struct Node *) malloc (sizeof (struct Node));
if (temp == NULL) {
printf ("god damn");
break;
}
temp -> data = num;
if (head == NULL) {
temp -> next = NULL;
head = temp;
}
else if (num <= head -> data) {
temp -> next = head;
head = temp;
}
else {
rear = head;
front = rear -> next;
while (front != NULL && front -> data <= num) {
rear = front;
front = front -> next;
}
temp -> next = front;
rear -> next = temp;
}
printf ("enter number please:\n");
scanf ("%d", &num);
}
return head;
}
struct Node * reverse_list (struct Node * head) {
struct Node * rear, * mid, * front;
if (head != NULL || head -> next == NULL) {
return head;
}
rear = head;
mid = rear ->next;
front = mid -> next;
while (front != NULL) {
mid -> next = rear;
rear = mid;
mid = front;
front = front ->next;
}
mid -> next = rear;
head -> next = NULL;
head = mid;
return head;
}
struct Node * delete_num (int wanted, struct Node * head) {
struct Node * rear, * front;
if (head == NULL) {
return head;
}
if (head -> data == wanted) {
struct Node * temp = head;
head = head -> next;
temp = NULL;
return head;
}
rear = head;
front = head -> next;
while (front != NULL) {
if (front -> data == wanted) {
break;
}
rear = front;
front = front -> next;
}
if (front != NULL) {
rear -> next = front -> next;
}
free (front);
return head;
}
int main() {
struct Node * head;
int wanted;
head = build_sorted_list(); /* Please pretend this function exists */
reverse_list (head);
printf ("please enter a number to delete:");
scanf ("%d", &wanted);
delete_num (wanted, head);
free_list (head);
return 0;
}
As a rule of thumb with recursive functions that manage data structures. Operations on the structure should return the structure.
By definition, a linked list is either:
An empty list.
A node with a value, and a linked list as its tail.
A linked list has a recursive nature. You need to be very careful, recursive functions are very complex, despite the short amount of code.
In your main function you have a head pointer. It's not safe to assume that the head will remain the same. In fact, your problem comes when you remove the first element (head).
Your delete should be something like this:
struct Node * delete_num(int wanted, struct Node * head){
if( head == NULL)
return NULL; //Reached the end of the line, nothing to do.
if( head->data != wanted )
head->next = delete_num(wanted, head->next); //Haven't reached it yet, but
//the element must be down
//the line, I want to update
//my next in case it's the
//next node.
if(head->data == wanted){
struct Node * aux = head;
head = head->next;
free(aux);
}
return head;
}
This is assuming you only want to remove one element and your list doesn't admit repeated values.
Your call to this function from main should be:
head = delete_num(wanted, head);
Andres

Function to reverse order of linked list... last node does not link to rest of list

I have the following node structure in C:
#define SIZE 5
typedef struct node
{
int n;
struct node* next;
}node;
node* head = NULL;
void implement(int n); // create linked list
int length(void); // returns length of list
node* reverse(void); // reverses order of list
The list is implemented as follows:
void implement (int n)
{
for (int i = 0; i < n; i++)
{
node* new_node = malloc(sizeof(node));
if(new_node == NULL)
{
printf("out of memory!\n");
return;
}
new_node->n = i;
new_node->next = head;
head = new_node;
}
}
Which when printed to the terminal gives:
4 3 2 1 0
The head pointer is set to node 4.
I have written the following function to reverse the list as follows:
node* reverse(void)
{
node* cur = head;
node* next = NULL;
head = NULL;
while (cur->next != NULL)
{
next = cur->next;
cur->next = head;
head = cur;
cur = next;
}
return cur;
}
However, returning cur only gives:
0
Returning head provides:
1 2 3 4
So it is quite clear that the list is being reversed, but for some reason the last node (0) is not being linked to the remaining nodes. I am probably missing something obvious here, but can anyone give me any pointers? (no pun intended!)
Cheers
while (cur->next != NULL)
This stops when cur is at the last element (next is NULL); i.e. you don't process the last node.
Changing it to
while (cur != NULL)
and returning head should fix it.

insert node inbetween two others in a linked list

so I want to add inbetween two nodes of a linked list a new node whose data field basically contains the sum of the the previous and the next node. I can't understand why once I enter the while loop I can't get out of it. Any suggestions? Thank you.
Here's my code:
void modify_list (node *head) {
nodo *it = head;
nodo *prev = NULL;
int n_prev = 0;
int n_next = 0;
int sum = 0;
it = it->next;
prev = it;
while (it->next != NULL) {
it->data = n_next;
prev->data = n_prev;
sum = n_next + n_prev;
node *new;
new = malloc(sizeof(node));
if (new == NULL) {
printf("Error.\n");
return;
}
memset(nuovo, 0, sizeof(node));
new->data = sum;
prev->next = new;
new->next = it;
sum = 0;
prev = it;
it = it->next;
}
}
When you start the iteration, you are using:
it = it->next;
prev = it;
it and prev are pointing to the same node. A little while later, you are using:
prev->next = new;
which is the same as:
it->next = new;
That means it->next points to the new node. That means, you never really go past the newly created nodes.
You can fix this by using:
prev = it;
it = it->next;
before the start of the while loop.
I would make that more robust by using:
prev = it;
if ( it != NULL )
{
it = it->next;
}
At this time, it could be NULL. Change the conditional in the while statement to:
while (it != NULL) {
May I first suggest breaking your code up in smaller parts
Have a function that inserts a new object
For a singly linked list, this should insert after:
void InsertAfter( Node* n, DATA data )
{
Node* newnode = malloc(sizeof(Node));
newnode->data = data;
newnode->next = n->next;
n->next = newnode;
}
Then you have a function to find the insert-point
It could be for example
Node* FindLastSmaller( Node* n, DATA data )
{
while(true)
{
if( ! n->next ) return n;
if( n->next->data > data ) return n;
n = n->next;
}
}
Then they become easy to combine:
void InsertSorted( Node* n, DATA data )
{
Node* inspos = FindLastSmaller(n,data);
InsertAfter(inspos,data);
}
You can avoid adding special-cases for empty list, if head always
exists, and contains no data. (its a dummy node)

Removing an element from a doubly linked list

My code works for elements which are at the start of the linked list but not for the ones in the middle or the end. Appreciate your help!
void remove(){
if (!head)
printf("\nNo nodes to delete. List is empty.");
else{
int n;
struct node* help = head;
printf("Enter an element to delete: ");
scanf("%d", &n);
if(head->data == n){
help-> next->prev = 0;
head = help -> next;
} else{
while(help -> next){
if(help -> data == n){
help -> next -> prev = help -> prev;
help -> prev -> next = help -> next;
}
else help = help -> next;
}
}
}
}
First, use NULL not 0 for meaning NULL. In C, you need to. In C++ it's optional, but you tagged the question C.
help-> next->prev = 0;
You never check to see if there is a second element. If the list only has one element, this fails -- so it doesn't even always work for the first element.
while(help -> next){
if(help -> data == n){
This will stop you from ever deleting the last element, since you stop looking at the list once you get to (but before you deal with) the last element. But even if you didn't, the next lines:
help -> next -> prev = help -> prev;
help -> prev -> next = help -> next;
will cause you to crash if it's the last one, since it's not checking that there is another element after.
Also, the spaces before and after the arrow -> is pretty uncommon. I'd suggest not doing that anymore.
struct node *head, *tail; //global head & tail
int delete_item() {
int del_data = 0;
struct node *item = head, *tmp;
scanf("%d", &del_data);
while(item){
if(item->data == del_data){
tmp = item;
if(item->next){
//it's not tail
item->next->prev = item->prev;
}
else {
//it's tail
tail = item->prev;
if(tail)
tail->next = NULL;
}
if(item->prev){
//it's not head
item->prev->next = item->next;
}
else {
//it's head
head = item->next;
if(head)
head->prev = NULL;
}
//free memory
free(tmp);
}
//move forward
item = item->next;
}
return 0;
}
I was accidentally working on that until I found this way to delete the node
in given position :
void delete (int n) {
struct node *temp = head;
if (n == 1) {
head = temp->next;
(temp->next)->prev = head;
free(temp);
return;
}
for (int i = 0; i < n - 1; i++) {
temp = temp->next;
}
if (temp->next != NULL) {
(temp->next)->prev = temp->prev;
(temp->prev)->next = temp->next;
} else {
(temp->prev)->next = NULL;
}
free(temp);
}

Resources