我的算法日記(1)---2017/1/24

早就在知乎上聽說了《算法導論》的威名,也因為學編程以來大多寫的都是工程代碼,算法方面接觸的太少了,但是算法又是程序員的內功,又是面試必考的一個環節,所以我這個寒假開始準備開始啃《算法導論》。這個系列的博客也權當是一種筆記吧。

算法的正確性

算法的正確性我是第一次聽說,之前在學校上數據結構課時老師也沒講過(講過才奇怪了)。

算法的正確性是由循環不定式來證明的:

Initialization: It is true prior to the first iteration of the loop.

Maintenance: If it is true before an iteration of the loop, it remains true before the next iteration.

Termination: When the loop terminates, the invariant gives us a useful property that helps show that the algorithm is correct.

總結來說就是,由于算法一般會牽扯到循環,那么就要驗證這個循環的正確性,在進入這個循環之前,你得是正確的,也就是說在某種條件下,不進行循環,算法也能得出正確的解。進入循環后,在循環過程是正確的,也就是說你這個循環要一次比一次更加接近解,而不是遠離解。循環要能正確終止,不能是無限循環,而且退出要是有意義的,能提供有用的性質。

分治法

分治法的思想就是將原問題分解位幾個規模較小但類似的問題,將子問題再進行分解知道足夠簡單解,然后合并解,建立原方程的解。分治模式有三個基本步驟:

  1. 分解。
  2. 解決。
  3. 合并。

歸并排序

歸并排序就是運用了一個分治遞歸的思想。

  1. 分解。將原數組通過不停的調用一個函數,將自身一分為二。直到不能再分為止。
  2. 解決。用一個合并函數將兩個有序數組合并。最開始是兩個數組各只有一個數字。其排序方法可用兩堆撲克牌想象。想象左右兩堆撲克牌有序,第一次比較左右牌堆頂誰的數小,就把它移到中間牌堆去,第二次,第三次重復比較。。。。。直至比較完或有一個牌堆放完,直接將另一個牌堆的剩余牌依次放置中間牌堆。
  3. 合并。合并函數返回合并后的數組。
#include <iostream>
#include <iomanip>
using namespace std;
void Merge_Sort(int *A, int p, int r);
void Merge(int *A, int p, int q, int r);
int main()
{
    int A[8] = {8, 7, 6, 5, 4, 3, 2, 1};

    Merge_Sort(A, 0, 7);
    for (int i = 0; i < 8; i++)
    {
        cout << A[i] << " ";
    }
    return 0;
}

void Merge_Sort(int *A, int p, int r)
{
    if (p < r)
    {
        int q = (p + r) / 2;
        Merge_Sort(A, p, q);
        Merge_Sort(A, q + 1, r);
        Merge(A, p, q, r);
    }
}

void Merge(int *A, int p, int q, int r)
{
    int n1 = q - p + 1;
    int n2 = r - q;
    int L[n1 + 1];
    int R[n2 + 1];
    for (int i = 0; i < n1; i++)
    {
        L[i] = A[p + i];
    }
    for (int i = 0; i < n2; i++)
    {
        R[i] = A[q + i + 1];
    }
    L[n1] = 100;
    R[n2] = 100;
    int i = 0;
    int j = 0;
    for (int k = p; k <= r; k++)
    {
        if (L[i] <= R[j])
        {
            A[k] = L[i];
            i++;
        } else
        {
            A[k] = R[j];
            j++;
        }
    }
}

注意這里使用了哨兵法避免每次都判斷數組是否已用完。哨兵的值應該待排數組的任何一個數都大。(實際使用絕不可能是100,這里只是為了方便)

二分查找

二分查找是指一個有序數組,每次都把這個數組的中間下標值與待查數比較,若相同則返回,若待查數更大(設該有序數組為升序),則將中間下標設為start下標,end下標不變,重新計算新的中間下標與待查數比較。重復此過程。

#include <iostream>
#include <iomanip>
using namespace std;
int binary_find(int *num, int key, int start, int end);
int main()
{
    int num[7] = {1, 2, 3, 4, 5, 6, 7};
    int a;
    cin >> a;
    cout<<"in NO "<<binary_find(num,a,0,6)+1<<endl;
    return 0;
}
int binary_find(int *num, int key,int start,int end)
{
    if (start > end)
    {
        return -2;
    }
    int mid = (start + end) / 2;
    if(key>num[mid])
    {
        binary_find(num, key, mid+1, end);
    } else if (key < num[mid])
    {
        binary_find(num, key, start, mid-1);
    } else if(key==num[mid])
    {
        return mid;
    }
}

如果start下標都比end下標大了,這就說明沒有找到,返回一個無意義的值或者返回false就行了。

題目:假設A[1...n]是一個有n個不同數的數組。若i < j且A[i] > A[j],則稱其為一個逆序對。 給出一個確定在n個元素的任何排列中逆序對數量的算法,最壞情況需要θ(nlogn)的時間。

這道題通過修改歸并排序的代碼就可以完成。考慮到排序的過程有兩種情況,一是待排數組本來就有序,這種情況就不存在逆序對。二是待排數組無序,這就存在逆序對。那我們所要做的就是將需要進行排序操作的下標記下來。

#include <iostream>
#include <iomanip>
using namespace std;
int Merge_Sort(int *A, int p, int r);
int Merge(int *A, int p, int q, int r);
int main()
{
    int A[8] = {7, 8, 6, 5, 4, 3, 2, 1};
    cout<<Merge_Sort(A, 0, 7);
    return 0;
}

int Merge_Sort(int *A, int p, int r)
{

    if (p < r)
    {
        int inversion = 0;
        int q = (p + r) / 2;
        inversion+=Merge_Sort(A, p, q);
        inversion+=Merge_Sort(A, q + 1, r);
        inversion+=Merge(A, p, q, r);
        return inversion;
    } else
    {
        return 0;
    }
}

int Merge(int *A, int p, int q, int r)
{
    int inversion = 0;
    int n1 = q - p + 1;
    int n2 = r - q;
    int L[n1 + 1];
    int R[n2 + 1];
    for (int i = 0; i < n1; i++)
    {
        L[i] = A[p + i];
    }
    for (int i = 0; i < n2; i++)
    {
        R[i] = A[q + i + 1];
    }
    L[n1] = 100;
    R[n2] = 100;
    int i = 0;
    int j = 0;
    for (int k = p; k <= r; k++)
    {
        if (L[i] <= R[j])
        {
            A[k] = L[i];
            i++;
        } else
        {
            A[k] = R[j];
            j++;
            inversion += n1 - i;
        }
    }
    return inversion;
}

我們設左牌堆(即左邊的數組)為較小堆,右邊為較大堆。代碼中最為關鍵的一行是

inversion += n1 - i; 為什么呢?因為當我們要將右邊的牌放到中間(即排序后的數組)去時,右邊的這張牌比左邊牌堆的所有牌都大而右邊的這張牌與左邊所有牌都構成一對逆序對, n1 - i 就是計算左邊一共有多少少張牌的。

由于初學算法,以上全是我的個人理解,代碼中的不足錯誤之處還請各位大牛批評指出。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容

  • 該系列文章主要是記錄下自己暑假這段時間的學習筆記,暑期也在實習,抽空學了很多,每個方面的知識我都會另起一篇博客去記...
    Yanci516閱讀 12,252評論 6 19
  • 本文分析冒泡、選擇、插入、希爾、快速、歸并和堆排序,為不影響閱讀體驗,將關于時間、空間復雜度和穩定性的概念放在博文...
    DeppWang閱讀 432評論 0 2
  • 勒布朗·詹姆斯,1984年12月30日出生在美國俄亥俄州阿克倫,美國職業籃球運動員,司職小前鋒,綽號“小皇帝”,現...
    drewww閱讀 588評論 14 5
  • 單憑借興趣就能堅持下來的事情恐怕不多,缺乏訓練的興趣愛好,很快就會碰到限制,也就是我們常說的”玻璃頂“。多數人的培...
    汪超_1362閱讀 154評論 0 0
  • 未來我們要生活在陽光下,不久希望我也要變成一束溫暖的陽光。 有一句話特別好,人為什么會有收獲,那是因為你嘗試做了人...
    陽光下的雨同學閱讀 560評論 10 5