1.1 一维数组
数组是最简单的,也最基础的数据结构。数组是一个有序的数据集合,用索引(或者叫下标)访问数据。在大多数编程语言,比如C/C++/java/javascript中,数组的索引都是以0开始。它的逻辑结构是一段连续的空间(因为虚拟内存的原因,实际情况下大数组可能由多个不连续的内存页组成)。
如下图所示:
1 |
1 |
2 |
3 |
5 |
8 |
0 |
1 |
2 |
3 |
4 |
5 |
上图中,第一行表示数据第二行表示索引
一维数组,我会以一个案例来开始。
假设有这么一个需求,求11个点能构成多少种二叉树。比如3个点,可以构成五种二叉树:
按照序言里的步骤,我们第一步是仔细研究需求。从上图看,所有的树都是线性结构。那么不同在哪里?可见这个问题其实是数学里的组合计数问题。
解题思路
以3个点为例子,作为二叉树必须要有根节点。那么利用分类归纳法,可以这样计算出来。
场景1 只有左节点
场景2 左右节点都存在
场景3 只有右节点
以上场景实际上是什么问题?
是结点的左右分配问题!那么假设N个结点能组成二叉树的数目为C(n)
场景1
左节点数量2,右节点数量0,树的数量为左边树的数量C(2)*右边树的数量 C(0)
场景2
左节点数量1,右节点数量1,树的数量为左边树的数量C(1)*右边树的数量 C(1)
场景3
左节点数量0,右节点数量2,树的数量为左边树的数量C(0)*右边树的数量 C(2)
所以对于N个结点,就可以分成N种场景。左边的数目确定了,右边的数目就也确定了。所以可以写出递归公式,是乘积的求和:
那么这个问题,用什么数据结构呢?根据递归公式,可以清楚地知道我们需要存储C(0)到C(n-1)的所有数据,所以这个问题,一维数组这种数据结构最合适了。经过分析之后,代码就非常容易写了。
Python代码
def c(n): if n < 2: return 1 array = [0] * (n + 1) array[1] = array[0] = 1 for i in range(2, n + 1): for j in range(0, i): array[i] += array[j] * array[i - j - 1] return array[n]
需要注意的是python中没有严格意义上的数组。为了严谨,这里附上java代码:
public static int catalan(int n) { if (n < 2) { return 1; } int[] array = new int[n + 1]; array[1] = array[0] = 1; for (int i = 2; i < n + 1; i++) { for (int j = 0; j < i; j++) { array[i] += array[j] * array[i - j - 1]; } } return array[n]; }
这个数列就是著名的明安图数列,也叫卡特兰数列。不过在我们这个例子中,与卡特兰数列错了一位而已。
我们的例子中c(0)=1 c(1)=1 c(2)=2……
而卡特兰数列是c(1)=1 c(2)=1 c(3)=2……
递归公式,与卡特兰数列一模一样。
这个题目属于数学专业研究生的组合计数学课程。其实我是反对把知识分什么中学知识、大学知识、硕士阶段、博士阶段的。我们要做的仅仅是,按照编程的套路,先仔细分析需求,选择合适的数据结构,然后设计算法,编写代码,就可以了。