3- singly linked list data structure

Then on a dynamic array of static arrays by expansion after creating a dynamic array, here again create a dynamic array achieved through a one-way linked list. First, first to analyze the shortcomings dynamic array, to be able to understand the significance of the list. First, review the process of adding and deleting dynamic array before under:

Dynamic array element added when the worst case is inserted into the head of the array element, so the element is required to move backward turn, depends on the number of operands for the current element, complexity is O (n), preferably the situation is appended to the tail of the array, no moving elements, the complexity is O (1). The average complexity is O (n). Since the expansion of operations are not required every time you add, capacity is needed only when overflow expansion when complexity is O (n), not when the expansion is O (1), amortized complexity is still O (1) .

Delete and add substantially the same, the complexity of the tail best case is to delete the team O (1), the worst case is to delete the first team, the complexity is O (n), the average complexity is O (n).

Whereas in the time index value, since essentially by index values ​​directly from the array, the array values ​​complexity is O (1), so that the complexity of the elements is taken O (1).

Note: do not need to be traversed from the values ​​in the array, but directly through the address value calculation complexity is O (1).

Then the list will not be faster than a static array of it, let's implement a custom singly linked list, then compare to know. First, a reminder, a one-way linked list may not think so fast, oh.

First look at the different arrays and linked lists in memory

The list is not contiguous in memory, stored in a linked list of each unit is called a node, the one-way linked list, each node stores two values, a pointer to the storage element is stored, pointing to the other pointer to the next node, this list can be found under the current node by a pointer pointing to a node in each node, as long as the list to obtain a first node, the node can access to the list, it is possible to simultaneously get all the elements of the list.

Way linked list node structure

Such a look-way linked list is not very simple, the Objective-C language, each node is equivalent object has two member variables, a stored-value is and the other is able to save a node object, the following declare a one-way linked list of node objects:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface JKRSingleLinkedListNode : NSObject

@property (nonatomic, strong, nullable) id object;
@property (nonatomic, strong, nullable) JKRSingleLinkedListNode *next;

- (instancetype)init __unavailable;
+ (instancetype)new __unavailable;
- (instancetype)initWithObject:(nullable id)object next:(nullable JKRSingleLinkedListNode *)next;

@end

NS_ASSUME_NONNULL_END
复制代码

Way linked list structure

Since the only way to get the head node list, you can gain access to all the nodes, then the one-way linked list needs to be stored member variables need only two, one is _size, storage length of this list. Another is _first, save the first node of the list.

Note: _size stored in the parent class, all of the interface declaration is also the parent class, see definition of the parent class dynamic array expansion through a static array , the full source end of the article.

#import "JKRBaseList.h"
#import "JKRSingleLinkedListNode.h"

NS_ASSUME_NONNULL_BEGIN

@interface JKRSingleLinkedList : JKRBaseList {
    // NSUInteger _size; 
    JKRSingleLinkedListNode *_first;
}

@end

NS_ASSUME_NONNULL_END
复制代码

Here is the complete memory structure diagram of a one-way linked list can be more direct understanding way linked list structure:

Find node by index

The above list structure can be seen in FIG., The length of the head node list object stores the list and this list, if a specific one to be obtained by the index node, the node needs to start from scratch by the next pointer back one by one to find until it finds the index th node.

Depending on the complexity index time, taking index 0 as the node need only access to the head node, only one visit. Last access node requires the head node has access to the end node, the number of visits depends on the number of nodes. In summary average time complexity of O (n).

- (JKRSingleLinkedListNode *)nodeWithIndex:(NSInteger)index {
    [self rangeCheckForExceptAdd:index];
    JKRSingleLinkedListNode *node = _first;
    for (NSInteger i = 0; i < index; i++) {
        node = node.next;
    }
    return node;
}
复制代码

By index value

The above has been achieved node to get the index position, where just call method to get the node, then the node returns the value stored on it. Find the node with the time complexity: O (n)

- (id)objectAtIndex:(NSUInteger)index {
    return [self nodeWithIndex:index].object;
}
复制代码

Add Nodes

Node is inserted in the middle of the list

Node is inserted in the middle of the list, as shown, there are three nodes in the list, in this case we need a list index for the location of a node is inserted:

At this point need to do it is to make index node 0 of the next point to the new node, and let the new node next point to the original index node 1:

Such an insert will be successfully inserted into the linked list node in two nodes:

Inserting the head node in the linked list

Below the insertion node in the linked list Head:

Then you need to point to the new node's next _first original list, and the list of _first point to the new node:

After successfully inserted in the list header linked list node structure:

Node of the list to add code to achieve

Fully step, add code to the list of nodes to achieve the following two situations:

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
    [self rangeCheckForAdd:index];
    
    if (index == 0) {
        JKRSingleLinkedListNode *node = [[JKRSingleLinkedListNode alloc] initWithObject:anObject next:_first];
        _first = node;
    } else {
        JKRSingleLinkedListNode *prev = [self nodeWithIndex:index - 1];
        JKRSingleLinkedListNode *node = [[JKRSingleLinkedListNode alloc] initWithObject:anObject next:prev.next];
        prev.next = node;
    }
    
    _size++;
}
复制代码

index bounds checking when adding a dynamic array of parent unified implementation.

Because a node is added, though only one insertion operations, it involves looking at the index position of the nodes, the complexity of the lookup is O (n), so to add a node complexity of O (n).

Delete Node

To delete a node in the middle of the list

Remove list index is assumed that the node 1, as shown below:

Only need to be removed before the next pointer to a node node change point, the next point to be deleted node of a node, then the node is deleted because there is no reference, it will automatically be recovered:

Delete the linked list structure:

Delete list head node

The first node deletes the list below:

To _first only way linked list head pointer to the original node to a node:

Delete the linked list structure:

To delete a node list of code implementation

In summary both cases, the list of deleted code is as follows:

- (void)removeObjectAtIndex:(NSUInteger)index {
    [self rangeCheckForExceptAdd:index];
    
    JKRSingleLinkedListNode *node = _first;
    if (index == 0) {
        _first = _first.next;
    } else {
        JKRSingleLinkedListNode *prev = [self nodeWithIndex:index - 1];
        node = prev.next;
        prev.next = node.next;
    }
    _size--;
}
复制代码

With the add operation node, while deleting nodes need only one operation, but involves looking at index nodes, the complexity of the lookup is O (n), so the complexity of the deleted node also is O (n).

As for the other features are based on the above call to implement several interfaces, such as the tail added or deleted head nodes, etc., do not list them, and finally have the source code.

And dynamic arrays complexity versus time

data structure Dynamic Array Singly linked list
A detailed breakdown The best worst average The best worst average
Insert element anywhere O (1) O (n) O (n) O (1) O (n) O (n)
Delete any location element O (1) O (n) O (n) O (1) O (n) O (n)
Replace any position elements O (1) O (1) O (1) O (1) O (n) O (n)
Find any location element O (1) O (1) O (1) O (1) O (n) O (n)
Add an element to the end O (1) O (n) O (1) O (n) O (n) O (n)
Delete trailing elements O (1) O (1) O (1) O (n) O (n) O (n)
Add an element to the head O (n) O (n) O (n) O (1) O (1) O (1)
Delete header element O (n) O (n) O (n) O (1) O (1) O (1)

The above is summed up by the time complexity of comparison:

  • Inserted anywhere elements: dynamic array elements need to move, and move closer to the head more times. Singly linked list node for the need to find a pointer operation is performed, and the tail closer look more times.
  • Delete anywhere elements: dynamic array elements need to move, and move closer to the head more times. Singly linked list node for the need to find a pointer operation is performed, and the tail closer look more times.
  • Alternatively anywhere elements: dynamic arrays directly by index taking a value for the location, the operand 1 is stable. Singly linked list node for the need to find a pointer operation is performed, and the tail closer look more times.
  • Find anywhere elements: dynamic arrays directly by index taking a value for the location, the operand 1 is stable. Singly linked list node for the need to find a pointer operation is performed, and the tail closer look more times.
  • Adding elements to the tail: tail add dynamic array index value can be added directly to the corresponding end of the array, although there may expansion operation, but is still down amortized O (1). Singly linked list node from scratch tail node has been found, it is O (n).
  • Removing the trailing elements: Dynamic end of the array can be directly added in the index adding value corresponding to the end of the array, a fixing operations. Singly linked list node from scratch tail node has been found, it is O (n).
  • Add elements to the head: dynamic array requires the greatest number of mobile elements, to O (n). Singly linked list requires only one operation.
  • Delete header element: Dynamic Array requires the greatest number of mobile elements is O (n). Singly linked list requires only one operation.

From the above analysis can be up to, at the time the check list is not all cases are better than the complexity of a dynamic array, when required frequent deletion and added to the array head, the one-way linked list superior dynamic array. When frequent need to delete and add the end of the array, the array is superior dynamic way linked list.

The following test:

10,000 for the head insertion and deletion operations, dynamic arrays and Comparative singly linked list:

10000 tail for insertion and deletion operations, dynamic arrays and Comparative singly linked list:

So better not to say that dynamic arrays on a one-way linked list certain time complexity, still have to distinguish between the different scenarios. If you require frequent head array insert and delete operations, one-way linked list is much better than the dynamic array. If the need for frequent insertions and deletions in the end of the array, dynamic array is much better than a one-way linked list.

Source

Click to view source code

Guess you like

Origin blog.csdn.net/weixin_34162401/article/details/91399651