[Algorithm Basics] Arrays and linked lists, dynamic arrays, circular arrays, variants of linked lists

Table of contents

1 Array

1.1 Definition and characteristics

1.2 Basic operations

1.3 Time complexity of arrays

1.4 Application scenarios

2 Linked List

2.1 Definition and characteristics:

2.1.1 Singly Linked List

2.1.2 Doubly Linked List:

2.1.3 Circular Linked List:

2.2 Basic operations

2.2.1 Create linked list:

2.2.2 Insert node:

2.2.3 Delete node

2.2.4 Traversing the linked list

2.3 Time complexity

2.4 Application scenarios


1 Array

  • An array is a linear data structure consisting of elements of the same type, each element accessed through an index.
  • Elements are stored contiguously in memory.
  • The size of an array is usually fixed at creation time and cannot be easily expanded or reduced.

1.1 Definition and characteristics

Example: 

# 创建一个整数数组,包含5个元素
my_array = [10, 20, 30, 40, 50]

# 访问数组中的元素,通过索引访问
print(my_array[0])  # 输出:10
print(my_array[3])  # 输出:40

# 修改数组中的元素
my_array[2] = 35

# 遍历数组并打印所有元素
for element in my_array:
    print(element)

# 输出:
# 10
# 20
# 35
# 40
# 50

          Array is a common linear data structure that contains elements of the same type, and each element can be accessed through an index. The elements in an array are stored contiguously in memory, which means their addresses are consecutive, which facilitates fast element access. The size of an array is usually fixed when it is created and cannot be easily expanded or reduced. This is the main difference between arrays and other dynamic data structures (such as dynamic arrays or linked lists).

        In the example, we create an array with 5 integer elements and use indexing to access and modify the elements. The elements in the array are stored contiguously in memory so that we can access them efficiently. Please note that the size of the array is fixed at creation time, if more elements are needed, a new array needs to be created.


1.2   Basic operations

  • Create an array: Declare the array type and size.
  • Access elements: Use an index to access an element at a specific position.
  • Insert element: Insert a new element into the array at the specified position.
  • Delete element: Delete the element at the specified position from the array.
  • Update element: Modify the element value at a specific position in the array.

Create an array :

  • When creating an array, you usually need to declare the array's type and size. Different programming languages ​​may have different syntax for creating arrays.
  • The size of an array indicates how many elements the array can hold, and once the size is fixed, it usually cannot be changed easily.
my_array = [0, 0, 0, 0, 0] # 创建一个包含5个元素的整数数组

 Access elements :

You can use the index of an array to access an element at a specific position. The index usually starts from 0 and increases, indicating the position of the element in the array.

value = my_array[2] # 访问索引为2的元素,将值赋给变量value

 Insert elements :

        Inserting elements into an array usually requires specifying the position to insert and the value to insert. Insertion operations may require moving subsequent elements to make space.

my_array.insert(2, 25) # 在索引为2的位置插入值25

  Delete elements

Removing elements from an array usually requires specifying the position to remove. Deletion may require moving subsequent elements to fill gaps.

del my_array[3] # 删除索引为3的元素

 Update element :

Updating the value of an element in an array simply accesses the element by index and assigns the new value to that element.

my_array[1] = 15 # 更新索引为1的元素值为15

These operations are common to arrays, but note that the nature of arrays depends on the programming language and library implementation. In some languages, arrays may be of fixed size, while in others there may be dynamic arrays that automatically expand to accommodate more elements. Make sure you understand the behavior and limitations of arrays in the programming language or library you are using.

# 创建一个整数数组
my_array = [1, 2, 3, 4, 5]

# 访问数组元素
first_element = my_array[0]  # 第一个元素
second_element = my_array[1]  # 第二个元素

# 修改数组元素
my_array[2] = 10  # 将第三个元素修改为10

# 获取数组的长度
array_length = len(my_array)  # 返回数组的长度,这里为5

# 插入元素(在索引位置2插入新元素)
my_array.insert(2, 6)  # [1, 2, 6, 10, 4, 5]

# 删除元素(删除第四个元素)
del my_array[3]  # [1, 2, 6, 4, 5]

1.3 Time complexity of arrays

  • Accessing elements: O(1)
  • Inserting and deleting elements (at specific positions): O(n)
  • Update elements: O(1)

Accessing elements : O(1) 

        The time complexity of accessing an element at a specific index position in an array is constant time because the memory address of the element can be calculated directly from the index.

my_array = [1, 2, 3, 4, 5] 
element = my_array[2] # 访问索引为2的元素(值为3),时间复杂度为O(1)

Inserting and deleting elements (at specific positions) : O(n)

  • Inserting and deleting elements at a specific position in an array requires moving all elements after that position backward or forward to make room for new or filler elements. Therefore, the time complexity of these operations is linear and related to the array length.

Example

my_array = [1, 2, 3, 4, 5]
my_array.insert(2, 6)  # 在索引2处插入元素6,需要将后面的元素移动,时间复杂度为O(n)
del my_array[3]  # 删除索引3处的元素,需要将后面的元素移动,时间复杂度为O(n)

Update elements : O(1)

  • The time complexity of updating an element at a specific index position in an array is constant time because you only have to find the index and modify the value at that position.

Example:

my_array = [1, 2, 3, 4, 5]
my_array[2] = 10  # 更新索引2处的元素为10,时间复杂度为O(1)

        It is important to note that although the time complexity of inserting and deleting elements is O(n), in some cases it may actually only take constant time if the operation is performed at the end of the array. But in the worst case, a large number of elements need to be moved, so the average time complexity is O(n).

        Summary: The time complexity of an array depends on the specific operation. The operation of accessing elements is very efficient (O(1)), but the operations of inserting and deleting elements are usually slower (O(n)), and the operation of updating elements is also very efficient. (O(1)). Therefore, when choosing a data structure, you need to weigh the advantages and disadvantages of arrays according to different operational requirements.

1.4   Application scenarios

  • Suitable for situations where fast access to elements and a known size is required.
  • Arrays are used to implement various data structures, such as stacks, queues, matrices, etc.

Arrays are great for:

  1. Fast access to elements : Since the elements in an array are stored contiguously in memory, the element at a specific position can be accessed very quickly through indexing. This makes arrays ideal for applications that require frequent access to elements.

  2. Known size : The size of an array is usually fixed at creation time and cannot be easily expanded or reduced. Therefore, using an array is a good choice when the size of the data set is known to be fixed or can be determined in advance.

  3. Implement other data structures : Arrays can be used to implement various other data structures, such as stacks, queues, matrices, etc. For example, stacks can be manipulated through the end of the array, queues can be manipulated through the front of the array, and matrices can be represented using multidimensional arrays.

Example 1: Implementing a stack using arrays

class Stack:
    def __init__(self):
        self.items = []

    def push(self, item):
        self.items.append(item)

    def pop(self):
        if not self.is_empty():
            return self.items.pop()

    def is_empty(self):
        return len(self.items) == 0

    def peek(self):
        if not self.is_empty():
            return self.items[-1]

    def size(self):
        return len(self.items)

# 创建一个栈并进行操作
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop())  # 输出: 3
print(stack.peek())  # 输出: 2

Example 2: Using arrays to represent matrices

# 创建一个3x3矩阵
matrix = [[1, 2, 3],
          [4, 5, 6],
          [7, 8, 9]]

# 访问矩阵中的元素
element = matrix[0][1]  # 访问第一行第二列的元素,输出: 2

# 更新矩阵中的元素
matrix[1][2] = 10  # 更新第二行第三列的元素为10

# 输出整个矩阵
for row in matrix:
    print(row)
# 输出:
# [1, 2, 3]
# [4, 5, 10]
# [7, 8, 9]

        In summary, arrays are useful in situations where fast access to elements and a known size is required, and can be used to implement a variety of common data structures and data representations.


2 Linked List

2.1 Definition and characteristics :

  • A linked list is a linear data structure consisting of nodes, each node containing data and a pointer to the next node.
  • The nodes in the linked list are not necessarily stored continuously in memory, they are connected together through pointers.
  • Linked lists can be of different types such as singly linked lists, doubly linked lists, and circular linked lists.

        A linked list is a common linear data structure. Unlike an array, the elements (nodes) in a linked list are not stored continuously in memory, but are connected to each other through pointers. Each node contains two parts: data and a pointer to the next node.

The main linked list types include:

2.1.1 Singly Linked List

  • Each node contains data and a pointer to the next node.
  • The pointer to the last node usually points to null.

2.1.2 Doubly Linked List:

  • Each node contains data, a pointer to the next node, and a pointer to the previous node.
  • A linked list can be traversed in both directions.

2.1.3 Circular Linked List:

  • A variant of a one-way or two-way linked list in which the last node points to the first node, forming a cycle.

        The advantage of linked lists is that memory can be allocated dynamically at runtime, so there is no need to pre-specify the size like arrays .

        This makes linked lists more flexible in some situations, but also makes accessing elements less efficient because the linked list needs to be traversed in order.


Example (Singly Linked List in Python):

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None  # 初始时没有下一个节点

# 创建链表
node1 = Node(10)
node2 = Node(20)
node3 = Node(30)

# 连接节点
node1.next = node2
node2.next = node3

# 遍历链表并打印数据
current_node = node1
while current_node is not None:
    print(current_node.data)
    current_node = current_node.next

 This is a simple example of a one-way linked list where each node contains an integer data and a pointer to the next node. The linked list starts from node1 and contains node2 and node3 in sequence. The pointer of the last node is None. When traversing the linked list, we start from the first node and move to the next node through the next pointer until the end of the linked list.

2.2   Basic operations

  • Create a linked list: declare the head node and initialize it to empty.
  • Insert node: Insert a new node in the linked list, usually with head insertion and tail insertion.
  • Delete node: Delete the specified node from the linked list.
  • Traverse the linked list: visit all nodes in the linked list in sequence.

        A linked list is a basic data structure that consists of nodes, each node containing data and a pointer to the next node. The basic operations of linked lists are explained in detail below, with examples.

2.2.1 Create linked list :

        The creation of a linked list usually starts with declaring the head node, which is initially empty. The head node is the entry point of the linked list and is used to access the entire linked list.

2.2.2 Insert node :

        There are two common ways to insert a new node into a linked list:

  • Insert at the Beginning: Insert a new node at the beginning of the linked list, and the new node becomes the new head node.
  • Insert at the End: Insert a new node at the end of the linked list.

2.2.3 Delete node

        Deleting a specified node from a linked list usually involves the following steps:

  • Find the previous node (predecessor node) of the node to be deleted.
  • Update the pointer of the predecessor node to point to the node next to the node to be deleted, thus bypassing the node to be deleted.

2.2.4 Traversing the linked list

        Traversing a linked list means visiting all nodes in the linked list in sequence, usually using a loop.

Here is a simple example that demonstrates how to create, insert, delete, and traverse a linked list:

# 定义链表节点类
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

# 定义链表类
class LinkedList:
    def __init__(self):
        self.head = None

    # 插入节点到链表末尾(尾插法)
    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
        else:
            current = self.head
            while current.next:
                current = current.next
            current.next = new_node

    # 删除节点
    def delete(self, data):
        if not self.head:
            return
        if self.head.data == data:
            self.head = self.head.next
            return
        current = self.head
        while current.next:
            if current.next.data == data:
                current.next = current.next.next
                return
            current = current.next

    # 遍历链表并打印节点数据
    def display(self):
        current = self.head
        while current:
            print(current.data, end=" -> ")
            current = current.next
        print("None")

# 创建一个空链表
my_linked_list = LinkedList()

# 插入节点
my_linked_list.append(1)
my_linked_list.append(2)
my_linked_list.append(3)

# 遍历并显示链表
my_linked_list.display()  # 输出: 1 -> 2 -> 3 -> None

# 删除节点
my_linked_list.delete(2)

# 再次遍历并显示链表
my_linked_list.display()  # 输出: 1 -> 3 -> None

2.3 Time complexity

  1. Visiting nodes: O(n)
  2. Inserting and deleting nodes: O(1) (if the node location is known) or O(n) (if the location needs to be found)

        A linked list is a linear data structure whose time complexity for access, insertion, and deletion operations depends on the location of the operation and whether the location of the node is known. The time complexity of linked lists is explained in detail below, with examples.

Access node :

        The nodes in the linked list are not directly accessed through the index like the array. Instead, the nodes need to be traversed one by one starting from the head node and along the pointer. Therefore, the time complexity of accessing the node is O(n), where n is the length of the linked list.

Insert node :

        The time complexity of inserting a node depends on the insertion position:

  • If the location to be inserted is known, such as the head of a linked list, then the time complexity of inserting a node is O(1) because only the pointer needs to be updated.
  • If you need to insert a node in the middle or at the end of the linked list, but you don't know the insertion location, you need to traverse the linked list first to find the insertion location, so the time complexity of inserting the node is O(n).

Deleting a node : The time complexity of deleting a node also depends on where it is deleted:

  • If the location of the node to be deleted is known, such as the head of the linked list, then the time complexity of deleting the node is O(1) because only the pointer needs to be updated.
  • If you need to delete a node in the middle or at the end of the linked list, but do not know the deletion position, you need to traverse the linked list to find the deletion position first, so the time complexity of deleting the node is O(n).

Here is an example that demonstrates the operations of accessing, inserting, and deleting nodes and their time complexity:

# 定义链表节点类
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

# 定义链表类
class LinkedList:
    def __init__(self):
        self.head = None

    # 插入节点到链表末尾(尾插法)
    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
        else:
            current = self.head
            while current.next:
                current = current.next
            current.next = new_node

    # 删除节点
    def delete(self, data):
        if not self.head:
            return
        if self.head.data == data:
            self.head = self.head.next
            return
        current = self.head
        while current.next:
            if current.next.data == data:
                current.next = current.next.next
                return
            current = current.next

    # 访问节点(需要遍历链表)
    def access(self, index):
        current = self.head
        count = 0
        while current:
            if count == index:
                return current.data
            current = current.next
            count += 1
        return None

# 创建一个空链表
my_linked_list = LinkedList()

# 插入节点
my_linked_list.append(1)
my_linked_list.append(2)
my_linked_list.append(3)

# 访问节点(通过索引访问第二个节点)
value = my_linked_list.access(1)
print("访问节点:", value)  # 输出: 2

# 删除节点
my_linked_list.delete(2)

# 访问节点(通过索引访问第二个节点,此时已删除节点2)
value = my_linked_list.access(1)
print("访问节点:", value)  # 输出: 3

2.4 Application scenarios

  • Suitable for situations where nodes need to be frequently inserted and deleted, or data is stored without knowing the amount of data.
  • Linked lists are often used to implement data structures such as stacks, queues, hash tables, and LRU caches.
  • Special types of linked lists, such as doubly linked lists, can be used for bidirectional traversal.

 

  1. Dynamic array :

    • Dynamic arrays are array-based data structures that can automatically expand or shrink in size to accommodate different numbers of elements.
    • Dynamic arrays are typically used in scenarios where flexibility and efficiency are required, but may require more memory.
  2. Loop array :

    • A circular array is a special type of array whose elements can be accessed iteratively, commonly used in ring buffers and circular queues.
  3. Variations of linked lists :

    • One-way linked list: Each node has a pointer to the next node.
    • Doubly linked list: Each node has a pointer to the previous node and the next node.
    • Circular linked list: The tail node points to the head node, forming a closed loop.

The above is an outline of the basic knowledge of arrays and linked lists. They are important concepts in data structures and are very important for understanding and designing algorithms and solving various programming problems. In actual programming, choosing an appropriate data structure depends on the needs of the problem and performance requirements.

 

Guess you like

Origin blog.csdn.net/qq_35831906/article/details/133272424