In the previous article, I learned "sequence table (seqlist)". In this article, I will refer to "single-chain table (linklist )". At the end of the previous article, we pointed out that the sequence table requires a set of continuous memory space, and to ensure the sequence of elements, when inserting/deleting elements, the following elements must be moved. If you need to insert or delete elements frequently in your application, the overhead will be high.
The linked list structure is the opposite. First, let's look at the following structure:
Each element has at least two attributes: Data and next. Data is used to store data, and next is used to identify the elements behind it (a "Pointer ).
Elements in the linked list, also known as node nodes. below is the generic version of node. CS
Namespace linear table {public class node <t> {private t data; private node <t> next; Public node (T Val, node <t> P) {DATA = val; next = P;} public node (node <t> P) {next = P;} public node (T Val) {DATA = val; next = NULL;} public node () {DATA = default (t); next = NULL;} public t data {get {return data;} set {DATA = value ;}} public node <t> next {get {return next;} set {next = value ;}}}}
The storage of linked lists does not require that all elements be stored in sequence, because the next node can be found using the next node, which is like a chain consisting of beads ", to find one of the beads
A node (usually known as a head node) starts to locate the next node based on next until it finds the desired node.
The linked list must have a head node as the Start Node, which is different from the sequence table. below is the implementation of a single-chain table:
Using system; using system. text; namespace linear table {public class linklist <t>: ilistds <t> {private node <t> head; Public node <t> head {get {return head ;} set {head = value ;}} public linklist () {head = NULL ;} /// <summary> /// class indexer /// </Summary> /// <Param name = "Index"> </param> /// <returns> </returns> Public t this [int Index] {get {return this. getitemat (INDEX) ;}/// <summary> /// returns the length of a single-chain table // /</Summary> // <returns> </returns> Public int count () {node <t> P = head; int Len = 0; while (P! = NULL) {Len ++; P = P. next;} return Len;} // <summary> // clear // </Summary> Public void clear () {head = NULL ;} /// <summary> /// whether it is null /// </Summary> /// <returns> </returns> Public bool isempty () {return head = NULL ;} /// <summary> /// add an element to the end /// </Summary> /// <Param name = "item"> </param> Public void append (T item) {node <t> d = new node <t> (item); node <t> N = new node <t> (); If (Head = nu Ll) {head = D; return;} n = head; while (N. Next! = NULL) {n = n. next;} n. next = D;} // The Public void insertbefore (T item, int I) {If (isempty () | I <0) {console. writeline ("list is empty or position is error! "); Return;} // insert if (I = 0) {node <t> q = new node <t> (item); q. next = head; // change "Header" to the second element head = Q; // set itself to "Header" return;} node <t> N = head; node <t> d = new node <t> (); Int J = 0; // locate the previous Element D while (N. next! = NULL & J <I) {d = N; n = n. next; j ++;} If (N. next = NULL) // It indicates to insert (append) {node <t> q = new node <t> (item); N. next = Q; q. next = NULL;} else {If (j = I) {node <t> q = new node <t> (item); D. next = Q; q. next = n ;}}} /// <summary> /// Insert the element item after position I /// </Summary> /// <Param name = "item"> </param> // /<Param name = "I"> </param> Public void insertafter (T item, int I) {If (isempty () | I <0 ){ Console. writeline ("list is empty or position is error! "); Return;} if (I = 0) {node <t> q = new node <t> (item); q. next = head. next; head. next = Q; return;} node <t> P = head; Int J = 0; while (P! = NULL & J <I) {P = P. next; j ++;} If (j = I) {node <t> q = new node <t> (item); q. next = P. next; p. next = Q;} else {console. writeline ("position is error! ");}} /// <Summary> /// Delete the element of location I /// </Summary> /// <Param name = "I"> </param> // <returns> </returns> Public t removeat (int I) {If (isempty () | I <0) {console. writeline ("link is empty or position is error! "); Return default (t);} node <t> q = new node <t> (); if (I = 0) {q = head; head = head. next; return Q. data;} node <t> P = head; Int J = 0; while (P. next! = NULL & J <I) {J ++; q = P; P = P. next;} If (j = I) {q. next = P. next; return p. data;} else {console. writeline ("the node is not exist! "); Return default (t );}} /// <summary> /// obtain the element at the specified position /// </Summary> /// <Param name = "I"> </param> // <returns> </returns> Public t getitemat (int I) {If (isempty () {console. writeline ("list is empty! "); Return default (t);} node <t> P = new node <t> (); P = head; if (I = 0) {return p. data;} Int J = 0; while (P. next! = NULL & J <I) {J ++; P = P. next;} If (j = I) {return p. data;} else {console. writeline ("the node is not exist! "); Return default (t) ;}// search for the index public int indexof (T value) {If (isempty () {console. writeline ("list is empty! "); Return-1 ;}node <t> P = new node <t> (); P = head; int I = 0; while (! P. Data. Equals (value) & P. Next! = NULL) {P = P. next; I ++;} return I;} // <summary> // element inversion // </Summary> Public void reverse () {linklist <t> result = new linklist <t> (); node <t> T = This. head; result. head = new node <t> (T. data); t = T. next; // (traverse the elements of the current link from the head and insert them to another empty linked list one by one. In this way, the element sequence of the new linked list is opposite to that of the original linked list) while (T! = NULL) {result. insertbefore (T. data, 0); t = T. next;} This. head = result. head; // directly mount the original linked list to the inverted linked list; Result = NULL; // explicitly clear the reference of the original linked list, so that GC can be recycled directly} public override string tostring () {stringbuilder sb = new stringbuilder (); node <t> N = This. head; sb. append (N. data. tostring () + ","); While (N. next! = NULL) {sb. append (N. next. data. tostring () + ","); n = n. next;} return sb. tostring (). trimend (',');}}}
The following is an example of an algorithm used to insert or delete a single-chain table:
You can see that when an element is inserted or deleted in a linked list, you do not need to move the subsequent elements. You only need to modify the next point of the elements and adjacent nodes, therefore, the overhead of inserting/deleting elements is much lower than that of sequential tables. However, you should also note that other operations, such as searching for elements and reversing inverted linked lists, may need to traverse all elements in the entire linked list.
Test code snippet:
Console. writeline ("-----------------------------------"); console. writeline ("single-chain table test starts... "); linklist <string> link = new linklist <string> (); Link. head = new node <string> ("X"); Link. insertbefore ("W", 0); Link. insertbefore ("V", 0); Link. append ("Y"); Link. insertbefore ("Z", Link. count (); console. writeline (link. count (); // 5 console. writeline (link. tostring (); // V, W, X, Y, Z console. writeline (link [1]); // W C Onsole. writeline (link [0]); // v console. writeline (link [4]); // Z console. writeline (link. indexof ("Z"); // 4 console. writeline (link. removeat (2); // X console. writeline (link. tostring (); // V, W, Y, Z link. insertbefore ("X", 2); console. writeline (link. tostring (); // V, W, X, Y, Z console. writeline (link. getitemat (2); // X link. reverse (); console. writeline (link. tostring (); // Z, Y, X, W, V link. insertafter ("1", 0); Link. INSE Rtafter ("2", 1); Link. insertafter ("6", 5); Link. insertafter ("8", 7); Link. insertafter ("A", 10); // position is error! Console. writeline (link. tostring (); // Z, 1, 2, Y, X, W, 6, V, 8
The sequence table or linked list should be selected in actual applications. The main reason is: the frequency of element insertion/deletion and the degree of tolerance for memory allocation. If you do not need to allocate a group of consecutive memory areas at the beginning, you can automatically increase the memory usage based on the increase in elements, or insert/delete a large number of times, we recommend that you use a linked list, otherwise, use the sequence table.
Note: You can add another Prev element to the node to indicate who the previous node is, that is, there are two directions at the same time: Next and Prev, this improved linked list is called a "two-way linked list", which helps reduce the number of traversal cycles in some cases. In this article, only one linked list pointing to next is called a "single-chain table ".