线性结构——排序和查找

排序和查找

排序算法

原地排序:在排序过程中不产生新的数组,以下所示排序算法都是原地排序

排序算法没有优劣之分,在不同的场景中,不同的排序算法执行效率不同。

  1. 冒泡排序 Bubble Sort

一次冒泡排序,可以将某个区域序列的最大值排序到该区域的最后一位,具体的方式是:

  1. 将第1位和第2位比较,如果前者比后者大则交换
  2. 将第2位和第3位比较,如果前者比后者大则交换
  3. 依次类推,直到比较到该区域的最后两位
  4. 重复上述过程,直到序列排序完成

一次冒泡过程如下
在这里插入图片描述

冒泡排序效率一般,在各种情况下不会太好,也不至于太差
如果数组本身是比较有序的,冒泡排序还是不错的

程序

//交换函数
function swap(arr, a, b) {
    var temp = arr[b];
    arr[b] = arr[a];
    arr[a] = temp;
}

//冒泡排序
function bubbleSort(arr) {
    for (var i = 0; i < arr.length - 1; i++) {
        //一共冒泡arr.length-1次
        for (var j = 0; j <= arr.length - i - 2; j++) {
            //第i次冒泡要将~o-arr.length-i-2相邻比较
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1);
            }
        }
    }
}
  1. 插入排序 Insertion Sort

将序列分为两个部分,一部分是有序的,一部分是无序的,现在要做的是,就是不断的从无序的部分取出数据,加入到有序的部分,直到整个排序完成

例如:序列[5, 7, 2, 3, 6]

  1. 分为有序的序列和无序的序列 (5) (7 2 3 6)
  2. 不断的扩充有序序列 (5 7) (2 3 6)
  3. 不断的扩充有序序列 (2 5 7) (3 6)
  4. 不断的扩充有序序列 (2 3 5 7) (6)
  5. 不断的扩充有序序列 (2 3 5 6 7)
  6. 排序完成

原理动画如下:
在这里插入图片描述

插入排序在小规模的数组中效率很高,如果数组本身是比较有序的,则效率会进一步提升(从后往前,如果第i次排序,i比i-1大,那么此次就不用排序,提高了效率)

V8引擎在数组比较小的时候会使用插入排序

程序

//插入排序
function insertSort(arr) {
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] < arr[i - 1]) {
            //只有arr[i]<arr[i-1]的情况才需要操作
            //此时搞定i~0
            var temp = arr[i];
            //先把arr[i]拿出来,等找到它的插入位置时再将其放入
            for (var j = i; j >= 0; j--) {
                if (j > 0 && temp < arr[j - 1]) {
                    //j前面有东西,并且比temp大
                    arr[j] = arr[j - 1];
                } else {
                    break;
                }
            }
            arr[j] = temp;
        }

    }
}
  1. 快速排序 Quick Sort

递归的最底部情形,是数列的大小是零或一
选择一个数(比如序列的最后一位)作为基准数,将整个序列排序成两部分,一部分比该数小,另一部分比该数大,基准数在中间,然后对剩余的序列做同样的事情,直到排序完成.整个排序过程可以递归进行,以此达到整个数据变成有序序列.

例如:序列[5, 7, 2, 3, 6, 4]

  1. 选择4作为基准数,排序成为:(3, 2) 4 (7, 6, 5)
  2. 对于3,2, 继续使用该方式排序,得到: (2, 3) 4 (7,6,5)
  3. 对于7,6,5,继续使用该方式排序,得到: (2, 3) 4 (5,6,7)
  4. 排序完成

当数组比较大并且比较混乱的时候,使用快速排序效率很高
V8引擎在数组较大时会自动使用快速排序

程序

//快速排序
function quickSort(arr) {
    function _quickSort(left, right) {
        if (left < 0 || right > arr.length - 1 || left > right) {
            return;
        }
        //选择最右边的为基准数
        var key = arr[right];
        //left,right保持不变,故用low和high表示移动的指针
        var low = left, high = right;
        //当low和high指针相同时结束循环,把arr[low]或arr[high]赋值为key
        while (low < high) {
            while (arr[low] < key && low < high) {
                low++;
            }
            //当low所指大于key时,将其值赋给high所指的值,实现放到右边的操作
            arr[high] = arr[low];
            while (arr[right] > key && low < high) {
                high--;
            }
            arr[low] = arr[high];
        }
        //保证循环结束后,low===high
        arr[low] = key;

        //搞定左边
        _quickSort(left, low - 1);
        //搞定右边
        _quickSort(low + 1, right);
    }
    _quickSort(0, arr.length - 1);
    //一开始,在0~最大坐标之间进行排序
}

查询算法

  1. 顺序查找 Inorder Search

即普通的遍历,属于算法的穷举法,没啥好解释的

  1. 二分查找 Binary Search

如果一个序列是一个排序好的序列,则使用二分查找可以极大的缩短查找时间

具体的做法是:

查找该序列中间未知的数据

  1. 相等,找到
  2. 要找的数据较大,则对后续部分的数据做同样的步骤
  3. 要找的数据较小,则对前面部分的数据做同样的步骤

程序

//二分查找

var num = 0;//测试查找的次数

function binarySearch(arr, target) {
    function _binarySearch(left, right) {
        num ++;
        if (left === right) {
            return arr[left] === target;
        }
        if (left < 0 || right > arr.length - 1 || left > right) {
            return false;
        }
        var mid = Math.floor((left + right) / 2);
        if (arr[mid] === target) {
            return true;
        }
        if (arr[mid] > target) {
            //从左边继续寻找
            return _binarySearch(left, mid - 1);
        }
        if (arr[mid] < target) {
            //从右边继续寻找
            return _binarySearch(mid + 1, right);
        }
    }
    return _binarySearch(0, arr.length - 1);
}
  1. 插值查找 Interpolation Search

插值查找是对二分查找的进一步改进,二分查找是看中间位置,插值查找是根据目标值定位到其可能在的下标

如果序列不仅是一个排序好的序列,而且序列的步长大致相同,使用插值查找会更快的找到目标。

插值查找基于如下假设:下标之间的距离比和数据之间的距离比大致相同,即:

(目标下标-最小下标) / (最大下标 - 最小下标) ≈ (目标值 - 最小值) / (最大值 - 最小值)

因此可以算出大致的下标落点:

目标下标 ≈ (目标值 - 最小值) / (最大值 - 最小值) * (最大下标 - 最小下标) + 最小下标

这样就可以计算出大致的下标落点,后续的比较和二分查找一样。

程序

//插值查找
function interpolationSearch(arr, target) {
    function _interpolationSearch(left, right) {
        num ++;
        if (left === right) {
            return arr[left] === target;
        }
        if (left < 0 || right > arr.length - 1 || left > right) {
            return false;
        }
        var mid = Math.floor((target-arr[left])*(right-left)/(arr[right]-arr[left])+left);
        //判断一下算出的下标值mid有无异常
        if(mid < left || mid > right){
            return false;
        }
        if (arr[mid] === target) {
            return true;
        }
        if (arr[mid] > target) {
            //从左边继续寻找
            return _interpolationSearch(left, mid - 1);
        }
        if (arr[mid] < target) {
            //从右边继续寻找
            return _interpolationSearch(mid + 1, right);
        }
    }
    return _interpolationSearch(0, arr.length - 1);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值