[C language] sequence list and linked list

1. Sequence table

A sequential table is a more advanced array.
Array features: a continuous storage space to store elements of the same type.
A sequence table can store elements of any type, but a sequence table can only store elements of the same type.

1.1 implementation of first version code

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

//Sequence table structure definition
typedef struct Vector {
    int *data;  //Continuous storage space
    int size; //Total number of elements that the sequence table can hold
    int length; //Number of stored elements
} Vec;

/*
 * Initialization of sequence table
 */
Vec *init(int n) {
    Vec *v = (Vec *)malloc(sizeof(Vec)); 
    v->data = (int *)malloc(sizeof(int) * n);
    v->size = n;
    v->length = 0;
    return v;
}


/*
 * Insertion of sequence table
 */
int insert(Vec *v, int val, int ind) {
    if (v == NULL) //Sequence table does not exist
        return 0;
    if (ind < 0 || ind > v->length) //The inserted position is illegal
        return 0;
    if (v->length == v->size) //The element in the sequence table has reached the maximum capacity
        return 0;
    for (int i = v->length; i > ind; i--) { //Move the element at and after the position to be inserted, starting from the last element
        v->data[i] = v->data[i - 1];
    }
    v->data[ind] = val;
    v->length++;
    return 1;
}


/*
 * Deletion of sequence table
 */
int erase(Vec *v, int ind) {
    if (v == NULL) //Judge whether the sequence table exists
        return 0;
    if (ind < 0 || ind >= v->length) //ind illegal
        return 0;
    for (int i = ind + 1; i < v->length; i++) {
        v->data[i - 1] = v->data[i];
    }
    v->length--;
    return 1;
}


/*
 * Destruction of sequence table
 */
void clear(Vec *v) {
    if (v == NULL) return ; //Judge whether the sequence table exists
    //Destroy malloc requested space
    free(v->data); //Release data area
    free(v); //Destruction sequence table
    return ;
}


/*
 * Output of sequence table elements
 */
void output(Vec *v) {
    if (v == NULL) return ;
    printf("Vector : [");
    for (int i = 0; i < v->length; i++) {
        i && printf(", ");
        printf("%d", v->data[i]);
    }
    printf("]\n");
    return ;
}


int main() {
    srand(time(0)); //Set random seed
    #define max_op 20 
    Vec *vec = init(max_op);
    for (int i = 0; i < max_op; i++) {
        int val = rand() % 100;
        int ind = rand() % (vec->length + 3) - 1; //The randomly generated subscript may be negative or greater than V - > length, making the test more comprehensive
        int op = rand() % 4;
        switch (op) {
            case 0:
            case 1:
            case 2: {
                printf("insert %d at %d to Vector, result = %d\n", val, ind, insert(vec, val, ind));
            } break;
            case 3: {
                printf("erase an item at %d from Vector, result = %d\n", ind, erase(vec, ind));
            } break;
        }
        output(vec);
        printf("\n");
    }
    clear(vec);
    #undef max_op 

    return 0;
}

When the current sequence table is full, no more elements can be inserted, which is no different from the array. However, the sequence table is a more advanced array, which can be expanded.

1.2 expansion of sequence table

Dynamic memory development method: the three z methods will open up a continuous memory space and return the first address of this space.

  • malloc: dynamically apply for space in the heap area. It is uncertain whether there is content in the memory

  • calloc: dynamically apply for a piece of memory space, and the memory space can be set to a fixed value, that is, the emptying operation

  • realloc: to open up space again, you can open up a continuous storage space again, so as to achieve the effect of capacity expansion.

    • 1) For example, if the expansion is twice the original size, realloc will try to open up an equivalent space behind the current space data (data is the first address of the space). If the expansion is successful, the first address of the data will also be returned;
    • 2) If the data space cannot be doubled, a space twice as large as the original data space will be found in the memory space, and the contents of the original data will be sequentially copied to the new space. After the copy is completed, realloc will actively release the original data space.
    • 3) If both 1) and 2) fail, realloc fails and returns a NULL address.

The following code is problematic:

int expand(Vec *v) {
	int extr_size = v->size;
	v->data = (int *)realloc(v->data, sizeof(int) * (v->size + extr_size));
	return 1;
}

There are three situations for realloc to open up memory space. If the development fails, the empty address is returned, that is, V - > data becomes NULL, then the original address V - > data will be lost forever, resulting in memory leakage.

The right way:

int expand(Vec *v) { //Double each time
    int extr_size = v->size;
    int *p; //To prevent memory leakage, define a pointer variable p
    while (extr_size) {
        p = (int *)realloc(v->data, sizeof(int) * (v->size + extr_size));//realloc V - > data space
        if (p) break; //If p is not empty, it indicates that the development is successful
        extr_size /= 2; //The attempt to open up the space failed, so I tried to open up half
    }
    if (extr_size == 0) return 0; //Development failure
    v->data = p; //The address of the newly opened space is assigned to V - > data
    v->size += extr_size;
    return 1;
}

1.3 final code implementation

Full code:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define COLOR(a, b) "\033[" #b "m" a "\033[0m"
#define GREEN(a) COLOR(a, 32)

typedef struct Vector {
    int *data;
    int size;
    int length;
} Vec;

/*
 * Initialization of sequence table
 */
Vec *init(int n) {
    Vec *v = (Vec *)malloc(sizeof(Vec));
    v->data = (int *)malloc(sizeof(int) * n);
    v->size = n;
    v->length = 0;
    return v;
}

/*
 * Extension of sequence table
 */
int expand(Vec *v) {
    int extr_size = v->size;
    int *p; //To prevent memory leakage, define pointer variables and save new addresses
    while (extr_size) {
        p = (int *)realloc(v->data, sizeof(int) * (v->size + extr_size)); //Expand the space pointed to by V - > data
        if (p) break; //p is empty, indicating that the space realloc failed
        extr_size /= 2; //The attempt to open up the space failed, so I tried to open up half
    }
    if (extr_size == 0) //Development failure
        return 0;
    v->data = p; //Assign the newly opened space address to V - > data
    v->size += extr_size;
    return 1;
}

/*
 * Insertion of sequence table
 */
int insert(Vec *v, int val, int ind) {
    if (v == NULL) 
        return 0;
    if (ind < 0 || ind > v->length) 
        return 0;
    if (v->length == v->size) { //The stored element has reached the maximum capacity
        if (!expand(v)) 
            return 0;
        printf(GREEN("Success to expand! The vector size is %d") "\n", v->size);
    }
    for (int i = v->length; i > ind; i--) {
        v->data[i] = v->data[i - 1];
    }
    v->data[ind] = val;
    v->length++;
    return 1;
}

/*
 * Deletion of sequence table
 */
int erase(Vec *v, int ind) {
    if (v == NULL) return 0;
    if (ind < 0 || ind >= v->length) return 0;
    for (int i = ind + 1; i < v->length; i++) {
        v->data[i - 1] = v->data[i];
    }
    v->length--;
    return 1;
}

/*
 * Destruction of sequence table
 */
void clear(Vec *v) {
    if (v == NULL) return ;
    free(v->data);
    free(v);
    return ;
}

/*
 * Output of sequence table
 */
void output(Vec *v) {
    if (v == NULL) return ;
    printf("Vector : [");
    for (int i = 0; i < v->length; i++) {
        i && printf(", ");
        printf("%d", v->data[i]);
    }
    printf("]\n");
    return ;
}

int main() {
    srand(time(0));
    #define max_op 20 
    Vec *vec = init(5);
    for (int i = 0; i < max_op; i++) {
        int val = rand() % 100;
        int ind = rand() % (vec->length + 3) - 1;
        int op = rand() % 4;
        switch (op) {
            case 0:
            case 1:
            case 2: {
                printf("insert %d at %d to Vector, result = %d\n", val, ind, insert(vec, val, ind));
            } break;
            case 3: {
                printf("erase an item at %d from Vector, result = %d\n", ind, erase(vec, ind));
            } break;
        }
        output(vec);
        printf("\n");
    }
    clear(vec);
    #undef max_op
    return 0;
}

2. Linked list

The linked list can be divided into two parts: inside the program (how the program is defined) and inside the memory.

Because the linked list can be divided into two parts: internal program and internal memory, the linked list is also divided into two parts for definition: the definition of head node (internal program) and the definition of linked list node (internal memory).

Each node of the linked list contains two parts: data field and pointer field.

The sequence table needs to open up a continuous storage space. If the sequence table is full, it needs to be expanded. The linked list is logically sequential and not necessarily physical. The linked list does not need to be expanded. The space it opens up for storing a node does not require continuity physically.

2.1 code implementation

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct ListNode { //Internal memory
    int data;
    struct ListNode *next; //ListNode *next cannot be used because the current structure has not been defined
} ListNode;

typedef struct List { //Program internal
    ListNode head; //It is defined as a node rather than a pointer to facilitate the operation of the linked list. Head is a head node. The data field has no meaning. It is used to represent the head of the linked list
    int length; //Number of current linked list nodes
} List;

/*
 * Initialization of linked list nodes
 */
ListNode *getNewNode(int val) {
    ListNode *node = (ListNode *)malloc(sizeof(ListNode));
    node->data = val;
    node->next = NULL;
    return node;
}

/*
 * Initialization of linked list
 */
List *getLinkList() {
    List *l = (List *)malloc(sizeof(List));
    l->head.next = NULL;
    l->length = 0;
    return l;
}

/*
 * Insertion of linked list nodes
 */
int insert(List *l, int ind, int val) {
    if (l == NULL) 
        return -1;
    if (ind < 0 || ind > l->length) 
        return -1;
    ListNode *node = getNewNode(val);
    ListNode *p = &(l->head);
    while (ind--) { //Find the previous position of the position to be inserted
        p = p->next;
    }
    node->next = p->next;
    p->next = node;
    l->length++;
    return 0;
} 


/*
 * Deletion of linked list nodes
 */
int erase(List *l, int ind) {
    if (l == NULL)
        return -1;
    if (ind < 0 || ind >= l->length) 
        return -1;
    ListNode *p = &(l->head);
    while (ind--) {
        p = p->next;
    }
    ListNode *temp = p->next;
    p->next = temp->next;
    free(temp);
    l->length--;
    return 0;
}

/*
 * Printing of linked list nodes
 */
void output(List *l) {
    if (l == NULL) return ;
    printf("List(%d) = [", l->length);
    for (ListNode *p = l->head.next; p; p = p->next) {
        printf("%d->", p->data);
    }
    printf("NULL]\n");
    return ;
}

/*
 * Destruction of linked list nodes
 */
void clear_node(ListNode *node) {
   if (node == NULL) return ;
    free(node);
    return ;
}

/*
 * Destruction of linked list
 */
void clear(List *l) {
    if (l == NULL) return ;
    ListNode *p = l->head.next;
    ListNode *q;
    while (p) {
        q = p->next;
        clear_node(p);
        p = q;
    }
    free(l);
    return ;
}

int main() {
    srand(time(0));
    #define max_op 20
    List *l = getLinkList();
    for (int i = 0; i < max_op; i++) {
        int val = rand() % 100;
        int ind = rand() % (l->length + 3) - 1;
        int op = rand() % 4;
        switch (op) {
            case 0:
            case 1:
            case 2: {
                printf("insert %d at %d to List, result = %d\n", val, ind, insert(l, ind, val));
            } break;
            case 3: {
                printf("erase an item at %d from List, result = %d\n", ind, erase(l, ind));
            } break;
        }
        output(l);
        printf("\n");
    }
    clear(l);
    #undef max_op
    return 0;
}

2.2 turnover of linked list: head insertion method

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct ListNode {
    int data;
    struct ListNode *next;
} ListNode;

typedef struct List {
    ListNode head;
    int length;
} List;

ListNode *getNode(int val) {
    ListNode *node = (ListNode *)malloc(sizeof(ListNode));
    node->data = val;
    node->next = NULL;
    return node;
}

List *getLinkList() {
    List *l = (List *)malloc(sizeof(List));
    l->head.next = NULL;
    l->length = 0;
    return l;
}

int insert(List *l, int ind, int val) {
    if (l == NULL) 
        return -1;
    if (ind < 0 || ind > l->length) 
        return -1;
    ListNode *node = getNode(val);
    ListNode *p = &(l->head);
    while (ind--) {
        p = p->next;
    }
    node->next = p->next;
    p->next = node;
    l->length++;
    return 0;
}

int erase(List *l, int ind) {
    if (l == NULL) return -1;
    if (ind < 0 || ind > l->length) return -1;
    ListNode *p = &(l->head);
    while (ind--) p = p->next;
    ListNode *temp = p->next;
    p->next = temp->next;
    free(temp);
    l->length--;
    return 0;
}

/*
 * Flip of linked list
 */
//Header insertion method: each insertion node is behind the virtual node head
//Space complexity O(1)
void reverse(List *l) { 
    ListNode *p = l->head.next;
    l->head.next = NULL;
    ListNode *q;
    while (p) {
        q = p->next;
        p->next = l->head.next;
        l->head.next = p;
        p = q;
    }
    return ;
}

void output(List *l) {
    if (l == NULL) return ;
    printf("List(%d) = [", l->length);
    for (ListNode *p = l->head.next; p; p = p->next) {
        printf("%d->", p->data);
    }
    printf("NULL]\n");
    return ;
}

void clear_node(ListNode *node) {
    if (node == NULL) return ;
    free(node);
    return ;
}

void clear(List *l) {
    if (l == NULL) return ;
    ListNode *p = l->head.next;
    ListNode *q;
    while (p) {
        q = p->next;
        clear_node(p);
        p = q;
    }
    free(l);
    return ;
}


int main() {
    srand(time(0));
    #define max_op 20 
    List *l = getLinkList();
    for (int i = 0; i < max_op; i++) {
        int val = rand() % 100;
        int ind = rand() % (l->length + 3) - 1;
        int op = rand() % 4;
        switch (op) {
            case 0:
            case 1: {
                printf("insert %d at %d to List, result = %d\n", val, ind, insert(l, ind, val));
            } break;
            case 2: {
                printf("erase an item at %d from List, result = %d\n", ind, erase(l, ind));
            } break;
            case 3: {
                printf("Reverse the List!\n");
                reverse(l);
            } break;
        }
        output(l);
        printf("\n");
    }
    clear(l);
    #undef max_op
    return 0;
}

2.3 exercises

2.3.1 Leetcode 19. Delete the penultimate node

[idea] use the fast and slow pointer to move forward first N N N steps, and then the fast and slow pointers advance one step at the same time until the fast pointer reaches the end of the linked list. Because the head node may change in the final result, a virtual head node is needed to point to the real head node.
[Code]

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
    struct ListNode *p = (struct ListNode *)malloc(sizeof(struct ListNode));
    p->next = head;
    struct ListNode *slow = p, *fast = head;
    while (n--) fast = fast->next;
    while (fast) {
        slow = slow->next;
        fast = fast->next;
    }
    struct ListNode *temp = slow->next;
    slow->next = temp->next;
    free(temp);
    return p->next;
}

2.3.2 Leetcode 24. Exchanging nodes in linked list

[Code]

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* swapPairs(struct ListNode* head){
    if (head == NULL || head->next == NULL) return head;
    struct ListNode *p = (struct ListNode *)malloc(sizeof(struct ListNode));
    p->next = head;
    struct ListNode *now = p;
    while (now->next && now->next->next) {
        struct ListNode *l = now->next, *r= now->next->next;
        now->next = r;
        l->next = r->next;
        r->next = l;
        now = l;
    }
    return p->next;
}

2.3.3 Leetcode 83. Delete duplicate elements in the sorting linked list

[Code]

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* deleteDuplicates(struct ListNode* head){
    if (head == NULL || head->next == NULL) return head;
    struct ListNode *p = head;
    while (p->next) {
        if (p->next->val == p->val) {
            p->next = p->next->next;
        } else {
            p = p->next;
        }
    }
    return head;
}

2.3.4 Leetcode 141. Circular linked list

[idea] the ring linked list is judged by the fast and slow pointers: the fast pointer takes two steps at a time, and the slow pointer takes one step at a time. If the fast and slow pointers coincide, there is a ring; Otherwise, there is no ring.
[Code]

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    //Fast and slow pointer: the fast pointer takes two steps at a time and the slow pointer takes one step at a time. If they coincide, there are rings in the linked list, otherwise there is no ring
    struct ListNode *slow = head, *fast = head;
    while (fast && fast->next) {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow) {
            return true;
        }
    }
    return false;
}

2.3.5 Leetcode 160. Intersecting linked list

[Code]

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
//Double pointer
//After pA reaches the end of A linked list, it points to the head of B linked list; After pB reaches the end of the B linked list, it points to the head of the A linked list
//If they have intersecting nodes, the final pA = pB; Otherwise, the final pA and pB will end the loop with pA = pB = NULL
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if (headA == NULL || headB == NULL) return NULL;
    struct ListNode *pA = headA, *pB = headB;
    while (pA != pB) {
        pA = pA == NULL ? headB : pA->next;
        pB = pB == NULL ? headA : pB->next;
    } 
    return pA;
}

2.3.6 Leetcode 202. Happy number

[title]
Write an algorithm to judge a number n n n is not a happy number.
"Happy number" is defined as:

  • For a positive integer, replace the number with the square sum of the numbers at each position each time.
  • Then repeat this process until the whole number becomes 1, or it may be an infinite loop, but it never becomes 1.
  • If it can be changed to 1, then this number is the happy number.

If n n If n is the happy number, return true; false if not

[idea] the essence is the problem of judging links in linked lists.

[Code]

int getNext(int x) {
    int z = 0;
    while (x) {
        z += (x % 10) * (x % 10);
        x /= 10;
    }
    return z;
}

//Speed pointer
bool isHappy(int n){
    int slow = n, fast = n;
    do {
        slow = getNext(slow);
        fast = getNext(getNext(fast));
    } while (slow != fast && fast != 1);
    return fast == 1;
}

2.3.7 Leetcode 203. Remove linked list elements

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode *dummyHead = (struct ListNode *)malloc(sizeof(struct ListNode));
    dummyHead->next = head;
    struct ListNode *temp = dummyHead;
    while (temp->next != NULL) {
        if (temp->next->val == val) {
            temp->next = temp->next->next;
        } else {
            temp = temp->next;
        }
    }
    return dummyHead->next;
}

2.3.8 Leetcode 206. Reverse linked list

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head){
    if (head == NULL) return head;
    struct ListNode *p = head->next;
    head->next = NULL;
    struct ListNode *q;
    while (p) {
        q = p->next;
        p->next = head;
        head = p;
        p = q;
    }
    return head;
}

2.3.9 Leetcode 234. Palindrome linked list

  • Method 1: save the value of each node in the array, and then judge the value in the array with double pointers
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

#define MAX_N 100000
int arr[MAX_N + 5];

bool isPalindrome(struct ListNode* head){
    //The array holds the value of each node, and then traverses the array with double pointers
    if (head == NULL || head->next == NULL) return true;
    struct ListNode *p = head;
    int k = 0;
    while (p) {
        arr[k++] = p->val;
        p = p->next;
    }

    for (int i = 0, j = k - 1; i < j; i++, j--) {
        if (arr[i] != arr[j]) return false;
    }
    return true;
}
  • Method 2
  1. Use the fast and slow pointer (the fast pointer takes 2 steps each time and the slow pointer takes 1 step each time) to find the midpoint of the linked list
  2. Reverse the second half of the linked list (i.e. from the midpoint to the last node)
  3. Double pointers determine whether the data of each node in the first half and the second half are equal.
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


bool isPalindrome(struct ListNode* head){
    //1. The speed pointer finds the midpoint of the linked list
    struct ListNode *slow = head, *fast = head;
    while (fast && fast->next) {
        fast = fast->next->next;
        if (fast != NULL) slow = slow->next;
    }
    //Now slow is the midpoint
    //2. Turn over the rear half
    struct ListNode *l = slow, *r = slow->next;
    l->next = NULL;
    while (r) {
        struct ListNode *temp = r->next;
        r->next = l;
        l = r;
        r = temp;
    }
    //3. Double pointer to judge the front and rear segments
    struct ListNode *p = head, *q = l;
    while (p && q) {
        if (p->val != q->val) return false;
        p = p->next;
        q = q->next;
    }
    return true;

}

2.3.10 Leetcode 237. Delete nodes in the linked list

[title]
Please write a function so that it can delete a given (non end) node in a linked list. The only parameter passed in to the function is the node to be deleted.
There is a linked list - head = [4,5,1,9], which can be expressed as:

Example 1:

Input: head = [4,5,1,9], node = 5
 Output:[4,1,9]
Explanation: given the second node with a value of 5 in your linked list, after calling your function, the linked list should be 4 -> 1 -> 9.

Example 2:

Input: head = [4,5,1,9], node = 1
 Output:[4,5,9]
Explanation: given the third node with a value of 1 in your linked list, after calling your function, the linked list should be 4 -> 5 -> 9.

Tips:

  • The linked list contains at least two nodes
  • The values of all nodes in the linked list are unique
  • The given node is a non end node and must be a valid node in the linked list
  • Don't return any results from your function

[Code]

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
void deleteNode(struct ListNode* node) {
    if (node == NULL || node->next == NULL) {
        node = NULL;
        return ;
    }
    //Gets the value of the next node
    node->val = node->next->val;
    //Delete the node that has obtained the value
    node->next = node->next->next;
}

Tags: C data structure linked list

Posted on Tue, 07 Sep 2021 19:20:46 -0400 by jackiw