Basic concept and implementation of linear table
Definition of linear table
A linear table is a finite sequence of data elements with the same characteristics. The number of elements contained in the sequence is called the length of the linear table, expressed in n. Note: n can be equal to 0, indicating that the linear table is an empty table.
Linear table is a simple data structure, which can be imagined as a team of students. The number of students corresponds to the length of the linear table. The number of students is limited, which shows that the linear table is a sequence; Everyone in the team is a student, which reflects that the elements in the linear table are the same characteristics; The linear table can be ordered or disordered. If students line up according to their height, it reflects the order of the linear table.
Logical characteristics of linear table
In a team of students, only one student is at the head of the team, and also only one student is at the end of the team. There are no other students in front of the students at the head of the team, and there are no other students behind the students at the end of the team. In addition to the students at the head and end of the team, other students have a precursor and a successor at the same time. The same is true for linear tables. There is only one header element and one footer element. The header element has no precursor and the footer element has no successor. Other elements have a precursor and a successor.
Storage structure of linear table
The storage structure of linear list includes sequential storage structure and chain storage structure. The former is called linear list and the latter is called chain list. The following two storage structures are compared
- Sequential table: all elements in the linear table are stored in a continuous storage space starting from the specified storage location at one time according to their logical order. In this way, the storage location of the first element in the linear table is the specified storage location, and the i+1 element is immediately after the storage location of the i element.
- Linked list: each node contains not only the information of the stored elements, but also the information of the logical relationship between the elements. For example, the predecessor node in the single linked list contains the address information of the successor node, so that the location of the successor node can be found through the address information in the predecessor node.
Comparison of two storage structures
- The sequence table is like a row of rooms. The house number of each room is the distance from the room to 0 o'clock, and the room length is 1. Therefore, as long as you know the location of 0 o'clock, you can find any room immediately through the room number. This is the first feature of sequential table - random access feature; The rooms are close to each other, that is, a piece of space is occupied continuously, and the amount of space occupied by the room is determined, and will not be reduced or increased. This is the second characteristic of the sequence table, that is, the sequence table requires to occupy continuous storage space, and the storage space can only be allocated in advance. Once allocated, it will remain unchanged during operation. During the insertion operation, the sequence table needs to move multiple elements. (because the space is fixed, the elements behind it must be moved back in turn when inserting)
- In the linked list, the element nodes are scattered in the storage space, and each node has the address of the next node. According to this address, each node in the linked list can be found in turn, but the next node can be found only after knowing the content of the previous node. Therefore, the linked list does not support random access. Each node in the linked list needs to divide a part of space to store the pointer of the next node, so the storage space utilization of the linked list is slightly lower than that of the sequential list. The position of the current node in the linked list is indicated by the address information in its predecessor node, not by its offset from the initial position. Therefore, the nodes of the linked list can be scattered anywhere in the memory, and there is no need to divide the space required by all nodes to the linked list at one time. Instead, several nodes need to be temporarily divided. Thus, the memory of the linked list supports dynamic allocation. There is no need to move elements when inserting.
There are five forms of linked lists
- Single linked list: each node contains not only the data domain, but also the pointer domain to point to the subsequent nodes. Here, we should also distinguish between the single linked list of the leading node and the single linked list of the non leading node.
- In the single linked list of the leading node, the head pointer points to the head node, and the value field of the head node does not contain any information. The data information is stored from the successor node of the starting node. When the head pointer is always not equal to NULL and head - > next is equal to NULL, the linked list is empty.
- The header pointer head in the single linked list without the leading node directly points to the starting node. When the head is NULL, the linked list is empty.
- Double linked list: a single linked list can only go from the start node to the terminal node, but not from the terminal node to the start node in reverse. If it is required to output the data sequence from the terminal node to the start node, it is very troublesome for the single linked list. In order to solve such problems, the construction of double linked list is to add a pointer field on the basis of single linked list to point to the precursor of the current node, so that it is convenient to find the precursor from the successor. Similarly, the double linked list is also divided into the double linked list with the leading node and the double linked list without the leading node. The situation is similar to the single linked list. The double linked list of the leading node. When head - > next is NULL, the linked list is empty; Double linked list without leading node. When head is NULL, the linked list is empty.
- Circular single linked list: a circular single linked list can be formed as long as the last pointer field of the single linked list points to the first node of the linked list. Circular single linked list can access any node in the linked list from any node. The circular single linked list of the leading node. When head is equal to head - > next, the linked list is empty; Circular single linked list without leading node. When head is equal to NULL, the linked list is empty.
- Circular double linked list: point the next pointer of the terminal node to the first node in the linked list, and point the prior pointer of the first node in the linked list to the terminal node. Circular double linked list can also be divided into nodes with and without leading nodes. When head is equal to NULL, the circular double linked list without leading nodes is empty. The circular linked list of the leading node has no NULL pointer. In its NULL state, head - > next and head - > prior must be equal to head. Therefore, to judge whether it is NULL, just check whether either of the two pointers of head - > next or head - > prior is equal to head.
- Static linked list: described by a two-dimensional array. The subscript of the array represents the address, one dimension represents the data, and the other dimension represents the pointer, pointing to the subscript of the previous element. If it is a terminal node, the pointer dimension is expressed as - 1.
common problem
- Space based comparison
- Storage allocation method: the storage space of the sequential list is allocated at one time, and the storage space of the linked list is allocated multiple times.
- Storage density (storage density = storage capacity occupied by node value field / total storage capacity occupied by node): the storage density of sequential list is 1, and the storage density of linked list is less than 1 (because there is pointer field).
- Time based comparison
- Access mode: the sequential table can be accessed randomly or sequentially. Linked lists can only be accessed sequentially.
- Number of elements moved during insertion / deletion: the sequence table needs to move nearly half of the elements on average; The linked list does not need to move elements, but only needs to modify the pointer.
Structure definition and basic operation of linear table
Structure definition of linear table
#define maxSize 100 / / define an integer constant maxSize with a value of 100
- Structure definition of sequence table
typedef struct Sqlist{ int data[maxSize]; int length; }Sqlist;
- Single linked list node definition
//Set the single linked list of the leading node typedef struct LNode{ int data; //Data field stored in data struct LNode *next; //Pointer to the successor node }LNode;//Define single linked list node type
- Double linked list node definition
typedef struct DLNode{ int data; struct DLNode *prior; //Pointer to the precursor node struct DLNode *next; //Pointer to the successor node }DLNode;
A node is a piece of storage space allocated by the user in memory. There is only one address to indicate its existence, and there is no explicit name. Therefore, when allocating the linked list node space, a pointer will be defined to store the address of this space.
For example: LNode *A = (LNode*)malloc(sizeof(LNode));, The user allocates an LNode node, that is, constructs an LNode node. Note that here A names two things: one is the node and the other is the pointer to the node.
Operation of sequence table
Operation definition
//Initialization sequence table Sqlist* initList(); //Insert data element after array int insertElem(Sqlist *L,int e); //Insert the e element at position p int insertPosElem(Sqlist *sqlist,int e,int p); //Find subscripts by element int findElem(Sqlist sqlist,int e); //Delete elements with subscript p int deleteElem(Sqlist *sqlist,int p,int *e); //Find the specified location element int getElem(Sqlist sqlist,int p,int *e); //Returns the position of the first element larger than x int findFirstElem(Sqlist sqlist,int x);
Operation implementation
Sqlist* initList(){ Sqlist *sqlist = (Sqlist*) malloc(sizeof(Sqlist)); //Creating this structure creates an array. sqlist->length = 0; return sqlist; } int insertElem(Sqlist *L,int e){ if(L->length == maxSize){ return 0; } L->data[L->length] = e; L->length++; return 1; } int insertPosElem(Sqlist *sqlist,int e,int p){ int i; if(p < 0 || p > sqlist->length || sqlist->length == maxSize){ return 0; } for(i = sqlist->length-1 ; i>=p ; --i){ sqlist->data[i+1] = sqlist->data[i]; } sqlist->data[p] = e; ++sqlist->length; return 1; } int findElem(Sqlist sqlist,int e){ for (int i = 0; i < sqlist.length; ++i) { if(e == sqlist.data[i]){ return i; } return -1; } } int deleteElem(Sqlist *sqlist,int p,int *e){ if(p<0 || p>sqlist->length-1){ return 0; } *e = sqlist->data[p]; for (int i = p; i < sqlist->length - 1; ++i) { sqlist->data[i] = sqlist->data[i+1]; } --(sqlist->length); return 1; } int getElem(Sqlist sqlist,int p,int *e){ if(p<0 || p>sqlist.length-1){ return 0; } *e = sqlist.data[p]; return 1; } int findFirstElem(Sqlist sqlist,int x){ int i; for (i = 0; i < sqlist.length; ++i) { if(x < sqlist.data[i]){ return i; } } return -1; }
Operation of single linked list
Operation definition
//Establishing single linked list by tail interpolation LNode* createListR(int a[],int n); //Establishing single linked list by head interpolation LNode* createListF(int a[],int n); //Traversal print linked list void printList(LNode* list); //Insert elements by tail interpolation void insertListR(LNode* list,int data); //Insert element by header void insertListF(LNode* list,int data); //Merge two incremental single linked lists into one incremental single linked list LNode* mergeUp(LNode *listA,LNode *listB); //Merge two increasing single linked lists into a decreasing single linked list LNode* mergeDe(LNode* listA,LNode* listB); //Find and delete the node by value int findAndDelete(LNode* list,int x);
Operation implementation
LNode* createListR(int a[],int n){ LNode* list = (LNode*) malloc(sizeof(LNode)); list->next = nullptr; LNode* tmpList = list; for (int i = 0; i < n; ++i) { LNode* newNode = (LNode*) malloc(sizeof(LNode)); newNode->next = nullptr; newNode->data = a[i]; tmpList->next = newNode; tmpList = tmpList->next; } return list; } LNode* createListF(int a[],int n){ LNode* list = (LNode*) malloc(sizeof(LNode)); list->next = nullptr; LNode* tmpList = list; for (int i = 0; i < n; ++i) { LNode* newNode = (LNode*) malloc(sizeof(LNode)); newNode->data = a[i]; newNode->next = nullptr; //Key steps of head interpolation newNode->next = tmpList->next; //The next of newNode points to the next of the list header node. tmpList->next = newNode; //The next of the list header node points to newNode. } return list; } void insertListR(LNode* list,int data){ LNode* tempList = list; LNode* newNode = (LNode*)malloc(sizeof(LNode)); newNode->data = data; newNode->next = nullptr; while(tempList!=nullptr){ tempList = tempList->next; } tempList->next = newNode; } LNode* mergeUp(LNode *listA,LNode *listB){ LNode *p = listA->next; LNode *q = listB->next; LNode *listC = (LNode*)malloc(sizeof(LNode)); LNode *tempListC = listC; while(q!=nullptr && p!=nullptr){ if(p->data <= q->data){ LNode* newNode = (LNode*)malloc(sizeof(LNode)); newNode->data = p->data; newNode->next = nullptr; tempListC->next = newNode; tempListC = tempListC->next; p = p->next; }else{ LNode* newNode = (LNode*)malloc(sizeof(LNode)); newNode->data = q->data; newNode->next = nullptr; tempListC->next = newNode; tempListC = tempListC->next; q = q->next; } } if(p!=nullptr){ //Because we need to connect p to the back of C, so we only need to connect p to the back. tempListC->next = p; } if(q!=nullptr){ tempListC->next = q; } return listC; } LNode* mergeDe(LNode* listA,LNode* listB){ LNode* p = listA->next; LNode* q = listB->next; LNode *listC = (LNode*) malloc(sizeof(LNode)); listC->next = nullptr; LNode *tempListC = listC; while(p!=nullptr && q!=nullptr){ if(p->data <= q->data){ insertListF(listC,p->data); p = p->next; }else{ insertListF(listC,q->data); q = q->next; } } while(p != nullptr){ insertListF(listC,p->data); p = p->next; } while(q != nullptr){ insertListF(listC,q->data); q = q->next; } return listC; } void insertListF(LNode* list,int data){ LNode* newNode = (LNode*) malloc(sizeof(LNode)); newNode->next = nullptr; newNode->data = data; LNode *tempList = list; newNode->next = tempList->next; tempList->next = newNode; } int findAndDelete(LNode* list,int x){ LNode *p,*q; p = list; while(p->next != nullptr){ if(p->next->data == x){ break; } p = p->next; } if(p->next == nullptr){ return 0; }else{ q = p -> next; p->next = p->next->next; free(q); return 1; } } void printList(LNode* list){ LNode* tempNode = list->next; while (tempNode != nullptr){ printf("%d ",tempNode->data); tempNode = tempNode->next; } }
Operation of double linked list
Operation definition
//Creating bidirectional linked list by tail interpolation DLNode* createDListR(int a[],int n); //Traversal print linked list void printDList(DLNode* head); //Insert elements by tail interpolation void insertListR(DLNode* head,int element); //Insert element by header void insertListL(DLNode* head,int element); //Delete element element void deleteList(DLNode* head,int element);
Operation implementation
DLNode* createDListR(int a[],int n){ DLNode* head = (DLNode*)malloc(sizeof(DLNode)); head->next = nullptr; head->prior = nullptr; DLNode* tempHead = head; for (int i = 0; i < n; ++i) { DLNode *newNode = (DLNode*)malloc(sizeof(DLNode)); newNode->data = a[i]; newNode->next = nullptr; tempHead->next = newNode; newNode->prior = tempHead; tempHead = tempHead->next; } return head; } void insertListR(DLNode* head,int element){ DLNode *temp = head->next; while(temp->next != nullptr){ temp = temp->next; } DLNode *newNode = (DLNode*)malloc(sizeof(DLNode)); newNode->data = element; newNode->next = nullptr; temp->next = newNode; newNode->prior = temp; } void insertListL(DLNode* head,int element){ DLNode *newNode = (DLNode*)malloc(sizeof(DLNode)); newNode->data = element; newNode->next = nullptr; newNode->prior = nullptr; DLNode *temp = head; DLNode *tempNext = head->next; newNode->next = tempNext; head->next = newNode; newNode->prior = head; tempNext->prior = newNode; } void deleteList(DLNode* head,int element){ DLNode *temp = head; while(temp != nullptr){ if(temp->data == element){ break; } temp = temp->next; } temp = temp->prior; temp->next = temp->next->next; temp->next->prior = temp; } void printDList(DLNode* head){ DLNode *temp = head->next; while (temp != nullptr){ printf("%d ",temp->data); temp = temp->next; } printf("\n"); }
Operation of circular linked list
Circular single linked list and circular double linked list are transformed from the corresponding single linked list and double linked list. You only need to establish a connection between the terminal node and the head node. The next of the terminal node of the circular single linked list points to the header node; The next of the terminal node of the circular double linked list points to the header node, and the prior pointer of the header node points to the footer node. It should be noted that if the P pointer goes down the circular linked list, the condition for P to go to the end of the list is p - > next = = head. The operation is the same as single linked list and double linked list.
Inverse problem
Given a linear table, how to invert the elements in it? Two integer variables i and J can be set. i points to the first element and j points to the last element. Exchange i and j elements while making i and j go opposite until they meet. The implementation is as follows, assuming that the elements are stored in a [], left and right are the subscripts at both ends of the array.
for(int i = left,j = right;i < j; i++,j--){ temp = a[i]; a[i] = a[j]; a[j] = temp; }
Inverse problem: given a linear table, inverse the elements in it.
- Move the front k elements of an array with a length of n in reverse order to the back of the array. It is required that the data in the original array is not lost, and the position of other elements is irrelevant.
- You only need to reverse the entire array, that is, after meeting the reverse order of the front-end k elements, put them into the back-end of the array.
- Move the front k elements of an array with a length of n to the back of the array in the original order. It is required that the data in the original array is not lost, and the other positions are irrelevant.
- Just reverse the first k elements, and then reverse the entire array.
- Move the elements of the array (x0,x1,x2,..., XN-1) into (xp,xp+1,..., xn-1.x0,x1,..., xp-1), that is, move p units to the left.
- Just reverse the elements from 0 to p-1, then reverse p~n-1, and then reverse the whole element.
//Move the front k elements of an array with a length of n in reverse order to the back of the array. It is required that the data in the original array is not lost, and the position of other elements is irrelevant. void reverse(int a[],int left,int right,int k){ int temp; for (int i = left,j = right; i < left+k && i < j; ++i,--j) { temp = a[i]; a[i] = a[j]; a[j] = temp; } } //Move the front k elements of an array with a length of n to the back of the array in the original order. It is required that the data in the original array is not lost, and the other positions are irrelevant. void moveToEnd(int a[],int n,int k){ reverse(a,0,k-1,k); reverse(a,0,n-1,k); } //Move the elements of the array (x0,x1,x2,...,xn-1) into (xp,xp+1,...,xn-1.x0,x1,...,xp-1), that is, move p units to the left void moveP(int a[],int n,int p){ reverse(a,0,p-1,p); reverse(a,p,n-1,n-p); reverse(a,0,n-1,n); } //Output array void printArray(int a[],int length){ for (int i = 0; i < length; ++i) { printf("%d ",a[i]); } printf("\n"); }