算法-蛮力法
蠻力法的主要思想就是用最簡單的思路解決問題,一般性能不好,但仍然很重要。
查找問題中的蠻力法
順序查找
按照順序進行查找,一般可以對算法進行適當優化,如設置哨兵等,能夠改進時間性能,但只能減少系數,而數量級不會改變。
串匹配問題
串匹配定義:給定兩個串S=”s1s2s……n“和T=”t1t2t……m“,在主串S中查找子串T的過程稱為串匹配(也稱模式匹配),T稱為模式
BF算法
絕對的蠻力:從主串S的第一個字符開始和模式T的第一個字符進行比較,若相等,則繼續比較二者的后續字符;若不相等,則從主串S的第二個字符開始和模式T的第一個字符進行比較,重復上述過程,若T中的字符串全部比較完畢,則說明本趟匹配成功;若最后一輪匹配的其實位置是n-m,則主串S中剩下的字符不足夠匹配整個模式T,匹配失敗。
KMP算法
基本思想是:主串不進行回溯
畫了一個簡圖,對于主串S和子串T,在叉號位置(即j)不一致了,此時T的子串1(abcd)和子串2(abcd)內容是一致的,將子串1的下一位(即星號所在位置k)移到叉號所在位置,就可以繼續進行比較。這個思路是不是就可以快速提高匹配效率。
所以這個算法需要先計算出不匹配字符前面的字符串,即是真前綴又是真后綴的最長子串。將這個子串長度加一便是當前字符不匹配時,需要右移的到T的字符的位置。我們將這個值記錄到next數組中。
next[j]的值意味著當j不匹配的時候,從T的第k個字符進行比較。
計算next[j]的方法:假設next[j]的值是k
所以最終算法思路為,先計算T的next,然后將S和T進行比較,當在T的位置j不一致的時候,將T滑到next[j]的位置,繼續比較。
排序問題中的蠻力法
選擇排序
掃描整個序列,找到整個序列的最小記錄和序列中的第一個記錄交換,從而將最小記錄放到它在有序區的最終位置上,然后再從第二個記錄開始掃描序列,重復這個過程。
起泡排序
起泡排序開始的時候掃描整個序列,扎起掃描過程中兩兩比較相鄰記錄,如果反序則交換,最終,最大記錄就被沉到了序列的最后一個位置,第二趟掃描將第二大記錄沉到了導數第二個位置,重復操作,知道n-1趟掃描后,整個序列就排序好了。
起泡排序有優化方案
- 如果沒有交換,說明排序已經完成,無需繼續排序
- 如果在某個位置之后,再也沒有交換,說明在該位置之后都是排序好的,該位置之后無需繼續排序
組合問題中的蠻力法
生成排列對象
蠻力法生成{1,2,……,n}的n!排列。假設已經生成了(n-1)!個排列,可以把n插入到n-1個元素的每一種排列中的n個位置中去,來得到問題規模為n的所有排列。
生成子集
n個元素的集合A={a1,a2,……,an}的所有2n的自己和長度為n的所有2n的比特串之間的一一對應關系。
核心就在于這個一一對應關系,比特串里的0與1代表集合中的單個元素存在與不存在。
0/1背包問題
0/1背包問題是給定n個重量為{w1,w2,……,wn}、價值{v1,v2,……,vn}的物品和一個容量為C的背包,求這些物品中的一個最有價值的子集,并且要能夠裝到背包中。
這個問題使用蠻力法的話,就是計算出所有物品的子集,一個一個計算,看哪個價值最高。需要用到上面生成子集的方案。
這個就不做題了,一是因為主要功能在生成子集里已經做了,二是在樂扣里沒有找到合適的題目,大家如果知道合適的題目可以推薦一下。
任務分配問題
假設有n個人物需要分配給n個人執行,每個任務只分配給一個人,每個人只分配一個任務,且第j個任務分配給第i個人的成本是C[i,j](1<=i,j<=n),任務分配問題要求找出總成本最小的分配方案。
這個問題使用蠻力法的話,就是生成所有任務的全排列,總數為n!,然后一個一個計算哪個價值最高,需要用到上面生成排列對象的方案。
這個同樣也不做題了,原因和0/1背包問題一樣,核心邏輯在生成排列對象里完成了。總體而言,蠻力法解決組合問題,除非問題規模非常小,否則蠻力法幾乎不實用。
圖問題中的蠻力法
哈密頓回路問題
注明愛爾蘭數學家哈密頓提出注明的周游世界問題,他用正十二面體的20個訂單代表20個城市,要求從一個城市出發,經過每個城市恰好一次,然后回到出發城市。
用蠻力法解哈密頓回路問題
- 相鄰頂點有存在邊
- 第一個頂點和最后一個頂點存在邊
這個不做題了,核心還是求全排列,然后判斷是否有邊。真用蠻力法做,想想肯定得超時。
TSP問題
TSP問題是指旅行家要旅行n個城市然后回到出發城市,要求各個城市經歷且僅經歷一次,要求所走的路程最短。
這個問題和哈密頓回路的區別是加了路程最短的要求。
用蠻力法解TSP問題
這個也不做了,核心還是求全排列,必然超時
幾何問題中的蠻力法
最近對問題
最近對問題要求找出一個包含n個點的幾何中距離最近的兩個點。
蠻力法求解最近對問題的過程是:分別計算每一對點之間的距離,然后找出距離最小的那一對,為了避免對同一對點計算兩次距離,只考慮i<j的哪些點對(Pi,Pj)。
凸包問題
凸集合:對于平面上的一個點的有限幾何,如果以集合中任意兩點P和Q為端點的線段上的點都屬于該集合,則稱該集合為凸集合。
凸包:一個點集S的凸包是包含S的最小凸集合,其中,最小是指S的凸包一定是所有包含S的凸集合的子集。
凸包人話版:凸包就是你畫了一個封閉的范圍,這個范圍包含了集合中所有的點。最小凸包就是利用集合S上的點構建封閉范圍,里面包含集合所有點
使用蠻力法計算凸包:對于一個由n個點構成的集合S中的兩個點Pi和Pj,當且僅當該集合中的其他點都位于穿過這兩點的直線的同一邊時(假設不存在三點同線的情況),他們的連線是該集合凸包邊界的一部分。對每一對點都檢驗一遍后,滿足條件的線段構成了該凸包的邊界。這套方案時間復雜度O(n^3)。
這個就不做了,做法和最近對問題差不多,主要區別是找到對后,需要和剩余的其他點再做一次運算。而且樂扣里沒有發現相關的題目。
總結
看了這么多類型的題后,感覺蠻力法對有些問題還是有用的,對有些問題規模較小也還是有些用處的,對規模較大的問題,就完全沒有辦法了。
這里面的類型比較重要的有
查找問題:順序查找,KMP
排序問題:選擇排序、起泡排序。這兩個方法效率都不好,今后可以使用更高效的排序算法。但這兩種方法也提供了很不錯的思想。
組合問題:生成排序對象、生成子集。這兩個方法一個時間復雜度O(n!),一個為O(2^n),雖然效率不行,但是這是很多其他問題的基礎,如圖問題、0/1背包問題,任務分配問題等
幾何問題:最近對問題、凸包問題,整體思路比較簡單,兩重循環可以解決。
這些里面,尤其是KMP、生成排序對象、生成子集,有時間可以自己動手寫一下,還是有一定難度的。
最后
大家如果喜歡我的文章,可以關注我的公眾號(程序員麻辣燙)
往期文章回顧:
算法
技術
讀書筆記
思考
總結
- 上一篇: IM即时通讯项目讲解(一) 实现类似q
- 下一篇: Alternate Realities大