Data structure --- Union search

Why is there a union search set?

Here we can use an example from life to help you understand and collect information. You will definitely find a phenomenon when you go to school. Before school starts, no one knows anyone. Everyone is a small group. But after school starts, Because there is a deskmate next to your seat, you and your deskmate will get to know each other and play happily together not long after school starts. At this time, two small groups of one person merge into a small group of two people. Later, you You may often turn your head to the back to get to know the people behind you. After getting to know each other, you get to know the people at the table behind you and bring your tablemates to play with them. Then at this time, two small children of two will get to know each other. The group will merge into a small group of 4 people. As time goes by, groups of different sizes will merge with each other. Eventually, the people in a class will change from a small group with everyone to a large group with everyone together. , then in order to describe the fusion process of different groups, there is the data structure of the check union set.

The principle of union search

Let’s first take a look at the more official definition of union set: In some application problems, n different elements need to be divided into some disjoint sets. At the beginning, each element forms a single-element set of its own, and then the sets belonging to the same group of elements are merged according to certain rules. In this process, the operation of querying which set a certain element belongs to is repeatedly used. An abstract data type suitable for describing this type of problem is called a union-findset. Bingchaji is a forest. The so-called forest refers to a forest composed of multiple trees. For example, a company recruits a total of 10 people from all over the country this year. Xi'an recruits 4 people, Chengdu recruits 3 people, Wuhan recruits 3 people, and 10 people come from different places. The school does not know each other at first, and each student is an independent small group. Now these students are numbered: {0, 1, 2, 3,4, 5, 6, 7, 8, 9}; Give the following The array is used to store the small group. The absolute value of the numbers in the array represents: the number of members in the small group. Then the array is as follows: After graduation, students will go to work in the company, and students in each
Insert image description here
place spontaneously organize into small groups. The teams go on the road together, then at this time they merge from one person and one group into multiple people and one group, so Xi'an student team s1={0,6,7,8}, Chengdu student team s2={1,4,9} , Wuhan student team s3={2,3,5}, 10 people formed three small groups. Assuming that the three 0, 1, and 2 on the right serve as captains, then the current structure should become as follows:
Insert image description here
Three groups are three trees, and these three trees constitute a forest. So there is a question here: how do we combine these three trees into a forest? The answer is to use an array to merge these trees. The subscript of the array corresponds to the element, and the value corresponding to the subscript represents the relationship between different elements. If you are the root node, then the value corresponding to your subscript represents your The number of nodes in the tree. If you are the root node or child node of a subtree, then the value corresponding to your subscript is the position of your parent node in the array. For example, element 0 is the root node of the tree in Xi'an. 0 corresponds to the subscript 0 in the array, then the data recorded in subscript 0 is -4, which means there are 4 nodes in the tree in Xi'an. For example, if the subscript corresponding to element 5 is 5, then the subscript in the array The data recorded in mark 5 is 2, which means that the current node is a child node. The position of the parent node of this node in the array is 5, and so on for other nodes, so the above array will become like this: Got
Insert image description here
it After how to represent multiple trees, let's take a look at how to merge two trees into one tree. For example, if we merge Xi'an and Chengdu above, we can modify the parent node of the root node in the Chengdu tree so that it points to Xi'an. The root node of the tree is implemented, then the picture here becomes as follows:
Insert image description here
Then in the array we have to modify the subscript values ​​corresponding to the root nodes of the two trees. First, modify the subscript value of 1 to 0 to represent the node. The parent node of 1 is 0. Change the value with subscript 0 to -7. Because a new tree is merged, the number of nodes in the current tree becomes more. Then the content in the current array becomes as follows , the red background represents the root nodes of different trees, the red color has changed from the original 3 to 2, then this means that the current forest only has two trees. After knowing the representation
Insert image description here
method of the forest and the principle of tree fusion, we can Simulate the implementation and find the set.

Simulate implementation and lookup

Preparation

First, there must be an integer array in the class to represent the relationship between each element:

class UnionFindSet
{
    
    
public:

private:
	vector<int> _ufs;//用来表示元素之间的关系
};

But there is a problem in doing this: how do we know which element the subscript in the array corresponds to? So we have to create a vector container to facilitate us to find the element corresponding to the subscript, and because the element can be of various types, here we have to add a class template. There is a parameter in the template indicating which is the current and search processing After we have this container, we can view the elements corresponding to the subscript, so what if we want to view the subscript corresponding to the element? So we have to create a map container to record the subscript corresponding to each element, then the current code becomes as follows:

template<class T>
class UnionFindSet
{
    
    
public:

private:
	vector<int> _ufs;//用来表示元素之间的关系
	vector<T> _a;//根据下标找元素
	map<pair<T, int>> _indexmap;//根据元素找到下标
};

Constructor

The constructor requires two parameters, one parameter receives the data array that the current container needs to process, and the other parameter indicates the number of data currently processed:

UnionFindSet(const T* sorce, size_t num)
{
    
    

}

Then we create a loop, take the value of the parameter array in the loop and insert it into the array _a, because the loop starts from 0 and the array _a is also inserted from the position where the subscript is 0, so in the loop We can insert data into the _indexmap container by the way, so the code here is as follows:

UnionFindSet(const T* sorce, size_t num)
{
    
    
	for (size_t i = 0; i < num; i++)
	{
    
    
		_a.push_back(sorce[i]);
		_indexmap[sorce[i]] = i;
	}
}

Finally, expand the length of the container _ufs to num, and initialize the value of each element to -1. The complete code is as follows:

UnionFindSet(const T* sorce, size_t num)
	:_ufs(num,-1)
{
    
    
	for (size_t i = 0; i < num; i++)
	{
    
    
		_a.push_back(sorce[i]);
		_indexmap[sorce[i]] = i;
	}
}

We can use the following code to perform the following tests:

int main()
{
    
    
	string s1[] = {
    
     "张三","李四","王五","赵六" };
	UnionFindSet<string> uf(s1, 4);
	return 0;
}

Through debugging, we can see that the contents of this container are as follows:
Insert image description here
because we have not done any merge operation, the value of each element in the ufs array is -1, and the _a array records the elements corresponding to the subscript. 0 corresponds to Zhang San, 1 corresponds to Li Si, 2 corresponds to Wang Wu, 3 corresponds to Zhao Liu, and then _indexmap records the subscript corresponding to the element. After careful comparison, we can see that The recorded content corresponds to the content in the array, then our constructor is completed. Next, let’s look at the search function.

FindRoot

The FindRoot function searches for the root node of an element. If a node is not the root node, it stores the subscript of its parent node in the array. If a node is the root node, it stores the number of nodes contained in the current tree. Number assignment, so in the function, we can create a while loop to extract the subscript recorded in the array until the value corresponding to the subscript is negative, then the code here is as follows:

size_t FindRoot(T tmp)
{
    
    
	int x = _indexmap.find(tmp)->second;
	while (_ufs[x] >= 0)
	{
    
    
		x = _ufs[x];
	}
	return x;
}

Union

Pass two elements to the Union function, then the function can merge the trees where the two elements are located. At the beginning of the function, we first determine whether the root nodes of the trees where the two elements are located are the same. If they are the same, if they are not the same, we Just merge, then the code here is as follows:

void Union(T tmp1, T tmp2)
{
    
    
	size_t x1 = FindRoot(tmp1);
	size_t x2 = FindRoot(tmp2);
	if (x1 != x2)
	{
    
    
	//根节点不相等才进行合并
	}
}

The process of merging is very simple. Add the value of a root node to another root node, and then change the value to the subscript of another root node. Then the code here is as follows:

void Union(T tmp1, T tmp2)
{
    
    
	size_t x1 = FindRoot(tmp1);
	size_t x2 = FindRoot(tmp2);
	if (x1 != x2)
	{
    
    
	//根节点不相等才进行合并
		_ufs[x1] += _ufs[x2];
		_ufs[x2] = x1;
	}
}

SetCount

The function of this function is to count how many trees exist in the current container. So here we directly traverse the array _ufs through a loop. The presence of several nodes with negative elements in it indicates how many trees exist in the current container. The code here is as follows :

size_t SetCount()
{
    
    
	size_t num = 0;
	for (auto ch : _ufs)
	{
    
    
		if (ch < 0)
		{
    
    
			num++;
		}
	}
	return num;
}

And check the actual combat

Topic 1: Number of provinces

Question details:
Insert image description here
Question link-> Click here to try the question

Question analysis

With the union search set, it is a piece of cake to do this kind of question. First of all, the question gives us a two-dimensional array. This array represents the connection between various cities. If isConnected[0][1] is equal to 1, Then this means that City No. 1 and City No. 2 are connected, and then a group of interconnected cities are called provinces. The final question requires us to determine how many provinces currently exist based on a two-dimensional array, so here we can first create a Union-find objects, and then create an inline for loop to determine the cities that are linked to each other in the two-dimensional array. If a city i and city j are connected to each other, use union-find to merge the two cities and traverse After completion, you can return to the SetCount function of the union search set to end this question, because the parameter passed in the question is a container of vector<vector<int>>, and the constructor of the union search set we implemented above requires an array, so for convenience We simplify the above class and let it specifically serve int type data. Then the code here is as follows:

class UnionFindSet
{
    
    
public:
	UnionFindSet(int size)
		: _set(size, -1)
	{
    
    }

	size_t FindRoot(int x)
	{
    
    
		while(_set[x] >= 0)
			x = _set[x];

		return x;
	}

	void Union(int x1, int x2)
	{
    
    
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);

		if(root1 != root2)
		{
    
    
			_set[root1] += _set[root2];
			_set[root2] = root1;
		}
	}

	size_t SetCount()
	{
    
    
		size_t count = 0;
		for(size_t i = 0; i < _set.size(); ++i)
		{
    
    
			if(_set[i] < 0)
				count++;
		}

		return count;
	}

private:
	std::vector<int> _set;
};

Then the code for this question is as follows:

int findCircleNum(vector<vector<int>>& isConnected) {
    
    
    UnionFindSet uf(isConnected.size());
    for(int i=0;i<isConnected.size();i++)
    {
    
    
        for(int j=0;j<isConnected[0].size();j++)
        {
    
    
            if(i!=j&&isConnected[i][j]==1)
            {
    
    
                uf.Union(i, j);
            }
        }
    }
    return uf.SetCount();
}

The test results are as follows:
Insert image description here
You can see that the running results are correct.

Topic 2: Satisfiability of equations

Insert image description here
Question link-> Click here to try the question

Question analysis

This question is obviously solved by union search. Many expressions are provided in the array, so we first create a for loop to merge all the equal elements used in the expression together, and then create a loop to judge each unequal The expression checks whether the elements of the two times belong to the same tree. If it is a tree, it will return false directly. If it does not belong to the same tree, it will continue to judge. If all elements have been judged and there is no error, it will return true. , the parameter form given in the question is as follows:

bool equationsPossible(vector<string>& equations) {
    
    

}

We don’t know the number of elements, so here we directly stretch the space to a maximum of 26 letters, so here we open 26 sizes, and then use the relative mapping method to merge. The character a corresponds to 0, and the character b corresponds to 1. Keep going like this, then the code here is as follows:

bool equationsPossible(vector<string>& equations) {
    
    
	UnionFindSet uf(26);
	for(auto ch:equations)
	{
    
    
	    if(ch[1]=='=')
	    {
    
    
	        uf.Union(ch[0]-'a', ch[3]-'a');
	    }
	}
	for(auto ch:equations)
	{
    
    
	    if(ch[1]=='!')
	    {
    
    
	        if(uf.FindRoot(ch[0]-'a')==uf.FindRoot(ch[3]-'a'))
	        {
    
    
	            return false;
	        }
	    }
	}
	return true;
	}

The running results of the code are as follows:
Insert image description here
You can see that the running results are normal.

Guess you like

Origin blog.csdn.net/qq_68695298/article/details/131965569