排序算法

文章序

排序算法作為算法的入門抱究,也是在日常開發(fā)中十分常用的铝条,故在此整理出十大排序算法,方便自己和需要的朋友查閱

先定義交換函數(shù)

  //不使用額外空間交換算法,不能自己跟自己換
  function swap(array, i, j) {
    if (i === j) {
      return array;
    }
    let arr = array;
    arr[i] = arr[i] + arr[j];
    arr[j] = arr[i] - arr[j];
    arr[i] = arr[i] - arr[j];
    return arr;
  }

  //使用額外空間交換算法,可以自己跟自己換
  function swap(array, i, j) {
    let arr = array;
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
    return arr;
  }

1冒泡排序

原理:數(shù)組長度為 n杉辙,遍歷 n - 1 趟模捂,每趟從第 1 個(gè)元素向后兩兩比較,如果左邊的元素大于右邊的元素則將兩個(gè)元素?fù)?jù)交換蜘矢,直到左邊的元素為第 n - 1 個(gè)元素狂男,交換完畢后,此時(shí)數(shù)組最右端的元素即為該數(shù)組中最大的元素品腹。接著對該數(shù)組剩下的 n-1 個(gè)元素繼續(xù)冒泡排序岖食,直到整個(gè)數(shù)組有序排列
特性:時(shí)間復(fù)雜度:O(n^2),空間復(fù)雜度:O(1) 舞吭,穩(wěn)定排序 泡垃,原地排序
方法:兩層循環(huán)嵌套,外層共遍歷 n - 1 趟羡鸥,內(nèi)層遍歷從第 1 個(gè)元素到 n - 1 - i 個(gè)元素兔毙,進(jìn)行兩兩比較交換

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

2選擇排序

原理:遍歷 n - 1 趟,每趟遍歷找到剩余最小的元素放在前面兄春,依次類推,直到數(shù)組有序排列
特性:時(shí)間復(fù)雜度:O(n^2) 锡溯,空間復(fù)雜度:O(1) 赶舆,非穩(wěn)定排序,原地排序
方法:兩層循環(huán)嵌套祭饭,外層共遍歷 n - 1 趟芜茵,內(nèi)層遍歷從第 i 個(gè)元素開始,找到最小的元素和第 i 個(gè)元素交換

  function selectionSort(arr) {
    let res = arr;
    let n = arr.length;
    let minIndex = 0;
    for(let i = 0; i < n - 1; i++) {
      minIndex = i;
      for(let j = i + 1; j < n; j++) {
        if(res[j] < res[minIndex]) {
          minIndex = j;
        }
      }
      if(minIndex !== i) {
        res = swap(res, i, minIndex);
      }
    }
    return res;
  }

3插入排序

原理:從第 1 個(gè)元素開始倡蝙,將后面的元素與該元素比較九串,大則放在右邊,小則放在左邊寺鸥,此時(shí)該兩元素構(gòu)成數(shù)組有序猪钮,剩余元素構(gòu)成的數(shù)組無序,依次將剩余的元素向前面已有序數(shù)組中插入胆建,直到整個(gè)數(shù)組有序
特性:時(shí)間復(fù)雜度:O(n^2)烤低,空間復(fù)雜度:O(1) ,穩(wěn)定排序 笆载,原地排序
方法:兩層循環(huán)嵌套扑馁,外層共遍歷 n - 1 趟代表插入 n - 1 個(gè)元素涯呻,內(nèi)層遍歷前面已有序數(shù)組直到找到合適的插入的元素的位置

  function insertSort(arr) {
    let res = arr;
    let n = arr.length;
    for (let i = 1; i < n; i++) {
      for (let j = i - 1; j >= 0; j--) {
        if (res[j] < res[j + 1]) {
          break;
        }
        swap(res, j, j + 1);
      }
    }
    return res;
  }

4希爾排序

原理:定義增量(gap) h,將待排數(shù)組分割成為 h 個(gè)子數(shù)組分別進(jìn)行插入排序腻要,操作之后減小 h 的值复罐,再次分別進(jìn)行插入排序,直到 h = 1雄家,則再進(jìn)行最后一次插入排序效诅,完成后整個(gè)數(shù)組排序完成
特性:時(shí)間復(fù)雜度:O(nlogn) ,空間復(fù)雜度:O(1) 咳短,非穩(wěn)定排序填帽,原地排序
方法:三層遍歷,最外層每次減少增量h的值咙好,內(nèi)兩層做插入排序篡腌,注意間隔不再是 1 而是增量 h

  function shellSort(arr) {
    let res = arr;
    let n = arr.length;
    let h = parseInt(n / 2);
    while (h >= 1) {
      //進(jìn)行插入排序
      let i = 0;
      let temp = 0;
      while(temp < h) {
        for (let j = i - h; j >= 0; j = j - h) {
          if (res[j] < res[j + h]) {
            break;
          }
          swap(res, j, j + h);
        }
        i = i + h;
        if(i >= n) {
          temp++;
          i = temp;
        }
      }
      h = parseInt(h / 2);
    }
    return res;
  }

5快速排序

原理:選擇一個(gè)元素,將比他大的都放到右側(cè)勾效,比它小的都放在左側(cè)嘹悼,此時(shí)數(shù)組分成兩個(gè)數(shù)組,再次對兩個(gè)數(shù)組進(jìn)行快排层宫,這樣遞歸下去直到整個(gè)數(shù)組有序
特性:時(shí)間復(fù)雜度:O(nlogn)杨伙,空間復(fù)雜度:O(nlogn) ,非穩(wěn)定排序萌腿,原地排序
方法:設(shè)指針 cur 指向第 1 個(gè)元素開始限匣,指針 i 從 cur 指向元素向右走,指針 j 從最后一個(gè)元素向前走毁菱,j 先走米死,當(dāng) j 找到比 cur 小的元素停止,當(dāng) i 找到比 cur 大的元素停止贮庞,交換 i j指向的元素峦筒,直到i j相遇,交換此時(shí)cur 和 i 指向元素的位置窗慎,對此時(shí)指針左右兩側(cè)數(shù)組遞歸進(jìn)行快排

  function quickSort(arr) {
    let n = arr.length;
    handleSort(arr, 0, n - 1);
    return arr;
  }
  const handleSort = (arr, start, end) => {
    if (start >= end) {
      return;
    }
    let cur = start,
      i = start + 1,
      j = end;
    let mid = start;
    while (true) {
      while (j >= start && j > i) {
        if (arr[j] < arr[cur]) {
          break;
        }
        j--;
      }
      while (i <= end && i < j) {
        if (arr[i] > arr[cur]) {
          break;
        }
        i++;
      }
      if (i === j) {
        if (arr[i] < arr[cur]) {
          swap(arr, cur, i);
          mid = i;
        }
        break;
      }
      swap(arr, i, j);
    }
    handleSort(arr, start, mid - 1);
    handleSort(arr, mid + 1, end);
  };

6歸并排序

原理:將數(shù)組除2拆分物喷,直到拆分到僅有一個(gè)元素的數(shù)組,此時(shí)該數(shù)組有序遮斥,將該數(shù)組與上一次拆分的另一個(gè)數(shù)組合并峦失,此時(shí)本次拆分的數(shù)組有序,繼續(xù)向上和上一次拆分的另一個(gè)數(shù)組合并伏伐,直到整個(gè)數(shù)組合并完畢宠进,整個(gè)數(shù)組有序
特性:時(shí)間復(fù)雜度:O(nlogn),空間復(fù)雜度:O(n)藐翎,穩(wěn)定排序材蹬,非原地排序
方法:遞歸操作实幕,先定義拆分函數(shù)mergeSort,再定義合并函數(shù)merge堤器,拆分函數(shù)負(fù)責(zé)將入?yún)?shù)組除2拆分昆庇,并對拆分過的數(shù)組繼續(xù)調(diào)用拆分函數(shù),當(dāng)兩次拆分函數(shù)都調(diào)用完畢返回結(jié)果則調(diào)用合并函數(shù)

  function mergeSort(arr) {
    let n = arr.length;
    if (n < 2) {
      return arr;
    }
    let mid = parseInt(n / 2);
    let left = arr.slice(0, mid);
    let right = arr.slice(mid, n);
    let sortedLeft = mergeSort(left);
    let sortedRight = mergeSort(right);
    return merge(sortedLeft, sortedRight);
  }
  const merge = (left, right) => {
    let res = [];

    while (left.length > 0 && right.length > 0) {
      if (left[0] < right[0]) {
        res.push(left.shift());
      } else {
        res.push(right.shift());
      }
    }
    if (left.length > 0) {
      res = res.concat(left);
    }
    if (right.length > 0) {
      res = res.concat(right);
    }
    return res;
  };

7堆排序

原理:大頂堆為二叉樹數(shù)結(jié)構(gòu)闸溃,任意節(jié)點(diǎn)的值均大于左右子節(jié)點(diǎn)整吆,數(shù)組可以構(gòu)建一顆二叉樹結(jié)構(gòu),將此數(shù)組調(diào)整為二叉樹符合大頂堆的順序辉川,再依次交換第 0 個(gè)元素和第 n 個(gè)元素表蝙,并重新調(diào)整堆使之符合大頂堆定義,n依次遞減直到n = 1乓旗,此時(shí)排序完成
特性:時(shí)間復(fù)雜度:O(nlogn)府蛇,空間復(fù)雜度:O(1),非穩(wěn)定排序屿愚,原地排序
方法:parseInt(n / 2)該元素為最后一個(gè)非葉子節(jié)點(diǎn)的數(shù)據(jù)汇跨,從這里開始遍歷,構(gòu)建大頂堆妆距。大頂堆構(gòu)建完成后將第 0 個(gè)元素和最后一個(gè)元素交換穷遂,此時(shí)剩下的堆不符合大頂堆定義,調(diào)整堆使符合大頂堆定義娱据,再次交換第 0 個(gè)元素和此時(shí)最后一個(gè)元素蚪黑,循環(huán) n - 1 次結(jié)束獲得有序數(shù)組

  function heapSort(arr) {
    let n = arr.length;

    //從最后一個(gè)非葉子節(jié)點(diǎn)開始遍歷,構(gòu)建大頂堆
    for (let i = parseInt(n / 2); i >= 0; i--) {
      heapify(arr, i, n);
    }
    //大頂堆構(gòu)建完畢中剩,開始從最后一個(gè)元素開始交換
    for (let j = arr.length - 1; j > 0; j--) {
      n--;
      swap(arr, 0, j);
      heapify(arr, 0, n);
    }
    return arr;
  }
  function heapify(arr, i, n) {
    //調(diào)整堆
    let leftChild = 2 * i + 1;
    let rightChild = 2 * i + 2;
    let largest = i;

    if (leftChild < n && arr[leftChild] > arr[largest]) {
      largest = leftChild;
    }
    if (rightChild < n && arr[rightChild] > arr[largest]) {
      largest = rightChild;
    }

    if (largest !== i) {
      swap(arr, i, largest);
      heapify(arr, largest, n);
    }
  }

8計(jì)數(shù)排序

原理:開辟新的數(shù)組 newArray祠锣,長度為 k,將待排數(shù)組內(nèi)的元素值作為 newArray 的下標(biāo)咽安,將數(shù)組 newArray 從頭遍歷依次輸出賦值給原數(shù)組即完成排序
特性:時(shí)間復(fù)雜度:O(n + m),空間復(fù)雜度:O(n + m)蓬推,穩(wěn)定排序妆棒,非原地排序
方法:

  function countingSort(arr) {
    let newArray = [];
    let k = 0;
    for (let i = 0; i < arr.length; i++) {
      newArray[arr[i]] = arr[i];
    }
    for (let j = 0; j < newArray.length; j++) {
      if (newArray[j]) {
        arr[k] = newArray[j];
        k++;
      }
    }
    return arr;
  }

9桶排序

原理:創(chuàng)建多個(gè)桶空間用來存放一定范圍內(nèi)的數(shù)據(jù),每個(gè)桶內(nèi)再排序沸伏,將數(shù)據(jù)按順序從非空桶中取出糕珊,排序完成
特性:時(shí)間復(fù)雜度:O(n + m),空間復(fù)雜度:O(n + m)毅糟,穩(wěn)定排序红选,非原地排序
方法:創(chuàng)建一個(gè)數(shù)組,該數(shù)組內(nèi)元素為數(shù)組表示不同的桶

  function bucketSort(arr) {
    let n = arr.length;
    let max = arr[0];
    let min = arr[0];
    //找到最大最小值
    arr.forEach(item => {
      if (max < item) {
        max = item;
      } else if (min > item) {
        min = item;
      }
    });
    //設(shè)數(shù)組長度為桶長度姆另,獲取桶數(shù)量
    let bucketCount = parseInt((max - min) / n);
    let bucketList = new Array(bucketCount + 1);
    for (let i = 0; i < bucketList.length; i++) {
      bucketList[i] = [];
    }
    //將每一個(gè)數(shù)據(jù)放入到合適的桶
    for (let j = 0; j < n; j++) {
      let index = parseInt((arr[j] - min) / n);
      bucketList[index].push(arr[j]);
    }
    //對每一個(gè)桶進(jìn)行排序喇肋,這里選擇計(jì)數(shù)排序
    bucketList.forEach(bucket => {
      countingSort(bucket);
    });
    for (let k = 0; k < bucketList.length; k++) {
      if (k === 0) {
        arr = [];
      }
      arr = arr.concat(bucketList[k]);
    }
    return arr;
  }

10基數(shù)排序

原理:將數(shù)組按照從低位到高位的順序排序坟乾,當(dāng)每一趟都排序完成之后整個(gè)數(shù)組有序
特性:時(shí)間復(fù)雜度:O(nm),空間復(fù)雜度:O(n + m)蝶防,穩(wěn)定排序甚侣,非原地排序
方法:先找到最大的數(shù)并獲取其位數(shù) n,然后從個(gè)位開始向上遍歷间学,共 n 次殷费,每次遍歷獲取該數(shù)當(dāng)前位的數(shù)并和桶里的數(shù)的當(dāng)前位的數(shù)比較,沒有當(dāng)前位則補(bǔ) 0

  function radixSort(arr) {
    let max = arr[0];
    //找到最大的數(shù)并獲取其位數(shù)
    arr.forEach(number => {
      if (number > max) {
        max = number;
      }
    });
    const n = max.toString().length;
    // let divide = Math.pow(10, n);
    let divide = 1;

    for (let i = 0; i < n; i++) {
      let bucket = new Array(arr.length);
      for (let j = 0; j < arr.length; j++) {
        let tempNum = parseInt(arr[j] / divide);
        while (tempNum >= 10) {
          tempNum %= 10;
        }
        for (let k = 0; k < bucket.length; k++) {
          if (bucket[k]) {
            let tempBucketNum = parseInt(bucket[k] / divide);
            while (tempBucketNum >= 10) {
              tempBucketNum %= 10;
            }
            if (tempNum < tempBucketNum) {
              bucket.splice(k, 0, arr[j]);
              bucket.pop();
              break;
            }
          } else {
            bucket[k] = arr[j];
            break;
          }
        }
      }
      divide *= 10;
      arr = bucket;
    }
    return arr;
  }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末低葫,一起剝皮案震驚了整個(gè)濱河市详羡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嘿悬,老刑警劉巖实柠,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鹊漠,居然都是意外死亡主到,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門躯概,熙熙樓的掌柜王于貴愁眉苦臉地迎上來登钥,“玉大人,你說我怎么就攤上這事娶靡∧晾危” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵姿锭,是天一觀的道長塔鳍。 經(jīng)常有香客問我,道長呻此,這世上最難降的妖魔是什么轮纫? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮焚鲜,結(jié)果婚禮上掌唾,老公的妹妹穿的比我還像新娘。我一直安慰自己忿磅,他們只是感情好糯彬,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著葱她,像睡著了一般撩扒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吨些,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天搓谆,我揣著相機(jī)與錄音炒辉,去河邊找鬼。 笑死挽拔,一個(gè)胖子當(dāng)著我的面吹牛辆脸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播螃诅,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼啡氢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了术裸?” 一聲冷哼從身側(cè)響起倘是,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袭艺,沒想到半個(gè)月后搀崭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猾编,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年瘤睹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片答倡。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轰传,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘪撇,到底是詐尸還是另有隱情获茬,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布倔既,位于F島的核電站恕曲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏渤涌。R本人自食惡果不足惜佩谣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望实蓬。 院中可真熱鬧稿存,春花似錦、人聲如沸瞳秽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽练俐。三九已至,卻和暖如春冕臭,著一層夾襖步出監(jiān)牢的瞬間腺晾,已是汗流浹背燕锥。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悯蝉,地道東北人归形。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像鼻由,于是被迫代替她去往敵國和親暇榴。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容