幾種常見的算法 和 幾種常用的排序算法


算法: 解決問題的方法

總結一下常用的幾種算法

1.遞推法

遞推是序列計算機中的一種常用算法。它是按照一定的規律來計算序列中的每個項,通常是通過計算機前面的一些項來得出序列中的指定項的值。其思想是把一個復雜的龐大的計算過程轉化為簡單過程的多次重復,該算法利用了計算機速度快和不知疲倦的機器特點。

例如

如果你手里4個球分別: 紅、藍、黑、黃, 而現在只有一個球里面裝有答案, 你需要一個一個的打開去嘗試那個球里面有答案, 這個過程就是遞推法, 逐一排查

 

 

2. 遞歸法

程序調用自身的編程技巧稱為遞歸(recursion)。一個過程或函數在其定義或說明中有直接或間接調用自身的一種方法,它通常把一個大型復雜的問題層層轉化為一個與原問題相似的規模較小的問題來求解,遞歸策略只需少量的程序就可描述出解題過程所需要的多次重復計算,大大地減少了程序的代碼量。遞歸的能力在於用有限的語句來定義對象的無限集合。一般來說,遞歸需要有邊界條件、遞歸前進段和遞歸返回段。當邊界條件不滿足時,遞歸前進;當邊界條件滿足時,遞歸返回。

注意:

(1) 遞歸就是在過程或函數里調用自身;

(2) 在使用遞歸策略時,必須有一個明確的遞歸結束條件,稱為遞歸出口。

例如

程序員都應該知道遞歸的意思, 方法里面調用自己

 

 

3. 窮舉法

窮舉法,或稱為暴力破解法,其基本思路是:對於要解決的問題,列舉出它的所有可能的情況,逐個判斷有哪些是符合問題所要求的條件,從而得到問題的解。它也常用於對於密碼破譯,即將密碼進行逐個推算直到找出真正的密碼為止。

例如

一個已知是四位並且全部由數字組成的密碼,其可能共有10000種組合,因此最多嘗試10000次就能找到正確的密碼。理論上利用這種方法可以破解任何一種密碼,問題只在於如何縮短試誤時間。因此有些人運用計算機來增加效率,有些人輔以字典來縮小密碼組合的范圍。

 

 

 

4. 貪心算法

貪心算法是一種對某些求最優解問題的更簡單、更迅速的設計技術。

用貪心法設計算法的特點是一步一步地進行,常以當前情況為基礎根據某個優化測度作最優選擇,而不考慮各種可能的整體情況,它省去了為找最優解要窮盡所有可能而必須耗費的大量時間,它采用自頂向下,以迭代的方法做出相繼的貪心選擇,每做一次貪心選擇就將所求問題簡化為一個規模更小的子問題, 通過每一步貪心選擇,可得到問題的一個最優解,雖然每一步上都要保證能獲得局部最優解,但由此產生的全局解有時不一定是最優的,所以貪婪法不要回溯。

貪婪算法是一種改進了的分級處理方法,其核心是根據題意選取一種量度標准,然后將這多個輸入排成這種量度標准所要求的順序,按這種順序一次輸入一個量,如果這個輸入和當前已構成在這種量度意義下的部分最佳解加在一起不能產生一個可行解,則不把此輸入加到這部分解中。這種能夠得到某種量度意義下最優解的分級處理方法稱為貪婪算法。

對於一個給定的問題,往往可能有好幾種量度標准。初看起來,這些量度標准似乎都是可取的,但實際上,用其中的大多數量度標准作貪婪處理所得到該量度意義下的最優解並不是問題的最優解,而是次優解。因此,選擇能產生問題最優解的最優量度標准是使用貪婪算法的核心。

一般情況下,要選出最優量度標准並不是一件容易的事,但對某問題能選擇出最優量度標准后,用貪婪算法求解則特別有效。

例如

做一道復雜的數學運算題其實就是最好的貪心算法, 都需要一步一步的算才能得到最后的結果, 上一步的結果才能成接下一步過程, 最后得到結果

 

 

5. 分治法

分治法是把一個復雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最后子問題可以簡單的直接求解,原問題的解即子問題的解的合並。

分治法所能解決的問題一般具有以下幾個特征:

(1) 該問題的規模縮小到一定的程度就可以容易地解決;

(2) 該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質;

(3) 利用該問題分解出的子問題的解可以合並為該問題的解;

(4) 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子子問題。

 

 不做多余說明, 其實就是問題的逆向解題, 當前問題需要的條件,找到上一步怎么才能得到這些條件, 為了找尋這些條件又得到了一些新問題, 逐一最后把最上層的條件滿足,所有的小問題都解決了

 

 

6. 動態規划法

動態規划是一種在數學和計算機科學中使用的,用於求解包含重疊子問題的最優化問題的方法。其基本思想是,將原問題分解為相似的子問題,在求解的過程中通過子問題的解求出原問題的解。動態規划的思想是多種算法的基礎,被廣泛應用於計算機科學和工程領域。

動態規划程序設計是對解最優化問題的一種途徑、一種方法,而不是一種特殊算法。不象前面所述的那些搜索或數值計算那樣,具有一個標准的數學表達式和明確清晰的解題方法。動態規划程序設計往往是針對一種最優化問題,由於各種問題的性質不同,確定最優解的條件也互不相同,因而動態規划的設計方法對不同的問題,有各具特色的解題方法,而不存在一種萬能的動態規划算法,可以解決各類最優化問題。因此讀者在學習時,除了要對基本概念和方法正確理解外,必須具體問題具體分析處理,以豐富的想象力去建立模型,用創造性的技巧去求解。

 

 

 

7. 迭代法

迭代法也稱輾轉,是一種不斷用變量的舊值遞推新值的過程,跟迭代法相對應的是直接法(或者稱為一次解法),即一次性解決問題。迭代法又分為精確迭代和近似迭代。“二分法”和“牛頓迭代法”屬於近似迭代法。迭代算法是用計算機解決問題的一種基本方法。它利用計算機運算速度快、適合做重復性操作的特點,讓計算機對一組指令(或一定步驟)進行重復執行,在每次執行這組指令(或這些步驟)時,都從變量的原值推出它的一個新值。

例如

app版本的不斷升級其實就是迭代法的具體表現

 

 

8. 分支界限法

分枝界限法是一個用途十分廣泛的算法,運用這種算法的技巧性很強,不同類型的問題解法也各不相同。

分支定界法的基本思想是對有約束條件最優化問題的所有可行解(數目有限)空間進行搜索。該算法在具體執行時,把全部可行的解空間不斷分割為越來越小的子集(稱為分支),並為每個子集內的解的值計算一個下界或上界(稱為定界)。在每次分支后,對凡是界限超出已知可行解值那些子集不再做進一步分支,這樣,解的許多子集(即搜索樹上的許多結點)就可以不予考慮了,從而縮小了搜索范圍。這一過程一直進行到找出可行解為止,該可行解的值不大於任何子集的界限。因此這種算法一般可以求得最優解

貪心算法一樣,這種方法也是用來為組合優化問題設計求解算法的,所不同的是它在問題的整個可能解空間搜索,所設計出來的算法雖其時間復雜度貪婪算法高,但它的優點是與窮舉法類似,都能保證求出問題的最佳解,而且這種方法不是盲目的窮舉搜索,而是在搜索過程中通過限界,可以中途停止對某些不可能得到最優解的子空間進一步搜索(類似於人工智能中的剪枝),故它比窮舉法效率更高。

例如

猜數其實就是分支界限法的一個表現

 

 

8. 回溯法

回溯法(探索與回溯法)是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為“回溯點”。

 

其基本思想是,在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,如果包含,就從該結點出發繼續探索下去,如果該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜索算法)。 若用回溯法求問題的所有解時,要回溯到根,且根結點的所有可行的子樹都要已被搜索遍才結束。 而若使用回溯法求任一個解時,只要搜索到問題的一個解就可以結束。

 

排序: 是計算機內經常進行的一種操作,其目的是將一組“無序”的記錄序列調整為“有序”的記錄序列

 

總結一下幾種常用的排序算法

常見排序算法

快速排序希爾排序堆排序直接選擇排序不是穩定的排序算法,而基數排序冒泡排序直接插入排序、折半插入排序、歸並排序是穩定的排序算法

穩定排序:假設在待排序的文件中,存在兩個或兩個以上的記錄具有相同的關鍵字,在用某種排序法排序后,若這些相同關鍵字的元素的相對次序仍然不變,則這種排序方法是穩定的。

 

1. 冒泡排序: 2個相鄰的數逐一比較, 保持着大的始終在后面的原則, 直到最后都比較完 理論上會比較n*(n-1)/2次

 int a[] = {1,31,12,23,5,11,7,2,4,6,8,0,21,34,1321,3};
    int count = sizeof(a)/ sizeof(1);
    
    float tmp = a[0];
    int loopCount = 0;
    while (1) {
        loopCount++;
        for (int i = 0; i < sizeof(a)/ sizeof(1)-loopCount; i++) {
            if(a[i] <= a[i+1]) {
                continue;
            }
            tmp = a[i];
            a[i] = a[i+1];
            a[i+1] = tmp;
        }
        if (loopCount == count) break;
    }
    
    
    for (int i = 0; i < sizeof(a)/ sizeof(1); i++) {
        printf("%d\n", a[i]);
    }

 

 快速排序: 也是交換排序的一種衍生, 與冒泡屬於一類鼻祖

 2.選擇排序:  每次從未排序的元素中選擇最小的元素放在有序區

 

   int a[] = {3,31,12,23,5,11,7,2,4,6,8,0,21,34,1321,1};
    
    int tmp, btmp;
    for (int i = 0; i < sizeof(a)/ sizeof(1)-1; i++) {
        tmp = i;
        // 找到最小值的索引
        for (int j = i+1; j <sizeof(a)/ sizeof(1); j++) {
            if (a[tmp] > a[j]) tmp = j;
        }
        
        if (tmp == i) continue;
        // 最小值與i互換, 這樣就形成了前面有序, 后面無序
        btmp = a[tmp];
        a[tmp] = a[i];
        a[i] = btmp;
    }
    
    for (int i = 0; i < sizeof(a)/ sizeof(1); i++) {
        printf("%d\n", a[i]);
    }

 

快速排序: 在元素中找一個基值, 大於基值的在基值后面, 小於基值在前面, 這樣把所有元素根據基值分成2組, 再遞歸快速排序

直到最后, 內部需要交換排序的支持.

 

插入排序: 將無序的列表逐一在有序的序列中逐一比較, 找到元素自己的位置, 插入其中, 使有序列表后面的元素移動n個位置

鏈表是最好的例子


希爾排序: 也是插入排序的衍生

 

雞尾酒排序: 從前往后找最大的, 從后往前找最小的

 

想了解更多排序算法

http://www.cnblogs.com/eniac12/p/5329396.html

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM