The data structure of the Go language: Linked list

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed. Data structures and algorithms are an important part of computer science. Although sometimes they look scary, most algorithms have a simple explanation. In the same way, the learning and application of the algorithm can be interesting when the problem can be explained clearly by the algorithm. The target audience for this article is those who are uncomfortable with the list, or those who want to see and learn how to build a linked list with Golang. We will see how to implement them through a (slightly) practical example, rather than a simple theory and code example. Before this, let's talk about some theories. # # Chain table is one of the more simple data structures. Wikipedia's article on linked lists points out that:> in computer science, a linked list is a linear set of data elements, where the linear order is not given by their physical location in memory. Instead, each element points to the next element. It is a data structure consisting of a set of nodes that collectively represent a sequence. In its simplest form, each node consists of data and a reference to the next node (in other words, a link). Although these may seem too or confusing, let's break it down. Linear data structure, is a kind of its elements to form a sequence of the information structures. It's so simple. Why is the physical location in memory not important? When you have an array, the amount of memory in the array is fixed, that is, if you have an array of 5 items, the language will only fetch 5 memory addresses in memory, one after the other. Because these addresses create a sequence, the array knows what memory its value will be stored in, so the physical location of the values creates a sequence. With the linked list, it's a little different. In the definition, you will notice that "each element points to the next", using "data and references (in other words, links) to the next node." This means that each node of the linked list stores two things: a value and a reference to the next node in the list. It's so simple. # # Data Flow all that human beings perceive is some kind of information or data, and our senses and minds know how to handle it and turn it into useful information. Whether we are looking, smelling, or touching, we are dealing with the data and finding meaning from the data. When we browse our social media networks, we always turn to data, in chronological order, with endless information. So how do we use a linked list to model this kind of news flow? Let's take a quick look at a simple Tweet, for example:! [] (Https://raw.githubusercontent.com/studygolang/gctt-images/master/data-structures/tweet_jack.png) example of the purpose of our social network , we get inspiration from Twitter and create a ' Post' Type, it has a ' body ', a ' publishdate ' and a ' next ' post link: ' Gotype post struct {body stringpublishdate Int64//Unix Timestampne XT *post//link to the next post} ' next, how do we model a feed for a post? If we know that the data flow is made up of a post that is attached to another, then we can try to create one of these types: "' Gotype feed struct {length int///We'll use it laterstart *post} ' feed ' structure There will be a start (or ' start ') that points to the first ' Post ' and a ' length ' property in the feed, which will store the size of the ' Feed ' at any time. So, suppose we want to create a ' feed ' with two posts, the first step is to create a ' Append ' function on the ' feed ' type: ' Gofunc (f *feed) Append (Newpost *post) {if f.length = = 0 {f. start = Newpost} else {currentpost: = f.startfor Currentpost.next! = Nil {currentpost = Currentpost.next}currentpost.next = newpost}f.length++} "' Then we can call it two times: ' ' gofunc main () {f: = &feed{}p1: = post{body:" Lorem ipsum ",}f.append (&AMP;P1) Fmt. Printf ("Length:%v\n", F.length) fmt. Printf ("First:%v\n", F.start) P2: = post{body: "dolor sit amet",}f.append (&AMP;P2) fmt. Printf ("Length:%v\n", F.length) fmt. Printf ("First:%v\n", F.start) fmt. Printf ("Second:%v\n", F.start.next)} "What is this code for?" First, the ' main ' function- Create a pointer to the ' feed ' structure, and the two ' Post ' structure contains some fictional content, which calls the ' Append ' function on the ' feed ' twice, making it a length of 2. We check the ' feed ' two values, it accesses ' feed ' (actually ' post ') ' Start ' and ' start ' after the ' next ' item, which is the second ' post '. When we run the program, the output will be: "' Length:1first: &{lorem ipsum 1257894000 <nil>}length:2first: &{lorem ipsum 1257894000 0x10444280}second: &{dolor sit amet 1257894000 <nil>} ' It can be seen that when we add the first ' post ' to the ' Feed ', its length is ' 1 ' and the first ' post ' is There is a ' body ' and a ' publishdate ' (as a Unix timestamp), at the same time, its ' next ' value is ' nil '. Then we add the second ' post ' to the ' Feed ' and when we look at two ' Posts ' we see that the first ' post ' is the same as the previous content, but its pointer points to the next ' post ' in the list. The second ' post ' also has a ' body ' and a ' publishdate ', but no pointer to the next ' post ' in the list. In addition, when we add more ' Posts ', the length of ' Feed ' will also increase. Now let's look back at the ' Append ' function and deconstruct it so that we can better understand how to use the linked list. First, the function creates a pointer to the ' post ' value, takes the ' body ' argument as the ' body ' of ' post ' and sets ' publishdate ' to the Unix timestamp representation of the current time. Then we check if ' length ' of ' Feed ' is ' 0 '-which means it doesn't have ' Post '. The first added ' post ' will be set to start ' post ', for the sake of convenience, we name it ' start '. However, if the ' Feed ' is longer than 0, then our algorithm will change differently. It will start with ' start ' of ' Feed ' and it will traverse all the ' PosT ' until you find a pointer that does not point to ' next '. It will then append the new ' post ' to the last ' post ' of the list. # # optimization ' Append ' Imagine that we have a user brush ' Feed ', just like any other social network. Since the articles are arranged chronologically, based on ' Publishdate ', the ' feed ' will become more and more as the user slides, and more ' Post ' will be appended to the ' feed '. With this approach in mind, we use the ' Append ' function because ' Feed ' is getting longer and the ' Append ' function will pay more and more dearly. Why? Because we have to traverse the whole ' Feed ' and add a ' Post ' at the end. If you've ever heard of the ' Big-o ' notation, the algorithm has an ' O (n) ' time complexity, which means it always traverses the entire ' Feed ' before adding ' Post '. As you can imagine, this can be very inefficient, especially if the "Feed" grows quite long. How to improve the "append" function to reduce its [asymptotic complexity] (https://en.wikipedia.org/wiki/Asymptotic_computational_complexity)? Because our ' Feed ' data structure is just a ' Post ' list, to traverse it, we must know the beginning of the list (called ' Start '), which is the ' Post ' type pointer. Because in our example ' Append ' always adds a ' Post ' to the end of ' feed ', if ' feed ' not only knows its starting ' start ' element, but also knows its ' end ' ending element, then we can greatly improve the performance of the algorithm. Of course, there is always a tradeoff for optimization, and the tradeoff here is that the data structure consumes more memory (new attributes for the ' Feed ' structure). Extending our ' feed ' data structure is easy: ' Gotype feed struct {length intstart *postend *post} ' but our ' Append ' algorithm must be tuned to fit the new structure of ' feed '. This is the ' Append ' version of the ' End ' property using ' Post ': ' Gofunc (f *feed) Append (Newpost *post) {if f.length = = 0 {F.start = Newpostf.end = Newpost} else {LastpoST: = F.endlastpost.next = Newpostf.end = newpost}f.length++} "This looks a little simpler, right? Let me give you some good news: 1. The code is now simpler and shorter. 2. We have greatly improved the time complexity of the function. We're looking at the algorithm, and it does two things: if ' feed ' is empty, it will set a new ' post ' as the start and end of the ' feed ', whereas it will set a new ' post ' as ' end ' and attach to the previous ' post ' in the list. It is important that it is simple and that the algorithm complexity is ' O (1) ', also known as constant time complexity. This means that the ' Append ' will perform the same action regardless of the length of the ' Feed ' structure. It's simple, right? But let's imagine that ' Feed ' is actually a ' Post ' list in our configuration file. Therefore, they are ours and we should be able to delete them. I mean, what kind of social network doesn't allow users (at least) to delete their posts? # # Remove a ' post ' as we created in the previous section, we want our ' Feed ' users to be able to delete their posts. So how do we build the model? If our ' Feed ' is an array, we'll delete the entry and process it, right? This is actually the place where the list shines. When the array size changes, the runtime captures a new block of memory to store the array's items. Because of its design of the linked list, each entry has a pointer to the next node in the list and can be scattered across the entire memory space, from a spatial point of view, adding or removing nodes from the list is a low overhead. When a person wants to remove a node from a linked list, only the neighbor node of the deleted node needs to be connected. The garbage collection language (such as the Go language) makes this easier because we don't have to worry about releasing the allocated memory--GC will start and delete all unused objects. To make it easy for us to operate, let's set a limit on the ' Post ' on each ' Feed ' and it will have a unique ' publishdate '. This means that publishers can create a ' Post ' on their ' Feed ' every second. By putting it into practice, we can easily remove ' Post ' from ' Feed ': ' Gofunc (f *feed) remove (publishdate Int64) {if f.length = = 0 {Panic (errors. New ("Feed is Empty"))}var Previouspost *POSTCURrentpost: = f.startfor currentpost.publishdate! = publishdate {if Currentpost.next = nil {panic (errors. New ("No such Post found.")} Previouspost = Currentpostcurrentpost = Currentpost.next}previouspost.next = currentpost.nextf.length--} "Remove" The function will take ' post ' as a ' publishdate ' as a parameter, it will detect which ' post ' needs to be deleted (or not linked). This function is very small. If it detects that the ' start ' item of ' feed ' will be deleted, it will reassign ' start ' of ' feed ' and add a second ' Post ' to ' feed '. Otherwise, it jumps to every ' post ' in ' Feed ' until it encounters a ' post ', and the ' post ' has a matching ' publishdate ' as a function parameter. When it finds one, it connects the previous and the next ' Post ', effectively removing the middle (match) from the ' Feed '. There is a boundary condition where we need to make sure we overwrite the ' Remove ' function--if the ' Feed ' does not have a ' Post ' with the specified ' Publishdate '? For simplicity, the function checks the next ' Post ' in the ' Feed ' and then jumps to it. If the next is a nil function, it tells us that it can't find a ' Post ' of ' publishdate '. # # Insert a ' Post ' now that we have a way to add and remove, let's take a look at some hypothetical scenarios. It is assumed that the source of generating ' Post ' is not sending them to our application in chronological order. This means that you need to put ' Post ' to the right place in ' Feed ' based on ' publishdate '. For example: "' Gofunc (f *feed) Insert (Newpost *post) {if f.length = = 0 {F.start = newpost} else {var previouspost *postcurrentpo ST: = f.startfor currentpost.publisHdate < newpost.publishdate {Previouspost = Currentpostcurrentpost = Previouspost.next}previouspost.next = Newpostnewpost.next = currentpost}f.length++} "In essence, this is an algorithm very similar to the ' Remove ' function, because although they all do a very different thing (add V.s in ' Feed '). Delete ' Post '), which are based on the search algorithm. This means that these two functions actually traverse the entire ' Feed ', search for ' post ' matching ' publishdate ', and receive a ' post ' in the parameters of the function. The only difference is that ' Insert ' will actually place a new ' post ' in the date match, and ' remove ' will remove ' post ' from ' Feed '. In addition, this means that both functions have the same time complexity, that is, O (n). This means that in the worst case, the function must traverse the entire ' Feed ' to reach the entry that needs to be inserted into the new ' Post ' (or delete). # # What if we use arrays? If you ask yourself, let me say first, you have a point of view. Indeed, we can store all the ' Post ' in an array (or a slice of the Go language), easily push the entry onto it, and even use the O (1) complexity random access. Given the nature of the array, its value must be stored in memory, so the read speed is very fast and the overhead is low. Once you have something stored in an array, you can use its 0-based index to get it. When inserting an entry, the efficiency of the array is not as good as the linked list, either in the middle or at the end. This is because if the array does not retain more memory for the new item, it will have to keep it and use it. However, if the next memory address is not idle, it will have to "move" to a new memory address, and only then have the space to accommodate all of its items (new and old). Looking at all the examples and discussions we have so far, we can create a linked list of time complexity for each of the algorithms we create, and compare them with the same algorithm for the array: | Action | Array | Linked List | | ------- |:-----:| -----------:|| Access | O (1) | O (n) | | Search | O (n) | O (n) | | prepend | O (1) | O (1) | | Append | O (n) | O (1) | | Delete | O (n) | O (n) | As you can see, when faced with a particular problem, choosing the right data structure will really accomplish or ruin the product you created. For an ever-growing ' Feed ', inserting ' Post ' is the most important and the list will do better because the insertion is much less expensive. However, if we have a different problem on our hands that requires frequent deletion or a lot of search/searching, then we have to choose the right data structure for the problem we are dealing with. You can see the entire implementation of ' Feed ' and experience it in [here] (https://play.golang.org/p/fqLPjf_ekD6). In addition, the Go language has its own linked list implementation, it has built up some good features. You can see its documentation in [here] (https://golang.org/pkg/container/list/).

Via:https://ieftimov.com/golang-datastructures-linked-lists

Author: Ilija Eftimov Translator: Sergeychang proofreading: Rxcai

This article by GCTT original compilation, go language Chinese network honor launches

This article was originally translated by GCTT and the Go Language Chinese network. Also want to join the ranks of translators, for open source to do some of their own contribution? Welcome to join Gctt!
Translation work and translations are published only for the purpose of learning and communication, translation work in accordance with the provisions of the CC-BY-NC-SA agreement, if our work has violated your interests, please contact us promptly.
Welcome to the CC-BY-NC-SA agreement, please mark and keep the original/translation link and author/translator information in the text.
The article only represents the author's knowledge and views, if there are different points of view, please line up downstairs to spit groove

697 Reads
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.