目录
并查集
并查集: 查询两个元素是否处于同一集合、将两个集合合并为一个集合。两个方法时间复杂度都为O(1) 维护三个集合:elementMap fatherMap sizeMap elementMap:用户给的每个数据都包装成element,key是用户给的元素 value是对应包装后的元素对象 fatherMap:每个元素对象指向自己的父 sizeMap:每个代表元素所在的集合有多少个元素
isSameSet:判断用户给的两个元素是否在同一集合(是否有同一代表) 首先判断这两个元素是否注册过,再判断两个元素的代表元素是否是同一个
union:合并两个集合元素 首先判断是否注册过,再拿到这两个元素的代表元素,如果不是同一代表元素就进行合并,把元素个数少的挂在元素个数多的下面(少的代表元素的父改成多的代表元素), 更新sizeMap
findHead:找到给定元素的代表元素(最顶的元素) 如果该元素不等于该元素的父元素,就一直往上找。可以建一个栈用来收集沿途的元素,最后将这些元素的父直接设置为代表元素(扁平化)。
package com.wtp.基础提升.并查集; import java.util.HashMap; import java.util.List; import java.util.Stack; public class UnionFind<V> { public static class Element<V>{ public V value; public Element(V value) { value = this.value; } } private HashMap<V,Element<V>> elementMap; private HashMap<Element<V>,Element<V>> fatherMap; private HashMap<Element<V>,Integer> sizeMap; public UnionFind(List<V> list) { elementMap = new HashMap<>(); fatherMap = new HashMap<>(); sizeMap = new HashMap<>(); for(V v : list) { Element<V> element = new Element<V>(v); elementMap.put(v,element); fatherMap.put(element, element); sizeMap.put(element, 1); } } private Element<V> findHead(Element<V> element) { Stack<Element<V>> stack = new Stack<>(); while(element != fatherMap.get(element)) { stack.push(element); element = fatherMap.get(element); } while(!stack.isEmpty()) { fatherMap.put(stack.pop(), element); } return element; } public boolean isSameSet(V v1,V v2) { if(!elementMap.containsKey(v1) || !elementMap.containsKey(v1)) { return false; } return findHead(elementMap.get(v1)) == findHead(elementMap.get(v1)); } public void union(V v1,V v2) { if(elementMap.containsKey(v1) && elementMap.containsKey(v2)) { Element<V> e1 = findHead(elementMap.get(v1));//默认小 Element<V> e2 = findHead(elementMap.get(v2)); if(e1 != e2) { Element<V> small = sizeMap.get(e1) < sizeMap.get(e2) ? e1 : e2; Element<V> big = small != e1 ? e1 : e2; fatherMap.put(small, big); sizeMap.put(big, sizeMap.get(e1) + sizeMap.get(e2)); sizeMap.remove(small); } } } }
Kmp
kmp的核心在于next数组的求解。有了next数组,就可以在匹配时不相等的情况下回溯。 next数组的值是当前位的前面子串的最长前后缀的长度。 求解当前next数组的值时最好的情况是 next[i - 1] + 1; 求解next数组需要待匹配串的自我匹配,i和k错开一位,如果所在字符相等就赋值next[++i] = ++k;如果不相等就k = next[k];进行回溯。 回溯的原因是需要在前面相等的前后缀中再找 缩短相等前后缀的范围。
package com.wtp.基础提升.Kmp; public class kmp { public static void main(String[] args) { test(); } public static void test() { int maxCount = 500000; int maxLength = 1000; Long start; Long end; Long l1 = 0L; Long l2 = 0L; for(int i = 0;i < maxCount;i++) { String str1 = getRandomStr(maxLength); String str2 = getRandomStr(maxLength); start = System.currentTimeMillis(); int res1 = str1.indexOf(str2); end = System.currentTimeMillis(); l1 += end - start; start = System.currentTimeMillis(); int res2 = kmp(str1,str2); end = System.currentTimeMillis(); l2 += end - start; if(res1 != res2) { System.out.println("error! " + res1 + "!=" + res2); System.out.println(str1); System.out.println(str2); return; } } System.out.println("success!"); System.out.println("系统用时:" + l1 + "毫秒"); System.out.println("kmp用时:" + l2 + "毫秒"); } public static String getRandomStr(int maxLength) { StringBuffer sb = new StringBuffer(); maxLength = (int)(Math.random() * maxLength) + 1; for(int i = 0;i < maxLength;i++) { char c = (char)((int)(Math.random() * 26) + 'a'); sb.append(c); } return sb.toString(); } public static int kmp(String s,String m) { if(s == null || m == null || s.length() < 1 || s.length() < m.length()) { return -1; } char[] str1 = s.toCharArray(); char[] str2 = m.toCharArray(); int p1 = 0; int p2 = 0; int[] next = getNext(m); while(p1 < str1.length && p2 < str2.length) { if(str1[p1] == str2[p2]) { p1++; p2++; }else if(next[p2] == -1) {//如果next数组前面没有相同前缀 p1++; }else { p2 = next[p2]; } } return p2 == str2.length ? p1 - p2 : -1; } public static int[] getNext(String str) { if(str == null || str.length() == 0) { return null; } if(str.length() == 1) { return new int[] {-1}; } int[] next = new int[str.length()]; next[0] = -1; next[1] = 0; char[] chs = str.toCharArray(); int cn = 0; int p = 2; while(p < str.length()) { if(chs[cn] == chs[p - 1]) { next[p++] = ++cn; }else if(cn > 0) { cn = next[cn]; }else { next[p++] = 0; } } return next; } }