演算法系列 演算法入門之遞迴分而治之思想的實現

2022-08-05 17:36:14 字數 3307 閱讀 1221

別想太多,肯定要!!!

你以為的演算法是各種排序,選擇排序、快速排序、歸併排序,廣深搜尋、動態規劃......

然而,演算法實際上指的是解決某個實際問題的方法。

解決同一個問題的方法有很多,比如迴圈輸出某個陣列,可以有for、for in、for of、map、foreach等,不同的實現方法會反映不同的效能,這些效能通常用執行時間來表示,執行時間越短,效能越好,目前我可以告訴你的是,上面的幾個迴圈中,原生for迴圈的效能是最好的。

下面講的都是非常非常非常非常簡單的演算法知識!!你千萬不要害怕!!

陣列陣列是演算法中最常用到的資料結構,給你一串陣列,你能很快的根據索引找到那個元素。

你或許知道時間複雜度o(n),我們叫他大o表示法,這是大寫字母o,不是數字0,別搞錯了。通常大o表示的是演算法的最差情況。

陣列o(時間複雜度)

讀取o(1)

寫入o(n)

刪除o(n)

陣列的大o很好理解,讀取的時候,最壞情況就是1次,因為陣列是記憶體上連續的地址(計算機的知識),可以直接根據地址(索引)找到那個元素。

寫入的時候,如果是在陣列的末尾push新的元素,那麼前面已有的元素地址不需要改變,但是如果是在陣列的頭部push新的元素,那麼所有已有的元素的地址都要加1,即需要移動n個元素,所以大o是n。

刪除操作時,和插入一樣,最好的情況是刪除末尾的元素,複雜度就是1,最壞的情況是刪除第一個元素,所有剩下的元素都需要地址減1,即需要移動n次。

或許你會發現上面有點不對勁,在刪除的時候,不是移動 n-1 個元素嗎?其實這就是要知道的大o表示法只是描述次數和資料量的線性關係,我們關注的是線性變化的規律,不在乎那一點點影響。

連結串列連結串列比較複雜,我們這裡只關心連結串列的一些特點。

連結串列和陣列一樣,通常也存在記憶體中,連結串列可以存在記憶體的任何地方,它不一定是連續的。這句話你可能不太理解。舉個例子,假設你有的記憶體條有8g,這8g可能被分配給多個應用程式,你建立了一個陣列,長度是10,那麼,系統會分配10個連續的記憶體地址給你使用。而連結串列呢,假設你有10個資料,可以通過連結串列插入到記憶體的空餘地址位置,中間可能被其他資料隔開。類似於插班生來到了你們班,插入了任意一個空位裡面。

連結串列還有一個重要的特性就是他的讀取必須是從頭開始遍歷,因為只有當前的元素位置才有下一個元素的指標!!你不能直接讀取第n個元素!

連結串列o(時間複雜度)

讀取o(n)

寫入o(1)

刪除o(1)

你會發現連結串列的讀取大o是n,也就是說最壞的情況下,如果那個需要讀取的元素剛好在連結串列的最末尾,那麼,你就需要遍歷整個連結串列。

寫入和刪除都是o(1),這和連結串列的特點有關,你可以在任意一個指標寫入新的元素和刪除連結串列的元素,而只需要將前一個元素的指標指向新的元素或者下一個元素即可。連結串列沒有地址的概念,所以不需要移動地址。

形象表達記憶體中陣列和連結串列的特點

上面的文字你覺得抽象的話,可以看下面的**,假設這一段記憶體條,上面一共有8個記憶體地址,現在都是空餘的,當你建立一個長度為2的陣列時 new array(2),系統會分配2個記憶體地址給陣列,可能是地址0,1。然後繼續建立一個長度為1的陣列 new array(1),系統會分配1個記憶體地址給陣列,假設是地址4,現在整個記憶體被2個陣列給分割開來了,單個陣列的記憶體一定是連續的,不同的陣列之間不需要連續。

這時候,你再建立一個連結串列,有3個元素,現在地址2、3、5、6、7都是空閒的,假設連結串列的第一個元素是2,那麼下一個元素可以指向任意一個空閒的地址,比如3,到地址3的時候,地址4已經有陣列的元素在佔用了,不用擔心,連結串列可以將指標指向地址5,這樣連結串列的第三個元素就儲存在地址5上面了。

這樣你是不是更加清晰的理解了陣列和連結串列的基本特點了。01

2345

67組合型資料結構

陣列和連結串列也可以組合起來成為一種複合型的資料結構,稱為“鏈組結構”,不是戀父、不是戀母,而是鏈組!

作為前端,實際上只需要考慮和陣列相關的基本演算法就行了,還有就是各種效能提升的訣竅。

我向演算法工程師請教如何學好演算法,他跟我提議說先看懂漢諾塔,這是一個小朋友都會玩的遊戲,裡面用到了遞迴的思想。但是我在這裡不說漢諾塔,而是從遞迴的簡單實現入手。

以前我也寫過遞迴的文章,es6中也有尾遞迴優化的介紹。但遞迴的思想不只是應用在階乘演算法中,還有各種場景需要遞迴,特別是在函數語言程式設計中,遞迴的地位顯得越發的重要。

遞迴實現倒計時函式

下面這個倒計時函式使用了遞迴,而且使用了尾遞迴優化。你或許不瞭解尾遞迴優化,我想你可以去看一下 尾遞迴優化特點

function countdown(i) , 1000)

}countdown(10)

遞迴實現階乘

階乘是什麼?n!表示 1x2x3x...xn

function t(i, s=1) 

const s = t(5)

console.log(s)

遞迴之分而治之思想實現陣列元素求和

需求是這樣的,假設你有一個數字組成的陣列,現在你需要寫一個函式求所有元素的和,比如[2, 4, 6]。

這裡不單單是遞迴的思想,還有一種思想叫做分而治之,分而治之的思想分為2個步驟,一是找出基線條件。二是每次呼叫遞迴都離基線條件更近一步。

那麼陣列[2, 4, 6]的基線條件是什麼呢?其實它就是一個臨界情況,比如當陣列元素為空時,或者陣列只剩一個元素時[2]。這個基線有什麼作用呢?當遞迴達到基線時,就返回結果,不再遞迴。

下面的**實際上是根據這樣一個步驟去執行的,[2, 4] + 6 => [2] + 4 + 6 => 2 + 4 + 6,通過陣列不斷的拆分和求和,直至陣列達到基線條件,這時候將相加的和返回。

未尾遞迴優化

function add_1(arr, len=arr.length, sum=arr[len-1]) 

const r = add_1([2, 4, 6])

console.log(r) // 12

尾遞迴優化

function add_2(arr, len=arr.length, sum=arr[len-1]) 

const p = add_2([2, 4, 6])

console.log(p) //12

學習演算法是一個漫長的過程,第一次學網頁設計的時候,div都學習了大半年才搞懂什麼玩意,後來css的學習時間更長,js的學習從開始到現在始終在進行著,正則的學習一開始也是很痛苦,最後,輪到了演算法,只有像以前學習前端知識那樣堅持下去,才能學好演算法!!

演算法系列之 2 排序演算法簡介

簡介 在演算法的世界裡,排序是最基本又是最重要的一類。在tacp the art of computing programming 中,佔據...

排序演算法系列之插入排序 1

排序 即是以一定的規則組織資料 排序演算法的優劣關鍵在於比較資料的交換和移動次數 排序演算法需要考慮的幾點 資料交換次數 移動次數 資料越界 演算法的實用性 符合多種型別 預知 使用c 拓展方法進行資料輸出 使用stringbuilder節約記憶體開銷 public static class debugex...

排序演算法系列之氣泡排序 3

把長度為 l 的陣列樹立起來 從最底部n l 1 開始 依次向上兩兩交換資料 每輪把最小的資料冒泡到頂部 使得頂部datas o datas i 為有序陣列 底部datas i datas n 為無序陣列 結束條件 排序好的資料長度為n 1 即 l 2 的時候 排序結束 缺點 資料需要兩兩交換冒泡到...