Data Structure Chapter 2 Homework Linear Table Xi'an Petroleum University

How many nodes need to be moved on average to insert and delete a node in the sequence table? The exact number of moves depends on what two factors?

When inserting and deleting a node in the sequence table, the average number of nodes moved depends on two factors: the insertion/deletion position and the length of the current sequence table.

  1. Insertion/deletion position: If you want to perform an insertion/deletion operation at the beginning or end of the sequence table, you do not need to move other nodes, so the number of nodes moved is smaller. However, if you want to perform an insertion/deletion operation in the middle of the sequence table, then all nodes behind this position need to be moved backward/forward by one position, and the number of nodes moved will be more.

  2. The length of the current sequence table: As the length of the sequence table increases, the number of nodes that need to be moved for insertion and deletion operations will also increase. Because as the sequence table expands, the number of nodes that need to be moved is proportional to the number of data in the sequence table.

In the worst case, when inserting or deleting a node in the sequence list, it may be necessary to move all nodes located after the insertion/deletion position, so the number of nodes moved is n-m+1, where n is the sequence list The length of m is the insertion/deletion position.

It is important to note that this is only an estimate of the average situation. In some special cases, there may be specific algorithms or optimization techniques that can reduce the number of nodes moved. However, due to the nature of sequential tables, insertion and deletion operations are usually slow, especially when frequent insert/deletion operations are performed in large sequential tables. If you need to perform insertion and deletion operations efficiently, consider using other data structures, such as linked lists. Linked lists generally have better performance for insertion and deletion operations, but are relatively slow for random access.

Insert image description here

In the linked storage structure of a linear list, explain the fundamental difference between the head pointer and the head node, and the relationship between the head node and the first node.

In the linked storage structure of a linear list, there is a fundamental difference between the head pointer and the head node, and there is a specific relationship between the head node and the first node.

  1. Head Pointer: The head pointer is a pointer to the first node of the linked list. It is only used to record the starting position of the linked list and does not contain actual data. The head pointer can be NULL, indicating that the linked list is empty.

  2. Head Node: The head node is an empty node set up to facilitate linked list operations, located before the node pointed by the head pointer. The head node has two functions:

    • Simplify the insertion and deletion operations of linked lists, operate after the head node, and unify the processing of the first node and other nodes.
    • When calculating the length of the linked list, the head node is not calculated, so that the length is 0 when the linked list is empty, and the length is equal to the actual number of nodes when it is not empty.
  3. First Node: The first node is the first actual data node in the linked list, and is also the node pointed by the head pointer. It contains the actual data and a pointer to the next node.

To sum up, the head pointer is just a pointer to the starting position of the linked list, and the head node is an empty node set for convenient operation and does not contain actual data. The first node is the first actual data node in the linked list and is also the node pointed by the head pointer.

In a specific chain storage structure, you can choose whether to use the head node according to your needs. When using the head node, the head pointer points to the head node, and the first node is the first node after the head node. When the head node is not used, the head pointer points directly to the first node.

It should be noted that in a chained storage structure, insertion and deletion operations usually require operating the previous node to modify the pointer pointing. The introduction of the head node can be treated uniformly, making it more convenient to process various operations of the linked list.

Insert image description here

1. Design an efficient algorithm to reverse all elements of the sequence list L. The space complexity of the algorithm is required to be 0(1). 2. For a sequence list L of length n, find the data element with the smallest value in the sequence list. 3. In the singly linked list L with the head node, delete all nodes with a value of 4. Let L be a singly linked list with the head node, and write an algorithm to output the value of each node in reverse from the end to the beginning.

  1. An efficient algorithm for inverting the sequence list L is as follows:
def reverse_list(L):
    n = len(L)
    for i in range(n//2):
        L[i], L[n-i-1] = L[n-i-1], L[i]

The time complexity is O(n) and the space complexity is O(1).

  1. The algorithm to find the smallest data element in the sequence list L of length n is as follows:
def find_min(L):
    min_value = L[0]
    for i in range(1, len(L)):
        if L[i] < min_value:
            min_value = L[i]
    return min_value

The time complexity is O(n).

  1. The algorithm for deleting all nodes with value x in the singly linked list L with the head node and releasing its space is as follows:
def del_node(L, x):
    pre = L
    cur = L.next
    while cur != None:
        if cur.data == x:
            pre.next = cur.next
            del cur
            cur = pre.next
        else:
            pre = cur
            cur = cur.next

The time complexity is O(n) and the space complexity is O(1).

  1. The algorithm for outputting the value of each node in the singly linked list L of the leading node from beginning to end is as follows:
def print_reverse(L):
    if L.next == None:
        return
    print_reverse(L.next)
    print(L.next.data)

The time complexity is O(n) and the space complexity is O(n) (due to the use of recursion). If the space complexity is required to be O(1), it can be implemented using a loop, as follows:

def print_reverse(L):
    cur = L.next
    pre = None
    while cur != None:
        next = cur.next
        cur.next = pre
        pre = cur
        cur = next
    while pre != None:
        print(pre.data)
        pre = pre.next

The time complexity is O(n) and the space complexity is O(1). 1. The efficient algorithm for inverting the sequence list L is as follows:

def reverse_list(L):
    n = len(L)
    for i in range(n//2):
        L[i], L[n-i-1] = L[n-i-1], L[i]

The time complexity is O(n) and the space complexity is O(1).

  1. The algorithm to find the smallest data element in the sequence list L of length n is as follows:
def find_min(L):
    min_value = L[0]
    for i in range(1, len(L)):
        if L[i] < min_value:
            min_value = L[i]
    return min_value

The time complexity is O(n).

  1. The algorithm for deleting all nodes with value x in the singly linked list L with the head node and releasing its space is as follows:
def del_node(L, x):
    pre = L
    cur = L.next
    while cur != None:
        if cur.data == x:
            pre.next = cur.next
            del cur
            cur = pre.next
        else:
            pre = cur
            cur = cur.next

The time complexity is O(n) and the space complexity is O(1).

  1. The algorithm for outputting the value of each node in the singly linked list L of the leading node from beginning to end is as follows:
def print_reverse(L):
    if L.next == None:
        return
    print_reverse(L.next)
    print(L.next.data)

The time complexity is O(n) and the space complexity is O(n) (due to the use of recursion). If the space complexity is required to be O(1), it can be implemented using a loop, as follows:

def print_reverse(L):
    cur = L.next
    pre = None
    while cur != None:
        next = cur.next
        cur.next = pre
        pre = cur
        cur = next
    while pre != None:
        print(pre.data)
        pre = pre.next

The time complexity is O(n) and the space complexity is O(1).

Introduce the linear table

A linear table is a common data structure, which is a data collection composed of a set of elements arranged in order. Each element in a linear list has a unique predecessor element and a unique successor element, except that the first element has no predecessor element and the last element has no successor element.

The characteristics of linear tables are:

  1. The order between elements is determined, and the relationship between elements is one-to-one.
  2. Elements in a linear list can be accessed by subscript or position.
  3. The length of a linear table can be fixed or variable.

There are two common ways to implement linear lists: sequential lists and linked lists.

  1. Sequential table: A sequential table uses a continuous memory space to store elements in a linear table. In a sequence table, the physical addresses of elements in memory are arranged in logical order. Elements can be accessed directly through their index values. Insertion and deletion operations require moving other elements, and the time complexity is O(n), but the time complexity of reading elements is O(1).

  2. Linked list: A linked list is a data structure that uses a set of nodes connected to each other through pointers. Each node contains a data element and a pointer to the next node. The storage locations of elements in a linked list in memory can be discontinuous. The insertion and deletion operations of the linked list only need to modify the pointing of the pointer, and the time complexity is O(1). However, accessing the elements requires traversing one by one starting from the head node, and the time complexity is O(n).

Linear tables are widely used in various fields. Data structures such as arrays, linked lists, stacks, and queues are all specific implementations of linear tables. Among algorithms and data structures, linear tables are one of the most basic, simplest, and most commonly used data structures.

In addition to sequential lists and linked lists, there are other common linear list implementations, as described below:

  1. Doubly linked list: A doubly linked list adds a pointer to the predecessor node on the basis of the linked list, so that it can be traversed forward or backward starting from any node. This allows for easy insertion, deletion and other operations, but will add some additional space overhead accordingly.

  2. Circular linked list: A circular linked list is a special type of linked list whose tail node points to the head node, forming a closed loop. In this way, the entire linked list can be traversed through any node, and insertion and deletion operations can be easily performed.

The main operations of linear tables include:

  1. Insertion: Insert an element at a specified position, moving the position and the elements behind it back.
  2. Delete: To delete the element at the specified position, the elements behind the position need to be moved forward.
  3. Search: Find the corresponding element based on the element value or index.
  4. Modify: Modify the element value at the specified position.
  5. Traversal: Visit all elements in a linear list in sequence.

Linear tables have a wide range of application scenarios, such as:

  1. Array: Used to store and manipulate a group of data elements of the same type.
  2. Queue: A data structure used to implement first-in-first-out (FIFO), such as task scheduling, message delivery, etc.
  3. Stack: A data structure used to implement last-in-first-out (LIFO), such as function call stack, expression evaluation, etc.
  4. Hash table: A data structure implemented using arrays and linked lists for efficient storage and lookup of key-value pairs.
  5. String: A string can be thought of as a linear list of characters.

As a simple and common data structure, linear tables provide convenience and foundation for us to solve various practical problems. In practical applications, choosing a suitable linear table implementation can improve the efficiency of the algorithm and the maintainability of the code.

When it comes to manipulating linear tables, there are also some common algorithms and tricks that can improve efficiency and solve specific problems:

  1. Merging linear tables: When two linear tables need to be merged, the characteristics of the sequential table can be used to copy the elements of one linear table to another linear table one by one with a time complexity of O(m+n). end of table.

  2. Reverse a linear table: You can reverse a linear table by traversing the linear table and moving elements from their original positions to new positions in sequence. The time complexity of this operation is O(n), where n is the length of the linear table.

  3. Fast and slow pointers: In linked lists, you can use fast and slow pointer techniques to solve some problems. The fast pointer moves two steps at a time, and the slow pointer moves one step at a time. By comparing the positions of the fast and slow pointers, you can determine whether there is a loop in the linked list or find the intermediate node of the linked list.

  4. Double pointer method: The double pointer method refers to using two pointers to traverse, search or process in a linear table at the same time. Common applications include speed pointers, left and right pointers, etc. For example, when searching for the presence of a target element in an ordered array, you can use two pointers to traverse from the beginning to the end of the array at the same time, and move the pointers based on the comparison results to improve search efficiency.

  5. Ring Buffer: A ring buffer is a special linear table implementation that loops through a fixed-size buffer. By maintaining a read pointer and a write pointer, efficient data insertion and deletion operations can be achieved. Ring buffers are widely used in scenarios such as data stream processing and circular queues.

The above are some common linear table operation algorithms and techniques. They can choose the appropriate method according to the needs of specific problems. In practical applications, selecting appropriate data structures and algorithms based on specific circumstances can improve program efficiency and performance.

There are also some common questions and algorithms to consider when it comes to the application of linear tables:

  1. Find the maximum and minimum values: Find the maximum and minimum elements in the linear table by traversing the linear table and recording the current maximum and minimum values. The time complexity is O(n).

  2. Sorting of linear tables: Common sorting algorithms such as bubble sort, insertion sort, selection sort, quick sort, merge sort, etc. can be applied to the sorting operation of linear tables. Choose an appropriate sorting algorithm based on specific needs and data size.

  3. Deduplication of linear tables: If there are duplicate elements in the linear table, you can use a hash table or sorting method to perform deduplication operations. Use a hash table to store elements in the hash table to determine whether they have already appeared; use sorting to delete duplicate elements through traversal.

  4. Substring matching: For linear tables of string type, you can use the KMP algorithm, Boyer-Moore algorithm, etc. to perform efficient substring matching operations.

  5. Inversion of a linear list: Arranging the elements in a linear list in reverse order can be achieved using the double pointer method or the stack.

  6. Slicing of a linear list: intercept a continuous element from a linear list to form a new linear list. This can be achieved using the interception operator or using fast and slow pointers in a linked list.

  7. Linear table splicing: Join two linear tables to form a new linear table. You can directly copy the elements of the second linear list one by one to the end of the first linear list in the sequential list, or point the tail node of the first linked list to the head node of the second linked list in the linked list.

The above are some common linear table application problems and algorithms. Choosing appropriate algorithms and data structures based on specific needs and scenarios can solve various practical problems.

There are also some common algorithms and techniques to consider when it comes to the application of linear tables:

  1. Subarray sum problem: Given an integer array, find the subarray with the largest sum. You can use dynamic programming algorithms or greedy algorithms to solve this problem.

  2. Find the median of two sorted arrays: Given two sorted arrays, find the median after combining the two arrays. You can use the idea of ​​merge sort to find the median during the merge process.

  3. Rotation of a linear table: cyclically shift elements in a linear table to the right or left according to a specified step size. This can be achieved using the idea of ​​array flipping.

  4. Intersection and union of linear lists: Given two linear lists, find their intersection and union. You can use a hash table to record the number of occurrences of elements, and then get the intersection and union as needed.

  5. Longest increasing subsequence: Given a sequence, find the longest increasing subsequence. Dynamic programming algorithms can be used to solve this problem.

  6. Ring detection: Determine whether a linked list has a ring. You can use the fast and slow pointers to judge. If there is a cycle, the fast pointer will eventually catch up with the slow pointer.

  7. Solve the string edit distance: given two strings, it is required to convert one string into another string through insertion, deletion and replacement operations, and find the minimum number of operations. Dynamic programming algorithms can be used to solve this problem.

The above are some common algorithms and techniques for linear table applications. These algorithms and techniques are very useful in solving practical problems, and the appropriate method can be selected according to specific needs. At the same time, different problems may require a combination of multiple algorithms and techniques to solve, and flexible use can improve efficiency and accuracy in solving problems.

Linear table application examples

Linear tables are one of the most basic data structures in computer science. In practical applications, there are many different scenarios and problems that can be solved using linear tables. The following are some common examples of linear table applications:

  1. Path planning: In map applications, path planning is a common scenario using linear tables. You can convert the road network on the map into a graph, and then use graph traversal algorithms (such as breadth-first search, Dijkstra's algorithm, etc.) to find the shortest path between two locations.

  2. Sorting and searching: For sorting and searching operations on large amounts of data, linear tables can be used. For example, both the quick sort algorithm and the merge sort algorithm can be applied to the sorting operation of the sequence table, and the binary search algorithm can be applied to the search operation of the ordered sequence table.

  3. Cache Management: Cache management is an important issue in computer systems. Linear tables can be used to manage cache queues to improve access efficiency.

  4. String Processing: String manipulation is a very common problem in computer applications. For example, in a text editor, you can use a linear table to store text content and use a string matching algorithm to find and replace specific characters or words in the text.

  5. E-commerce recommendation system: In e-commerce platforms, recommendation systems are a key function. Linear tables can be used to store the user's browsing history and purchase history, and recommend corresponding products based on the user's interests and behavior data.

  6. Game development: In game development, information such as character attributes and item backpacks can be stored and managed using linear tables. For example, when a character is upgraded, the character's experience value, level, health and other attributes need to be modified. These attributes can be stored in a linear table.

The above are some common examples of linear table applications. Because linear tables are simple to understand and easy to implement, they are widely used in practical applications and have derived many related algorithms and technologies.
Insert image description here

The following is a code example using C language to implement a linear table application:

  1. Dynamic array implements linear table:
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    
    
    int* data;
    int length;
    int capacity;
} ArrayList;

ArrayList* createArrayList(int capacity) {
    
    
    ArrayList* list = (ArrayList*)malloc(sizeof(ArrayList));
    list->data = (int*)malloc(sizeof(int) * capacity);
    list->length = 0;
    list->capacity = capacity;
    return list;
}

void destroyArrayList(ArrayList* list) {
    
    
    free(list->data);
    free(list);
}

void addElement(ArrayList* list, int element) {
    
    
    if (list->length >= list->capacity) {
    
    
        // 超出容量时,扩展动态数组
        int newCapacity = list->capacity * 2;
        list->data = (int*)realloc(list->data, sizeof(int) * newCapacity);
        list->capacity = newCapacity;
    }
    list->data[list->length] = element;
    list->length++;
}

int getElement(ArrayList* list, int index) {
    
    
    if (index >= 0 && index < list->length) {
    
    
        return list->data[index];
    }
    return -1; // 返回-1表示索引越界或数据不存在
}

void printArrayList(ArrayList* list) {
    
    
    for (int i = 0; i < list->length; i++) {
    
    
        printf("%d ", list->data[i]);
    }
    printf("\n");
}

int main() {
    
    
    ArrayList* list = createArrayList(5);

    addElement(list, 1);
    addElement(list, 2);
    addElement(list, 3);
    addElement(list, 4);
    addElement(list, 5);

    printf("Elements in the list: ");
    printArrayList(list);

    int element = getElement(list, 2);
    printf("Element at index 2: %d\n", element);

    destroyArrayList(list);

    return 0;
}
  1. Single linked list implements linear list:
#include <stdio.h>
#include <stdlib.h>

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

typedef struct {
    
    
    Node* head;
    int length;
} LinkedList;

LinkedList* createLinkedList() {
    
    
    LinkedList* list = (LinkedList*)malloc(sizeof(LinkedList));
    list->head = NULL;
    list->length = 0;
    return list;
}

void destroyLinkedList(LinkedList* list) {
    
    
    Node* current = list->head;
    while (current != NULL) {
    
    
        Node* temp = current->next;
        free(current);
        current = temp;
    }
    free(list);
}

void addElement(LinkedList* list, int element) {
    
    
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = element;
    newNode->next = NULL;

    if (list->head == NULL) {
    
    
        list->head = newNode;
    } else {
    
    
        Node* current = list->head;
        while (current->next != NULL) {
    
    
            current = current->next;
        }
        current->next = newNode;
    }

    list->length++;
}

int getElement(LinkedList* list, int index) {
    
    
    if (index >= 0 && index < list->length) {
    
    
        Node* current = list->head;
        for (int i = 0; i < index; i++) {
    
    
            current = current->next;
        }
        return current->data;
    }
    return -1; // 返回-1表示索引越界或数据不存在
}

void printLinkedList(LinkedList* list) {
    
    
    Node* current = list->head;
    while (current != NULL) {
    
    
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

int main() {
    
    
    LinkedList* list = createLinkedList();

    addElement(list, 1);
    addElement(list, 2);
    addElement(list, 3);
    addElement(list, 4);
    addElement(list, 5);

    printf("Elements in the list: ");
    printLinkedList(list);

    int element = getElement(list, 2);
    printf("Element at index 2: %d\n", element);

    destroyLinkedList(list);

    return 0;
}

The above is a code example using C language to implement a linear table application. These examples demonstrate the implementation of linear tables based on two different data structures: dynamic arrays and singly linked lists. Please choose the appropriate implementation method according to your needs. In actual use, remember to release memory and consider boundary conditions and exception handling.

Guess you like

Origin blog.csdn.net/shaozheng0503/article/details/132999172