Rust 算法排位記 - 插入排序的圖示與代碼實現

Rust 代碼在編寫過程當中與其它語言的略有不一樣,由於它的編譯器不容許有任何不安全的寫法,遂代碼編寫過程當中花費時間最長的莫過於查找編譯報錯的緣由。這樣也有好處——代碼寫好以後,穩定性高得一筆!git

如下是來自菜鳥教程中的排序定義和動圖示意:github

插入排序(英語:Insertion Sort)是一種簡單直觀的排序算法。它的工做原理是經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。

咱們來捋一捋,插入排序的主要邏輯爲:算法

  1. 外循環先指定一個數,一般是第一個數
  2. 接着在內循環中將這個外循環指定數與左側數逐個比較,並根據比較結果將外循環指定數插入在內循環數的左邊或不動
  3. 左側爲排序好的元素,遂內循環中的比較是外循環指定數不停地與左側元素進行比較
  4. 當外循環指定數小於左側元素時交換位置,不然不動
  5. 以此類推,直到外層循環結束

如今有一組須要排序的元素:安全

[7, 21, 9, 13, 109, 9, 2, 50, 33, -1, 20, 11]

按照插入排序的邏輯外循環指定第一個數 7,選定後 for 循環將從下標爲 1 的元素開始循環。代碼表現爲:async

for i in 1..vectors.len(){}

下標爲 0 的第一個數 7 是排序好的,那麼 for 循環從下標爲 1 的第二個數 21 開始。在內循環中將這個外循環指定數與左側數 [7] 逐個比較,當外循環指定數小於左邊元素時交換位置,不然不動。spa

此時進入下一輪外循環,指定下標爲 2 的第三個數 9。在內循環中將這個外循環指定數與左側數 [7, 21] 逐個比較,當外循環指定數小於左邊元素 21 時交換位置,遇到 7 不動。遂元素組變爲 :code

[7, 9, 21, 13, 109, 9, 2, 50, 33, -1, 20, 11]blog

此時進入下一輪外循環,指定下標爲 3 的第四個數 13。在內循環中將這個外循環指定數與左側數 [7, 9, 21] 逐個比較,當外循環指定數小於左邊元素 21 時交換位置,遇到 9 不動。遂元素組變爲 :排序

[7, 9, 13, 21, 109, 9, 2, 50, 33, -1, 20, 11]教程

依此類推 ...

此時進入第 N 輪外循環,指定下標爲 5 的第六個數 9。在內循環中將這個外循環指定數與左側數 [7, 9, 13, 21, 109] 逐個比較:

  • 當外循環指定數小於左邊元素 109 時交換位置;
  • 當外循環指定數小於左邊元素 21 時交換位置;
  • 當外循環指定數小於左邊元素 13 時交換位置;

遇到 9 不動。遂元素組變爲 :

[7, 9, 9, 13, 21, 109, 2, 50, 33, -1, 20, 11]

此時進入第 N 輪外循環,指定下標爲 6 的第七個數 2。在內循環中將這個外循環指定數與左側數 [7, 9, 9, 13, 21, 109] 逐個比較:

  • 當外循環指定數小於左邊元素 109 時交換位置;
  • 當外循環指定數小於左邊元素 21 時交換位置;
  • 當外循環指定數小於左邊元素 13 時交換位置;
  • 當外循環指定數小於左邊元素 9 時交換位置;
  • 當外循環指定數小於左邊元素 9 時交換位置;
  • 當外循環指定數小於左邊元素 7 時交換位置;

遍歷至元素組左側盡頭,此時 j 不大於等於 0,內循環結束。遂元素組變爲 :

[2, 7, 9, 9, 13, 21, 109, 50, 33, -1, 20, 11]

在此以前,2 的左側有不少元素,它須要與這些元素逐個比較並交換位置。數字 2 的位置變化過程以下:

[7, 9, 9, 13, 21, 2, 109, 50, 33, -1, 20, 11]
[7, 9, 9, 13, 2, 21, 109, 50, 33, -1, 20, 11]
[7, 9, 9, 2, 13, 21, 109, 50, 33, -1, 20, 11]
[7, 9, 2, 9, 13, 21, 109, 50, 33, -1, 20, 11]
[7, 2, 9, 9, 13, 21, 109, 50, 33, -1, 20, 11]
[2, 7, 9, 9, 13, 21, 109, 50, 33, -1, 20, 11]

從過程當中可看到,內循環的每一輪,數字 2 都會往左移動,直到前面沒有比 2 大的數字。

以此規則類推,元素排序的最終結果爲:

[-1, 2, 7, 9, 9, 11, 13, 20, 21, 33, 50, 109]

具體代碼實現

首先定義一組元素,並打印:

fn main() {
    let mut vectors = vec![7, 21, 9, 13, 109, 9, 2, 50, 33, -1, 20, 11];
    println!("vectors: {:?}", vectors);
}

而後定義排序方法:

fn insert_sort(vectors: &mut Vec<i32>) -> &Vec<i32>{
        vectors
}

排序方法的外循環是 for 循環:

fn insert_sort(vectors: &mut Vec<i32>) -> &Vec<i32>{
        for i in 1..vectors.len(){
        
    }
        vectors
}

這裏將外層元素賦值給可變變量 current,同時設定內循環左側元素的下標:

fn insert_sort(vectors: &mut Vec<i32>) -> &Vec<i32>{
        for i in 1..vectors.len(){
            let mut current = vectors[i];
            let mut j = i - 1;
    }
        vectors
}

在內循環中的將這個外循環指定數與左側數逐個比較,當外循環指定數小於左側元素時交換位置,不然不動。

與左側元素比較用 j = i - 1, vectors[j] 表示;

不停地與左側元素比較用 while j >= 0, j = j -1 表示;

比較用 current < vectors[j] 表示;

交換位置沒法像 Python 那樣 a, b = b, a,只能用 c = a, a = b, b = c 這種加入第三個數的方式倒騰。遂代碼以下

fn insert_sort(vectors: &mut Vec<i32>) -> &Vec<i32>{
    for i in 1..vectors.len(){
        let mut current = vectors[i];
        let mut j = i - 1;
        while j >= 0 && current < vectors[j]{
            let middle = vectors[j+1];
            vectors[j+1] = vectors[j];
            vectors[j] = middle;
            j = j - 1;
        }
    }
    vectors
}

不過這樣寫的話,外循環指定數爲比左側全部數都小的狀況下會沒法經過編譯的。例如外循環指定數爲 2 時須要與左側全部數進行比較,直到 j = 0,但 while 中最後一句是 j = j - 1,運行到這裏後 j = -1。按照正常運行流程,程序會進入到下一輪 while j >= 0 的內循環,但因爲 j = -1,就不會進入 while 循環體。Python 語言這樣寫是沒問題的,但 Rust 的編譯器不容許,遂須要在 j = j - 1 外層增長控制語句 if j = 0。

理論的驗證

上面的理論看似有理有據使人信服,但究竟對不對呢?

有沒有可能分析錯誤呢?

雖然程序是對的,但萬一描述出來的邏輯有誤呢?

咱們能夠經過打印程序執行過程當中的外循環指定數 current、內循環左側第一個數和每次交換位置後元素組 vectors 來觀察循環比較時元素位置的變化狀況。添加了打印語句的代碼以下:

fn insert_sort(vectors: &mut Vec<i32>) -> &Vec<i32>{
    for i in 1..vectors.len(){
        let mut current = vectors[i];
        let mut j = i - 1;
        println!("current vectors: {:?}", vectors);
        println!("current: {} < vectors[j]: {}", current, vectors[j]);
        while j >= 0 && current < vectors[j]{
            let middle = vectors[j+1];
            vectors[j+1] = vectors[j];
            vectors[j] = middle;
            if j > 0{
                /* rust 不容許while j >=0 中 j = 0 時還減 1
                致使 j 在 while 中爲負數這種危險寫法*/
                j = j - 1;  // j 遞減即不斷地跟左邊比較
            }
            println!("after vectors: {:?}", vectors);
        }
    }
    vectors
}

代碼運行後的打印結果如爲:

vectors: [7, 21, 9, 13, 109, 9, 2, 50, 33, -1, 20, 11]
current vectors: [7, 21, 9, 13, 109, 9, 2, 50, 33, -1, 20, 11]
current: 21 < vectors[j]: 7
current vectors: [7, 21, 9, 13, 109, 9, 2, 50, 33, -1, 20, 11]
current: 9 < vectors[j]: 21
after vectors: [7, 9, 21, 13, 109, 9, 2, 50, 33, -1, 20, 11]
current vectors: [7, 9, 21, 13, 109, 9, 2, 50, 33, -1, 20, 11]
current: 13 < vectors[j]: 21
after vectors: [7, 9, 13, 21, 109, 9, 2, 50, 33, -1, 20, 11]
current vectors: [7, 9, 13, 21, 109, 9, 2, 50, 33, -1, 20, 11]
current: 109 < vectors[j]: 21
current vectors: [7, 9, 13, 21, 109, 9, 2, 50, 33, -1, 20, 11]
current: 9 < vectors[j]: 109
after vectors: [7, 9, 13, 21, 9, 109, 2, 50, 33, -1, 20, 11]
after vectors: [7, 9, 13, 9, 21, 109, 2, 50, 33, -1, 20, 11]
after vectors: [7, 9, 9, 13, 21, 109, 2, 50, 33, -1, 20, 11]
current vectors: [7, 9, 9, 13, 21, 109, 2, 50, 33, -1, 20, 11]
current: 2 < vectors[j]: 109
after vectors: [7, 9, 9, 13, 21, 2, 109, 50, 33, -1, 20, 11]
after vectors: [7, 9, 9, 13, 2, 21, 109, 50, 33, -1, 20, 11]
after vectors: [7, 9, 9, 2, 13, 21, 109, 50, 33, -1, 20, 11]
after vectors: [7, 9, 2, 9, 13, 21, 109, 50, 33, -1, 20, 11]
after vectors: [7, 2, 9, 9, 13, 21, 109, 50, 33, -1, 20, 11]
after vectors: [2, 7, 9, 9, 13, 21, 109, 50, 33, -1, 20, 11]
current vectors: [2, 7, 9, 9, 13, 21, 109, 50, 33, -1, 20, 11]
current: 50 < vectors[j]: 109
after vectors: [2, 7, 9, 9, 13, 21, 50, 109, 33, -1, 20, 11]
current vectors: [2, 7, 9, 9, 13, 21, 50, 109, 33, -1, 20, 11]
current: 33 < vectors[j]: 109
after vectors: [2, 7, 9, 9, 13, 21, 50, 33, 109, -1, 20, 11]
after vectors: [2, 7, 9, 9, 13, 21, 33, 50, 109, -1, 20, 11]
current vectors: [2, 7, 9, 9, 13, 21, 33, 50, 109, -1, 20, 11]
current: -1 < vectors[j]: 109
after vectors: [2, 7, 9, 9, 13, 21, 33, 50, -1, 109, 20, 11]
after vectors: [2, 7, 9, 9, 13, 21, 33, -1, 50, 109, 20, 11]
after vectors: [2, 7, 9, 9, 13, 21, -1, 33, 50, 109, 20, 11]
after vectors: [2, 7, 9, 9, 13, -1, 21, 33, 50, 109, 20, 11]
after vectors: [2, 7, 9, 9, -1, 13, 21, 33, 50, 109, 20, 11]
after vectors: [2, 7, 9, -1, 9, 13, 21, 33, 50, 109, 20, 11]
after vectors: [2, 7, -1, 9, 9, 13, 21, 33, 50, 109, 20, 11]
after vectors: [2, -1, 7, 9, 9, 13, 21, 33, 50, 109, 20, 11]
after vectors: [-1, 2, 7, 9, 9, 13, 21, 33, 50, 109, 20, 11]
current vectors: [-1, 2, 7, 9, 9, 13, 21, 33, 50, 109, 20, 11]
current: 20 < vectors[j]: 109
after vectors: [-1, 2, 7, 9, 9, 13, 21, 33, 50, 20, 109, 11]
after vectors: [-1, 2, 7, 9, 9, 13, 21, 33, 20, 50, 109, 11]
after vectors: [-1, 2, 7, 9, 9, 13, 21, 20, 33, 50, 109, 11]
after vectors: [-1, 2, 7, 9, 9, 13, 20, 21, 33, 50, 109, 11]
current vectors: [-1, 2, 7, 9, 9, 13, 20, 21, 33, 50, 109, 11]
current: 11 < vectors[j]: 109
after vectors: [-1, 2, 7, 9, 9, 13, 20, 21, 33, 50, 11, 109]
after vectors: [-1, 2, 7, 9, 9, 13, 20, 21, 33, 11, 50, 109]
after vectors: [-1, 2, 7, 9, 9, 13, 20, 21, 11, 33, 50, 109]
after vectors: [-1, 2, 7, 9, 9, 13, 20, 11, 21, 33, 50, 109]
after vectors: [-1, 2, 7, 9, 9, 13, 11, 20, 21, 33, 50, 109]
after vectors: [-1, 2, 7, 9, 9, 11, 13, 20, 21, 33, 50, 109]
results: [-1, 2, 7, 9, 9, 11, 13, 20, 21, 33, 50, 109]

因而可知,理論部分的描述是正確的

完整的 Rust 插入排序代碼以下:

Rust 算法代碼倉庫地址 https://github.com/asyncins/a...

相關文章
相關標籤/搜索