Today this blog to talk about several common search algorithms, of course, this blog is only involved in some of the search algorithm, the next few blog will be introduced about the search related content. This blog mainly describes the lookup table in order to find, binary find, interpolation lookup and Fibonacci find. This blog will give the corresponding search algorithm and related code, and give the corresponding test cases. Of course, this blog will still use the object-oriented language Swift to achieve the corresponding demo, and will be on GitHub to share the relevant demo.
Lookups are more common in life, and the kinds of lookups involved in this blog are based on a linear structure of lookups. In other words, our lookup table is a linear table, and we want to find the position of an element in a linear table. Sequential lookups are compared from one to the other until they are found, and this method works for unordered lookup tables. Binary lookups, interpolation lookups, and Fibonacci lookup tables are all ordered, and the contents below are described in detail. Go to the topic of today's blog.
First, the definition of the search protocol
Because this blog we are involved in finding a variety of ways to find tables, and the data structure of the lookup table is linear structure. Based on the features of Swift's object-oriented language and the principles of interface-oriented programming , we first define a protocol for all of our lookup methods. all the search methods in this blog will follow this lookup type , so it is convenient for external unified call and easy for us to test and extend.
The searchtype protocol Below is what we define as the lookup protocol. This protocol is relatively simple, but it is still more important, the agreement defines the way in which this blog is involved in the method of external invocation. The search () method in the agreement is the external method to invoke. The first parameter of the function is the lookup table to find , and the second parameter is the keyword to look for . The return value of the function is the position of the keyword in the lookup table. If not found, it will return 0.
Second, sequential search
The above also makes a simple mention, and the sequential lookup table is compared from start to finish, until we find the location of the element we are looking for. if it is not found, it returns 0. Of course, in this process of searching sequentially we can see the order of finding tables that are suitable for unordered searches. That is, when we use sequential lookups to find tables, we do not care about the order of lookup tables.
For a more intuitive understanding of order lookups, we can look at the below. The a~h element is stored in the lookup table, we want to find the position of the G element in the lookup table, we need to match it from a, and when G is found, it returns the position of G in the lookup table.
According to the above we are not difficult to give the code implementation, the code below this sequentialsearch This class is the order we create the assignment of the class lookup. of course, this class should follow SearchTypeand give the search () method implementation . The implementation in the search () method is simpler, which is a for loop that matches from beginning to end. When the match succeeds, it returns the position of the keyword in the linear table. The code is relatively simple here do not do too much to repeat.
For sequential lookups, we can optimize them. In the search implementation, I is taken from the range, so I have to judge whether I is in a particular range at a time. We don't have to make this judgment in our optimized code. the method of optimization is to append the keyword item we want to match to the tail of the lookup table, which we call the Sentinel, and if the result of the lookup is the position of the sentry, then thesearch () function returns zero if the lookup fails. Of course you can also put the sentry in the first position, from the back to the search, but if your lookup table is sequential storage, it is not recommended to insert Sentinel into the first position, because the sequential table insert operation is relatively time-consuming.
According to the above, we are not difficult to give the corresponding code implementation. The code snippet below is a sequential lookup method that sets the Sentinel. Because the code is relatively simple, here do not do too much to repeat.
Third, binary search
Binary lookup is also called binary lookup, binary lookup is an ordered lookup table, that is, our lookup table is already sequenced. The reason for this is called binary lookup, because if it does not match every time the keyword is compared, the lookup table is two based on the matching result, excluding the half of the key, and then the half that contains the keyword continues to binary the search.
Below is binary find, in the bottom, we look for a--h the location of the keyword G in this lookup table. Below is a detailed description of each step
-
(1) The scope of the lookup table is marked, and the first level of the lookup table is the entire table, so the bottom boundary of the lookup table is low=1, and the top border of the lookup table is high=8. Find the middle position of the table mid=low+ (high-low)/2= (high+low)/2 = 4. So we compare the size of G with the d corresponding to mid. The comparison result is g>d.
-
(2) As a result of the previous comparison, we learned that the first half of the data in the previous round is not the keyword g we are looking for. So discard the data from the first half of the lookup table and redefine the scope of the lookup table, because the elements at the mid and the match are complete, and to discard the first half of the data, we just need to update the bottom bounds of the lookup table to move to mid. That is, the scope of the lookup table is narrowed down to the last part of the lookup table range . Now look for the bottom boundary of the table Low=mid + 1 = 4+1 = 5. The position of the mid is also changed when the bottom boundary of the lookup table is updated, so we want to update the mid, where mid is still the center of low and High, and mid = (high + low)/2 = (8+5)/2=6. At the moment the element at mid is F, and G and F are compared to G > F.
-
(3) As a result of g>f, we conclude that the data for the first half of the previous lookup table needs to be discarded, so you need to update the value of low, low= mid + 1 = 6+1 = 7. Mid = (8+7)/2=7. the element at the mid of the moment is G, so find the value we're looking for, back to mid = 7.
The above is an example of a complete binary lookup, but in the above example only the values of low and mid are updated because the first half is discarded. When Item<items[mid] , we need to discard the second half of the lookup table and update the value of the top margin high. It is not difficult to conclude that the upper boundary high value is updated to high=mid-1. Reduce the range of lookup tables to the first half to continue looking. According to these statements, it is not difficult to give a code implementation, the code snippet below is the implementation of the Swift language binary find. As shown below:
Iv. Interpolation Lookup
Interpolation lookup is actually the optimization of the above binary lookup, because splitting the lookup table from the middle is not the optimal solution. Because our lookup table is orderly, when we feel that a value is larger, we look directly from behind. For example, in a real life example, when you are in the dictionary is, looking for "zhi" related words, if you let you go directly to the content, you must be running the dictionary behind a few pages, rather than from the middle of a two-point.
Interpolation is a way to get mid closer to the value we're looking for, narrowing the lookup table to a smaller range, so that the search efficiency will definitely increase. As for how to get the mid closer to the value we're looking for, then this is what we're going to do with the interpolation lookup. In binary lookup we know mid = low + (high-low). Because the weight value in front of High-Low is 1/2, the lookup table is binary. interpolation lookup is the modification of this 1/2 weight value into a more reasonable value .
We modify the above expression to mid = low + weight* (high-low), and from this expression we can see that the greater the value of the weight, the mid value is also back, which is in line with the rules we want. Because our lookup table is ordered, the more keywords we find, the more we get back, we can find the value of weight based on the keyword we are looking for. It is not difficult for us to find out weight= (key-low)/(high-low). The above expression can be used to find the weight of the key value in the lookup table in the scope of the current lookup table.
To say so much, the difference between the interpolation lookup and the binary lookup is that of the mid calculation method. Below is a complete example of interpolation lookup. We want to find the location of 82 in the corresponding lookup table. The specific steps are as follows:
-
(1), first initialize the scope of our lookup table Low=1, high=8. Calculate the weight of our keyword 82 within the current lookup table range weight= (key-low)/(high-low) = (82-10)/(98-10) =0.82. By weight, we can easily find the mid value mid = low + weight* (high-low) = 1 + 0.82* (8-1) =6. So we compare 82 with items[mid]=79, we know 82>79.
-
(2), by the above 82>79 comparison results, the search table before mid can be discarded, so we can find the table below the boundary update to low=mid+1=7. In the updated lookup table, 82 corresponds to the weighted value weight= (82-82)/(98-82) =0. By the weight of the moment we can find out mid=7+0* (8-7) = 7. At this point we compare the values of 82 in mid and find that the match is successful and the mid is returned.
The code implementation of the above procedure is not complicated, just replace the calculation of mid in the binary lookup. The Interpolationsearch class below is our interpolation lookup class, and of course the class also follows the searchtype protocol. In the code snippet below, in addition to the Code in the Red box section, the rest of the code is exactly the same as the binary lookup. The code is relatively simple, so don't do too much of it here.
Five, Fibonacci search
Next we'll talk about Fibonacci (Fibonacci) lookup . is to separate the lookup table by the Fibonacci sequence. If you have known Fibonacci series before, then Fibonacci lookup should be well understood. Below we generate the Fibonacci sequence, and then use this sequence to split our lookup table.
1. Generate Fibonacci Series
First we want to generate a Fibonacci sequence for our Fibonacci lookup to use. In the Fibonacci sequence, the value of the next item equals the sum of the first two values, and if it is represented by a mathematical formula, it is f (n) =f (n-1) +f (n-2) (n>1), f (0) =0, f (1) =1, according to which we can generate our Fibonacci series. In the Fibonacci sequence, the greater n,the zh value of f (n-1)/f (n) is closer to 0.618, we know that 0.618 is the golden ratio, so the Fibonacci sequence is called the Golden segment . So we're going to implement a Fibonacci lookup that can also be called a golden split .
First, we start with the rules of the Fibonacci sequence to generate Fibonacci series. This is how we generate the Fibonacci sequence. The Fibonaccisearch class Below is the class we Fibonacci find, where the Fibonacci sequence is stored in fibonaccisequence . The createfibonaccisequence () method below is the way to create a Fibonacci sequence. As shown below:
2.Fibonacci Find
Fibonacci Lookup is actually the use of the Fibonacci sequence to split the lookup table, split into F (n-1) and F (n-2) two parts . That is, if the number of our lookup table elements is f (n), then the number of elements of low to mid (the first half of the lookup table) is F (n-1), and the second half (min---high) is f (n-2). With the above segmentation, we know mid = low + F (n-1)-1.
To put it bluntly,Fibonacci lookup is actually using the Fibonacci sequence to split the lookup table, then find the position of the mid, compare the keyword with mid, and then decide whether to discard the second half or the first part . Below are the steps to find 82 in the corresponding lookup table using the Fibonacci sequence.
-
(1), first prepare the Fibonacci Series reserve, and then calculate the number of lookup table elements n in the range of the Fibonacci series. The number of lookup tables in the example below is 9, by F (6) =8 < 9 < F (7) =13 This relationship, we know that the lookup table from 9 elements to 13 elements can be divided with the Fibonacci sequence, because F (7) = 13, we will mark 7, That's key=7.
-
(2), in order to be able to use the Fibonacci sequence for segmentation, we expanded the lookup table to 13 elements ( F (7) =five). The value of the expanded element behind the lookup table is consistent with the last element of the original lookup table.
-
(3), the expanded lookup table using Fibonacci series for the first round of segmentation. Because f (7) =13=f (6) + f (5) = 8 + 5, we divide the lookup table into two parts, the first half of the elements are f (6) = 8 , the second half is F (5) =5 , and now our M The value of ID is Mid=low + F (6)-1 = 1+8-1=8. We compare 82 elements out of mid (82<98).
-
(4), 82<98 This result we can narrow the lookup table to the first half of the above split. So we're going to update the high value to High = Mid-1 = 7. We continue to use the first half of the Fibonacci sequence to split the first half of the number is f (6) =8 , because f (6) =f (5) +f (4) = 5+3, so we can divide the new lookup table into f (5) =5 and f (4) =3 two parts. Now the mid=low+f (5) -1=1+5-1 = 5 . 82 compared to items[5]=58 , you can draw 82>58, key=6 at the moment.
-
(5), by 82>58 This result we can know that the first half of the previous round of the lookup table should be discarded. We narrowed the lookup table to the second half (section F (4)). The second half of the number of elements is f (4) = 3, we can continue to split the lookup table, the moment the key=4. We first update the position of the low, low=mid+1=5+1 = 6. Then mid = Low+f (3)-1 = 6+2-1=7. At this moment 82=items[mid]=items[7]=82, find successful will return mid .
3, Fibonacci find the Code implementation
After the analysis of the principle, it is not difficult to give the code implementation. The gross structure is still similar to the binary search. is to calculate the value of the mid based on the Fibonacci sequence, and then shrink the range of the lookup table continuously. First we need to find the current lookup table that needs to be extended to several elements that can be segmented by the Fibonacci sequence. The function below is to calculate the number of elements after the lookup table expands. The Findnumberinfibonacci () method has a parameter that is the number of elements of the current lookup table, and the return value of the method is the number of lookup tables after the expansion .
To find out the number to expand, we need to expand the lookup table. The method below is to augment the lookup table. The element used when expanding is the last value of the original lookup table.
After the lookup table has been expanded, it is time to look for it. Below is the core code for Fibonacci lookup. The specific steps of the code correspond to the example diagram above, which is one by one. One thing to note is the update of the key value. The key in the code below is actually the subscript of the Fibonacci sequence, the number of lookup tables in the current range ==f[key]. Because the scope of our lookup table is shrinking, the key value also changes. We divide the lookup table (the number of elements of the lookup table to F[key]) into two parts of F[key-1] (the first half) and F[key-2] (the second half) , and if the latter part is discarded , the key value is key-1 , If you discard the first half, then Key=key-2, this needs to be noted.
Vi. Test Cases
At this point, we searched sequentially, binary lookup, interpolation lookup, Fibonacci lookup Chat, and gave the corresponding code implementation. Then it's time for us to test. Because all of the lookup classes above follow a searchtype protocol , all of our test cases can be shared, which is one of the benefits of interface-oriented programming. Below is our test case for this blog post.
The above test case we are using a, as long as we pass in a different lookup class object, we can use the appropriate lookup method to find. Below is the output of our test case for this blog post.
This blog is long enough, first come here, the above example of the full demo will be on GitHub to share, the next blog we will introduce several other ways to find.
GitHub link Address:https://github.com/lizelu/DataStruct-Swift/tree/master/SearchDemo
Data structure (ix) Order lookup for lookup tables, binary lookups, interpolation lookups, and Fibonacci lookups