Oracle

Oracle

oracle, 非常oracle.

Data Structure Notes 02 - Singly Linked List

Linked List Data Structure

Importance of this Section#

Because the singly linked list is a very basic data structure, once you learn how to describe a singly linked list using the C language, describing other data structures such as heaps, stacks, doubly linked lists, and hash tables will be a piece of cake. If you are a beginner, it is highly recommended that you type the code yourself.

Nodes of a Singly Linked List#

Representation of a Node in a Singly Linked List

As shown in the figure, a node in a singly linked list consists of a data field and a pointer field. They can be considered as a whole and are called nodes. Later, we will use a structure to represent a node.
The data field, as the name suggests, is where the data is stored.
The pointer field is used to store the address of the next node.

Representation of a Singly Linked List#

A singly linked list is a linked representation and implementation of a linear list. The nodes are linked together to form a singly linked list.

Representation of a Singly Linked List

Definition of the Structure Representing a Node#

How do we describe a node using the C language? Here we use struct.

struct node {
    /* Next node */
    struct node *next;
    /* Value */
    int data;
};

First, we define a structure, and the tag of the structure is node.
Second, it has two attributes: one is the data of type int, which is the data field mentioned earlier. The other is the pointer field.
How do we understand struct node *next?
Remember, the pointer field is used to store pointer variables, and this variable is called next. Also, because this pointer points to the address of the next node, in other words, this pointer points to a structure we defined to represent a node. Therefore, the type of this pointer variable is struct node. The asterisk * indicates that it is a pointer variable. So when combined, it becomes struct node *next.
Finally, for convenience, we can use the typedef keyword to give an alias to struct node.

typedef struct node {
    /* Next node */
    struct node *next;
    /* Value */
    int data;
} list;

This way, in the subsequent code writing, list is equivalent to struct node.
For example, when we use this structure to create a new node, list *new_node is equivalent to struct node *new_node.

Creating a Singly Linked List#

list * create_list()
{
    /* Create a new node, and since we used the typedef keyword, node *head is equivalent to struct node *head */
    list *head = (list *)malloc(sizeof(list));
    if(head==NULL) return NULL;
    /* Initialize the node */
    head->data = 0; // The data field of the head node is used to represent the length of the linked list
    head->next = NULL; 
    return head;
}

Head Pointer

This function creates a singly linked list and returns the head pointer.
The head pointer is a pointer that points to the address of the head node, and it is of the same type as the pointer that points to the next node in the node.
First, we allocate a block of memory of size list using the malloc function and return a pointer to the first address of this memory block, which is then assigned to the head pointer variable.
Then, we check if this pointer is NULL. If it is NULL, it means that the memory allocation failed (which is generally unlikely).
Next, we initialize the node.
Finally, the function returns the head pointer and ends.

Why set a head node?
The data field of the head node is generally meaningless. It is set here for the convenience of later insertion and deletion operations, and the head node is not necessary for a linked list.
The first element node after the head node is called the first element node.

Inserting into a Singly Linked List#

Consider the following situation: a new node n needs to be inserted after node x.

Insertion into a Singly Linked List

The dashed line in the figure represents a disconnection, the same below.
According to the general idea, it might be:

x->next = n;
n->next = x->next;

Obviously, this is incorrect because after executing x->next = n, n->next = x->next is equivalent to n->next = n, so the correct approach should be:

n->next = x->next;
x->next = n;

Complete version of the insertion function:
The insertion function takes three parameters: the pointer head to the linked list of the node to be inserted, the data of the new node, and the position to be inserted pos.

list * list_insert_node(list *head, int data, int pos)
{   
    int i;
    list *curr = head;

    /* If the position to be inserted is greater than the length of the linked list, it is an illegal operation */
    if(pos > curr->data) return NULL;

    /* Create a node and initialize it */
    list *node = (list *)malloc(sizeof(list));
    if(node==NULL) return NULL;
    node->data = data;
    node->next = NULL;

    /* Traverse the linked list and find the position to be inserted */
    for(i=0;i<pos;i++){
        curr = curr->next;
    }

    /* Insertion */
    node->next = curr->next;
    curr->next = node;

    /* Increase the length of the linked list by 1 */
    head->data++;
    return head;
}

You can call it for testing in the main function:

list *l = create_list();
printf("Current length of the linked list: %d\n", l->data);
list_insert_node(l, 1, 0);
printf("Current length of the linked list: %d\n", l->data);

Compile and output using gcc:

gcc -fsanitize=address -fno-omit-frame-pointer  -g linked_list.c  && ./a.out

The output is normal and there are no memory error messages:

Current length of the linked list: 0
Current length of the linked list: 1

Traversing a Singly Linked List#

It is relatively simple, and no further explanation is needed.

/* Print the data in the linked list, excluding the data of the head node */
void print_list(list *head)
{
    list *curr = head->next;
    while (curr)
    {
        printf("%d \t", curr->data);
        curr = curr->next;
    }
    printf("\n");
}

Deleting from a Singly Linked List#

Deletion from a Singly Linked List
Assuming that the node after the head needs to be deleted, simply let head->next = head->next->next, but don't forget to free the memory of the deleted node.
Based on this idea:

list *list_delete_data(list *head, int pos)
{
    int i;
    list *curr = head;

    /* If the position to be deleted is greater than the length of the linked list, it is an illegal operation */
    if(pos > curr->data) return NULL;

    /* Traverse the linked list and find the pointer to the node before the node to be deleted */
    for(i=0;i<pos;i++){
        curr = curr->next;
    }
    // Temporarily record the node to be deleted
    list *temp = curr->next;
    // Delete the node
    curr->next = curr->next->next;
    
    // Free the memory of the deleted node
    free(temp);
    head->data--;
    return head;
}

Testing the Singly Linked List#

In this way, a basic linked list is written, and you can test it in the main function.

int main()
{
    int i;
    list *l = create_list();
    // Insert data multiple times
    for(i=0;i<5;i++){
        list_insert_node(l, i, 0);
        print_list(l);
    }
    list_delete_data(l, 0);
    print_list(l);
    return 0;
}

Compile and output:

# gcc -fsanitize=address -fno-omit-frame-pointer  -g linked_list.c  && ./a.out
0 
1       0 
2       1       0 
3       2       1       0 
4       3       2       1       0 
3       2       1       0 

Complete Code#

For the complete code, please refer to the code listing.
https://github.com/austin2035/learn-data-structures

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.