题
打印由星号组成的x层三角形,如下图:

解
看规律, 设三角层数为x,每层星星数为y,每层开头空格数为z
那么,对于第i层:
- y 为1,3,5,7,9...奇数。光知道奇数你并不知道印象中的公式是2n-1还是2n+1更哪个更适合这里, 那我们一步步来, 这里隔离变与不变, 1是最开始的基准, 3和1的关系是+2, 5和3的关系是+2... 因此每次加2,即1, 1+2, 1+2+2, 1+2+2+2...注意这里隔离变与不变, 数量不变的1, 和数量变化的2, 那么就看这个变化的2的数量和i的关系, 即0, 1, 2, 3...和1, 2, 3...的关系,这样才由题意得y = 1+(i-1)*2 = 2i-1;
- z 在最后一层为0,朝上依次增加1,共有x层,故最上层为x-1,第二层x-2, 第三层x-3, 因此z = x-i;
虽然很多时候这两个简单的变量之间的数量关系可以一眼看出, 但通常都是碰运气的不断尝试拼凑, 本着九年义务教育提供的数学素养, 我们应该可以从头推出这些式子, 并且有理有据, 而不是背下来.
个人最讨厌背了,除了3.1415926, 九九乘法表之类的, 爸妈的手机号... 其他时候是能不背就不背.
我们如果能掌握如何产生公式, 是不是就比直接背公式更容易记忆呢?短期看背的收益是高回报的, 无需思考, 提取就可以. 而理解就像计算机中的函数, 他们多了之后可以互相利用, 组合成更复杂的功能. 而背除了诗词能假以时日身临其境,有所感慨可以陶冶情操外, 很多时候他们真的除了提取数据, 毫无发展和其他有趣的事情联系起来从而有更大的用处.
当然理解是要付出代价的, 即理解的时间, 和从原数据推演计算的时间, 因此重要的或者有趣的才要去理解, 如果是一次性使用的信息, 比如学校旁的小面馆说13号担担面好了~13号在哪~, 这样的信息还是直接背下来比较好, 当然你也可以想象背叛上帝的犹大, 借此把13记下来
而手机验证码的六位, 原先还要不断重复口念450893, 生怕有人跟我说话我又要切换应用重新记, 哈利路亚, 这种纯粹没有逻辑的死板的信息, 现在的输入法自带的信息提示, 不用切换到短信, 直接点一下就Ok了.
看, 越是死的东西, 越是会被机器+软件替换掉.
简易代码实现
const printTriangle = x => {
for(let i=1; i<=x; i++) {
console.log(" ".repeat(x-i)+ "*".repeat(2*i-1))
}
}
printTriangle(7)
效果:
VM189:3 *
VM189:3 ***
VM189:3 *****
VM189:3 *******
VM189:3 *********
VM189:3 ***********
VM189:3 *************
遐想
代码逻辑很简单,这里有一点引起我的遐想。
以*.repeat()为例,如果
- 需要1个
*,是我们程序员手动输入的,无法精简 - 需要2个
*,可能我们更愿意手动输入,也无需精简 - 需要3个
*,可能我们会想相关重复字符的API是不是比三个星星简单,于是想到str.repeat(), 但很明显,还是手动输入***更简单 - 需要4,5,6,7个
*呢?很明显,这时候我们理性一点,在这种事上不能够选择相信我们的眼睛真的每次都能数对数目,尽管小学算术经常拿满分 :) - 而如果我们不是一次只需要几个
*,而是同时需要1个,2个,3个,4个*呢?这时候我们会选择自动生成。即有str.repeat()那就用它,没有的话呢?
手动实现str.repeat()
第一步,先保证基本能用最简单的
const repeat = (str,n) => {
let result = "";
for(let i=0; i<n; i++) {
result = result + str
}
return result
}
复杂度:O(n)
第二步: 优化
上面的实现间接生成了任意长度的*字符串
*, 手动输入**, 由*+****, 由**+*****, 由***+******, 由****+*******, 由*****+*- ...
而对于本题目需要奇数个长度的*, 这里生成的一半都是不需要的, 造成了浪费
那么怎么才能不浪费呢.
*, **, ****, ********, 每次以之前的字符为基本, 自我复制,达到2倍法来逼近指定长度, 达到log2(n)的复杂度(想一想二分法...)
但是对于本题而言, *, **, ****, ********, ****************是相对不够的, 所以
- 对于每行的
*, 从第一层的*开始, 不断的在上一层字符串 +**, - 而对于每行的空格
" ", 一开始就有x-1个, 所以要最大速度的增加到x-1个字符串, 用2倍法, O(log2(n))复杂度, 2^40 = 10995,1162,7776, 即长度为一万亿以下的字符串的重复, 不超过40次计算即可拼接成.
const printTriangle = x => {
let blanks_max_length = x-1
let blanks_max = "" // empty str
let blank = " " // blank str
for(;blanks_max_length!=0;){ // only stop while length became 0,
// there is no (let i i...i++), because it's all about length!
if(blanks_max_length%2===1) { // remember decimal 5 trans into binary 101?
// 5/2 = 2...1, 2/2 = 1...0, 1/2 = 0...1,
// the first remainder 1 indicates 1 while the last 1 indicate 2^2 which is 4
blanks_max = blanks_max + blank // so here if there remains 1, blanks_max should add 1 blank
}
blanks_max_length = Math.floor(blanks_max_length/2) // here is the result of /2
blank = blank + blank // here is where log2(n) come from, cause we double the string rather than linear increase
}
let star = "*"
const addon = "**"
// first line
console.log(blanks_max + star)
// other lines
for(let i=2; i<x; i++) {
star = star + addon
console.log(blanks_max.slice(0, x-i) + star)
}
// last line
console.log(star+addon)
}
- 对比结果时, 我发现第一行和其他行的格式缩进不一致, 找了半天代码, 我默认其他是对的, 那么一对比结果可能正确的那个就表现的错误, 而我怎么也找不到这错在哪, 这个时候要联系上下文, 我发现其实这只是因为两个逻辑, 第一个逻辑产生了1个实例即第一行, 第二个逻辑产生了更多的实例即其他行, 当错误的那个逻辑应用在很多行, 导致其他行一致性的表现规律, 以至于你认为其他行都格式正确了, 第一行是错误的, 其实这不是多对一, 本质上这是一对一, 如果不是这个逻辑的这一行错了, 就是那个逻辑的很多行错了!
- 为什么会这么用除2判断余数来标识32,16,8421码的位是否存在的方式来组合总空格的长度以及用空格double的方式实现复杂度O(log2(n)), 首先考虑的是先生成第一层最长的空格, 然后依次递减. 其实这是个str.repeat()从O(n)到O(log2(n))的方法,被强行套在这里.
- 关于本题实际上更适合生成空字符串逐个加1长度, 存入栈, 然后打印的时候出栈, 满足倒序打印的特点
代码如下
const printTriangle = x => {
let blanks_max_length = x-1
let blanks_arr = []
let blank_char = " "
let tmp =""
for (let i=0; i<blanks_max_length; i++) {
tmp = tmp + blank_char
blanks_arr.push(tmp)
}
let star = "*"
const addon = "**"
// first line
console.log(blanks_arr.pop() + star)
// other lines
for(let i=2; i<x; i++) {
star = star + addon
console.log(blanks_arr.pop() + star)
}
// last line
console.log(star+addon)
}
噢, 丑陋的代码, 就是为了不产生浪费的字符串.
好了, 上面给出了一些个人答案,这些答案分别面向节省程序员时间(str.repeat)、面向自己实现str.repeat(),面向不浪费生成的对象等角度。
最后还是好爱第一个答案,不禁让我想到一句话
JavaScript是世界上最好的语言~