This document discusses linked lists in C programming. It begins by introducing linked lists as a way to overcome the fixed size of arrays. Each node of a linked list contains a data field and a link to the next node. Operations for adding nodes to the head, tail, or middle of a singly linked list are described. Deleting nodes from the head, tail, or middle are also covered. Finally, doubly linked lists are introduced, which allow traversal in both directions by including a pointer to the previous node in each node.
linked list
singly linked list
insertion in singly linked list
DELETION IN SINGLY LINKED LIST
Searching a singly linked list
Doubly Linked List
insertion from Doubly linked list
DELETION from Doubly LINKED LIST
Searching a doubly linked list
Circular linked list
linked list
singly linked list
insertion in singly linked list
DELETION IN SINGLY LINKED LIST
Searching a singly linked list
Doubly Linked List
insertion from Doubly linked list
DELETION from Doubly LINKED LIST
Searching a doubly linked list
Circular linked list
Linked List Static and Dynamic Memory AllocationProf Ansari
Static variables are declared and named while writing the program. (Space for them exists as long as the program, in which they are declared, is running.) Static variables cannot be created or destroyed during execution of the program in which they are declared.
Dynamic variables are created (and may be destroyed) during program execution since dynamic variables do not exist while the program is compiled, but only when it is run, they cannot be assigned names while it is being written. The only way to access dynamic variables is by using pointers. Once it is created, however, a dynamic variable does contain data and must have a type like any other variable. If a dynamic variable is created in a function, then it can continue to exist even after the function terminates.
Linked Linear List
We saw in previous chapters how static representation of linear ordered list through Array leads to wastage of memory and in some cases overflows. Now we don't want to assign memory to any linear list in advance instead we want to allocate memory to elements as they are inserted in list. This requires Dynamic Allocation of memory and it can be achieved by using malloc() or calloc() function.
But memory assigned to elements will not be contiguous, which is a requirement for linear ordered list, and was provided by array representation. How we could achieve this?
Linked List Static and Dynamic Memory AllocationProf Ansari
Static variables are declared and named while writing the program. (Space for them exists as long as the program, in which they are declared, is running.) Static variables cannot be created or destroyed during execution of the program in which they are declared.
Dynamic variables are created (and may be destroyed) during program execution since dynamic variables do not exist while the program is compiled, but only when it is run, they cannot be assigned names while it is being written. The only way to access dynamic variables is by using pointers. Once it is created, however, a dynamic variable does contain data and must have a type like any other variable. If a dynamic variable is created in a function, then it can continue to exist even after the function terminates.
Linked Linear List
We saw in previous chapters how static representation of linear ordered list through Array leads to wastage of memory and in some cases overflows. Now we don't want to assign memory to any linear list in advance instead we want to allocate memory to elements as they are inserted in list. This requires Dynamic Allocation of memory and it can be achieved by using malloc() or calloc() function.
But memory assigned to elements will not be contiguous, which is a requirement for linear ordered list, and was provided by array representation. How we could achieve this?
We would like to build a generic list in such a way that we can start.pdfyadavup216
We would like to build a generic list in such a way that we can start either at the front, or at the
back, and move through the list in either direction. Here is an example of such a list (shown here
with Strings), drawn as an object diagram: Every Deque has a header that consists of the Sentine
L node. Ihis header neld does not change, but the fields within the Sentinel node (that provide the
links to the first and the last item in the Deque) can and will change. Each data Node has two
links, one to the next item in the list and one to the previous item in the list. The Sentinel node is
always present. It also has next and prev fields, which are consistently updated to link to the head
of the list and to the tail of the list, respectively. The Sentinel and Node classes both inherit from
a common superclass (shown below), because the next or prev item after any given node may be
either a data-carrying Node or the Sentinel marking the end of the list. The list shown above has
four data-carrying nodes and the lone sentinel. The empty list has just the Sentinel, and its links
to the first and to the last item just reference the Sentinel node itself. So an empty list would have
the following object diagram:
The list shown above has four data-carrying nodes and the lone sentinel. The empty list has just
the Sentinel, and its links to the first and to the last item just reference the Sentinel node itself. So
an empty list would have the following object diagram: The class diagram for these classes is as
follows:
We have an abstract class ANode T, which can be either a Sentinel T> node or an actual Node T
containing data. We use an abstract class here instead of an interface because we need to share
the next and prev fields. Moreover, because all ANodes are contained and hidden within the
Deque, no external code needs to know about it: they are implementation details rather than a
publicly visibly datatype definition. 8.1 Data definitions and examples - Define the classes
ANode T, Node T, Sentinel T, and Deque T. - For Sentinel, define a constructor that takes zero
arguments, and initializes the next and prev fields of the Sentinel to the Sentinel itself. - For
Node T, define two constructors: the first one takes just a value of type T, initializes the data
field. The next and prev fields would be nutl. The second convenience constructor should take a
value of type T and two ANode nodes, initialize the data field to the given value, initialize the
next and prev fields to the given nodes, and also update the given nodes to refer back to this
node. Throw an IllegalArgumentException in this constructor if either of the given nodes is null.
(You can use if (theNode == null) {} to test for null-ness.) Note carefully the order of the
arguments in this constructor! The order should match the class diagram above; getting the order
wrong will result in oddly "backwards" lists. - For Deque T, define two constructors: one which
takes zero arguments and initial.
This is a presentation on Linked Lists, one of the most important topics on Data Structures and algorithms. Anyone who is new to DSA or wants to have a theoretical understanding of the same can refer to it :D
This tutorial explains about linked List concept. it contains types of linked list also. All possible graphical representations are included for better understanding.
DevOps and Testing slides at DASA ConnectKari Kakkonen
My and Rik Marselis slides at 30.5.2024 DASA Connect conference. We discuss about what is testing, then what is agile testing and finally what is Testing in DevOps. Finally we had lovely workshop with the participants trying to find out different ways to think about quality and testing in different parts of the DevOps infinity loop.
Accelerate your Kubernetes clusters with Varnish CachingThijs Feryn
A presentation about the usage and availability of Varnish on Kubernetes. This talk explores the capabilities of Varnish caching and shows how to use the Varnish Helm chart to deploy it to Kubernetes.
This presentation was delivered at K8SUG Singapore. See https://feryn.eu/presentations/accelerate-your-kubernetes-clusters-with-varnish-caching-k8sug-singapore-28-2024 for more details.
GraphRAG is All You need? LLM & Knowledge GraphGuy Korland
Guy Korland, CEO and Co-founder of FalkorDB, will review two articles on the integration of language models with knowledge graphs.
1. Unifying Large Language Models and Knowledge Graphs: A Roadmap.
https://arxiv.org/abs/2306.08302
2. Microsoft Research's GraphRAG paper and a review paper on various uses of knowledge graphs:
https://www.microsoft.com/en-us/research/blog/graphrag-unlocking-llm-discovery-on-narrative-private-data/
"Impact of front-end architecture on development cost", Viktor TurskyiFwdays
I have heard many times that architecture is not important for the front-end. Also, many times I have seen how developers implement features on the front-end just following the standard rules for a framework and think that this is enough to successfully launch the project, and then the project fails. How to prevent this and what approach to choose? I have launched dozens of complex projects and during the talk we will analyze which approaches have worked for me and which have not.
The Art of the Pitch: WordPress Relationships and SalesLaura Byrne
Clients don’t know what they don’t know. What web solutions are right for them? How does WordPress come into the picture? How do you make sure you understand scope and timeline? What do you do if sometime changes?
All these questions and more will be explored as we talk about matching clients’ needs with what your agency offers without pulling teeth or pulling your hair out. Practical tips, and strategies for successful relationship building that leads to closing the deal.
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...UiPathCommunity
💥 Speed, accuracy, and scaling – discover the superpowers of GenAI in action with UiPath Document Understanding and Communications Mining™:
See how to accelerate model training and optimize model performance with active learning
Learn about the latest enhancements to out-of-the-box document processing – with little to no training required
Get an exclusive demo of the new family of UiPath LLMs – GenAI models specialized for processing different types of documents and messages
This is a hands-on session specifically designed for automation developers and AI enthusiasts seeking to enhance their knowledge in leveraging the latest intelligent document processing capabilities offered by UiPath.
Speakers:
👨🏫 Andras Palfi, Senior Product Manager, UiPath
👩🏫 Lenka Dulovicova, Product Program Manager, UiPath
Transcript: Selling digital books in 2024: Insights from industry leaders - T...BookNet Canada
The publishing industry has been selling digital audiobooks and ebooks for over a decade and has found its groove. What’s changed? What has stayed the same? Where do we go from here? Join a group of leading sales peers from across the industry for a conversation about the lessons learned since the popularization of digital books, best practices, digital book supply chain management, and more.
Link to video recording: https://bnctechforum.ca/sessions/selling-digital-books-in-2024-insights-from-industry-leaders/
Presented by BookNet Canada on May 28, 2024, with support from the Department of Canadian Heritage.
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Ramesh Iyer
In today's fast-changing business world, Companies that adapt and embrace new ideas often need help to keep up with the competition. However, fostering a culture of innovation takes much work. It takes vision, leadership and willingness to take risks in the right proportion. Sachin Dev Duggal, co-founder of Builder.ai, has perfected the art of this balance, creating a company culture where creativity and growth are nurtured at each stage.
Search and Society: Reimagining Information Access for Radical FuturesBhaskar Mitra
The field of Information retrieval (IR) is currently undergoing a transformative shift, at least partly due to the emerging applications of generative AI to information access. In this talk, we will deliberate on the sociotechnical implications of generative AI for information access. We will argue that there is both a critical necessity and an exciting opportunity for the IR community to re-center our research agendas on societal needs while dismantling the artificial separation between the work on fairness, accountability, transparency, and ethics in IR and the rest of IR research. Instead of adopting a reactionary strategy of trying to mitigate potential social harms from emerging technologies, the community should aim to proactively set the research agenda for the kinds of systems we should build inspired by diverse explicitly stated sociotechnical imaginaries. The sociotechnical imaginaries that underpin the design and development of information access technologies needs to be explicitly articulated, and we need to develop theories of change in context of these diverse perspectives. Our guiding future imaginaries must be informed by other academic fields, such as democratic theory and critical theory, and should be co-developed with social science scholars, legal scholars, civil rights and social justice activists, and artists, among others.
Let's dive deeper into the world of ODC! Ricardo Alves (OutSystems) will join us to tell all about the new Data Fabric. After that, Sezen de Bruijn (OutSystems) will get into the details on how to best design a sturdy architecture within ODC.
Neuro-symbolic is not enough, we need neuro-*semantic*Frank van Harmelen
Neuro-symbolic (NeSy) AI is on the rise. However, simply machine learning on just any symbolic structure is not sufficient to really harvest the gains of NeSy. These will only be gained when the symbolic structures have an actual semantics. I give an operational definition of semantics as “predictable inference”.
All of this illustrated with link prediction over knowledge graphs, but the argument is general.
UiPath Test Automation using UiPath Test Suite series, part 4DianaGray10
Welcome to UiPath Test Automation using UiPath Test Suite series part 4. In this session, we will cover Test Manager overview along with SAP heatmap.
The UiPath Test Manager overview with SAP heatmap webinar offers a concise yet comprehensive exploration of the role of a Test Manager within SAP environments, coupled with the utilization of heatmaps for effective testing strategies.
Participants will gain insights into the responsibilities, challenges, and best practices associated with test management in SAP projects. Additionally, the webinar delves into the significance of heatmaps as a visual aid for identifying testing priorities, areas of risk, and resource allocation within SAP landscapes. Through this session, attendees can expect to enhance their understanding of test management principles while learning practical approaches to optimize testing processes in SAP environments using heatmap visualization techniques
What will you get from this session?
1. Insights into SAP testing best practices
2. Heatmap utilization for testing
3. Optimization of testing processes
4. Demo
Topics covered:
Execution from the test manager
Orchestrator execution result
Defect reporting
SAP heatmap example with demo
Speaker:
Deepak Rai, Automation Practice Lead, Boundaryless Group and UiPath MVP
1. 6
6
6
6 L
L
L
LINKED
INKED
INKED
INKED L
L
L
LISTS
ISTS
ISTS
ISTS
A List is an ordered set of items. In computer programs, we often need to maintain lists, such as list of
marks, list of students etc. The most common way to maintain a list is using an array of the list data type.
Eg- int arr[] = {0,1,2,3,4};
char word[] = {‘H’, ’e’, ‘l’, ‘l’, ‘o’, 0};
However, in most programming languages and in C, arrays are of fixed size. Array size must be known at
the compile time, and cannot be changed at run time. However, arrays are allocated continuous memory
blocks, and as each array element is of fixed size, the address of any array element can be calculated
directly. This allows accessing the array elements randomly and efficiently. Time taken to access any array
element is constant time - time taken to calculate the address and memory access time. For example, in
figure 6-1, the address of arr[3] can be directly evaluated as arr + 3* sizeof(array_element).
Figure 6-1 Array organization in memory
6.1
6.1
6.1
6.1 I
I
I
INTRODUCTION TO
NTRODUCTION TO
NTRODUCTION TO
NTRODUCTION TO L
L
L
LINKED
INKED
INKED
INKED L
L
L
LISTS
ISTS
ISTS
ISTS (R
(R
(R
(REFERENCE
EFERENCE
EFERENCE
EFERENCE-
-
-
-B
B
B
BASED
ASED
ASED
ASED L
L
L
LISTS
ISTS
ISTS
ISTS)
)
)
)
To overcome the difficulty of fixed size in static arrays, C and other languages allow one to create Linked
lists that consists of number of nodes linked by their memory addresses. Nodes can be created at run
time and add to the list.
Figure 6-2Illustration of a Linked List
2. Figure 6-2 illustrates a typical linked list with 3 nodes. Each node consists of a data or information part
and a link that carries the address of the next node in the list. In a linked list, nodes need not be created
in continuous memory blocks - this cannot be guaranteed as nodes are created dynamically, and therefore
each node need to be linked to the next node explicitly. A special head pointer is required to keep track
of the linked list.
We can use a C structure to define the composition of a node as follows.
struct node { int info; // assumes int data
struct node* link; };
We can now create a single node linked list as follows.
struct node* head = (struct node*)malloc(sizeof(struct node));
head->info = 1;
head->link = 0;
In the first line, we call sizeof() operator in C for the struct node data type, which returns the number
of bytes, an instance of struct node consumes in memory. This returned value (a long int) is passed in turn
to the malloc(), dynamic memory allocation function in C, usually defined in alloc.h or stdlib.h library.
malloc()allocates the specified number of bytes in memory at runtime and return a pointer to the
allocated memory block. However, to use this memory area as an instance of a struct node, we need to
type cast the return pointer to struct node*. Subsequently, one can access the parts of the struct
node instance as illustrated in the last two lines of the above code segment. The resultant single node list
is illustrated in figure 6-3.
Figure 6-3 Single node Linked List
One can go on adding any number of nodes to the above linked list dynamically at run time. However,
when accessing the nodes, one can only access the head node directly. To access a middle node the list
should be traversed starting from the head node.
Eg - for(temp=head; temp->link; temp=temp->link)
if(temp->info==key) break;
3. Figure 6-4, illustrates variations of singly linked lists - singly linked in the sense there is only one pointer
or link maintained within any node. One can choose a variation of a list in figure 6-4, based on the
application to minimize traversing through the list.
Figure 6-4 Variations of Singly Linked Lists
6.2
6.2
6.2
6.2 A
A
A
ADDING
DDING
DDING
DDING N
N
N
NODES TO
ODES TO
ODES TO
ODES TO S
S
S
SINGLY
INGLY
INGLY
INGLY L
L
L
LINKED
INKED
INKED
INKED L
L
L
LISTS
ISTS
ISTS
ISTS
Nodes can be created at run time, copy the data to be stored in the node, and linked to an existing linked
list. The new node can be added at the head end or tail end of the list or insert after a given node in the
list.
6.2.1
6.2.1
6.2.1
6.2.1 Adding a Node at the Head
Adding a Node at the Head
Adding a Node at the Head
Adding a Node at the Head
Figure 6-5Adding a node at the head end of a Linked List
4. Figure 6-5 illustrates adding a node at the head end of a Linked List. This involves, getting the next
part/variable of the new node point to the current head node and getting the head variable to point to
the newly created node.
Eg- new_node->next = head; // Link the new node at head end
head = new_node; // update head
Another point to consider here is to ensure whether the above two lines of code can cater for any
situation, for example would it work for an empty list. For an empty list, head variable will carry a NULL,
and this NULL value (0) will be assigned to the next field of the new node in the first line of the code above,
thereby properly grounding the new node. This is required as the new node is going to be the tail node as
well, in this scenario. Second line of code, again get the head variable to correctly point to the new node.
6.2.2
6.2.2
6.2.2
6.2.2 Adding a Node
Adding a Node
Adding a Node
Adding a Node at the Tail
at the Tail
at the Tail
at the Tail
Figure 6-6 illustrates, adding a node at the tail end of a Linked List. This involves, getting the current tail
node's next variable to point to the newly created node and properly grounding the new node.
Eg- new_node->next = 0; // Ground the new node
tail->next = new_node; // Link the new node at the tail
tail = new_node; // Update tail if available
If a tail variable is not maintained, then one must get a temporary tail variable pointing to the last node
of the given Linked List.
Eg- for(tail=head; tail; tail=tail->next) if(!tail->next) break;
Once we exit from the above loop, either tail is 0, indicating an empty list or otherwise pointing to the last
node of a given nonempty list.
When we do not maintain a tail pointer, no need to update this variable, as done in the last line of the
previous code, since it is a temporary variable having no further use.
Figure 6-6Adding a node at the tail end of a Linked List
5. As earlier, we need to check whether the previous 3 lines of code can cater for any scenario. To cater for
an empty list as well, we can modify the code as follows.
1 new_node->next = 0; // We still need this
2 if(tail) tail->next = new_node; // if list not empty
3 else head = new_node; // working on an empty list
4 tail = new_node; // Update tail if available as before
Line 1 we need to execute for any scenario as the new node is going to be the last node anyway. For a
nonempty list, tail (given tail variable or a temporary variable set to point to the tail) will be pointing to
the last node, and the new node need to be linked to the last node, as done in Line 2. For an empty List,
however, what we need to adjust is the head variable as this would have been having the value 0, pointing
nowhere. As before, if we maintain a tail variable with the list, then we need to update this variable to
point to the new tail node, as done in Line 4.
6.2.3
6.2.3
6.2.3
6.2.3 Inserting N
Inserting N
Inserting N
Inserting Node
ode
ode
odes
s
s
s
Figure 6-7Inserting a node to a Linked List
AS illustrated in figure 6-7, if we have a pointer temp pointing to a node of a linked list, we can insert a
new node after the node pointed to by temp as follows.
5 newNode->next = temp->next; // Link the nodes after temp to newNode
6 temp->next = newNode; // Link newNode to temp
Again, as before, if we consider possible special scenarios we need to consider cases, (a) temp being
pointing to the tail node, (b) temp pointing to the head node, (c) temp being NULL - inserting to an empty
List.
Case (a): If temp is pointing to the last node, since temp->next is NULL, line 5 will correctly ground the new
node, which is going to be the last node. Line 6 will again correctly link the new node to the previous last
node. The two lines of code will cater for this situation without any change. However, if the list maintains
a tail pointer, then this need to be updated to point to the new node, which has become the tail node of
the list.
6. Case (b): If temp is pointing to the head node, one can easily visualize using the diagram in figure 6-7
that the same two lines of code can function without any changes.
Case(c): if one wants to cater to the scenario of temp being NULL, we can modify the above two lines as
follows.
7 if(temp){ newNode->next = temp->next;
8 temp->next = newNode;
9 } else { temp = newNode;
10 temp->next = 0;
11 }
6.3
6.3
6.3
6.3 D
D
D
DELETING
ELETING
ELETING
ELETING N
N
N
NODES FROM
ODES FROM
ODES FROM
ODES FROM S
S
S
SINGLY
INGLY
INGLY
INGLY L
L
L
LINKED
INKED
INKED
INKED L
L
L
LISTS
ISTS
ISTS
ISTS
Like adding nodes to a linked List, nodes can be deleted from a Linked List in many ways.
6.3.1
6.3.1
6.3.1
6.3.1 Deleting
Deleting
Deleting
Deleting the
the
the
the Head
Head
Head
Head Node
Node
Node
Node
Figure 6-8Deleting a node from the head of a Linked List
As illustrated in figure 6-8, deleting the head node involves, setting the head pointer to the next node
and freeing the head node. The information stored in the head node can be returned if required.
Eg- struct node* temp = head; // get a temp point to head node
head = head->next; // update head to point to new head
free(temp); // free the previous head node
We also need to consider here the special case of deleting the head node from single node Linked list,
resulting an empty list. In this situation head->next will be NULL, and the second line sets head to NULL,
indicating an empty List. The rest functions as normal and no medications are required to the above code
to cater to this scenario.
6.3.2
6.3.2
6.3.2
6.3.2 Deleting the
Deleting the
Deleting the
Deleting the Tail
Tail
Tail
Tail Node
Node
Node
Node
Deleting the tail node is illustrated in figure 6-8. This involves locating the node before the tail node,
grounding it, and releasing the tail node. If the list maintains a tail pointer then that need to be updated
7. to point to the new tail node. As before, if required data stored in the tail node can be returned to the
caller.
Figure 6-9 Deleting the tail node from a Linked List
Eg- for(temp=head; temp->next->next; temp=temp->next);
free(temp->next); // free the tail node
temp->next=0; // ground the node before the tail node
tail=temp; //update tail if maintained
The above for loop locates the node before the tail node. When temp points to the node one before the
tail node, temp->next->next becomes 0 and exit from the loop.
For the above code to work temp->next->next should be valid at least at the loop start. This is valid only
when the linked list has at least two nodes. For this case where we have at least two nodes in the given
Linked List, the above code works correctly. To delete the tail node from a single node Linked List, we can
alter the code as follows.
12 if(!head) return; // cannot delete from an empty list
13 if(head->next){ // have at least 2 nodes
14 for(temp=head; temp->next->next; temp=temp->next);
15 free(temp->next); // free the tail node
16 temp->next=0; // ground the node before tail node
17 tail=temp; //update tail if maintained
18 }else{ free(head); // single node linked list
19 head=tail=0; // setup an empty list
20 }
6.3.3
6.3.3
6.3.3
6.3.3 Deleting a Middle Node
Deleting a Middle Node
Deleting a Middle Node
Deleting a Middle Node
Deleting a middle node from a singly linked list involves, linking the previous node directly to the following
node bypassing the node intended to be deleted and freeing the memory of the node intended to be
deleted. This is illustrated in the figure 6-10.
8. Figure 6-10 Deleting a middle node from a Linked List
To link the previous node directly to the following node, we need to write the next field value of the node
to be deleted (following node's address) to the previous nodes next field. If we have a pointer ptr to the
previous node this is a simple task as illustrated in Case 1 below. However, if we have a pointer to the
node to be deleted then to access the previous node's next field, we need to start from the head and
traverse the list to get a pointer to the previous node, which can be expensive. When we have a pointer
to a node it's easy to delete the following node rather than the node pointed to by the given pointer.
Case 1: Having a pointer ptr, pointing to the previous node of the node to be deleted.
Eg- temp = ptr->next; //get temp point to the node to be deleted
ptr->next = temp->next; // remove the node from the List
free(temp); // Free the node's memory
Case 2: Having a pointer ptr, pointing to the node to be deleted.
Figure 6-11 illustrates an efficient strategy to cater for this situation.
Figure 6-11 Deleting a middle node from a Linked List
9. In figure 6-11, to delete the node pointed to by mid, we copy the data stored in the following node to the
current node, and delete the following node. Copying the following node's data to the current node, we
effectively covert the problem in Case 2 to a problem in Case 1. Deleting the following node is handled as
done by Case 1. The complete code for Case 2 look like follows.
21 mid->data = mid->next->data; // convert to case 1
22 temp = mid->next; // Here onwards identical to case 1 steps
23 mid->next = temp->next; // except ptr is mid here
24 free(temp);
Note that, any of the above solutions cannot be used to delete the tail node. Case 2 can be applied to
delete the head node with an additional statement to update the head variable, whereas Case 1 cannot
apply to delete the head node.
6.4
6.4
6.4
6.4 D
D
D
DOUBLY
OUBLY
OUBLY
OUBLY L
L
L
LINKED
INKED
INKED
INKED L
L
L
LISTS
ISTS
ISTS
ISTS
Figure 6-12 illustrates a Doubly Linked List in contrast to a Singly Linked List. In Doubly Linked List, each
node maintains two pointers, one pointing to the following node as in a Singly Linked List, and a second
pointer pointing to the previous node. This allows a Doubly Linked List to be traversed in both directions.
However, the cost one must incur is the additional pointer that need to be maintained for each node, in
all List operations, such as inserting and deleting nodes.
Figure 6-12 Doubly Linked List vs. Singly Linked List
10. 6.5
6.5
6.5
6.5 L
L
L
LINKED
INKED
INKED
INKED L
L
L
LIST
IST
IST
IST ADT
ADT
ADT
ADT
An example of a singly linked list ADT prototype is given below. Apart from the basic operation discussed
in the previous sections some helper functions are also included to better utilize the ADT. One point of
interest is the use of struct node** type arguments in many functions in the prototype.
Typical Linked List Implementation prototype in C:
struct node{ float data; // data structure for a node
struct node* next;};
struct node* makeNode(float item); //creates a node with data value
// item &returns the node address
struct node* addHead(struct node** list, float item);
// add a new node to the head
struct node* addTail(struct node** list, float item);
// add a new node to the tail
struct node* insert(struct node* ptr, float item); //insert a node
// after the node pointed to by ptr
struct node* delHead(struct node** list, float item); //delete head
struct node* delTail(struct node** list, float item); //delete tail
struct node* del(struct node* ptr, float item); //delete a middle node
void display(struct node* list); // display the list contents
int size(struct node* list); // returns the number of nodes
In the above prototype, a Linked List is identified by a pointer variable pointing to the head node. The type
of this pointer variable should be struct node*. If a function changes the head node of the list, it needs to
alter the value of this pointer variable that identifies the list by holding the address of the head node. As
this pointer variable always lies outside the scope of the function, to write into this pointer variable the
function needs the address (pointer) of this variable. This implies the argument that passes to a function
(that needs altering the head node) should be the address of the pointer variable that points to the head
node. Thus, the type of the function argument receiving the list identity should be struct node**. To
further elaborate this point, let us understand the operation of the addHead() function given in the
prototype implementation above.
addHead() adds a node to the head end of a given Linked List. Given a data value, to create a node, store
the given data value within the node and to return a pointer to the newly created node can be delegated
to a separate function makeNode(). Thus, makeNode() can be reused by other functions intending to add
nodes to the Linked List. addHead() can now create a new node via makeNode() and link the given list to
the new node and assign the head pointer to point to the newly created node as shown in figure 6-5. The
complete implementation code for the addHead() is given below.
11. 30 struct node* addHead(struct node** list, float item){
31 struct node* new_node=makeNode(item); //create node with item
32 if(!new_node) return 0; // return null if makeNode()fails
33 new_node->next = *list; // otherwise link *list to new_node
34 return *list=new_node; // update head to point new_node and
35 } // return *list for any further use
addHead() accepts two arguments, list and item. list is a pointer pointing to a variable that points to the
head node which is maintained, outside the scope of addHead() as shown in figure 6-13. Line 31, calls
makeNode() to create a new node in the heap memory, update data part of the new node with the item
value. Line 32 is an error handling statement to capture possible failure in creating a new node and exit if
necessary. In line 33, *list, which is the value of head is copied to next part of the newly created node, so
that the given linked list trails from the new node. Finally, line 34 updates the head variable with the
address of the new node, and returns the same value to the function name.
Figure 6-13 Usage of addHead() local variables