array[maxIndex])
21 | {
22 | maxIndex = j;
23 | }
24 | }
25 | //每次选择完成后,将maxIndex所在元素和length-i-1的元素互换
26 | int temp = array[array.length-i-1];
27 | array[array.length-i-1] = array[maxIndex];
28 | array[maxIndex] = temp;
29 | log.info("第{}轮排序后的数组为:{}", i+1, array);
30 | }
31 | }
32 |
33 | public static void main(String[] args) {
34 | int[] array= {29,10,14,37,20,25,44,15};
35 | SelectionSort1 selectionSort=new SelectionSort1();
36 | selectionSort.doSelectionSort(array);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/stack/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | learn-algorithm
7 | org.example
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | stack
13 |
14 |
15 |
--------------------------------------------------------------------------------
/stack/src/main/java/com/flydean/ArrayStack.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | /**
4 | * @author wayne
5 | * @version ArrayStack
6 | */
7 | public class ArrayStack {
8 |
9 | //实际存储数据的数组
10 | private int[] array;
11 | //stack的容量
12 | private int capacity;
13 | //stack头部指针的位置
14 | private int topIndex;
15 |
16 | public ArrayStack(int capacity){
17 | this.capacity= capacity;
18 | array = new int[capacity];
19 | //默认情况下topIndex是-1,表示stack是空
20 | topIndex=-1;
21 | }
22 |
23 | /**
24 | * stack 是否为空
25 | * @return
26 | */
27 | public boolean isEmpty(){
28 | return topIndex == -1;
29 | }
30 |
31 | /**
32 | * stack 是否满了
33 | * @return
34 | */
35 | public boolean isFull(){
36 | return topIndex == array.length -1 ;
37 | }
38 |
39 | public void push(int data){
40 | if(isFull()){
41 | System.out.println("Stack已经满了,禁止插入");
42 | }else{
43 | array[++topIndex]=data;
44 | }
45 | }
46 |
47 | public int pop(){
48 | if(isEmpty()){
49 | System.out.println("Stack是空的");
50 | return -1;
51 | }else{
52 | return array[topIndex--];
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/stack/src/main/java/com/flydean/DyncArrayStack.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | /**
4 | * @author wayne
5 | * @version DyncArrayStack
6 | */
7 | public class DyncArrayStack {
8 |
9 | //实际存储数据的数组
10 | private int[] array;
11 | //stack的容量
12 | private int capacity;
13 | //stack头部指针的位置
14 | private int topIndex;
15 |
16 | public DyncArrayStack(int capacity){
17 | this.capacity= capacity;
18 | array = new int[capacity];
19 | //默认情况下topIndex是-1,表示stack是空
20 | topIndex=-1;
21 | }
22 |
23 | /**
24 | * stack 是否为空
25 | * @return
26 | */
27 | public boolean isEmpty(){
28 | return topIndex == -1;
29 | }
30 |
31 | /**
32 | * stack 是否满了
33 | * @return
34 | */
35 | public boolean isFull(){
36 | return topIndex == array.length -1 ;
37 | }
38 |
39 | public void push(int data){
40 | if(isFull()){
41 | System.out.println("Stack已经满了,stack扩容");
42 | expandStack();
43 | }
44 | array[++topIndex]=data;
45 | }
46 |
47 | public int pop(){
48 | if(isEmpty()){
49 | System.out.println("Stack是空的");
50 | return -1;
51 | }else{
52 | return array[topIndex--];
53 | }
54 | }
55 |
56 | //扩容stack,这里我们简单的使用倍增方式
57 | private void expandStack(){
58 | int[] expandedArray = new int[capacity* 2];
59 | System.arraycopy(array,0, expandedArray,0, capacity);
60 | capacity= capacity*2;
61 | array= expandedArray;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/stack/src/main/java/com/flydean/LinkedListStack.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | /**
4 | * @author wayne
5 | * @version LinkedListStack, 2020/7/12 9:08 下午
6 | */
7 | public class LinkedListStack {
8 |
9 | private Node headNode;
10 |
11 | class Node {
12 | int data;
13 | Node next;
14 | //Node的构造函数
15 | Node(int d) {
16 | data = d;
17 | }
18 | }
19 |
20 | public void push(int data){
21 | if(headNode == null){
22 | headNode= new Node(data);
23 | }else{
24 | Node newNode= new Node(data);
25 | newNode.next= headNode;
26 | headNode= newNode;
27 | }
28 | }
29 |
30 | public int top(){
31 | if(headNode ==null){
32 | return -1;
33 | }else{
34 | return headNode.data;
35 | }
36 | }
37 |
38 | public int pop(){
39 | if(headNode ==null){
40 | System.out.println("Stack是空的");
41 | return -1;
42 | }else{
43 | int data= headNode.data;
44 | headNode= headNode.next;
45 | return data;
46 | }
47 | }
48 |
49 | public boolean isEmpty(){
50 | return headNode==null;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/stringMatch/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/stringMatch/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.1.5
9 |
10 |
11 | com.flydean
12 | stringMatch
13 | 0.0.1-SNAPSHOT
14 | stringMatch
15 | stringMatch
16 |
17 | 17
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-test
28 | test
29 |
30 |
31 |
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-maven-plugin
37 |
38 |
39 | paketobuildpacks/builder-jammy-base:latest
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/stringMatch/src/main/java/com/flydean/stringmatch/KMP.java:
--------------------------------------------------------------------------------
1 | package com.flydean.stringmatch;
2 |
3 | /**
4 | * Knuth-Morris-Pratt(KMP)算法是一种用于在一个文本串中查找一个模式串出现位置的字符串匹配算法。它的特点在于避免了对于每个位置的匹配都回溯到模式串的开头,从而减少了比较的次数,提高了匹配效率。
5 | *
6 | * ### KMP算法的基本思想:
7 | * 1. **预处理模式串:** KMP算法首先对模式串进行预处理,得到一个部分匹配表(Partial Match Table),记为`next[]`。这个表用于指导匹配过程中的跳跃。
8 | *
9 | * 2. **匹配过程:** 在匹配过程中,当发生不匹配时,根据部分匹配表中的信息,调整模式串的位置,使得不必回溯到模式串的开头,从而提高匹配效率。
10 | *
11 | * ### 部分匹配表 `next[]` 的构建:
12 | * - `next[i]` 表示当第 `i` 个字符不匹配时,模式串应该跳跃的位置。
13 | * - 对于模式串 `p`,`next[0] = -1`,`next[1] = 0`。
14 | * - 对于 `i > 1`,若 `p[0...k-1]` 是 `p[0...i-1]` 的最大相同前缀后缀,令 `next[i] = k`,否则令 `k = next[k]`。
15 | *
16 | * ### KMP算法步骤:
17 | * 1. 初始化文本串指针 `i` 和模式串指针 `j`。
18 | * 2. 若当前字符匹配,则 `i` 和 `j` 同时后移。
19 | * 3. 若当前字符不匹配,根据 `next[j]` 调整 `j` 的位置。
20 | * 4. 重复步骤2-3,直到找到匹配或文本串遍历完。
21 | *
22 | * ### 算法复杂度:
23 | * - KMP算法的时间复杂度是 O(n + m),其中 n 是文本串的长度,m 是模式串的长度。
24 | * - 部分匹配表的构建时间是 O(m)。
25 | *
26 | * KMP算法在大规模文本匹配中具有较高的效率,尤其在一些大数据处理场景下表现优越。
27 | */
28 |
29 | import java.util.Arrays;
30 |
31 | public class KMP {
32 | public static void main(String[] args) {
33 | String str1 = "BBC ABCDAB ABCDABCDABDE";
34 | String str2 = "ABCDAB";
35 | int[] next = KMP_next(str2);
36 | System.out.println("next=" + Arrays.toString(next));
37 | int index = KmpSearch(str1, str2);
38 | System.out.println(index);
39 | }
40 |
41 | //KMP搜索算法
42 | public static int KmpSearch(String str1, String str2) {
43 | int[] next = KMP_next(str2);
44 | //遍历
45 | for (int i = 0, j = 0; i < str1.length(); i++) {
46 | while (j > 0 && str1.charAt(i) != str2.charAt(j)) {
47 | j = next[j - 1];
48 | }
49 | if (str1.charAt(i) == str2.charAt(j)) {
50 | j++;
51 | }
52 | if (j == str2.length()) {
53 | return i - j + 1;
54 | }
55 | }
56 | return -1;
57 | }
58 |
59 | //获取到一个字符串的部分匹配值
60 | public static int[] KMP_next(String dest) {
61 | //创建一个数组next,保存部分匹配值
62 | int[] next = new int[dest.length()];
63 | next[0] = 0;//如果字符串是长度为1 部分匹配值就是0
64 | for (int i = 1, j = 0; i < dest.length(); i++) {
65 | //当dest.charAt(j) != dest.charAt(i),我们需要从next[j-1]获取新的j
66 | //知道我们发现有dest.charAt(j) == dest.charAt(i)成立才停止
67 | while (j > 0 && dest.charAt(j) != dest.charAt(i)) {
68 | j = next[j - 1];
69 | }
70 | //当dest.charAt(j) == dest.charAt(i)满足时,部分匹配值就是+1
71 | if (dest.charAt(j) == dest.charAt(i)) {
72 | j++;
73 | }
74 | next[i] = j;
75 | }
76 | return next;
77 | }
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/stringMatch/src/main/java/com/flydean/stringmatch/KMPTest.java:
--------------------------------------------------------------------------------
1 | package com.flydean.stringmatch;
2 |
3 | import java.util.Arrays;
4 |
5 | public class KMPTest {
6 |
7 | public static void main(String[] args) {
8 | String str1 = "BBC ABCDAB ABCDABCDABDE";
9 | String str2 = "ABCDAB";
10 | int[] next = getNext(str2);
11 | System.out.println("next=" + Arrays.toString(next));
12 | int index = KmpSearch(str1, str2);
13 | System.out.println(index);
14 | }
15 |
16 |
17 | public static int KmpSearch(String str1, String str2) {
18 | int[] next = getNext(str2);
19 | for (int i = 0, j = 0; i < str1.length(); i++) {
20 | while (j > 0 && str1.charAt(i) != str2.charAt(j)) {
21 | j = next[j - 1];
22 | }
23 | if (str1.charAt(i) == str2.charAt(j)) {
24 | j++;
25 | }
26 | if (j == str2.length()) {
27 | return i - j + 1;
28 | }
29 | }
30 | return -1;
31 | }
32 |
33 | public static int[] getNext(String dest) {
34 |
35 | int[] next = new int[dest.length()];
36 | next[0] = 0;
37 | for (int i = 1, j = 0; i < dest.length(); i++) {
38 | while (j > 0 && dest.charAt(i) != dest.charAt(j)) {
39 | j = next[j - 1];
40 | }
41 | if (dest.charAt(i) == dest.charAt(j)) {
42 | j++;
43 | }
44 | next[i] = j;
45 | }
46 | return next;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/stringMatch/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tree/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | learn-algorithm
7 | org.example
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | tree
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tree/src/main/java/com/flydean/BinarySearchTree.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | /**
4 | * @author wayne
5 | * @version BinarySearchTree, 2020/7/20
6 | */
7 | public class BinarySearchTree {
8 |
9 | //根节点
10 | Node root;
11 |
12 | class Node {
13 | int data;
14 | Node left;
15 | Node right;
16 |
17 | public Node(int data) {
18 | this.data = data;
19 | left = right = null;
20 | }
21 | }
22 |
23 | //搜索方法,默认从根节点搜索
24 | public Node search(int data){
25 | return search(root,data);
26 | }
27 |
28 | //递归搜索节点
29 | private Node search(Node node, int data)
30 | {
31 | // 如果节点匹配,则返回节点
32 | if (node==null || node.data==data)
33 | return node;
34 |
35 | // 节点数据大于要搜索的数据,则继续搜索左边节点
36 | if (node.data > data)
37 | return search(node.left, data);
38 |
39 | // 如果节点数据小于要搜素的数据,则继续搜索右边节点
40 | return search(node.right, data);
41 | }
42 |
43 | // 插入新节点,从根节点开始插入
44 | public void insert(int data) {
45 | root = insert(root, data);
46 | }
47 |
48 | //递归插入新节点
49 | private Node insert(Node node, int data) {
50 |
51 | //如果节点为空,则创建新的节点
52 | if (node == null) {
53 | node = new Node(data);
54 | return node;
55 | }
56 |
57 | //节点不为空,则进行比较,从而递归进行左侧插入或者右侧插入
58 | if (data < node.data)
59 | node.left = insert(node.left, data);
60 | else if (data > node.data)
61 | node.right = insert(node.right, data);
62 |
63 | //返回插入后的节点
64 | return node;
65 | }
66 |
67 | // 删除新节点,从根节点开始删除
68 | void delete(int data)
69 | {
70 | root = delete(root, data);
71 | }
72 |
73 | //递归删除节点
74 | Node delete(Node node, int data)
75 | {
76 | //如果节点为空,直接返回
77 | if (node == null) return node;
78 |
79 | //遍历左右两边的节点
80 | if (data < node.data)
81 | node.left = delete(node.left, data);
82 | else if (data > root.data)
83 | node.right = delete(node.right, data);
84 |
85 | //如果节点匹配
86 | else
87 | {
88 | //如果是单边节点,直接返回其下面的节点
89 | if (node.left == null)
90 | return node.right;
91 | else if (node.right == null)
92 | return node.left;
93 |
94 | //如果是双边节点,则先找出右边最小的值,作为根节点,然后将删除最小值过后的右边的节点,作为根节点的右节点
95 | node.data = minValue(node.right);
96 |
97 | // 从右边删除最小的节点
98 | node.right = delete(node.right, node.data);
99 | }
100 |
101 | return node;
102 | }
103 |
104 | //查找节点的最小值
105 | int minValue(Node node)
106 | {
107 | int minv = node.data;
108 | while (node.left != null)
109 | {
110 | minv = node.left.data;
111 | node = node.left;
112 | }
113 | return minv;
114 | }
115 |
116 | //中序遍历BST
117 | public void inOrder(){
118 | inOrder(root);
119 | }
120 |
121 | //递归中序遍历
122 | private void inOrder(Node node){
123 | if (node != null) {
124 | inOrder(node.left);
125 | System.out.println(node.data);
126 | inOrder(node.right);
127 | }
128 | }
129 |
130 | public static void main(String[] args) {
131 | BinarySearchTree binarySearchTree= new BinarySearchTree();
132 | binarySearchTree.insert(34);
133 | binarySearchTree.insert(21);
134 | binarySearchTree.insert(15);
135 | binarySearchTree.insert(44);
136 | binarySearchTree.insert(43);
137 | binarySearchTree.insert(37);
138 | binarySearchTree.insert(12);
139 | binarySearchTree.inOrder();
140 | binarySearchTree.delete(34);
141 | binarySearchTree.inOrder();
142 | }
143 |
144 |
145 |
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/tree/src/main/java/com/flydean/FenwickTree.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * FenwickTree也叫做BIT(Binary Indexed Tree),树状数组
7 | * 是一种用于高效处理对一个存储数字的列表进行更新及求前缀和的数据结构。
8 | * @author wayne
9 | * @version FenwickTree, 2020/8/22
10 | */
11 | public class FenwickTree {
12 |
13 | private ArrayList ft;
14 |
15 | private int lowBitOne(int S) { return (S & (-S)); }
16 |
17 | public FenwickTree() {}
18 |
19 | // 初始化数组,值为0
20 | public FenwickTree(int n) {
21 | ft = new ArrayList<>();
22 | for (int i = 0; i <= n; i++) ft.add(0);
23 | }
24 |
25 | public int rangeSumQuery(int j) { // 范围查询 1 - j
26 | int sum = 0; for (; j > 0; j -= lowBitOne(j)) {
27 | sum += ft.get(j);
28 | }
29 | return sum;
30 | }
31 |
32 | public int rangeSumQuery(int i, int j) { // 范围查询 i - j
33 | return rangeSumQuery(j) - rangeSumQuery(i-1);
34 | }
35 |
36 | // 构造FenwickTree,更新相应的值
37 | void update(int i, int v) {
38 | for (; i < ft.size(); i += lowBitOne(i)){
39 | ft.set(i, ft.get(i)+v);
40 | }
41 | }
42 |
43 | public static void main(String[] args) {
44 | // idx 0 1 2 3 4 5 6 7 8 9 10, no index 0!
45 | FenwickTree ft = new FenwickTree(10); // ft = {-,0,0,0,0,0,0,0, 0,0,0}
46 | ft.update(2, 1); // ft = {-,0,1,0,1,0,0,0, 1,0,0}, idx 2,4,8 => +1
47 | ft.update(4, 1); // ft = {-,0,1,0,2,0,0,0, 2,0,0}, idx 4,8 => +1
48 | ft.update(5, 2); // ft = {-,0,1,0,2,2,2,0, 4,0,0}, idx 5,6,8 => +2
49 | ft.update(6, 3); // ft = {-,0,1,0,2,2,5,0, 7,0,0}, idx 6,8 => +3
50 | ft.update(7, 2); // ft = {-,0,1,0,2,2,5,2, 9,0,0}, idx 7,8 => +2
51 | ft.update(8, 1); // ft = {-,0,1,0,2,2,5,2,10,0,0}, idx 8 => +1
52 | ft.update(9, 1); // ft = {-,0,1,0,2,2,5,2,10,1,1}, idx 9,10 => +1
53 | System.out.printf("%d\n", ft.rangeSumQuery(1, 1)); // 0 => ft[1] = 0
54 | System.out.printf("%d\n", ft.rangeSumQuery(1, 2)); // 1 => ft[2] = 1
55 | System.out.printf("%d\n", ft.rangeSumQuery(1, 6)); // 7 => ft[6] + ft[4] = 5 + 2 = 7
56 | System.out.printf("%d\n", ft.rangeSumQuery(1, 10)); // 11 => ft[10] + ft[8] = 1 + 10 = 11
57 | System.out.printf("%d\n", ft.rangeSumQuery(3, 6)); // 6 => rsq(1, 6) - rsq(1, 2) = 7 - 1
58 |
59 | ft.update(5, 2); // update demo
60 | System.out.printf("%d\n", ft.rangeSumQuery(1, 10)); // now 13
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/tree/src/main/java/com/flydean/MaxSegmentTree.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | /**
4 | * @author wayne
5 | * @version MaxSegmentTree, 2020/8/18
6 | * 最大线段树,非叶子节点存储的是范围内的最大值
7 | */
8 | public class MaxSegmentTree {
9 |
10 | private int[] segmentTree; //新构建的segmentTree,对于满二叉树 最后一层的节点数乘以2 大致就是整棵树的节点数。
11 | //但是线段树并不一定是满二叉树,但是一定是平衡二叉树,所以需要多冗余一层。也就是 乘以4 就足以盛放所有的节点数
12 | //新构建的segmentTree 以index=1为起点
13 | private int[] originalArray; //原始数组
14 | private int n; //原始数组的长度
15 | private int left (int p) { return p << 1; } //左子结点为2*p
16 | private int right(int p) { return (p << 1) + 1; } //右子结点为2*p + 1
17 |
18 | /**
19 | * 构建segmentTree
20 | * @param treeIndex 当前需要添加节点的索引
21 | * @param arrayLeft 数组的左边界
22 | * @param arrayRight 数组的右边界
23 | */
24 | private void build(int treeIndex, int arrayLeft, int arrayRight) {
25 | if (arrayLeft == arrayRight) //如果相等则随便选择一个赋值
26 | segmentTree[treeIndex] = originalArray[arrayLeft];
27 | else { // 否则分别构建左侧子树和右侧子树,并根据我们需要构建的segmentTree类型来设置当前节点的值
28 | build(left(treeIndex) , arrayLeft , (arrayLeft + arrayRight) / 2);
29 | build(right(treeIndex), (arrayLeft + arrayRight) / 2 + 1, arrayRight);
30 | int p1 = segmentTree[left(treeIndex)], p2 = segmentTree[right(treeIndex)];
31 | segmentTree[treeIndex] = (p1 >= p2) ? p1 : p2;
32 | } }
33 |
34 |
35 | /**
36 | * 范围查询
37 | * @param treeIndex 当前要查找的节点index
38 | * @param arrayLeft 数组左边界
39 | * @param arrayRight 数组右边界
40 | * @param searchLeft 搜索左边界
41 | * @param searchRight 搜索右边界
42 | * @return
43 | */
44 | private int rangeQuery(int treeIndex, int arrayLeft, int arrayRight, int searchLeft, int searchRight) {
45 | if (searchLeft > arrayRight || searchRight < arrayLeft) return -1; // 搜索超出数组范围
46 | if (arrayLeft >= searchLeft && arrayRight <= searchRight) return segmentTree[treeIndex]; // 搜索的是整个数组范围,则直接返回根元素
47 |
48 | // 否则左右搜索
49 | int p1 = rangeQuery(left(treeIndex) , arrayLeft, (arrayLeft+arrayRight) / 2, searchLeft, searchRight);
50 | int p2 = rangeQuery(right(treeIndex), (arrayLeft+arrayRight) / 2 + 1, arrayRight, searchLeft, searchRight);
51 |
52 | if (p1 == -1) return p2; // 如果超出范围,则返回另外一个
53 | if (p2 == -1) return p1;
54 | return (p1 >= p2) ? p1 : p2; } //返回最小的那个
55 |
56 |
57 | /**
58 | * 更新数组中的某个节点
59 | * @param treeIndex 树的index
60 | * @param arrayLeft 数组左边界
61 | * @param arrayRight 数组右边界
62 | * @param arrayIndex 要更新的数组index
63 | * @param newValue 要更新的值
64 | * @return
65 | */
66 | private int updatePoint(int treeIndex, int arrayLeft, int arrayRight, int arrayIndex, int newValue) {
67 | // 设置i 和 j 等于要更新的数组index
68 | int i = arrayIndex, j = arrayIndex;
69 |
70 | // arrayIndex超出范围,则直接返回
71 | if (i > arrayRight || j < arrayLeft)
72 | return segmentTree[treeIndex];
73 |
74 | // 左右两个index相等
75 | if (arrayLeft == i && arrayRight == j) {
76 | originalArray[i] = newValue; // 找到要更新的index
77 | return segmentTree[treeIndex] = originalArray[i]; // 更新segmentTree
78 | }
79 |
80 | // 分别获得左右子树的最小值
81 | int p1, p2;
82 | p1 = updatePoint(left(treeIndex) , arrayLeft , (arrayLeft + arrayRight) / 2, arrayIndex, newValue);
83 | p2 = updatePoint(right(treeIndex), (arrayLeft + arrayRight) / 2 + 1, arrayRight , arrayIndex, newValue);
84 |
85 | // 更新treeIndex的值
86 | return segmentTree[treeIndex] = (p1 >= p2) ? p1 : p2;
87 | }
88 |
89 | public MaxSegmentTree(int[] array) {
90 | originalArray = array; n = originalArray.length; // 拷贝原始数组
91 | segmentTree = new int[4 * n]; //初始化新数组,长度是4*n
92 | for (int i = 0; i < 4 * n; i++) segmentTree[i] = 0;
93 | build(1, 0, n - 1); // 递归构建segmentTree,以index=1为起点
94 | }
95 |
96 | public int rangeQuery(int i, int j) { return rangeQuery(1, 0, n - 1, i, j); } // overloading
97 |
98 | public int updatePoint(int idx, int newValue) {
99 | return updatePoint(1, 0, n - 1, idx, newValue);
100 | }
101 |
102 | public static void main(String[] args) {
103 | int[] A = new int[] { 18, 17, 13, 19, 15, 11, 20 }; // the original array
104 | MaxSegmentTree st = new MaxSegmentTree(A);
105 |
106 | System.out.printf(" idx 0, 1, 2, 3, 4, 5, 6\n");
107 | System.out.printf(" A is {18,17,13,19,15, 11,20}\n");
108 | System.out.printf("RMQ(1, 3) = %d\n", st.rangeQuery(1, 3)); // answer = 13
109 |
110 | System.out.printf(" idx 0, 1, 2, 3, 4, 5, 6\n");
111 | System.out.printf("Now, modify A into {18,17,13,19,15,100,20}\n");
112 | st.updatePoint(5, 100); // update A[5] from 11 to 100
113 | System.out.printf("These values do not change\n");
114 | System.out.printf("RMQ(1, 3) = %d\n", st.rangeQuery(1, 3)); // 13
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/tree/src/main/java/com/flydean/MinSegmentTree.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | /**
4 | * @author wayne
5 | * @version MinSegmentTree, 2020/8/18
6 | * 最小线段树,非叶子节点存储的是范围内的最小值
7 | */
8 | public class MinSegmentTree {
9 |
10 | private int[] segmentTree; //新构建的segmentTree,对于满二叉树 最后一层的节点数乘以2 大致就是整棵树的节点数。
11 | //但是线段树并不一定是满二叉树,但是一定是平衡二叉树,所以需要多冗余一层。也就是 乘以4 就足以盛放所有的节点数
12 | //新构建的segmentTree 以index=1为起点
13 | private int[] originalArray; //原始数组
14 | private int n; //原始数组的长度
15 | private int left (int p) { return p << 1; } //左子结点为2*p
16 | private int right(int p) { return (p << 1) + 1; } //右子结点为2*p + 1
17 |
18 | /**
19 | * 构建segmentTree
20 | * @param treeIndex 当前需要添加节点的索引
21 | * @param arrayLeft 数组的左边界
22 | * @param arrayRight 数组的右边界
23 | */
24 | private void build(int treeIndex, int arrayLeft, int arrayRight) {
25 | if (arrayLeft == arrayRight) //如果相等则随便选择一个赋值
26 | segmentTree[treeIndex] = originalArray[arrayLeft];
27 | else { // 否则分别构建左侧子树和右侧子树,并根据我们需要构建的segmentTree类型来设置当前节点的值
28 | build(left(treeIndex) , arrayLeft , (arrayLeft + arrayRight) / 2);
29 | build(right(treeIndex), (arrayLeft + arrayRight) / 2 + 1, arrayRight);
30 | int p1 = segmentTree[left(treeIndex)], p2 = segmentTree[right(treeIndex)];
31 | segmentTree[treeIndex] = (p1 <= p2) ? p1 : p2;
32 | } }
33 |
34 |
35 | /**
36 | * 范围查询
37 | * @param treeIndex 当前要查找的节点index
38 | * @param arrayLeft 数组左边界
39 | * @param arrayRight 数组右边界
40 | * @param searchLeft 搜索左边界
41 | * @param searchRight 搜索右边界
42 | * @return
43 | */
44 | private int rangeQuery(int treeIndex, int arrayLeft, int arrayRight, int searchLeft, int searchRight) {
45 | if (searchLeft > arrayRight || searchRight < arrayLeft) return -1; // 搜索超出数组范围
46 | if (arrayLeft >= searchLeft && arrayRight <= searchRight) return segmentTree[treeIndex]; // 搜索的是整个数组范围,则直接返回根元素
47 |
48 | // 否则左右搜索
49 | int p1 = rangeQuery(left(treeIndex) , arrayLeft, (arrayLeft+arrayRight) / 2, searchLeft, searchRight);
50 | int p2 = rangeQuery(right(treeIndex), (arrayLeft+arrayRight) / 2 + 1, arrayRight, searchLeft, searchRight);
51 |
52 | if (p1 == -1) return p2; // 如果超出范围,则返回另外一个
53 | if (p2 == -1) return p1;
54 | return (p1 <= p2) ? p1 : p2; } //返回最小的那个
55 |
56 |
57 | /**
58 | * 更新数组中的某个节点
59 | * @param treeIndex 树的index
60 | * @param arrayLeft 数组左边界
61 | * @param arrayRight 数组右边界
62 | * @param arrayIndex 要更新的数组index
63 | * @param newValue 要更新的值
64 | * @return
65 | */
66 | private int updatePoint(int treeIndex, int arrayLeft, int arrayRight, int arrayIndex, int newValue) {
67 | // 设置i 和 j 等于要更新的数组index
68 | int i = arrayIndex, j = arrayIndex;
69 |
70 | // arrayIndex超出范围,则直接返回
71 | if (i > arrayRight || j < arrayLeft)
72 | return segmentTree[treeIndex];
73 |
74 | // 左右两个index相等
75 | if (arrayLeft == i && arrayRight == j) {
76 | originalArray[i] = newValue; // 找到要更新的index
77 | return segmentTree[treeIndex] = originalArray[i]; // 更新segmentTree
78 | }
79 |
80 | // 分别获得左右子树的最小值
81 | int p1, p2;
82 | p1 = updatePoint(left(treeIndex) , arrayLeft , (arrayLeft + arrayRight) / 2, arrayIndex, newValue);
83 | p2 = updatePoint(right(treeIndex), (arrayLeft + arrayRight) / 2 + 1, arrayRight , arrayIndex, newValue);
84 |
85 | // 更新treeIndex的值
86 | return segmentTree[treeIndex] = (p1 <= p2) ? p1 : p2;
87 | }
88 |
89 | public MinSegmentTree(int[] array) {
90 | originalArray = array; n = originalArray.length; // 拷贝原始数组
91 | segmentTree = new int[4 * n]; //初始化新数组,长度是4*n
92 | for (int i = 0; i < 4 * n; i++) segmentTree[i] = 0;
93 | build(1, 0, n - 1); // 递归构建segmentTree,以index=1为起点
94 | }
95 |
96 | public int rangeQuery(int i, int j) { return rangeQuery(1, 0, n - 1, i, j); } // overloading
97 |
98 | public int updatePoint(int idx, int newValue) {
99 | return updatePoint(1, 0, n - 1, idx, newValue);
100 | }
101 |
102 | public static void main(String[] args) {
103 | int[] A = new int[] { 18, 17, 13, 19, 15, 11, 20 }; // the original array
104 | MinSegmentTree st = new MinSegmentTree(A);
105 |
106 | System.out.printf(" idx 0, 1, 2, 3, 4, 5, 6\n");
107 | System.out.printf(" A is {18,17,13,19,15, 11,20}\n");
108 | System.out.printf("RMQ(1, 3) = %d\n", st.rangeQuery(1, 3)); // answer = 13
109 |
110 | System.out.printf(" idx 0, 1, 2, 3, 4, 5, 6\n");
111 | System.out.printf("Now, modify A into {18,17,13,19,15,100,20}\n");
112 | st.updatePoint(5, 100); // update A[5] from 11 to 100
113 | System.out.printf("These values do not change\n");
114 | System.out.printf("RMQ(1, 3) = %d\n", st.rangeQuery(1, 3)); // 13
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/tree/src/main/java/com/flydean/SuffixTree.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * @author wayne
7 | * @version SuffixTree, 2020/11/7
8 | */
9 | public class SuffixTree {
10 |
11 | SuffixTrieNode root = new SuffixTrieNode();
12 |
13 | // Constructor (Builds a trie of suffies of the
14 | // given text)
15 | SuffixTree(String txt) {
16 |
17 | // Consider all suffixes of given string and
18 | // insert them into the Suffix Trie using
19 | // recursive function insertSuffix() in
20 | // SuffixTrieNode class
21 | for (int i = 0; i < txt.length(); i++)
22 | root.insertSuffix(txt.substring(i), i);
23 | }
24 |
25 | /* Prints all occurrences of pat in the Suffix Trie S
26 | (built for text) */
27 | void searchTree(String pat) {
28 |
29 | // Let us call recursive search function for
30 | // root of Trie.
31 | // We get a list of all indexes (where pat is
32 | // present in text) in variable 'result'
33 | List result = root.search(pat);
34 |
35 | // Check if the list of indexes is empty or not
36 | if (result == null)
37 | System.out.println("Pattern not found");
38 | else {
39 |
40 | int patLen = pat.length();
41 |
42 | for (Integer i : result)
43 | System.out.println("Pattern found at position " +
44 | (i - patLen));
45 | }
46 | }
47 |
48 | // driver program to test above functions
49 | public static void main(String args[]) {
50 |
51 | // Let us build a suffix trie for text
52 | String txt = "www.flydean.com";
53 | SuffixTree S = new SuffixTree(txt);
54 |
55 | System.out.println("Search for 'ww'");
56 | S.searchTree("ww");
57 |
58 | System.out.println("\nSearch for 'flydean'");
59 | S.searchTree("flydean");
60 |
61 | System.out.println("\nSearch for 'ea'");
62 | S.searchTree("ea");
63 |
64 | System.out.println("\nSearch for 'com'");
65 | S.searchTree("com");
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/tree/src/main/java/com/flydean/SuffixTrieNode.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 | /**
7 | * @author wayne
8 | * @version SuffixTrieNode, 2020/11/7
9 | */
10 | public class SuffixTrieNode {
11 |
12 | final static int MAX_CHAR = 256;
13 |
14 | SuffixTrieNode[] children = new SuffixTrieNode[MAX_CHAR];
15 | List indexes;
16 |
17 | SuffixTrieNode() // Constructor
18 | {
19 | // Create an empty linked list for indexes of
20 | // suffixes starting from this node
21 | indexes = new LinkedList();
22 |
23 | // Initialize all child pointers as NULL
24 | for (int i = 0; i < MAX_CHAR; i++)
25 | children[i] = null;
26 | }
27 |
28 | // A recursive function to insert a suffix of
29 | // the text in subtree rooted with this node
30 | void insertSuffix(String s, int index) {
31 |
32 | // Store index in linked list
33 | indexes.add(index);
34 |
35 | // If string has more characters
36 | if (s.length() > 0) {
37 |
38 | // Find the first character
39 | char cIndex = s.charAt(0);
40 |
41 | // If there is no edge for this character,
42 | // add a new edge
43 | if (children[cIndex] == null)
44 | children[cIndex] = new SuffixTrieNode();
45 |
46 | // Recur for next suffix
47 | children[cIndex].insertSuffix(s.substring(1),
48 | index + 1);
49 | }
50 | }
51 |
52 | // A function to search a pattern in subtree rooted
53 | // with this node.The function returns pointer to a
54 | // linked list containing all indexes where pattern
55 | // is present. The returned indexes are indexes of
56 | // last characters of matched text.
57 | List search(String s) {
58 |
59 | // If all characters of pattern have been
60 | // processed,
61 | if (s.length() == 0)
62 | return indexes;
63 |
64 | // if there is an edge from the current node of
65 | // suffix tree, follow the edge.
66 | if (children[s.charAt(0)] != null)
67 | return (children[s.charAt(0)]).search(s.substring(1));
68 |
69 | // If there is no edge, pattern doesnt exist in
70 | // text
71 | else
72 | return null;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tree/src/main/java/com/flydean/TernaryTree.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | import java.util.HashSet;
4 |
5 | /**
6 | * @author wayne
7 | * @version TernaryTree, 2020/11/6
8 | */
9 | public class TernaryTree {
10 |
11 | enum NodeType
12 | {
13 | COMPLETED,
14 | UNCOMPLETED
15 | }
16 |
17 | static class Node
18 | {
19 | public char word;
20 |
21 | public Node leftChild, centerChild, rightChild;
22 |
23 | public NodeType type;
24 |
25 | public Node(char ch, NodeType type)
26 | {
27 | word = ch;
28 | this.type = type;
29 | }
30 | }
31 |
32 | private Node _root;
33 |
34 | private HashSet _hashSet;
35 |
36 |
37 | /**
38 | * 向node插入 s 中的 index 位的字符
39 | * @param s 整个单词
40 | * @param index 单词中的制定字符位
41 | * @param node 要被插入到的树的节点
42 | */
43 | private void insert(String s, int index, Node node)
44 | {
45 | if (null == node)
46 | {
47 | node = new Node(s.charAt(index), NodeType.UNCOMPLETED);
48 | }
49 |
50 | if (s.charAt(index) < node.word)
51 | {
52 | this.insert(s, index, node.leftChild);
53 | }
54 | else if (s.charAt(index) > node.word)
55 | {
56 | this.insert(s, index, node.rightChild);
57 | }
58 | else
59 | {
60 | if (index + 1 == s.length())
61 | {
62 | node.type = NodeType.COMPLETED;
63 | }
64 | else
65 | {
66 | this.insert(s, index + 1, node.centerChild);
67 | }
68 | }
69 | }
70 |
71 | /**
72 | * 将单词 s 插入到树中
73 | * @param s
74 | */
75 | public void insert(String s)
76 | {
77 | if (s == null || s.length() == 0 )
78 | {
79 | return ;
80 | }
81 |
82 | insert(s, 0, _root);
83 | }
84 |
85 | /**
86 | * 查找特定的单词
87 | * @param s 待查找的单词
88 | * @return
89 | */
90 | public Node find(String s)
91 | {
92 | if (s == null || s.length() == 0 )
93 | {
94 | return null;
95 | }
96 |
97 | int pos = 0;
98 | Node node = _root;
99 | _hashSet = new HashSet();
100 | while (node != null)
101 | {
102 | if (s.charAt(pos) < node.word)
103 | {
104 | node = node.leftChild;
105 | }
106 | else if (s.charAt(pos) > node.word)
107 | {
108 | node = node.rightChild;
109 | }
110 | else
111 | {
112 | if (++pos == s.length())
113 | {
114 | _hashSet.add(s);
115 | return node.centerChild;
116 | }
117 |
118 | node = node.centerChild;
119 | }
120 | }
121 |
122 | return null;
123 | }
124 |
125 | /**
126 | * 前缀匹配
127 | * @param prefix
128 | * @param node
129 | */
130 | private void DFS(String prefix, Node node)
131 | {
132 | if (node != null)
133 | {
134 | if (NodeType.COMPLETED == node.type)
135 | {
136 | _hashSet.add(prefix + node.word);
137 | }
138 |
139 | DFS(prefix, node.leftChild);
140 | DFS(prefix + node.word, node.centerChild);
141 | DFS(prefix, node.rightChild);
142 | }
143 | }
144 |
145 | /**
146 | * 相识度查找
147 | * @param s 要查找的单词
148 | * @return
149 | */
150 | public HashSet findSimilar(String s)
151 | {
152 | Node node = this.find(s);
153 | this.DFS(s, node);
154 | return _hashSet;
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/trie/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | learn-algorithm
7 | org.example
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | trie
13 |
14 |
15 |
--------------------------------------------------------------------------------
/trie/src/main/java/com/flydean/Trie.java:
--------------------------------------------------------------------------------
1 | package com.flydean;
2 |
3 | /**
4 | * @author wayne
5 | * @version Trie, 2020/11/5
6 | */
7 | public class Trie {
8 | // 假如字典中的单词只有26个英文字母
9 | static final int ALPHABET_SIZE = 26;
10 |
11 | // Trie的node节点
12 | static class TrieNode
13 | {
14 | TrieNode[] children = new TrieNode[ALPHABET_SIZE];
15 |
16 | // 这个节点是否是结束节点
17 | boolean isEndOfWord;
18 |
19 | //初始化TireNode节点
20 | TrieNode(){
21 | isEndOfWord = false;
22 | for (int i = 0; i < ALPHABET_SIZE; i++)
23 | children[i] = null;
24 | }
25 | }
26 |
27 | static TrieNode root= new TrieNode();
28 |
29 | // 如果当前level中不存在,那么就会在特定的位置插入新的节点
30 | // 如果当前level中存在该字符,则从其子节点继续插入
31 | // 最后,将该叶子节点标记为isEndOfWord。
32 | static void insert(String key)
33 | {
34 | int level;
35 | int length = key.length();
36 | int index;
37 |
38 | TrieNode currentNode = root;
39 |
40 | for (level = 0; level < length; level++)
41 | {
42 | index = key.charAt(level) - 'a';
43 | if (currentNode.children[index] == null)
44 | currentNode.children[index] = new TrieNode();
45 |
46 | currentNode = currentNode.children[index];
47 | }
48 |
49 | // 将叶子节点标记为 isEndOfWord
50 | currentNode.isEndOfWord = true;
51 | }
52 |
53 | // 执行搜索,也是按level来进行查询
54 | static boolean search(String key)
55 | {
56 | int level;
57 | int length = key.length();
58 | int index;
59 | TrieNode currentNode = root;
60 |
61 | for (level = 0; level < length; level++)
62 | {
63 | index = key.charAt(level) - 'a';
64 |
65 | if (currentNode.children[index] == null)
66 | return false;
67 |
68 | currentNode = currentNode.children[index];
69 | }
70 |
71 | return (currentNode != null && currentNode.isEndOfWord);
72 | }
73 |
74 | //判断该节点是否有子节点
75 | static boolean hasChild(TrieNode currentNode)
76 | {
77 | for (int i = 0; i < ALPHABET_SIZE; i++)
78 | if (currentNode.children[i] != null)
79 | return true;
80 | return false;
81 | }
82 |
83 | static TrieNode remove(TrieNode currentNode, String key, int level ){
84 | if(currentNode ==null){
85 | return null;
86 | }
87 |
88 | int length = key.length();
89 |
90 | //正在处理最后一个字符
91 | if(level == length){
92 | //将当前节点的标志位删除
93 | if(currentNode.isEndOfWord){
94 | currentNode.isEndOfWord= false;
95 | }
96 | //如果没有子节点,则只接受删除该节点
97 | if (!hasChild(currentNode)) {
98 | currentNode = null;
99 | }
100 |
101 | return currentNode;
102 | }
103 |
104 | // 如果不是最后一个节点,则递归调用其子节点
105 | int index = key.charAt(level) - 'a';
106 | currentNode.children[index] =
107 | remove(currentNode.children[index], key, level + 1);
108 |
109 | // 如果当前节点既没有子节点,也不是其他单词的结束节点,那么直接将这个节点删除即可。
110 | if (!hasChild(currentNode) && currentNode.isEndOfWord == false) {
111 | currentNode = null;
112 | }
113 | return currentNode;
114 | }
115 |
116 | public static void main(String args[])
117 | {
118 | String keys[] = {"www", "flydean", "com", "is", "a",
119 | "good", "website"};
120 |
121 | // 构造Trie tree
122 | int i;
123 | for (i = 0; i < keys.length ; i++)
124 | insert(keys[i]);
125 | }
126 | }
127 |
--------------------------------------------------------------------------------