JavaScript 打印星號三角形

打印由星號組成的x層三角形,以下圖: 程序員

看規律, 設三角層數爲x,每層星星數爲y,每層開頭空格數爲zbash

那麼,對於第i層:函數

  1. 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;
  2. z 在最後一層爲0,朝上依次增長1,共有x層,故最上層爲x-1,第二層x-2, 第三層x-3, 所以z = x-i;

雖然不少時候這兩個簡單的變量之間的數量關係能夠一眼看出, 但一般都是碰運氣的不斷嘗試拼湊, 本着九年義務教育提供的數學素養, 咱們應該能夠從頭推出這些式子, 而且有理有據, 而不是背下來.優化

我的最討厭背了,除了3.1415926, 九九乘法表之類的, 爸媽的手機號... 其餘時候是能不背就不背.ui

咱們若是能掌握如何產生公式, 是否是就比直接背公式更容易記憶呢?短時間看背的收益是高回報的, 無需思考, 提取就能夠. 而理解就像計算機中的函數, 他們多了以後能夠互相利用, 組合成更復雜的功能. 而背除了詩詞能假以時日身臨其境,有所感慨能夠陶冶情操外, 不少時候他們真的除了提取數據, 毫無發展和其餘有趣的事情聯繫起來從而有更大的用處.spa

固然理解是要付出代價的, 即理解的時間, 和從原數據推演計算的時間, 所以重要的或者有趣的纔要去理解, 若是是一次性使用的信息, 好比學校旁的小麪館說13號擔擔麪好了~13號在哪~, 這樣的信息仍是直接背下來比較好, 固然你也能夠想象背叛上帝的猶大, 藉此把13記下來code

而手機驗證碼的六位, 原先還要不斷重複口唸450893, 生怕有人跟我說話我又要切換應用從新記, 哈利路亞, 這種純粹沒有邏輯的死板的信息, 如今的輸入法自帶的信息提示, 不用切換到短信, 直接點一下就Ok了.cdn

看, 越是死的東西, 越是會被機器+軟件替換掉.對象

簡易代碼實現

const printTriangle = x => {
    for(let i=1; i<=x; i++) {
        console.log(" ".repeat(x-i)+ "*".repeat(2*i-1))
    }
}
printTriangle(7)
複製代碼

效果:blog

VM189:3       *
VM189:3      ***
VM189:3     *****
VM189:3    *******
VM189:3   *********
VM189:3  ***********
VM189:3 *************
複製代碼

遐想

代碼邏輯很簡單,這裏有一點引發個人遐想。

*.repeat()爲例,若是

  1. 須要1個*,是咱們程序員手動輸入的,沒法精簡
  2. 須要2個*,可能咱們更願意手動輸入,也無需精簡
  3. 須要3個*,可能咱們會想相關重複字符的API是否是比三個星星簡單,因而想到str.repeat(), 但很明顯,仍是手動輸入***更簡單
  4. 須要4,5,6,7個*呢?很明顯,這時候咱們理性一點,在這種事上不可以選擇相信咱們的眼睛真的每次都能數對數目,儘管小學算術常常拿滿分 :)
  5. 而若是咱們不是一次只須要幾個*,而是同時須要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)

第二步: 優化

上面的實現間接生成了任意長度的*字符串

  1. *, 手動輸入
  2. **, 由* + *
  3. ***, 由** + *
  4. ****, 由*** + *
  5. *****, 由**** + *
  6. ******, 由***** + *
  7. ...

而對於本題目須要奇數個長度的*, 這裏生成的一半都是不須要的, 形成了浪費

那麼怎麼才能不浪費呢.

*, **, ****, ********, 每次以以前的字符爲基本, 自我複製,達到2倍法來逼近指定長度, 達到log2(n)的複雜度(想想二分法...)

可是對於本題而言, *, **, ****, ********, ****************是相對不夠的, 因此

  1. 對於每行的*, 從第一層的*開始, 不斷的在上一層字符串 + **,
  2. 而對於每行的空格" ", 一開始就有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. 對比結果時, 我發現第一行和其餘行的格式縮進不一致, 找了半天代碼, 我默認其餘是對的, 那麼一對比結果可能正確的那個就表現的錯誤, 而我怎麼也找不到這錯在哪, 這個時候要聯繫上下文, 我發現其實這只是由於兩個邏輯, 第一個邏輯產生了1個實例即第一行, 第二個邏輯產生了更多的實例即其餘行, 當錯誤的那個邏輯應用在不少行, 致使其餘行一致性的表現規律, 以致於你認爲其餘行都格式正確了, 第一行是錯誤的, 其實這不是多對一, 本質上這是一對一, 若是不是這個邏輯的這一行錯了, 就是那個邏輯的不少行錯了!
  2. 爲何會這麼用除2判斷餘數來標識32,16,8421碼的位是否存在的方式來組合總空格的長度以及用空格double的方式實現複雜度O(log2(n)), 首先考慮的是先生成第一層最長的空格, 而後依次遞減. 其實這是個str.repeat()從O(n)到O(log2(n))的方法,被強行套在這裏.
  3. 關於本題實際上更適合生成空字符串逐個加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是世界上最好的語言~

相關文章
相關標籤/搜索