数据结构与算法 数据结构( data structure) 是计算机中存储、 组织数据的方式。
数据结构是一种具有一定逻辑关系, 在计算机中应用某种存储结构, 并且封装了相应操作的数据元素集合。
它包含三方面的内容, 逻辑关系、 存储关系及操作, 不同种类的数据结构适合于不同种类的应用。
线性结构和非线性结构
线性结构作为最常用的数据结构, 其特点是数据元素之间存在一对一的线性关系。
线性结构有两种不同的存储结构, 即顺序存储结构和链式存储结构。
顺序存储的线性表称为顺序表, 顺序表中的存储元素是连续的。
链式存储的线性表称为链表, 链表中的存储元素不一定是连续的, 元素节点中存放数据元素以及相邻元素的地址信息。
线性结构常见的有:
栈( Stack) : 栈是一种特殊的线性表, 它只能在一个表的一个固定端进行数据结点的插入和删除操作。
队列( Queue) : 队列和栈类似, 也是一种特殊的线性表。 和栈不同的是, 队列只允许在表的一端进行插入操作, 而在另一端进行删除操作。
数组( Array) : 数组是一种聚合数据类型, 它是将具有相同类型的若干变量有序地组织在一起的集合。
链表( Linked List) : 链表是一种数据元素按照链式存储结构进行存储的数据结构, 这种存储结构具有在物理上存在非连续的特点。
非线性结构常见的有:
树( Tree) : 树是典型的非线性结构, 它是包括, 2 个结点的有穷集合 K。
堆( Heap) : 堆是一种特殊的树形数据结构, 一般讨论的堆都是二叉堆。
图( Graph) : 图是另一种非线性数据结构。 在图结构中, 数据结点一般称为顶点, 而边是顶点的有序偶对。
散列表( Hash table) : 散列表源自于散列函数(Hash function), 其思想是如果在结构中存在关键字和T相等的记录, 那么必定在F(T)的存储位置可以找到该记录, 这样就可以不用进行比较操作而直接取得所查记录。
数据结构与算法的关系 数据结构研究的内容: 就是如何按一定的逻辑结构, 把数据组织起来, 并选择适当的存储表示方法把逻辑结构组织好的数据, 存储到计算机的存储器里。
算法研究的目的: 是为了更有效的处理数据, 提高数据运算效率。 数据的运算是定义在数据的逻辑结构上, 但运算的具体实现要在存储结构上进行。
算法一般有以下几种常用运算:
检索: 在数据结构里查找满足一定条件的节点。 一般是给定一个某字段的值, 找具有该字段值的节点。
插入: 往数据结构中增加新的节点。
删除: 把指定的结点从数据结构中去掉。
更新: 改变指定节点的一个或多个字段的值。
排序: 把节点按某种指定的顺序重新排列。 例如递增或递减。
算法的时间复杂度 度量一个程序/算法的执行时间有两种方法:
事后统计法:
要想对设计的算法的运行性能进行评测, 需要实际运行该程序
所得时间的统计量依赖于计算机的硬件、 软件等环境因素
这种方式, 要在同一台计算机的相同状态下运行, 才能比较那个算法速度更快
事前统计法: 通过分析某个算法的时间复杂度来判断哪个算法更优
时间频度: 一个算法花费的时间与算法中语句的执行次数成正比例, 哪个算法中语句执行次数多, 它花费时间就多。 一个算法中的语句执行次数称为语句频度或时间频度。 记为T(n)。
时间复杂度: 一般情况下, 算法中的基本操作语句的重复执行次数是问题规模 n 的某个函数, 用 T(n) 表示, 若有某个辅助函数 f(n), 使得当 n 趋近于无穷大时, T(n)/f(n)的极限值为不等于零的常数, 则称 f(n) 是 T(n) 的同数量级函数。 记作 T(n) = O(f(n)), 称 O(f(n)) 为算法的渐进时间复杂度, 简称时间复杂度。
时间复杂度计算中可以忽略常数项、 低次项、 系数。 如: 5n^2+7n 可以计算为 n^2。
T(n)不同, 但时间复杂度可能相同。 如: T(n)=n^2+7n+6 与 T(n)=3n^2+7n+6 它们的 T(n) 不同, 但时间复杂度相同, 都为 O(n^2)。
时间复杂度的计算方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 如: T(n) = 3n^2 + 7n + 6 用常数1代替运行时间中的所有加法常数 T(n) = 3n^2 + 7n + 6 => T(n) = 3n^2 + 7n + 1 忽略常数项 T(n) = 3n^2 + 7n + 1 => T(n) = 3n^2 + 7n 忽略低次项 T(n) = 3n^2 + 7n => T(n) = 3n^2 忽略系数 T(n) = 3n^2 => T(n) = n^2 结果 T(n) = n^2 => O(n^2)
常见的时间复杂度(由小到大排序)
常数阶: O(1)
对数阶: O(log_k n)
线性阶: O(n)
线性对数阶: O(n log_k n)
平方阶: O(n^2)
立方阶: O(n^3)
指数阶: O(2^n)²
阶乘阶: O(n!)
n的n次方阶: O(nⁿ)
平均时间复杂度和最坏时间复杂度
平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下, 该算法的运行时间。
最坏情况下的时间复杂度称最坏时间复杂度。 一般讨论的时间复杂度均是最坏情况下的时间复杂度。 这样做的原因匙最坏情况下的时间复杂度是算法在任何输入实例上运行时间的界限, 这就保证了算法的运行时间不会比最坏情况更长。
平均时间复杂度和最坏时间复杂度是否一致, 和算法有关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 排序法 排序方式 平均时间 最差情形 稳定度 额外空间 备注 插入 In-Place O (n²) O(n²) 稳定 O(1 ) 大部分已排序时较好 希尔 In-Place O (n㏒n) O(n的s次方 1 < s < 2 ) 不稳定 O(1 ) s是所选分组 选择 In-Place O (n²) O(n²) 不稳定 O(1 ) n小时较好 堆 In-Place O (n㏒n) O(n㏒n) 不稳定 O(1 ) n大时较好 冒泡 In-Place O (n²) O(n²) 稳定 O(1 ) n小时较好 快速 In-Place O (n㏒n) O(n²) 不稳定 O(n㏒n) n大时较好 归并 Out-Place O (n㏒n) O(n㏒n) 稳定 O(1 ) n大时较好 基数 Out-Place O (㏒RB) O(㏒RB) 稳定 O(n) B是真数( 0 -9 ) , R是基数( 个十百) 1. 稳定: 如果a原本在b前面, 而a=b, 排序之后a仍然在b的前面2. 不稳定: 如果a原本在b前面, 而a=b, 排序之后a可能会在b的后面3. 内排序: 所有排序操作都在内存中完成4. 外排序: 由于数据太大, 因此把数据放在磁盘中, 而排序通过磁盘和内存的数据传输才能进行5. n: 数据的规模6. In-Place: 不占用额外内存7. Out-Place: 占用额外内存
算法的空间复杂度
类似于时间复杂度的讨论, 一个算法的空间复杂度(Space Complexity)定义为该算法所耗费的存储空间, 它也是问题规模n的函数。
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。 有的算法需要占用的临时工作单元数与解决问题的规模n有关, 它随着n的增大而增大, 当n较大时, 将占用较多的存储单元, 例如快速排序和归并排序算法就属于这种情况。
在做算法分析时, 主要讨论的是时间复杂度。 从用户使用体验上看, 更看重的程序执行的速度。 一些缓存产品(redis,memcache)和算法(基数排序)本质就是用空间换时间。
数据结构: 数组 稀疏数组 当一个数组中大部分元素为 0, 或者为同一个值的数组时, 可以使用稀疏数组来保存该数组。
记录数组一共有几行几列, 有多少个不同的值。
把具有不同值的元素的行列及值记录在一个小规模的数组中, 从而缩小程序的规模。
二维数组转稀疏数组
遍历原始的二维数组, 得到有效数据的个数 sum。
根据sum就可以创建稀疏数组( Sparse Array)
sa = int[sum + 1][3]
将二维数组的有效数据数据存入到稀疏数组。
稀疏数组转二维数组
先读取稀疏数组的第一行, 根据第一行的数据, 创建原始的二维数组
arr2 = int[sa[0][0]][sa[0][1]]
在读取稀疏数组后几行的数据, 并赋给原始的二维数组即可。
转化代码如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 public class SparseArray { public static int [][] initArray() { int [][] arr2 = new int [11 ][11 ]; arr2[1 ][2 ] = 1 ; arr2[2 ][3 ] = 2 ; return arr2; } public static int [][] transformSparseArray(int [][] arr2) { int num = 0 ; for (int [] row : arr2) { for (int data : row) { if (data != 0 ) { num = num + 1 ; } } } int [][] sa = new int [num + 1 ][3 ]; sa[0 ][0 ] = arr2.length; sa[0 ][1 ] = arr2[0 ].length; sa[0 ][2 ] = num; int count = 0 ; for (int j = 0 ; j < arr2.length; j++) { for (int k = 0 ; k < arr2[j].length; k++) { if (arr2[j][k] != 0 ) { count = count + 1 ; sa[count][0 ] = j; sa[count][1 ] = k; sa[count][2 ] = arr2[j][k]; } } } return sa; } public static int [][] transformTwoArray(int [][] sa) { int [][] arr2 = new int [sa[0 ][0 ]][sa[0 ][1 ]]; for (int i = 1 ; i < sa.length; i++) { arr2[sa[i][0 ]][sa[i][1 ]] = sa[i][2 ]; } return arr2; } public static void main (String[] args) { int [][] arr2 = initArray(); for (int [] row : arr2) { for (int data : row) { System.out.printf("%d\t" , data); } System.out.println(); } System.out.println("--------------------" ); int [][] sa = transformSparseArray(arr2); for (int [] ints : sa) { System.out.printf("%d\t%d\t%d\t\n" , ints[0 ], ints[1 ], ints[2 ]); } System.out.println("--------------------" ); int [][] arr = transformTwoArray(sa); for (int [] row : arr) { for (int data : row) { System.out.printf("%d\t" , data); } System.out.println(); } } }
数据结构: 队列 队列是一个有序列表, 可以用数组或是链表来实现。 遵循先入先出的原则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 public class ArrayQueue { private int maxSize; private int front; private int rear; private int [] arr; private int conut; public ArrayQueue (int maxSize) { this .maxSize = maxSize; this .arr = new int [maxSize]; this .front = -1 ; this .rear = -1 ; conut = 0 ; } public boolean isFull () { return conut == maxSize; } public boolean isNull () { return conut == 0 ; } public void add (int value) { if (isFull()) { System.out.println("队列已满, 请稍后..." ); return ; } conut = conut + 1 ; rear = (rear + 1 ) % maxSize; arr[rear] = value; } public int get () { if (isNull()) { throw new RuntimeException ("队列为空, 不能取数据" ); } conut = conut - 1 ; front = (front + 1 ) % maxSize; return arr[front]; } public void show () { if (isNull()) { System.out.println("队列为空" ); return ; } for (int i = 0 ; i < conut; i++) { System.out.println("第" + (1 +i) + "位: " + arr[(front + 1 + i) % maxSize]); } } public int headValue () { if (isNull()) { throw new RuntimeException ("对列为空" ); } return arr[(front + 1 ) % maxSize]; } } public class Client { public static void main (String[] args) { ArrayQueue queue = new ArrayQueue (3 ); char key = ' ' ; Scanner scanner = new Scanner (System.in); boolean bool = true ; while (bool){ System.out.println("s(show): 显示队列" ); System.out.println("e(exit): 退出程序" ); System.out.println("a(add): 添加数据到队列" ); System.out.println("g(get): 从队列取出数据" ); System.out.println("h(head): 查看队列头的数据" ); key = scanner.next().charAt(0 ); switch (key) { case 's' : queue.show(); break ; case 'e' : bool = false ; scanner.close(); break ; case 'a' : System.out.println("请输入一个数" ); int nextInt = scanner.nextInt(); queue.add(nextInt); break ; case 'g' : try { int i = queue.get(); System.out.println("取出的数据是: " + i); } catch (Exception e) { System.out.println(e.getMessage()); } break ; case 'h' : try { int i = queue.headValue(); System.out.println("目前排在第一位的数据是: " + i); } catch (Exception e) { System.out.println(e.getMessage()); } break ; default : break ; } }; System.out.println("退出程序..." ); } }
数据结构: 链表 链表是一个有序列表, 链表是以节点的方式链式存储的, 各个节点不一定是连续储存的, 每个节点包含 data 域、 next 域, next 域指向下一个节点。
链表分为带头节点的链表和不带头节点的链表, 双向链表比单向链表多一个 prev 域指向前一个节点。
单向链表只能查找一个方向, 而双向链表可以向前向后查找。
单向链表不能自我删除, 需要依靠辅助节点, 而双向链表可以自我删除。
单向链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 public class SinglyLinkedList <E> implements Iterable <E> { private Node headNode; public SinglyLinkedList () { this .headNode = new Node (); } private class Node { private E data; private Node next; public Node () {} public Node (E data) { this .data = data; } public E getData () { return data; } public void setData (E data) { this .data = data; } public Node getNext () { return next; } public void setNext (Node next) { this .next = next; } @Override public String toString () { return "Node{" + "data=" + data + '}' ; } } private Node getNode (int index) { Node nodeNext = this .headNode.getNext(); int tempIndex = 0 ; while (nodeNext != null ) { if (tempIndex == index) { return nodeNext; } nodeNext = nodeNext.getNext(); tempIndex = tempIndex + 1 ; } throw new RuntimeException ("根据下标未找到数据~~~" ); } public void add (E data) { Node node = new Node (data); Node nextNode = this .headNode.getNext(); if (nextNode == null ) { this .headNode.setNext(node); return ; } Node prevNode = nextNode; while (true ) { if (nextNode == null ) { prevNode.setNext(node); break ; } prevNode = nextNode; nextNode = nextNode.getNext(); } } public void delete (int index) { Node node = getNode(index); if (node.getNext() == null ) { if (index == 0 ) { this .headNode.setNext(null ); } else { getNode(index - 1 ).setNext(null ); } } else { Node nextNode = node.getNext(); if (index == 0 ) { this .headNode.setNext(nextNode); } else { Node prevNode = getNode(index - 1 ); prevNode.setNext(nextNode); } } } public void update (int index, E data) { getNode(index).setData(data); } public E get (int index) { return getNode(index).getData(); } public String toString () { Node nodeNext = this .headNode.getNext(); StringBuilder result = new StringBuilder ("[" ); while (true ) { if (nodeNext != null ) { result.append(nodeNext.getData().toString()); if (nodeNext.getNext() != null ) { result.append(", " ); nodeNext = nodeNext.getNext(); } else { break ; } } } result.append("]" ); return result.toString(); } public int length () { int length = 0 ; Node next = this .headNode.getNext(); while (next != null ) { next = next.getNext(); length = length + 1 ; } return length; } private Node iteratorTemp; @Override public Iterator<E> iterator () { iteratorTemp = this .headNode; return new Iterator <E>() { @Override public boolean hasNext () { return iteratorTemp.getNext() != null ; } @Override public E next () { Node next = iteratorTemp.getNext(); iteratorTemp = next; return next.getData(); } }; } public void reverse () { Node headNodeTemp = new Node (); Node next = this .headNode.getNext(); if (next == null ) return ; Node nodeTemp = next.getNext(); next.setNext(null ); headNodeTemp.setNext(next); while (nodeTemp != null ) { Node node = nodeTemp.getNext(); nodeTemp.setNext(headNodeTemp.getNext()); headNodeTemp.setNext(nodeTemp); nodeTemp = node; } this .headNode.setNext(headNodeTemp.getNext()); } } public class Client { public static void main (String[] args) { SinglyLinkedList<String> singlyLinkedList = new SinglyLinkedList <>(); singlyLinkedList.add("唐僧" ); singlyLinkedList.add("白龙马" ); singlyLinkedList.add("孙悟空" ); singlyLinkedList.add("猪悟能" ); singlyLinkedList.add("沙悟净" ); System.out.println(singlyLinkedList.toString()); System.out.println("链表总长度: " + singlyLinkedList.length()); System.out.println("--- 把白龙马修改为三太子 ---" ); singlyLinkedList.update(1 , "三太子" ); System.out.println(singlyLinkedList.toString()); System.out.println("--- 删除白龙马 ---" ); singlyLinkedList.delete(1 ); System.out.println(singlyLinkedList.toString()); System.out.println("--- 获取下标为1的值 ---" ); System.out.println(singlyLinkedList.get(1 )); System.out.println("--- 迭代器 ---" ); Iterator<String> iterator = singlyLinkedList.iterator(); while (iterator.hasNext()) { String next = iterator.next(); System.out.println(next); } System.out.println("--- 链表反转后的值 ---" ); singlyLinkedList.reverse(); System.out.println(singlyLinkedList.toString()); } }
单向环形列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 public class SinglyCircleLinkedList <E> { private Node firstNode; public SinglyCircleLinkedList () { } class Node { private E data; private Node nextNode; public Node () { } public Node (E data) { this .data = data; } public E getData () { return data; } public void setData (E data) { this .data = data; } public Node getNextNode () { return nextNode; } public void setNextNode (Node nextNode) { this .nextNode = nextNode; } } public void add (E data) { Node node = new Node (data); if (firstNode == null ) { node.setNextNode(node); firstNode = node; } else { Node nextNode = firstNode; while (nextNode.getNextNode() != firstNode) { nextNode = nextNode.getNextNode(); } node.setNextNode(firstNode); nextNode.setNextNode(node); } } public void show () { Node nextNode = firstNode; while (true ) { System.out.println(nextNode.getData().toString()); if (nextNode.getNextNode() == firstNode) { break ; } nextNode = nextNode.getNextNode(); } } public int length () { int length = 0 ; if (this .firstNode != null ) { Node temp = this .firstNode; while (true ) { length = length + 1 ; if (this .firstNode == temp.getNextNode()) { break ; } temp = temp.getNextNode(); } } return length; } public void josephusRun (int k, int m) { if (k < 1 || k > length() || m < 1 ) return ; int count = 0 ; Node prevNode; Node temp = this .firstNode; while (true ) { if (this .firstNode == temp.getNextNode()) { prevNode = temp; break ; } temp = temp.getNextNode(); } Node numNode = this .firstNode; while (true ) { count = count + 1 ; if (count == k) { break ; } prevNode = numNode; numNode = numNode.getNextNode(); } while (this .firstNode != null ) { if (prevNode == numNode) { System.out.println(numNode.getData().toString()); this .firstNode = null ; break ; } count = 0 ; while (true ) { count = count + 1 ; if (count == m) { System.out.println(numNode.getData().toString()); prevNode.setNextNode(numNode.getNextNode()); numNode = numNode.getNextNode(); break ; } prevNode = numNode; numNode = numNode.getNextNode(); } } } } public class Client { public static void main (String[] args) { SinglyCircleLinkedList<String> singlyCircleLinkedList = new SinglyCircleLinkedList <>(); singlyCircleLinkedList.add("刘一" ); singlyCircleLinkedList.add("陈二" ); singlyCircleLinkedList.add("张三" ); singlyCircleLinkedList.add("李四" ); singlyCircleLinkedList.add("王五" ); singlyCircleLinkedList.add("赵六" ); singlyCircleLinkedList.add("孙七" ); singlyCircleLinkedList.add("周八" ); singlyCircleLinkedList.add("吴九" ); singlyCircleLinkedList.add("郑十" ); singlyCircleLinkedList.show(); System.out.println(singlyCircleLinkedList.length()); singlyCircleLinkedList.josephusRun(2 , 2 ); } }
双向链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 public class DoubleLinkedList <E> { private Node headNode; public DoubleLinkedList () { this .headNode = new Node (); } private class Node { private E data; private Node prevNode; private Node nextNode; public Node () {} public Node (E data) { this .data = data; } public E getData () { return data; } public void setData (E data) { this .data = data; } public Node getPrevNode () { return prevNode; } public void setPrevNode (Node prevNode) { this .prevNode = prevNode; } public Node getNextNode () { return nextNode; } public void setNextNode (Node nextNode) { this .nextNode = nextNode; } @Override public String toString () { return "Node{" + "data=" + data + '}' ; } } private Node getNode (int index) { Node nodeNext = this .headNode.getNextNode(); int tempIndex = 0 ; while (nodeNext != null ) { if (tempIndex == index) { return nodeNext; } nodeNext = nodeNext.getNextNode(); tempIndex = tempIndex + 1 ; } throw new RuntimeException ("根据下标未找到数据~~~" ); } public void add (E data) { Node node = new Node (data); Node nextNode = this .headNode.getNextNode(); if (nextNode == null ) { this .headNode.setNextNode(node); return ; } Node temp = this .headNode; while (true ) { if (temp.getNextNode() == null ) { node.setPrevNode(temp); temp.setNextNode(node); break ; } temp = temp.getNextNode(); } } public void delete (int index) { Node node = getNode(index); Node prevNode = node.getPrevNode(); Node nextNode = node.getNextNode(); if (prevNode == null ) { if (nextNode == null ) { this .headNode.setNextNode(null ); } else { nextNode.setPrevNode(null ); this .headNode.setNextNode(nextNode); } } else { if (nextNode != null ) { nextNode.setPrevNode(prevNode); } prevNode.setNextNode(nextNode); } } public void update (int index, E data) { getNode(index).setData(data); } public E get (int index) { return getNode(index).getData(); } public String toString () { Node nodeNext = this .headNode.getNextNode(); StringBuilder result = new StringBuilder ("[" ); while (true ) { if (nodeNext != null ) { result.append(nodeNext.getData().toString()); if (nodeNext.getNextNode() != null ) { result.append(", " ); nodeNext = nodeNext.getNextNode(); } else { break ; } } } result.append("]" ); return result.toString(); } public int length () { int length = 0 ; Node next = this .headNode.getNextNode(); while (next != null ) { next = next.getNextNode(); length = length + 1 ; } return length; } } public class Client { public static void main (String[] args) { DoubleLinkedList<String> singlyLinkedList = new DoubleLinkedList <>(); singlyLinkedList.add("唐僧" ); singlyLinkedList.add("白龙马" ); singlyLinkedList.add("孙悟空" ); singlyLinkedList.add("猪悟能" ); singlyLinkedList.add("沙悟净" ); System.out.println(singlyLinkedList.toString()); System.out.println("链表总长度: " + singlyLinkedList.length()); System.out.println("--- 把白龙马修改为三太子 ---" ); singlyLinkedList.update(1 , "三太子" ); System.out.println(singlyLinkedList.toString()); System.out.println("--- 删除白龙马 ---" ); singlyLinkedList.delete(1 ); System.out.println(singlyLinkedList.toString()); System.out.println("--- 获取下标为1的值 ---" ); System.out.println(singlyLinkedList.get(1 )); } }
数据结构: 栈 栈( stack) 是一个先入后出( FILO-First In Last Out) 的有序列表, 元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。
允许插入和删除的一端, 为变化的一端, 称为栈顶, 另一端为固定的一端, 称为栈底。 最先放入栈中元素在栈底, 最后放入的元素在栈顶。 删除元素时, 最后放入的元素最先删除, 最先放入的元素最后删除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 public class Stack { private int maxSize; private int [] stack; private int top = -1 ; public Stack (int maxSize) { this .maxSize = maxSize; stack = new int [this .maxSize]; } public boolean isFull () { return this .top == this .maxSize - 1 ; } public boolean isEmpty () { return this .top == -1 ; } public void push (int value) { if (isFull()) { System.out.println("栈满" ); return ; } top = top + 1 ; stack[top] = value; } public int pop () { if (isEmpty()) { throw new RuntimeException ("栈空, 没有数据~" ); } int value = stack[top]; top = top - 1 ; return value; } public void show () { if (isEmpty()) { System.out.println("null" ); return ; } for (int i = top; i >= 0 ; i--) { System.out.printf("stack[%d]=%d\n" , i, stack[i]); } } } class ClientStack { public static void main (String[] args) { Stack stack = new Stack (5 ); stack.push(1 ); stack.push(2 ); stack.push(3 ); stack.push(4 ); stack.push(5 ); System.out.println("--- 显示栈的数据 ---" ); stack.show(); System.out.println("--- 循环出站 ---" ); while (!stack.isEmpty()) { try { int pop = stack.pop(); System.out.println(pop); } catch (Exception ignored) {} } System.out.println("--- 显示栈的数据 ---" ); stack.show(); } }
数据结构: 哈希表 哈希表(Hash table, 也叫散列表), 是根据关键码值(Key Value)而直接进行访问的数据结构。 也就是说, 它通过把关键码值映射到表中一个位置来访问记录, 以加快查找的速度。 这个映射函数叫做散列函数, 存放记录的数组叫做散列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 public class Emp { public int id; public String name; public Emp next; public Emp (int id,String name) { super (); this .id = id; this .name = name; } } public class EmpLinkedList { private Emp head; public void add (Emp emp) { if (head == null ) { head = emp; return ; } Emp curEmp = head; while (true ) { if (curEmp.next == null ) { break ; } curEmp = curEmp.next; } curEmp.next = emp; } public void list (int no) { if (head == null ) { System.out.println("第" +(no+1 )+"条链表为空" ); return ; } System.out.print("第" +(no+1 )+"条链表的信息为" ); Emp curEmp = head; while (true ) { System.out.printf("=> id=%d name=%s\t" , curEmp.id, curEmp.name); if (curEmp.next == null ) { break ; } curEmp = curEmp.next; } System.out.println(); } public Emp findEmpById (int id) { Emp curEmp = head; while (true ) { if (curEmp == null ){ break ; } if (curEmp.id == id) { break ; } curEmp = curEmp.next; } return curEmp; } } public class HashTab { private EmpLinkedList[] empLinkedListArray; private int size; public HashTab (int size) { this .size = size; empLinkedListArray = new EmpLinkedList [size]; for (int i = 0 ; i < size; i++) { empLinkedListArray[i] = new EmpLinkedList (); } } public void add (Emp emp) { int empLinkedListNo = hashFun(emp.id); empLinkedListArray[empLinkedListNo].add(emp); } public void list () { for (int i = 0 ; i < size; i++){ empLinkedListArray[i].list(i); } } public void findEmpById (int id) { int empLinkedListNo = hashFun(id); Emp emp = empLinkedListArray[empLinkedListNo].findEmpById(id); if (emp != null ){ System.out.printf("在第%d条链表中找到 雇员 id = %d\n" , (empLinkedListNo + 1 ), id); } else { System.out.println("在哈希表中, 没有找到该雇员~" ); } } public int hashFun (int id) { return id % size; } } public class Run { public static void main (String[] args) { HashTab hashTab = new HashTab (7 ); String key = "" ; Scanner scanner = new Scanner (System.in); while (true ) { System.out.println("add:添加雇员" ); System.out.println("list:显示雇员" ); System.out.println("find:查找雇员" ); System.out.println("exit:退出系统" ); key = scanner.next(); switch (key) { case "add" : System.out.println("输入id" ); int id = scanner.nextInt(); System.out.println("输入名字" ); String name = scanner.next(); Emp emp = new Emp (id, name); hashTab.add(emp); break ; case "list" : hashTab.list(); break ; case "find" : System.out.println("输入id" ); hashTab.findEmpById(scanner.nextInt()); break ; case "exit" : scanner.close(); System.exit(0 ); default : break ; } } } }
数据结构: 树
二叉树(BinaryTree)
每个节点最多只能有两个子节点称为二叉树, 二叉树的子节点分为左节点和右节点。
如果该二叉树的所有叶子节点都在最后一层, 并且结点总数=2ⁿ-1, n为层数, 则我们称为满二叉树。
如果该二叉树的所有叶子节点都在最后一层或者倒数第二层, 而且最后一层的叶子节点在左边连续, 倒数第二层的叶子节点在右边连续, 我们称为完全二叉树。
多叉树(MultiwayTree)
每个节点可以有更多的数据项和更多的子节点, 就是多叉树。
节点的度: 在一个节点中, 子节点数量为2, 节点的度就是2, 一个节点的子节点数量为999, 节点的度就是999。
树的度: 在一棵树所有节点中, 最大节点的度就是树的度。
2-3树、 2-3-4树
2-3树是由二节点和三节点构成的树。
2-3-4树是由二节点、 三节点和四节点构成的树。
2-3树、 2-3-4树的所有叶子节点都在同一层。
有两个子节点的节点叫二节点, 二节点要么没有子节点, 要么有两个子节点。
有三个子节点的节点叫三节点, 三节点要么没有子节点, 要么有三个子节点。
有四个子节点的节点叫四节点, 四节点要么没有子节点, 要么有四个子节点。
B树(B-Tree)
B即Balanced, 平衡的意思。
B树通过重新组织节点, 降低树的高度, 并且减少i/o读写次数来提升效率。
B树的阶: 节点的最多子节点个数。 比如2-3树的阶是3, 2-3-4树的阶是4。
B树的搜索, 从根结点开始, 对结点内的关键字(有序)序列进行二分查找, 如果命中则结束, 否则进入查询关键字所属范围的儿子结点, 重复, 直到所对应的儿子指针为空, 或已经是叶子结点。
关键字集合分布在整颗树中, 即叶子节点和非叶子节点都存放数据, 搜索有可能在非叶子结点结束, 其搜索性能等价于在关键字全集内做一次二分查找。
B+树
B+树是B树的变体, 也是一种多路搜索树。
B+树的搜索与B树也基本相同, 区别是B+树只有达到叶子结点才命中( B树可以在非叶子结点命中) , 不可能在非叶子结点命中, 其性能也等价于在关键字全集做一次二分查找。
所有关键字都出现在叶子结点的链表中, 即数据只能在叶子节点( 也叫稠密索引) , 且链表中的关键字( 数据) 恰好是有序的。
非叶子结点相当于是叶子结点的索引( 稀疏索引) 叶子结点相当于是存储关键字( 数据) 的数据层, 更适合文件索引系统。
B树和B+树各有自己的应用场景, 没有性能好坏之分。
B*树
B*树是B+树的变体, 在B+树的非根节点和非叶子结点再增加指向兄弟的指针。
B*树定义了非叶子结点关键字个数至少为(2/3)*M, 即块的最低使用率为2/3, 而B+树的块的最低使用率为B+树的1/2。
B*树分配新结点的概率比B+树要低, 空间使用率更高。
简单二叉树 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 public class HeroNode { private int no; private String name; private HeroNode left; private HeroNode right; public HeroNode (int no, String name) { this .no = no; this .name = name; } public int getNo () { return no; } public void setNo (int no) { this .no = no; } public String getName () { return name; } public void setName (String name) { this .name = name; } public HeroNode getLeft () { return left; } public void setLeft (HeroNode left) { this .left = left; } public HeroNode getRight () { return right; } public void setRight (HeroNode right) { this .right = right; } @Override public String toString () { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + '}' ; } public void preOrder () { System.out.println(this ); if (this .left != null ) { this .left.preOrder(); } if (this .right != null ) { this .right.preOrder(); } } public void infixOrder () { if (this .left != null ) { this .left.infixOrder(); } System.out.println(this ); if (this .right != null ) { this .right.infixOrder(); } } public void postOrder () { if (this .left != null ) { this .left.postOrder(); } if (this .right != null ) { this .right.postOrder(); } System.out.println(this ); } public HeroNode preOrderSearch (int no) { System.out.println("进入前序遍历查找" ); if (this .no == no) { return this ; } HeroNode resNode = null ; if (this .left != null ) { resNode = this .left.preOrderSearch(no); } if (resNode != null ) { return resNode; } if (this .right != null ) { resNode = this .right.preOrderSearch(no); } return resNode; } public HeroNode infixOrderSearch (int no) { HeroNode resNode = null ; if (this .left != null ) { resNode = this .left.infixOrderSearch(no); } if (resNode != null ) { return resNode; } System.out.println("进入中序遍历查找" ); if (this .no == no) { return this ; } if (this .right != null ) { resNode = this .right.infixOrderSearch(no); } return resNode; } public HeroNode postOrderSearch (int no) { HeroNode resNode = null ; if (this .left != null ) { resNode = this .left.postOrderSearch(no); } if (resNode != null ){ return resNode; } if (this .right != null ) { resNode = this .right.postOrderSearch(no); } if (resNode != null ){ return resNode; } System.out.println("进入后序遍历查找" ); if (this .no == no){ return this ; } return null ; } public void delNode (int no) { if (this .left != null && this .left.no == no){ this .left = null ; return ; } if (this .right != null && this .right.no == no){ this .right = null ; return ; } if (this .left != null ) { this .left.delNode(no); } if (this .right != null ) { this .right.delNode(no); } } } public class BinaryTree { private HeroNode root; public void setRoot (HeroNode root) { this .root = root; } public void preOrder () { if (this .root != null ) { this .root.preOrder(); } } public void infixOrder () { if (this .root != null ) { this .root.infixOrder(); } } public void postOrder () { if (this .root != null ) { this .root.postOrder(); } } public HeroNode preOrderSearch (int no) { if (root != null ) { return root.preOrderSearch(no); } else { return null ; } } public HeroNode infixOrderSearch (int no) { if (root != null ) { return root.infixOrderSearch(no); } else { return null ; } } public HeroNode postOrderSearch (int no) { if (root != null ) { return this .root.postOrderSearch(no); } else { return null ; } } public void delNode (int no) { if (root != null ) { if (root.getNo() == no) { root = null ; } else { root.delNode(no); } } else { System.out.println("空树, 不能删除~" ); } } } public class Run { public static void main (String[] args) { BinaryTree binaryTree = new BinaryTree (); HeroNode root = new HeroNode (1 , "宋江" ); HeroNode node2 = new HeroNode (2 , "吴用" ); HeroNode node3 = new HeroNode (3 , "卢俊义" ); HeroNode node4 = new HeroNode (4 , "林冲" ); HeroNode node5 = new HeroNode (5 , "关胜" ); root.setLeft(node2); root.setRight(node3); node3.setLeft(node5); node3.setRight(node4); binaryTree.setRoot(root); System.out.println("前序遍历" ); binaryTree.preOrder(); System.out.println("中序遍历" ); binaryTree.infixOrder(); System.out.println("后序遍历" ); binaryTree.postOrder(); System.out.println("前序遍历查找" ); HeroNode resNode = binaryTree.preOrderSearch(5 ); if (resNode != null ) { System.out.printf("找到了, 信息为 no=%d name=%s" , resNode.getNo(), resNode.getName()); } else { System.out.printf("没有找到 no = %d 的英雄" , 5 ); } System.out.println("中序遍历查找" ); resNode = binaryTree.infixOrderSearch(5 ); if (resNode != null ) { System.out.printf("找到了, 信息为 no=%d name=%s" , resNode.getNo(), resNode.getName()); } else { System.out.printf("没有找到 no = %d 的英雄" , 5 ); } System.out.println("后序遍历查找" ); resNode = binaryTree.postOrderSearch(5 ); if (resNode != null ) { System.out.printf("找到了, 信息为 no=%d name=%s" , resNode.getNo(), resNode.getName()); } else { System.out.printf("没有找到 no = %d 的英雄" , 5 ); } System.out.println(); System.out.println("删除前, 前序遍历" ); binaryTree.preOrder(); binaryTree.delNode(5 ); System.out.println("删除后, 前序遍历" ); binaryTree.preOrder(); } }
数组二叉树 数组存储方式和树的存储方式可以相互转换, 即数组可以转换成树, 树也可以转换成数组。
数组二叉树通常只考虑完全二叉树
第n个元素的左子节点为: 2*n + 1
第n个元素的右子节点为: 2*n + 2
第n个元素的父节点为: (n-1) / 2
n: 表示二叉树中的第几个元素, 从0开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class ArrBinaryTree { private int [] arr; public ArrBinaryTree (int [] arr) { this .arr = arr; } public void preOrder () { this .preOrder(0 ); } private void preOrder (int index) { if (arr == null || arr.length == 0 ) { System.out.println("数组为空, 不能按照二叉树的前序遍历" ); } System.out.println(arr[index]); int indexLeft = 2 * index + 1 ; if (indexLeft < arr.length) { preOrder(indexLeft); } int indexRight = 2 * index + 2 ; if (indexRight < arr.length) { preOrder(indexRight); } } } public class Run { public static void main (String[] args) { int [] arr = {1 , 2 , 3 , 4 , 5 , 6 , 7 }; ArrBinaryTree tree = new ArrBinaryTree (arr); tree.preOrder(); } }
线索二叉树
n个结点的二叉链表中含有n+1 [推导公式: 2n-(n-1) = n + 1]个空指针域。
一个结点的前一个结点, 称为前驱结点; 一个结点的后一个结点, 称为后继结点。
利用二叉链表中的空指针域, 存放指向该结点在某种遍历次序下的前驱和后继结点的指针, 这种附加的指针称为”线索”。
这种加上了线索的二叉链表称为线索链表, 相应的二叉树称为线索二叉树(Threaded BinaryTree)。
根据线索性质的不同, 线索二叉树可分为前序线索二叉树、 中序线索二叉树、 后序线索二叉树。
注意: 当线索化二叉树后, Node节点的属性 left 和 right, 有如下情况
left 可能指向左子树, 也有可能指向前驱节点。
right 可能指向右子树, 也有可能指向后继节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 public class HeroNode { private int no; private String name; private HeroNode left; private HeroNode right; private int leftType; private int rightType; public HeroNode (int no, String name) { this .no = no; this .name = name; } public int getNo () { return no; } public void setNo (int no) { this .no = no; } public String getName () { return name; } public void setName (String name) { this .name = name; } public HeroNode getLeft () { return left; } public void setLeft (HeroNode left) { this .left = left; } public HeroNode getRight () { return right; } public void setRight (HeroNode right) { this .right = right; } public int getLeftType () { return leftType; } public void setLeftType (int leftType) { this .leftType = leftType; } public int getRightType () { return rightType; } public void setRightType (int rightType) { this .rightType = rightType; } @Override public String toString () { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + '}' ; } } public class ThreadedBinaryTree { private HeroNode root; private HeroNode pre = null ; public void setRoot (HeroNode root) { this .root = root; } public void threadedInfix () { threadedInfix(root); } private void threadedInfix (HeroNode node) { if (node == null ) { return ; } threadedInfix(node.getLeft()); if (node.getLeft() == null ){ node.setLeft(pre); node.setLeftType(1 ); } if (pre != null && pre.getRight() == null ) { pre.setRight(node); pre.setRightType(1 ); } pre = node; threadedInfix(node.getRight()); } public void threadedList () { HeroNode node = root; while (node != null ) { while (node.getLeftType() == 0 ) { node = node.getLeft(); } System.out.println(node); while (node.getRightType() == 1 ) { node = node.getRight(); System.out.println(node); } node = node.getRight(); } } } public class Run { public static void main (String[] args) { HeroNode root = new HeroNode (1 ,"tom" ); HeroNode node2 = new HeroNode (3 , "jack" ); HeroNode node3 = new HeroNode (6 ,"smith" ); HeroNode node4 = new HeroNode (8 ,"mary" ); HeroNode node5 = new HeroNode (10 ,"king" ); HeroNode node6 = new HeroNode (14 ,"dim" ); root.setLeft(node2); root.setRight(node3); node2.setLeft(node4); node2.setRight(node5); node3.setLeft(node6); ThreadedBinaryTree tree = new ThreadedBinaryTree (); tree.setRoot(root); tree.threadedInfix(); HeroNode leftNode = node5.getLeft(); HeroNode rightNode = node5.getRight(); System.out.println("10号结点的前驱结点是:" + leftNode); System.out.println("10号结点的后继结点是:" + rightNode); tree.threadedList(); } }
二叉排序树 二叉排序树也翻译为: BST:(Binary Sort(Search) Tree), 对于二叉排序树的任何一个非叶子节点, 要求左子节点的值比当前节点的值小, 右子节点的值比当前节点的值大。
特别说明: 如果有相同的值, 可以将该节点放在左子节点或右子节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 public class Node { int value; Node left; Node right; public Node (int value) { this .value = value; } @Override public String toString () { return "Node{" + "value=" + value + '}' ; } public void add (Node node) { if (node == null ) { return ; } if (node.value < this .value) { if (this .left == null ) { this .left = node; } else { this .left.add(node); } } else { if (this .right == null ) { this .right = node; } else { this .right.add(node); } } } public void infixOrder () { if (this .left != null ) { this .left.infixOrder(); } System.out.println(this ); if (this .right != null ) { this .right.infixOrder(); } } public Node search (int value) { if (value == this .value){ return this ; } else if ( value < this .value) { if (this .left == null ) { return null ; } return this .left.search(value); } else { if (this .right == null ) { return null ; } return this .right.search(value); } } public Node searchParent (Node node) { int value = node.value; if ((this .left != null && this .left.value == value) || (this .right != null && this .right.value == value)) { return this ; } else { if (value < this .value && this .left != null ) { return this .left.searchParent(node); } else if (value >= this .value && this .right != null ) { return this .right.searchParent(node); } return null ; } } } public class BinarySortTree { private Node root; public void add (Node node) { if (root == null ) { root = node; } else { root.add(node); } } public Node search (int value) { if (root != null ) { return root.search(value); } else { return null ; } } public Node searchParent (Node node) { if (node != null ) { return root.searchParent(node); } else { return null ; } } public void delNode (int value) { if (root == null ) return ; Node node = search(value); if (node == null ) return ; Node nextNode = null ; if (node.left != null && node.right != null ) { nextNode = node.right; while (nextNode.left != null ) { nextNode = nextNode.left; } delNode(nextNode.value); nextNode.left = node.left; if (nextNode != node.right) { nextNode.right = node.right; } } else { if (node.left != null ) { nextNode = node.left; } if (node.right != null ) { nextNode = node.right; } } Node parentNode = searchParent(node); if (parentNode != null ) { if (parentNode.left != null && parentNode.left.value == value) { parentNode.left = nextNode; } else if (parentNode.right != null && parentNode.right.value == value) { parentNode.right = nextNode; } } else { root = nextNode; } } public void infixOrder () { if (root != null ) { root.infixOrder(); } } } public class Run { public static void main (String[] args) { int [] arr = {7 ,3 ,10 ,12 ,5 ,1 ,9 ,2 }; BinarySortTree binarySortTree = new BinarySortTree (); for (int i = 0 ; i < arr.length; i++) { binarySortTree.add(new Node (arr[i])); } binarySortTree.infixOrder(); binarySortTree.delNode(3 ); binarySortTree.infixOrder(); } }
平衡二叉树 平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树, 可以保证查询效率较高。
平衡二叉树的左右两个子树的高度差的绝对值不超过1, 并且左右两个子树都是一棵平衡二叉树。 平衡二叉树的常用实现方法有红黑树、 AVL、 替罪羊树、 Treap、 伸展树等。
平衡二叉树是在二叉排序树的基础上就行了优化, 通过左右旋转的方法, 保证左右两个子树的高度差的绝对值不超过1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 public class Node { int value; Node left; Node right; public Node (int value) { this .value = value; } public void add (Node node) { if (node == null ) { return ; } if (node.value < this .value) { if (this .left == null ) { this .left = node; } else { this .left.add(node); } } else { if (this .right == null ) { this .right = node; } else { this .right.add(node); } } if (rightHeight() - leftHeight() > 1 ) { if (right != null && right.leftHeight() > right.rightHeight()) { right.rightRotate(); } leftRotate(); } else if (leftHeight() - rightHeight() > 1 ) { if (left != null && left.rightHeight() > left.leftHeight()) { left.leftRotate(); } rightRotate(); } } @Override public String toString () { return "Node{" + "value=" + value + '}' ; } public void infixOrder () { if (this .left != null ) { this .left.infixOrder(); } System.out.println(this ); if (this .right != null ) { this .right.infixOrder(); } } public int height () { int leftHeight = left == null ? 1 : left.height() + 1 ; int rightHeight = right == null ? 1 : right.height() + 1 ; return Math.max(leftHeight, rightHeight); } public int leftHeight () { if (left == null ) { return 0 ; } return left.height(); } public int rightHeight () { if (right == null ) { return 0 ; } return right.height(); } private void leftRotate () { Node newNode = new Node (value); newNode.left = left; newNode.right =right.left; value = right.value; right = right.right; left = newNode; } private void rightRotate () { Node newNode = new Node (value); newNode.right = right; newNode.left = left.right; value = left.value; left = left.left; right = newNode; } } public class AVLTree { private Node root; public Node getRoot () { return root; } public void add (Node node) { if (root == null ) { root = node; } else { root.add(node); } } public void infixOrder () { if (root != null ) { root.infixOrder(); } } } public class Run { public static void main (String[] args) { int [] arr = {10 ,11 ,7 ,6 ,8 ,9 }; AVLTree avlTree = new AVLTree (); for (int i = 0 ; i < arr.length; i++) { avlTree.add(new Node (arr[i])); } avlTree.infixOrder(); System.out.println(avlTree.getRoot().height()); System.out.println(avlTree.getRoot().leftHeight()); System.out.println(avlTree.getRoot().rightHeight()); } }
赫夫曼树 给定n个权值作为n个叶子结点, 构造一棵二叉树, 若该树的带权路径长度(WPL)达到最小, 称这样的二叉树为最优二叉树, 也称为哈夫曼树(Huffman Tree), 还有的书翻译为霍夫曼树。
路径: 在一棵树中, 从一个结点往下可以达到的孩子结点或孙子结点之间的通路, 称为路径。
路径长度: 通路中, 分支的数目称为路径长度。 若规定根结点的层数为1, 则从根结点到第n层结点的路径长度: 为n-1。
结点的权: 若将树中的结点, 赋一个有着某种含义的数值, 则这个数值称为该结点的权。
带权路径长度: 从根结点到该结点之间的路径长度与该结点的权的乘积。
树的带权路径长度: 所有叶子结点的带权路径长度之和, 记为WPL(weighted path length)。
WPL最小的就是赫夫曼树, 赫夫曼树是带权路径长度最短的树, 权值较大的结点离根较近。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 public class Node implements Comparable <Node> { int value; Node left; Node right; public Node (int value) { this .value = value; } public int getValue () { return value; } public void setLeft (Node left) { this .left = left; } public void setRight (Node right) { this .right = right; } @Override public String toString () { return "Node{" + "value=" + value + '}' ; } @Override public int compareTo (Node node) { return this .value - node.value; } public void preOrder () { System.out.println(this ); if (this .left != null ) { this .left.preOrder(); } if (this .right != null ) { this .right.preOrder(); } } } public class HuffmanTree { public static Node createHuffmanTree (int [] arr) { List<Node> nodes = new ArrayList <Node>(); for (int value : arr) { nodes.add(new Node (value)); } while (nodes.size() > 1 ) { Collections.sort(nodes); Node leftNode = nodes.get(0 ); Node rightNode = nodes.get(1 ); Node node = new Node (leftNode.getValue() + rightNode.getValue()); node.setLeft(leftNode); node.setRight(rightNode); nodes.add(node); nodes.remove(leftNode); nodes.remove(rightNode); } return nodes.get(0 ); } } public class Run { public static void main (String[] args) { int [] arr = {13 , 7 , 8 , 3 , 29 , 6 , 1 }; Node node = HuffmanTree.createHuffmanTree(arr); node.preOrder(); } }
数据结构: 图
图是一种数据结构, 其中结点( 顶点) 可以具有零个或多个相邻元素。
边: 两个顶点之间的连接称为边。
有权图: 边带权值的图是有权图, 也叫网。
有向图: 顶点之间单向连接是有向图。
无向图: 顶点之间双向连接是无向图。
图的表示方式有两种: 二维数组表示( 邻接矩阵) 、 链表表示( 邻接表) 。
邻接矩阵
邻接矩阵是表示图形中顶点之间相邻关系的矩阵。
对于n个顶点的图而言, 矩阵是的row和col表示的是1….n个点。
邻接表
邻接矩阵需要为每个顶点都分配n个边的空间, 其实有很多边都是不存在, 会造成空间的一定损失。
邻接表的实现只关心存在的边, 不关心不存在的边。 因此没有空间浪费, 邻接表由数组和链表组成。
图的搜索: 即是对结点的访问。 一个图有很多个结点, 遍历这些结点, 需要特定策略, 一般有两种访问策略, 深度优先搜索和广度优先搜索。
深度优先搜索(Depth First Search)
需要使用一个数组保存节点是否已经访问, 已经访问过的节点不再访问。
从初始访问结点出发, 这个结点可能有多个邻接结点, 首先访问第一个邻接结点, 结点加入数组。
然后再以这个被访问的邻接结点作为初始结点, 继续访问第一个未被访问的邻接结点。
直到最后未找到结点的邻接结点, 返回最后未找到结点的上一个节点, 访问第二个未被访问邻接结点。
以第二个邻接结点作为初始结点, 继续访问第二个邻接结点的第一个邻接结点。
直到最后未找到结点的邻接结点, 返回该结点的上一个节点, 访问第三个未被访问邻接结点。
一直递归访问, 直到这个结点也没有邻接结点, 在回到上一个节点, 访问第二个结点, 回到第四步。
广度优先搜索(Broad First Search)
还需要使用一个队列保存访问过的结点顺序, 按这个顺序来访问这些结点的邻接结点。
访问初始结点, 并标记结点为已访问, 结点加入队列。
当队列非空时, 继续执行, 否则算法结束。
出队列, 取得队头结点, 查找头结点的第一个邻接结点。
若头结点的邻接结点不存在, 则转到步骤3, 否则循环执行以下步骤
若结点尚未被访间, 则访问结点并标记为已访问, 结点加入队列。
查找头结点的邻接结点后的下一个邻接结点, 转到步骤6。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 public class Graph { private ArrayList<String> vertexList; private int [][] edges; private int numOfEdges; private boolean [] isVisited; public Graph (int n) { vertexList = new ArrayList <String>(n); edges = new int [n][n]; numOfEdges = 0 ; isVisited = new boolean [n]; } public void insertVertex (String vertex) { vertexList.add(vertex); } public void insertEdge (int v1, int v2, int weight) { edges[v1][v2]= weight; edges[v2][v1]= weight; numOfEdges++; } public int getNumOfVertex () { return vertexList.size(); } public int getNumOfEdges () { return numOfEdges; } public String getValueByIndex (int i) { return vertexList.get(i); } public int getWeight (int v1, int v2) { return edges[v1][v2]; } public void showGraph () { for (int [] link : edges) { System.out.println(Arrays.toString(link)); } } public int getFirstNeighbor (int index) { for (int j = 0 ;j < vertexList.size(); j++) { if (edges[index][j] > 0 ) { return j; } } return -1 ; } public int getNextNeighbor (int v1, int v2) { for (int j = v2 + 1 ; j < vertexList.size(); j++) { if (edges[v1][j] > 0 ){ return j; } } return -1 ; } public void dfs (boolean [] isVisited, int i) { System.out.print(getValueByIndex(i) + "=>" ); isVisited[i] = true ; int w = getFirstNeighbor(i); while (w != -1 ){ if (isVisited[w]) { w = getNextNeighbor(i, w); } else { dfs(isVisited, w); } } } public void dfs () { for (int i = 0 ; i < getNumOfVertex(); i++) { if (!isVisited[i]) { dfs(isVisited, i); } } isVisited = new boolean [getNumOfVertex()]; } public void bfs (boolean [] isVisited, int i) { int u; int w; LinkedList<Integer> queue = new LinkedList <Integer>(); System.out.print(getValueByIndex(i) + "=>" ); isVisited[i]= true ; queue.addLast(i); while (!queue.isEmpty()) { u = queue.removeFirst(); w = getFirstNeighbor(u); while (w != -1 ){ if (!isVisited[w]) { System.out.print(getValueByIndex(w) + "=>" ); isVisited[w] = true ; queue.addLast(w); } w = getNextNeighbor(u, w); } } } public void bfs () { for (int i = 0 ; i < getNumOfVertex(); i++) { if (!isVisited[i]) { bfs(isVisited, i); } } isVisited = new boolean [getNumOfVertex()]; } } public class Run { public static void main (String[] args) { String[] vertexes = {"1" ,"2" ,"3" ,"4" ,"5" ,"6" ,"7" ,"8" }; Graph graph = new Graph (vertexes.length); for (String vertex : vertexes) { graph.insertVertex(vertex); } graph.insertEdge(0 , 1 , 1 ); graph.insertEdge(0 , 2 , 1 ); graph.insertEdge(1 , 3 , 1 ); graph.insertEdge(1 , 4 , 1 ); graph.insertEdge(3 , 7 , 1 ); graph.insertEdge(4 , 7 , 1 ); graph.insertEdge(2 , 5 , 1 ); graph.insertEdge(2 , 6 , 1 ); graph.insertEdge(5 , 6 , 1 ); System.out.println("查看图" ); graph.showGraph(); System.out.println("深度优先遍历" ); graph.dfs(); System.out.println(); System.out.println("广度优先遍历" ); graph.bfs(); System.out.println(); } }
算法: 递归 阶乘 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Factorial { public static int factorial (int n) { if (n == 1 ) { return 1 ; } else { int f = factorial(n - 1 ); int num = f * n; System.out.println(f + "x" + n + "=" + num); return num; } } public static void main (String[] args) { factorial(5 ); } }
迷宫 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 public class Maze { public static int [][] initMaze() { int [][] map = new int [8 ][8 ]; for (int row = 0 ; row < map.length; row++) { map[row][0 ] = 1 ; map[row][map[row].length - 1 ] = 1 ; } for (int col = 0 ; col < map.length; col++) { map[0 ][col] = 1 ; map[map.length - 1 ][col] = 1 ; } map[1 ][2 ] = 1 ; map[3 ][1 ] = 1 ; map[3 ][2 ] = 1 ; map[3 ][6 ] = 1 ; mazeShow(map); return map; } public static void mazeShow (int [][] map) { for (int [] ints : map) { for (int anInt : ints) { System.out.print(anInt + " " ); } System.out.println(); } } public static boolean setWay (int [][] map, int x, int y) { if (map[map.length - 2 ][map.length - 2 ] == 2 ) { return true ; } else { if (map[y][x] == 0 ) { map[y][x] = 2 ; if (setWay(map, x + 1 , y)) { return true ; } else if (setWay(map, x, y + 1 )) { return true ; } else if (setWay(map, x - 1 , y)) { return true ; } else if (setWay(map, x, y - 1 )) { return true ; } else { map[y][x] = 3 ; return false ; } } else { return false ; } } } public static void main (String[] args) { int [][] ints = initMaze(); boolean bool = setWay(ints, 1 , 1 ); if (bool) { System.out.println("破解完成" ); } else { System.out.println("迷宫无解" ); } mazeShow(ints); } }
八皇后 在8×8格的国际象棋上摆放8个皇后, 使其不能互相攻击, 即任意两个皇后都不能处于同一行、 同一列或同一斜线上, 一共有多少种摆法?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 public class Queen { int count = 0 ; int [][] queenBoard; int [][] position; public Queen () { queenBoard = new int [8 ][8 ]; position = new int [8 ][2 ]; System.out.println("棋盘下标: " ); System.out.println(" ↑" ); for (int i = 7 ; i >= 0 ; i--) { System.out.print(i+"|" ); for (int j = 0 ; j < 8 ; j++) { System.out.print(i + "," + j + " " ); } System.out.println(); } System.out.println("Y|X-0--1---2---3---4---5---6---7-→" ); } public void show () { System.out.println("第" + count + "种解法: " ); System.out.println(Arrays.deepToString(position)); for (int [] ints : position) { queenBoard[7 -ints[1 ]][ints[0 ]] = 1 ; } System.out.println(" ↑" ); for (int i = 0 ; i < queenBoard.length; i++) { System.out.print((7 -i) + "|" ); for (int anInt : queenBoard[i]) { System.out.print(anInt + " " ); } System.out.println(); } System.out.println("Y|0-1-2-3-4-5-6-7-X→" ); queenBoard = new int [8 ][8 ]; } private void check (int num) { if (num == 9 ) { count = count + 1 ; show(); } else { for (int i = 0 ; i < 8 ; i++) { if (judge(i, num - 1 , num)) { position[num - 1 ][0 ] = i; position[num - 1 ][1 ] = num - 1 ; check(num + 1 ); } } } } public boolean judge (int x, int y, int num) { for (int i = 0 ; i < (num - 1 ); i++) { if (position[i][0 ] == x) { return false ; } if (position[i][1 ] == y) { return false ; } if (Math.abs(x - position[i][0 ]) == Math.abs(y - position[i][1 ])) { return false ; } } return true ; } public static void main (String[] args) { Queen queen = new Queen (); queen.check(1 ); } }
算法: 查找 顺序查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class SeqSearch { public static void main (String[] args) { int [] arr = {1 , 9 , 34 , 11 , -1 , 34 , 39 }; System.out.println(seqSearch(arr, 34 )); System.out.println(seqSearchAll(arr, 34 )); } public static int seqSearch (int [] arr, int findVal) { for (int i = 0 ; i < arr.length; i++) { if (arr[i] == findVal) { return i; } } return -1 ; } public static List<Integer> seqSearchAll (int [] arr, int findVal) { List<Integer> result = new ArrayList <Integer>(); for (int i = 0 ; i < arr.length; i++) { if (arr[i] == findVal) { result.add(i); } } return result; } }
二分查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 public class BinarySearch { public static void main (String[] args) { int [] arr = {1 , 8 , 10 , 89 , 89 , 89 , 1000 , 1234 }; System.out.println(binarySearch(arr, 0 , arr.length - 1 , 101 )); System.out.println(binarySearchAll(arr, 0 , arr.length - 1 , 89 )); } public static int binarySearch (int [] arr, int left, int right, int findVal) { if (left > right) { return -1 ; } int mid = (left + right) / 2 ; int midVal = arr[mid]; if (findVal > midVal) { return binarySearch(arr, mid + 1 , right, findVal); } else if (findVal < midVal) { return binarySearch(arr, left, mid - 1 , findVal); } else { return mid; } } public static List<Integer> binarySearchAll (int [] arr, int left, int right, int findVal) { if (left > right) { return new ArrayList <Integer>(); } int mid = (left + right) / 2 ; int midVal = arr[mid]; if (findVal > midVal) { return binarySearchAll(arr, mid + 1 , right, findVal); } else if (findVal < midVal) { return binarySearchAll(arr, left, mid - 1 , findVal); } else { List<Integer> result = new ArrayList <Integer>(); result.add(mid); int temp = mid - 1 ; while (true ) { if (temp < 0 || arr[temp] != findVal) { break ; } result.add(temp); temp = temp - 1 ; } temp = mid + 1 ; while (true ) { if (temp > right || arr[temp] != findVal) { break ; } result.add(temp); temp = temp + 1 ; } return result; } } }
插值查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 public class InsertSearch { public static void main (String[] args) { int [] arr = new int [100 ]; for (int i = 0 ; i < 100 ; i++) { arr[i] = i + 1 ; } System.out.println(insertSearch(arr, 0 , arr.length - 1 , 1 )); System.out.println(insertSearchAll(arr, 0 , arr.length - 1 , 89 )); } public static int insertSearch (int [] arr, int left, int right, int findVal) { if (left > right || arr[0 ] > findVal || arr[arr.length - 1 ] < findVal) { return -1 ; } int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]); int midVal = arr[mid]; if (findVal > midVal) { return insertSearch(arr, mid + 1 , right, findVal); } else if (findVal < midVal) { return insertSearch(arr, left, mid - 1 , findVal); } else { return mid; } } public static List<Integer> insertSearchAll (int [] arr, int left, int right, int findVal) { if (left > right || arr[0 ] > findVal || arr[arr.length - 1 ] < findVal) { return new ArrayList <Integer>(); } int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]); int midVal = arr[mid]; if (findVal > midVal) { return insertSearchAll(arr, mid + 1 , right, findVal); } else if (findVal < midVal) { return insertSearchAll(arr, left, mid - 1 , findVal); } else { List<Integer> result = new ArrayList <Integer>(); result.add(mid); int temp = mid - 1 ; while (true ) { if (temp < 0 || arr[temp] != findVal) { break ; } result.add(temp); temp = temp - 1 ; } temp = mid + 1 ; while (true ) { if (temp > right || arr[temp] != findVal) { break ; } result.add(temp); temp = temp + 1 ; } return result; } } }
斐波那契查找
黄金分割点是指把一条线段分割为两部分, 使其中一部分与全长之比等于另一部分与这部分之比。 其比值是一个无理数, 用分数表示为(√5-1)/2, 取其前三位数字的近似值是0.618。 由于按此比例设计的造型十分美丽, 因此称为黄金分割, 也称为中外比。 这个分割点就叫做黄金分割点( golden section ratio) , 通常用 Φ 表示。 这是一个十分有趣的数字, 以0.618来近似表示, 通过简单的计算就可以发现: (1-0.618)/0.618≈0.618, 即一条线段上有两个黄金分割点。
斐波那契数列 {1, 1, 2, 3, 5, 8, 13, 21, 34, 55}, 前两个值相加等于第三个值, 以此类推。 发现斐波那契数列的两个相邻数的比例: 34/55, 无限接近黄金分割值 0.618。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 public class FibonacciSearch { public static void main (String[] args) { int [] arr = {1 , 8 , 89 , 1000 , 1234 }; System.out.println(fibSearch(arr, 89 )); } public static int [] fib(int length) { int [] f = new int [length]; f[0 ] = 1 ; f[1 ] = 1 ; for (int i = 2 ; i < length; i++) { f[i] = f[i-1 ] + f[i-2 ]; } return f; } public static int fibSearch (int [] arr, int findVal) { int right = arr.length - 1 ; int [] f = fib(20 ); int k = 0 ; for (int i = 0 ; i < f.length; i++) { if (f[i] >= right) { k = i; break ; } } int [] temp = new int [f[k]]; for (int i = 0 ; i < arr.length; i++) { temp[i] = arr[i]; } for (int i = arr.length; i < temp.length; i++) { temp[i] = arr[arr.length - 1 ]; } int mid = 0 ; int left = 0 ; while (left <= right) { mid = left + f[k-1 ] - 1 ; if (findVal > temp[mid]) { left = mid + 1 ; k = k - 2 ; } else if (findVal < temp[mid]) { right = mid - 1 ; k = k - 1 ; } else { if (mid <= right) { return mid; } else { return right; } } } return -1 ; } }
算法: 排序 插入排序 插入式排序属于内部排序法, 是对于欲排序的元素以插入的方式找寻该元素的适当位置, 以达到排序的目的。
直接插入排序 插入排序 (Insertion Sort) 的基本思想是: 把n个待排序的元素看成为一个有序表和一个无序表, 开始时有序表中只包含一个元素, 无序表中包含有n-1个元素, 排序过程中每次从无序表中取出第一个元素, 把它的排序码依次与有序表元素的排序码进行比较, 将它插入到有序表中的适当位置, 使之成为新的有序表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class InsertSort { public static void main (String[] args) { int [] arr = {3 , 9 , 1 , 10 , 12 }; for (int i = 1 ; i < arr.length; i++) { int insertValue = arr[i]; for (int n = i - 1 ; n >= 0 ; n--) { if (arr[n] > insertValue) { arr[n + 1 ] = arr[n]; arr[n] = insertValue; } } } System.out.println(Arrays.toString(arr)); } }
希尔排序 希尔排序是希尔 (DonaldShell Sort) 于1959年提出的一种排序算法。 希尔排序也是种插入排序, 它是简单插入排序经过改进之后的一个更高效的版本, 也称为缩小增量排序。 它的基本思想是: 把记录按下标的一定增量分组, 对每组使用直接插入排序算法排序.随着增量逐渐减少, 每组包含的关键词越来越多, 当增量减至1时, 整个文件恰被分成一组, 算法便终止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public class ShellSort { public static void main (String[] args) { int [] arr = {8 ,9 ,1 ,7 ,2 ,3 ,5 ,4 ,6 ,0 }; shellMove(arr); System.out.println(Arrays.toString(arr)); } private static void shellInsert (int [] arr) { int temp = 0 ; for (int group = (arr.length / 2 ); group > 0 ; group /= 2 ) { for (int i = group; i < arr.length; i++) { for (int n = (i - group); n >= 0 ; n-=group) { System.out.print(arr[n] +":" + arr[n + group] + "\t" ); if (arr[n] > arr[n + group]) { temp = arr[n]; arr[n] = arr[n + group]; arr[n + group] = temp; } } System.out.println(); } System.out.println("-----" ); } } private static void shellMove (int [] arr) { for (int gap = arr.length / 2 ; gap > 0 ; gap /= 2 ) { for (int i = gap; i < arr.length; i++) { int j = i; int temp = arr[j]; if (arr[j] < arr[j - gap]) { while (j - gap >= 0 && temp < arr[j - gap]) { arr[j] = arr[j - gap]; j -= gap; } arr[j] = temp; } } } } }
选择排序 选择式排序属于内部排序法, 是从欲排序的数据中, 按指定的规则选出某一元素, 再依规定交换位置后达到排序的目的。
简单选择排序 选择排序 (Select Sort) 也是一种简单的排序方法。 它的基本思想是: 第一次从arr[0]-arr[n-1]中选取最小值, 与arr[0]交换, 第二次从arr[1]-arr[n-1]中选取最小值, 与arr[1]交换, 第三次从arr[2]-arr[n-1]中选取最小值, 与arr[2]交换, ···, 第i次从arr[i-1]-arr[n-1]中选取最小值, 与arr[i-1]交换, ···, 第n-1次从arr[n-2]-arr[n-1]中选取最小值, 与arr[n-2]交换, 总共通过n-1次, 得到一个按排序码从小到大排列的有序序列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class SelectSort { public static void main (String[] args) { int [] arr = {3 , 9 , 1 , 10 , 12 }; for (int i = 0 ; i < arr.length - 1 ; i++) { int index = i; int indexValue = arr[i]; for (int n = 1 + i; n < arr.length; n++) { if (indexValue > arr[n]) { index = n; indexValue = arr[n]; } } if (index != i) { arr[index] = arr[i]; arr[i] = indexValue; } } System.out.println(Arrays.toString(arr)); } }
堆排序
堆排序是利用堆这种数据结构而设计的一种排序算法, 堆排序是一种选择排序, 它也是不稳定排序, 堆也是完全二叉树。
每个结点的值都大于或等于其左右子结点的值称为大顶堆, 左节点的值和右节点的值的没有大小关系区分。
大顶堆特点, 数组储存:arr[i] >= arr[2i+1] && arr[i] >= arr[2 i+2], i对应第几个节点, i从0开始。
每个结点的值都小于或等于其左右子结点的值, 称为小顶堆。
小顶堆特点, 数组储存:arr[i] <= arr[2*i+1] && arr[i] <= arr[2*i+2], i对应第几个节点, i从0开始。
一般升序采用大顶堆, 降序采用小顶堆。
堆排序使用的都是数组储存二叉树的结构: 数组二叉树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public class HeapSort { public static void main (String[] args) { int [] arr = {4 , 6 , 8 , 5 , 9 }; heapSort(arr); } public static void heapSort (int arr[]) { int nodeCount = arr.length /2 - 1 ; for (int i = nodeCount; i >= 0 ; i--) { adjustHeap(arr, i, arr.length); } System.out.println("数组转化为大顶堆: " + Arrays.toString(arr)); int temp = 0 ; for (int j = arr.length - 1 ; j > 0 ; j--) { temp = arr[j]; arr[j] = arr[0 ]; arr[0 ] = temp; adjustHeap(arr,0 , j); } System.out.println("排序后数组: " + Arrays.toString(arr)); } public static void adjustHeap (int [] arr, int i, int length) { int temp = arr[i]; for (int k = 2 * i + 1 ; k < length; k = k * 2 + 1 ) { if (k + 1 < length && arr[k] < arr[k + 1 ]) { k = k + 1 ; } if (arr[k] > temp) { arr[i] = arr[k]; i = k; } else { break ; } } arr[i] = temp; } }
交换排序 冒泡排序 冒泡排序 (Bubble Sort) 的基本思想是: 通过对待排序序列从前向后 (从下标较小的元素开始), 依次比较相邻元素的值, 若发现逆序则交换, 使值较大的元素逐渐从前移向后部, 就象水底下的气泡一样逐渐向上冒。 因为排序的过程中, 各元素不断接近自己的位置, 如果一圈比较下来没有进行过交换, 就说明序列有序, 因此要在排序过程中设置一个标志flag判断元素是否进行过交换。 从而减少不必要的比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class BubbleSort { public static void main (String[] args) { int [] arr = {3 , 9 , 1 , 10 , 12 }; int temp = 0 ; boolean flag = false ; for (int i = 0 ; i < arr.length - 1 ; i++) { for (int n = 0 ; n < arr.length - 1 - i; n++) { if (arr[n] > arr[n + 1 ]) { temp = arr[n]; arr[n] = arr[n + 1 ]; arr[n + 1 ] = temp; flag = true ; } } if (!flag) { break ; } else { flag = false ; } } System.out.println(Arrays.toString(arr)); } }
快速排序 快速排序 (Quick Sort) 是对冒泡排序的一种改进。 基本思想是: 通过一趟排序将要排序的数据分割成独立的两部分, 其中一部分的所有数据都比另外一部分的所有数据都要小, 然后再按此方法对这两部分数据分别进行快速排序, 整个排序过程可以递归进行, 以此达到整个数据变成有序序列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 public class QuickSort { public static void main (String[] args) { int [] arr = {-9 , 78 , 0 , -789 , 23 , -567 , 70 }; quickSort(arr, 0 , arr.length - 1 ); System.out.println(Arrays.toString(arr)); } public static void quickSort (int [] arr, int left, int right) { int l = left; int r = right; int pivot = arr[(left + right) / 2 ]; int temp; while (l < r){ while (arr[l] < pivot) { l = l + 1 ; } while (arr[r] > pivot) { r = r - 1 ; } if (l >= r) { break ; } temp = arr[l]; arr[l] = arr[r]; arr[r] = temp; if (arr[r] == pivot) { l = l + 1 ; } if (arr[l] == pivot) { r = r - 1 ; } } if (l == r) { l = l + 1 ; r = r - 1 ; } if (left < r) { quickSort(arr, left, r); } if (right > l) { quickSort(arr, l, right); } } }
归并排序 归并排序 (Merge Sort) 是利用归并的思想实现的排序方法, 该算法采用经典的分治 (divide-and-conquer) 策略。 分治法将问题分 (divide) 成一些小的问题然后递归求解, 而治 (conquer) 的阶段则将分的阶段得到的各答案” 修补”在一起, 即分而治之。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 public class MergeSort { public static void main (String[] args) { int [] arr = {8 , 5 , 4 , 6 , 1 , 3 , 7 , 2 }; int [] temp = new int [arr.length]; mergeSort(arr, 0 , arr.length - 1 , temp); System.out.println(Arrays.toString(arr)); } public static void mergeSort (int [] arr, int left, int right, int [] temp) { if (left < right) { int mid = (left + right) / 2 ; mergeSort(arr, left, mid, temp); mergeSort(arr, mid + 1 , right, temp); merge(arr, left, mid, right, temp); } } public static void merge (int [] arr, int left, int mid, int right, int [] temp) { int i = left; int j = mid + 1 ; int t = 0 ; while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[t] = arr[i]; t = t + 1 ; i = i + 1 ; } else { temp[t] = arr[j]; t = t + 1 ; j = j + 1 ; } } while (i <= mid) { temp[t] = arr[i]; t = t + 1 ; i = i + 1 ; } while (j <= right) { temp[t] = arr[j]; t = t + 1 ; j = j + 1 ; } int arrIndex = left; for (int n = 0 ; n < t; n++) { arr[arrIndex] = temp[n]; arrIndex = arrIndex + 1 ; } }; }
基数排序 基数排序 (Radix Sort) 属于“ 分配式排序” (distribution sort), 又称“ 桶子法” (Bucket Sort) 或 (Bin Sort), 顾名思义, 它是通过键值的各个位的值, 将要排序的元素分配至某些“ 桶” 中, 达到排序的作用。 将所有待比较数值统一为同样的数位长度, 数位较短的数前面补零。 然后, 从最低位开始, 依次进行一次排序。 这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 public class RadixSort { public static void main (String[] args) { int [] arr = {53 , 3 , 542 , 748 , 14 , 214 }; radixSort(arr); System.out.println(Arrays.toString(arr)); } public static void radixSort (int [] arr) { int [][] bucket = new int [10 ][arr.length]; int [] bucketCount = new int [bucket.length]; int max = arr[0 ]; for (int i = 1 ; i < arr.length; i++) { if (max < arr[i]) { max = arr[i]; } } int maxLength = (max + "" ).length(); for (int i = 0 , n = 1 ; i < maxLength; i++, n *= 10 ) { for (int j = 0 ; j < arr.length; j++) { int bucketNum = arr[j] / n % 10 ; int bucketIndex = bucketCount[bucketNum]; bucket[bucketNum][bucketIndex] = arr[j]; bucketCount[bucketNum] = bucketCount[bucketNum] + 1 ; } int index = 0 ; for (int j = 0 ; j < bucket.length; j++) { int bucketIndex = bucketCount[j]; if (bucketIndex > 0 ) { for (int k = 0 ; k < bucketIndex; k++) { arr[index] = bucket[j][k]; index = index + 1 ; } bucketCount[j] = 0 ; } } } } }
算法: 压缩 赫夫曼编码 赫夫曼编码也翻译为: 哈夫曼编码(Huffman Coding), 又称霍夫曼编码, 是一种编码方式, 是可变字长编码(VLC)的一种, 属于一种程序算法。
赫夫曼编码是赫夫曼树 在电讯通信中的经典的应用之一。 广泛地用于数据文件压缩, 其压缩率通常在20%~90%之间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 public class Node implements Comparable <Node> { int weight; Byte data; Node left; Node right; public Node (int weight, Byte data) { this .weight = weight; this .data = data; } @Override public int compareTo (Node node) { return this .weight - node.weight; } @Override public String toString () { return "Node{" + "weight=" + weight + ", data=" + data + '}' ; } public void preorder () { System.out.println(this ); if (this .left != null ) { this .left.preorder(); } if (this .right != null ) { this .right.preorder(); } } } public class HuffmanCode { private Map<Byte, String> huffmanCodes; private String endBinaryString; public HuffmanCode () { this .huffmanCodes = new HashMap <>(); } public Map<Byte, String> getHuffmanCodes () { return huffmanCodes; } public String getEndBinaryString () { return endBinaryString; } public byte [] encrypt(byte [] bytes) { List<Node> nodes = this .getNodes(bytes); Node huffmanTree = this .createHuffmanTree(nodes); this .createHuffmanCode(huffmanTree); return this .encryptHuffmanCodeBytes(bytes, this .huffmanCodes); } private byte [] encryptHuffmanCodeBytes(byte [] bytes, Map<Byte, String> huffmanCodes) { StringBuffer buffer = new StringBuffer (); for (byte b : bytes) { String path = huffmanCodes.get(b); buffer.append(path); } int len; if (buffer.length() % 8 == 0 ) { len = buffer.length() / 8 ; } else { len =buffer.length() / 8 + 1 ; } byte [] huffmanCodeBytes = new byte [len]; int index = 0 ; for (int start = 0 ; start < buffer.length(); start += 8 ) { int end = start + 8 > buffer.length() ? buffer.length() : start + 8 ; String binaryString = buffer.substring(start, end); if (end == buffer.length()) { this .endBinaryString = binaryString; } huffmanCodeBytes[index] = (byte ) Integer.parseInt(binaryString,2 ); index = index + 1 ; } return huffmanCodeBytes; } public byte [] decrypt(byte [] bytes, Map<Byte, String> huffmanCodes, String endBinaryString) { StringBuffer buffer = new StringBuffer (); for (int index = 0 ; index < bytes.length; index++) { byte b = bytes[index]; String binaryString = Integer.toBinaryString(b&0xFF ); binaryString = String.format("%8s" , binaryString).replace(' ' , '0' ); if (index != bytes.length - 1 ) { buffer.append(binaryString); } else { buffer.append(endBinaryString); } } Map<String, Byte> map = new HashMap <String, Byte>(); for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) { map.put(entry.getValue(), entry.getKey()); } List<Byte> byteList = new ArrayList <>(); for (int i = 0 ; i < buffer.length();) { int count = 1 ; boolean flag = true ; Byte b = null ; while (flag){ String key = buffer.substring(i, i+count); b = map.get(key); if (b == null ){ count = count + 1 ; } else { i = count + i; flag = false ; } } byteList.add(b); } byte [] byteArray = new byte [byteList.size()]; for (int i = 0 ; i < byteList.size(); i++) { byteArray[i] = byteList.get(i); } return byteArray; } private void createHuffmanCode (Node huffmanTree) { getCodes(huffmanTree.left,"0" , new StringBuffer ()); getCodes(huffmanTree.right,"1" , new StringBuffer ()); } private void getCodes (Node node, String code, StringBuffer path) { StringBuffer buffer = new StringBuffer (path); buffer.append(code); if (node != null ) { if (node.data == null ) { getCodes(node.left, "0" , buffer); getCodes(node.right, "1" , buffer); } else { huffmanCodes.put(node.data, buffer.toString()); } } } private Node createHuffmanTree (List<Node> nodes) { while (nodes.size() > 1 ) { Collections.sort(nodes); Node leftNode = nodes.get(0 ); Node rightNode = nodes.get(1 ); Node node = new Node (leftNode.weight + rightNode.weight, null ); node.left = leftNode; node.right = rightNode; nodes.add(node); nodes.remove(leftNode); nodes.remove(rightNode); } return nodes.get(0 ); } private List<Node> getNodes (byte [] bytes) { Map<Byte, Integer> weights = new HashMap <>(); for (byte b : bytes) { Integer weight = weights.get(b); if (weight == null ){ weights.put(b, 1 ); } else { weights.put(b, weight + 1 ); } } List<Node> nodes = new ArrayList <Node>(); weights.forEach((b, weight) -> { Node node = new Node (weight, b); nodes.add(node); }); return nodes; } } public class Run { public static void main (String[] args) { String srcFile = "C:\\Users\\Administrator\\Desktop\\1.jpg" ; String dstFile = "C:\\Users\\Administrator\\Desktop\\1.zip" ; encrypt(srcFile, dstFile); String dstFile1 = "C:\\Users\\Administrator\\Desktop\\11.jpg" ; decrypt(dstFile, dstFile1); } private static void encrypt (String srcFile, String dstFile) { FileInputStream is = null ; OutputStream os = null ; ObjectOutputStream oos = null ; try { is = new FileInputStream (srcFile); byte [] contentBytes = new byte [is.available()]; is.read(contentBytes); HuffmanCode huffmanCode = new HuffmanCode (); byte [] byteCodes = huffmanCode.encrypt(contentBytes); Map<Byte, String> huffmanCodes = huffmanCode.getHuffmanCodes(); String endBinaryString = huffmanCode.getEndBinaryString(); os = new FileOutputStream (dstFile); oos = new ObjectOutputStream (os); oos.writeObject(byteCodes); oos.writeObject(huffmanCodes); oos.writeObject(endBinaryString); } catch (Exception e) { e.printStackTrace(); } finally { try { if (is != null ) { is.close(); } if (os != null ) { os.close(); } if (oos != null ) { oos.close(); } } catch (Exception e) { e.printStackTrace(); } } } public static void decrypt (String srcFile, String dstFile) { InputStream is = null ; ObjectInputStream ois = null ; OutputStream os = null ; try { is = new FileInputStream (srcFile); ois = new ObjectInputStream (is); byte [] huffmanBytes = (byte []) ois.readObject(); Map<Byte, String> huffmanCodes = (Map<Byte, String>) ois.readObject(); String endBinaryString = (String) ois.readObject(); HuffmanCode huffmanCode = new HuffmanCode (); byte [] decrypt = huffmanCode.decrypt(huffmanBytes, huffmanCodes, endBinaryString); os = new FileOutputStream (dstFile); os.write(decrypt); } catch (Exception e) { e.printStackTrace(); } finally { try { if (is != null ) { is.close(); } if (ois != null ) { ois.close(); } if (os != null ) { os.close(); } } catch (Exception e) { e.printStackTrace(); } } } }
算法: 常用 分治算法 分治法在每一层递归上都有三个步骤
分解: 将原问题分解为若干个规模较小, 相互独立, 与原问题形式相同的子问题
解决: 若子问题规模较小而容易被解决则直接解, 否则递归地分解各个子问题
合并: 将各个子问题的解, 合并为原问题的解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Dac { public static void main (String[] args) { hanoiTower(2 ,'A' ,'B' ,'C' ); } public static void hanoiTower (int num, char a, char b, char c) { if (num == 1 ) { System.out.println("第1个盘从 " + a +"->" + c); } else { hanoiTower(num - 1 , a, c, b); System.out.println("第" + num + "个盘从 " + a +"->" + c); hanoiTower(num - 1 , b, a, c); } } }
贪心算法 贪婪算法(贪心算法)是指在对问题进行求解时, 在每一步选择中都采取最好或者最优(即最有利)的选择, 从而希望能够导致结果是最好或者最优的算法。
贪婪算法所得到的结果不一定是最优的结果(有时候会是最优解), 但是都是相对近似最优解的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 public class Greedy { public static void main (String[] args) { List<String> greedy = greedy(); System.out.println(greedy); } public static List<String> greedy () { HashSet<String> allAreas = new HashSet <String>(); allAreas.add("北京" ); allAreas.add("上海" ); allAreas.add("天津" ); allAreas.add("广州" ); allAreas.add("深圳" ); allAreas.add("成都" ); allAreas.add("杭州" ); allAreas.add("大连" ); HashMap<String, HashSet<String>> broadcasts = new HashMap <String, HashSet<String>>(); HashSet<String> hashSet1 = new HashSet <String>(); hashSet1.add("北京" ); hashSet1.add("上海" ); hashSet1.add("天津" ); HashSet<String> hashSet2 = new HashSet <String>(); hashSet2.add("广州" ); hashSet2.add("北京" ); hashSet2.add("深圳" ); HashSet<String> hashSet3 = new HashSet <String>(); hashSet3.add("成都" ); hashSet3.add("上海" ); hashSet3.add("杭州" ); HashSet<String> hashSet4 = new HashSet <String>(); hashSet4.add("上海" ); hashSet4.add("天津" ); HashSet<String> hashSet5 = new HashSet <String>(); hashSet5.add("杭州" ); hashSet5.add("大连" ); broadcasts.put("K1" , hashSet1); broadcasts.put("K2" , hashSet2); broadcasts.put("K3" , hashSet3); broadcasts.put("K4" , hashSet4); broadcasts.put("K5" , hashSet5); ArrayList<String> selects = new ArrayList <String>(); HashSet<String> tempSet =new HashSet <String>(); String maxKey; while (allAreas.size() != 0 ) { maxKey = null ; for (String key : broadcasts.keySet()) { tempSet.clear(); HashSet<String> areas = broadcasts.get(key); tempSet.addAll(areas); tempSet.retainAll(allAreas); if (tempSet.size() > 0 && (maxKey == null || tempSet.size() > broadcasts.get(maxKey).size())) { maxKey = key; } } if (maxKey != null ) { selects.add(maxKey); allAreas.removeAll(broadcasts.get(maxKey)); } } return selects; } }
回溯算法 回溯算法实际上一个类似枚举的搜索尝试过程, 主要是在搜索尝试过程中寻找问题的解, 当发现已不满足求解条件时, 就“ 回溯” 返回, 尝试别的路径。
回溯法是一种选优搜索法, 按选优条件向前搜索, 以达到目标。 但当探索到某一步时, 发现原先选择并不优或达不到目标, 就退回一步重新选择, 这种走不通就退回再走的技术为回溯法, 而满足回溯条件的某个状态的点称为“ 回溯点” 。
许多复杂的, 规模较大的问题都可以使用回溯法, 有“ 通用解题方法” 的美称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 public class Backtracking { public static void main (String[] args) { int row = 1 ; int column = 3 ; int [][] chessboard = new int [X][Y]; long start = System.currentTimeMillis(); backtracking(chessboard, row, column, 1 ); long end = System.currentTimeMillis(); System.out.println("共耗时:" +(end-start)+"秒" ); for (int [] rows : chessboard) { for (int step : rows) { System.out.print(step +"\t" ); } System.out.println(); } } private static int X = 6 ; private static int Y = 6 ; private static boolean [] visited = new boolean [X * Y]; private static boolean finished; public static void backtracking (int [][] chessboard, int row, int column, int step) { chessboard[row][column] = step; visited[row * X + column] = true ; Point point = new Point (column, row); ArrayList<Point> ps = next(point); sort(ps); while (!ps.isEmpty()) { Point p = ps.remove(0 ); if (!visited[p.y * X + p.x]){ backtracking(chessboard, p.y, p.x, step + 1 ); } } if (step < X * Y && !finished) { chessboard[row][column] = 0 ; visited[row * X + column] = false ; } else { finished = true ; } } public static ArrayList<Point> next (Point curPoint) { ArrayList<Point> ps = new ArrayList <Point>(); Point p1 = new Point (); if ((p1.x = curPoint.x - 2 ) >= 0 && (p1.y = curPoint.y - 1 ) >= 0 ) { ps.add(new Point (p1)); } if ((p1.x = curPoint.x - 1 ) >= 0 && (p1.y = curPoint.y - 2 ) >= 0 ) { ps.add(new Point (p1)); } if ((p1.x = curPoint.x + 1 ) < X && (p1.y = curPoint.y - 2 ) >= 0 ) { ps.add(new Point (p1)); } if ((p1.x = curPoint.x + 2 ) < X && (p1.y = curPoint.y - 1 ) >= 0 ) { ps.add(new Point (p1)); } if ((p1.x = curPoint.x + 2 ) < X && (p1.y = curPoint.y + 1 ) < Y) { ps.add(new Point (p1)); } if ((p1.x = curPoint.x + 1 ) < X && (p1.y = curPoint.y + 2 ) < Y) { ps.add(new Point (p1)); } if ((p1.x = curPoint.x - 1 ) >= 0 && (p1.y = curPoint.y + 2 ) < Y) { ps.add(new Point (p1)); } if ((p1.x = curPoint.x - 2 ) >= 0 && (p1.y = curPoint.y + 1 ) < Y) { ps.add(new Point (p1)); } return ps; } public static void sort (ArrayList<Point> ps) { ps.sort(new Comparator <Point>(){ @Override public int compare (Point o1, Point o2) { int count1 = next(o1).size(); int count2 = next(o2).size(); if (count1< count2) { return -1 ; } else if (count1 == count2) { return 0 ; } else { return 1 ; } } }); } }
KMP算法 KMP是一个解决模式串在文本串是否出现过, 如果出现过, 最早出现的位置的经典算法。
KMP方法算法就利用之前判断过信息, 通过一个next数组, 保存模式串中前后最长公共子序列的长度, 每次回湖时, 通过next数组找到, 前面匹配过的位置, 省去了大量的计算时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 public class KMP { public static void main (String[] args) { String str1 = "BBC ABCDAB ABCDABCDABDE" ; String str2 = "ABCDABD" ; int violenceMatch = violenceMatch(str1, str2); System.out.println("index=" + violenceMatch); int kmp = kmp(str1, str2); System.out.println("index=" + kmp); } public static int kmp (String str1, String str2) { int [] next = kmpNext(str2); for (int i = 0 , j = 0 ; i < str1.length(); i++){ while (j > 0 && str1.charAt(i) != str2.charAt(j)) { j = next[j-1 ]; } if (str1.charAt(i) == str2.charAt(j)) { j++; } if (j == str2.length()) { return i - j + 1 ; } } return -1 ; } public static int [] kmpNext(String dest) { int [] next = new int [dest.length()]; next[0 ] = 0 ; for (int i = 1 , j = 0 ; i < dest.length(); i++) { while (j > 0 && dest.charAt(i) != dest.charAt(j)) { j = next[j-1 ]; } if (dest.charAt(i) == dest.charAt(j)) { j++; } next[i] = j; } return next; } public static int violenceMatch (String str1, String str2) { char []s1 = str1.toCharArray(); char []s2 = str2.toCharArray(); int siLen = s1.length; int s2Len = s2.length; int i = 0 ; int j = 0 ; while (i < siLen && j < s2Len){ if (s1[i] == s2[j]) { i++; j++; } else { i = i - (j - 1 ); j = 0 ; } } if (j == s2Len) { return i - j; } return -1 ; } }
普里姆算法 普利姆(Prim)算法求最小生成树, 也就是在包含n个顶点的连通图中, 找出只有(n-1)条边包含所有n个顶点的连通子图, 也就是所谓的极小连通子图。
设G=(V,E)是连通网, T=(U,D)是最小生成树, V,U是顶点集合, E,D是边的集合
若从顶点u开始构造最小生成树, 则从集合V中取出顶点u放入集合U中, 标记顶点v的visited[u]=1
若集合U中顶点ui与集合V-U中的顶点vj之间存在边, 则寻找这些边中权值最小的边, 但不能构成回路, 将顶点vj加入集合U中, 将边(ui,vj)加入集合D中, 标记visited[vj]=1
重复步骤2, 直到U与V相等, 即所有顶点都被标记为访问过, 此时D中有n-1条边
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 public class Prim { public static void main (String[] args) { char [] data = new char [] {'A' ,'B' ,'C' ,'D' ,'E' ,'F' ,'G' }; int verxs = data.length; int [][] weight = new int [][] { {0 ,5 ,7 ,0 ,0 ,0 ,2 }, {5 ,0 ,0 ,9 ,0 ,0 ,3 }, {7 ,0 ,0 ,0 ,8 ,0 ,0 }, {0 ,9 ,0 ,0 ,0 ,4 ,0 }, {0 ,0 ,8 ,0 ,0 ,5 ,4 }, {0 ,0 ,0 ,4 ,5 ,0 ,6 }, {2 ,3 ,0 ,0 ,4 ,6 ,0 } }; MGraph graph = new MGraph (verxs); MinTree minTree = new MinTree (); minTree.createGraph(graph, verxs, data, weight); minTree.showGraph(graph); prim(graph, 0 ); } public static void prim (MGraph graph, int v) { int [] visited = new int [graph.verxs]; visited[v] = 1 ; int h1 = -1 ; int h2 = -1 ; int minWeight = 10000 ; for (int k= 1 ; k < graph.verxs; k++) { for (int i = 0 ; i < graph.verxs; i++) { for (int j = 0 ; j < graph.verxs; j++) { if (visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] != 0 && graph.weight[i][j] < minWeight) { minWeight = graph.weight[i][j]; h1 = i; h2 = j; } } } System.out.println("边<" + graph.data[h1] + "," + graph.data[h2] + ">权值:" + minWeight); visited[h2] = 1 ; minWeight = 10000 ; } } } class MinTree { public void createGraph (MGraph graph, int verxs, char data[], int [][] weight) { int i, j; for (i = 0 ; i < verxs; i++) { graph.data[i] = data[i]; for (j = 0 ; j < verxs; j++) { graph.weight[i][j] = weight[i][j]; } } } public void showGraph (MGraph graph) { for (int [] link : graph.weight) { System.out.println(Arrays.toString(link)); } } } class MGraph { int verxs; char [] data; int [][] weight; public MGraph (int verxs) { this .verxs = verxs; data = new char [verxs]; weight = new int [verxs][verxs]; } }
二分查找算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class BinarySearch { public static void main (String[] args) { int [] arr = {1 ,3 ,8 ,10 ,11 ,67 ,100 }; int index = bs(arr, 1 ); System.out.println(index); } public static int bs (int [] arr, int target) { int left = 0 ; int right = arr.length -1 ; while (left <= right) { int mid = (left + right) / 2 ; if (arr[mid] == target) { return mid; } else if (arr[mid] > target) { right = mid -1 ; } else { left = mid + 1 ; } } return -1 ; } }
动态规划算法 动态规划(Dynamic Programming)算法的核心思想是, 将大问题划分为小问题进行解决, 从而一步步获取最优解的处理算法。
动态规划算法与分治算法类似, 其基本思想也是将待求解问题分解成若干个子问题, 先求解子问题, 然后从这些子问题的解得到原问题的解。
与分治法不同的是, 动态规划适合于用动态规划求解的问题, 经分解得到子问题往往不是互相独立的。 即下一个子阶段的求解是建立在上一个子阶段的解的基础上, 进行进一步的求解。
动态规划可以通过填表的方式来逐步推进, 得到最优解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public class Dynamic { public static void main (String[] args) { knapsackProblem(); } public static void knapsackProblem () { int [] w = {1 ,4 ,3 }; int [] val = {1500 ,3000 ,2000 }; int m = 4 ; int n = val.length; int [][] v = new int [n+1 ][m+1 ]; int [][] p = new int [n+1 ][m+1 ]; for (int i = 1 ; i < v.length; i++) { for (int j = 0 ; j < v[0 ].length; j++) { if (w[i-1 ] > j) { v[i][j] = v[i-1 ][j]; } else { if (v[i-1 ][j] < val[i-1 ] + v[i-1 ][j-w[i -1 ]]) { v[i][j] = val[i-1 ] + v[i-1 ][j-w[i -1 ]]; p[i][j] = 1 ; } else { v[i][j] = v[i-1 ][j]; } } } } for (int i = 0 ; i < v.length; i++) { System.out.println(Arrays.toString(v[i])); } int i = p.length -1 ; int j= p[0 ].length-1 ; while (i > 0 && j > 0 ) { if (p[i][j] == 1 ) { System.out.printf("第%d个商品放入到背包\n" , i); j-= w[i-1 ]; } i--; } } }
弗洛伊德算法 弗洛伊德(Floyd)算法也是一种用于寻找给定的加权图中顶点间最短路径的算法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 public class Floyd { private static final int N = 65535 ; public static void main (String[] args) { char [] vertexs ={'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' }; int [][] matrix = { { 0 , 5 , 7 , N, N, N, 2 }, { 5 , 0 , N, 9 , N, N, 3 }, { 7 , N, 0 , N, 8 , N, N}, { N, 9 , N, 0 , N, 4 , N}, { N, N, 8 , N, 0 , 5 , 4 }, { N, N, N, 4 , 5 , 0 , 6 }, { 2 , 3 , N, N, 4 , 6 , 0 }, }; FGraph graph = new FGraph (vertexs, matrix); floyd(graph); graph.print(); } public static void floyd (FGraph graph) { int [][] dis = graph.dis; int [][] pre = graph.pre; int len = 0 ; for (int k = 0 ; k < dis.length; k++) { for (int i = 0 ; i < dis.length; i++) { for (int j = 0 ; j < dis.length; j++) { len = dis[i][k] + dis[k][j]; if (len < dis[i][j]) { dis[i][j] = len; pre[i][j] = pre[k][j]; } } } } } } class FGraph { char [] vertexs; int [][] matrix; int [][] dis; int [][] pre; public FGraph (char [] vertexs, int [][] matrix) { this .vertexs = vertexs; this .matrix = matrix; this .dis = matrix; this .pre = new int [vertexs.length][vertexs.length]; for (int i = 0 ; i < vertexs.length; i++) { Arrays.fill(pre[i], i); } } public void print () { System.out.println("图为:" ); for (int i = 0 ; i < vertexs.length; i++) { for (int j = 0 ; j < vertexs.length; j++) { System.out.printf("%12d" , matrix[i][j]); } System.out.println(); } System.out.println("到达目标顶点的前驱顶点为:" ); for (int i = 0 ; i < vertexs.length; i++) { for (int j = 0 ; j < vertexs.length; j++) { System.out.printf("%c" , vertexs[pre[i][j]]); } System.out.println(); } System.out.println("从各个顶点出发到其它顶点的距离为:" ); for (int i = 0 ; i < vertexs.length; i++) { for (int j = 0 ; j < vertexs.length; j++) { System.out.printf("(" + vertexs[i] + "到" + vertexs[j] + "的最短路径是:%10d)" , dis[i][j]); } System.out.println(); } } }
克鲁斯卡尔算法 克鲁斯卡尔(Kruskal)算法, 是用来求加权连通图的最小生成树的算法。
基本思想: 按照权值从小到大的顺序选择n-1条边, 并保证这n-1条边不构成回路。
具体做法: 首先构造一个只含n个顶点的森林, 然后依权值从小到大从连通网中选择边加入到森林中, 并使森林中不产生回路, 直至森林变成一棵树为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 public class Kruskal { private static final int INF = Integer.MAX_VALUE; public static void main (String[] args) { char [] vertexs ={'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' }; int [][] matrix = { { 0 , 12 , INF, INF, INF, 16 , 14 }, { 12 , 0 , 10 , INF, INF, 7 , INF}, {INF, 10 , 0 , 3 , 5 , 6 , INF}, {INF, INF, 3 , 0 , 4 , INF, INF}, {INF, INF, 5 , 4 , 0 , 2 , 8 }, { 16 , 7 , 6 , INF, 2 , 0 , 9 }, { 14 , INF, INF, INF, 8 , 9 , 0 } }; Graph graph = new Graph (vertexs, matrix); graph.print(); EData[] kruskal = kruskal(graph); for (EData eData : kruskal) { if (eData != null ) { System.out.println(eData.toString()); } } } public static EData[] kruskal(Graph graph) { EData[] edges = graph.getEdges(); graph.sortEdges(edges); int [] ends = new int [graph.edgeNum]; EData[] rets = new EData [graph.edgeNum]; int index = 0 ; for (int i = 0 ; i < graph.edgeNum; i++) { int p1 = graph.getPosition(edges[i].start); int p2 = graph.getPosition(edges[i].end); int m = graph.getEnd(ends,p1); int n = graph.getEnd(ends, p2); if (m != n) { ends[m] = n; rets[index++] = edges[i]; } } return rets; } } class Graph { int edgeNum; char [] vertexs; int [][] matrix; private static final int INF= Integer.MAX_VALUE; public Graph (char [] vertexs, int [][] matrix) { int vlen = vertexs.length; this .vertexs = new char [vlen]; for (int i = 0 ; i < vertexs.length; i++) { this .vertexs[i] = vertexs[i]; } this .matrix = new int [vlen][vlen]; for (int i = 0 ; i < vlen; i++) { for (int j = 0 ; j < vlen; j++) { this .matrix[i][j] = matrix[i][j]; } } for (int i = 0 ; i < vlen; i++) { for (int j = i + 1 ; j < vlen; j++) { if (this .matrix[i][j] != INF){ edgeNum++; } } } } public void print () { System.out.println("图为:" ); for (int i = 0 ; i < vertexs.length; i++) { for (int j = 0 ; j < vertexs.length; j++) { System.out.printf("%12d" , matrix[i][j]); } System.out.println(); } } public int getPosition (char ch) { for (int i= 0 ; i < vertexs.length; i++) { if (vertexs[i] == ch) { return i; } } return -1 ; } public EData[] getEdges() { int index = 0 ; EData[] edges = new EData [edgeNum]; for (int i = 0 ; i < vertexs.length; i++) { for (int j = i + 1 ; j < vertexs.length; j++) { if (matrix[i][j]!= INF) { edges[index++] = new EData (vertexs[i], vertexs[j], matrix[i][j]); } } } return edges; } public void sortEdges (EData[] edges) { for (int i = 0 ; i < edges.length - 1 ; i++) { for (int j = 0 ; j < edges.length- 1 -i; j++) { if (edges[j].weight > edges[j+1 ].weight) { EData tmp = edges[j]; edges[j] = edges[j+1 ]; edges[j+1 ] = tmp; } } } } public int getEnd (int [] ends, int i) { while (ends[i] != 0 ) { i = ends[i]; } return i; } } class EData { char start; char end; int weight; public EData (char start, char end, int weight) { this .start = start; this .end = end; this .weight = weight; } @Override public String toString () { return "EData{" + "start=" + start + ", end=" + end + ", weight=" + weight + '}' ; } }
迪杰斯特拉算法 迪杰斯特拉(Dijkstra)算法是典型最短路径算法, 用于计算一个结点到其他结点的最短路径。
它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想), 直到扩展到终点为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 public class Dijkstra { private static final int N = Integer.MAX_VALUE; public static void main (String[] args) { char [] vertexs ={'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' }; int [][] matrix = { { N, 5 , 7 , N, N, N, 2 }, { 5 , N, N, 9 , N, N, 3 }, { 7 , N, N, N, 8 , N, N}, { N, 9 , N, N, N, 4 , N}, { N, N, 8 , N, N, 5 , 4 }, { N, N, N, 4 , 5 , N, 6 }, { 2 , 3 , N, N, 4 , 6 , N}, }; DGraph graph = new DGraph (vertexs, matrix); graph.print(); dijkstra(graph, 2 ); } public static void dijkstra (DGraph graph, int index) { VisitedVertex visitedVertex = new VisitedVertex (graph, index); visitedVertex.update(index); for (int j = 0 ; j < graph.vertexs.length; j++) { index = visitedVertex.updateArr(); visitedVertex.update(index); } visitedVertex.show(); } } class VisitedVertex { DGraph graph; public int [] already_arr; public int [] pre_visited; public int [] dis; public VisitedVertex (DGraph graph, int index) { this .graph = graph; this .already_arr = new int [graph.vertexs.length]; this .pre_visited = new int [graph.vertexs.length]; this .dis = new int [graph.vertexs.length]; Arrays.fill(dis, Integer.MAX_VALUE); this .already_arr[index] = 1 ; this .dis[index] = 0 ; } public boolean in (int index) { return already_arr[index] == 1 ; } public void updateDis (int index, int len) { dis[index] = len; } public void updatePre (int index, int pre) { pre_visited[pre] = index; } public int getDis (int index) { return dis[index]; } public void update (int index) { int len = 0 ; for (int j = 0 ; j < graph.matrix[index].length; j++) { if (graph.matrix[index][j] == Integer.MAX_VALUE) { continue ; } len = getDis(index) + graph.matrix[index][j]; if (!in(j) && len < getDis(j)) { updatePre(j, index); updateDis(j, len); } } } public int updateArr () { int min = Integer.MAX_VALUE, index = 0 ; for (int i = 0 ; i < already_arr.length; i++) { if (already_arr[i] == 0 && dis[i] < min) { min = dis[i]; index = i; } } already_arr[index] = 1 ; return index; } public void show () { int count = 0 ; for (int i : dis) { if (i != Integer.MAX_VALUE) { System.out.print(graph.vertexs[count]+"(" +i+")" ); } else { System.out.println("N" ); } count++; } } } class DGraph { char [] vertexs; int [][] matrix; public DGraph (char [] vertexs, int [][] matrix) { this .vertexs = vertexs; this .matrix = matrix; } public void print () { System.out.println("图为:" ); for (int i = 0 ; i < vertexs.length; i++) { for (int j = 0 ; j < vertexs.length; j++) { System.out.printf("%12d" , matrix[i][j]); } System.out.println(); } } }
学习资源