Leetcode Union-find (and collection) topic (i)

Source: Internet
Author: User
Leetcode Union-find (and collection) topic (i)

Objective:
Writing this topic is still the same as before, an article write a blog tired of writing. A special topic for a change of feeling. (Manual funny

Okay, actually, it's just that. The collection is a more practical data structure. Before listening to the teacher in class mentioned once, before that very early heard this, but also because the lazy did not really understand. Taking advantage of these two days of free time to acquaint with it, it is not difficult to find. (difficulty and two-point search of the change is similar to it)

Let's get down to business.

Leetcode Union-find and check the collection of special topics together Union-find Brief introduction and check the collection of the comparison and check the set of the basic implementation of a simple basic implementation and search the set of simple routines and check the set of simple application examples Leetcode 547 Fri End Circles Leetcode 737 sentence similarity II This summary of the sequel explains the end

A brief introduction of Union-find and collection and find out what the collection is

And the collection is, as its name says, a data structure for fast merging (Union) and lookup (find) operations.
Actually, it's over here. In order to make it clear, say more details.
First consider finding, and finding the least complex data structure is. The HashMap Hash Map is almost certain. Except this one. Set,bbst Set, Bbst is a number of different levels of complexity (the internal use of the red and black tree to achieve the right to remember). It is almost impossible to merge HashMap Hash Map (there is little organization in itself, and it is at least as complex as O (n) o (n) levels to merge. In other cases, the complexity of the tree is only O (1) O (1) but this is not considered in the case of rebalancing.

First consider the merger, the first thought of the tree, the simple tree of the merger should be calculated in the data structure is very simple. so at present, the tree structure in the Union−find union-find problem should be a better solution (this is the case, and the collection is also borrowed from the structure of the tree)

What I understand and search for is based on the organization of the tree, which is simplified and abstracted to almost only the data structure of union union and find found two functions. (In fact, the two may extend some other function, after that) because of the discard of some structural information, according to the Trade−off trade-off law, better time efficiency.

Here we first describe and look at the general idea of the collection (the following UF to refer to Union-find). Take an integer unordered number pair as an example.
Consider the following examples:
(1, 2), (2, 3), (6, 7), (5, 6), (7, 4)

We regard each number pair as having a relationship and assume that the relationship is transitive (many practical problems are so).

Now we're going to cluster these relationships. Each class elects a representative to represent all the elements of the class (typically the first element that exists in the class). Sets the element representing the i-th element as subscript father[i]. After such representation, the merge and lookup are performed as follows:
1. Merge: Set We are now going to merge (A, b), then we need to first find ' A, B ' respective representatives (recorded as Father[a], father[j]). Make one representative, as the other representative can. IE Father[father[a]] = father[b]

Note: The simplicity and the collection do not specify the direction of the merger. The upper-style can also be reversed. However, subsequent optimizations and specific problems may constrain this .
2. Find: To find out if two elements are in the same category, just see if they represent the same element. i.e. Father[a] = = Father[b] and compare the collection

Originally here want to write advantages, think about it or put it to the next one. Here first write some and tree contrast, for the next optimization to do some matting.
In fact, the above describes the establishment and merging of a tree. Exactly the same.
Before the "Representative element" is actually the root of the tree, the initial situation each point is a tree , each get a new relationship, is to a tree to add a leaf node. For merging, it is to take the root of a tree as a child of another tree; For a lookup, it is to see whether the root node of the two points is the same.

So why do you have to create such a data structure concept? This is because of optimization. At the very least, I think the simple and search collection is a direct, tree-like forest of raw data. It's just an array of expressions.
As I have said before, we can optimize because for UF, we can discard some of the structure of the tree and only need to leave the structure that supports merging and finding. The direction of optimization is to start with complexity: the merge complexity O (1) O (1) is already up to the upper limit, and the search complexity is not optimistic: the average complexity and optimal complexity is O (logn) O (log N), and the worst case is O (n) o (n).
That is divided into the following two kinds of situations:
1. For the worst complexity O (n) o (n), the reason for this is obvious because the constructed trees are not balanced and approximate linear lists (degenerate). To optimize, you just need to balance as much as you can. Of course we can't do the balancing algorithm. But take this action: each time merging, the smaller layer of the tree as a child node of the larger tree (note: And the concept of the number of layers is called rank). In this way we tend to merge in a way that tends to balance the trees.
2. For average complexity, note that the time spent is proportional to the path length of the root to the node. So the compression path becomes another direction of optimization. Of course we want the path length to be only 1, then the complexity will change to O (1) O (1).
Note: The compression path here is the trade-off exchange. For a common tree it is necessary to record the root to leaf path, but for the purpose of simply merging the lookup, this structure is not required, and therefore it is sacrificed in exchange for higher efficiency.

The optimization described below will be primarily about the implementation of optimization based on the above two ideas. Before we do that, though, let's take a look at the basic implementation of UF. (In fact, a lot of problems, not optimized simple and search set is enough) and search the basic implementation of a simple basic implementation

The following code is a check-and-search implementation of the relationship between integer pairs:

struct UF {
    uf (int n) {
        nums.resize (n);
        for (int i = 0; i < n; ++i) {
            nums[i] = i;
        }
    }
    BOOL Union (int a, int b) {
        int ancestera = Find (a), Ancesterb = find (b);
        if (Ancestera = = Ancesterb) return false; need not to union.
        else {
            Nums[ancesterb] = Ancestera;
            return true;
        }
    int find (int k) {
        int i = k;
        while (i!= nums[i]) i = nums[i]; Here I am the root.
        return i;
    }
    Vector<int> nums;
};

The entire data structure is very concise. A little explanation:
1. The nums array is an array of the subscripts that record the parent node of each point. For the root node, num[i] = = I
2. Initialize each point is a separate tree, itself is the root.
3. Each lookup goes up along the parent node until the root node.
4. Each merge sets the parent node of one root node to another root node. optimization of the search and collection

I've mentioned two ways to optimize. We just need to make these two concrete implementations.
1. First of all, ranking (rank) optimization.
This optimization is not complicated. That is, maintain an array of depths for each root node, and each time the merge is performed, select a tree with a small depth to merge into the deep tree. This allows for a lower depth value for the entire tree. (no change in depth)
The depth of the update is that when the two trees are the same depth, choose the merge method, but at this time the new root node after merging depth + 1 below is the compression path optimization. It is worth noting that this process is not at the time of merging, but each time, the node above the current lookup path directly as a leaf node connected to the root node.
This optimization is easier to write. Use recursive writing even directly line to fix, as follows:

int find (x) {
    if (Father[x]!= x) father[x] = find (father[x));
    return father[x];
}

I generally use a non recursive method as follows:

    int find (int k) {
        int i = k, j = k;
        while (i!= nums[i]) i = nums[i]; Here I am the ancester.
        while (Nums[j]!= i) {  //update the whole path to connect to the Ancester directly.
            int t = nums[j];
            NUMS[J] = i;
            j = t;
        }
        return i;
    }
and the simple routines of looking up the collection

As a result of a total of not doing a few questions, so the temporary understanding of the routine is not much. Should be the basis of the application bar.
1. The solution of the connected component number for undirected graphs.
This can be said to be the most basic and check the collection. The answer is given by scanning the collection over and over again.

For the merging of intervals
This kind of problem should be noted is the choice of each merger. If the choice is arbitrary, but if the artificial provision of a position (the interval is the most left or the most right) as the root, then we are in the merger of the main focus on the side of the processing can be. Rather than on both sides.
Here's a question that's specifically about this. The discussion will not commence for the time being.

Merging in a two-dimensional matrix.
The problem in the two-dimensional matrix is very similar to the interval, but the merging within the matrix is continuous, unlike the interval may be discontinuous. In fact, it's also about determining the directionality of a root node (for example, on the left).
Because two-dimensional merging is actually more complex (mainly to be considered in the direction of the merger). Sometimes it is not a good choice to check the collection. In general, unless there is a certain dynamic query requirements, I think it is simpler to use BFS or DFS than using and searching the set. examples of simple application of the search and collection 1. Leetcode 547. Friend Circles

The subject of this topic is to find the number of connected components in a undirected graph represented by a matrix.
This problem is a typical problem in the application of the collection. The code is as follows. Note that this is almost a copy of the basic and search set without making any changes. Just add a variable to determine the number of merges.

Union find struct UF {uf (int n) {nums.resize (n);
        for (int i = 0; i < n; ++i) {nums[i] = i;
    } setnum = n;
        BOOL Union (int a, int b) {int ancestera = find (a), Ancesterb = find (b); if (Ancestera = = Ancesterb) return false;
        need not to union.
            else {Nums[ancesterb] = Ancestera;
            --setnum;
        return true;
        int find (int k) {int i = k, j = k; while (i!= nums[i]) i = nums[i];
        Here I am the ancester.
            while (Nums[j]!= i) {//Update the whole path to connect to the Ancester directly.
            int t = nums[j];
            NUMS[J] = i;
        j = t;
    return i;
    } vector<int> Nums;
int setnum;


};
        Class Solution {public:int findcirclenum (vector<vector<int>>& M) {UF uf_set (m.size ()); for (int i = 0; i < m.size (); ++i) {for (int J = 0; J < M[i].size (); ++J) {if (m[i][j] = = 1) uf_set.
            Union (i, j);
    } return uf_set.setnum; }
};
2. Leetcode 737. Sentence similarity II

The subject of the topic is given a similar sequence of words, and the similarity is transitive (this is an obvious hint that requires a merge operation). Then give the two word vectors and ask whether the two vectors are similar to each other.

Basic idea: according to similar words to set up and search the list, set up after the completion of scanning two words vector, see whether each pair are in a similar field. (That is, the root of each pair of words is consistent)

Here's a little trick: follow the title and search the set, and you need a map[string, string. However, if you use a word in advance to make a map of a map[string, int], then we need to deal with one of the simplest and more.

The code is as follows:

struct UF {
    uf (int n) {
        father.resize (n + 1);
        for (int i = 0; i < father.size (); ++i) {
            Father[i] = i
        }
    }
    BOOL Union (int a, int b) {
        int root1 = Find (a), Root2 = find (b);
        if (root1!= root2) {
            Father[root2] = ROOT1;//or father[root1] = Root2;
            return true;
        }
        Ret

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.