The first few articles describe the basic sorting algorithm, which is usually a prelude to the lookup operation. This article begins with a basic lookup algorithm.
In the introduction of the search algorithm, the first need to understand the symbolic table of this abstract data structure, this article first introduced what is the symbol table, and this abstract data structure of the API, and then introduced two simple symbol table implementation way.
A symbol table
Before we begin to introduce the lookup algorithm, we need to define an abstract data structure called the symbol table, which is similar to the one we used in C #, which is an abstraction of a key-value pair element with a key and value for each element, dictionary. We can add the Key,value key to the inside, or we can find the value by key. In real life, we often encounter a variety of needs based on key to find the value of the case, such as DNS lookup IP address according to the domain name, the library based on the index number to find books and so on:
To implement this function, we define an abstract data structure and then use the appropriate data structure to implement:
public class St<key, value>
ST () |
Create a Lookup Table object |
void Put (Key key, Value Val) |
Inserts a key value pair record into the collection, if value is empty, does not add |
Value Get (Key key) |
Find value based on key if no return null is found |
void Delete (Key key) |
Delete a record with key |
Boolean Contains (Key key) |
Determines whether a record of key is present in the collection |
Boolean IsEmpty () |
Determine if the lookup table is empty |
int Size () |
Returns the number of key-value pairs in the collection |
Iterable<key> Keys () |
Returns all the keys in the collection |
Two implementations 1 using unordered list to implement lookup table
The key to the implementation of the lookup table is the choice of data structure, the simplest implementation is the use of unordered lists, each node records the key value, value, and the object pointing to the next record.
, when we insert an element into a linked list, it starts from the table header, and if found, updates value, otherwise, inserts a new node element in the table header.
It's also easy to implement:
public class Sequentsearchsymboltable<tkey, tvalue>: Symboltables<tkey, tvalue> where tkey:icomparable <tkey>, iequatable<tkey>{private int length = 0; Node first; Private class Node {public TKey key {get; set;} Public TValue value {get; set;} Public Node Next {get; set;} Public Node (TKey key, TValue value, Node next) {This.key = key; This.value = value; This.next = Next; }} public override TValue Get (TKey key) {TValue result = default (TValue); Node temp = first; while (temp! = null) {if (Temp.key.Equals (key)) {result = Temp.value; Break } temp = Temp.next; } return result; } public override void Put (TKey key, TValue value) {Node temp = first; while (temp! = null) {if (Temp.key.Equals (key)) {temp.value = value; Return } temp = Temp.next; } first = new Node (key, value, first); length++; } ....}
Analysis:
From the diagram or code analysis, the insertion of the first to find, if there is updated value, when looking for the need to find from the linked header, so the average time to insert and find the complexity of O (n). So there is no more efficient method, the following is the introduction of binary search.
2 Finding a table using a binary lookup implementation
And the use of unordered linked list implementation, the idea of binary search is to maintain a two-dimensional array in accordance with key, each time the search, with the intermediate elements to compare, if the element is small, then continue to the left half recursive lookup, or continue to the right half recursive lookup. The entire implementation code is as follows:
Class Binarysearchsymboltable<tkey, tvalue>: Symboltables<tkey, tvalue> where Tkey:icomparable<tkey iequatable<tkey>{private tkey[] keys; Private tvalue[] values; private int length; private static readonly int init_capacity = 2; public binarysearchsymboltable (int capacity) {keys = new tkey[capacity]; values = new Tvalue[capacity]; length = capacity; Public binarysearchsymboltable (): This (init_capacity) {}///<summary>///Find value by key. First find the position of key in the keys, if it is within the length range, and the value that exists at that location equals key, then the value is returned//////</summary>///<param name= "key" &G t;</param>//<returns></returns> public override TValue Get (TKey key) {int i = Rank (k EY); if (i < length && keys[i]. Equals (key)) return values[i]; else return default (TValue); }///<summary> Insert the Key,value key value pair into the symbol table. If there is an equal key, theUpdate the value directly, otherwise insert the key,value in the appropriate location///1. First move the elements behind the position back to///2. Then the element is placed in the position for I///</summary>//<p Aram Name= "key" ></param>///<param Name= "value" ></param> public override void Put (TKey key, TV Alue value) {int i = Rank (key); if (i < length && keys[i]. Equals (key)) {Values[i] = value; Return }//If the lengths are equal, the if (length = = keys) is enlarged. Length) Resize (2 * keys. Length); for (int j = length; j > i; j--) {keys[j] = keys[j-1]; VALUES[J] = values[j-1]; } Keys[i] = key; Values[i] = value; length++; }///<summary>///Returns the position of key in the array///</summary>//<param name= "key" ></param>// <returns></returns> private int Rank (TKey key) {int lo = 0; int hi = length-1; while (lo <= hi) {int mid = lo + (Hi-lo)/2; if (Key.compareto (Keys[mid) > 0) lo = mid + 1; else if (Key.compareto (Keys[mid]) < 0) Hi = mid-1; else return mid; } return lo; } 。。。 }
The emphasis here is on the rank method, which we can see first to get the mid position, then compare the current element to the mid position element, and then update the LO or hi position to replace with mid, or return to mid if equal is found, otherwise return the appropriate position where the element should be inserted in the collection. The above is implemented using iterations, or it can be rewritten as recursion:
private int Rank (TKey key, int lo, int hi) { if (lo >= hi) return lo; int mid = lo + (Hi-lo)/2; if (Key.compareto (Keys[mid]) > 0) return Rank (Key, mid + 1, HI); else if (Key.compareto (Keys[mid]) < 0) return Rank (Key, Lo, hi-1); else return mid;}
The two points are searched as follows:
Analysis:
Using an ordered two-dimensional array to implement a lookup table, it can be seen that using a binary lookup only requires a maximum of lgn+1 comparison to find the corresponding element, so the search efficiency is relatively high.
But for an INSERT element, each time a nonexistent element is inserted, the element needs to be placed in the specified position, then the elements behind him are moved back, so the average time complexity O (n) is still relatively inefficient for insertion.
Three summary
This paper introduces the abstract data structure of symbol table, then introduces two basic implementations: the implementation based on unordered list and the implementation based on ordered array, the time complexity of the two implementations is as follows:
As you can see, the binary lookup method using ordered arrays improves the lookup speed of the symbol table, but the insertion efficiency is still not improved, and a sort operation is required to maintain the array in order. These two implementations are simple and intuitive, but cannot achieve high search and insertion efficiency at the same time. Then there is no data structure can be found at the time of high efficiency, in the insertion time also has a good efficiency, this article is only a primer, the following series will introduce binary search tree, balance the search tree and hash table.
Hopefully this article will help you understand the basic concepts of lookup tables and the two basic implementations.
Discussion on algorithm and data structure: six symbol table and its basic realization