算法與數據結構(一)


學習慕課網《算法與數據結構》

目錄:

第一節:簡介

第二節、排序基礎

    1.冒泡排序

    2.選擇排序

    3.插入排序

    4.希爾排序

第三節、高級排序算法

    1.歸並排序

    2.快速排序

思考題

第四節、堆和堆排序

    1.最大堆

        1)最大堆添加元素

        2)最大堆中取出元素

        3)最大堆排序

        4)原地堆排序

        5)最大索引堆

    2.最小堆

    排序算法總結

    思考題

第五節、二分搜索樹

    1.二分查找法

    2.二叉搜索樹

        1)二叉搜索樹插入元素

        2)二叉搜索樹查找元素

        3)二叉搜索樹遍歷元素

        4)二叉搜索樹刪除節點

 

第一節、簡介

學了那么多算法與數據結構,最后用慕課網的實戰《算法與數據結構來做一次總結》

有需要這門視頻的同學可以留言郵箱我會將百度雲鏈接發給他

 

測試1:普通隨機數組

測試2:接近有序的數組

測試3:大量重復數據的數組

我的簡單測試案例:[ 5, 8, 3, 6, 4, 2, 1, 7]

第二節、排序基礎

1.冒泡排序

冒泡排序(Bubble Sort),是一種計算機科學領域的較簡單的排序算法。

它重復地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。

這個算法的名字由來是因為越大的元素會經由交換慢慢“浮”到數列的頂端,故名“冒泡排序”。

1普通版冒泡排序(與后一個位置元素比較,若較小,當前位置與后一個位置進行交換)

2改進版冒泡排序(保存當前位置的值,與后一個位置元素比較,若較小,后一個位置的值賦值給當前位置)

初始:[ 5, 8, 3, 6, 4, 2, 1, 7]      操作次數

      [ 8, 5, 6, 4, 3, 2, 7, 1]      7

      [ 8, 6, 5, 4, 3, 7, 2, 1]      6

      [ 8, 6, 5, 4, 7, 3, 2, 1]      5

      [ 8, 6, 5, 7, 4, 3, 2, 1]      4

      [ 8, 6, 7, 5, 4, 3, 2, 1]      3

      [ 8, 7, 6, 5, 4, 3, 2, 1]      2

      [ 8, 7, 6, 5, 4, 3, 2, 1]      1

      [ 8, 7, 6, 5, 4, 3, 2, 1]      0

外層固定8個循環,內層固定7,6,5,4,3,2,1操作

時間復雜度為O(N^2)

 

2.選擇排序

(比較出最小的元素,保存元素位置,和指定位置交換)

選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工作原理是每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的數據元素排完。 選擇排序是不穩定的排序方法(比如序列[553]第一次就將第一個[5][3]交換,導致第一個5挪動到第二個5后面)。

初始:[ 5, 8, 3, 6, 4, 2, 1, 7]      操作次數

      [ 1, 8, 3, 6, 4, 2, 5, 7]      7

      [ 1, 2, 3, 6, 4, 8, 5, 7]      6

      [ 1, 2, 3, 6, 4, 8, 5, 7]      5

      [ 1, 2, 3, 4, 6, 8, 5, 7]      4

      [ 1, 2, 3, 4, 5, 8, 6, 7]      3

      [ 1, 2, 3, 4, 5, 6, 8, 7]      2

      [ 1, 2, 3, 4, 5, 6, 7, 8]      1

      [ 1, 2, 3, 4, 5, 6, 7, 8]      0

外層固定8個循環,內層固定7,6,5,4,3,2,1操作

時間復雜度為O(N^2)

 

3.插入排序

有一個已經有序的數據序列,要求在這個已經排好的數據序列中插入一個數,但要求插入后此數據序列仍然有序,這個時候就要用到一種新的排序方法——插入排序法,插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,算法適用於少量數據的排序,時間復雜度為O(n^2)。是穩定的排序方法。插入算法把要排序的數組分成兩部分:第一部分包含了這個數組的所有元素,但將最后一個元素除外(讓數組多一個空間才有插入的位置),而第二部分就只包含這一個元素(即待插入元素)。在第一部分排序完成后,再將這個最后元素插入到已排好序的第一部分中。

插入排序的基本思想是:每步將一個待排序的記錄,按其關鍵碼值的大小插入前面已經排序的文件中適當位置上,直到全部插入完為止。

1)普通版本插入排序(從1號元素位置開始與前一個元素做比較,較小就交換位置,較大就停止)------頻繁交換位置(賦值三次)太耗費時間

2)改進版本插入排序(保存一個循環位置的元素副本,與當前位置的前一個位置元素作比較,只將當前元素賦值為前一個較大的元素,最后確認之后再將副本元素賦值)------減少賦值次數(之前的每一次交換減少為一次賦值)

初始:[ 5, 8, 3, 6, 4, 2, 1, 7]      操作次數

      [ 5, 8, 3, 6, 4, 2, 1, 7]      0

      [ 3, 5, 8, 6, 4, 2, 1, 7]      2

      [ 3, 5, 6, 8, 4, 2, 1, 7]      1

      [ 3, 4, 5, 6, 8, 2, 1, 7]      3

      [ 2, 3, 4, 5, 6, 8, 1, 7]      5

      [ 1, 2, 3, 4, 5, 6, 8, 7]      6

      [ 1, 2, 3, 4, 5, 6, 7, 8]      1

外層固定7個循環,內層最多會有1,2,3,4,5,6,7操作(可以提前結束循環)

最好時間復雜度為O(N),最壞時間復雜度為O(N^2)

 

4.希爾排序

希爾排序(Shell's Sort)是插入排序的一種又稱“縮小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法。該方法因D.L.Shell1959年提出而得名。

希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序算法排序;隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。 [1] 最后一個增量必須為1)

希爾排序(盡可能的在增量為1的排序之前用少量代價讓數組接近完全排序)

增量為:4,2,1案例

(一)增量為4,外層循環(4次),中層循環(1次),內層循環操作0

初始:[ 5, 8, 3, 6, 4, 2, 1, 7]

  [ 4, 8, 3, 6, 5, 2, 1, 7] 1

  [ 4, 2, 3, 6, 5, 8, 1, 7] 1

  [ 4, 2, 1, 6, 5, 8, 3, 7] 1

      [ 4, 2, 1, 6, 5, 8, 3, 7]  0

(二)增量為2,外層循環(2次),中層循環(3次),內層循環操作0

初始:[ 4, 2, 1, 6, 5, 8, 3, 7]

      [ 1, 2, 4, 6, 5, 8, 3, 7] 0

      [ 1, 2, 4, 6, 5, 8, 3, 7] 0

      [ 1, 2, 3, 6, 4, 8, 5, 7] 2

      [ 1, 2, 3, 6, 4, 8, 5, 7] 0

      [ 1, 2, 3, 6, 4, 8, 5, 7] 0

      [ 1, 2, 3, 6, 4, 7, 5, 8]1

(三)增量為1,外層循環(1次),中層循環(7次),內層循環操作0  

初始:[ 1, 2, 3, 6, 4, 7, 5, 8]

      [ 1, 2, 3, 6, 4, 7, 5, 8] 0

      [ 1, 2, 3, 6, 4, 7, 5, 8] 0

      [ 1, 2, 3, 6, 4, 7, 5, 8] 0

      [ 1, 2, 3, 4, 6, 7, 5, 8] 1

      [ 1, 2, 3, 4, 6, 7, 5, 8] 0

      [ 1, 2, 3, 4, 5, 6, 7, 8] 2

      [ 1, 2, 3, 4, 5, 6, 7, 8]0

最好時間復雜度為O(N),最壞時間復雜度為O(N^2),但是相對於插入排序快一些

public static void main(String[] args) {
    int[] a = {5, 8, 3, 6, 4, 2, 1, 7};
    int h = a.length;
    //h為遞減量
    while (h > 1) {
        h = h / 2;
        System.out.println(h + "增量排序開始");
        for (int x = 0; x < h; x++) {
            for (int i = x + h; i < a.length; i = i + h) {
                int temp = a[i];
                int j;
                for (j = i - h; j >= 0 && a[j] > temp; j = j - h) {
                    a[j + h] = a[j];
                }
                a[j + h] = temp;
                System.out.print(" 增量排序之后:");
                for (int k = 0; k < a.length; k++) {
                    System.out.print(a[k] + " ");
                }
                System.out.println();
            }
        }
    }
}

 

第三節、高級排序算法

1.歸並排序

歸並排序(MERGE-SORT)是建立在歸並操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合並,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合並成一個有序表,稱為二路歸並。

1)普通版本歸並排序

/**
 * @Author: Xiong
 * @Date: 2018/6/5 16:11
 */
public class TestMain {
    /**
     * 對[l...mid]到[mid+1...r]兩部分進行歸並
     */
    private static void merge(int[] a, int l, int mid, int r) {
        int[] aux = new int[r - l + 1];
        System.arraycopy(a, l, aux, 0, r + 1 - l);
        //[l...mid]到[mid+1...r]
        //i是前一個數組指向的起始地址,j是后一個數組指向的起始地址
        int i = l, j = mid + 1;
        for (int k = l; k <= r; k++) {
            if (i > mid) {
                a[k] = aux[j - l];
                j++;
            } else if (j > r) {
                a[k] = aux[i - l];
                i++;
            } else if (aux[i - l] < aux[j - l]) {
                a[k] = aux[i - l];
                i++;
            } else {
                a[k] = aux[j - l];
                j++;
            }
        }
    }
    /**
     * 處理[l,r]返回內的數組
     */
    private static void mergeSort(int[] a, int l, int r) {
        if (l >= r) {
            return;
        }
        int mid = (l + r) / 2;
        mergeSort(a, l, mid);
        mergeSort(a, mid + 1, r);
        merge(a, l, mid, r);
    }
    public static void main(String[] args) {
        int[] a = new int[]{5, 8, 3, 6, 4, 2, 1, 7};
        mergeSort(a, 0, a.length - 1);
        for (int anA : a) {
            System.out.print(anA + " ");
        }
    }
}

 

2)改進版本歸並排序-----兩點改進

改進一,當數組數量小於16時,改成插入排序

l 改進二,當接近有序的排序的時候,需要修改部分操作

private static void mergeSort(int[] a, int l, int r) {
    if (l >= r) {
        return;
    }
    int mid = (l + r) / 2;
    mergeSort(a, l, mid);
    mergeSort(a, mid + 1, r);
    merge(a, l, mid, r);
}

private static void mergeSort(int[] a, int l, int r) {
    //改進1,對所有數組有效
    if (r - l <= 16) {
        insertionSort(a);
        return;
    }
    int mid = (l + r) / 2;
    mergeSort(a, l, mid);
    mergeSort(a, mid + 1, r);
    //改進2,對於近乎有序的數組有效,但是對於一般數組可能會減少性能
    if (a[mid] > a[mid + 1]) {
        merge(a, l, mid, r);
    }
}

 

3)自底向上的歸並排序

初始:[ 5, 8, 3, 6, 4, 2, 1, 7]      操作次數

 

 

2.快速排序

快速排序(Quicksort)是對冒泡排序的一種改進。

快速排序由C. A. R. Hoare1962年提出。它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。

1)普通版本

2)接近有序的序列,改進版(一)

3)重復值多的序列,改進版本(一)

4)重復值多的序列,改進版本(二),三路快速排序

初始:[ 5, 8, 3, 6, 4, 2, 1, 7]      操作次數

 

思考題

1)思考題逆序數------歸並排序ONlogN

2)尋找數組中第N大的值------快速排序ON

 

 

第四節、堆和堆排序

二叉堆分為最小堆和最大堆兩種,通過數組實現數據結構,數組從1開始。

堆更多場景用在動態維護上面

1.最大堆

最大堆是堆的兩種形式之一。

根結點(亦稱為堆頂)的關鍵字是堆里所有結點關鍵字中最大者,稱為大根堆,又稱最大堆(大頂堆)。

大根堆要求根節點的關鍵字既大於或等於左子樹的關鍵字值,又大於或等於右子樹的關鍵字值。

1)最大堆添加元素

對最后一個元素,下標為count元素進行上升操作(Shift Up

 

 

 

2)最大堆堆中取出元素

堆中取出元素,然后將最后一個元素放到位置1,進行下降操作(Shift Down)操作

 

小優化,將交換位置改為賦值

 

 

3)最大堆堆排序

① 先將數組元素一個個存入堆中,再取出來放入數組中(NlogN

② 數組轉換為堆優化,先將數組復制為1為起始下標的數組,然后在非葉子節點的元素上下標為(count/2)進行下降(shift down)操作,這個名稱為:heapify,在取出來放入堆中;heapify的時間復雜度為ON

 

4)原地堆排序

優化一(數組->最大堆):不再創建數組,從原本0開始的數組進行heapify

優化二(最大堆->排序好的數組):數組排序時,將最后一個元素和第一個元素交換位置,對第一個元素進行shift Down操作

 

5)最大索引堆

一個堆,堆放索引,尋找堆中索引所對應的優先級與其他堆中索引對應的優先級進行比較,然后再進行shift up或者shift down操作

一個數組,數組中放優先級,自始不會變化

1)入堆操作,出堆操作

2)改變堆的優先級操作,優先級是ON)級別操作,如果對N個元素進行優先級改變就是ON^2)操作,不理想

3)改變堆優先級操作(優化)

 

 

l 反向堆索引的數組

創建一個索引數組revrev的索引是index堆中的元素,rev的元素是index堆中的索引,通過找rev中的索引,直接找到對應的元素修改他的優先級,然后再回歸到index堆中進行shift up或者shift down操作

可能存在的問題:堆中沒有rev所對應的元素,即出堆之后將rev指向0

2.最小堆

自己實現吧!

 

排序算法總結

 

 

 

 

穩定性:舉個例子:對於學生已經按照姓名排序之后,再按照成績排序,成績排序之后,相同成績的學生還是按照姓名排好順序,這就是穩定性

 

 

思考題

1.堆實現優先隊列

2.堆實現人工智能自動攻擊對象

3.100萬元素中選出前100

一個100的最小堆,最小的元素如果比要存的元素小,即當前要存的元素比較大,就吧當前元素存進去,再shift down操作,即N*logM時間復雜度

4.堆實現多路歸並

5.d叉堆,本節實現的是二叉堆

6.統一以0為開始索引,1開始的索引是經典實現,但是不符合現實邏輯

7.一個最大堆,一個最小堆,同時處理一個數組中的元素

8.二項堆

9.斐波那契堆

 

第五節、二分搜索樹

1.二分查找法(Binary Search

二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。但是,折半查找要求線性表必須采用順序存儲結構,而且表中元素按關鍵字有序排列。 [1]

查找必須為順序表,查找效率O(logN)

 

 

 

 

bugmid = l+r/2 l+r可能超過int的最大值

修改:mid = l + (r-l)/2

 

 

2.二叉搜索樹

二叉查找樹(Binary Search Tree),(又:二叉搜索樹,二叉排序樹)它或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 它的左、右子樹也分別為二叉排序樹。

查找表的實現 - 字典數據結構。

字典的keyvaluekey值不為整數時,增,刪,改時間復雜度都是O(logN)

n 二分搜索樹:每個節點的鍵值大於左孩子,每個幾點的兼職小於右孩子,以左右孩子為根的字數仍為二分搜索樹。

n 二分查找樹不一定為完全二叉樹。

 

1)二分搜索樹插入元素

n 一個個比較,如果比當前節點大,就查找右節點,比當前節點小,就查找左節點,如果不存在節點,就可以保存當前元素

如果存在重復值,即key值一樣,即覆蓋原來的value

 

 

2)二分搜索樹查找元素

n 查找最小值:一直找二分搜索樹的左節點

n 查找最大值:一直找二分搜索樹的右節點

n 查找任意值:

 

 

3)二分搜索樹的遍歷

n 前序遍歷

n 中序遍歷

n 后序遍歷

n 層序遍歷:使用隊列進行遍歷

 

4)二分搜索樹刪除節點

n 刪除最大值:

n 刪除最小值:

刪除任意一個節點:Hubbard Deletion,刪除節點的右子樹的最小值放到該位置;同理左子樹的最大值

 


免責聲明!

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



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