常用演算法

X.. R H發表於2020-12-02

分治演算法

把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合併。這個技巧是很多高效演算法的基礎,如排序演算法(快速排序,歸併排序),傅立葉變換(快速傅立葉變換)……

分治演算法可以求解的一些經典問題
二分搜尋
大整數乘法
棋盤覆蓋
合併排序
快速排序
線性時間選擇
最接近點對問題
迴圈賽日程表
漢諾塔

動態規劃演算法

揹包問題:有一個揹包,容量為4磅 , 現有如下物品
在這裡插入圖片描述
1.要求達到的目標為裝入的揹包的總價值最大,並且重量不超出
2.要求裝入的物品不能重複(01揹包問題)

完全揹包問題就是不限裝的次數

在這裡插入圖片描述
在這裡插入圖片描述

思想:

動態規劃演算法與分治演算法類似,其基本思想也是將待求解問題分解成若干個子問題,先求解子問題.

與分治法不同的是,適合於用動態規劃求解的問題,經分解得到子問題往往不是互相獨立的。 ( 即下一個子階段的求解是建立在上一個子階段的解的基礎上,進行進一步的求解 )

動態規劃可以通過填表的方式來逐步推進,得到最優解

字串匹配

暴力匹配

如果當前字元匹配成功(即str1[i] == str2[j]),則i++,j++,繼續匹配下一個字元
如果失配(即str1[i]! = str2[j]),令i = i - (j - 1),j = 0。相當於每次匹配失敗時,i 回溯,j 被置為0。
用暴力方法解決的話就會有大量的回溯,每次只移動一位,若是不匹配,移動到下一位接著判斷,浪費了大量的時間

KMP

暴力匹配是每次遇到匹配不成功,j置0,i向後移1位,而kmp是向後移動n位,這個n位是通過搜尋表來確定的
部分匹配值
ABCDABD
在這裡插入圖片描述
kmp:
1.先得到子串的部分匹配表
2.使用部分匹配表KMP匹配

字首表示最大公共元素長度,最後一個去掉前面加一個-1,這裡字首表就是
-1,0,0,0,0,1,2

貪心

貪婪演算法(貪心演算法)是指在對問題進行求解時,在每一步選擇中都採取最好或者最優(即最有利)的選擇,從而希望能夠導致結果是最好或者最優的演算法。
貪婪演算法所得到的結果不一定是最優的結果(有時候會是最優解),但是都是相對近似(接近)最優解的結果

組合的時候,窮舉法是很低的。
假設總的有n個廣播臺,則廣播臺的組合總共有2ⁿ -1 個,假設每秒可以計算10個子集, 如圖
在這裡插入圖片描述

貪心演算法最佳應用-集合覆蓋

假設存在如下表的需要付費的廣播臺,以及廣播臺訊號可以覆蓋的地區。 如何選擇最少的廣播臺,讓所有的地區都可以接收到訊號
在這裡插入圖片描述
在這裡插入圖片描述
遍歷所有廣播臺,找到廣播臺中覆蓋地區最多的,然後把allArrayList中的對應的城市刪了,然後再遍歷所有廣播電視臺,找到剩餘最多的,直到allArrayList中的城市刪完

最小生成樹(MST)

最小生成樹

給定一個帶權的無向連通圖,如何選取一棵生成樹,使樹上所有邊上權的總和為最小,這叫最小生成樹 ,N個頂點,一定有N-1條邊,N-1條邊都在圖中。

求最小生成樹的演算法主要是普里姆演算法和克魯斯卡爾演算法

普利姆演算法(Prim)

類似於廣度優先???
在這裡插入圖片描述

在這裡插入圖片描述

克魯斯卡爾演算法(Kruskal)

基本思想:按照權值從小到大的順序選擇n-1條邊,並保證這n-1條邊不構成迴路

具體做法: 首先構造一個只含n個頂點的森林,然後依權值從小到大從連通網中選擇邊加入到森林中,並使森林中不產生迴路,直至森林變成一棵樹為止

克 魯 斯 卡 爾 算 法 分 析 :

問題一 對圖的所有邊按照權值大小進行排序。
問題二 將邊新增到最小生成樹中時,怎麼樣判斷是否形成了迴路

如 何 判 斷 是 否 構 成 回 路 :

A<B<C<D…
在這裡插入圖片描述

最短路徑問題

迪傑斯特拉(Dijkstra)演算法

計算一個結點到其他結點的最短路徑。 它的主要特點是以起始點為中心向外層層擴充套件(廣度優先搜尋思想),直到擴充套件到終點為止。
在這裡插入圖片描述
弗洛伊德(Floyd)演算法

演算法複雜度比迪傑斯特拉要高

弗洛伊德演算法 VS 迪傑斯特拉演算法:

迪傑斯特拉演算法通過選定的被訪問頂點,求出從出發訪問頂點到其他頂點的最短路徑;弗洛伊德演算法中每一個頂點都是出發訪問點,所以需要將每一個頂點看做被訪問頂點,求出從每一個頂點到其他頂點的最短路徑

演算法思路:

三個頂點i,j,k,設定頂點vi到頂點vk的最短路徑已知為Lik,頂點vk到vj的最短路徑已知為Lkj,頂點vi到vj的路徑為Lij,則vi到vj的最短路徑為:min((Lik+Lkj),Lij),vk的取值為圖中所有頂點,則可獲得vi到vj的最短路徑。至於vi到vk的最短路徑Lik或者vk到vj的最短路徑Lkj,是以同樣的方式獲得

在這裡插入圖片描述

騎士周遊(馬踏棋盤)

騎士周遊問題的解決步驟和思路

  1. 建立棋盤 chessBoard , 是一個二維陣列
  2. 將當前位置設定為已經訪問,然後根據當前位置,計算馬兒還能走哪些位置,並放入到一個集合中(ArrayList), 最多有8個位置, 每走一步,就使用step+1
  3. 遍歷ArrayList中存放的所有位置,看看哪個可以走通 , 如果走通,就繼續,走不通,就回溯.
  4. 判斷馬兒是否完成了任務,使用 step 和應該走的步數比較 , 如果沒有達到數量,則表示沒有完成任務,將整個棋盤置0

注意:馬兒不同的走法(策略),會得到不同的結果,效率也會有影響(優化)

//建立一個Point
Point p1 = new Point();
if((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y -1) >= 0) {
ps.add(new Point(p1));
}

使用貪心演算法對原來的演算法優化
1。 我們獲取當前位置,可以走的下一個位置的集合
//獲取當前位置可以走的下一個位置的集合
ArrayList ps = next(new Point(column, row));
2. 我們需要對 ps 中所有的Point 的下一步的所有集合的數目,進行非遞減排序,就ok ,
9, 7, 6, 5, 3, 2 , 1 //遞減排序
1, 2, 3, 4,5,6, 10, //遞增排序

1, 2, 2, 2, 3,3, 4, 5, 6 // 非遞減
9, 7, 6,6, 6, 5,5, 3, 2 , 1 //非遞增

相關文章