算法初體驗

咱們在面向對象的演進過程一文中介紹了面向對象發展的幾個階段,其中第一個階段遠古時期的程序由數據結構和算法組成的。其中,數據結構表示數據的組織形式,基本的數據結構包括數組、鏈表、棧、隊列、樹、哈希表、圖、堆等。而算法表示對數據結構中的數據進行處理的方式或過程,換句話說,就是解決問題的方法。它們倆之間的關係:數據結構爲算法服務,不少算法依賴於特定的數據結構,但不是所有算法,算法能夠和數據結構沒有關係。本期咱們就來聊一聊算法。面試

學習算法的重要性

在介紹具體算法以前,我先談一下我的對學習算法的初心。個人初心無非有兩點:一,BAT等互聯網公司招聘面試時要問算法知識,若是想要進入互聯網公司,我就必須學好算法;二,經過學習算法提高我的開發的基本功,這樣一來,對於不一樣場景我就能夠正確選擇對應的數據結構和算法,使得程序更健壯,提升程序的運行效率。算法

應用領域

目前計算機各個細分領域涉及到不一樣的算法。好比說搜索引擎,平時咱們使用google、百度等瀏覽器,只要咱們輸入一個關鍵字,瀏覽器就會快速地返回相關的集合,這個集合的背後就隱藏着許多算法。若是沒有這些算法,咱們是不可能這麼快速地獲得想要的結果。再好比說人工智能,經過計算模型算法實現人體識別、語音識別等各應用場景。數組

算法分析

上文咱們已經介紹到算法就是解決問題的方法,而對於同一個問題,可能存在不一樣的解決方法。所以,爲了衡量一個算法的優劣,提出了時間複雜度空間複雜度這兩個概念。瀏覽器

時間複雜度微信

通常狀況下,算法中基本操做重複執行的次數是問題規模n的某個函數f(n),算法的時間度量記爲 T(n) = O(f(n)),它表示隨問題規模n的增大,算法執行時間的增加率和f(n)的增加率相同,稱做算法的漸近時間複雜度,簡稱時間複雜度。數據結構

空間複雜度數據結構和算法

空間複雜度是對一個算法在運行過程當中臨時佔用存儲空間大小的量度,記作S(n)=O(f(n))。一個算法的優劣主要從算法的執行時間和所須要佔用的存儲空間兩個方面衡量。函數

排序算法

根據時間複雜度咱們大致能夠將排序算法分爲兩類,一類是以選擇排序爲表明的O(n^2)的算法,另外一類是以快速排序爲表明的O(nlogn)的算法。看到這裏咱們不由會問:既然有O(nlogn)的排序算法,那些O(n^2)的算法還有存在的必要嗎?要回答這個問題,先來看下O(n^2)的排序算法的特色:首先,它相對是比較基礎的,編碼簡單,易於實現,在一些特定場景下O(n^2)更適合 ,譬如在機器語言中O(n^2)更容易實現;其次,簡單的排序算法思路衍生出複雜的排序算法,好比說希爾排序是對插入排序的優化;最後,對於一些簡單的算法,因爲它們自己的一些性質,能夠被用做改進更復雜排序算法的子過程當中。基於此,本文O(n^2)排序算法中兩個表明性的算法即選擇算法和插入算法。
image.png學習

選擇排序

思想:在整個待排序數組裏找到最小的值,而後和待排序中的第一個元素進行交換,接着在剩下的元素裏找到最小的元素,接着將它和待排序中的第一個元素進行交換,以此類推。爲了加深你們的理解,舉個具體例子,對八、六、二、三、一、五、七、4進行升序排序。
image.png優化

選擇排序的Java語言實現以下:

/**
     * 思路:每次從待選數組中選擇一個最小元素,而後和對應位置交換位置
     * @param arr
     * @param n
     */
    public void sort(int[] arr, int n) {
        for(int i=0;i<n;i++) {
            // 1. 尋找[i,n)區間裏的最小元素
            int minIndex = i;
            for(int j=i+1;j<n;j++ ) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            // 2. 交換位置
            this.swap(arr,i,minIndex);

        }
    }

插入排序

思路:插入排序是在一個已經有序的小序列的基礎上,一次插入一個元素。固然,剛開始這個有序的小序列只有1個元素,就是第一個元素。比較是從有序序列的末尾開始,也就是想要插入的元素和已經有序的最大者開始比起,若是比它大則直接插入在其後面,不然一直往前找直到找到它該插入的位置。若是遇見一個和插入元素相等的,那麼插入元素把想插入的元素放在相等元素的後面。因此,相等元素的先後順序沒有改變,從原無序序列出去的順序就是排好序後的順序,因此插入排序是穩定的。

image.png
插入排序的Java語言實現以下:

public void sort(Comparable[] arr){
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            // 尋找元素arr[i]合適的插入位置
            for( int j = i; j > 0 && arr[j].compareTo(arr[j-1]) < 0 ; j--)
                swap(arr, j, j-1);
        }
    }

經過比較選擇排序和插入排序的代碼實現,咱們能夠發現一旦有部分排序好以後,新插入一個數若是比排好序最大值還要大,則不用再和其餘數字比較,減小了比較次數。可是,咱們應該注意到插入排序在每次遍歷的時候都須要進行交換操做,這個交換操做包含三次賦值操做,致使插入排序的時間要比選擇排序的時間更長。針對這個問題,咱們的先輩們想到了一個方法:先將待比較元素複製一份,而後依次和有序數組中的元素進行比較,若是比有序數組中的元素小,則將有序數組中的元素覆蓋待比較元素,以此類推。以下圖所示,首先咱們將元素6複製一份,接着驗證元素6是否應當放在當前位置,經過比較6和它以前的元素大小,發現元素8應該放在元素6的位置上,所以將元素8覆蓋元素6,而後咱們考查元素6是否應該放在前一個元素位置上,此時,因爲元素8在第0個位置上咱們就不用比較直接覆蓋。它的Java代碼實現以下:

image.png

for (int i = 0; i < n; i++) {
            // 尋找元素arr[i]合適的插入位置
            Comparable e = arr[i];
            int j = i;
            for( ; j > 0 && arr[j-1].compareTo(e) > 0 ; j--)
                arr[j] = arr[j-1];
            arr[j] = e;
        }

這樣一來,內循環只須要進行一次賦值操做,效率獲得了大大優化,不只超過了選擇排序,並且在待排序數組是有序的狀況下,時間複雜度能夠達到O(n)


image

歡迎關注微信公衆號:木可大大,全部文章都將同步在公衆號上。

相關文章
相關標籤/搜索