JavaScript演算法實現——排序

来源:https://www.cnblogs.com/jaxu/archive/2019/08/23/11382646.html
-Advertisement-
Play Games

在電腦編程中,排序演算法是最常用的演算法之一,本文介紹了幾種常見的排序演算法以及它們之間的差異和複雜度。 冒泡排序 冒泡排序應該是最簡單的排序演算法了,在所有講解電腦編程和數據結構的課程中,無一例外都會拿冒泡排序作為開篇來講解排序的原理。冒泡排序理解起來也很容易,就是兩個嵌套迴圈遍曆數組,對數組中的元素 ...


  在電腦編程中,排序演算法是最常用的演算法之一,本文介紹了幾種常見的排序演算法以及它們之間的差異和複雜度。

冒泡排序

  冒泡排序應該是最簡單的排序演算法了,在所有講解電腦編程和數據結構的課程中,無一例外都會拿冒泡排序作為開篇來講解排序的原理。冒泡排序理解起來也很容易,就是兩個嵌套迴圈遍曆數組,對數組中的元素兩兩進行比較,如果前者比後者大,則交換位置(這是針對升序排序而言,如果是降序排序,則比較的原則是前者比後者小)。我們來看下冒泡排序的實現:

function bubbleSort(array) {
    let length = array.length;
    for (let i = 0; i < length; i++) {
        for (let j = 0; j < length - 1; j++) {
            if (array[j] > array[j + 1]) {
                [array[j], array[j + 1]] = [array[j + 1], array[j]];
            }
        }
    }
}

  上面這段代碼就是經典的冒泡排序演算法(升序排序),只不過交換兩個元素位置的部分我們沒有用傳統的寫法(傳統寫法需要引入一個臨時變數,用來交換兩個變數的值),這裡使用了ES6的新功能,我們可以使用這種語法結構很方便地實現兩個變數值的交換。來看下對應的測試結果:

let array = [];
for (let i = 5; i > 0; i--) {
    array.push(i);
}

console.log(array.toString()); // 5,4,3,2,1
bubbleSort(array);
console.log(array.toString()); // 1,2,3,4,5

   在冒泡排序中,對於內層的迴圈而言,每一次都是把這一輪中的最大值放到最後(相對於升序排序),它的過程是這樣的:第一次內層迴圈,找出數組中的最大值排到數組的最後;第二次內層迴圈,找出數組中的次大值排到數組的倒數第二位;第三次內層迴圈,找出數組中的第三大值排到數組的倒數第三位......以此類推。所以,對於內層迴圈,我們可以不用每一次都遍歷到length - 1的位置,而只需要遍歷到length - 1 - i的位置就可以了,這樣可以減少內層迴圈遍歷的次數。下麵是改進後的冒泡排序演算法:

function bubbleSortImproved(array) {
    let length = array.length;
    for (let i = 0; i < length; i++) {
        for (let j = 0; j < length - 1 - i; j++) {
            if (array[j] > array[j + 1]) {
                [array[j], array[j + 1]] = [array[j + 1], array[j]];
            }
        }
    }
}

  運行測試,結果和前面的bubbleSort()方法得到的結果是相同的。

let array = [];
for (let i = 5; i > 0; i--) {
    array.push(i);
}

console.log(array.toString()); // 5,4,3,2,1
bubbleSortImproved(array);
console.log(array.toString()); // 1,2,3,4,5

  在實際應用中,我們並不推薦使用冒泡排序演算法,儘管它是最直觀的用來講解排序過程的演算法。冒泡排序演算法的複雜度為O(n2)

選擇排序

  選擇排序與冒泡排序很類似,它也需要兩個嵌套的迴圈來遍曆數組,只不過在每一次迴圈中要找出最小的元素(這是針對升序排序而言,如果是降序排序,則需要找出最大的元素)。第一次遍歷找出最小的元素排在第一位,第二次遍歷找出次小的元素排在第二位,以此類推。我們來看下選擇排序的的實現:

function selectionSort(array) {
    let length = array.length;
    let min;

    for (let i = 0; i < length - 1; i++) {
        min = i;
        for (let j = i; j < length; j++) {
            if (array[min] > array[j]) {
                min = j;
            }
        }

        if (i !== min) {
            [array[i], array[min]] = [array[min], array[i]];
        }
    }
}

  上面這段代碼是升序選擇排序,它的執行過程是這樣的,首先將第一個元素作為最小元素min,然後在內層迴圈中遍曆數組的每一個元素,如果有元素的值比min小,就將該元素的值賦值給min。內層遍歷完成後,如果數組的第一個元素和min不相同,則將它們交換一下位置。然後再將第二個元素作為最小元素min,重覆前面的過程。直到數組的每一個元素都比較完畢。下麵是測試結果:

let array = [];
for (let i = 5; i > 0; i--) {
    array.push(i);
}

console.log(array.toString()); // 5,4,3,2,1
selectionSort(array);
console.log(array.toString()); // 1,2,3,4,5

  選擇排序演算法的複雜度與冒泡排序一樣,也是O(n2)

插入排序

  插入排序與前兩個排序演算法的思路不太一樣,為了便於理解,我們以[ 5, 4, 3, 2, 1 ]這個數組為例,用下圖來說明插入排序的整個執行過程:

  在插入排序中,對數組的遍歷是從第二個元素開始的,tmp是個臨時變數,用來保存當前位置的元素。然後從當前位置開始,取前一個位置的元素與tmp進行比較,如果值大於tmp(針對升序排序而言),則將這個元素的值插入到這個位置中,最後將tmp放到數組的第一個位置(索引號為0)。反覆執行這個過程,直到數組元素遍歷完畢。下麵是插入排序演算法的實現:

function insertionSort(array) {
    let length = array.length;
    let j, tmp;

    for (let i = 1; i < length; i++) {
        j = i;
        tmp = array[i];
        while (j > 0 && array[j - 1] > tmp) {
            array[j] = array[j - 1];
            j--;
        }
        array[j] = tmp;
    }
}

  對應的測試結果:

let array = [];
for (let i = 5; i > 0; i--) {
    array.push(i);
}

console.log(array.toString()); // 5,4,3,2,1
insertionSort(array);
console.log(array.toString()); // 1,2,3,4,5

  插入排序比冒泡排序和選擇排序演算法的性能要好。

歸併排序

  歸併排序比前面介紹的幾種排序演算法性能都要好,它的複雜度為O(nlogn)

  歸併排序的基本思路是通過遞歸調用將給定的數組不斷分割成最小的兩部分(每一部分只有一個元素),對這兩部分進行排序,然後向上合併成一個大數組。我們還是以[ 5, 4, 3, 2, 1 ]這個數組為例,來看下歸併排序的整個執行過程:

  首先要將數組分成兩個部分,對於非偶數長度的數組,你可以自行決定將多的分到左邊或者右邊。然後按照這種方式進行遞歸,直到數組的左右兩部分都只有一個元素。對這兩部分進行排序,遞歸向上返回的過程中將其組成和一個完整的數組。下麵是歸併排序的演算法的實現:

const merge = (left, right) => {
    let i = 0;
    let j = 0;
    const result = [];

    // 通過這個while迴圈將left和right中較小的部分放到result中
    while (i < left.length && j < right.length) {
        if (left[i] < right[i]) result.push(left[i++]);
        else result.push(right[j++]);
    }

    // 然後將組合left或right中的剩餘部分
    return result.concat(i < left.length ? left.slice(i) : right.slice(j));
};

function mergeSort(array) {
    let length = array.length;
    if (length > 1) {
        const middle = Math.floor(length / 2); // 找出array的中間位置
        const left = mergeSort(array.slice(0, middle)); // 遞歸找出最小left
        const right = mergeSort(array.slice(middle, length)); // 遞歸找出最小right
        array = merge(left, right); // 將left和right進行排序
    }
    return array;
}

  主函數mergeSort()通過遞歸調用本身得到left和right的最小單元,這裡我們使用Math.floor(length / 2)將數組中較少的部分放到left中,將數組中較多的部分放到right中,你可以使用Math.ceil(length / 2)實現相反的效果。然後調用merge()函數對這兩部分進行排序與合併。註意在merge()函數中,while迴圈部分的作用是將left和right中較小的部分存入result數組(針對升序排序而言),語句result.concat(i < left.length ? left.slice(i) : right.slice(j))的作用則是將left和right中剩餘的部分加到result數組中。考慮到遞歸調用,只要最小部分已經排好序了,那麼在遞歸返回的過程中只需要把left和right這兩部分的順序組合正確就能完成對整個數組的排序。

  對應的測試結果:

let array = [];
for (let i = 5; i > 0; i--) {
    array.push(i);
}

console.log(array.toString()); // 5,4,3,2,1
console.log(mergeSort(array).toString()); // 1,2,3,4,5

快速排序

  快速排序的複雜度也是O(nlogn),但它的性能要優於其它排序演算法。快速排序與歸併排序類似,其基本思路也是將一個大數組分為較小的數組,但它不像歸併排序一樣將它們分割開。快速排序演算法比較複雜,大致過程為:

  1. 從給定的數組中選取一個參考元素。參考元素可以是任意元素,也可以是數組的第一個元素,我們這裡選取中間位置的元素(如果數組長度為偶數,則向下取一個位置),這樣在大多數情況下可以提高效率。
  2. 創建兩個指針,一個指向數組的最左邊,一個指向數組的最右邊。移動左指針直到找到比參考元素大的元素,移動右指針直到找到比參考元素小的元素,然後交換左右指針對應的元素。重覆這個過程,直到左指針超過右指針(即左指針的索引號大於右指針的索引號)。通過這一操作,比參考元素小的元素都排在參考元素之前,比參考元素大的元素都排在參考元素之後(針對升序排序而言)。
  3. 以參考元素為分隔點,對左右兩個較小的數組重覆上述過程,直到整個數組完成排序。

  下麵是快速排序演算法的實現:

const partition = (array, left, right) => {
    const pivot = array[Math.floor((right + left) / 2)];
    let i = left;
    let j = right;

    while (i <= j) {
        while (array[i] < pivot) {
            i++;
        }
        while (array[j] > pivot) {
            j--;
        }
        if (i <= j) {
            [array[i], array[j]] = [array[j], array[i]];
            i++;
            j--;
        }
    }
    return i;
};

const quick = (array, left, right) => {
    let length = array.length;
    let index;
    if (length > 1) {
        index = partition(array, left, right);
        if (left < index - 1) {
            quick(array, left, index - 1);
        }
        if (index < right) {
            quick(array, index, right);
        }
    }
    return array;
};

function quickSort(array) {
    return quick(array, 0, array.length - 1);
}

  假定數組為[ 3, 5, 1, 6, 4, 7, 2 ],按照上面的代碼邏輯,整個排序的過程如下圖所示:

  下麵是測試結果:

let array = [3, 5, 1, 6, 4, 7, 2];
console.log(array.toString()); // 3,5,1,6,4,7,2
console.log(quickSort(array).toString()); // 1,2,3,4,5,6,7

  快速排序演算法理解起來有些難度,可以按照上面給出的示意圖逐步推導一遍,以幫助理解整個演算法的實現原理。

堆排序

  在電腦科學中,堆是一種特殊的數據結構,它通常用樹來表示數組。堆有以下特點:

  • 堆是一棵完全二叉樹
  • 子節點的值不大於父節點的值(最大堆),或者子節點的值不小於父節點的值(最小堆)
  • 根節點的索引號為0
  • 子節點的索引為父節點索引 × 2 + 1
  • 右子節點的索引為父節點索引 × 2 + 2

  堆排序是一種比較高效的排序演算法。

  在堆排序中,我們並不需要將數組元素插入到堆中,而只是通過交換來形成堆,以數組[ 3, 5, 1, 6, 4, 7, 2 ]為例,我們用下圖來表示其初始狀態:

  那麼,如何將其轉換成一個符合標準的堆結構呢?先來看看堆排序演算法的實現:

const heapify = (array, heapSize, index) => {
    let largest = index;
    const left = index * 2 + 1;
    const right = index * 2 + 2;
    if (left < heapSize && array[left] > array[index]) {
        largest = left;
    }
    if (right < heapSize && array[right] > array[largest]) {
        largest = right;
    }
    if (largest !== index) {
        [array[index], array[largest]] = [array[largest], array[index]];
        heapify(array, heapSize, largest);
    }
};

const buildHeap = (array) => {
    let heapSize = array.length;
    for (let i = heapSize; i >= 0; i--) {
        heapify(array, heapSize, i);
    }
};

function heapSort(array) {
    let heapSize = array.length;
    buildHeap(array);

    while (heapSize > 1) {
        heapSize--;
        [array[0], array[heapSize]] = [array[heapSize], array[0]];
        heapify(array, heapSize, 0);
    }

    return array;
}

  函數buildHeap()將給定的數組轉換成堆(按最大堆處理)。下麵是將數組[ 3, 5, 1, 6, 4, 7, 2 ]轉換成堆的過程示意圖:

  在函數buildHeap()中,我們從數組的尾部開始遍歷去查看每個節點是否符合堆的特點。在遍歷的過程中,我們發現當索引號為6、5、4、3時,其左右子節點的索引大小都超出了數組的長度,這意味著它們都是葉子節點。那麼我們真正要做的就是從索引號為2的節點開始。其實從這一點考慮,結合我們利用完全二叉樹來表示數組的特性,可以對buildHeap()函數進行優化,將其中的for迴圈修改為下麵這樣,以去掉對子節點的操作。

for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
    heapify(array, heapSize, i);
}

  從索引2開始,我們查看它的左右子節點的值是否大於自己,如果是,則將其中最大的那個值與自己交換,然後向下遞歸查找是否還需要對子節點繼續進行操作。索引2處理完之後再處理索引1,然後是索引0,最終轉換出來的堆如圖中的4所示。你會發現,每一次堆轉換完成之後,排在數組第一個位置的就是堆的根節點,也就是數組的最大元素。根據這一特點,我們可以很方便地對堆進行排序,其過程是:

  • 將數組的第一個元素和最後一個元素交換
  • 減少數組的長度,從索引0開始重新轉換堆

  直到整個過程結束。對應的示意圖如下:

  堆排序的核心部分在於如何將數組轉換成堆,也就是上面代碼中buildHeap()和heapify()函數部分。

  同樣給出堆排序的測試結果:

let array = [3, 5, 1, 6, 4, 7, 2];
console.log(array.toString()); // 3,5,1,6,4,7,2
console.log(heapSort(array).toString()); // 1,2,3,4,5,6,7

有關演算法複雜度

  上面我們在介紹各種排序演算法的時候,提到了演算法的複雜度,演算法複雜度用大O表示法,它是用大O表示的一個函數,如:

  • O(1):常數
  • O(log(n)):對數
  • O(log(n) c):對數多項式
  • O(n):線性
  • O(n2):二次
  • O(nc):多項式
  • O(cn):指數

  我們如何理解大O表示法呢?看一個例子:

function increment(num) {
    return ++num;
}

  對於函數increment(),無論我傳入的參數num的值是什麼數字,它的運行時間都是X(相對於同一臺機器而言)。函數increment()的性能與參數無關,因此我們可以說它的演算法複雜度是O(1)(常數)。

  再看一個例子:

function sequentialSearch(array, item) {
    for (let i = 0; i < array.length; i++) {
        if (item === array[i]) return i;
    }
    return -1;
}

  函數sequentialSearch()的作用是在數組中搜索給定的值,並返回對應的索引號。假設array有10個元素,如果要搜索的元素排在第一個,我們說開銷為1。如果要搜索的元素排在最後一個,則開銷為10。當數組有1000個元素時,搜索最後一個元素的開銷是1000。所以,sequentialSearch()函數的總開銷取決於數組元素的個數和要搜索的值。在最壞情況下,沒有找到要搜索的元素,那麼總開銷就是數組的長度。因此我們得出sequentialSearch()函數的時間複雜度是O(n),n是數組的長度。

  同理,對於前面我們說的冒泡排序演算法,裡面有一個雙層嵌套的for迴圈,因此它的複雜度為O(n2)。

  時間複雜度O(n)的代碼只有一層迴圈,而O(n2)的代碼有雙層嵌套迴圈。如果演算法有三層嵌套迴圈,它的時間複雜度就是O(n3)。

  下表展示了各種不同數據結構的時間複雜度:

數據結構 一般情況 最差情況
插入 刪除 搜索 插入 刪除 搜索
數組/棧/隊列 O(1) O(1) O(n) O(1) O(1) O(n)
鏈表 O(1) O(1) O(n) O(1) O(1) O(n)
雙向鏈表 O(1) O(1) O(n) O(1) O(1) O(n)
散列表 O(1) O(1) O(1) O(n) O(n) O(n)
BST樹 O(log(n)) O(log(n)) O(log(n)) O(n) O(n) O(n)
AVL樹 O(log(n)) O(log(n)) O(log(n)) O(log(n)) O(log(n)) O(log(n))

數據結構的時間複雜度

 

節點/邊的管理方式 存儲空間 增加頂點 增加邊 刪除頂點 刪除邊 輪詢
領接表 O(| V | + | E |) O(1) O(1) O(| V | + | E |) O(| E |) O(| V |)
鄰接矩陣 O(| V |2) O(| V |2) O(1) O(| V |2) O(1) O(1)

圖的時間複雜度  

 

演算法(用於數組) 時間複雜度
最好情況 一般情況 最差情況
冒泡排序 O(n) O(n2) O(n3)
選擇排序 O(n2) O(n2) O(n2)
插入排序 O(n) O(n2) O(n2)
歸併排序 O(log(n)) O(log(n)) O(log(n))
快速排序 O(log(n)) O(log(n)) O(n2)
堆排序 O(log(n)) O(log(n)) O(log(n))

排序演算法的時間複雜度

搜索演算法

  順序搜索是一種比較直觀的搜索演算法,上面介紹演算法複雜度一小節中的sequentialSearch()函數就是順序搜索演算法,就是按順序對數組中的元素逐一比較,直到找到匹配的元素。順序搜索演算法的效率比較低。

  還有一種常見的搜索演算法是二分搜索演算法。它的執行過程是:

  1. 將待搜索數組排序。
  2. 選擇數組的中間值。
  3. 如果中間值正好是要搜索的值,則完成搜索。
  4. 如果要搜索的值比中間值小,則選擇中間值左邊的部分,重新執行步驟2。
  5. 如果要搜索的值比中間值大,則選擇中間值右邊的部分,重新執行步驟2。

  下麵是二分搜索演算法的具體實現:

function binarySearch(array, item) {
    quickSort(array); // 首先用快速排序法對array進行排序

    let low = 0;
    let high = array.length - 1;

    while (low <= high) {
        const mid = Math.floor((low + high) / 2); // 選取中間位置的元素
        const element = array[mid];

        // 待搜索的值大於中間值
        if (element < item) low = mid + 1;
        // 待搜索的值小於中間值
        else if (element > item) high = mid - 1;
        // 待搜索的值就是中間值
        else return true;
    }

    return false;
}

  對應的測試結果:

const array = [8, 7, 6, 5, 4, 3, 2, 1];
console.log(binarySearch(array, 2)); // true

   這個演算法的基本思路有點類似於猜數字大小,每當你說出一個數字,我都會告訴你是大了還是小了,經過幾輪之後,你就可以很準確地確定數字的大小了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • js節點操作 整個頁面可以看成文檔節點,節點用node表示。頁面裡面全是節點,元素節點, 屬性節點,文本節點(文字,空格,換行), 節點:一定有節點類型,節點名稱,節點值 節點類型的值: 元素節點為1,屬性節點為2,文本節點為3 通過節點,可以更好的獲取元素。 邏輯全在代碼裡面 js部分 長話短說: ...
  • 個人博客主題美化 選擇主題 Hexo預設的主題是landscape,推薦以下主題: 1. "snippet" 2. "Hiero" 3. "JSimple" 4. "BlueLake" 詳見:https://github.com/search?q=hexo theme 應用主題 1. 下載主題 2. ...
  • 一.準備環境 1.安裝node.js 簡介 因為hexo是基於node.js的,所以node.js是必須要裝的,安裝也特別簡單,去 "官網" 下載適合自己windows的版本。安裝過程一直下一步就行了,什麼也不用選,在網上查資料的時候,看到有說要在Custom Setup這一步記得選 Add to ...
  • nodes.js詳細安裝 "Node.js" 本章節我們將向大家介紹在window和Linux上安裝Node.js的方法。 本安裝教程以Node.js v4.4.3 LTS(長期支持版本)版本為例。 Node.js安裝包及源碼下載地址為:https://nodejs.org/en/download/ ...
  • H5對自定義屬性的規定和添加獲取自定義屬性的方法 元素屬性那麼多,如何區分是自帶的屬性還是預設的屬性呢? H5規定自帶的屬性有個data- 首碼,如data-index="1",那麼,如何設置和獲取屬性值呢,請看下文。 <script> var divele = document.querySele ...
  • tab欄切換製作 先上圖 要求1:預設狀態,第一個選項卡被選中,展示第一個選項卡的內容 策略:第一個選項卡預設有被選中的樣式,第一個選項卡對應的display: block,其他的dispaly設為none 要求2: 選項卡模塊:被點擊的變為紅底白字,其他的變為白底紅字 策略:排他思想,每次點擊一個 ...
  • WebSocket 是 HTML5 新增的協議,它的目的是在瀏覽器和伺服器之間建立一個不受限的雙向通信的通道,比如說,伺服器可以在任意時刻發送消息給瀏覽器。 為什麼傳統的 HTTP 協議不能做到 WebSocket 實現的功能?這是因為 HTTP 協議是一個請求-響應協議,請求必須先由瀏覽器發給服務 ...
  • 由來 "推薦在這裡閱讀" js操作二進位數據三兄弟 ArrayBuffer對象, TypeArray視圖和DataView視圖 它們都以數組的語法處理二進位數據,所以統稱為二進位數組 ::: tip 二進位數組並不是真正的數組,而是類似數組的對象 ::: 設計的目的 與WebGL項目有關,WebGL ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...