diff --git a/notes/数据库系统原理.md b/notes/数据库系统原理.md index 81061862..c8e5ef20 100644 --- a/notes/数据库系统原理.md +++ b/notes/数据库系统原理.md @@ -66,7 +66,7 @@ ## 4. 持久性 -一旦事务提交,则其所在的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。持久性通过数据库备份和恢复来保证。 +一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。持久性通过数据库备份和恢复来保证。 # 数据不一致 diff --git a/notes/算法.md b/notes/算法.md index f6fcf43e..ad4679ca 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -84,7 +84,7 @@ 指数函数可以转换为线性函数,从而在函数图像上显示的更直观。 -T(N)=aN3 转换为 lg(T(N))=3lgN+lga + 转换为

@@ -133,7 +133,7 @@ public class ThreeSum { } ``` -该程序的内循环为 if (a[i] + a[j] + a[k] == 0) 语句,总共执行的次数为 N3/6-N2/2+N/3,因此它的近似执行次数为 \~N3/6,增长数量级为 N3。 +该程序的内循环为 if (a[i] + a[j] + a[k] == 0) 语句,总共执行的次数为 N3/6 - N2/2 + N/3,因此它的近似执行次数为 \~N3/6,增长数量级为 N3。 **改进** @@ -149,12 +149,11 @@ public class ThreeSumFast { int cnt = 0; for (int i = 0; i < N; i++) { for (int j = i + 1; j < N; j++) { - for (int k = j + 1; k < N; k++) { - // rank() 方法返回元素在数组中的下标,如果元素不存在,这里会返回 -1。应该注意这里的下标必须大于 j,这样就不会重复统计了。 - if (BinarySearch.rank(-a[i] - a[j], a) > j) { - cnt++; - } - } + // rank() 方法返回元素在数组中的下标,如果元素不存在,这里会返回 -1。 + // 应该注意这里的下标必须大于 j,这样就不会重复统计了。 + if (BinarySearch.rank(-a[i] - a[j], a) > j) { + cnt++; + } } } return cnt; @@ -190,8 +189,7 @@ public class ThreeSumFast { **均摊分析** -将所有操作的总成本所以操作总数来将成本均摊。例如对一个空栈进行 N 次连续的 push() 调用需要访问数组的元素为 N+4+8+16+...+2N=5N-4(N 是向数组写入元素,其余的都是调整数组大小时进行复制需要的访问数组操作),均摊后每次操作访问数组的平均次数为常数。 - +将所有操作的总成本除于操作总数来将成本均摊。例如对一个空栈进行 N 次连续的 push() 调用需要访问数组的元素为 N+4+8+16+...+2N=5N-4(N 是向数组写入元素,其余的都是调整数组大小时进行复制需要的访问数组操作),均摊后每次操作访问数组的平均次数为常数。 # 排序 @@ -219,7 +217,7 @@ private void exch(Comparable[] a, int i, int j){ ### 1.2 选择排序 -找到数组中的最小元素,然后将它与数组的第一个元素交换位置。然后再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。 +找到数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。

@@ -242,7 +240,7 @@ public class Selection { ### 1.3 插入排序 -将一个元素插入到已排序的数组中,使得插入之后的数组也是有序的。插入排序从左到右插入每个元素,每次插入之后左部的子数组是有序的。 +入排序从左到右进行,每次都将当前元素插入到左部已经排序的数组中,使得插入之后左部数组依然有序。

@@ -283,7 +281,7 @@ public class Shell { int N = a.length; int h = 1; while (h < N / 3) { - h = 3 * h + 1;// 1, 4, 13, 40, ... + h = 3 * h + 1; // 1, 4, 13, 40, ... } while (h >= 1) { for (int i = h; i < N; i++) { @@ -297,7 +295,7 @@ public class Shell { } ``` -希尔排序的运行时间达不到平方级别,使用递增序列 1, 4, 13, 40, ... 的希尔排序所需要的比较次数不会超过 N 的若干倍乘于递增序列的长度。后面介绍的高级排序算法只会比希尔排序快两倍左右。 +希尔排序的运行时间达不到平方级别,使用递增序列 1, 4, 13, 40, ... 的希尔排序所需要的比较次数不会超过 N 的若干倍乘于递增序列的长度。后面介绍的高级排序算法只会比希尔排序快两倍左右。 ## 2 归并排序 @@ -307,6 +305,8 @@ public class Shell { ### 2.1 归并方法 +归并方法将数组中两个已经排序的部分归并成一个。 + ```java public class MergeSort { private static Comparable[] aux; @@ -349,7 +349,7 @@ private static void sort(Comparable[] a, int lo, int hi) {

-很容易看出该排序算法的时间复杂度为 O(NlgN)。 +因为每次都将问题对半分成两个子问题,而这种对半分的算法复杂度一般为 O(NlgN),因此该归并排序方法的时间复杂度也为 O(NlgN)。 因为小数组的递归操作会过于频繁,因此使用插入排序来处理小数组将会获得更高的性能。 @@ -600,9 +600,9 @@ Java 系统库中的主要排序方法为 java.util.Arrays.sort(),对于原始 ### 5.3 基于切分的快速选择算法 -快速排序的 partition() 方法,会将数组的 a[lo] 至 a[hi] 重新排序并返回一个整数 j 使得 a[lo..j-1] 小于等于 a[j],且 a[j+1..hi] 大于等于 a[j]。那么如果 j=k,a[j] 就是第 k 个数。 +快速排序的 partition() 方法,会返回一个整数 j 使得 a[lo..j-1] 小于等于 a[j],且 a[j+1..hi] 大于等于 a[j],此时第 a[j] 就是数组的第 j 大元素。 -该算法是线性级别的,因为每次正好将数组二分,那么比较的总次数为 (N+N/2+N/4+..),直到找到第 k 个元素,这个和显然小于 2N。 +可以利用这个特性找出数组的第 k 个元素。 ```java public static Comparable select(Comparable[] a, int k) { @@ -617,6 +617,8 @@ public static Comparable select(Comparable[] a, int k) { } ``` +该算法是线性级别的,因为每次正好将数组二分,那么比较的总次数为 (N+N/2+N/4+..),直到找到第 k 个元素,这个和显然小于 2N。 + # 查找 本章使用三种经典的数据结构来实现高效的符号表:二叉查找树、红黑树和散列表。