There are two main types of data structures:
1. Logical Structure refers to the relationship between elements in data objects.
2. Physical structure refers to the structure in which elements are stored as physical memory space.
The logical structure is what we often call tables, trees, and graphs. Defines the relationship between elements in the dataset.
The physical structure refers to the storage of elements in the memory (or disk). As we know, the memory is actually a group of storage units that store elements in the memory unit, the two most common methods are,
1> sequential storage, that is, opening up a set of continuous storage units to store the elements we need to put into (MAP array ). 2> chained storage, elements may not be stored in a group of continuous memory units,
We have pointers to the storage locations of two adjacent elements (that is, the linked list we often call ).
1. Linear table
1. Definition
A finite sequence of zero or multiple data elements. First, there is a sequence between elements. If there are multiple elements, the first element has no precursor, And the last element has no successor,
All other intermediate elements have only one precursor and one successor. The sequence is the same as a team.
The linear table concept is defined by the logical structure of the data amount. In this data set, each element is arranged in sequence like a team.
2. Storage of linear tables:
Then we need to perform operations (add, delete, modify, and query) on such a dataset and store it in the computer memory. To store the dataset of a linear table in memory,
The fastest thing we can think of is to store them in sequence and store them in a continuous memory unit.
This is the sequential storage of linear tables. The most common example is the array we use. The elements in the linear table are stored in sequence using sequential storage units with a segment of addresses.
Structure Code for sequential storage of a C-code linear table # define maxsize 20 typedef int elemtype; typedef struct {elemtype data [maxsize]; int length;} sqlist;
Advantages and disadvantages of linear table sequential storage structure:
Advantages: you do not need to add additional storage space to represent the logical relationship between elements in the table;
You can quickly access any element in a table.
Disadvantage: a large number of elements need to be moved to the delete operation after insertion;
When the length of a linear table is unstable, it is difficult to determine the storage space, which may easily cause storage space fragmentation.
Therefore, we have introduced the storage method of linear tables, chain storage, or linked list storage. This is also the most common data structure used in job and study interviews.
Chain storage structure of linear tables
Implementation of the C code single-chain table: Structure pointer typedef struct node {elemtpye data; struct node * Next;} node; typedef struct node * linklist;
Chained storage means that the memory units of element storage can be discontinuous and scattered. How to maintain the relationship between elements (logical structure, the precursor and successor of each element .)
That is, a pointer field is used to store the direct relationship between the pointer and the frontend or the successor.
The above is a single-chain table pointer structure, that is, each element stores the memory space address of its successor element.
Application Scenario Selection
You can simply know the common operations of data, such as adding, deleting, modifying, and querying.
The sequential storage speed is very fast when data operations are biased towards query and modification, while the linked list must be traversed from the first node.
However, when an element is inserted or deleted, each operation of sequential storage must move the element to maintain the sequence. For a linked list, after a new storage unit is added, just change the pointer pointing to several pointer domains.
Tips1. However, we should note that the insert and delete operations of a table are actually composed of two parts: traverse to find the I-th element and then delete or insert it. In fact, the time complexity of this step is O (1 ),
The time complexity of a single-chain table is O (n). Although the sequence table needs to move the elements after I one by one, the call time is O (n ), A single-chain table does not need to move elements,
The time is O (1 ). In fact, it looks like even. In this way, a single-chain table has no major advantage over the sequential storage structure of a linear table. However, if k elements are inserted at point I,
Then, as long as the first traversal finds the I-th element, the subsequent operation is basically O (1), and the sequential operation, is to move n-I elements every time, O (N ),
The effect is displayed.
Tips2. for sequence tables, if we do not consider the dead corner of the storage space allocation during the insert or delete operations (that is, the storage space can be dynamically applied without Overflow ),
All operations are performed on the last element. That is, deletion and insertion are performed at the end of the table, so there is no need to move a large number of elements. This is also a prototype of the stack concept.
There are not many discussions about sequence tables.
However, the Linked List data structure involves many problems.
The linked list can also be derived based on the operation needs. The circular linked list (that is, the end element of the table points to the header node ), two-way linked list (elements can be quickly located to find their precursor and successor ).
Common Operations on linked lists
1. Reverse of a single-chain table.
// CCode: single-chain table implementation: Structure pointer typedef struct node {elemtpye data; struct node * Next;} node; typedef struct node * linklist; linklist * reveral (linklist * ls) {// The linked list is empty or if (LS = NULL | LS-> next = NULL) {return ls;} node * Pre * cur, * NEX; pre = ls; cur = head-> next; while (cur! = NULL) // If the node exists, continue to traverse {NEX = cur-> next; // Save the subsequent node cur-> next = pre; // flip pre = cur; cur = NEX;} LS-> next = NULL; LS = pre; return ls ;}
(Amount, the code format may be very nonstandard. I am not writing C and C ++ code .)
Code Analysis: for example, if three nodes are a-> B-> C, convert them to C-> B->;
Then I will traverse each point and reverse it, for example, above, pre = A, cur = B;
In traversal, it is used to determine whether the current node to be reversed exists. If yes, it is necessary to operate B-> A, after B-> C, information I need to use a secondary pointer to point to C;
When cur = NULL, The traversal is complete. At this moment, we can simulate the form of the linked list as follows:
A-> <-B <-C;
You need to change a, that is, the original header node, To the end node and point to NULL;
Therefore, LS-> next = NULL is executed;
Then point the head pointer of the linked list to the PRE node, because in the while loop, we can see that cur = NULL, that is, pre-> next = NULL, that is, it traverses the End Node of the linked list.
2. Search for intermediate elements in the linked list
This is quite fun.
First of all, in terms of thinking, to find the intermediate elements, we can first know the length of the entire linked list. Record the length of the linked list first.
Then, traverse the n/2 nodes from the beginning.
Then we can see that a clever and interesting method is
Define two pointers: * a, * B, and a point to the next element in each loop, and B points to the next element each time. That is, the traversal speed is twice that of, when B traverses the end element,
A actually points to the intermediate elements of the linked list.
In simple terms, this method only needs to traverse n nodes.
Node * findmid (linklis * ls) {// empty linked list or single-node table if (LS = NULL | LS-> next = NULL) {return ls ;} node * I, * j; I = J = ls; // start from the Start Node while (I-> next! = NULL & J-> next! = NULL) // determine whether to traverse to the End Node {I = I-> next; j = J-> next;} return I ;}
3. Determine whether a linked list has a ring.
If a linked list has a ring, an endless loop will occur if it is traversed.
Then how can we determine whether a linked list has loops.
The most common idea is to mark him like walking through the maze. You can simply put the vertices that have been traversed in a set (set can ensure uniqueness. Sorry, my pure Java idea ).
If you cannot use the Helper element, you can also use the above search idea, that is, if there is a ring, there will always be a moment when the two pointers that have been traversing at different steps in it.
Node * I, * j; I = J = ls; // start from the Start Node while (I-> next! = NULL & J-> next! = NULL) // determine whether to traverse to the End Node {I = I-> next; j = J-> next; if (I = J) {return true }}
Then, you can find the entry point of the ring during expansion.
For this question, because we traditionally set the length of each step for two pointers to 1 or 2.
For problems encountered in the ring, I first thought about whether to skip it. That is, when a catches up with B, A is before B after the next event.
This is exactly why the step size is set to 1, 2, so that the above scenario will not appear.
In the ring, when a and B meet, it must be a to catch up with B, and the first encounter, B must not finish a ring. A may have n loops.
Reference Baidu Library.
4. Determine whether two linked lists are intersecting.
The simplest idea is to check whether each node in the linked list is in the B linked list. In this way, each time a node in Table A is obtained, and then the elements of the list B are scanned,
The complexity of the entire algorithm is the length of a x B.
The second idea is to use the count method to scan the linked list and store it in a hashmap as a key-value pair, and then query the number of nodes in the B linked list.
The algorithm complexity at this moment is a Length + B length. However, the application for auxiliary space is required.
In the third method, if two linked lists overlap, we can see that the nodes after the intersection nodes are the same, that is, we can directly compare the tail nodes of the two linked lists,
If they are the same, they are the same. The algorithm complexity at the moment is the length and size of two linked lists, but no extra space is required.
Bool iscrossing (linklist * a, linklist * B) {node * atail, btail; // You can first determine whether linked list A and B are nullatail = A; btail = B; while (a-> next! = NULL) {A = A-> next;} while (B-> next! = NULL) {B = B-> next;} if (a = B) {return true;} return false ;}
Compared with the sequence table, the linked list has a high playability, which is also a common problem for everyone at work or in interviews.
Over