Algorithm Learning (Discipleship Program) 2-3 Union-find and classic problem study notes

foreword

6.7. I haven't updated my study notes for a long time, because during this period of time, I really didn't study hard, so I didn't have the energy to summarize. It doesn't matter what I am busy with, what matters is what I will face.
I will face a trouble, how to catch up the progress.
So this round, this week I will face a challenge how to finish learning, writing, and learning in the shortest time. I hope that the way I exercise can be useful to more people. Now I'll start the timer and at the end I'll tally the total time spent.

The method used:

  • First, fast-forward all the videos at 2x speed, and finish the basic part on the premise of using Baidu to preview. If there are parts that you don’t understand, record the time bookmark and write down the outline in the process
  • Secondly, preview all the exercises. If you read all the questions once, it will be divided into two parts: knowing and not. For the knowing part, the video can be skipped at 2x speed, and the not able part can be watched at 1.2x speed or the original speed.
  • When solving problems again and again, write the problem-solving ideas for the problems that you will not do, and write the problem-solving ideas for interesting problems, and provide code as much as possible, and summarize the problems that you can do as much as possible.
  • Finally, review and complete the outline.

(Try this method, start now, the video of this lesson is 4h long)

2-3 Union-find and classic problems

And lookup

concept

  • Union search is a method, usually active in the code as a tool class.
  • And check set is used to process and judge whether two elements belong to the same set (find method)
  • The union search is used to merge the sets where the two elements are located, so that the two sets are connected (merge method)

Basic algorithm Quick-find algorithm, coloring method

When faced with the problem of how to distinguish different sets, a set of basic methods is proposed, that is, given an extra dimension , the code names of these sets in this extra dimension are different, andcollection interiorThe elements in the extra dimension have the same code name , so that any element can judge whether it is the same element according to the value of this extra dimension.

This extra dimension is called color .

When merging two sets, the method used is to dye the color of the representative element of one set to the representative color of the other set (the reason for using the dyeing method here is that it is not clear whether other colors are used, only to determine These two colors are used, so discard one color and use one color to command this new set, so it is called dyeing)

Sample code:

	class UnionSet{
        private int [] color;
        private int n;

        public UnionSet(int n){
            this.n = n;
            color = new int [n+1]; 
            //n+1的意义是n指代0~n个,如果n的意义是n个,可以不加1
            for(int i = 0;i<= n;i++){
                color [i] = i;    //对维度(颜色)进行初始化
            }
        }

        public int find(int n){       //查找元素的颜色
            return color [n];
        }

        public void merge (int a,int b){
            int cb = color[b];    //获取用于被覆盖的颜色

            if(color[a] == cb ) return;  //相同颜色不需要合并

            for(int i = 0;i<= n;i++){
                if(color [i] == cb)
                    color [i] = color[a];//对维度(颜色)覆盖(低性能的原因)
            }
        }
    }

I will talk about the flattening optimization of the Quick-Union method later . It looks very similar to this set of coloring methods. They try to make elements point to one element as a set of distinguishing concepts, but the fundamental problem between the two is that the coloring method Color is a non-inheritable target vector , so when coloring, all elements in one set need to be changed to another.
And flattening refers to one element. When merging collections, only one element needs to be modified. The details will be described below.

Tree structure algorithm Quick-Union algorithm, tree structure

The simple and easy-to-understand coloring method is mentioned above, but the shortcomings are obvious . When the collection is merged,Need for all elements of a collectionto process.
Can it be changed to only one ?
Yes, here we need to borrow the tree structure, the tree structure is a way to define a set of sets with a root
(but this tree is a reverse tree , because the normal tree has a root pointing to the child node, while the tree here is It is expected that the child node can find the parent node, so it is actually a loop-free graph structure, but for the convenience of description, it is still called a tree , but the pointing relationship of the nodes is opposite)

When managing in a tree structure, due to the existence of roots, it is only necessary to determine whether the roots of the elements are the same to obtain whether two sets of elements belong to the same set . When merging collections, you only need to connect the parent node of one collection under the root node of another collection (or directly under the current node of another tree)

Sample code:
(The code below is connected to the root node)

	class UnionSet{
        private int [] fa;
        private int n;

        public UnionSet(int n){
            this.n = n;
            fa = new int [n+1]; 
            //n+1的意义是n指代0~n个,如果n的意义是n个,可以不加1
            for(int i = 0;i<= n;i++){
                fa [i] = i;           //对各自根(父节点)进行初始化
            }
        }

        public int find(int x){       //查找根
            if(fa[x] == x ) return x;
            return find (fa[x] );
        }

        public void merge (int a,int b){
            int finda = find(a);
            int findb = find(b);  

            if(finda == findb ) return;  //相同根不需要合并

            fa[finda] = findb;            //有待优化         
        }
    }

Weighted-Quick-Union algorithm, weight optimization, placement optimization

Although the performance has been preliminarily optimized by choosing to connect to the root node when merging collections, there is also a consideration of which tree root to choose as the final root. The factor for this consideration is the average node query time of the final tree. That is, the average time for the final merged tree to query the root node of a node.

According to theory and experiment (directly say the conclusion)Letting the root of the tree with more nodes as the final root can result in a shorter final average query time

This optimization scheme is manifested in the method (merge method):

	class UnionSet{
        private int [] fa;
        private int [] size;
        private int n;

        public UnionSet(int n){
            this.n = n;
            fa = new int [n+1]; 
            size = new int [n+1]; 
            //n+1的意义是n指代0~n个,如果n的意义是n个,可以不加1
            for(int i = 0;i<= n;i++){
                fa [i] = i;           //对各自根(父节点)进行初始化
                size[i] = 1;          //对当前树高进行记录
            }
        }

        public int find(int x){       //查找根
            if(fa[x] == x ) return x;
            return find (fa[x] );
        }

        public void merge (int a,int b){
            int ra = find(a);
            int rb = find(b);  

            if(ra == rb ) return;  //相同根不需要合并

            if(size[ra]<size[rb]){        //如果B树更大
                fa[ra] = rb;              //A接入B树下 
                size[rb] += size[ra];     
            }else{
                fa[rb] = ra;              //B接入A树下 
                size[ra] += size[rb];   
            }     
        }
    }

PathCompression-Quick-Union Algorithm Path Compression Optimization

The merging of trees is an optimization strategy, but the tree structure itself can also be optimized, because the basis for judging whether the same tree is only based on the root, and the main factor affecting performance (average search times) is the tree height , so if you canlower the tree height, then the performance improves.

This optimized solution is expressed in the method (find method):

        public int find(int x){       //查找根
            if(fa[x] == x ) return x;

            int root = find (fa[x] ); //将节点接到根上
            fa[x] = root;
            return root;
        }

performance comparison

These two optimization schemes can actually coexist because they act on different methods. In the comparison in class, the performance of double optimization and any single optimization strategy is close , but both are better than the coloring method ( Quick-find), the coloring rule is better than Quick-Union without optimization

But when programming, the improved code of the path compression algorithm is very simple, so it is very suitable for use in rapid development (competition)
(this is also used as a tool class in the exercises below)

Example:

	class UnionSet{
        private int [] fa;
        private int n;

        public UnionSet(int n){
            this.n = n;
            fa = new int [n+1];  
            //n+1的意义是n指代0~n个,如果n的意义是n个,可以不加1
            for(int i = 0;i<= n;i++){
                fa [i] = i;           //对各自根(父节点)进行初始化
            }
        }

        public int find(int x){       //查找根
            if(fa[x] == x ) return x;

            int root = find (fa[x] ); //将节点接到根上
            fa[x] = root;
            return root;
        }

        public void merge (int a,int b){
            int ra = find(a);
            int rb = find(b);  

            if(ra == rb ) return;  //相同根不需要合并
            fa[ra] = rb;              //A接入B树下    
        }
    }

Basic topic

LeetCode-547. Number of Provinces

Link: https://leetcode-cn.com/problems/number-of-provinces

There are n cities, some of which are connected to each other and some of which are not. If city a is directly connected to city b, and city b is directly connected to city c, then city a is indirectly connected to city c.

A province is a group of directly or indirectly connected cities that does not contain other cities that are not connected.

Give you an nxn matrix isConnected, where isConnected[i][j] = 1 means that the i-th city is directly connected to the j-th city, and isConnected[i][j] = 0 means that the two are not directly connected.

Returns the number of provinces in the matrix.

Example 1:
figure 1

输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2

problem solving ideas

This question is a simple question, which is not worth describing in terms of difficulty, but it can be used to demonstrate PC-Quick-Union (path compression and search algorithm).

The idea of ​​solving this question is to define connected cities as provinces, and different provinces are not connected, so provinces are collections, and this question isAsk for several connected sets

sample code

(demonstrates the simple use of UnionSet)

class Solution {
    public int findCircleNum(int[][] isConnected) {
        int n = isConnected.length;
        UnionSet US = new UnionSet(n);

        for(int i = 0;i<n;i++){
            for(int j= 0;j<n;j++){
                if(isConnected [i][j] ==1){
                    US.merge(i,j);
                }
            }
        }

        int res = 0;
        for(int i = 0;i<n;i++){
            int root = US.find(i);
            if(root == i){
                res++;
            }
        }

        return res;
    }

    class UnionSet{
        private int [] fa;
        private int n;

        public UnionSet(int n){
            this.n = n;
            fa = new int [n+1]; 
            //n+1的意义是n指代0~n个,如果n的意义是n个,可以不加1
            for(int i = 0;i<= n;i++){
                fa [i] = i;           //对各自根(父节点)进行初始化
            }
        }

        public int find(int x){       //查找根
            if(fa[x] == x ) return x;

            int root = find (fa[x] ); //将节点接到根上
            fa[x] = root;
            return root;
        }

        public void merge (int a,int b){
            int ra = find(a);
            int rb = find(b);  

            if(ra == rb ) return;  //相同根不需要合并
            fa[ra] = rb;              //A接入B树下    
        }
    }
}

LeetCode-200. Number of islands

Link: https://leetcode-cn.com/problems/number-of-islands

Given a 2D grid consisting of '1' (land) and '0' (water), please count the number of islands in the grid.

Islands are always surrounded by water, and each island can only be formed by connecting horizontally and/or vertically adjacent land.

Also, you can assume that the mesh is surrounded by water on all four sides.

Example 1:

输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1

problem solving ideas

The difficulty of this question is to record the Unicom relationship. For this, the islands can be labeled, and then circularly query whether the island is connected to other islands, thereby generating a 1-to-1 Unicom relationship, and then use the merge method to search and collect according to this relationship, and finallyStatistics have several collectionsThat's it
(code omitted)

LeetCode-990. Equation Satisfiability (Interesting)

Link: https://leetcode-cn.com/problems/satisfiability-of-equality-equations

Given an array of string equations representing relationships between variables, each string equation equations[i] has length 4 and takes one of two different forms: "a==b" or "a !=b". Here, a and b are lowercase letters (not necessarily different), denoting single-letter variable names.

Returns true only if an integer can be assigned to the variable name such that all given equations are satisfied, false otherwise.

Example 1:

输入:["a==b","b!=a"]
输出:false
解释:如果我们指定,a = 1 且 b = 1,那么可以满足第一个方程,但无法满足第二个方程。没有办法分配变量同时满足这两个方程。

Example 2:

输入:["b==a","a==b"]
输出:true
解释:我们可以指定 a = 1 且 b = 1 以满足满足这两个方程。

Example 3:

输入:["a==b","b==c","a==c"]
输出:true

Example 4:

输入:["a==b","b!=c","c==a"]
输出:false

Example 5:

输入:["c==c","b==d","x!=z"]
输出:true

hint:

1. 1 <= equations.length <= 500
2. equations[i].length == 4
3. equations[i][0] 和 equations[i][3] 是小写字母
4. equations[i][1] 要么是 '=',要么是 '!'
5. equations[i][2] 是 '='

problem solving ideas

The difficulty of this question is to find contradictions, so first, according to the equality relationship, merge the sets of equal letters, and then use the unequal statement to judge whether they are really unequal (do not belong to the same set), if the plaintext of the equation is found to be Not equal by definition, but actually equal is considered a contradiction and returns false. The rest of the cases return true;
the local inspection isDetermine whether a set of elements belong to the same setThe way;

(code omitted)

Summarize

Through the above three questions, I have a preliminary understanding of the role of union search in solving problems:
as a tool for dealing with connectivity relationships

And look up advanced

LeetCode-684. Redundant connections (interesting)

Link: https://leetcode-cn.com/problems/redundant-connection

In this problem, a tree refers to an undirected graph that is connected and acyclic.

Input a graph consisting of a tree with N nodes (node ​​values ​​are not repeated 1, 2, ..., N) and an additional edge. The two vertices of the additional edge are included between 1 and N, and this additional edge does not belong to the existing edges in the tree.

The resulting graph is a two-dimensional array of edges. The element of each edge is a pair [u, v], which satisfies u < v, and represents the edge of the undirected graph connecting vertices u and v.

Returns an edge that can be deleted such that the resulting graph is a tree with N nodes. If there is more than one answer, the last occurrence of the edge in the 2D array is returned. The answer edge [u, v] should satisfy the same format u < v.

Example 1:

输入: [[1,2], [1,3], [2,3]]
输出: [2,3]
解释: 给定的无向图为:
  1
 / \
2 - 3

Example 2:

输入: [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
解释: 给定的无向图为:
5 - 1 - 2
    |   |
    4 - 3

problem solving ideas

The starting point of this question is to use the additional edge. In this question, having an edge means that the two sets are connected. When the two sets are already connected, the edge you are trying to add at this time is redundant. Since the meaning of the question indicates that the last edge needs to be output, it is only necessary to judge when trying to establish a new connection relationshipWhether it is already a member of the same collectionThat is, if there is, prepare the output, and overwrite the last attempt to output the value

sample code

(still very simple)

class Solution {
    public int[] findRedundantConnection(int[][] edges) {
        int size = edges.length;

        UnionSet US = new UnionSet(size);

        for(int i= 0;i<size;i++){
            int a = edges[i] [0];
            int b = edges[i] [1];

            if( US.find(a)!= US.find(b)){
                US.merge(a,b);
            }else{
                return new int [] {a,b};
            }
               
        } 
        return null;
    }

    class UnionSet{
        private int [] fa;
        private int n;

        public UnionSet(int n){
            this.n = n;
            fa = new int [n+1];  
            //n+1的意义是n指代0~n个,如果n的意义是n个,可以不加1
            for(int i = 0;i<= n;i++){
                fa [i] = i;           //对各自根(父节点)进行初始化
            }
        }

        public int find(int x){       //查找根
            if(fa[x] == x ) return x;

            int root = find (fa[x] ); //将节点接到根上
            fa[x] = root;
            return root;
        }

        public void merge (int a,int b){
            int ra = find(a);
            int rb = find(b);  

            if(ra == rb ) return;  //相同根不需要合并
            fa[ra] = rb;              //A接入B树下    
        }
    }
}

LeetCode-1319. The number of operations connected to the network (interesting)

Link: https://leetcode-cn.com/problems/number-of-operations-to-make-network-connected

Use Ethernet cables to connect n computers into a network, and the numbers of the computers are from 0 to n-1. A cable is denoted by connections, where connections[i] = [a, b] connects computers a and b.

Any computer in the network can directly or indirectly access any other computer in the same network through the network.

Given your initial wiring connections for this computer network, you can unplug the cable between any two directly connected computers and use it to connect a pair of computers that are not directly connected. Please calculate and return the minimum number of operations required to connect all computers. Returns -1 if not possible.

Example 2:

Example image 2

输入:n = 6, connections = [[0,1],[0,2],[0,3],[1,2],[1,3]]
输出:2

problem solving ideas

This question is very simple.
The first is to count how many sets there are, then you can calculate the minimum number of lines that make up the current set, which should be the total number a minus the number b (ab) of sets.
Then consider how many lines there are currently, and whether extra lines can be provided for the Unicom set. Assuming that the current line is C, the minimum line used to connect all the sets is (a-1), if c can be no less than (a -1), then the required bus minus the current minimum required bus is to change the number of lines required ((a-1)-(ab)), and thus only need to calculateThere are several collectionsThat's it.
(code omitted)

LeetCode-128. Longest Consecutive Sequence (Interesting)

Link: https://leetcode-cn.com/problems/longest-consecutive-sequence

Given an unsorted integer array nums, find the length of the longest sequence of consecutive numbers (it is not required that the elements of the sequence are consecutive in the original array).

Advanced: Can you design and implement a solution with time complexity O(n)?

Example 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

Example 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

problem solving ideas

The simplest solution for this question is to sort directly and then traverse, but this certainly does not meet the advanced requirements of time complexity.
Therefore, if you want to optimize the time complexity, you can only exchange space for time, and you can only solve the problem with a fixed number of traversals. For this, you must think of a storage strategy with a retrieval efficiency of 1, hashMap.

Then, the concept conversion of the topic is required to find a sequence of consecutive numbers, so it is understood that the sequence of numbers is a connection rule, so a certain number can be generated to expect to connect with a smaller number, and to expect to be connected with a larger number The two query conditions of the number of China Unicom, if there is China Unicom, if not, wait for China Unicom.

To sum up, the idea of ​​solving the problem is to store the subscript and value of the array into the Map as a key-value pair relationship, and require the key to be the value of the array, so as to obtain the reverse corresponding relationship. Then traverse once first, each time expecting to find the previous and next digits in the Map for collection merging, if not found, store them in the Map.

After such a round, the union search set will contain several sets

Then query and check the maximum collection length in the collection class. This question requires a fine-tuning of the tool class so that it can be calculatedMaximum collection length

sample code

class Solution {
    public int longestConsecutive(int[] nums) {

        int len = nums.length;

        UnionSet US = new UnionSet(len);
        HashMap <Integer,Integer> map = new HashMap <Integer,Integer> ();

        for(int i=0;i<len;i++){
            if(map.get(nums[i])!=null)
                continue;

            map.put(nums[i],i);  //存入

            Integer wantLeft =  map.get(nums[i]-1);
            Integer wantRight =  map.get(nums[i]+1);

            if(wantLeft!=null){ //如果有则联通
                US.merge(wantLeft,i);
            }
            if(wantRight!=null){ //如果有则联通
                US.merge(wantRight,i);
            }


        }

        int res = 0;
        for(int i=0;i<len;i++){
            if(US.find(i) == i){
                res = res>US.getSize(i)?res:US.getSize(i);
            }
        }

        return res;
    }

    class UnionSet{
        private int [] fa;
        private int [] size;
        private int n;

        public UnionSet(int n){
            this.n = n;
            fa = new int [n+1];  
            size = new int [n+1];  
            //n+1的意义是n指代0~n个,如果n的意义是n个,可以不加1
            for(int i = 0;i<= n;i++){
                fa [i] = i;           //对各自根(父节点)进行初始化
                size [i] = 1;
            }
        }

        public int find(int x){       //查找根
            if(fa[x] == x ) return x;

            int root = find (fa[x] ); //将节点接到根上
            fa[x] = root;
            return root;
        }

        public int getSize(int x){       //查找大小
            return size [x];
        }

        public void merge (int a,int b){
            int ra = find(a);
            int rb = find(b);  

            if(ra == rb ) return;  //相同根不需要合并
            fa[ra] = rb;              //A接入B树下  

            size [rb] += size [ra] ; 
        }
    }
}

LeetCode-947. Remove the most stones in the same row or column (interesting) (no)

Link: https://leetcode-cn.com/problems/most-stones-removed-with-same-row-or-column

n stones are placed at some integer coordinate points in a 2D plane. There can only be at most one stone on each coordinate point.

If there are other stones in the same row or column of a stone, then the stone can be removed.

Given an array stones of length n, where stones[i] = [xi, yi] represents the position of the i-th stone, return the maximum number of stones that can be removed.

Example 1:

输入:stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]
输出:5
解释:一种移除 5 块石头的方法如下所示:
1. 移除石头 [2,2] ,因为它和 [2,1] 同行。
2. 移除石头 [2,1] ,因为它和 [0,1] 同列。
3. 移除石头 [1,2] ,因为它和 [1,0] 同行。
4. 移除石头 [1,0] ,因为它和 [0,0] 同列。
5. 移除石头 [0,1] ,因为它和 [0,0] 同行。
石头 [0,0] 不能移除,因为它没有与另一块石头同行/列。

Example 2:

输入:stones = [[0,0],[0,2],[1,1],[2,0],[2,2]]
输出:3
解释:一种移除 3 块石头的方法如下所示:
1. 移除石头 [2,2] ,因为它和 [2,0] 同行。
2. 移除石头 [2,0] ,因为它和 [0,0] 同列。
3. 移除石头 [0,2] ,因为它和 [0,0] 同行。
石头 [0,0] 和 [1,1] 不能移除,因为它们没有与另一块石头同行/列。

problem solving ideas

(Not because I didn't think of it at the first time)

This question is still a question of Unicom, and the rules of Unicom are a pair of stones in the same row or in the same row.
After understanding it as a Unicom problem, the first thing to do in this question is to generate a set of Unicom relationship tables based on the array of stones, and perform a merge query to obtain several sets.

Then there is a key concept, how many stones can be taken away in a connected set?
It can be demonstrated that the total number can be taken minus one stone (size-1)
, then how many stones can be taken away by several sets must be the total number minusNumber of collectionsThe stone (size-n)

(This question is the same thinking as the previous question on computer connection, the code is omitted)

Briefly describe the key argument: a collection must be able to take away the total minus one stone (size-1)

Any element that can be added to the set is entered into the set because it is in the same horizontal or vertical direction, so it can be kicked out in reverse, and at the same time, in order to ensure that it will not be added again, any stone in any position is taken away, so A few elements can be added and finally a few elements can be kicked out, and the total is 1+a (a indicates the number of additions)

LeetCode-1202. Swap elements in a string

Link: https://leetcode-cn.com/problems/smallest-string-with-swaps

You are given a string s, and some array pairs of "index pairs" in the string, where pairs[i] = [a, b] represent two indices in the string (numbering starts from 0).

You can swap the characters at any pair of indices in pairs any number of times.

Returns the lexicographically smallest string that s can become after a number of swaps.

Example 1:

输入:s = "dcab", pairs = [[0,3],[1,2],[0,2]]
输出:"abcd"
解释:
交换 s[0] 和 s[3], s = "bcad"
交换 s[0] 和 s[2], s = "acbd"
交换 s[1] 和 s[2], s = "abcd"

problem solving ideas

This topic is stillconnectivity issues, the index pair is the connection condition, and finally sort the elements in a collection, and then output the sorted results.

This question can be done as an advanced question, and it is required to return the number of exchanges when the string with the smallest lexicographical order is returned. The
difficulty should not increase.

But it is also not conducive to better learning and collection, so the code for solving this question is abbreviated

(I think repeated use of tool classes, but there is no need to adapt to the requirements, there is no room for improvement)

LeetCode-721. Account Consolidation

Link: https://leetcode-cn.com/problems/accounts-merge

Given a list accounts, each element accounts[i]is a list of strings, the first element accounts[i][0]is the name (name), and the remaining elements are emails, representing the email address of the account.

Now, we want to merge these accounts. If both accounts have some email addresses in common, then both accounts must belong to the same person. Note that even if two accounts have the same name, they may belong to different people because people may have the same name. A person can initially have any number of accounts, but all of their accounts have the same name.

After merging the accounts, returns the accounts in the following format: the first element of each account is the name, and the remaining elements are the email addresses in character ASCII order. The accounts themselves can be returned in any order.

Example 1:

输入:
accounts = [["John", "[email protected]", "[email protected]"], ["John", "[email protected]"], ["John", "[email protected]", "[email protected]"], ["Mary", "[email protected]"]]
输出:
[["John", '[email protected]', '[email protected]', '[email protected]'],  ["John", "[email protected]"], ["Mary", "[email protected]"]]
解释:
第一个和第三个 John 是同一个人,因为他们有共同的邮箱地址 "[email protected]"。 
第二个 John 和 Mary 是不同的人,因为他们的邮箱地址没有被其他帐户使用。
可以以任何顺序返回这些列表,例如答案 [['Mary','[email protected]'],['John','[email protected]'],
['John','[email protected]','[email protected]','[email protected]']] 也是正确的。

problem solving ideas

First of all, it needs to be assumed that the names of the same accounts are the same. This is not mentioned in the question stem. The concept of merge search in this question is still the same. What needs to be merged is the address of the array, which is the subscript of the acoustic array. Use to represent this set of mailbox relationships. Then, according to whether the same mailbox is held in different mailbox relationships, it is judged whether the collection can be merged.
After the merger, thewithin the same setAll the mailboxes of are exported by name. (The name of the same account needs to have the same definition here, which is the default if I didn’t see it in the title)

(The code is omitted, it is still repeated simple application)

Summarize

In the process of solving these problems, the function of combining and searching is still used, but some problems will fine-tune this tool class, which is the flexible use of tools. In actual use, there will be many changes, and this solution needs to be used flexibly.
The most used functions in the above topic are:

  • Merge into sets, and finally judge the status of each set
  • Determine whether two elements can be merged into a set, and determine whether two elements belong to a set

In addition, coding skills are taught in the class. The coding must be hierarchical, and this part can only be watched along with the video. The language description is the hierarchical design module, and it is compiled. It is best to retain the ability of staged debugging in any development.

Additional optional questions

LeetCode-765. Couple holding hands (interesting)

N pairs of lovers sit on 2N seats arranged in a row and want to hold each other's hand. Calculate the minimum number of seat swaps so that each couple can sit side by side. A swap selects any two people and asks them to stand up and swap seats.

People and seats are represented by integers from 0 to 2N-1, lovers are numbered sequentially, the first pair is (0, 1), the second pair is (2, 3), and so on, the last pair is (2N- 2, 2N-1).

The initial seat row[i] of these couples is determined by the person who initially sat in the i-th seat.

Example 1:

输入: row = [0, 2, 1, 3]
输出: 1
解释: 我们只需要交换row[1]和row[2]的位置即可。

Example 2:

输入: row = [3, 2, 0, 1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。

problem solving ideas

This question is very interesting. First of all, we need to clarify a concept.A couple must use the (2i-2, 2i-1) seat as the holding hand position, so a round of traversal can be performed, and the couples who have successfully held hands are eliminated first.

Then all the rest failed to match, so pairwise exchange must be carried out. At this time, two situations will occur. One is that after the exchange, both parties are paired successfully, and the other is that only one pair is successfully paired.

(I guess a question at this time, is it possible that I did not pair successfully in one exchange, but the value of making the next exchange succeed at once) (
Obviously not, because this is still an average of 1 at a time, so there are only these two case)

And if the pairing is successful, it can be removed from the demand pool, so it is possible to achieve double pairing to reduce the number of exchanges.

So2 pairs of seatsSwap is favorable, whether it will occur,Only 3 pairs of seat swaps are requiredWell, it is entirely possible that for example, a needs b, b needs c, and c needs a, so will there be other4 only need 4 pairsWell, that is, if you find these 4 in this group, you can complete the internal exchange.entirely possible

From this, I proposed the concept demand ring . The characteristic of a demand ring is that only the seat exchange in the ring can complete the matching demand, and the number of swaps is the smallest (if there is no limit, then two demand rings can be set into one big ring. , although not actually generated)

The double-pair relationship is a minimal cycle. (In fact, the smallest ring has been paired successfully, and the size of the ring is 1, but in order to simplify the judgment, I define the smallest ring as 2)

And the most permutation times in a ring is the ring element -1

So far, the idea of ​​solving this problem is to find out how many rings there are.and calculate the size of each ring. And this kind of demand should be solved by using union search. The difficulty lies only here.

The code design idea is as follows:

First, traverse all, while traversing the division of seats and try to match them in place. If unsuccessful, store the corresponding relationship in the map. Here, two maps are prepared to represent the left and right correspondence and the right and left correspondence. Relationship, so that any element can find the same group of elements of the currently required element.

Take the arrangement of 0, 2, 1, 3 as an example, 0 is searched for 1, and 3 is obtained in the same group, and 2 is searched to obtain 0 in the same group. The element in the ring is 4, and the minimum exchange for 2 groups is . At this time, the remaining elements (2, 1, 3) do not need to be searched again, because these have formed a ring in the search of 0.

Therefore, the solution to this question only needs to determine whether an element has been searched.

(So ​​far, I have found that it seems that there is no need to perform union search, because it is only necessary to count the traced path and the elements that have been traced)

(In summary, I think it has nothing to do with union check)

problem solving code

(This is my solution, do you have any good solutions?)

class Solution {
    public int minSwapsCouples(int[] row) {
        HashMap<Integer,Integer> LR = new HashMap<Integer,Integer> ();
        HashMap<Integer,Integer> RL = new HashMap<Integer,Integer> ();

        for(int i = 0;i<row.length;i+=2){ //第一轮关系存储
            LR.put(row[i],row[i+1]);
            RL.put(row[i+1],row[i]);
        }

        int res = 0;

        boolean inLoop [] = new boolean [row.length];

        for(int i = 0;i<row.length;i+=2){ //第二轮寻找成环
            if(inLoop[row[i]] == false){
                inLoop[row[i]] = true;         //不重复
                res += getLoopSize(LR,RL,row[i],row[i],inLoop); //从什么开始到什么结束,成环
            }
        }
        return res;
    }

    public int getLoopSize (HashMap<Integer,Integer> LR,
        HashMap<Integer,Integer> RL,int want,int end,
        boolean inLoop []){


        if(want %2==0)                //期望寻找奇数
            want = want +1;
        else
            want = want -1; 

        inLoop[want] = true;   
        
        Integer getR = LR.get(want);
        Integer getL = RL.get(want);

        //System.out.println(getR+""+getL+"end:"+end);

        if(getR!=null){              //标识路劲
            inLoop[getR] = true;
        }else if(getL != null){
            inLoop[getL] = true;
        }

        if((getR!=null&&end == getR)||
            (getL != null&&end == getL))           //找到自己了停下,成环
            return 0;
        
        if(getR!=null){
            return 1+getLoopSize(LR,RL,getR,end,inLoop);
        }else if(getL != null){
            return 1+getLoopSize(LR,RL,getL,end,inLoop);
        }
        
        return 0;
    }
}

LeetCode-685. Redundant Connection II (interesting) (no)

Link: https://leetcode-cn.com/problems/redundant-connection-ii

In this problem, a rooted tree refers to a directed graph that satisfies the following conditions. The tree has only one root node, and all other nodes are successors of this root node.Every node in the tree except the root node has one and only one parent node, and the root node has no parent node

Input a directed graph, which consists of a tree with n nodes (node ​​values ​​are not repeated, from 1 to n) andan additional directed edgeconstitute. An additional edge is included between two different vertices from 1 to n, and this additional edge does not belong to the existing edges in the tree.

The resulting graph is a two-dimensional array of edges. Each element is a pair [ui, vi] denoting an edge connecting a vertex ui to a vertex vi in ​​a directed graph, where ui is a parent node of vi.

Return an edge that can be removed such that the remaining graph is a rooted tree of n nodes. If there are multiple answers, returns the answer that appeared last in the given 2D array.

Example 1:
image 3

输入:edges = [[1,2],[1,3],[2,3]]
输出:[2,3]

Example 2:
Figure 4

输入:edges = [[1,2],[2,3],[3,4],[4,1],[1,5]]
输出:[4,1]

problem solving ideas

(I can’t see it at a glance. This question took me more than 1H, and the rest of the time is mainly codewords)

This question is similar to the redundant connection above the variant question of this question, but there is a key node. This question is a directed graph, so generating the root node will become more difficult.
But is it really that hard?

First, modify the tool class to an algorithm without any optimization, so that the splicing relationship of the tree structure can be defined when merging, so
I will get a solution that can also determine whether the same collection is set, and when I try to add a relationship, if The two nodes of this relationship already exist in the collection, it is not allowed to add and return.

(However, the topic description has to return the answer that appeared last in the given two-dimensional array, so it needs to be traversed, and every time there is a new return requirement, just overwrite the last value to be returned)

(So ​​far I guess there is no difficulty in this question, let me try programming)

(It failed, the key is that the definition of tree in this question is a normal tree structure, that is, the structure in which the root node points to the leaf node)

Therefore, the thinking is reversed. Since the title stem is described as a tree with several sub-nodes, an anti-tree with the same degree and the opposite direction is made to obtain the root node .

First of all, when forming a relationship,A node should not be pointed to by multiple node nodes, that is to form and check in the forward direction first. Judging that the two can no longer be in the same set, if they appear, it means that the current tree structure is wrong. But the problem with such a design is how to know whether a leaf node has been pointed to . For this, each node can be marked. When this node is pointed to, then the root node of this node must not be itself .

Therefore, it is necessary to build an anti-tree (defining the tree whose pointing relationship conforms to the array pair, it is a positive tree, otherwise it is an anti-tree), this tree points from the child node to the root node,When it is found that the root node of a pointed node is not itself, then this node must have experienced the state of two parent nodes. Therefore, the edge at this time should be deleted

But how to discard one of them, one solution for this is to find the degree (array) of the pair of redirections and thenAssume that one of theObserve whether the status can run normally, if not, replace it.

(The loophole of this idea is, will there be multiple redirected different edges in this question? If so, this idea cannot be used) (
I think it must be done when trying because the conflicting edge may appear in the early stage, and it is decided to keep the edge settings are generated later)

At this time, it is assumed that when the successful tree is finally generated, it should be that the root nodes of all nodes in the anti-tree finally point to a target node.

Due to the description of the question stem: an additional edge , the answer is only generated in two possibilities, so you only need to discard the new edge and try to build it when you encounter a conflict. If the construction fails, you should discard the other one.

Then it is equivalent to two conditions in the judgment, not only requiring that no loop can be formed, but also that a node cannot be pointed to by multiple nodes.

When a node is pointed to by multiple nodes, the answer is found in the two edges pointing to this node. When there is a loop, the answer is found in the formation of the ring.

Therefore, when a fault occurs for the first time, the source of the fault can be distinguished first, and it can be clearly defined,The other edges not related to the fault are correct

Therefore, we should make use of this rule. When it is a double-pointing error, skip the judgment condition and continue to build. If the build is successful, it means that the skipped one is redundant, otherwise it is another one. When facing a looping error, it should be skipped If the current side is built with the next side first, if an error still occurs, it means that the current side should not be skipped, and what needs to be discarded are other factors that form a loop. For this, adjust the overall structure to give priority to the correct part in the back. The method is used to re-determine, and finally it is determined that the redundant edge is the desired result.

Since this is discussed further,Assuming that the first fault that occurs is a looping fault, what will the second fault be, the second fault may not occur or be bidirectional . When it does not occur, the last origin of the first fault is the edge that can be discarded. When it happens, why is it bidirectional?

This is because although it is possible to form a loop again, since there is only one faulty side, there must be a common side for the two loops to form a loop, and a double loop with a common side will definitely appear bidirectional .

At this point, the problem can be found. If there is a double fault in this question, there must be a double point, and the double point can lock the answer in 2.

  • When the first failure is bidirectional, you can choose to discard one and continue to build to judge whether the discarded one is correct
  • When the first fault is a ring, there must be no wrong edge outside the ring, so when the inner edge of the ring and the outer edge of the ring form a bidirectional direction, the inner edge of the ring can be discarded.

(I have finished my guess at this point, but there is still a loophole. Is it possible that although the continued construction is successful, but the deleted edge is wrong, I don’t think so)

(successful, comfortable)

problem solving code

class Solution {

    public int[] findRedundantDirectedConnection(int[][] edges) {
        int n = edges.length;
        UnionSet US = new UnionSet (n);
        return findRedundantDirectedConnection(edges,US);
    }
    public int[] findRedundantDirectedConnection(int[][] edges,UnionSet US) {
        int n = edges.length;

        int res [] = new int [2];
        int anotherRes [] = new int [2];

        boolean checkLine = false;       //判定边是否需要被再允许添加
        boolean loopFlag  = false;       //表示是否是成环故障

        for(int i=0;i<n;i++){
            int fa = US.find(edges[i][0]);
            int fb = US.find(edges[i][1]);


            if(fa!=fb&&fb == edges[i][1]){         //首先要求反树,被指向的目标只被指向一次
                                                   //并且还能符合联通集合的要求允许联通
                US.merge(edges[i][1],edges[i][0]); //那么加入反树的枝干中,指向根节点
            }else{
                if(checkLine){                      //再次失败
                    if(!loopFlag)
                        return anotherRes;
                    else{
                        //成环时二次错误,则舍弃环内的双指向
                        //意味着有一组已经进行了指向,两者必须舍弃一个
                        res [1] =  edges[i][1];

                        System.out.println(res [0]+"+"+res [1]);

                        //找到前一个矛盾点
                        for(int j = 0;j<i;j++){
                            if(edges[i][1] == edges[j][1]){
                                anotherRes [0] =  edges[j][0];
                                anotherRes [1] =  edges[j][1];
                                return anotherRes;
                            }
                        }
                    }   
                }
                    
                if(fb != edges[i][1]){             //优先考虑双指向的情况
                    if(!checkLine){
                        checkLine = true;
                        //意味着有一组已经进行了指向,两者必须舍弃一个
                        res [0] =  edges[i][0];
                        res [1] =  edges[i][1];

                        System.out.println("line1:"+res [0]+"+"+res [1]);

                        //找到前一个矛盾点
                        for(int j = 0;j<i;j++){
                            if(edges[i][1] == edges[j][1]){
                                anotherRes [0] =  edges[j][0];
                                anotherRes [1] =  edges[j][1];
                                break;
                            }
                        }
                    }
                }else{                          //成环导致故障因此需要舍弃元素必然环的构成
                    if(!checkLine){
                        //成环故障的方案是
                        //优先执行执行其他正确的节点
                        res [0] =  edges[i][0];
                        res [1] =  edges[i][1];
                        loopFlag = true;
                        checkLine = true;
                        System.out.println("line1:"+res [0]+".."+res [1]);
                    }
                }
            }
        }
        return res;
    }

    class UnionSet{
        private int [] fa;
        private int n;

        public UnionSet(int n){
            this.n = n;
            fa = new int [n+1];  
            //n+1的意义是n指代0~n个,如果n的意义是n个,可以不加1
            for(int i = 0;i<= n;i++){
                fa [i] = i;           //对各自根(父节点)进行初始化
            }
        }

        public int find(int x){       //查找根
            if(fa[x] == x ) return x;
            return find(fa[x]);
        }

        public void merge (int a,int b){
            int ra = find(a);
            int rb = find(b);  

            if(ra == rb ) return;  //相同根不需要合并
            fa[ra] = rb;           //改为A的根接B树的根 
        }
    }
}

Easter egg topic //oj.kaikeba.com, 214 Moments topic

epilogue

The concept part is finished in about 40 minutes,
the exercise part is finished in 3 hours,
and the notes are written and released in 7
hours.

Compared with the previous time loss, it should be an improvement. If I didn't freeze on the last question, I should be able to complete the study with approximately 2 times the class hours.

In this lesson, I have fully practiced the function of searching and collecting. If you have any questions about this, or if you have doubts about my problem-solving, you can leave a message in the comment area, thank you.

(1000 blog post plans, progress plus 1, (._ .) ✎_ learning plan to start)

Guess you like

Origin blog.csdn.net/ex_xyz/article/details/117728004