持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
算法设计期末考试要来了,借用这个机会手写一下那些我会的排序算法,常见的算法有哈希排序,冒泡排序、插入排序、计数排序、归并排序、快速排序、选择排序等。
哈希排序
这个可以说是我最开始学排序的时候学的了,其思想很简单就是借助数组的下标是有序的来进行排序,我们直接来看实现:
let arr=[1,5,6,4,9,31];
function haxiSort(arr){
let tf=new Array(Math.max(...arr)+1).fill(false);
arr.forEach(item => {
tf[item]=true;
});
for(let i=0;i<tf.length;i++){
tf[i]&&console.log(i);
}
}
haxiSort(arr);
代码很简单,我们只要通过判断这个数组是否出现过就可以,但是这个排序需要通过消耗大量的空间,同时如果数组存在相同的元素的时候就会出现问题。
计数排序
这个可以说是哈希排序的升级版了,由于我们前面说的哈希排序只能相同的元素,所以我们转变到计数排序来解决这个问题,其主要思想也是借助数组的下标是有序的来进行排序,但是区别于哈希的是其哈希数组存放的是数字不再是Boolean类型,这样我们就可以统计数字出现的次数。我们来看下代码:
let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function coutSort(arr) {
let tf = new Array(Math.max(...arr) + 1).fill(0);
arr.forEach((item) => {
tf[item]++;
});
for (let i = 0; i < tf.length; i++) {
for (let j = 0; j < tf[i]; j++) {
console.log(i);
}
}
}
coutSort(arr);
这样我们就完成了我们的计数排序。同时它还有另外一个优势,有的时候我们的数据不是直接通过数组存放,而是读入的形式给你的(如c中的scanf),但是给的数据量特别大,数据范围特别小,这个时候也可以使用我们的计数排序来进行排序,这样我们声明的存放数据就可以以范围大小来定大小,而不是数据量。
直接选择排序
let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function selectSort(arr){
for(let i=0;i<arr.length-1;i++) {
let index=i;
for(let j=i+1;j<arr.length;j++){
if(arr[i]>arr[j]){
index=j;
}
}
let tem=arr[i];
arr[i]=arr[index];
arr[index]=tem;
}
}
selectSort(arr);
console.log(arr);
这个排序的主要思想就是每次都找剩余数组中的最大/最小值,直到倒数第二个的最大值/最小值找到后,那么整个数组就有序了。
直接插入排序
这种排序算法适合数组已经基本有序,其基本思想是在原本已经有序的数组中插入新的元素,我们直接上代码:
let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function insertSort(arr){
for(let i=1;i<arr.length;i++){
if(arr[i-1]>arr[i]){
let value=arr[i];
let index=i-1;
do{
arr[index+1]=arr[index];
index--;
}while(index>0&&arr[index]>=value);
arr[index+1]=value;
}
}
}
insertSort(arr);
console.log(arr);
我们通过不断覆盖的方式找到新加入元素的位置,从局部有序到最后的全局有序。
冒泡排序
这可以说是最经典的一种排序算法了,其关键思想就是寻找数组中的逆序对,然后将其进行交换直到数组中不存在逆序对即可,我们来看一下它的代码:
let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function bubollSort(arr){
let sjh=arr.length-1;
do{
let jh=0;
for(let i=0;i<sjh;i++){
if(arr[i]>arr[i+1]){
let tem=arr[i];
arr[i]=arr[i+1];
arr[i+1]=tem;
jh=i;
}
}
sjh=jh;
}while(sjh!==0);
}
bubollSort(arr);
console.log(arr);
注意这里我们每次查找逆序对只用找到上次最后交换的地方,而不用遍历整个数组。
快速排序
这是一种基于分治思想的排序算法,主要思想是确定一个数的位置,后将数组以该位置一分为二进行同样的递归处理,知道整个数组有序:
let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function quickSort(arr, left, right) {
if (left < right) {
let mid = findPos(arr, left, right);
quickSort(arr, left, mid - 1);
quickSort(arr, mid + 1, right);
}
}
function findPos(arr, left, right) {
let temValue = arr[left];
while (left < right) {
while (left < right && arr[right] >= temValue) {
right--;
}
arr[left] = arr[right];
while (left < right && arr[left] <= temValue) {
left++;
}
arr[right] = arr[left];
}
arr[left] = temValue;
return left;
}
quickSort(arr, 0, arr.length - 1);
console.log(arr);
归并排序
这也是基于分治思想的一种排序算法,其基本思想是将数组不断的分组,对最小组进行排序后再进行合并,得到最终有序的数组:
let arr = [1, 5, 6, 5, 5, 5, 5, 5, 4, 9, 31];
function mergeSort(arr, left, right) {
if (left < right) {
let mid = Math.trunc((left + right) / 2);
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, mid + 1, right);
}
}
function merge(arr, left1, right1, left2, right2) {
let temArr = arr.slice(left1, right1 + 1);
let i=0;
while(left2<=right2&&i<temArr.length){
if(temArr[i]<arr[left2]){
arr[left1++]=temArr[i];
i++;
}else{
arr[left1++]=arr[left2++];
}
}
if(i!==temArr.length){
for(let j=i;j<temArr.length;j++){
arr[left1++]=temArr[j];
}
}else{
for(let j=left2;j<right2;j++){
arr[left1++]=arr[j];
}
}
}
mergeSort(arr,0,arr.length-1);
console.log(arr);