├── data └── original.dat ├── imgs ├── qq.jpeg ├── rbt.jpg ├── K-Sort.png ├── algorithm.png ├── k-sort-demo.png └── k-路归并(外)排序.png ├── src ├── main │ └── java │ │ └── com │ │ └── kk │ │ ├── datastructure │ │ ├── skiplist │ │ │ ├── EntryConsumer.java │ │ │ ├── SkipListEntry.java │ │ │ └── SkipList.java │ │ ├── tree2 │ │ │ ├── bst │ │ │ │ ├── BSTNode.java │ │ │ │ └── BST.java │ │ │ ├── avl │ │ │ │ ├── AVLNode.java │ │ │ │ └── AVLTree.java │ │ │ ├── redblack │ │ │ │ ├── DeletePoint.java │ │ │ │ ├── RedBlackNode.java │ │ │ │ └── RedBlackTree.java │ │ │ └── huffman │ │ │ │ ├── Node.java │ │ │ │ ├── HuffmanTree.java │ │ │ │ └── HuffmanCodec.java │ │ ├── bitmap │ │ │ ├── roaring │ │ │ │ ├── container │ │ │ │ │ ├── Container.java │ │ │ │ │ ├── BitmapContainer.java │ │ │ │ │ └── ArrayContainer.java │ │ │ │ ├── util │ │ │ │ │ └── Util.java │ │ │ │ ├── RoaringBitmap.java │ │ │ │ └── ContainersArray.java │ │ │ ├── simple │ │ │ │ └── BitSet.java │ │ │ ├── hll │ │ │ │ └── HyperLogLog.java │ │ │ └── bloom │ │ │ │ └── BloomFilter.java │ │ ├── HashMap.java │ │ ├── heap │ │ │ └── MaxHeap.java │ │ └── SingleLinkedList.java │ │ ├── sort │ │ ├── mem │ │ │ ├── BubbleSort.java │ │ │ ├── InsertSort.java │ │ │ ├── SelectSort.java │ │ │ ├── SortSupport.java │ │ │ ├── ShellSort.java │ │ │ ├── CountSort.java │ │ │ ├── QuickSort.java │ │ │ ├── RadixSort.java │ │ │ ├── BucketSort.java │ │ │ └── MergeSort.java │ │ └── outer │ │ │ ├── LoserTree.java │ │ │ ├── ReplaceSelectionSort.java │ │ │ └── KOuterSort.java │ │ ├── util │ │ └── FileUtil.java │ │ └── str │ │ ├── Sunday.java │ │ ├── KMP.java │ │ └── BoyerMoore.java └── test │ └── java │ └── com │ └── kk │ ├── str │ ├── SundayTest.java │ ├── KMPTest.java │ └── BoyerMooreTest.java │ ├── sort │ ├── outer │ │ ├── ReplaceSelectionSortTest.java │ │ ├── LoserTreeTest.java │ │ └── KOuterSortTest.java │ └── SortTest.java │ ├── util │ ├── PrintHelper.java │ ├── FileUtils.java │ ├── BSTPrinter.java │ ├── AVLPrinter.java │ ├── HuffmanPrinter.java │ └── RedBlackTreePrinter.java │ └── datastructure │ ├── bitmap │ ├── hll │ │ └── HyperLogLogTest.java │ ├── roaring │ │ └── RoaringBitmapTest.java │ └── bloom │ │ └── BloomFilterTest.java │ ├── HashMapTest.java │ ├── tree2 │ ├── huffman │ │ ├── HuffmanTreeTest.java │ │ └── HuffmanCodecTest.java │ ├── bst │ │ └── BSTTest.java │ ├── redblack │ │ └── RedBlackTreeTest.java │ └── avl │ │ └── AVLTreeTest.java │ ├── skiplist │ └── SkipListTest.java │ ├── SingleLinkedListTest.java │ └── heap │ └── MaxHeapTest.java ├── .gitignore ├── README.md └── pom.xml /data/original.dat: -------------------------------------------------------------------------------- 1 | 4223 2 | 546 3 | 34 4 | 12 5 | 5493 6 | 45 7 | 88 8 | 100 9 | 43 -------------------------------------------------------------------------------- /imgs/qq.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeartx/awesome-algorithm/HEAD/imgs/qq.jpeg -------------------------------------------------------------------------------- /imgs/rbt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeartx/awesome-algorithm/HEAD/imgs/rbt.jpg -------------------------------------------------------------------------------- /imgs/K-Sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeartx/awesome-algorithm/HEAD/imgs/K-Sort.png -------------------------------------------------------------------------------- /imgs/algorithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeartx/awesome-algorithm/HEAD/imgs/algorithm.png -------------------------------------------------------------------------------- /imgs/k-sort-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeartx/awesome-algorithm/HEAD/imgs/k-sort-demo.png -------------------------------------------------------------------------------- /imgs/k-路归并(外)排序.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeartx/awesome-algorithm/HEAD/imgs/k-路归并(外)排序.png -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/skiplist/EntryConsumer.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.skiplist; 2 | 3 | /** 4 | * 条目消费者 5 | */ 6 | public interface EntryConsumer { 7 | boolean item(int level, int pos, SkipListEntry entry); 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | [Common] 3 | tmp 4 | 5 | [Mac] 6 | .DS_Store 7 | 8 | [IDE] 9 | .idea 10 | 11 | [Maven] 12 | target 13 | 14 | [Android] 15 | *.iml 16 | build 17 | output 18 | 19 | [Node] 20 | node_modules 21 | 22 | [bower] 23 | bower_components 24 | 25 | [Python] 26 | venv 27 | **chache** 28 | 29 | # 【open】 30 | #doc 31 | release 32 | -------------------------------------------------------------------------------- /src/test/java/com/kk/str/SundayTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.str; 2 | 3 | import org.junit.Test; 4 | 5 | public class SundayTest { 6 | String text="substring searching"; 7 | String pattern="search"; 8 | 9 | @Test 10 | public void findTest(){ 11 | System.out.println("-->" + Sunday.find(text,pattern)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/bst/BSTNode.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.bst; 2 | 3 | /** 4 | * 二叉搜索树节点 5 | */ 6 | public class BSTNode { 7 | public String key; 8 | public Object val; 9 | 10 | public BSTNode left; 11 | public BSTNode right; 12 | 13 | public BSTNode(String key) { 14 | this.key = key; 15 | } 16 | 17 | public BSTNode(String key, Object val) { 18 | this.key = key; 19 | this.val = val; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/kk/str/KMPTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.str; 2 | 3 | import com.kk.util.PrintHelper; 4 | import org.junit.Test; 5 | 6 | public class KMPTest { 7 | String target = "cababacababc"; 8 | String temp = "ababc"; 9 | 10 | @Test 11 | public void calJumpTableTest() { 12 | System.out.println("-->"); 13 | 14 | PrintHelper.printArr(KMP.calJumpTable(temp)); 15 | } 16 | 17 | @Test 18 | public void findTest() { 19 | KMP.find(target, temp); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Awesome Algorithm 解构算法 4 | -- 5 | 以独特的思考、精准的文字、直观的例图剖析编程中最常用、最抽象、最复杂的算法、数据结构、并发编程、一致性安全等问题。 6 | 7 | [完整脑图](http://kjeek.com/algo/awesome-algorithm/) 8 | 9 | ![](imgs/algorithm.png) 10 | 11 | ### 红黑树 12 | 13 | ![](imgs/rbt.jpg) 14 | 15 | ### k-sort 图解排序算法 16 | 17 | - [脑图](https://mm.edrawsoft.cn/wx.html?work_id=83385) 18 | ![](imgs/K-Sort.png) 19 | 20 | - [例图](https://mm.edrawsoft.cn/wx.html?work_id=83385) 21 | ![](imgs/k-sort-demo.png) 22 | 23 | 24 | ### k-路归并(外)排序 25 | 26 | ![](imgs/k-路归并(外)排序.png) 27 | -------------------------------------------------------------------------------- /src/test/java/com/kk/sort/outer/ReplaceSelectionSortTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.outer; 2 | 3 | import com.kk.util.FileUtils; 4 | import org.junit.Test; 5 | 6 | import java.io.IOException; 7 | 8 | public class ReplaceSelectionSortTest { 9 | String original = "data/original.dat"; 10 | ReplaceSelectionSort sorter = new ReplaceSelectionSort(); 11 | 12 | @Test 13 | public void splitToSeqfileTest() throws IOException { 14 | FileUtils.cleanData(); 15 | 16 | sorter.splitToSeqfile(original, null); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/avl/AVLNode.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.avl; 2 | 3 | /** 4 | * 平衡二叉搜索树节点 5 | */ 6 | public class AVLNode { 7 | public String key; 8 | public Object val; 9 | 10 | public AVLNode left; 11 | public AVLNode right; 12 | 13 | // 当前节点深度:表示到最长分支的距离 14 | public int depth; 15 | 16 | public AVLNode(String key) { 17 | this.key = key; 18 | } 19 | 20 | public AVLNode(String key, Object val) { 21 | this.key = key; 22 | this.val = val; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/redblack/DeletePoint.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.redblack; 2 | 3 | /** 4 | * 删除点 5 | */ 6 | public class DeletePoint { 7 | public RedBlackNode parent; 8 | public RedBlackNode del; 9 | public RedBlackNode son; 10 | public boolean isLeft; 11 | 12 | public DeletePoint(RedBlackNode parent, RedBlackNode del, RedBlackNode son, boolean isLeft) { 13 | this.parent = parent; 14 | this.del = del; 15 | this.son = son; 16 | this.isLeft = isLeft; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/bitmap/roaring/container/Container.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.roaring.container; 2 | 3 | /** 4 | * 容器基类 5 | */ 6 | public abstract class Container { 7 | int cardinality; 8 | 9 | public boolean isEmpty() { 10 | return cardinality == 0; 11 | } 12 | 13 | public abstract Container add(char x); 14 | 15 | public abstract Container remove(char x); 16 | 17 | public abstract boolean contains(char x); 18 | 19 | public int getCardinality() { 20 | return cardinality; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/kk/util/PrintHelper.java: -------------------------------------------------------------------------------- 1 | package com.kk.util; 2 | 3 | public class PrintHelper { 4 | 5 | public static void printArr(int[] arr, int start, int end) { 6 | for (int i = start; i < end + 1; i++) { 7 | System.out.print(arr[i]); 8 | System.out.print("\t"); 9 | } 10 | } 11 | 12 | public static void printArr(int[] arr) { 13 | for (int num : arr) { 14 | System.out.print(num); 15 | System.out.print("\t"); 16 | } 17 | 18 | System.out.println(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/bitmap/hll/HyperLogLogTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.hll; 2 | 3 | import org.junit.Test; 4 | 5 | public class HyperLogLogTest { 6 | @Test 7 | public void test() { 8 | HyperLogLog hyperLogLog = new HyperLogLog(); 9 | 10 | hyperLogLog.add("1"); 11 | hyperLogLog.add("2"); 12 | hyperLogLog.add("3"); 13 | hyperLogLog.add("c"); 14 | hyperLogLog.add("c"); 15 | hyperLogLog.add("d"); 16 | 17 | System.out.println("-->" + hyperLogLog.cardinality()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/BubbleSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | /** 4 | * 冒泡排序 5 | */ 6 | public class BubbleSort extends SortSupport { 7 | public static void bubbleSort(int[] arr) { 8 | int len = arr.length; 9 | 10 | for (int i = 0; i < len; i++) { // 外层控制最右侧边界,每次-` 11 | for (int j = 0; j < len - i - 1; j++) { // 内层通过比较、交换,将最值移至上述边界处 12 | // 比较响铃的2个值,大的移至右侧 13 | if (arr[j] > arr[j + 1]) { 14 | swap(arr, j, j + 1); 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/InsertSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | public class InsertSort extends SortSupport { 4 | public static void insertSort(int[] arr) { 5 | int len = arr.length; 6 | 7 | for (int i = 1; i < len; i++) {// 外层循环控制有序边界,[0,i-1]有序 8 | for (int j = i; j >= 1; j--) { // 内循环向前逐渐比较、交换 9 | if (arr[j] > arr[j - 1]) { // 违反规则、往前交换 10 | swap(arr, j, j - 1); 11 | continue; 12 | } 13 | 14 | // 交换插入到正确的位置,结束本次循环 15 | break; 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/SelectSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | /** 4 | * 选择(极值)排序 5 | */ 6 | public class SelectSort extends SortSupport { 7 | public static void selectSort(int[] arr) { 8 | int len = arr.length; 9 | 10 | for (int i = 0; i < len; i++) { // 外循环控制排序边界 11 | int minValId=i; 12 | 13 | for (int j = i+1; j < len; j++) { // 内循环在未排序范围内向右选择最小值 14 | if (arr[minValId]>arr[j]){ 15 | minValId=j; 16 | } 17 | } 18 | 19 | // 将选择出来的最小值交换到以排序边界i 20 | swap(arr,i,minValId); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/HashMapTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure; 2 | 3 | import org.junit.Test; 4 | 5 | public class HashMapTest { 6 | @Test 7 | public void test() { 8 | HashMap hashMap = new HashMap(); 9 | 10 | hashMap.put("lisi", "lisi"); 11 | hashMap.put("wangwu", "wangwu"); 12 | hashMap.put("zhaoliu", "zhaoliu"); 13 | hashMap.put("zhangsan", "zhangsan"); 14 | 15 | System.out.println("get(wangwu)-->" + hashMap.get("wangwu")); 16 | 17 | System.out.println("remove(wangwu)-->" + hashMap.remove("wangwu")); 18 | System.out.println("get(wangwu)-->" + hashMap.get("wangwu")); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/skiplist/SkipListEntry.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.skiplist; 2 | 3 | /** 4 | * 跳表条目 5 | */ 6 | public class SkipListEntry { 7 | // 数据 8 | public String key; 9 | public Integer value; 10 | 11 | // 上下左右指针 12 | public SkipListEntry up; 13 | public SkipListEntry down; 14 | 15 | public SkipListEntry left; 16 | public SkipListEntry right; 17 | 18 | // 负无穷、正无穷作为每层的头、尾节点 19 | public static final String INFINITE_NEGATIVE = "-oo"; 20 | public static final String INFINITE_POSITIVE = "+oo"; 21 | 22 | public SkipListEntry(String key, Integer value) { 23 | this.key = key; 24 | this.value = value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/com/kk/util/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.kk.util; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import java.util.stream.Stream; 8 | 9 | public class FileUtils { 10 | public static void cleanData() throws IOException { 11 | Stream files = Files.list(Paths.get("data/")); 12 | 13 | files.forEach(path -> { 14 | String fileName = path.getFileName().toString(); 15 | 16 | if (!fileName.equals("original.dat")) { 17 | try { 18 | Files.deleteIfExists(Paths.get("data/" + fileName)); 19 | } catch (IOException e) { 20 | e.printStackTrace(); 21 | } 22 | } 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/bitmap/roaring/RoaringBitmapTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.roaring; 2 | 3 | import org.junit.Test; 4 | 5 | 6 | public class RoaringBitmapTest { 7 | RoaringBitmap roaringBitmap = new RoaringBitmap(); 8 | 9 | @Test 10 | public void commonTest() { 11 | roaringBitmap.add(1); 12 | roaringBitmap.add(88); 13 | roaringBitmap.add(250); 14 | roaringBitmap.add(1000); 15 | 16 | System.out.println("contains 88-->" + roaringBitmap.contains(88)); 17 | System.out.println("contains 250-->" + roaringBitmap.contains(250)); 18 | System.out.println("contains 999-->" + roaringBitmap.contains(999)); 19 | 20 | roaringBitmap.remove(88); 21 | System.out.println("\nafter remove 88-->" + roaringBitmap.contains(88)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/tree2/huffman/HuffmanTreeTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.huffman; 2 | 3 | import com.kk.util.HuffmanPrinter; 4 | import org.junit.Test; 5 | 6 | public class HuffmanTreeTest { 7 | HuffmanTree huffmanTree = new HuffmanTree(); 8 | 9 | @Test 10 | public void putTest() { 11 | huffmanTree.put( 12 | new Node("2", 2), 13 | new Node("3", 3), 14 | new Node("6", 6), 15 | new Node("8", 8), 16 | new Node("9", 9) 17 | ); 18 | 19 | System.out.println("huffman tree-->"); 20 | HuffmanPrinter.printNode(huffmanTree.root); 21 | } 22 | 23 | @Test 24 | public void wplTest() { 25 | putTest(); 26 | 27 | System.out.println("wpl-->" + huffmanTree.wpl()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/kk/sort/outer/LoserTreeTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.outer; 2 | 3 | import org.junit.Test; 4 | 5 | public class LoserTreeTest { 6 | LoserTree loserTree = new LoserTree(); 7 | 8 | @Test 9 | public void createTest() { 10 | int[] branches = {10, 9, 20, 6, 12}; 11 | loserTree.create(branches); 12 | 13 | System.out.println(loserTree); 14 | } 15 | 16 | @Test 17 | public void peekTest() { 18 | createTest(); 19 | 20 | System.out.println("-->" + loserTree.peek()); 21 | } 22 | 23 | @Test 24 | public void popTest() { 25 | createTest(); 26 | 27 | System.out.println("pop-->" + loserTree.pop(15)); 28 | System.out.println(loserTree); 29 | 30 | for (int i = 0; i < 6; i++) { 31 | System.out.print(i == 0 ? "" : "," + loserTree.pop()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/huffman/Node.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.huffman; 2 | 3 | public class Node implements Comparable { 4 | public String key; 5 | public int weight; // 权重,或者单词出现的频率 6 | 7 | // 指针 8 | public Node parent; 9 | public Node left; 10 | public Node right; 11 | 12 | 13 | public Node(int weight) { 14 | this.weight = weight; 15 | } 16 | 17 | public Node(String key, int weight) { 18 | this.key = key; 19 | this.weight = weight; 20 | } 21 | 22 | @Override 23 | public int compareTo(Node o) { 24 | return weight - o.weight; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "Node{" + 30 | "key='" + key + '\'' + 31 | ", weight=" + weight + 32 | ", left=" + left + 33 | ", right=" + right + 34 | '}'; 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/SortSupport.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | public class SortSupport { 4 | /** 5 | * 数组元素的交换 6 | */ 7 | protected static void swap(int[] arr, int i, int j) { 8 | int tmp = arr[i]; 9 | arr[i] = arr[j]; 10 | arr[j] = tmp; 11 | } 12 | 13 | /** 14 | * 找到最大值 15 | */ 16 | protected static int max(int[] arr) { 17 | int max = arr[0]; 18 | 19 | for (int i = 0; i < arr.length; i++) { 20 | if (arr[i] > max) { 21 | max = arr[i]; 22 | } 23 | } 24 | 25 | return max; 26 | } 27 | 28 | /** 29 | * 找到最小值 30 | */ 31 | protected static int min(int[] arr) { 32 | int min = arr[0]; 33 | 34 | for (int i = 0; i < arr.length; i++) { 35 | if (arr[i] < min) { 36 | min = arr[i]; 37 | } 38 | } 39 | 40 | return min; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/ShellSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | /** 4 | * 希尔排序 5 | * 4层循环 6 | * gap 控制步长 7 | * i 控制子序列左边界 8 | * j 插入排序外循环控制排序边界 9 | * k 插入排序内循环向前比较、交换 10 | */ 11 | public class ShellSort extends SortSupport { 12 | public static void shellSort(int[] arr) { 13 | int len = arr.length; 14 | 15 | for (int gap = len / 2; gap > 0; gap /= 2) { // 控制递减间隙 16 | for (int i = 0; i < gap; i++) { // 控制每个子序列的起始位置 17 | 18 | /** 19 | * 对子序列进行普通的插入排序 20 | */ 21 | for (int j = i; j < len; j += gap) { // 控制为排序的范围 22 | for (int k = j; k >= gap; k -= gap) { // 向前比较、交换 23 | if (arr[k] > arr[k - gap]) { // 违反规则、需要交换 24 | swap(arr, k, k - gap); 25 | 26 | continue; // 继续交换 27 | } 28 | break; 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/skiplist/SkipListTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.skiplist; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | public class SkipListTest { 8 | SkipList skipList = new SkipList(); 9 | 10 | @Before 11 | public void printBefore() { 12 | System.out.println("before-->"); 13 | System.out.println(skipList); 14 | 15 | putTest(); 16 | } 17 | 18 | @After 19 | public void printAfter() { 20 | System.out.println("after-->"); 21 | System.out.println(skipList); 22 | } 23 | 24 | @Test 25 | public void putTest() { 26 | skipList.put("10", 110); 27 | skipList.put("8", 18); 28 | skipList.put("6", 16); 29 | skipList.put("4", 14); 30 | } 31 | 32 | @Test 33 | public void getTest() { 34 | System.out.println("get(8)-->" + skipList.get("8")); 35 | } 36 | 37 | @Test 38 | public void removeTest() { 39 | skipList.remove("8"); 40 | 41 | getTest(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/tree2/huffman/HuffmanCodecTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.huffman; 2 | 3 | import com.kk.util.HuffmanPrinter; 4 | import org.junit.Test; 5 | 6 | public class HuffmanCodecTest { 7 | HuffmanCodec huffmanCodec = new HuffmanCodec(); 8 | 9 | String plain = "we will we will r u"; 10 | 11 | @Test 12 | public void putTest() { 13 | huffmanCodec.put( 14 | new Node("2", 2), 15 | new Node("3", 3), 16 | new Node("6", 6), 17 | new Node("8", 8), 18 | new Node("9", 9) 19 | ); 20 | 21 | System.out.println("huffman tree-->"); 22 | HuffmanPrinter.printNode(huffmanCodec.root); 23 | } 24 | 25 | @Test 26 | public void encodeTest() { 27 | System.out.println("encodes-->" + huffmanCodec.encode(plain)); 28 | HuffmanPrinter.printNode(huffmanCodec.root); 29 | } 30 | 31 | @Test 32 | public void codesMapTest() { 33 | putTest(); 34 | 35 | System.out.println("-->" + huffmanCodec.codesMap()); 36 | ; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/kk/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.kk.util; 2 | 3 | import java.io.*; 4 | import java.nio.file.Files; 5 | import java.nio.file.Paths; 6 | import java.util.stream.Stream; 7 | 8 | public class FileUtil { 9 | public static long countLines(String file) { 10 | Stream lines = null; 11 | try { 12 | lines = Files.lines(Paths.get(file)); 13 | } catch (IOException e) { 14 | e.printStackTrace(); 15 | return -1; 16 | } 17 | return lines.count(); 18 | } 19 | 20 | public static BufferedReader bufferedReader(String file) throws FileNotFoundException { 21 | return new BufferedReader(new InputStreamReader(new FileInputStream(file))); 22 | } 23 | 24 | public static void closeWriter(PrintWriter writer) { 25 | if (writer != null) { 26 | writer.flush(); 27 | writer.close(); 28 | } 29 | } 30 | 31 | public static void closeReader(Reader reader) { 32 | try { 33 | if (reader != null) reader.close(); 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/kk/sort/outer/KOuterSortTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.outer; 2 | 3 | import com.kk.util.FileUtils; 4 | import org.junit.Test; 5 | 6 | import java.io.FileNotFoundException; 7 | import java.io.IOException; 8 | import java.io.PrintWriter; 9 | import java.util.Random; 10 | 11 | public class KOuterSortTest { 12 | String original = "data/original.dat"; 13 | 14 | KOuterSort kOuterSort = new KOuterSort(); 15 | 16 | @Test 17 | public void genTestData() throws FileNotFoundException { 18 | Random random = new Random(); 19 | PrintWriter writer = new PrintWriter(original); 20 | 21 | for (int i = 0; i < 100 * 1000; i++) { 22 | writer.println(random.nextInt(Integer.MAX_VALUE)); 23 | } 24 | } 25 | 26 | @Test 27 | public void nextMergeFileTest() throws IOException { 28 | System.out.println("-->" + kOuterSort.nextMergeFile("ori-seq-0")); 29 | System.out.println("-->" + kOuterSort.nextMergeFile("ori-merge-0")); 30 | } 31 | 32 | @Test 33 | public void sortTest() throws IOException { 34 | FileUtils.cleanData(); 35 | 36 | kOuterSort.sort(original); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/kk/str/BoyerMooreTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.str; 2 | 3 | import com.kk.util.PrintHelper; 4 | import org.junit.Test; 5 | 6 | public class BoyerMooreTest { 7 | String target="HERE IS A SIMPLE EXAMPLE"; 8 | String template = "EXAMPLE"; 9 | String template2 = "EXAMPXA"; 10 | 11 | @Test 12 | public void findTest(){ 13 | System.out.println("-->" + BoyerMoore.find(target,template));; 14 | } 15 | 16 | @Test 17 | public void preBadCharTableTest() { 18 | int[] badCharsTable = BoyerMoore.preBadCharsTable(template); 19 | System.out.println("-->"); 20 | PrintHelper.printArr(badCharsTable); 21 | } 22 | 23 | @Test 24 | public void suffixesTest() { 25 | int[] suffixes = BoyerMoore.suffixes(template2); 26 | 27 | System.out.println("-->"); 28 | PrintHelper.printArr(suffixes); 29 | } 30 | 31 | @Test 32 | public void preGoodSuffixesTest() { 33 | int[] suffixes = BoyerMoore.suffixes(template2); 34 | int[] goodSuffixes = BoyerMoore.preGoodSuffixes(template2, suffixes); 35 | 36 | System.out.println("-->"); 37 | PrintHelper.printArr(goodSuffixes); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/SingleLinkedListTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | /** 8 | * 单向链表 9 | */ 10 | 11 | public class SingleLinkedListTest { 12 | SingleLinkedList linkedList = new SingleLinkedList(); 13 | 14 | @Before 15 | public void init() { 16 | linkedList.add(1).add(2).add(5).add(8).add(10); 17 | System.out.println("init-->" + linkedList); 18 | } 19 | 20 | @After 21 | public void after() { 22 | System.out.println("after-->" + linkedList); 23 | } 24 | 25 | @Test 26 | public void addTest() { 27 | linkedList.add(7).add(8); 28 | } 29 | 30 | @Test 31 | public void insertTest() { 32 | linkedList.insert(2, 8) 33 | .insert(1, 10); 34 | } 35 | 36 | @Test 37 | public void deleteTest() { 38 | linkedList.delete(2); 39 | } 40 | 41 | @Test 42 | public void getTest() { 43 | System.out.println("get(2)-->" + linkedList.get(2)); 44 | } 45 | 46 | @Test 47 | public void findTest(){ 48 | System.out.println("find(5)-->" + linkedList.find(5).val); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/CountSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | /** 4 | * 计数排序 5 | */ 6 | public class CountSort { 7 | public static void countSort(int[] arr) { 8 | // 找到极值 9 | int min = arr[0]; 10 | int max = arr[0]; 11 | 12 | for (int i = 0; i < arr.length; i++) { 13 | if (arr[i] > max) { 14 | max = arr[i]; 15 | } 16 | 17 | if (arr[i] < min) { 18 | min = arr[i]; 19 | } 20 | } 21 | 22 | // 创建计数数组 23 | int[] countArr = new int[max - min + 1]; 24 | 25 | // 遍历原数组计数 26 | for (int i = 0; i < arr.length; i++) { 27 | // 每出现一次+1 28 | countArr[arr[i] - min] = countArr[arr[i] - min] + 1; 29 | } 30 | 31 | // 遍历计数数组,按计数和偏移写回原数组 32 | int arrCoursour = 0; 33 | 34 | for (int i = 0; i < countArr.length; i++) { 35 | int count = countArr[i]; 36 | 37 | if (count == 0) continue; 38 | 39 | int num = i + min; 40 | 41 | for (int j = 0; j < count; j++) { // 计数几次、写回几次 42 | arr[arrCoursour] = num; 43 | arrCoursour++; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/QuickSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | /** 4 | * 快速排序 5 | */ 6 | public class QuickSort extends SortSupport { 7 | // 快速排序 8 | public static void quickSort(int[] array, int start, int end) { 9 | if (start >= end) { 10 | return; 11 | } 12 | 13 | int basePosition = partition(array, start, end); 14 | 15 | //递归处理左端 16 | quickSort(array, start, basePosition - 1); 17 | // 递归处理右端 18 | quickSort(array, basePosition + 1, end); 19 | } 20 | 21 | /** 22 | * 大小分区,基数归位 23 | * 将小值移至左端小值区、大值移至右侧大值区,最终将基数归位 24 | */ 25 | public static int partition(int[] array, int start, int end) { 26 | int base = array[start]; 27 | 28 | while (start < end) { 29 | // 右侧向左(<---)找到较小值 30 | while (start < end && array[end] >= base) 31 | end--; 32 | 33 | // 将找到的较小值填入左端坑中 34 | array[start] = array[end]; 35 | 36 | // 左侧向右(-->)寻找较大值 37 | while (start < end && array[start] <= base) 38 | start++; 39 | 40 | // 较大值填入右端坑中 41 | array[end] = array[start]; 42 | } 43 | 44 | array[end] = base; 45 | 46 | return start; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/bitmap/bloom/BloomFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.bloom; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | import java.util.UUID; 8 | 9 | public class BloomFilterTest { 10 | @Test 11 | public void test() { 12 | BloomFilter bloomFilter = new BloomFilter(2000); 13 | 14 | // 产生1000个不同key 15 | Set toPutSet = new HashSet<>(); 16 | 17 | int sample = 2000; 18 | 19 | for (int i = 0; i < sample; i++) { 20 | toPutSet.add(UUID.randomUUID().toString()); 21 | } 22 | 23 | // 存入布隆过滤器中 24 | toPutSet.forEach(key -> { 25 | bloomFilter.add(key); 26 | }); 27 | 28 | // 测试一定不存在 29 | int total = 0; 30 | int ok = 0; 31 | 32 | for (int i = 0; i < sample; i++) { 33 | String key = UUID.randomUUID().toString(); 34 | 35 | if (!toPutSet.contains(key)) { 36 | total++; 37 | 38 | if (bloomFilter.notExists(key)) { 39 | ok++; 40 | } 41 | } 42 | } 43 | 44 | System.out.println(String.format("【不存在】成功率:total=%s,ok=%s,rate=%s", total, ok, 1.0 * ok / total * 100)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/redblack/RedBlackNode.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.redblack; 2 | 3 | /** 4 | * 红黑树节点 5 | */ 6 | public class RedBlackNode { 7 | public String key; 8 | public Object val; 9 | 10 | public RedBlackNode parent; 11 | public RedBlackNode left; 12 | public RedBlackNode right; 13 | 14 | public static final int COLOR_BLACK = 0; 15 | public static final int COLOR_RED = 1; 16 | 17 | // 默认红色 18 | public int color = COLOR_RED; 19 | 20 | public RedBlackNode(String key, Object val, int color) { 21 | this.key = key; 22 | this.val = val; 23 | this.color = color; 24 | } 25 | 26 | public RedBlackNode(String key, int color) { 27 | this.key = key; 28 | this.color = color; 29 | } 30 | 31 | public RedBlackNode(String key, Object val) { 32 | this.key = key; 33 | this.val = val; 34 | } 35 | 36 | public boolean isRed() { 37 | return color == COLOR_RED; 38 | } 39 | 40 | public boolean isBlack() { 41 | return color == COLOR_BLACK; 42 | } 43 | 44 | public void toRed() { 45 | color = COLOR_RED; 46 | } 47 | 48 | public void toBlack() { 49 | color = COLOR_BLACK; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return key + "->" + val; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/bitmap/roaring/util/Util.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.roaring.util; 2 | 3 | public class Util { 4 | /** 5 | 在[begin,end)范围内的数组中查找值k。 如果找到该值,则返回其索引。 6 | 如果不是,则返回-(i + 1),其中i是要插入值的索引。 假定该数组包含排序的值,其中短裤被解释为无符号整数。 7 | * 8 | * @param begin first index (inclusive) 9 | * @param end last index (exclusive) 10 | * @param k value we search for 11 | * @return count 12 | */ 13 | public static int binarySearch(char[] arr, int begin, int end, 14 | char k) { 15 | if ((end > 0) && ((arr[end - 1]) < (int) (k))) { 16 | return -end - 1; 17 | } 18 | 19 | int low = begin; 20 | int high = end - 1; 21 | 22 | while (low <= high) { 23 | int middleIndex = (low + high) >>> 1; 24 | int middleValue = (arr[middleIndex]); 25 | 26 | if (middleValue < (int) (k)) { 27 | low = middleIndex + 1; 28 | } else if (middleValue > (int) (k)) { 29 | high = middleIndex - 1; 30 | } else { 31 | return middleIndex; 32 | } 33 | } 34 | 35 | // 最后未搜到的话,返回后继位置 36 | return -(low + 1); 37 | } 38 | 39 | public static char highBits(int x) { 40 | return (char) (x >>> 16); 41 | } 42 | 43 | public static char lowBits(int x) { 44 | return (char) x; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/kk/str/Sunday.java: -------------------------------------------------------------------------------- 1 | package com.kk.str; 2 | 3 | /** 4 | * Sunday字符串匹配算法 5 | */ 6 | public class Sunday { 7 | static int find(String text, String pattern) { 8 | int txtLen = text.length(), patLen = pattern.length(); 9 | 10 | int i = 0, j = 0; // 分别记录text索引,pattern索引 11 | 12 | int startForPat = 0; // 记录模板串相对于目标串的起始位置 13 | int nextI; // 记录模板串尾部在目标串的下一个字符索引 14 | 15 | while (i < txtLen && j < patLen) { 16 | // 向右逐个比较,直到不相等 17 | if (text.charAt(i) == pattern.charAt(j)) { 18 | i++; 19 | j++; 20 | continue; 21 | } 22 | 23 | nextI = startForPat + patLen; 24 | 25 | if (nextI >= txtLen) return -1; 26 | 27 | // 查找下一个字符在模板串的位置 28 | int idOfNext = idForNext(pattern, text.charAt(nextI)); 29 | 30 | // 移动 31 | i += patLen - idOfNext; 32 | startForPat = i; 33 | 34 | j = 0; 35 | 36 | if (startForPat + patLen > txtLen) return -1; // 如果匹配长度超过主串,匹配失败 37 | } 38 | 39 | return i <= txtLen ? startForPat : -1; 40 | } 41 | 42 | /** 43 | * 查找下一个字符在模板串的位置 44 | */ 45 | static int idForNext(String pattern, char next) { 46 | int patLen = pattern.length(); 47 | int i; 48 | 49 | for (i = patLen - 1; i >= 0 && next != pattern.charAt(i); i--) ; 50 | 51 | return i; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.kk 8 | kk-algorithm 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-compiler-plugin 16 | 17 | 8 18 | 8 19 | 20 | 21 | 22 | 23 | 24 | 25 | junit 26 | junit 27 | 4.12 28 | test 29 | 30 | 31 | 32 | commons-codec 33 | commons-codec 34 | 1.13 35 | 36 | 37 | 38 | org.apache.commons 39 | commons-lang3 40 | 3.9 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/heap/MaxHeapTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.heap; 2 | 3 | import com.kk.util.PrintHelper; 4 | import org.junit.After; 5 | import org.junit.Test; 6 | 7 | public class MaxHeapTest { 8 | MaxHeap maxHeap = new MaxHeap(); 9 | 10 | @After 11 | public void after() { 12 | System.out.println(); 13 | System.out.println("after-->"); 14 | PrintHelper.printArr(maxHeap.data); 15 | } 16 | 17 | @Test 18 | public void addTest() { 19 | maxHeap.add(2); 20 | maxHeap.add(7); 21 | maxHeap.add(10); 22 | maxHeap.add(0); 23 | maxHeap.add(-18); 24 | maxHeap.add(20); 25 | maxHeap.add(44); 26 | 27 | System.out.println("before-->"); 28 | PrintHelper.printArr(maxHeap.data); 29 | System.out.println(); 30 | } 31 | 32 | @Test 33 | public void popTest() { 34 | System.out.println("poping-->"); 35 | 36 | for (int i = 0; i < maxHeap.data.length; i++) { 37 | System.out.print("\t" + maxHeap.pop()); 38 | } 39 | } 40 | 41 | @Test 42 | public void replaceTest() { 43 | maxHeap.replace(15); 44 | } 45 | 46 | @Test 47 | public void heapifyTest() { 48 | int[] inordeNums = {4, 2, 8, 15, 7, 10}; 49 | 50 | maxHeap.heapify(inordeNums); 51 | 52 | int size = maxHeap.size; 53 | 54 | for (int i = 0; i < size; i++) { 55 | System.out.print("\t" + maxHeap.pop()); 56 | ; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/RadixSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 基数排序 8 | */ 9 | public class RadixSort extends SortSupport { 10 | public static void radixSort(int[] arr) { 11 | // 找到最大值 12 | int max = max(arr); 13 | 14 | // max决定了最高基数(循环次数),从低基数到高基数排序,1,10,100... 15 | for (int base = 1; max / base > 0; base = base * 10) { 16 | // 构建基数桶 17 | Object[] buckets = createBuckets(); 18 | 19 | // 分配:遍历原数组每个元素按pos位入桶 20 | for (int i = 0; i < arr.length; i++) { 21 | // 获取该位数字 22 | int posNum = (arr[i] / base) % 10; 23 | // 放入对应的桶中 24 | ((List) buckets[posNum]).add(arr[i]); 25 | } 26 | 27 | // 回收:遍历桶,将每个桶内的元素按序放回arr 28 | int arrCursor = 0; 29 | 30 | for (int i = 0; i < buckets.length; i++) { 31 | List nodesList = (List) buckets[i]; 32 | 33 | for (Integer num : nodesList) { 34 | arr[arrCursor] = num; 35 | arrCursor++; 36 | } 37 | } 38 | } 39 | } 40 | 41 | /** 42 | * 创建基数桶 43 | */ 44 | private static Object[] createBuckets() { 45 | Object[] buckets = new Object[10]; 46 | 47 | for (int i = 0; i < buckets.length; i++) { 48 | buckets[i] = new ArrayList(); 49 | } 50 | return buckets; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/bitmap/simple/BitSet.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.simple; 2 | 3 | public class BitSet { 4 | public static final int ADDRESS_BITS_PER_LONG = 6; // 每个long的地址数 5 | 6 | // long数组存放数据 7 | private long[] data; 8 | 9 | /** 10 | * 构造方法,传入预期的最大pos. 11 | */ 12 | public BitSet(int size) { 13 | this.data = new long[size >> ADDRESS_BITS_PER_LONG]; 14 | } 15 | 16 | /** 17 | * 获取指定pos的布尔值 18 | */ 19 | public boolean get(int pos) { 20 | return (data[idxFromPos(pos)] & (1 << offset(pos))) != 0; 21 | } 22 | 23 | /** 24 | * 将对应位置的值设置为传入的bool值 25 | */ 26 | public void set(int pos, boolean isTrue) { 27 | int idx = idxFromPos(pos); 28 | 29 | if (isTrue) { 30 | data[idx] |= 1 << offset(pos); // 对应的offset设置为1 31 | } else { 32 | data[idx] &= ~(1 << offset(pos)); // 对应的offset设置为0 33 | } 34 | } 35 | 36 | 37 | /** 38 | * 从pos推导对应的索引 39 | */ 40 | private int idxFromPos(int pos) { 41 | return pos >> ADDRESS_BITS_PER_LONG; 42 | } 43 | 44 | /** 45 | * 偏移 46 | */ 47 | public int offset(int pos) { 48 | return pos % 64; 49 | } 50 | 51 | public static void main(String[] args) { 52 | BitSet bitSet = new BitSet(1000); 53 | bitSet.set(10, true); 54 | bitSet.set(100, true); 55 | 56 | System.out.println(bitSet.get(9)); 57 | System.out.println(bitSet.get(18)); 58 | System.out.println(bitSet.get(100)); 59 | 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/BucketSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | /** 8 | * 桶排序 9 | * 分组入桶、桶内排序、按序回收 10 | */ 11 | public class BucketSort extends SortSupport { 12 | /** 13 | * 本例按照[min,mid],(mid,max]分2组 14 | */ 15 | public static void bucketSort(int[] arr) { 16 | int min = min(arr); 17 | int max = max(arr); 18 | int mid = min + max / 2; 19 | 20 | Object[] buckets = new Object[2]; 21 | 22 | for (int i = 0; i < buckets.length; i++) { 23 | buckets[i] = new ArrayList(); 24 | } 25 | 26 | // 入桶 27 | for (int i = 0; i < arr.length; i++) { 28 | int num = arr[i]; 29 | 30 | if (num <= mid) { // [min,mid] 0号组 31 | ((List) buckets[0]).add(num); 32 | } else { // (mid,max] 1号组 33 | ((List) buckets[1]).add(num); 34 | } 35 | } 36 | 37 | // 每组排序 38 | for (int i = 0; i < buckets.length; i++) { 39 | // 内部适用归并或快速排序 40 | Collections.sort((List) buckets[i]); 41 | } 42 | 43 | // 按序回收所有桶 44 | int arrCursor = 0; 45 | 46 | for (int i = 0; i < buckets.length; i++) { 47 | List sortedNums = (List) buckets[i]; 48 | 49 | for (Integer num : sortedNums) { 50 | arr[arrCursor] = num; 51 | arrCursor++; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/kk/str/KMP.java: -------------------------------------------------------------------------------- 1 | package com.kk.str; 2 | 3 | /** 4 | * KMP字符串匹配算法 5 | */ 6 | public class KMP { 7 | public static int find(String str, String temp) { 8 | return find(str.toCharArray(), temp.toCharArray()); 9 | } 10 | 11 | public static int find(char[] str, char[] temp) { 12 | int n = str.length; 13 | 14 | int tempLen = temp.length; 15 | 16 | int[] jumpTable = calJumpTable(temp); 17 | 18 | int j = 0; //j表示当前模版串的待匹配位置 19 | 20 | for (int i = 0; i < n; ++i) { 21 | while (str[i] != temp[j]) { // 失配点 22 | j = jumpTable[j]; //不停的转移,直到可以匹配或者走到0 23 | 24 | if (j == 0) break; 25 | } 26 | 27 | // 调整失配点之后,向后移位逐个比较 28 | if (str[i] == temp[j]) j++; 29 | 30 | if (j == tempLen) return i - tempLen + 1; 31 | } 32 | 33 | // 未找到 34 | return -1; 35 | } 36 | 37 | static int[] calJumpTable(String temp) { 38 | return calJumpTable(temp.toCharArray()); 39 | } 40 | 41 | /** 42 | * 计算失配-跳转表 43 | */ 44 | static int[] calJumpTable(char[] temp) { 45 | int len = temp.length; 46 | int[] table = new int[len + 1]; 47 | 48 | table[0] = table[1] = 0; // 边界 49 | 50 | for (int n = 1; n < len; ++n) { 51 | int jump = table[n]; 52 | 53 | while (temp[n] != temp[jump]) { 54 | // jump 向前追溯 55 | jump = table[jump]; 56 | 57 | if (jump == 0) { // 为0,表示已回退到指定点 58 | break; 59 | } 60 | } 61 | 62 | // 递推 63 | table[n + 1] = temp[n] == temp[jump] ? jump + 1 : 0; 64 | } 65 | 66 | return table; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/com/kk/sort/SortTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort; 2 | 3 | import com.kk.sort.mem.*; 4 | import org.junit.After; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | public class SortTest { 9 | int[] nums = {8, 3, 6, 7, 1, 9}; 10 | 11 | @Before 12 | public void printBeforeSort() { 13 | System.out.println(" nums to sort-->" + arrJoin(nums)); 14 | } 15 | 16 | @After 17 | public void printAfterSort() { 18 | System.out.println("sorted-->" + arrJoin(nums)); 19 | } 20 | 21 | private String arrJoin(int[] arr) { 22 | StringBuffer builder = new StringBuffer(); 23 | for (Object s : arr) { 24 | builder.append(s + ","); 25 | } 26 | 27 | return builder.toString(); 28 | } 29 | 30 | @Test 31 | public void bubbleSortTest() { 32 | BubbleSort.bubbleSort(nums); 33 | } 34 | 35 | @Test 36 | public void selectSortTest() { 37 | SelectSort.selectSort(nums); 38 | } 39 | 40 | @Test 41 | public void insertSortTest() { 42 | InsertSort.insertSort(nums); 43 | } 44 | 45 | @Test 46 | public void shellSortTest() { 47 | ShellSort.shellSort(nums); 48 | } 49 | 50 | @Test 51 | public void mergeSortTest() { 52 | MergeSort.sort(nums); 53 | } 54 | 55 | @Test 56 | public void quickSortTest() { 57 | QuickSort.quickSort(nums, 0, nums.length - 1); 58 | } 59 | 60 | @Test 61 | public void countSortTest() { 62 | CountSort.countSort(nums); 63 | } 64 | 65 | @Test 66 | public void radixSortTest() { 67 | RadixSort.radixSort(nums); 68 | } 69 | 70 | @Test 71 | public void bucketSortTest() { 72 | BucketSort.bucketSort(nums); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/mem/MergeSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.mem; 2 | 3 | public class MergeSort extends SortSupport { 4 | 5 | public static void sort(int[] arr) { 6 | //在排序前,先建好一个长度等于原数组长度的临时数组, 7 | //避免递归中频繁开辟空间 8 | int[] temp = new int[arr.length]; 9 | 10 | margeSort(arr, 0, arr.length - 1, temp); 11 | } 12 | 13 | public static void margeSort(int[] arr, int left, int right, int[] temp) { 14 | if (left >= right) { // 递分终止条件,左右边界重合 15 | return; 16 | } 17 | 18 | int mid = (left + right) / 2; 19 | 20 | // 左边归并排序,使得左子序列有序 21 | margeSort(arr, left, mid, temp); 22 | // 右边归并排序,使得右子序列有序 23 | margeSort(arr, mid + 1, right, temp); 24 | 25 | // 递归返回后、将两个有序子数组(tmp数组的2个有序区域[left,mid],[mid+1,right])合并操作 26 | merge(arr, left, mid, right, temp); 27 | } 28 | 29 | /** 30 | * 合并左右2个有序的子序列 31 | * 32 | *

33 | * i-> 34 | * left mid mid+1 right 35 | * j-> 36 | */ 37 | public static void merge(int[] arr, int left, int mid, int right, int[] temp) { 38 | int i = left; //左序列指针 39 | int j = mid + 1; //右序列指针 40 | int t = 0; //临时数组指针 41 | 42 | while (i <= mid && j <= right) { // 遍历左、右2个有组子序列,找到最值的一侧不断右移,直到该层终点 43 | if (arr[i]>arr[j]) { 44 | temp[t++] = arr[i++]; 45 | } else { 46 | temp[t++] = arr[j++]; 47 | } 48 | } 49 | 50 | while (i <= mid) { // 将左边剩余元素填充进temp中 51 | temp[t++] = arr[i++]; 52 | } 53 | 54 | while (j <= right) {//将右序列剩余元素填充进temp中 55 | temp[t++] = arr[j++]; 56 | } 57 | 58 | t = 0; 59 | // 将合并后结果copy到原数组中 60 | while (left <= right) { 61 | arr[left++] = temp[t++]; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/huffman/HuffmanTree.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.huffman; 2 | 3 | import java.util.PriorityQueue; 4 | 5 | 6 | /** 7 | * Huffman树 8 | */ 9 | public class HuffmanTree { 10 | int size = 0; 11 | Node root; 12 | 13 | // 记录所有的叶子节点,无需再遍历得到 14 | PriorityQueue leafs = new PriorityQueue<>(); 15 | 16 | /** 17 | * 存入多个节点 18 | */ 19 | public void put(Node... nodes) { 20 | // 入队列排序 21 | PriorityQueue queue = new PriorityQueue<>(nodes.length); 22 | 23 | if (null != root) { 24 | queue.add(root); 25 | } 26 | 27 | for (Node node : nodes) { 28 | queue.add(node); 29 | leafs.add(node); 30 | size++; 31 | } 32 | 33 | // 不断取出最小的2个值组合一个父节点,直到队列为空 34 | while (queue.size() != 1) { 35 | Node first = queue.poll(); 36 | Node second = queue.poll(); 37 | 38 | // 合并得到一个父节点 39 | createParent(first, second); 40 | 41 | queue.add(root); 42 | } 43 | } 44 | 45 | private void createParent(Node first, Node second) { 46 | root = new Node(first.weight + second.weight); 47 | root.left = first; 48 | root.right = second; 49 | 50 | first.parent = root; 51 | second.parent = root; 52 | } 53 | 54 | /** 55 | * 计算最短路径长度 56 | */ 57 | int wpl() { 58 | int wpl = 0; 59 | 60 | for (Node leaf : leafs) { 61 | int depth = depthForLeaf(leaf); 62 | wpl += leaf.weight * depth; 63 | } 64 | 65 | return wpl; 66 | } 67 | 68 | /** 69 | * 获取一个叶子的深度 70 | */ 71 | int depthForLeaf(Node node) { 72 | int depath = 0; 73 | 74 | while (node.parent != null) { // 不断向上判断父节点 75 | depath++; 76 | node = node.parent; 77 | } 78 | 79 | return depath; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/bitmap/hll/HyperLogLog.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.hll; 2 | 3 | import org.apache.commons.codec.digest.MurmurHash3; 4 | 5 | public class HyperLogLog { 6 | // 存放每桶最大值的桶 7 | int m = 64; 8 | int[] buckets = new int[m]; 9 | 10 | public void add(String key) { 11 | int hash32 = Math.abs(MurmurHash3.hash32(key)); 12 | 13 | int index = hash32 & 63; 14 | int val = hash32 >> m; 15 | int first1Pos = first1Pos(val); 16 | 17 | int lastMaxPos = buckets[index]; 18 | 19 | if (first1Pos > lastMaxPos) { 20 | buckets[index] = first1Pos; 21 | } 22 | } 23 | 24 | private int first1Pos(int val) { 25 | String valBits = Integer.toBinaryString(val); 26 | 27 | int len = valBits.length(); 28 | 29 | for (int i = len - 1; i > 0; i--) { 30 | if (valBits.charAt(i) == '1') { 31 | return len - i; 32 | } 33 | } 34 | 35 | return -1; 36 | } 37 | 38 | public long cardinality() { 39 | double sum = 0; 40 | 41 | double zeros = 0.0; 42 | 43 | for (int j = 0; j < m; j++) { 44 | int val = buckets[j]; 45 | 46 | sum += 1.0 / (1 << val); 47 | 48 | if (val == 0) { 49 | zeros++; 50 | } 51 | } 52 | 53 | double estimate = adjustConst() * (1 / sum); 54 | 55 | // 对0校正 56 | if (estimate <= (5.0 / 2.0) * m) { 57 | return Math.round(linearCounting(m, zeros)); 58 | } else { 59 | return Math.round(estimate); 60 | } 61 | } 62 | 63 | 64 | /** 65 | * 修正常数 66 | */ 67 | private double adjustConst() { 68 | return 0.709 * m * m; 69 | } 70 | 71 | /** 72 | * 线性计数 73 | */ 74 | private double linearCounting(int m, double zeroCounnt) { 75 | return m * Math.log(m / zeroCounnt); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/tree2/bst/BSTTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.bst; 2 | 3 | import com.kk.util.BSTPrinter; 4 | import org.junit.After; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | /** 9 | * 二叉搜索树 10 | */ 11 | public class BSTTest { 12 | BST bst = new BST(); 13 | 14 | @Before 15 | public void before() { 16 | putTest(); 17 | System.out.println("Before-->"); 18 | BSTPrinter.printNode(bst.root); 19 | System.out.println(); 20 | } 21 | 22 | @After 23 | public void after() { 24 | System.out.println(); 25 | System.out.println("After-->"); 26 | 27 | BSTPrinter.printNode(bst.root); 28 | } 29 | 30 | @Test 31 | public void putTest() { 32 | bst.put("zhaoliu", "zhaoliu"); 33 | bst.put("wangwu", "wangwu"); 34 | bst.put("lisi", "lisi"); 35 | 36 | bst.put("huang", "huang"); 37 | bst.put("kk", "kk"); 38 | bst.put("mm", "mm"); 39 | } 40 | 41 | @Test 42 | public void deleteMinTest() { 43 | bst.deleteMin(); 44 | BSTPrinter.printNode(bst.root); 45 | bst.deleteMin(); 46 | BSTPrinter.printNode(bst.root); 47 | bst.deleteMin(); 48 | } 49 | 50 | @Test 51 | public void sizeTest() { 52 | System.out.println("size()-->" + bst.size()); 53 | } 54 | 55 | @Test 56 | public void getTest() { 57 | System.out.println("get(wangwu)-->" + bst.get("wangwu")); 58 | } 59 | 60 | @Test 61 | public void minNodeTest() { 62 | BSTNode minNode = bst.minNode(bst.root); 63 | 64 | System.out.println("minNode-->"); 65 | BSTPrinter.printNode(minNode); 66 | } 67 | 68 | @Test 69 | public void deleteTest() { 70 | bst.delete("lisi"); 71 | System.out.println("delete(lisi)-->"); 72 | } 73 | 74 | @Test 75 | public void inorderTraverseTest(){ 76 | System.out.println("inorderTraverseTest-->" + bst); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/bitmap/bloom/BloomFilter.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.bloom; 2 | 3 | import com.kk.datastructure.bitmap.simple.BitSet; 4 | import org.apache.commons.codec.digest.MurmurHash3; 5 | 6 | import java.util.HashSet; 7 | import java.util.Random; 8 | import java.util.Set; 9 | import java.util.concurrent.atomic.AtomicBoolean; 10 | import java.util.function.Consumer; 11 | 12 | public class BloomFilter { 13 | // 数据规模,代表位图数组的大小 14 | int size; 15 | 16 | // 填充因子 17 | int inflate = 20; 18 | 19 | // 存储位图 20 | BitSet bitSet; 21 | 22 | // hash offsets,配合hash64实现hash函数列表,避免多次计算 23 | Set hashOffsets; 24 | int hashNums = 4; 25 | 26 | /** 27 | * @param n 数据规模 28 | */ 29 | public BloomFilter(int n) { 30 | this.size = n * 20; 31 | 32 | bitSet = new BitSet(size); 33 | 34 | initHashs(); 35 | } 36 | 37 | /** 38 | * 初始化hash列表 39 | */ 40 | private void initHashs() { 41 | hashOffsets = new HashSet<>(); 42 | 43 | Random random = new Random(); 44 | 45 | while (true) { 46 | if (hashOffsets.size() >= hashNums) return; 47 | 48 | int offset = random.nextInt(20) + 12; 49 | 50 | if (!hashOffsets.contains(offset)) { 51 | hashOffsets.add(offset); 52 | } 53 | } 54 | } 55 | 56 | private void hashs(String key, Consumer consumer) { 57 | int hash = MurmurHash3.hash32(key.getBytes()); 58 | 59 | hashOffsets.forEach(range -> { 60 | int rangedHash = hash >>> (32 - range); 61 | 62 | if (null != consumer) { 63 | consumer.accept(rangedHash); 64 | } 65 | }); 66 | } 67 | 68 | public void add(String key) { 69 | hashs(key, pos -> { 70 | bitSet.set(pos % size, true); 71 | }); 72 | } 73 | 74 | public boolean notExists(String key) { 75 | AtomicBoolean existed = new AtomicBoolean(true); 76 | 77 | hashs(key, pos -> { 78 | existed.set(bitSet.get(pos % size) && existed.get()); 79 | }); 80 | 81 | return !existed.get(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/bitmap/roaring/container/BitmapContainer.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.roaring.container; 2 | 3 | import static java.lang.Long.numberOfTrailingZeros; 4 | 5 | public class BitmapContainer extends Container { 6 | protected static int MAX_CAPACITY = 1 << 16; 7 | 8 | long[] data; 9 | int cardinality; 10 | 11 | public BitmapContainer() { 12 | this.cardinality = 0; 13 | this.data = new long[MAX_CAPACITY / 64]; 14 | } 15 | 16 | void fromArrayContainer(ArrayContainer arrayContainer) { 17 | this.cardinality = arrayContainer.cardinality; 18 | 19 | for (int i = 0; i < arrayContainer.cardinality; ++i) { 20 | char val = arrayContainer.data[i]; 21 | data[(val) / 64] |= (1L << val); 22 | } 23 | } 24 | 25 | @Override 26 | public Container add(char val) { 27 | // 高位索引 28 | int idx = val >>> 6; 29 | data[idx] = data[idx] | (1 << val); 30 | ++cardinality; 31 | return this; 32 | } 33 | 34 | @Override 35 | public boolean contains(char val) { 36 | return (data[val >>> 6] & (1L << val)) != 0; 37 | } 38 | 39 | @Override 40 | public Container remove(char i) { 41 | int index = i >>> 6; 42 | long item = data[index]; 43 | long mask = 1L << i; 44 | 45 | cardinality--; 46 | data[index] = item & ~mask; 47 | 48 | // 判断是否需要降级为array容器 49 | if (cardinality == ArrayContainer.DEFAULT_MAX_SIZE) {// this is 50 | this.toArrayContainer(); 51 | } 52 | 53 | return this; 54 | } 55 | 56 | ArrayContainer toArrayContainer() { 57 | ArrayContainer arrayContainer = new ArrayContainer(cardinality); 58 | arrayContainer.fromBitmapContainer(this); 59 | 60 | if (arrayContainer.getCardinality() != cardinality) { 61 | throw new RuntimeException("Internal error."); 62 | } 63 | 64 | return arrayContainer; 65 | } 66 | 67 | void fillArray(char[] array) { 68 | int pos = 0; 69 | int base = 0; 70 | for (int k = 0; k < data.length; ++k) { 71 | long bitset = data[k]; 72 | while (bitset != 0) { 73 | array[pos++] = (char) (base + numberOfTrailingZeros(bitset)); 74 | bitset &= (bitset - 1); 75 | } 76 | base += 64; 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/bitmap/roaring/RoaringBitmap.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.roaring; 2 | 3 | import com.kk.datastructure.bitmap.roaring.container.ArrayContainer; 4 | import com.kk.datastructure.bitmap.roaring.container.Container; 5 | import com.kk.datastructure.bitmap.roaring.util.Util; 6 | 7 | public class RoaringBitmap { 8 | ContainersArray containersArr = new ContainersArray(); 9 | 10 | public void add(int val) { 11 | char index = Util.highBits(val); 12 | int indexId = containersArr.getId(index); 13 | 14 | if (indexId >= 0) { // 找到了高位索引,插入到旧的容器中 15 | insertToOldContainer(indexId, val); 16 | } else { 17 | indexId = -indexId - 1; // 插入到新的容器 18 | insertToNewContainer(index, indexId, val); 19 | } 20 | } 21 | 22 | /** 23 | * 插入到新建的容器 24 | */ 25 | private Container insertToNewContainer(char curContainerIndex, int curContainerId, int val) { 26 | ArrayContainer arrayContainer = new ArrayContainer(); 27 | Container curContainer = arrayContainer.add(Util.lowBits(val)); 28 | 29 | containersArr.insertAt(curContainerId, curContainerIndex, curContainer); 30 | return curContainer; 31 | } 32 | 33 | /** 34 | * 插入到旧的容器 35 | */ 36 | private Container insertToOldContainer(int curContainerId, int val) { 37 | Container curContainer = containersArr.getContainerAtIndex(curContainerId); 38 | Container newContainer = curContainer.add(Util.lowBits(val)); 39 | 40 | if (newContainer != curContainer) { 41 | containersArr.setContainerAtIndex(curContainerId, newContainer); 42 | curContainer = newContainer; 43 | } 44 | 45 | return curContainer; 46 | } 47 | 48 | public boolean contains(int x) { 49 | char index = Util.highBits(x); 50 | Container container = containersArr.getContainer(index); 51 | return container != null && container.contains(Util.lowBits(x)); 52 | } 53 | 54 | 55 | public void remove(int x) { 56 | char index = Util.highBits(x); 57 | int indexId = containersArr.getId(index); 58 | 59 | if (indexId < 0) return; 60 | 61 | containersArr.setContainerAtIndex(indexId, containersArr.getContainerAtIndex(indexId).remove(Util.lowBits(x))); 62 | if (containersArr.getContainerAtIndex(indexId).isEmpty()) { 63 | containersArr.removeAtIndex(indexId); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/bitmap/roaring/ContainersArray.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.roaring; 2 | 3 | import com.kk.datastructure.bitmap.roaring.container.Container; 4 | import com.kk.datastructure.bitmap.roaring.util.Util; 5 | 6 | import java.util.Arrays; 7 | 8 | public class ContainersArray { 9 | static final int INITIAL_CAPACITY = 4; 10 | 11 | // 高位索引,char占位2个字节,即16位 12 | char[] highBits; 13 | // 索引对应的容器 14 | Container[] containers; 15 | 16 | int size = 0; 17 | 18 | protected ContainersArray() { 19 | this.highBits = new char[INITIAL_CAPACITY]; 20 | this.containers = new Container[INITIAL_CAPACITY]; 21 | } 22 | 23 | void insertAt(int i, char key, Container value) { 24 | extend(1); 25 | 26 | // i位的元素后移后插入i位 27 | System.arraycopy(highBits, i, highBits, i + 1, size - i); 28 | highBits[i] = key; 29 | System.arraycopy(containers, i, containers, i + 1, size - i); 30 | containers[i] = value; 31 | 32 | size++; 33 | } 34 | 35 | int getId(char index) { 36 | if ((size == 0) || (highBits[size - 1] == index)) { 37 | return size - 1; 38 | } 39 | 40 | return this.binarySearch(0, size, index); 41 | } 42 | 43 | private int binarySearch(int begin, int end, char key) { 44 | return Util.binarySearch(highBits, begin, end, key); 45 | } 46 | 47 | protected Container getContainerAtIndex(int i) { 48 | return this.containers[i]; 49 | } 50 | 51 | void setContainerAtIndex(int i, Container c) { 52 | this.containers[i] = c; 53 | } 54 | 55 | /** 56 | * 扩展数组 57 | */ 58 | void extend(int neddSpace) { 59 | if (this.size + neddSpace > this.highBits.length) { 60 | int newCapacity = 2 * (this.size + neddSpace); 61 | 62 | this.highBits = Arrays.copyOf(this.highBits, newCapacity); 63 | this.containers = Arrays.copyOf(this.containers, newCapacity); 64 | } 65 | } 66 | 67 | protected Container getContainer(char index) { 68 | int i = this.binarySearch(0, size, index); 69 | 70 | if (i < 0) return null; 71 | 72 | return this.containers[i]; 73 | } 74 | 75 | void removeAtIndex(int i) { 76 | System.arraycopy(highBits, i + 1, highBits, i, size - i - 1); 77 | highBits[size - 1] = 0; 78 | System.arraycopy(containers, i + 1, containers, i, size - i - 1); 79 | containers[size - 1] = null; 80 | size--; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/huffman/HuffmanCodec.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.huffman; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * 霍夫曼编解码器 9 | */ 10 | public class HuffmanCodec extends HuffmanTree { 11 | /** 12 | * 编码一个字符串,自动生成字典表,并根据字典表编码 13 | */ 14 | public String encode(String plain) { 15 | List keywordsList = tokenize(plain); 16 | 17 | Node[] nodes = new Node[keywordsList.size()]; 18 | int i = 0; 19 | 20 | for (String keyword : keywordsList) { 21 | nodes[i] = new Node(keyword, StringUtils.countMatches(plain, keyword)); 22 | i++; 23 | } 24 | 25 | put(nodes); 26 | 27 | Map codesMap = codesMap(); 28 | 29 | StringBuilder codesBuilder = new StringBuilder(); 30 | 31 | for (char ch : plain.toCharArray()) { 32 | codesBuilder.append(codesMap.get(char2Str(ch))); 33 | } 34 | 35 | return codesBuilder.toString(); 36 | } 37 | 38 | private String char2Str(char ch) { 39 | return new String(new char[]{ch}); 40 | } 41 | 42 | /** 43 | * 分词并去重 44 | */ 45 | private List tokenize(String plain) { 46 | char[] keywords = plain.toCharArray(); 47 | List keywordsList = new ArrayList<>(); 48 | 49 | for (char key : keywords) { 50 | String keyword = char2Str(key); 51 | 52 | if (keywordsList.indexOf(keyword) == -1) { 53 | keywordsList.add(keyword); 54 | } 55 | } 56 | 57 | return keywordsList; 58 | } 59 | 60 | /** 61 | * 编码 { "keyword":"001"} 62 | */ 63 | Map codesMap() { 64 | Map codesMap = new HashMap<>(); 65 | 66 | for (Node leaf : leafs) { 67 | 68 | String code = ""; 69 | Node node = leaf; 70 | 71 | while (node.parent != null) { 72 | Node parent = node.parent; 73 | 74 | if (parent.left == node) { // 左节点 --> 0 75 | code += "0"; 76 | } else { // 右节点 --> 1 77 | code += "1"; 78 | } 79 | 80 | node = parent; 81 | } 82 | 83 | codesMap.put(leaf.key, reverseStr(code)); 84 | } 85 | 86 | return codesMap; 87 | } 88 | 89 | /** 90 | * 翻转字符串 91 | */ 92 | String reverseStr(String str) { 93 | 94 | int len = str.length(); 95 | char[] reversed = new char[len]; 96 | 97 | for (int i = 0; i < len; i++) { 98 | reversed[i] = str.charAt(len - 1 - i); 99 | } 100 | 101 | return new String(reversed); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/test/java/com/kk/util/BSTPrinter.java: -------------------------------------------------------------------------------- 1 | package com.kk.util; 2 | 3 | import com.kk.datastructure.tree2.bst.BSTNode; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | public class BSTPrinter { 10 | 11 | public static > void printNode(BSTNode root) { 12 | int maxLevel = BSTPrinter.maxLevel(root); 13 | 14 | printNodeInternal(Collections.singletonList(root), 1, maxLevel); 15 | } 16 | 17 | private static > void printNodeInternal(List nodes, int level, int maxLevel) { 18 | if (nodes.isEmpty() || BSTPrinter.isAllElementsNull(nodes)) 19 | return; 20 | 21 | int floor = maxLevel - level; 22 | int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0))); 23 | int firstSpaces = (int) Math.pow(2, (floor)) - 1; 24 | int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1; 25 | 26 | BSTPrinter.printWhitespaces(firstSpaces); 27 | 28 | List newNodes = new ArrayList<>(); 29 | for (BSTNode node : nodes) { 30 | if (node != null) { 31 | System.out.print(node.val); 32 | newNodes.add(node.left); 33 | newNodes.add(node.right); 34 | } else { 35 | newNodes.add(null); 36 | newNodes.add(null); 37 | System.out.print(" "); 38 | } 39 | 40 | BSTPrinter.printWhitespaces(betweenSpaces); 41 | } 42 | System.out.println(""); 43 | 44 | for (int i = 1; i <= endgeLines; i++) { 45 | for (int j = 0; j < nodes.size(); j++) { 46 | BSTPrinter.printWhitespaces(firstSpaces - i); 47 | if (nodes.get(j) == null) { 48 | BSTPrinter.printWhitespaces(endgeLines + endgeLines + i + 1); 49 | continue; 50 | } 51 | 52 | if (nodes.get(j).left != null) 53 | System.out.print("/"); 54 | else 55 | BSTPrinter.printWhitespaces(1); 56 | 57 | BSTPrinter.printWhitespaces(i + i - 1); 58 | 59 | if (nodes.get(j).right != null) 60 | System.out.print("\\"); 61 | else 62 | BSTPrinter.printWhitespaces(1); 63 | 64 | BSTPrinter.printWhitespaces(endgeLines + endgeLines - i); 65 | } 66 | 67 | System.out.println(""); 68 | } 69 | 70 | printNodeInternal(newNodes, level + 1, maxLevel); 71 | } 72 | 73 | private static void printWhitespaces(int count) { 74 | for (int i = 0; i < count; i++) 75 | System.out.print(" "); 76 | } 77 | 78 | private static > int maxLevel(BSTNode node) { 79 | if (node == null) 80 | return 0; 81 | 82 | return Math.max(BSTPrinter.maxLevel(node.left), BSTPrinter.maxLevel(node.right)) + 1; 83 | } 84 | 85 | private static boolean isAllElementsNull(List list) { 86 | for (Object object : list) { 87 | if (object != null) 88 | return false; 89 | } 90 | 91 | return true; 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /src/test/java/com/kk/util/AVLPrinter.java: -------------------------------------------------------------------------------- 1 | package com.kk.util; 2 | 3 | import com.kk.datastructure.tree2.avl.AVLNode; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | public class AVLPrinter { 10 | 11 | public static > void printNode(AVLNode root) { 12 | int maxLevel = AVLPrinter.maxLevel(root); 13 | 14 | printNodeInternal(Collections.singletonList(root), 1, maxLevel); 15 | } 16 | 17 | private static > void printNodeInternal(List nodes, int level, int maxLevel) { 18 | if (nodes.isEmpty() || AVLPrinter.isAllElementsNull(nodes)) 19 | return; 20 | 21 | int floor = maxLevel - level; 22 | int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0))); 23 | int firstSpaces = (int) Math.pow(2, (floor)) - 1; 24 | int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1; 25 | 26 | AVLPrinter.printWhitespaces(firstSpaces); 27 | 28 | List newNodes = new ArrayList<>(); 29 | for (AVLNode node : nodes) { 30 | if (node != null) { 31 | System.out.print(node.val+"-"+node.depth); 32 | newNodes.add(node.left); 33 | newNodes.add(node.right); 34 | } else { 35 | newNodes.add(null); 36 | newNodes.add(null); 37 | System.out.print(" "); 38 | } 39 | 40 | AVLPrinter.printWhitespaces(betweenSpaces); 41 | } 42 | System.out.println(""); 43 | 44 | for (int i = 1; i <= endgeLines; i++) { 45 | for (int j = 0; j < nodes.size(); j++) { 46 | AVLPrinter.printWhitespaces(firstSpaces - i); 47 | if (nodes.get(j) == null) { 48 | AVLPrinter.printWhitespaces(endgeLines + endgeLines + i + 1); 49 | continue; 50 | } 51 | 52 | if (nodes.get(j).left != null) 53 | System.out.print("/"); 54 | else 55 | AVLPrinter.printWhitespaces(1); 56 | 57 | AVLPrinter.printWhitespaces((i + i - 1)); 58 | 59 | if (nodes.get(j).right != null) 60 | System.out.print("\\"); 61 | else 62 | AVLPrinter.printWhitespaces(1); 63 | 64 | AVLPrinter.printWhitespaces(endgeLines + endgeLines - i); 65 | } 66 | 67 | System.out.println(""); 68 | } 69 | 70 | printNodeInternal(newNodes, level + 1, maxLevel); 71 | } 72 | 73 | private static void printWhitespaces(int count) { 74 | for (int i = 0; i < count; i++) 75 | System.out.print(" "); 76 | } 77 | 78 | private static > int maxLevel(AVLNode node) { 79 | if (node == null) 80 | return 0; 81 | 82 | return Math.max(AVLPrinter.maxLevel(node.left), AVLPrinter.maxLevel(node.right)) + 1; 83 | } 84 | 85 | private static boolean isAllElementsNull(List list) { 86 | for (Object object : list) { 87 | if (object != null) 88 | return false; 89 | } 90 | 91 | return true; 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/HashMap.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure; 2 | 3 | import org.apache.commons.codec.digest.MurmurHash3; 4 | 5 | import java.util.LinkedList; 6 | 7 | /** 8 | * 哈希表条目 9 | */ 10 | class Entry { 11 | String key; 12 | Object val; 13 | 14 | public Entry(String key, Object val) { 15 | this.key = key; 16 | this.val = val; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return "Entry{" + 22 | "key='" + key + '\'' + 23 | ", val=" + val + 24 | '}'; 25 | } 26 | } 27 | 28 | /** 29 | * 哈希表 30 | */ 31 | public class HashMap { 32 | int capacity = 16; 33 | int size = 0; 34 | LinkedList[] data = new LinkedList[capacity]; 35 | 36 | /** 37 | * 存入 38 | */ 39 | public void put(String key, Object val) { 40 | // 查找对应的链表 41 | LinkedList entriesList = findListForKey(key); 42 | 43 | int pos = posForKey(entriesList, key); 44 | 45 | if (pos == -1) { // 不存在,插入 46 | entriesList.add(new Entry(key, val)); 47 | size++; 48 | } else { // 存在,更新val 49 | entriesList.get(pos).val = val; 50 | } 51 | } 52 | 53 | private LinkedList findListForKey(String key) { 54 | int index = indexForKey(key); 55 | return listForIndex(index); 56 | } 57 | 58 | /** 59 | * 查询key 60 | */ 61 | public Object get(String key) { 62 | // 查找对应的链表 63 | LinkedList entriesList = findListForKey(key); 64 | 65 | int pos = posForKey(entriesList, key); 66 | 67 | if (pos == -1) { 68 | return null; 69 | } else { 70 | return entriesList.get(pos).val; 71 | } 72 | } 73 | 74 | /** 75 | * 移除key,返回被移除掉的entry 76 | */ 77 | public Entry remove(String key) { 78 | LinkedList entriesList = findListForKey(key); 79 | 80 | int pos = posForKey(entriesList, key); 81 | 82 | if (pos == -1) { 83 | return null; 84 | } else { 85 | Entry toRmEntry = entriesList.get(pos); 86 | entriesList.remove(pos); 87 | size--; 88 | return toRmEntry; 89 | } 90 | } 91 | 92 | /** 93 | * 获取某位的链表,如果没有则新建 94 | */ 95 | LinkedList listForIndex(int index) { 96 | LinkedList linkedList = data[index]; 97 | 98 | if (null == linkedList) { 99 | linkedList = new LinkedList<>(); 100 | data[index] = linkedList; 101 | } 102 | 103 | return linkedList; 104 | } 105 | 106 | /** 107 | * 查询链表中是否存在指定key的节点 108 | */ 109 | int posForKey(LinkedList entryLinkedList, String key) { 110 | int pos = -1; 111 | 112 | for (int i = 0; i < entryLinkedList.size(); i++) { 113 | if (entryLinkedList.get(i).key.equals(key)) { 114 | pos = i; 115 | } 116 | } 117 | 118 | return pos; 119 | } 120 | 121 | /** 122 | * 计算指定key的索引 123 | */ 124 | int indexForKey(String key) { 125 | return Math.abs(MurmurHash3.hash32(key)) % 16; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/test/java/com/kk/util/HuffmanPrinter.java: -------------------------------------------------------------------------------- 1 | package com.kk.util; 2 | 3 | import com.kk.datastructure.tree2.huffman.Node; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | public class HuffmanPrinter { 10 | 11 | public static > void printNode(Node root) { 12 | int maxLevel = HuffmanPrinter.maxLevel(root); 13 | 14 | printNodeInternal(Collections.singletonList(root), 1, maxLevel); 15 | } 16 | 17 | private static > void printNodeInternal(List nodes, int level, int maxLevel) { 18 | if (nodes.isEmpty() || HuffmanPrinter.isAllElementsNull(nodes)) 19 | return; 20 | 21 | int floor = maxLevel - level; 22 | int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0))); 23 | int firstSpaces = (int) Math.pow(2, (floor)) - 1; 24 | int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1; 25 | 26 | HuffmanPrinter.printWhitespaces(firstSpaces); 27 | 28 | List newNodes = new ArrayList<>(); 29 | for (Node node : nodes) { 30 | if (node != null) { 31 | System.out.print((node.key == null ? "" : node.key) + "(" + node.weight + ")"); 32 | newNodes.add(node.left); 33 | newNodes.add(node.right); 34 | } else { 35 | newNodes.add(null); 36 | newNodes.add(null); 37 | System.out.print(" "); 38 | } 39 | 40 | HuffmanPrinter.printWhitespaces(betweenSpaces); 41 | } 42 | System.out.println(""); 43 | 44 | for (int i = 1; i <= endgeLines; i++) { 45 | for (int j = 0; j < nodes.size(); j++) { 46 | HuffmanPrinter.printWhitespaces(firstSpaces - i); 47 | if (nodes.get(j) == null) { 48 | HuffmanPrinter.printWhitespaces(endgeLines + endgeLines + i + 1); 49 | continue; 50 | } 51 | 52 | if (nodes.get(j).left != null) 53 | System.out.print("/"); 54 | else 55 | HuffmanPrinter.printWhitespaces(1); 56 | 57 | HuffmanPrinter.printWhitespaces(i + i - 1); 58 | 59 | if (nodes.get(j).right != null) 60 | System.out.print("\\"); 61 | else 62 | HuffmanPrinter.printWhitespaces(1); 63 | 64 | HuffmanPrinter.printWhitespaces(endgeLines + endgeLines - i); 65 | } 66 | 67 | System.out.println(""); 68 | } 69 | 70 | printNodeInternal(newNodes, level + 1, maxLevel); 71 | } 72 | 73 | private static void printWhitespaces(int count) { 74 | for (int i = 0; i < count; i++) 75 | System.out.print(" "); 76 | } 77 | 78 | private static > int maxLevel(Node node) { 79 | if (node == null) 80 | return 0; 81 | 82 | return Math.max(HuffmanPrinter.maxLevel(node.left), HuffmanPrinter.maxLevel(node.right)) + 1; 83 | } 84 | 85 | private static boolean isAllElementsNull(List list) { 86 | for (Object object : list) { 87 | if (object != null) 88 | return false; 89 | } 90 | 91 | return true; 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/outer/LoserTree.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.outer; 2 | 3 | public class LoserTree { 4 | int n; 5 | // 失败者完全二叉树数组 6 | int[] losers; 7 | 8 | // 分支数组 9 | int[] branches; 10 | 11 | public void create(int[] branches) { 12 | this.branches = branches; 13 | n = branches.length; 14 | losers = new int[n]; 15 | 16 | // 假定最后一个元素作为初始的最小值 17 | int idForMin = idForMin(); 18 | 19 | for (int i = 0; i < n; i++) { 20 | losers[i] = idForMin; 21 | } 22 | 23 | // 从尾部逐个向上调整 24 | for (int i = n - 1; i >= 0; i--) { 25 | adjustUp(i); 26 | } 27 | } 28 | 29 | /** 30 | * 找到最小值的id 31 | */ 32 | int idForMin() { 33 | int min = branches[0]; 34 | int id = 0; 35 | 36 | for (int i = 1; i < n; i++) { 37 | if (branches[i] < min) { 38 | min = branches[i]; 39 | id = i; 40 | } 41 | } 42 | 43 | return id; 44 | } 45 | 46 | /** 47 | * 获取当前lose节点的父节点 48 | */ 49 | int parent(int loserId) { 50 | return loserId / 2; 51 | } 52 | 53 | /** 54 | * 获取分支对应的loser 55 | */ 56 | int loserForBranch(int branchId) { 57 | return (n + branchId) / 2; 58 | } 59 | 60 | /** 61 | * 从某个分支向上调整 62 | */ 63 | void adjustUp(int minBranchId) { 64 | int parentLoser = loserForBranch(minBranchId); 65 | 66 | while (parentLoser > 0) { // 尚未到达根节点 67 | if (branches[minBranchId] > branches[losers[parentLoser]]) { // 比父节点大 ——> 失败 --> 失败者记录到父节点,胜者向上比赛 68 | int tmp = losers[parentLoser]; 69 | losers[parentLoser] = minBranchId; 70 | minBranchId = tmp; 71 | } 72 | 73 | parentLoser = parent(parentLoser); 74 | } 75 | 76 | // 将最终的胜者(最小值) 77 | losers[0] = minBranchId; 78 | } 79 | 80 | /** 81 | * 查看顶部最小值 82 | */ 83 | public int peek() { 84 | return branches[losers[0]]; 85 | } 86 | 87 | /** 88 | * 查看顶部最小值所在的分支id 89 | */ 90 | public int minBranch() { 91 | return losers[0]; 92 | } 93 | 94 | /** 95 | * 弹出最小值,并从最小分支读取下一个元素 96 | */ 97 | public int pop(int updateVal) { 98 | int min = peek(); 99 | int minBranch = losers[0]; 100 | 101 | // 更新最小值 102 | branches[minBranch] = updateVal; 103 | // 调整 104 | adjustUp(minBranch); 105 | 106 | return min; 107 | } 108 | 109 | public int pop() { 110 | return pop(Integer.MAX_VALUE); 111 | } 112 | 113 | /** 114 | * 是否有下一个 115 | * 当最小值不是 Integer.MAX_VALUE 的时候,说明还有 116 | */ 117 | public boolean hashNext() { 118 | return peek() != Integer.MAX_VALUE; 119 | } 120 | 121 | /** 122 | * 下一个 123 | */ 124 | public int next() { 125 | return pop(); 126 | } 127 | 128 | @Override 129 | public String toString() { 130 | StringBuilder builder = new StringBuilder(); 131 | builder.append("LoserTree["); 132 | 133 | for (int i = 0; i < losers.length; i++) { 134 | builder.append((i == 0 ? "" : ",") + losers[i]); 135 | } 136 | 137 | builder.append("]"); 138 | return builder.toString(); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/test/java/com/kk/util/RedBlackTreePrinter.java: -------------------------------------------------------------------------------- 1 | package com.kk.util; 2 | 3 | 4 | import com.kk.datastructure.tree2.redblack.RedBlackNode; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | public class RedBlackTreePrinter { 11 | 12 | public static > void printNode(RedBlackNode root) { 13 | int maxLevel = RedBlackTreePrinter.maxLevel(root); 14 | 15 | printNodeInternal(Collections.singletonList(root), 1, maxLevel); 16 | } 17 | 18 | private static > void printNodeInternal(List nodes, int level, int maxLevel) { 19 | if (nodes.isEmpty() || RedBlackTreePrinter.isAllElementsNull(nodes)) 20 | return; 21 | 22 | int floor = maxLevel - level; 23 | int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0))); 24 | int firstSpaces = (int) Math.pow(2, (floor)) - 1; 25 | int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1; 26 | 27 | RedBlackTreePrinter.printWhitespaces(firstSpaces); 28 | 29 | List newNodes = new ArrayList<>(); 30 | for (RedBlackNode node : nodes) { 31 | if (node != null) { 32 | System.out.print(node.val + "(" + (node.color == RedBlackNode.COLOR_BLACK ? "黑" : "红") + ")"); 33 | newNodes.add(node.left); 34 | newNodes.add(node.right); 35 | } else { 36 | newNodes.add(null); 37 | newNodes.add(null); 38 | System.out.print(" "); 39 | } 40 | 41 | RedBlackTreePrinter.printWhitespaces(betweenSpaces); 42 | } 43 | System.out.println(""); 44 | 45 | for (int i = 1; i <= endgeLines; i++) { 46 | for (int j = 0; j < nodes.size(); j++) { 47 | RedBlackTreePrinter.printWhitespaces(firstSpaces - i); 48 | if (nodes.get(j) == null) { 49 | RedBlackTreePrinter.printWhitespaces(endgeLines + endgeLines + i + 1); 50 | continue; 51 | } 52 | 53 | if (nodes.get(j).left != null) 54 | System.out.print("/"); 55 | else 56 | RedBlackTreePrinter.printWhitespaces(1); 57 | 58 | RedBlackTreePrinter.printWhitespaces(i + i - 1); 59 | 60 | if (nodes.get(j).right != null) 61 | System.out.print("\\"); 62 | else 63 | RedBlackTreePrinter.printWhitespaces(1); 64 | 65 | RedBlackTreePrinter.printWhitespaces(endgeLines + endgeLines - i); 66 | } 67 | 68 | System.out.println(""); 69 | } 70 | 71 | printNodeInternal(newNodes, level + 1, maxLevel); 72 | } 73 | 74 | private static void printWhitespaces(int count) { 75 | for (int i = 0; i < count; i++) 76 | System.out.print(" "); 77 | } 78 | 79 | private static > int maxLevel(RedBlackNode node) { 80 | if (node == null) 81 | return 0; 82 | 83 | return Math.max(RedBlackTreePrinter.maxLevel(node.left), RedBlackTreePrinter.maxLevel(node.right)) + 1; 84 | } 85 | 86 | private static boolean isAllElementsNull(List list) { 87 | for (Object object : list) { 88 | if (object != null) 89 | return false; 90 | } 91 | 92 | return true; 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/bitmap/roaring/container/ArrayContainer.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.bitmap.roaring.container; 2 | 3 | import com.kk.datastructure.bitmap.roaring.util.Util; 4 | 5 | import java.util.Arrays; 6 | 7 | public class ArrayContainer extends Container { 8 | private static int DEFAULT_INIT_SIZE = 4; 9 | // 存储阈值,>4096的时候,转换为bitmap容器 10 | static int DEFAULT_MAX_SIZE = 4096; 11 | 12 | char[] data; 13 | int cardinality = 0; 14 | 15 | public ArrayContainer() { 16 | this(DEFAULT_INIT_SIZE); 17 | } 18 | 19 | public ArrayContainer(int capacity) { 20 | data = new char[capacity]; 21 | } 22 | 23 | void fromBitmapContainer(BitmapContainer bitmapContainer) { 24 | this.cardinality = bitmapContainer.cardinality; 25 | bitmapContainer.fillArray(data); 26 | } 27 | 28 | @Override 29 | public Container add(char val) { 30 | if (isToEnd(val)) { // 追加到末尾 31 | if (handleAddCommon()) return toBitmapContainer().add(val); 32 | 33 | data[cardinality++] = val; 34 | } else { // 在中间插入 35 | int loc = Util.binarySearch(data, 0, cardinality, val); 36 | 37 | // 找到了,不必重复加入 38 | if (loc >= 0) return this; 39 | 40 | // 小于0,未找到(返回后继元素的下标)才加入 41 | if (handleAddCommon()) return toBitmapContainer().add(val); 42 | 43 | // 将大于x的元素右移 44 | int succeedPos = -loc; 45 | System.arraycopy(data, succeedPos - 1, 46 | data, succeedPos, cardinality - succeedPos + 1); 47 | // 插入合适的位置 48 | data[succeedPos - 1] = val; 49 | ++cardinality; 50 | } 51 | 52 | return this; 53 | } 54 | 55 | private boolean isToEnd(char x) { // 新元素大于当前存在的最大值 56 | return cardinality == 0 || 57 | (cardinality > 0 && (x) > (data[cardinality - 1])); 58 | } 59 | 60 | private boolean handleAddCommon() { 61 | // 超过容量转换为bitmap容器 62 | if (cardinality >= DEFAULT_MAX_SIZE) { 63 | return true; 64 | } 65 | 66 | if (cardinality >= this.data.length) { 67 | increaseCapacity(); 68 | } 69 | 70 | return false; 71 | } 72 | 73 | 74 | public BitmapContainer toBitmapContainer() { 75 | BitmapContainer bc = new BitmapContainer(); 76 | bc.fromArrayContainer(this); 77 | return bc; 78 | } 79 | 80 | 81 | private void increaseCapacity() { 82 | int newCapacity = (this.data.length == 0) ? DEFAULT_INIT_SIZE : this.data.length * 2; 83 | 84 | if (newCapacity > ArrayContainer.DEFAULT_MAX_SIZE) { 85 | newCapacity = ArrayContainer.DEFAULT_MAX_SIZE; 86 | } 87 | 88 | // 扩容 89 | this.data = Arrays.copyOf(this.data, newCapacity); 90 | } 91 | 92 | @Override 93 | public boolean contains(char val) { 94 | return Util.binarySearch(data, 0, cardinality, val) >= 0; 95 | } 96 | 97 | @Override 98 | public Container remove(char x) { 99 | int loc = Util.binarySearch(data, 0, cardinality, x); 100 | if (loc >= 0) { 101 | removeAtIndex(loc); 102 | } 103 | return this; 104 | } 105 | 106 | void removeAtIndex(int loc) { 107 | System.arraycopy(data, loc + 1, data, loc, cardinality - loc - 1); 108 | --cardinality; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/tree2/redblack/RedBlackTreeTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.redblack; 2 | 3 | import com.kk.util.RedBlackTreePrinter; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | /** 8 | * 红黑树 9 | */ 10 | public class RedBlackTreeTest { 11 | RedBlackTree redBlackTree = new RedBlackTree(); 12 | 13 | @Before 14 | public void before() { 15 | System.out.println("Before-->"); 16 | } 17 | 18 | @Test 19 | public void putTest() { 20 | redBlackTree.put("zz", "zz"); 21 | redBlackTree.put("ww", "ww"); 22 | redBlackTree.put("ll", "ll"); 23 | redBlackTree.put("hh", "hh"); 24 | redBlackTree.put("kk", "kk"); 25 | redBlackTree.put("mm", "mm"); 26 | redBlackTree.put("aa", "bb"); 27 | redBlackTree.put("pp", "pp"); 28 | // redBlackTree.put("xx", "xx"); 29 | redBlackTree.put("nn", "nn"); 30 | 31 | RedBlackTreePrinter.printNode(redBlackTree.root); 32 | } 33 | 34 | @Test 35 | public void findNodeTest() { 36 | putTest(); 37 | 38 | System.out.println("find(lisi)-->" + redBlackTree.findNearNode("lisi").val); 39 | System.out.println("find(gg)-->" + redBlackTree.findNearNode("gg").val); 40 | System.out.println("find(xx)-->" + redBlackTree.findNearNode("xx").val); 41 | } 42 | 43 | @Test 44 | public void getTest() { 45 | putTest(); 46 | 47 | System.out.println("get(lisi)-->" + redBlackTree.get("lisi")); 48 | System.out.println("get(kk)-->" + redBlackTree.get("kk")); 49 | System.out.println("get(gg)-->" + redBlackTree.get("gg")); 50 | } 51 | 52 | @Test 53 | public void leftRotateTest() { 54 | putTest(); 55 | 56 | redBlackTree.root.left.left = redBlackTree.leftRotate(redBlackTree.root.left.left); 57 | 58 | System.out.println("左旋lisi后-->"); 59 | RedBlackTreePrinter.printNode(redBlackTree.root); 60 | } 61 | 62 | @Test 63 | public void rightRotateTest() { 64 | putTest(); 65 | 66 | redBlackTree.root.left.left = redBlackTree.rightRotate(redBlackTree.root.left.left); 67 | 68 | System.out.println("右旋lisi后-->"); 69 | RedBlackTreePrinter.printNode(redBlackTree.root); 70 | } 71 | 72 | @Test 73 | public void flipColorsTest() { 74 | putTest(); 75 | 76 | redBlackTree.flipColors(redBlackTree.root.left.left); 77 | 78 | System.out.println("颜色翻转-->"); 79 | RedBlackTreePrinter.printNode(redBlackTree.root); 80 | } 81 | 82 | @Test 83 | public void minNodeTest() { 84 | putTest(); 85 | 86 | System.out.println("minNode-->" + redBlackTree.minNode(redBlackTree.root)); 87 | } 88 | 89 | @Test 90 | public void succeedNodeTest() { 91 | putTest(); 92 | 93 | System.out.println("succeedNode-->" + redBlackTree.succeedNode(redBlackTree.root.left)); 94 | } 95 | 96 | @Test 97 | public void deleteChildTest() { 98 | putTest(); 99 | 100 | DeletePoint deletePoint = redBlackTree.deleteEndChild(redBlackTree.root.left, redBlackTree.root.left.left); 101 | 102 | RedBlackTreePrinter.printNode(redBlackTree.root); 103 | } 104 | 105 | @Test 106 | public void deleteTest() { 107 | putTest(); 108 | 109 | redBlackTree.delete("pp"); 110 | // redBlackTree.delete("aa"); 111 | // redBlackTree.delete("ww"); 112 | 113 | System.out.println("delete-->\n"); 114 | 115 | RedBlackTreePrinter.printNode(redBlackTree.root); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/com/kk/datastructure/tree2/avl/AVLTreeTest.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.avl; 2 | 3 | import com.kk.util.AVLPrinter; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | /** 8 | * 二叉搜索树 9 | */ 10 | public class AVLTreeTest { 11 | AVLTree avl = new AVLTree(); 12 | 13 | @Before 14 | public void before() { 15 | putTest(); 16 | avl.refreshDepth(); 17 | 18 | System.out.println("Before-->"); 19 | AVLPrinter.printNode(avl.root); 20 | 21 | System.out.println(); 22 | } 23 | 24 | // @After 25 | public void after() { 26 | System.out.println(); 27 | System.out.println("After-->"); 28 | 29 | AVLPrinter.printNode(avl.root); 30 | } 31 | 32 | @Test 33 | public void putTest() { 34 | avl.put("zhaoliu", "zhaoliu"); 35 | avl.put("wangwu", "wangwu"); 36 | avl.put("lisi", "lisi"); 37 | 38 | avl.put("huang", "huang"); 39 | avl.put("kk", "kk"); 40 | avl.put("mm", "mm"); 41 | } 42 | 43 | @Test 44 | public void deleteMinTest() { 45 | avl.deleteMin(); 46 | AVLPrinter.printNode(avl.root); 47 | avl.deleteMin(); 48 | AVLPrinter.printNode(avl.root); 49 | avl.deleteMin(); 50 | 51 | after(); 52 | } 53 | 54 | @Test 55 | public void sizeTest() { 56 | System.out.println("size()-->" + avl.size()); 57 | } 58 | 59 | @Test 60 | public void getTest() { 61 | System.out.println("get(wangwu)-->" + avl.get("wangwu")); 62 | } 63 | 64 | @Test 65 | public void minNodeTest() { 66 | AVLNode minNode = avl.minNode(avl.root); 67 | 68 | System.out.println("minNode-->"); 69 | AVLPrinter.printNode(minNode); 70 | } 71 | 72 | @Test 73 | public void deleteTest() { 74 | avl.delete("lisi"); 75 | System.out.println("delete(lisi)-->"); 76 | 77 | after(); 78 | } 79 | 80 | @Test 81 | public void refreshDepthTest() { 82 | int depth = avl.refreshDepth(); 83 | System.out.println("depth-->" + depth); 84 | } 85 | 86 | @Test 87 | public void isUnbanlanceTest() { 88 | avl.refreshDepth(); 89 | after(); 90 | 91 | System.out.println("-->" + avl.isUnbanlance(avl.root)); 92 | } 93 | 94 | @Test 95 | public void leftRotateTest() { 96 | System.out.println("leftRotate-->lisi"); 97 | // 旋转后连接到父层 98 | avl.root.left.left = avl.leftRotate(avl.root.left.left); 99 | avl.refreshDepth(); 100 | 101 | after(); 102 | } 103 | 104 | @Test 105 | public void rightRotateTest() { 106 | System.out.println("rightRotate-->lisi"); 107 | // 旋转后连接到父层 108 | avl.root.left.left = avl.rightRotate(avl.root.left.left); 109 | avl.refreshDepth(); 110 | 111 | after(); 112 | } 113 | 114 | @Test 115 | public void isToLeftTest() { 116 | System.out.println("isToLeft-->" + avl.isToLeft(avl.root)); 117 | } 118 | 119 | @Test 120 | public void isToRightTest() { 121 | System.out.println("isToRight-->" + avl.isToRight(avl.root)); 122 | } 123 | 124 | @Test 125 | public void isLL() { 126 | System.out.println("isLL root-->" + avl.isLL(avl.root)); 127 | System.out.println("isLL lisi-->" + avl.isLL(avl.root.left.left)); 128 | } 129 | 130 | @Test 131 | public void balanceTest() { 132 | System.out.println("balance-->"); 133 | 134 | avl.root = avl.balance(avl.root); 135 | 136 | after(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/kk/str/BoyerMoore.java: -------------------------------------------------------------------------------- 1 | package com.kk.str; 2 | 3 | public class BoyerMoore { 4 | static int find(String target, String temp) { 5 | char[] pat = temp.toCharArray(); 6 | char[] str = target.toCharArray(); 7 | int n = str.length; 8 | int m = pat.length; 9 | 10 | // 计算坏字符表和好后缀表 11 | int[] badChars = preBadCharsTable(temp); 12 | int[] goodSuffixes = preGoodSuffixes(temp, suffixes(temp)); 13 | 14 | // 搜索 15 | int j, i = -1; 16 | while (i <= n - m) { 17 | // 从尾部逐个向前匹配,当对应位置相等的时候一直向前匹配 18 | for (j = m - 1; j >= 0 && pat[j] == str[j + i]; --j) ; 19 | 20 | // 出现失配点 21 | if (j < 0) { 22 | return i; 23 | } else { // 取较大的位移 24 | i += Math.max(badChars[j], goodSuffixes[j]); 25 | } 26 | } 27 | 28 | return i; 29 | } 30 | 31 | /** 32 | * 预生成坏字符串表 33 | */ 34 | static int[] preBadCharsTable(String template) { 35 | int len = template.length(); 36 | 37 | int[] badCharsTable = new int[len]; 38 | badCharsTable[0] = 1; 39 | 40 | for (int i = 1; i < len; i++) { // 遍历每一位 41 | char ch = template.charAt(i); 42 | 43 | int matchId = -1; 44 | 45 | // 向前遍历、比较 46 | for (int j = i - 1; j >= 0; j--) { 47 | if (ch == template.charAt(j)) { 48 | matchId = j; 49 | break; 50 | } 51 | } 52 | 53 | badCharsTable[i] = i - matchId; 54 | } 55 | 56 | return badCharsTable; 57 | } 58 | 59 | /** 60 | * 计算后缀 61 | * suffixes[i]的值为后缀串pat[0,i]与模式串pat的最长公共后缀长度 62 | */ 63 | static int[] suffixes(String template) { 64 | char[] pat = template.toCharArray(); 65 | 66 | int len = pat.length; 67 | 68 | int[] suffixes = new int[len]; 69 | 70 | for (int i = len - 1; i >= 0; --i) { // 从后往前、减小后缀长度 71 | int j = i; 72 | 73 | while (j >= 0) { // 公共后缀,继续向前查 74 | int correspondingPos = len - 1 - i + j; // pat对应的尾部位置 75 | 76 | if (pat[j] == pat[correspondingPos]) { // 相等的话则继续向前 77 | --j; 78 | } else { 79 | break; 80 | } 81 | } 82 | 83 | suffixes[i] = i - j; 84 | } 85 | 86 | return suffixes; 87 | } 88 | 89 | /** 90 | * 计算好后缀跳转表 91 | */ 92 | static int[] preGoodSuffixes(String template, int[] suffixes) { 93 | char[] pat = template.toCharArray(); 94 | 95 | int len = pat.length; 96 | int[] goodSuffixTable = new int[len]; 97 | 98 | // 初始假设没有子串或前缀与好后缀一致,全部设为模板串长度 99 | for (int i = 0; i < len; ++i) 100 | goodSuffixTable[i] = len; 101 | 102 | // 有子串与好后缀一致 103 | for (int i = 0; i < len - 1; ++i) {// 从后往前遍历后缀数组 104 | int mismatchPoint = len - 1 - suffixes[i]; 105 | goodSuffixTable[mismatchPoint] = len - 1 - i; // 跳到子串位置 106 | } 107 | 108 | // 有前缀与好后缀一致 109 | for (int i = len - 1; i >= 0; --i) { // 向前遍历每一个失配点 110 | if (suffixes[i] == i + 1) { // 后缀也是前缀 111 | for (int j = 0; j < len - 1 - i; ++j) { // 从前往后遍历前缀直到最长后缀 112 | if (goodSuffixTable[j] == len) // 如果子串匹配已经符合,则不再考虑前缀匹配 113 | goodSuffixTable[j] = len - 1 - i; 114 | } 115 | } 116 | } 117 | 118 | return goodSuffixTable; 119 | } 120 | } -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/heap/MaxHeap.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.heap; 2 | 3 | /** 4 | * 最大堆 5 | */ 6 | public class MaxHeap { 7 | int capacity = 10; 8 | 9 | // 以存入的节点数 10 | int size = 0; 11 | 12 | // 数据数组 13 | public int[] data = new int[capacity]; 14 | 15 | public void add(int val) { 16 | if (size == capacity) { 17 | throw new RuntimeException("堆已满"); 18 | } 19 | 20 | size++; 21 | data[size - 1] = val; 22 | 23 | // 上浮调整堆 24 | siftUp(size - 1); 25 | } 26 | 27 | /** 28 | * 弹出顶部最大值 29 | */ 30 | public int pop() { 31 | if (size == 0) { 32 | return 0; 33 | } 34 | 35 | int max = data[0]; 36 | // 用尾部替换堆顶 37 | data[0] = data[size - 1]; 38 | data[size - 1] = 0; 39 | 40 | size--; 41 | 42 | // 向下调整堆 43 | siftDown(0); 44 | 45 | return max; 46 | } 47 | 48 | /** 49 | * 更新根节点最大值 50 | */ 51 | public void replace(int val) { 52 | if (size == 0) { 53 | return; 54 | } 55 | 56 | // 更新 57 | data[0] = val; 58 | siftDown(0); 59 | } 60 | 61 | /** 62 | * 堆化 63 | */ 64 | public void heapify(int[] arr) { 65 | if (arr.length > capacity) { 66 | throw new IndexOutOfBoundsException(); 67 | } 68 | 69 | data = arr; 70 | size = arr.length; 71 | 72 | // 对每个节点进行 73 | for (int i = 0; i < size; i++) { 74 | siftUp(i); 75 | } 76 | } 77 | 78 | /** 79 | * 父节点索引 80 | */ 81 | private int parent(int childId) { 82 | int parent = (childId - 1) / 2; 83 | return parent >= 0 ? parent : -1; // 小于0,说明无父节点,当前节点是根节点 84 | } 85 | 86 | /** 87 | * 左子 88 | */ 89 | private int leftChild(int parent) { 90 | int left = 2 * parent + 1; 91 | return left < size ? left : -1; // 越界说明不存在 92 | } 93 | 94 | /** 95 | * 左子 96 | */ 97 | private int rightChild(int parent) { 98 | int right = 2 * parent + 2; 99 | return right < size ? right : -1; // 越界说明不存在 100 | } 101 | 102 | /** 103 | * 上浮:当前节点大于父节点,和父节点交换,递归向上直至不满足条件 104 | */ 105 | private void siftUp(int i) { 106 | int parent = parent(i); 107 | 108 | if (parent == -1) { // 超过根节点 109 | return; 110 | } 111 | 112 | if (data[parent] >= data[i]) { // 父节点大于等于子节点,插入到争取的位置 113 | return; 114 | } 115 | 116 | // 父节点小于子节点,交换 117 | swap(parent, i); 118 | 119 | // 递归向上处理 120 | siftUp(parent); 121 | } 122 | 123 | /** 124 | * 下沉:当前节点小于某个子节点,找到较大的子节点并交换,递归向下直至满足条件 125 | */ 126 | private void siftDown(int i) { 127 | int leftChild = leftChild(i); 128 | int rightChild = rightChild(i); 129 | 130 | if (leftChild == -1 && rightChild == -1) { // 没有左右子,直接退出 131 | return; 132 | } 133 | 134 | int largerChild = leftChild; // 假定左子较大 135 | 136 | if (rightChild != -1 && data[rightChild] > data[leftChild]) { 137 | largerChild = rightChild; 138 | } 139 | 140 | if (data[largerChild] > data[i]) { 141 | swap(largerChild, i); 142 | 143 | // 向下递归 144 | siftDown(largerChild); 145 | } 146 | } 147 | 148 | /** 149 | * 交换 150 | */ 151 | private void swap(int i, int j) { 152 | int tmp = data[i]; 153 | data[i] = data[j]; 154 | data[j] = tmp; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/outer/ReplaceSelectionSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.outer; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.PrintWriter; 5 | import java.util.ArrayList; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.PriorityQueue; 9 | 10 | import static com.kk.util.FileUtil.*; 11 | 12 | 13 | /** 14 | * 三元最小堆实现置换选择排序 15 | */ 16 | public class ReplaceSelectionSort { 17 | // int heapSize = 100 * 1000; 18 | int heapSize = 3; 19 | 20 | // java优先级队列内部默认最小堆 21 | PriorityQueue minHeap; 22 | 23 | public ReplaceSelectionSort(int heapSize) { 24 | this.heapSize = heapSize; 25 | minHeap = new PriorityQueue(heapSize); 26 | } 27 | 28 | public ReplaceSelectionSort() { 29 | minHeap = new PriorityQueue(heapSize); 30 | } 31 | 32 | /** 33 | * 分割大文件成初始顺串 34 | */ 35 | public void splitToSeqfile(String original, List seqfiles) { 36 | long count = countLines(original); 37 | 38 | if (count == 0) return; 39 | 40 | BufferedReader oriReader = null; 41 | 42 | String waitFile = null; 43 | int waitCount = 0; 44 | PrintWriter waitWriter = null; 45 | 46 | PrintWriter seqWriter = null; 47 | 48 | try { 49 | oriReader = bufferedReader(original); 50 | 51 | // 建立初始堆 52 | for (int i = 0; i < heapSize; i++) { 53 | String line = oriReader.readLine(); 54 | 55 | if (null == line) break; 56 | 57 | minHeap.add(Integer.parseInt(line)); 58 | } 59 | 60 | // 将最小值写入顺串文件 61 | String seqfile = nextSeqFile(original); 62 | 63 | if (null != seqfiles) { 64 | seqfiles.add(seqfile); 65 | } 66 | 67 | seqWriter = new PrintWriter(seqfile); 68 | 69 | Integer min = minHeap.poll(); 70 | seqWriter.println(min); 71 | 72 | // 读取剩余文件,判断是入堆还是写入等待文件 73 | while (true) { 74 | String line = oriReader.readLine(); 75 | 76 | if (null == line) break; // 文件末尾,退出 77 | 78 | int num = Integer.parseInt(line); 79 | 80 | if (num < min) { // 小于最小值,进入下一轮等待 81 | if (null == waitFile) { 82 | waitFile = nextWaitFile(original); 83 | waitWriter = new PrintWriter(waitFile); 84 | } 85 | 86 | waitCount++; 87 | waitWriter.println(num); 88 | } else { // 大于等于最小值,入堆 89 | minHeap.add(num); 90 | // 新最小值写入seq序列 91 | min = minHeap.poll(); 92 | seqWriter.println(min); 93 | } 94 | } 95 | 96 | // 将堆剩余元素刷新到seq文件 97 | PrintWriter finalSeqWriter = seqWriter; 98 | 99 | while (true) { 100 | Integer num = minHeap.poll(); 101 | 102 | if (null == num) break; 103 | 104 | finalSeqWriter.println(num); 105 | } 106 | 107 | } catch (Exception e) { 108 | e.printStackTrace(); 109 | } finally { 110 | closeReader(oriReader); 111 | closeWriter(seqWriter); 112 | closeWriter(waitWriter); 113 | } 114 | 115 | if (waitCount != 0) { // 有等待下轮的 116 | splitToSeqfile(waitFile, seqfiles); 117 | } 118 | } 119 | 120 | /** 121 | * 准备等待文件 122 | */ 123 | String nextWaitFile(String original) { 124 | return prepareSuffixedFile(original, "-wait-"); 125 | } 126 | 127 | /** 128 | * 准备下一个序列文件 129 | */ 130 | String nextSeqFile(String original) { 131 | return prepareSuffixedFile(original, "-seq-"); 132 | } 133 | 134 | String prepareSuffixedFile(String original, String suffix) { 135 | String flag = "-wait-"; 136 | 137 | if (!original.contains(flag)) { 138 | return original + suffix + 0; 139 | } 140 | 141 | String[] parts = original.split(flag); 142 | String name = parts[0]; 143 | int seq = Integer.parseInt(parts[1]) + 1; 144 | 145 | return name + suffix + seq; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/SingleLinkedList.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure; 2 | 3 | /** 4 | * 节点结构 5 | */ 6 | class Node { 7 | Object val; // 节点值 8 | Node next; // 下一个节点的指针 9 | 10 | public Node(Object val) { 11 | this.val = val; 12 | } 13 | } 14 | 15 | /** 16 | * 遍历节点时候的消费者 17 | */ 18 | interface NodeConsumer { 19 | boolean onNode(Node node, int id); 20 | } 21 | 22 | /** 23 | * 单向链表 24 | */ 25 | public class SingleLinkedList { 26 | // 头、尾节点 27 | Node head, tail; 28 | 29 | // 大小 30 | int size; 31 | 32 | /** 33 | * 尾部插入新节点 34 | */ 35 | public SingleLinkedList add(Object val) { 36 | Node node = new Node(val); 37 | 38 | if (size == 0) { // 没有元素 39 | head = tail = node; 40 | size = 1; 41 | return this; 42 | } 43 | 44 | // 有元素,肯定有尾部节点,直接连在后侧 45 | tail.next = node; 46 | tail = node; 47 | 48 | size++; 49 | 50 | return this; 51 | } 52 | 53 | /** 54 | * 在指定位置插入 55 | */ 56 | public SingleLinkedList insert(int i, Object val) { 57 | checkBounds(i); 58 | 59 | if (i == size - 1) { // 尾部插入==尾部追加 60 | return add(val); 61 | } 62 | 63 | Node nodeI = new Node(val); 64 | 65 | foreach((node, id) -> { 66 | if (id == i - 1) { // 找到要插入的位置 67 | Node curNodeI = node.next; // 当前的i节点 68 | 69 | node.next = nodeI; // 指向新的i节点 70 | nodeI.next = curNodeI; // 新旧i节点关联 71 | size++; 72 | 73 | return false; 74 | } 75 | 76 | return true; 77 | }); 78 | 79 | return this; 80 | } 81 | 82 | /** 83 | * 删除指定位置的节点 84 | */ 85 | public void delete(int id) { 86 | checkBounds(id); 87 | 88 | foreach((node, pos) -> { 89 | if (pos == id - 1) { 90 | Node prev = node; 91 | Node succeed = node.next.next; 92 | 93 | // 前驱指向后继 94 | prev.next = succeed; 95 | size--; 96 | return false; 97 | } 98 | 99 | return true; 100 | }); 101 | } 102 | 103 | /** 104 | * 获取指定位置的值 105 | */ 106 | public Object get(int id) { 107 | checkBounds(id); 108 | 109 | final Object[] vals = new Object[1]; 110 | 111 | foreach(new NodeConsumer() { 112 | @Override 113 | public boolean onNode(Node node, int pos) { 114 | if (pos == id) { 115 | vals[0] = node.val; 116 | return false; 117 | } else { 118 | return true; 119 | } 120 | 121 | } 122 | }); 123 | 124 | return vals[0]; 125 | } 126 | 127 | /** 128 | * 检查是否越界 129 | */ 130 | private void checkBounds(int id) { 131 | if (id < 0 || id >= size) { 132 | throw new IndexOutOfBoundsException(); 133 | } 134 | } 135 | 136 | /** 137 | * 遍历每个节点 138 | */ 139 | public void foreach(NodeConsumer nodeConsumer) { 140 | if (size == 0) return; 141 | 142 | Node node = head; 143 | 144 | int cursor = 0; // 遍历游标 145 | 146 | if (null != nodeConsumer) { 147 | if (!nodeConsumer.onNode(node, cursor)) { 148 | return; 149 | } 150 | } 151 | 152 | while (node.next != null) { 153 | cursor++; 154 | 155 | node = node.next; // 下一个节点 156 | 157 | if (null != nodeConsumer) { 158 | if (!nodeConsumer.onNode(node, cursor)) { 159 | return; 160 | } 161 | } 162 | } 163 | } 164 | 165 | /** 166 | * 查询第一次出现的节点 167 | */ 168 | public Node find(Object val){ 169 | final Node[] foundNode = new Node[1]; 170 | 171 | foreach((node, id) -> { 172 | if (node.val==val){ // 首次找到、不必再遍历后续节点 173 | foundNode[0] =node; 174 | return false; 175 | } 176 | 177 | return true; 178 | }); 179 | 180 | return foundNode[0]; 181 | } 182 | 183 | 184 | @Override 185 | public String toString() { 186 | StringBuilder builder = new StringBuilder(); 187 | builder.append("SingleLinkedList["); 188 | 189 | foreach((node, id) -> { 190 | if (id == 0) { 191 | builder.append(node.val); 192 | } else { 193 | builder.append("-->" + node.val); 194 | } 195 | 196 | return true; 197 | }); 198 | 199 | builder.append("]"); 200 | return builder.toString(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/com/kk/sort/outer/KOuterSort.java: -------------------------------------------------------------------------------- 1 | package com.kk.sort.outer; 2 | 3 | import com.kk.util.FileUtil; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.FileNotFoundException; 7 | import java.io.IOException; 8 | import java.io.PrintWriter; 9 | import java.nio.file.Files; 10 | import java.nio.file.Paths; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * K路外排序 16 | */ 17 | public class KOuterSort { 18 | int k = 3; 19 | 20 | // int heapSize=100*1000; 21 | 22 | public void sort(String original) throws IOException { 23 | long countLines = FileUtil.countLines(original); 24 | // 置换-选择拆分成大小不一的顺串文件 25 | ReplaceSelectionSort repSelSorter = new ReplaceSelectionSort((int) (countLines / 5)); 26 | 27 | List seqfilesList = new ArrayList<>(); 28 | repSelSorter.splitToSeqfile(original, seqfilesList); 29 | 30 | // 归并初始顺串 31 | merge(seqfilesList); 32 | } 33 | 34 | void merge(List seqsList) throws IOException { 35 | int len = seqsList.size(); 36 | 37 | if (len == 0) return; // 合并结束 38 | 39 | if (len == 1) { // 最后一个有序文件 40 | Files.copy(Paths.get(seqsList.get(0)), Paths.get("data/result-ordered")); 41 | return; 42 | } 43 | 44 | List mergesList = new ArrayList<>(); 45 | 46 | for (int i = 0; i < len; i += k) {// 每k个分一组 47 | // 打开每个要合并的分支文件 48 | List readerList = openReadersForBranch(seqsList, len, i); 49 | 50 | if (readerList.size() == 0) break; 51 | 52 | // 初始化败者树 53 | LoserTree loserTree = initLoserTreeFromReaders(readerList); 54 | 55 | // 不断的读取最小分支的下一个值更新败者树,并将最小值写入输出文件 56 | mergeByLoserTree(seqsList, mergesList, readerList, loserTree); 57 | } 58 | 59 | // 递归处理新合并的文件 60 | merge(mergesList); 61 | } 62 | 63 | /** 64 | * 通过最小树不断的合并每个分支文件 65 | */ 66 | private void mergeByLoserTree(List seqsList, List mergesList, List readerList, 67 | LoserTree loserTree) throws IOException { 68 | String mergeFile = nextMergeFile(seqsList.get(0)); 69 | mergesList.add(mergeFile); 70 | PrintWriter mergeWriter = new PrintWriter(mergeFile); 71 | 72 | while (loserTree.hashNext()) { // 还有下个元素,说明 73 | int minBranch = loserTree.minBranch(); 74 | int min = loserTree.peek(); 75 | 76 | // 写入输出 77 | mergeWriter.println(min); 78 | 79 | int nextVal = Integer.MAX_VALUE; 80 | 81 | String line = readerList.get(minBranch).readLine(); 82 | 83 | if (null != line) { 84 | nextVal = Integer.parseInt(line); 85 | } 86 | 87 | loserTree.pop(nextVal); 88 | } 89 | 90 | FileUtil.closeWriter(mergeWriter); 91 | } 92 | 93 | /** 94 | * 初始化败者树 95 | */ 96 | private LoserTree initLoserTreeFromReaders(List readerList) throws IOException { 97 | int[] branches = new int[readerList.size()]; 98 | 99 | for (int j = 0; j < readerList.size(); j++) { 100 | branches[j] = Integer.parseInt(readerList.get(j).readLine()); 101 | } 102 | 103 | LoserTree loserTree = new LoserTree(); 104 | loserTree.create(branches); 105 | 106 | return loserTree; 107 | } 108 | 109 | /** 110 | * 准备合并文件 111 | */ 112 | String nextMergeFile(String seqOrMerge) { 113 | String seqFlag = "-seq-"; 114 | String mergeFlag = "-merge-"; 115 | 116 | String[] parts; 117 | Integer order = 0; 118 | 119 | if (seqOrMerge.contains(seqFlag)) { // 初始顺串 ori-seq-0 -> ori-merge-0 120 | parts = seqOrMerge.split(seqFlag); 121 | order = Integer.parseInt(parts[1]); 122 | return parts[0] + mergeFlag + order; 123 | } else { // 中间合并文件 ori-merge-0 -> ori-merge-0-0 124 | parts = seqOrMerge.split(mergeFlag); 125 | order = Integer.parseInt(parts[parts.length - 1]); 126 | return seqOrMerge + "-" + order; 127 | } 128 | } 129 | 130 | /** 131 | * 打开每个要合并的分支文件 132 | */ 133 | private List openReadersForBranch(List seqsList, int len, int i) { 134 | List readerList = new ArrayList<>(); 135 | 136 | for (int j = 0; j < i + k; j++) { 137 | if (j > len - 1) continue; 138 | 139 | try { 140 | readerList.add(FileUtil.bufferedReader(seqsList.get(j))); 141 | } catch (FileNotFoundException e) { 142 | e.printStackTrace(); 143 | } 144 | } 145 | 146 | return readerList; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/bst/BST.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.bst; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 二叉搜索树 7 | */ 8 | public class BST { 9 | // 根节点 10 | BSTNode root; 11 | 12 | int size; 13 | 14 | /** 15 | * 添加新的节点 16 | */ 17 | public void put(String key, Object val) { 18 | root = put(root, key, val); 19 | size++; 20 | } 21 | 22 | /** 23 | * 递归尝试插入到子树的叶子节点,插入完成后将新的左子树或右子树返回给父调用 24 | */ 25 | private BSTNode put(BSTNode root, String key, Object val) { 26 | if (root == null) { // 遍历到叶子节点,创建新的叶子,插入到上个节点 27 | return new BSTNode(key, val); 28 | } 29 | 30 | int cmp = root.key.compareTo(key); 31 | 32 | if (cmp == 0) { // 相等,更新value 33 | root.val = val; 34 | } else if (cmp > 0) { // 插入到左子树,将插入完成后左子树关联到左连接上 35 | root.left = put(root.left, key, val); 36 | } else { 37 | root.right = put(root.right, key, val); // 向右子树插入,完成后将新的右子树连接到 38 | } 39 | 40 | return root; 41 | } 42 | 43 | public void deleteMin() { 44 | root = deleteMin(root); 45 | } 46 | 47 | /** 48 | * 删除最小值节点 49 | */ 50 | private BSTNode deleteMin(BSTNode root) { 51 | if (root == null) { 52 | return null; 53 | } 54 | 55 | if (root.left == null) { // 无左子树,已经是最小节点 56 | return root.right; // 将最小节点的右节点返回给父节点 57 | } 58 | 59 | root.left = deleteMin(root.left); // 删除左子树的最小节点,成功后返回连接到当前节点的左侧 60 | return root; 61 | } 62 | 63 | /** 64 | * 删除任意节点 65 | * 递归找到要删除的节点, 66 | * 用后继节点替换删除节点 67 | * 删除后继节点 68 | */ 69 | public void delete(String key) { 70 | root = delete(root, key); 71 | size--; 72 | } 73 | 74 | private BSTNode delete(BSTNode root, String key) { 75 | if (root == null) { 76 | return null; 77 | } 78 | 79 | int cmp = root.key.compareTo(key); 80 | 81 | if (cmp > 0) { 82 | root.left = delete(root.left, key); // 递归到左子树删除 83 | } else if (cmp < 0) { 84 | root.right = delete(root.right, key); // 递归到右子树删除 85 | } else if (cmp == 0) { 86 | // 无左子,直接将右子树拼接到父节点 87 | if (root.left == null) { 88 | return root.right; 89 | } 90 | 91 | // 无右子,直接将左子树连接到父节点 92 | if (root.right == null) { 93 | return root.left; 94 | } 95 | 96 | // 双子节点 97 | // 找到后继节点 98 | BSTNode succeed = minNode(root.right); 99 | // 拷贝后继值 100 | root.val = succeed.val; 101 | // 删除后继节点 102 | root.right = deleteMin(root.right); 103 | } 104 | 105 | // 返回最后的根节点 106 | return root; 107 | } 108 | 109 | /** 110 | * 大小 111 | */ 112 | public int size() { 113 | return size; 114 | } 115 | 116 | /** 117 | * 获取某个键值 118 | */ 119 | public Object get(String key) { 120 | return get(root, key); 121 | } 122 | 123 | /** 124 | * 通过递归不断查找左右子树,最终将查到的结果层层返回给父调用 125 | */ 126 | private Object get(BSTNode node, String key) { 127 | if (node == null) { // 查找不到,返回null 128 | return null; 129 | } 130 | 131 | // 和当前结点比较 132 | int cmp = key.compareTo(node.key); 133 | 134 | if (cmp < 0) { 135 | return get(node.left, key); // 递归在左子树查找 136 | } else if (cmp > 0) { // 递归在右子树查找 137 | return get(node.right, key); 138 | } else { 139 | return node.val; // 查找命中返回值,不再递归 140 | } 141 | } 142 | 143 | /** 144 | * 查找某棵树的节点 145 | */ 146 | public BSTNode minNode(BSTNode root) { 147 | if (root == null) { // 最后未找到 148 | return null; 149 | } 150 | 151 | if (root.left == null) { // 无左子,自己就是最小 152 | return root; 153 | } else { // 有左子,向左子树查,结果返回返回给父调用 154 | return minNode(root.left); 155 | } 156 | } 157 | 158 | /** 159 | * 中序遍历 左子树 -> 根 -> 右子树 160 | */ 161 | protected ArrayList inorderTraverse() { 162 | ArrayList valuesList = new ArrayList<>(); 163 | 164 | inorderTraverse(root, valuesList); 165 | 166 | return valuesList; 167 | } 168 | 169 | protected void inorderTraverse(BSTNode root, ArrayList valuesList) { 170 | if (root == null) { 171 | return; 172 | } 173 | 174 | // 先递归将左子树遍历好 175 | inorderTraverse(root.left, valuesList); 176 | // 中间访问根节点 177 | valuesList.add(root.val); 178 | // 后递归将右子树遍历好 179 | inorderTraverse(root.right, valuesList); 180 | } 181 | 182 | @Override 183 | public String toString() { 184 | StringBuilder builder = new StringBuilder(); 185 | 186 | builder.append("BST:["); 187 | 188 | final int[] i = {0}; 189 | inorderTraverse() 190 | .forEach(obj -> { 191 | if (i[0] == 0) { 192 | builder.append(obj); 193 | } else { 194 | builder.append("\t" + obj); 195 | } 196 | 197 | i[0]++; 198 | }); 199 | 200 | builder.append("]"); 201 | 202 | return builder.toString(); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/skiplist/SkipList.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.skiplist; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * 跳表 7 | */ 8 | public class SkipList { 9 | // 最高层的头尾节点 10 | public SkipListEntry head; 11 | public SkipListEntry tail; 12 | 13 | // 高度 14 | public int height; 15 | // 0层总节点数 16 | public int size; 17 | 18 | public Random random; // Coin toss 19 | 20 | public SkipList() { 21 | // 创建一个 -oo 和一个 +oo 对象 22 | head = new SkipListEntry(SkipListEntry.INFINITE_NEGATIVE, null); 23 | tail = new SkipListEntry(SkipListEntry.INFINITE_POSITIVE, null); 24 | 25 | // 将 -oo 和 +oo 相互连接 26 | head.right = tail; 27 | tail.left = head; 28 | 29 | size = 0; 30 | height = 0; 31 | random = new Random(); 32 | } 33 | 34 | /** 35 | * 查找指定key的节点,存在则返回0层该节点,不存在则返回0层前驱节点 36 | */ 37 | private SkipListEntry findEntry(String key) { 38 | SkipListEntry entryCoursor; // 游标 39 | 40 | // 从head头节点开始查找 41 | entryCoursor = head; 42 | 43 | while (true) { 44 | // 该层从左向右查找,直到右节点的key值大于要查找的key值 45 | while (entryCoursor.right.key != SkipListEntry.INFINITE_POSITIVE 46 | && entryCoursor.right.key.compareTo(key) <= 0) { // 小于该key的话一直往右查找 47 | entryCoursor = entryCoursor.right; 48 | } 49 | 50 | // 该层找到了前驱节点,如果有更低层的节点,则向低层移动 51 | if (entryCoursor.down != null) { 52 | entryCoursor = entryCoursor.down; 53 | } else { 54 | break; // 已经是最下层,退出查找循环 55 | } 56 | } 57 | 58 | // 最下层找到符合的节点或前驱节点,满足 entryCoursor<= key 59 | return entryCoursor; 60 | } 61 | 62 | /** 63 | * 获取指定的value 64 | */ 65 | public Integer get(String key) { 66 | SkipListEntry foundEntry = findEntry(key); 67 | 68 | if (foundEntry.key.equals(key)) { // key相等,表示是对应节点 69 | return foundEntry.value; 70 | } else { // 不相等说明只查找到了前驱节点 71 | return null; 72 | } 73 | } 74 | 75 | /** 76 | * 添加kv 77 | */ 78 | public Integer put(String key, Integer value) { 79 | // 找到最底层的对应节点或前驱节点 80 | SkipListEntry prev = findEntry(key); 81 | 82 | // 如果跳跃表中存在含有key值的节点,则进行value的修改操作即可完成 83 | if (prev.key.equals(key)) { 84 | Integer oldValue = prev.value; 85 | // 修改成新值 86 | prev.value = value; 87 | return oldValue; 88 | } 89 | 90 | // 如果跳跃表中不存在含有key值的节点,则进行新增操作,此时0层已插入结束 91 | SkipListEntry toPutEntry = insertByClockwise(key, value, prev); 92 | 93 | // 向上跳跃的高度 94 | int jumpHeight = 0; 95 | 96 | // 再使用随机数决定是否要向更高level攀升 97 | while (random.nextDouble() < 0.5) { 98 | // 如果新元素的级别已经达到跳跃表的最大高度,则新建空白层 99 | if (jumpHeight >= height) { 100 | addEmptyLevel(); 101 | } 102 | 103 | // 从该层前驱节点向左查询和上层关联的前驱节点 104 | prev = jumpUp(prev); 105 | 106 | // 创建将插入该层的索引节点,不含value 107 | SkipListEntry indexEntry = insertByClockwise(key, null, prev); 108 | 109 | // 上下层关联(索引节点与下层索引或值节点关联) 110 | indexEntry.down = toPutEntry; 111 | toPutEntry.up = indexEntry; 112 | 113 | toPutEntry = indexEntry; 114 | 115 | jumpHeight = jumpHeight + 1; 116 | } 117 | 118 | size = size + 1; 119 | 120 | // 返回null,没有旧节点的value值 121 | return null; 122 | } 123 | 124 | /** 125 | * 从该层前驱节点向左查询和上层关联的前驱节点 126 | */ 127 | private SkipListEntry jumpUp(SkipListEntry prev) { 128 | while (prev.up == null) { 129 | prev = prev.left; 130 | } 131 | 132 | prev = prev.up; 133 | return prev; 134 | } 135 | 136 | /** 137 | * 顺时针关联插入法 138 | */ 139 | private SkipListEntry insertByClockwise(String key, Integer value, SkipListEntry prev) { 140 | SkipListEntry toPutEntry = new SkipListEntry(key, value); 141 | 142 | // 后继节点 143 | SkipListEntry suceed = prev.right; 144 | // 指向后继 145 | toPutEntry.right = suceed; 146 | // 后继指向该节点 147 | suceed.left = toPutEntry; 148 | // 指向前驱 149 | toPutEntry.left = prev; 150 | // 前驱指向该节点 151 | prev.right = toPutEntry; 152 | 153 | return toPutEntry; 154 | } 155 | 156 | /** 157 | * 新值空白层 158 | */ 159 | private void addEmptyLevel() { 160 | SkipListEntry newHead, newTail; 161 | 162 | newHead = new SkipListEntry(SkipListEntry.INFINITE_NEGATIVE, null); 163 | newTail = new SkipListEntry(SkipListEntry.INFINITE_POSITIVE, null); 164 | 165 | newHead.right = newTail; 166 | newTail.left = newHead; 167 | 168 | // 上下层关联 169 | newHead.down = head; 170 | newTail.down = tail; 171 | head.up = newHead; 172 | tail.up = newTail; 173 | 174 | // 更新最高层头尾 175 | head = newHead; 176 | tail = newTail; 177 | 178 | height = height + 1; 179 | } 180 | 181 | /** 182 | * 剔除某个key 183 | */ 184 | public Integer remove(String key) { 185 | SkipListEntry found; 186 | 187 | found = findEntry(key); 188 | 189 | if (!found.key.equals(key)) { // key不匹配,说明是前驱节点,没有改key的节点 190 | return null; 191 | } 192 | 193 | Integer oldValue = found.value; 194 | 195 | while (found != null) { 196 | // 对应的上传索引节点 197 | SkipListEntry upIndex = found.up; 198 | 199 | // 前驱和后继直接关联 200 | SkipListEntry prev = found.left; 201 | SkipListEntry succeed = found.right; 202 | prev.right = succeed; 203 | succeed.left = prev; 204 | 205 | found = upIndex; 206 | } 207 | 208 | return oldValue; 209 | } 210 | 211 | /** 212 | * 从上、往下遍历每个层的所有节点 213 | */ 214 | public void foreach(EntryConsumer consumer) { 215 | if (size == 0) { 216 | return; 217 | } 218 | 219 | SkipListEntry levelHead = head; 220 | SkipListEntry cursor = levelHead; 221 | 222 | for (int level = height; level >= 0; level--) { // 外层控制遍历的层级,从上往下层遍历 223 | int pos = 0; 224 | 225 | while (cursor.right != null) { 226 | if (!isEndpoint(cursor)) { 227 | if (null != consumer && !consumer.item(level, pos, cursor)) { 228 | return; 229 | } 230 | 231 | pos++; 232 | } 233 | 234 | cursor = cursor.right; 235 | } 236 | 237 | // 记录下层头节点 238 | cursor = levelHead = levelHead.down; 239 | } 240 | } 241 | 242 | /** 243 | * 判断一个节点是否是终端节点(头、尾)节点 244 | */ 245 | private boolean isEndpoint(SkipListEntry entry) { 246 | return entry.key.equals(SkipListEntry.INFINITE_NEGATIVE) || entry.key.equals(SkipListEntry.INFINITE_POSITIVE); 247 | } 248 | 249 | @Override 250 | public String toString() { 251 | StringBuilder builder = new StringBuilder(); 252 | 253 | final int[] lastLevel = {height}; 254 | 255 | foreach((EntryConsumer) (level, pos, entry) -> { 256 | if (level != lastLevel[0]) { 257 | lastLevel[0] = level; 258 | builder.append("\n"); 259 | } 260 | 261 | builder.append("-->" + entry.key); 262 | 263 | return true; 264 | }); 265 | 266 | return builder.toString(); 267 | } 268 | } -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/avl/AVLTree.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.avl; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 平衡二叉搜索树 7 | */ 8 | public class AVLTree { 9 | // 根节点 10 | AVLNode root; 11 | 12 | int size; 13 | 14 | /** 15 | * 添加新的节点 16 | */ 17 | public void put(String key, Object val) { 18 | root = put(root, key, val); 19 | size++; 20 | } 21 | 22 | /** 23 | * 递归尝试插入到子树的叶子节点,插入完成后将新的左子树或右子树返回给父调用 24 | */ 25 | private AVLNode put(AVLNode root, String key, Object val) { 26 | if (root == null) { // 遍历到叶子节点,创建新的叶子,插入到上个节点 27 | return new AVLNode(key, val); 28 | } 29 | 30 | int cmp = root.key.compareTo(key); 31 | 32 | if (cmp == 0) { // 相等,更新value 33 | root.val = val; 34 | } else if (cmp > 0) { // 插入到左子树,将插入完成后左子树关联到左连接上 35 | root.left = put(root.left, key, val); 36 | } else { 37 | root.right = put(root.right, key, val); // 向右子树插入,完成后将新的右子树连接到 38 | } 39 | 40 | // 插入成功、回到父调用、准备连接到父层的时候做平衡处理 41 | refreshDepth(root); // 该子树深度 42 | 43 | if (isUnbanlance(root)) { // 失衡 --> 调整平衡 44 | root = balance(root); // 返回平衡后的新树根 45 | } 46 | 47 | return root; 48 | } 49 | 50 | public void deleteMin() { 51 | root = deleteMin(root); 52 | } 53 | 54 | /** 55 | * 删除最小值节点 56 | */ 57 | private AVLNode deleteMin(AVLNode root) { 58 | if (root == null) { 59 | return null; 60 | } 61 | 62 | if (root.left == null) { // 无左子树,已经是最小节点 63 | return root.right; // 将最小节点的右节点返回给父节点 64 | } 65 | 66 | root.left = deleteMin(root.left); // 删除左子树的最小节点,成功后返回连接到当前节点的左侧 67 | return root; 68 | } 69 | 70 | /** 71 | * 删除任意节点 72 | * 递归找到要删除的节点, 73 | * 用后继节点替换删除节点 74 | * 删除后继节点 75 | */ 76 | public void delete(String key) { 77 | root = delete(root, key); 78 | size--; 79 | } 80 | 81 | private AVLNode delete(AVLNode root, String key) { 82 | if (root == null) { 83 | return null; 84 | } 85 | 86 | int cmp = root.key.compareTo(key); 87 | 88 | if (cmp > 0) { 89 | root.left = delete(root.left, key); // 递归到左子树删除 90 | } else if (cmp < 0) { 91 | root.right = delete(root.right, key); // 递归到右子树删除 92 | } else if (cmp == 0) { 93 | // 无左子,直接将右子树拼接到父节点 94 | if (root.left == null) { 95 | return root.right; 96 | } 97 | 98 | // 无右子,直接将左子树连接到父节点 99 | if (root.right == null) { 100 | return root.left; 101 | } 102 | 103 | // 双子节点 104 | // 找到后继节点 105 | AVLNode succeed = minNode(root.right); 106 | // 拷贝后继值 107 | root.val = succeed.val; 108 | // 删除后继节点 109 | root.right = deleteMin(root.right); 110 | } 111 | 112 | refreshDepth(root); // 该子树深度 113 | 114 | if (isUnbanlance(root)) { // 失衡 --> 调整平衡 115 | root = balance(root); // 返回平衡后的新树根 116 | } 117 | 118 | // 返回最后的根节点 119 | return root; 120 | } 121 | 122 | /** 123 | * 大小 124 | */ 125 | public int size() { 126 | return size; 127 | } 128 | 129 | /** 130 | * 获取某个键值 131 | */ 132 | public Object get(String key) { 133 | return get(root, key); 134 | } 135 | 136 | /** 137 | * 通过递归不断查找左右子树,最终将查到的结果层层返回给父调用 138 | */ 139 | private Object get(AVLNode node, String key) { 140 | if (node == null) { // 查找不到,返回null 141 | return null; 142 | } 143 | 144 | // 和当前结点比较 145 | int cmp = key.compareTo(node.key); 146 | 147 | if (cmp < 0) { 148 | return get(node.left, key); // 递归在左子树查找 149 | } else if (cmp > 0) { // 递归在右子树查找 150 | return get(node.right, key); 151 | } else { 152 | return node.val; // 查找命中返回值,不再递归 153 | } 154 | } 155 | 156 | /** 157 | * 查找某棵树的节点 158 | */ 159 | public AVLNode minNode(AVLNode root) { 160 | if (root == null) { // 最后未找到 161 | return null; 162 | } 163 | 164 | if (root.left == null) { // 无左子,自己就是最小 165 | return root; 166 | } else { // 有左子,向左子树查,结果返回返回给父调用 167 | return minNode(root.left); 168 | } 169 | } 170 | 171 | /** 172 | * 中序遍历 左子树 -> 根 -> 右子树 173 | */ 174 | protected ArrayList inorderTraverse() { 175 | ArrayList valuesList = new ArrayList<>(); 176 | 177 | inorderTraverse(root, valuesList); 178 | 179 | return valuesList; 180 | } 181 | 182 | protected void inorderTraverse(AVLNode root, ArrayList valuesList) { 183 | if (root == null) { 184 | return; 185 | } 186 | 187 | // 先递归将左子树遍历好 188 | inorderTraverse(root.left, valuesList); 189 | // 中间访问根节点 190 | valuesList.add(root.key + "-" + root.depth); 191 | // 后递归将右子树遍历好 192 | inorderTraverse(root.right, valuesList); 193 | } 194 | 195 | /** 196 | * --------------------- AVL树相关的api 197 | */ 198 | 199 | 200 | /** 201 | * 刷新某科子树的深度 202 | */ 203 | public int refreshDepth() { 204 | return refreshDepth(root); 205 | } 206 | 207 | public int refreshDepth(AVLNode root) { 208 | if (null == root) { // null节点的深度为-1 209 | return -1; 210 | } 211 | 212 | root.depth = Math.max(refreshDepth(root.left), refreshDepth(root.right)) + 1; // 较深子节点的深度值+1 213 | 214 | // 返回当前节点的深度 215 | return root.depth; 216 | } 217 | 218 | /** 219 | * 获取某节点的深度 220 | */ 221 | public int depth(AVLNode node) { 222 | return node != null ? node.depth : -1; 223 | } 224 | 225 | /** 226 | * 判断当前节点是否是失衡点 在刷新当前子树的高度后比较node.left > node.right 227 | */ 228 | public boolean isUnbanlance(AVLNode node) { 229 | return Math.abs(depth(node.left) - depth(node.right)) > 1; 230 | } 231 | 232 | /** 233 | * 左旋 234 | * 1、父子交换 -> 接收左子 -> 返回原右子作为新的父节点,连接到上层 235 | */ 236 | public AVLNode leftRotate(AVLNode parent) { 237 | AVLNode right = parent.right; 238 | AVLNode rightLeft = right.left; 239 | 240 | // 父子交换 241 | right.left = parent; 242 | 243 | // 接收左子 244 | parent.right = rightLeft; 245 | 246 | // 返回新的父节点 247 | return right; 248 | } 249 | 250 | /** 251 | * 右旋 252 | * 1、父子交换 -> 接收左子 -> 返回原左子作为新的父节点,连接到上层 253 | */ 254 | public AVLNode rightRotate(AVLNode parent) { 255 | AVLNode left = parent.left; 256 | AVLNode leftRight = left.right; 257 | 258 | // 父子交换 259 | left.right = parent; 260 | 261 | // 接收左子 262 | parent.left = leftRight; 263 | 264 | // 返回新的父节点 265 | return left; 266 | } 267 | 268 | /** 269 | * 判断是否是LL失衡类型 270 | */ 271 | public boolean isLL(AVLNode unbalance) { 272 | return isToLeft(unbalance) && isToLeft(unbalance.left); 273 | } 274 | 275 | /** 276 | * 判断是否是RR失衡类型 277 | */ 278 | public boolean isRR(AVLNode unbalance) { 279 | return isToRight(unbalance) && isToRight(unbalance.right); 280 | } 281 | 282 | /** 283 | * 判断是否是LR失衡类型 284 | */ 285 | public boolean isLR(AVLNode unbalance) { 286 | return isToLeft(unbalance) && isToRight(unbalance.left); 287 | } 288 | 289 | /** 290 | * 判断是否是RL失衡类型 291 | */ 292 | public boolean isRL(AVLNode unbalance) { 293 | return isToRight(unbalance) && isToLeft(unbalance.right); 294 | } 295 | 296 | /** 297 | * 最深树走向是否是往左 298 | */ 299 | public boolean isToLeft(AVLNode node) { 300 | return depth(node.left) - depth(node.right) > 0; 301 | } 302 | 303 | /** 304 | * 最深树走向是否是往左 305 | */ 306 | public boolean isToRight(AVLNode node) { 307 | return depth(node.right) - depth(node.left) > 0; 308 | } 309 | 310 | /** 311 | * 平衡,根据不同的失衡类型,应用对应的选择策略,返回平衡后的新的根节点 312 | * 313 | * @return 314 | */ 315 | public AVLNode balance(AVLNode unbalance) { 316 | if (isLL(unbalance)) { // 左左 -> 失衡点右旋 317 | return rightRotate(unbalance); 318 | } 319 | 320 | if (isRR(unbalance)) { // 右右 -> 失衡点左旋 321 | return leftRotate(unbalance); 322 | } 323 | 324 | if (isLR(unbalance)) { // 左右 -> 先深点1左旋(转换为左左)-> 以失衡点右旋 325 | // 旋转后新的父节点连接到失衡点的左侧 326 | unbalance.left = leftRotate(unbalance.left); 327 | return rightRotate(unbalance); 328 | } 329 | 330 | if (isRL(unbalance)) { // 右左 -> 先以深点1左旋(转换为右右) -> 以失衡点左旋 331 | unbalance.right = rightRotate(unbalance.right); 332 | return leftRotate(unbalance); 333 | } 334 | 335 | return unbalance; 336 | } 337 | 338 | @Override 339 | public String toString() { 340 | StringBuilder builder = new StringBuilder(); 341 | 342 | builder.append("AVLTree:["); 343 | 344 | final int[] i = {0}; 345 | inorderTraverse() 346 | .forEach(obj -> { 347 | if (i[0] == 0) { 348 | builder.append(obj); 349 | } else { 350 | builder.append("\t" + obj); 351 | } 352 | 353 | i[0]++; 354 | }); 355 | 356 | builder.append("]"); 357 | 358 | return builder.toString(); 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /src/main/java/com/kk/datastructure/tree2/redblack/RedBlackTree.java: -------------------------------------------------------------------------------- 1 | package com.kk.datastructure.tree2.redblack; 2 | 3 | /** 4 | * 红黑树 5 | */ 6 | public class RedBlackTree { 7 | public RedBlackNode root; 8 | public int size; 9 | 10 | /** 11 | * 存入 12 | */ 13 | public void put(String key, Object val) { 14 | if (null == key) return; 15 | 16 | if (size == 0) { 17 | root = new RedBlackNode(key, val, RedBlackNode.COLOR_BLACK); 18 | size++; 19 | } 20 | 21 | // 遍历的游标 22 | RedBlackNode insertPoint = findNearNode(key); // 对应节点或父节点 23 | 24 | RedBlackNode inserteNode = null; 25 | 26 | int cmp = insertPoint.key.compareTo(key); 27 | 28 | if (cmp == 0) { // 对应节点,更新val 29 | insertPoint.val = val; 30 | } else if (cmp > 0) { 31 | inserteNode = insertToLeft(key, val, insertPoint); 32 | } else if (cmp < 0) { 33 | inserteNode = insertToRight(key, val, insertPoint); 34 | } 35 | 36 | if (null != inserteNode) { // 说明新插入了节点,需要平衡 37 | root = balancePut(inserteNode); 38 | } 39 | } 40 | 41 | /** 42 | * 获取某val 43 | */ 44 | public Object get(String key) { 45 | if (size == 0) { 46 | return null; 47 | } 48 | 49 | // 找到临近点 50 | RedBlackNode node = findNode(key); 51 | 52 | return node == null ? null : node.val; 53 | } 54 | 55 | /** 56 | * 查找某key节点,找到的话,返回该节点,否则返回最后遍历到的父节点(用于判断插入) 57 | */ 58 | public RedBlackNode findNearNode(String key) { 59 | if (size == 0) { 60 | return null; 61 | } 62 | 63 | // 遍历的游标 64 | RedBlackNode cursor = root; 65 | int cmp; 66 | 67 | while (true) { 68 | cmp = cursor.key.compareTo(key); 69 | 70 | if (cmp > 0) { // 偏小,往左遍历 71 | if (null == cursor.left) { // 无左值,遍历到最左端,新建节点插入到左端 72 | return cursor; 73 | } else { 74 | cursor = cursor.left; 75 | continue; 76 | } 77 | } else if (cmp < 0) { // 偏大,往右比较 78 | if (null == cursor.right) { 79 | // 无右值,遍历到最右端 80 | return cursor; 81 | } else { 82 | cursor = cursor.right; 83 | continue; 84 | } 85 | } else if (cmp == 0) { // key节点,更新值,无需平衡 86 | return cursor; 87 | } 88 | } 89 | } 90 | 91 | public RedBlackNode findNode(String key) { 92 | if (size == 0) { 93 | return null; 94 | } 95 | 96 | RedBlackNode foundNode = findNearNode(key); 97 | 98 | if (foundNode.key.compareTo(key) == 0) { 99 | return foundNode; 100 | } else { 101 | return null; 102 | } 103 | } 104 | 105 | /** 106 | * 是否是对应节点 107 | */ 108 | private boolean isNodeOfKey(RedBlackNode node, String key) { 109 | return node.key.compareTo(key) == 0; 110 | } 111 | 112 | /** 113 | * 是否是左节点 114 | */ 115 | private boolean isLeftNode(RedBlackNode parent, RedBlackNode child) { 116 | return parent.key.compareTo(child.key) > 0; 117 | } 118 | 119 | /** 120 | * 删除某个key 121 | */ 122 | public void delete(String key) { 123 | if (size == 0) return; 124 | 125 | RedBlackNode foundNode = findNode(key); 126 | 127 | if (null == foundNode) return; 128 | 129 | // 判断孩子数 130 | RedBlackNode parent = foundNode.parent; 131 | RedBlackNode left = foundNode.left; 132 | RedBlackNode right = foundNode.right; 133 | 134 | DeletePoint deletePoint; 135 | 136 | if (null != left && null != right) { // 双子 -> 删除后继节点 137 | RedBlackNode succeedNode = succeedNode(foundNode); 138 | foundNode.key = succeedNode.key; 139 | foundNode.val = succeedNode.val; 140 | 141 | deletePoint = deleteEndChild(succeedNode.parent, succeedNode); 142 | } else { // 非双子,删除,将孩子(null)提升 143 | deletePoint = deleteEndChild(parent, foundNode); 144 | } 145 | 146 | size--; 147 | 148 | if (size == 1) { 149 | root.color = RedBlackNode.COLOR_BLACK; 150 | } 151 | 152 | if (size > 1) { 153 | balanceDelete(deletePoint); 154 | } 155 | } 156 | 157 | /** 158 | * 删除一个末尾子节点 159 | */ 160 | public DeletePoint deleteEndChild(RedBlackNode parent, RedBlackNode child) { 161 | if (null == parent) { 162 | return null; 163 | } 164 | 165 | if (isLeftNode(parent, child)) { 166 | parent.left = firstNonullChild(child); 167 | 168 | if (child.right != null) { 169 | child.right.parent = parent; 170 | } 171 | 172 | child.parent = null; 173 | child.right = null; 174 | 175 | return new DeletePoint(parent, child, child.right, true); 176 | } else { 177 | parent.right = firstNonullChild(child); 178 | 179 | if (child.left != null) { 180 | child.left.parent = parent; 181 | } 182 | 183 | child.parent = null; 184 | child.left = null; 185 | 186 | return new DeletePoint(parent, child, child.left, false); 187 | } 188 | } 189 | 190 | /** 191 | * 获取节点的第一个非空节点 192 | */ 193 | private RedBlackNode firstNonullChild(RedBlackNode node) { 194 | if (null != node.left) { 195 | return node.left; 196 | } 197 | 198 | if (null != node.right) { 199 | return node.right; 200 | } 201 | 202 | return null; 203 | } 204 | 205 | /** 206 | * 查找一个节点的后继节点 207 | */ 208 | public RedBlackNode succeedNode(RedBlackNode node) { 209 | if (null == node) { 210 | return null; 211 | } 212 | 213 | return minNode(node.right); 214 | } 215 | 216 | /** 217 | * 查找一个子树的最小值节点 218 | */ 219 | public RedBlackNode minNode(RedBlackNode root) { 220 | if (null == root) { 221 | return null; 222 | } 223 | 224 | // 不断向左遍历,直到没有左孩子 225 | while (null != root.left) { 226 | root = root.left; 227 | } 228 | 229 | return root; 230 | } 231 | 232 | /** 233 | * 插入到某节点的左端 234 | */ 235 | private RedBlackNode insertToLeft(String key, Object val, RedBlackNode node) { 236 | RedBlackNode inserteNode = new RedBlackNode(key, val); 237 | node.left = inserteNode; 238 | inserteNode.parent = node; 239 | size++; 240 | return inserteNode; 241 | } 242 | 243 | /** 244 | * 插入到某节点的左端 245 | */ 246 | private RedBlackNode insertToRight(String key, Object val, RedBlackNode node) { 247 | RedBlackNode inserteNode = new RedBlackNode(key, val); 248 | node.right = inserteNode; 249 | inserteNode.parent = node; 250 | size++; 251 | return inserteNode; 252 | } 253 | 254 | /** 255 | * 平衡插入 256 | */ 257 | private RedBlackNode balancePut(RedBlackNode insertedPoint) { 258 | if (insertedPoint == root) { 259 | root.color = RedBlackNode.COLOR_BLACK; 260 | return insertedPoint; 261 | } 262 | 263 | RedBlackNode parent = insertedPoint.parent; 264 | RedBlackNode grandpa = parent.parent; 265 | 266 | if (parent.isBlack() || grandpa == null) { // 父黑,不需要平衡 267 | return root; 268 | } 269 | 270 | boolean isLeftSide = parent.key.compareTo(insertedPoint.key) > 0; 271 | 272 | boolean isParentLeftSide = grandpa.key.compareTo(parent.key) > 0; // 父节点是否在祖父的左侧 273 | RedBlackNode uncle = isParentLeftSide ? grandpa.right : grandpa.left; 274 | 275 | if (null != uncle && uncle.isRed()) { // 父红,叔红 --> 颜色翻转 276 | flipColors(grandpa); 277 | return balancePut(grandpa); // 向上递归处理 278 | } else { // 父红,叔黑 -> 根据失衡类型,选择对应的旋转策略 279 | RedBlackNode greatPa = grandpa != null ? grandpa.parent : null; //曾祖父 280 | 281 | if (isParentLeftSide && isLeftSide) { // 左-左 -> 右旋 282 | linkToParent(greatPa, rightRotate(grandpa)); 283 | } 284 | 285 | if (isParentLeftSide && !isLeftSide) { // 左-右 -> 左旋 -> 左-左-> 右旋 286 | linkToParent(grandpa, leftRotate(parent)); 287 | linkToParent(greatPa, rightRotate(grandpa)); 288 | } 289 | 290 | if (!isParentLeftSide && !isLeftSide) { // 右-右 -> 左旋 291 | linkToParent(greatPa, leftRotate(grandpa)); 292 | } 293 | 294 | if (!isParentLeftSide && isLeftSide) { // 右-左 -> 右旋 -> 右-右-> 左旋 295 | linkToParent(grandpa, rightRotate(parent)); 296 | linkToParent(greatPa, leftRotate(grandpa)); 297 | } 298 | } 299 | 300 | return root; 301 | } 302 | 303 | /** 304 | * 删除后平衡 305 | */ 306 | private void balanceDelete(DeletePoint deletePoint) { 307 | RedBlackNode parent = deletePoint.parent; 308 | RedBlackNode del = deletePoint.del; 309 | RedBlackNode son = deletePoint.son; 310 | 311 | if (del != null && del.isRed()) { // 删除红节点,不需要平衡 312 | return; 313 | } 314 | 315 | if (son != null && son.isRed()) { // 父黑,子红 -> 子变黑就好 316 | son.toBlack(); 317 | return; 318 | } 319 | 320 | RedBlackNode brother = deletePoint.isLeft ? parent.right : parent.left; 321 | 322 | if (parent.isRed() && isBrotherFamilyBlack(brother)) { // 父红、兄弟家族为黑->父、兄交换颜色 323 | swapColor(parent, brother); 324 | return; 325 | } 326 | 327 | if (deletePoint.isLeft) { 328 | if (brother.isRed()) { // 兄红 -> 以父左旋 329 | linkToParent(parent.parent, leftRotate(parent)); 330 | balanceDelete(new DeletePoint(parent, del, null, true)); 331 | return; 332 | } 333 | 334 | RedBlackNode broRight = brother.right; 335 | 336 | if (broRight != null && broRight.isRed()) { // 兄右红 337 | broRight.toBlack(); // 变黑,补充黑缺失 338 | linkToParent(parent.parent, leftRotate(parent)); 339 | return; 340 | } 341 | 342 | RedBlackNode broLeft = brother.left; 343 | 344 | if (broLeft != null && broLeft.isRed()) { // 兄左红 345 | linkToParent(parent, rightRotate(brother)); // -> 以兄右旋 346 | 347 | brother.toBlack(); // 黑色 348 | linkToParent(parent.parent, leftRotate(parent)); // 父左旋 349 | return; 350 | } 351 | 352 | handleFullBlack(parent, brother); 353 | } else { // 右侧 354 | if (brother.isRed()) { // 兄红 -> 以父右旋 355 | linkToParent(parent.parent, rightRotate(parent)); 356 | balanceDelete(new DeletePoint(parent, del, null, false)); 357 | return; 358 | } 359 | 360 | RedBlackNode broLeft = brother.left; 361 | 362 | if (broLeft != null && broLeft.isRed()) { // 兄左红 363 | broLeft.toBlack(); // 变黑,补充黑缺失 364 | linkToParent(parent.parent, rightRotate(parent)); 365 | return; 366 | } 367 | 368 | RedBlackNode broRight = brother.right; 369 | 370 | if (broRight != null && broRight.isRed()) { // 兄右红 371 | linkToParent(parent, leftRotate(brother)); // -> 以兄左旋 372 | brother.toBlack(); // 黑色 373 | linkToParent(parent.parent, rightRotate(parent)); // 父右旋 374 | return; 375 | } 376 | 377 | // 兄节点全黑的时候,向上迭代 378 | handleFullBlack(parent, brother); 379 | } 380 | } 381 | 382 | /** 383 | * 兄节点全黑的时候,向上迭代 384 | */ 385 | private void handleFullBlack(RedBlackNode parent, RedBlackNode brother) { 386 | if (isBrotherFamilyBlack(brother)) { // 全黑 387 | brother.toRed(); 388 | balanceDelete(new DeletePoint(parent.parent, parent, null, isLeftNode(parent.parent, parent))); 389 | } 390 | } 391 | 392 | /** 393 | * 判断兄弟一家是否全黑 394 | */ 395 | public boolean isBrotherFamilyBlack(RedBlackNode brother) { 396 | if (null == brother) { 397 | return false; 398 | } 399 | 400 | RedBlackNode left = brother.left; 401 | RedBlackNode right = brother.right; 402 | 403 | if ((null != left && left.isRed()) || (null != right && right.isRed())) { // 左子或右子为红 404 | return false; 405 | } 406 | 407 | return true; 408 | } 409 | 410 | /** 411 | * 连接到父节点,自动根据大小判断左右 412 | */ 413 | public void linkToParent(RedBlackNode parent, RedBlackNode child) { 414 | if (null == parent) { 415 | root = child; 416 | root.parent = null; 417 | return; 418 | } 419 | 420 | boolean isLarger = parent.key.compareTo(child.key) > 0; 421 | 422 | if (isLarger) { 423 | parent.left = child; 424 | } else { 425 | parent.right = child; 426 | } 427 | 428 | child.parent = parent; 429 | // root = parent; 430 | } 431 | 432 | /** 433 | * 左旋、变色 434 | */ 435 | public RedBlackNode leftRotate(RedBlackNode parent) { 436 | RedBlackNode right = parent.right; 437 | RedBlackNode rightLeft = right.left; 438 | 439 | // 父子交换 440 | right.left = parent; 441 | parent.parent = right; 442 | 443 | // 接收左子 444 | parent.right = rightLeft; 445 | if (rightLeft != null) { 446 | rightLeft.parent = parent; 447 | } 448 | 449 | // 交换颜色 450 | swapColor(parent, right); 451 | 452 | return right;// 返回新的父节点 453 | } 454 | 455 | /** 456 | * ` 457 | * 右旋、变色 458 | */ 459 | public RedBlackNode rightRotate(RedBlackNode parent) { 460 | RedBlackNode left = parent.left; 461 | RedBlackNode leftRight = left.right; 462 | 463 | // 父子交换 464 | left.right = parent; 465 | parent.parent = left; 466 | 467 | // 接收左子 468 | parent.left = leftRight; 469 | if (leftRight != null) { 470 | leftRight.parent = parent; 471 | } 472 | 473 | // 交换颜色 474 | swapColor(parent, left); 475 | 476 | return left;// 返回新的父节点 477 | } 478 | 479 | /** 480 | * 颜色翻转 481 | */ 482 | public void flipColors(RedBlackNode parent) { 483 | int parentColor = parent.color; 484 | int childColor = parent.left.color; 485 | 486 | parent.left.color = parentColor; 487 | parent.right.color = parentColor; 488 | 489 | parent.color = childColor; 490 | } 491 | 492 | private void swapColor(RedBlackNode parent, RedBlackNode child) { 493 | int tmp = parent.color; 494 | parent.color = child.color; 495 | child.color = tmp; 496 | } 497 | 498 | } 499 | 500 | --------------------------------------------------------------------------------