0% found this document useful (0 votes)
21 views35 pages

04 LL C Review

Uploaded by

yaqi.huang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
21 views35 pages

04 LL C Review

Uploaded by

yaqi.huang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 35

C review

Single Linked Lists


Dynamic Memory
Alexandra Stefan

9/26/2021 1
Outline
• Brief C discussion: malloc/calloc/free Assume:
typedef struct node * nodePT;
• Static vs Dynamically allocated memory struct node {
• Drawing nodes and pointers int data;
struct node * next;
• “Cheat sheet” for linked lists };
• Code - solution and drawing for:
• Create a list from an array (array_2_list(…)), delete an entire list
(destroy_list(…) )
• Insert/dele a node after a node and from a list
• Swap two consecutive nodes in a list
• Array of linked list – example and drawing
• Steps for developing the solution and the code for problems that involve loops
• Steps for array_2_list(…)
• Program state at different times for array_2_list(…)
• Steps for destroy_list(…)
• Worksheets for the problems solved above
10ab abcd dabc 200c
10ab 9 abcd 1 dabc 7 200c 5 NULL

L data next
2
Dynamic memory management: malloc()/calloc()/realloc() and free()
• References:
• https://www.tutorialspoint.com/c_standard_library/c_function_malloc.htm
• https://www.cplusplus.com/reference/cstdlib/malloc/
• malloc() – requests a chunk of memory of a size (in bytes) given as an
argument. Returns a pointer (the memory address of the first byte in that
chunk) . It returns NULL if failed (if it could not reserve the required amount
of memory). This memory must be released with free().
• calloc() – similar and initializes all bits to 0. Takes number of items and size
of an item. It is useful when requesting memory for several items of the
same type. E.g. to store an array. This memory must be released with free().
• realloc() resizes the memory. It returns the pointer to the new memory and
frees the original. This memory must be released with free().
• free() – releases the memory allocated by any one of the allocating method
above when given the pointer returned by that method.
• Number of CALLS to free() must be EQUAL to the number of CALLS to
malloc and calloc. Otherwise memory leaks occurs.

Assume:
E.g. typedef struct node * nodePT;
nodePT L=NULL; struct node {
L = (nodePT)malloc(sizeof(struct node)); int data;
free(L); struct node * next;
}; 3
Static (S) memory vs Dynamic (D) memory
• Allocated when?
• S - Allocated before the program starts executing.
• D - Allocated and freed during the program execution. Can change size.
• See "Difference between Static and Dynamic Memory Allocation in C"
• Created how? 10ab 10
• S - With variable declaration. E.g. nodePT L; (or int n=10;) L (8B) N (4B)
• D - With malloc()/calloc()/realloc(). E.g. L=malloc(sizeof(struct node); 9 abcd

• Freed when? data next

• S - When the function call finished (for variables local to that function), or when the
program finishes (for global and static variables)
• D - when free() is called for that pointer.
• Named?
• S – Yes: Variables (created with variable declaration) are named memory boxes. Using
their name we read, or modify the content of that memory box. E.g.
printf("%d",N); N=20; int arr[20];
• D – No: Dynamic memory boxes (chunks) are UNNAMED. NO variable NAME is associated
with them at creation (and thus have no name). Can be accessed from their pointer. E.g. .
printf("%d",*int_ptr); printf("%d",L->data); L->next=NULL;
• Being aware of this difference may avoid confusion.
4
Static vs Dynamic memory - drawing
• Below the boxes (the memory) for A and B is static and for the node box it is
dynamic
• Also remember that when A=B the content of box labeled B is copied into box labeled A.

Total memory used: 8B+8B+12B=28B 5


Cheat sheet for Linked Lists 10ab
10ab
9 abcd
• Dynamically allocated struct vs local pointer variable -
• DO not confuse the two! Use malloc/calloc to allocate space for a node and then use a POINTER
VARIABLE to hold the address of nodes and move through them (just as you use variable j to move
through numbers, say 0 to N)
• Must have one malloc/calloc call for every NODE needed.
• CALLS to malloc = CALLS to free
• To delete a node or insert a node, we must know the node that will be BEFORE it – (for
single-linked lists)
• Check that any pointer dereference is not NULL. (i.e. should never have “NULL->”)
• Test cases:
• L is NULL, L has only one node,
• Node being worked on is the first node or the last node (e.g. for deletion)
• When swapping, NAME the nodes to avoid overwriting a link
• DRAW the data. MAKE UP values for the memory addresses and any other data needed.
• LOOP to iterate through all the nodes in a list (assuming L points to the first actual node of
the list)
for(curr=L; curr!=NULL; curr=curr->next)
curr is the variable referencing every node (just like j holds numbers 0 to N)
curr = L // makes curr point to the first node by holding the mem address of that
node (like j=0)
curr!=NULL; // this is true when curr points to a valid node. When curr is the
last node, curr->next is NULL, thus curr=curr->next makes curr be NULL
curr = curr->next // makes curr point to the following node (by holding
now the address of that node) 6
a000 nodePT array_2_list(int arr[], int N) Trace the
a000 9 1 7 5 code execution. Color over and fill in each box as it is created and/or
arr updated
0 1 2 3 N
10ab abcd dabc 200c

j newP lastP
L data next

// creates a single linked list from an array


nodePT array_2_list(int arr[], int N) {//TC=Θ(N), SC=Θ(1)
int j;
nodePT lastP = NULL, newP=NULL;
nodePT L = (nodePT)malloc(sizeof(struct node));
L->data = arr[0];
L->next = NULL;
Assume:
lastP = L; typedef struct node * nodePT;
struct node {
for (j = 1; j<N; j++) {
int data;
newP = (nodePT)malloc(sizeof(struct node)); struct node * next;
newP->data = arr[j]; };

newP->next = NULL;
lastP->next = newP; Note: this code can be written even
simpler, but this version is very explicit and
lastP = newP;
can be applied to other scenarios.
}
return L;
7
}
Function array_2_list Same code, but use function new_node()to create a node.
Advantages:
Two implementations 1. The code is more readable.
Assume: 2. The new_node() function can be used in other places.
typedef struct node * nodePT; 3. If a change (improvement or bug fix) is done new_node(),
struct node {
int data;
it is done only once. (No code duplication)
struct node * next;
}; nodePT new_node(int value_in) {
nodePT result =
For both functions: time: Θ(N), space: Θ(1) (nodePT) malloc(sizeof (struct node));
result->data = value_in;
nodePT array_2_list(int arr[], int N) {
result->next = NULL;
int j;
return result;
nodePT lastP = NULL, newP=NULL;
}
nodePT L = (nodePT)malloc(sizeof(struct node));
nodePT array_2_list(int arr[], int N)
L->data = arr[0];
int j;
L->next = NULL;
nodePT lastP = NULL, newP=NULL;
lastP = L;
nodePT L = new_node(arr[0]);
for (j = 1; j<N; j++) {
lastP = L;
newP = (nodePT)malloc(sizeof(struct node));
for (j = 1; j< N; j++) {
newP->data = arr[j];
newP = new_node(arr[j]);
newP->next = NULL;
lastP->next = newP;
lastP->next = newP;
lastP = newP;
lastP = newP;
}
}
return L;
return L;
} 8
}
Delete an entire list: void destroy_list(nodePT L)
10ab abcd dabc 200c
10ab 9 abcd 1 dabc 7 200c 5 NULL
Given data: L data next

NULL
Final data: L

// Time complexity: Θ(N),


// where N is the size of the list
void destroy_list(nodePT L) {
nodePT next, curr;
curr = L;
L = NULL; // can be skipped here
while (curr!=NULL) {
next = curr->next;
L=NULL so that no
free(curr);
variable points to memory
curr = next; that was freed: pointer
} 10ab should not be used
} anymore.
9
Insert a node after a given node – node operation
/* Inserts newP after the node "prev".
Note that this is works on nodes. It does not matter how a list is
represented. prev is just a node. */
void insert_node_after(nodePT prev, nodePT newP) {
if ((prev == NULL)||(newP==NULL)) {
printf("\n Cannot insert after a NULL node. No action
taken.");
} else {
newP->next = prev->next; //5
prev->next = newP; //6
}
} Case when prev->next is NULL works fine:
Because we never ACCESS (with ->) the
that address, but we only COPY that NULL
from one box into another
prev newP 6aaa 6aaa
prev newP
20 ????
8acf 6aaa 20 ????
dddd 8acf 6aaa
NULL
6 5
8acf dddd 6
8acf
3 dddd X 15
3 NULL
6aaa 6 6aaa
10
Insert in a list, L, after a given node (assumed from L) Θ(1)
/* Inserts in list L, a the new node newP, after the node prev.
If prev is NULL it means newP must be linked to the beginning of L
Uses the list representation (L points to the first node with data) */
nodePT insert_node(nodePT L, nodePT prev, nodePT newP){
if (prev == NULL) { // case 1: inserts at the beginning of the list L
newP->next = L; //2
return newP; //3
}
else { // case 2:
insert_node_after(prev, newP); //4 does not affect the list head
return L; //5
} 6aaa
} NULL 6aaa 20 10ab
prev newP 5
6
Case 1: 10ab 8acf abcd 4ddd
prev==NULL 10ab 7 3 abcd 15 .... 1
Returns 6aaa L

Case 2 6aaa

prev != NULL 8acf 6aaa 20 abcd


prev newP
Returns 1000 6 5
10ab 8acf abcd 4ddd
10ab 7 3 abcd X 15 .... 1
L 6aaa 6
Q: Change the function to not return anything (remove line 5), and replace line 3 with L=newP .Will it work? Will it11be
correct? What scenario will be best for testing that?
Delete a node after a given node – node operation Θ(1)
/* Delete the node after the node "prev".
Note that this is works on nodes. It does not matter how a list is
represented. prev is just a node.*/
void delete_node_after(nodePT prev) {
if (prev == NULL) {
printf("\n Cannot delete after a NULL node. No action taken.");

} else {
nodePT toDel = prev->next; // 3
if (toDel != NULL){ // 4
prev->next = toDel->next; // 5 this crashes if toDel is NULL
free(toDel); // 6
}
}
} SOLUTON drawing:
50ab 50ab 30cd
prev toDel prev toDel
4ddd
50ab 30cd 8acf 50ab 30cd 8acf
7 30cd 3 8acf 9 .... 1 7 30cd 3 8acf 9 ...
Remove 8acf Remove
this node this node 12
Delete in a list, L, after a given node (assumed from L) - Θ(1)
/* Deletes from list L, the node after prev. If prev is NULL it means that the first
node of L must be deleted. Uses the list representation: L points to the 1st node.*/
nodePT delete_node(nodePT L, nodePT prev){
if (prev == NULL) { // delete the first node from L
if (L==NULL) { return NULL; } // no node in the list. nothing to delete
else {// case 2: delete 1st node and return the address of the new 1st node
nodePT newFirst = L->next;
free(L);
return newFirst;
}
} else { // case 3
delete_node_after(prev); // does not affect the list head
return L;
NULL 7a7b
} prev newFirst
} 10ab 7a7b 30cd 8acf
cd00
Case 2: 10ab 8 7a7b 2 3 9 .... 1
prev==NULL, L!=NULL L Remove this node
Returns 7a7b
Case 3: 50ab To see the work see delete_node_after
prev!=NULL prev
Returns 10ab cd00
10ab 50ab 30cd 8acf
10ab 8 .... 7 30cd 3 9 .... 1
13
L 8acf Remove this node
Swap the next 2 nodes after node prev Θ(1)
HINT: When swapping, NAME the nodes to avoid overwriting a link. Below lines 8,9,10 can be executed in any
order. If not named, a specific order would be needed.
// Swaps 2 nodes after prev. If prev is NULL or not enough nodes, it does nothing.
void swap_2_after(nodePT prev){
if ( (prev == NULL) || (prev->next == NULL) || (prev->next->next == NULL) ) {
printf("\n prev is NULL or not enough nodes!\n");
return;
}
nodePT A = prev->next; // 1st node in the swap, code crashes if NULL
nodePT B = prev->next->next; // 2nd node in the swap, code crashes if NULL
nodePT C = B->next; //1st node after the nodes to be swapped (A, B). Ok if NULL
prev->next = B; //8
A->next = C; //9
B->next = A; //10
}
10ab 7a7b 30cd 8acf
prev A B C
10
10ab 7a7b 30cd cd00
8acf
8 7a7b 2 30cd X 3 8acf X 9 .... 1
30cd X 8acf 717b
Line 8 9 14
Array of linked lists – Drawings of listArr at different
stages in the program.
simple example
/* assume new_node(), array_2_list(), and listArr listArr
print_list_horiz() are the ones from the
list implementation provided. created in after loop
*/ line 1 in line 2
typedef struct node * nodePT;
0 XXXX 0 NULL
truct node {
int data; 1 XXXX 1 NULL
struct node * next;
2 XXXX 2 NULL
};
3 XXXX 3 NULL
int arr[] = {5,1,8};
nodePT listArr[5]; //1 4 XXXX 4 NULL
// size: 5*sizeof(memory address) = 5*8B=40B
listArr
// set every pointer/list to NULL after lines
for(j=0; j<5; j++) { // 2 4 and 5 07cc
listArr[j]=NULL;
0 07cc 5 NULL
}
abcd 200c
listArr[0] = new_node(5); //4 1 NULL dabc
listArr[2] = array_2_list(arr, 3); //5 5 dabc 1 200c 8 NULL

print_list_horiz(listArr[0]);
2 abcd
print_list_horiz(listArr[1]); 3 NULL
print_list_horiz(listArr[2]);
print_list_horiz(listArr[3]); 4 NULL
15
Steps for developing an algorithm (and code) with a loop –
(similar to proof by induction)
• Any code that has a loop can only be correct if there is a specific property that the loop
preserves. More specifically, there is a relation between the current state of program DATA and
the iteration of that loop.
• 0. When developing code that involves loops, first draw a picture of the given data and the final
resulting data.
Then start form the data (the actual data and the variables that you will use to store and access
that data) and the relation between the data and the loop iteration.
• 1: loop - decide roughly what the loop does (overall and in one iteration)
• 2a: identify property - What is the expected program state before iteration j. (CLEARLY state
what each variable holds: each variable must have a clear meaning and must hold specific data
(related to processing the first (j-1) items/data).
• 2b: j -> (j+1) - assume the property holds before iteration j and prove/check it holds before
iteration (j+1), i.e. running the code iteration j, preserved that property .
After the current iteration, j, the variables will hold the same information but related to
processing the first j items.
• 3: solved in the end - check that when the loop finished, the problem is solved
• 4: fix start - check and fix so that the data has the property immediately before the FIRST
iteration starts. Most times, this needs fixing.
• PROGRAM STATE = all variables and their content and any other memory or data accessed by the program at THAT SPECIFIC TIME
in the execution.
• Below is an example for using this method to compute the sum of the elements in an array of int and
16
• to create a single linked list with data from an array of int
Steps for developing an algorithm (and code) with a loop –
for computing sum over the elements from an array
• int sumArr(int arr[], int N) c34d 9 1 7 5 4 22
arr 0 1 2 3 N sumVal
• 0. Draw a picture of the given data and the final resulting data.
Then start form the data and the relation between the data and the loop iteration.
• 1: loop (& vars) - decide roughly what the loop does (overall and in one iteration)
• At each iteration, add one more number from the array, for(j=0; j<N; j++) {// add arr[j]
• 2a: identify property - What is the expected program state before iteration j.
Before iteration j, sumVal will have the sum of the elements at indexes 0 to (j-1) ,
sumVal =sumVal+arr[j]. E.g. for before j=2, sumVal=10
• 2b: j -> (j+1) - assume the property holds before iteration j and prove/check it holds before iteration
(j+1), i.e. running the code iteration j, preserved that property .
in iteration for j=2 we do: sumVal=sumVal+arr[j] = 10+arr[2] = 10+7=17 => yes j->(j+1)
• 3: solved in the end - check that when the loop finished, the problem is solved
Yes, it stops when j is N, i.e. here when j is 4. By case 2 above now sumVal has the sum of elements
from indices 0 to N-1 (here indexes: 0,1,2,3) .
• 4: fix start - check and fix so that the data has the property immediately before the FIRST iteration
starts. Most of the times, this needs fixing.
before iteration for j =0 starts, what is sumVal? It should be 0 => sumVal = 0
int sumArr(int arr[], int N) {
int j, int sumVal=0;
for(j=0; j<N j++) { sumVal=sumVal+arr[j]; }
return sumVal; 17
}
Step 1 - Creating a linked list with data from an array of int
a000
Given data: a000 9 1 7 5 4
array of int, arr and int N Drawing: arr 0 1 2 3 N

10ab abcd dabc 200c


Data to be created:
10ab 9 abcd 1 dabc 7 200c 5 NULL
Single linked list. Drawing: data next
L

nodePT array_2_list(int arr[], int N)

Solve the problem using the relation between data and loop iteration
Step 1. What will control the loop? What do we loop over?
Ans: Add a node for one item in arr.

We will iterate over the array arr, using the index, j (


for(j=0;j<N;j++) {
// create a new node,
// write arr[j] as data in it, (and possibly NULL in next)
// add it to the end of the list
}

18
Step 2a - Creating a linked list with data from an array of int
a000
Given data: a000 9 1 7 5 4
array of int, arr and int N Drawing: arr 0 1 2 3 N

10ab abcd 200c


Data to be created: dabc
10ab 9 abcd 1 dabc 7 200c 5 NULL
Single linked list. Drawing:
L data next

Solve the problem using the relation between data and loop iteration continued

Step 2a: What is the expected program state before iteration j (program state means what
value will the variables have).
a. Items at indexes 0 to (j-1) were processed, a node was created for each one of them and
they are in linked in a linked list in this order. The last node will have arr[j-1] as data. E.g.
before (j=2) have the nodes at addresses 10ab and abcd, and abcd has data 1.
b. What is next for the last node (abcd)? Should it be a valid memory address or NULL? I
choose NULL so that it is a correct last node in the list and it does not have what to point at
a000
Program state (immediately a000 9 1 7 5
4
after j became 2): arr N
0 1 2 3
abcd
10ab
2 10ab 9 abcd 1 NULL
j L data next
19
Step 2b - Creating a linked list with data from an array of int
a000
Given data: a000 9 1 7 5 4
array of int, arr and int N Drawing: arr 0 1 2 3 N
10ab abcd 200c
Data to be created: dabc
10ab 9 abcd 1 dabc 7 200c 5 NULL
Single linked list. Drawing:
L data next

Solve the problem using the relation between data and loop iteration continued.
Step 2b. j-> (j+1) Work done in one iteration (must preserve the state):
What should be done in the iteration when j=2?
a. Create a new node, store its address in a variable, say newP:
struct node * newP = (nodePT)malloc(sizeof(struct node)),
b. Write data in it: copy arr[j] in its data field, NULL in next
newP->data=arr[j]; newP->next=NULL
c. Link the new node at the end of the current list: set the next of the last node in the list
(abcd) to have the memory address of the new node. We just realized we need a name for that
last node, thus we need a (struct node *) variable. Say lastP of type struct node * .
lastP->next=newP.
d. Check that the data is good for the next iteration: Before iteration for index 3 (when j=3) is
my data as expected? No because lastP is still abcd, but now the last node is at address dabc
=> update lastP as lastP=newP;
10ab abcd dabc
2 dabc abcd
9 abcd 1 dabc 7 NULL
j newP lastP 10ab
data next 20
L
Step 3 - Creating a linked list with data from an array of int
a000
Given data: a000 9 1 7 5 4
array of int, arr and int N Drawing: arr 0 1 2 3 N
10ab abcd 200c
Data to be created: dabc
10ab 9 abcd 1 dabc 7 200c 5 NULL
Single linked list. Drawing:
L data next

Solve the problem using the relation between data and loop iteration continued.
Step 3. solved in the end- Check the state at the end of the loop. Will the problem be solved?
After the iteration for the last index (j=3), do we have the entire list? – yes, it seems to be so,
and the last node points to NULL (indicate the end of the list) thanks to our choice for newP-
>next = NULL

21
Step 4 - Creating a linked list with data from an array of int
Solve the problem using the relation between data and loop iteration continued.
Step 4. Check the FIRST iteration of the loop. What data will it use? Will this be a special case?
Yes. It is a special case because:
a. The address of the first node must be copied in box L (the others are not written in L)
b. When creating the first node (for the number 9), there is NO other node in the list, thus
there is no lastP, thus the code in the loop may break.
 Treat this special case separate, NOT in the loop. This is just my personal choice => modify
the loop to start at index 1 , not 0 ( for(j=1;j<N;j++) ) (the other way is to create
a special case inside the loop for j==0. There are pros and cons to both options).
 Create a node, store its memory address in L :
nodePT L=malloc(sizeof(struct node)).
Write data in it: L->data = arr[0]; L->data = NULL . How will this node be
used by the loop? It is currently the last node in the list, thus make lastP point to it by
copying its address in lastP (lastP = L). Check what is expected of lastP? It is expected
that it point to NULL (i.e. next is NULL.) . It does!
a000
a000
9 1 7 5
Note that even if the array
arr
Program state: 0 1 2 3
had size 1, the list will be
10ab correct (last node points to
10ab 10ab 9 NULL
NULL) (This is one possible
lastP L data next
special case.) 22
a000 nodePT array_2_list(int arr[], int N) Trace the
a000 9 1 7 5 code execution. Color over and fill in each box as it is created and/or
arr updated
0 1 2 3 N
10ab abcd dabc 200c

j newP lastP
L data next

// creates a single linked list from an array


nodePT array_2_list(int arr[], int N) {
int j;
nodePT lastP = NULL, newP=NULL;
nodePT L = (nodePT)malloc(sizeof(struct node));
L->data = arr[0];
L->next = NULL;
Assume:
lastP = L; typedef struct node * nodePT;
for (j = 1; j<N; j++) { struct node {
int data;
newP = (nodePT)malloc(sizeof(struct node)); struct node * next;
newP->data = arr[j]; };

newP->next = NULL;
Note: this code can be written even
lastP->next = newP;
simpler, but this version is very explicit and
lastP = newP; can be applied to other scenarios.
}
return L;
23
}
a000
a000 9 1 7 5 4
arr
0 1 2 3 N
10ab
1 NULL 10ab
10ab 9 NUL
j newP lastP
L data next

// creates a single linked list from an array PROGRAM STATE (all data - all
nodePT array_2_list(int arr[], int N) {
memory used and its content)
int j;
Above is the program state just
nodePT lastP = NULL, newP=NULL; before iteration for j=1 starts
nodePT L = (nodePT)malloc(sizeof(struct node)); (immediately after executing j=1)
L->data = arr[0];
L->next = NULL; This is in text form (to be used in
lastP = L;
online exam)
arr=(a000; 9,1,7,5); N=(...; 4)
for (j = 1; j<N; j++) {
L=(…;10ab),
newP = (nodePT)malloc(sizeof(struct node)); newP=(…;NULL), lastP=(…;10ab)
newP->data = arr[j]; j=(…;1)
newP->next = NULL; (10ab;9,NULL)
lastP->next = newP; Assume:
typedef struct node * nodePT;
lastP = newP;
struct node {
} int data;
struct node * next;
return L; };
} 24
a000
a000 9 1 7 5 4
arr
0 1 2 3 N
10ab abcd dabc
3 dabc dabc
10ab 9 abcd 1 dabc 7 NULL
j newP lastP
L data next

// creates a single linked list from an array PROGRAM STATE (all program data)
nodePT array_2_list(int arr[], int N) { just before iteration for j=3 starts
int j; (immediately after j is updated to 3).
All the data shown is consistent with
nodePT lastP = NULL, newP=NULL;
what the program does (even the
nodePT L = (nodePT)malloc(sizeof(struct node)); NULL in dabc.
L->data = arr[0];
L->next = NULL; This is in text form (to be used in
online exam)
lastP = L;
arr=(a000; 9,1,7,5); N=(...; 4)
for (j = 1; j<N; j++) { L=(…;10ab),
newP = (nodePT)malloc(sizeof(struct node)); newP=(…;dabc), lastP=(…;dabc)
newP->data = arr[j]; j=(…;3)
newP->next = NULL; (10ab;9,abcd)->(abcd; dabc)->
(dabc; NULL)
lastP->next = newP;
lastP = newP; Assume:
} typedef struct node * nodePT;
struct node {
return L; int data;
struct node * next;
} }; 25
Delete an entire list
10ab abcd dabc 200c
10ab 9 abcd 1 dabc 7 200c 5 NULL
Given data: L data next

10ab
Final data: L

• Function signature: void delete(nodePT L);


• STEPS.
• 1: loop - decide roughly what the loop does (overall and in one iteration)
• 2a: identify property - What is the expected program state before iteration j.
(CLEARLY state what each variable holds: each variable must have a clear meaning
and must hold specific data (related to processing the first (j-1) data.
• 2b: j -> (j+1) - assume the property holds before iteration j and prove/check it
holds before iteration (j+1).
After the current iteration, j, the variables will hold the same information but
related to processing the first j data.
• 3: solved in the end- check that when the loop finished, the problem is solved
• 4: fix start - check and fix so that the data has the property right before the FIRST
iteration starts. Most times, this needs fixing.

26
Delete an entire list
10ab abcd dabc 200c
10ab 9 abcd 1 dabc 7 200c 5 NULL
Given data: L data next

NULL
Final data: L
• Function signature: void delete(nodePT L);
• STEPS.
• 1: loop - decide roughly what the loop does (overall and in one iteration)
Iterates over every node, and in one iteration it deletes one node. Use name curr
for every node, curr
work needed to delete and free curr
• 2a: identify property - What is the expected program state before iteration j. (CLEARLY
state what each variable holds: each variable must have a clear meaning and must hold
specific data (related to processing the first (j-1) data.
The first (j-1) nodes are deleted. Nodes before curr are deleted and freed
• 2b: j -> (j+1) - assume the property holds before iteration j and prove/check it holds
before iteration (j+1)
If free(curr), we lose the link to the next node (we do not know the number dabc) => need
another pointer variable to hold it => use name next . Order matters!!!!
next = curr->next; // line 21
free(curr); // line 22
curr = next; // line 23
10ab abcd dabc 200c
abcd dabc 10ab XXXXXX 1 dabc 7 200c 5 NULL
27
curr next L
Delete an entire list
10ab abcd dabc 200c
10ab 9 abcd 1 dabc 7 200c 5 NULL
Given data: L data next

NULL
Final data: L
• Function signature: void delete(nodePT L);
• STEPS.
• 3 : solved in the end - check that when the loop finished, the problem is solved
In the last iteration curr points to the last node, 200c, and it deletes in that iteration. L should be fixed to
NULL after the loop: L=NULL;
• 4: fix start - check and fix so that the data has the property right before the FIRST iteration starts. Most
times, this needs fixing.
The only variables used are curr and next.
curr should point to the first node: curr=L
The CONTENT of after is set before it is used, so it is ok.

curr = L;
L=NULL; // if at the end of a function, this can be skipped
while(curr!=NULL){
next = curr->next; // line 21
free(curr); // line 22
curr = next; // line 23
}

10ab abcd dabc 200c


abcd dabc 10ab 9 abcd 1 dabc 7 200c 5 NULL
28
curr next L data next
Delete an entire list
10ab abcd dabc 200c
10ab 9 abcd 1 dabc 7 200c 5 NULL
Given data: L data next

NULL
Final data: L

// Time complexity: Θ(N),


// where N is the size of the list
void destroy_list(nodePT L) {
nodePT next, curr;
curr = L;
L = NULL; // can be skipped here
while (curr!=NULL) {
next = curr->next;
L=NULL so that no
free(curr);
variable points to memory
curr = next; that was freed: pointer
} 10ab should not be used
} anymore.
29
a000 nodePT array_2_list(int arr[], int N) Trace the
a000 9 1 7 5 code execution. Color over and fill in each box as it is created and/or
arr updated
0 1 2 3 N
10ab abcd dabc 200c

j newP lastP
L data next

// creates a single linked list from an array


nodePT array_2_list(int arr[], int N) {
int j;
nodePT lastP = NULL, newP=NULL;
nodePT L = (nodePT)malloc(sizeof(struct node));
L->data = arr[0];
L->next = NULL;
Assume:
lastP = L; typedef struct node * nodePT;
for (j = 1; j<N; j++) { struct node {
int data;
newP = (nodePT)malloc(sizeof(struct node)); struct node * next;
newP->data = arr[j]; };

newP->next = NULL;
Note: this code can be written even
lastP->next = newP;
simpler, but this version is very explicit and
lastP = newP; can be applied to other scenarios.
}
return L;
30
}
nodePT array_2_list(int arr[], int N)
Trace the code execution. Draw the data.

// creates a single linked list from an array


nodePT array_2_list(int arr[], int N) {
int j;
nodePT lastP = NULL, newP=NULL;
nodePT L = (nodePT)malloc(sizeof(struct node));
L->data = arr[0];
L->next = NULL;
Assume:
lastP = L; typedef struct node * nodePT;
for (j = 1; j<N; j++) { struct node {
int data;
newP = (nodePT)malloc(sizeof(struct node)); struct node * next;
newP->data = arr[j]; };

newP->next = NULL;
Note: this code can be written even
lastP->next = newP;
simpler, but this version is very explicit and
lastP = newP; can be applied to other scenarios.
}
return L;
31
}
Delete an entire list, L: void destroy_list(nodePT L)
10ab abcd dabc 200c
10ab 9 abcd 1 dabc 7 200c 5 NULL
Given data: L data next

NULL
Final data: L

32
Insert a node after a given node – node operation
/* Inserts newP after the node "prev".
Note that this is works on nodes. It does not matter how a list is
represented. prev is just a node. */
void insert_node_after(nodePT prev, nodePT newP) {
if (((prev == NULL)||(newP==NULL)) {
printf("\n Cannot insert after a NULL node. No action
taken.");
} else {
newP->next = prev->next; //5
prev->next = newP; //6
}
}
Does this code work well when
prev->next is NULL ?

prev newP 6aaa prev newP 6aaa


8acf 6aaa 20 ????
8acf 6aaa 20 ????

8acf dddd
8acf
3 dddd 15
3 NULL

33
Delete a node after a given node – node operation
/* Delete the node after the node "prev".
Note that this is works on nodes. It does not matter how a list is
represented. prev is just a node.*/
void delete_node_after(nodePT prev) {
if (prev == NULL) {
printf("\n Cannot delete after a NULL node. No action taken.");

} else {
nodePT toDel = prev->next; // 3
if (toDel != NULL){ // 4
prev->next = toDel->next; // 5 this crashes if toDel is NULL
free(toDel); // 6
}
}
}
50ab
prev
50ab 30cd 8acf 4ddd
7 30cd 3 8acf 9 .... 1

Remove
this node 34
Swap the next 2 nodes after node prev
HINT: When swapping, NAME the nodes to avoid overwriting a link. Below lines 8,9,10 can be executed in
any order. If not named, a specific order would be needed.
// Swaps 2 nodes after prev. If prev is NULL or not enough nodes, it does nothing.
void swap_2_after(nodePT prev){

10ab
prev

10ab 7a7b 30cd cd00


8acf
8 7a7b 2 30cd 3 8acf 9 .... 1

35

You might also like