写在前面:
昨晚本来是帮师姐做笔试题的,结果gg了,然后网上搜了一下,相关的资料和实现也比较少,所以决定总结一下,共同学习。欢迎各位交流想法,提意见。
题目描述:
偷懒,直接放的图片_
要求:给定一数组,输出其构造的Kolakoski序列的前n项。
背景知识介绍:
百度百科介绍
Kolakoski序列是一个仅由1和2组成的无限数列,是一种通过“自描述”来定义的数列。他在整数数列大全网站上排名第二位,足见该数列在组合数学界中的重要性。他的前几项为:1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1,2,2,1,2,1,1,2,1,2,2,1,1,…
它的定义很简单,若把数列中相同的数定为一组,令a(1)=1,a(2)=2,则a(n)等于第n组数的长度。可以根据这个定义来推算第三项以后的数:例如由于a(2)=2,因此第2组数的长度是2,因此a(3)=2,;由于a(3)=2,所以第三组数的长度是2,因此a(4)=a(5)=1;由于a(4)=1,a(5)=1,所以第四组数和第五组数的长度都为1,因此a(6)=2,a(7)=1,以此类推。
所以我们很容易发现:Kolakoski序列是一个分形数列:即将数列中相邻的数字以其个数合并,得到的仍将是数列本身。
维基百科介绍
翻译自wikipedia:Kolakoski sequence原文介绍
以数组[1,2]来构造的Kolakoski序列:
1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1,2,2,1,2,1,1,2,1,2,2,1,1,… (sequence A000002 in the OEIS) 是最经典的Kolakoski序列。它的“自生成”过程可以通过下图进行描述!!!
当然,除此之外,还有很多这种序列,比如[1,2,3,4],它生成的Kolakoski序列为:1, 2, 2, 3, 3, 4, 4, 4, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 1, 2, 3, 4, 4, 1, 1, 2, 2, 3, 3, 4, 4, 4, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 1, 2, 3, 3, 4, 4, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 1, 1, 1, 1, 2, 3, 4, 1, 1,… (sequence A079730 in the OEIS)
序列分析:
我们可以看到,这个序列是由给定数组的数交替组成,关键在于求每个数的个数!通过观察上面的例子,发现分组之后每组的数字个数放在一起仍然是原数列,也就是说当前分组的个数是序列前边的某个数。换句话说,每个数字本身就是长度。那么就需要有两个索引变量,一个是Kolakoski序列索引count,一个是给定数组的索引。
当前Kolakoski序列的第i个相同数字构成的子串的长度将成为变换后的Kolakoski序列中的第i个数字(也就是多对一),换而言之,变换后的Kolakoski序列的数对应变换前的Kolakoski序列的子串长度。
算法分析:
我认为该序列最本质的特性,每个数字不仅是数字,还是相同数字的长度。
以[1,2,3,4]构造Kolakoski序列为例:它的前几项为1, 2, 2, 3, 3, 4, 4, 4, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,…
设n1 = 1,n2 = 2,n3 = 3,n4 = 4
- 因为还没有数据输出,所以置第一个输出为x1 = n1 = 1,即输出数字1的“1个拷贝”
- 同样第二个数字也没有输出,所以置第二个输出为x2 = n2 = 2,即输出数字2的“2个拷贝”
- 因为上一步输出了“两个2”,所以置第三个输出为x3 = 2,即输出数字3的“2个拷贝”
- 置第四个输出为x4 = 3,即输出数字4的“3个拷贝”
- 置第五个输出为x5 = 3,此时因为原始数组的数字已经过完一遍,所以再从头开始,即输出数字1的“3个拷贝”
- 置第六个输出为x6 = 4,即输出数字2的“4个拷贝”
- 以此类推。。。
代码实现:
public static ArrayList<Integer> solution(int[] array, int n) {
int m = array.length;
ArrayList<Integer> res = new ArrayList<>();
int k = 0; // 用于标志 array 数组索引
int count = 0; // 用于计数,控制循环次数
while (count < n) {
if (k == m) // 当数组里的数字都已经出现过一遍时,需要从头再开始
k = 0;
res.add(array[k]); // 每次先将第一个不同的数添加
// System.out.println("" + res.get(count));
// 关键就是控制下面循环添加的次数,因为该序列的数字,本身就是相邻相同数字的个数
for (int i = 0; i < res.get(count) - 1; i++) {
res.add(array[k]);
}
k++;
count++;
}
// for (k = 0; k < n; k++)
// System.out.println("" + res.get(k));
// 如果要求 Kolakoski 序列的第 x 项,构造完后 res.get(x); 即可
return res;
}
——乐于分享,共同进步,欢迎补充
——Any comments greatly appreciated
——诚心欢迎各位交流讨论!QQ:1138517609
——CSDN:https://blog.csdn.net/u011489043
——简书:https://www.jianshu.com/u/4968682d58d1
——GitHub:https://github.com/selfconzrr