Web高级 JavaScript中的算法
算法
排序算法
- 稳定排序
待排序序列中相等元素在排序完成后,原有先后顺序不变。 - 非稳定排序
- 有序度
待排序序列中有序关系的元素对个数。 - 逆序度
1. 插入排序
- 遍历有序数组,对比待插入的元素大小,找到位置。把该位置后的元素依次后移。
- 时间复杂度: O(N2)
2. 选择排序
- 区分已排序区间和未排序区间,每次从未排序区间选择最小的放在已排序区间的最后。
- 时间复杂度: O(N2)
3. 归并排序
- 将待排序元素从中间分为二半,对左右分别递归排序,最后合并在一起。
- 思想: 分治思想
- 时间复杂度: O(nLogN)
- 常见实现: 递归
- 特点: 非原地排序,需要借助额外存储空间,在数据量较大时空间复杂度较高。
4. 快速排序
- 选择一个pivot分区点,将小于pivot的数据放到左边,大于的放到右边,在用同样方法递归排序左右。
- 思想: 分治思想
- 时间复杂度: O(nLogN)
- 常见实现: 递归
- 特点: 非稳定原地排序,另外pivot选择比较重要,常见的有随机选择,前中后三值取中值等。
5. 桶排序
- 将待排序数据根据值区间划分m个桶,再对桶内进行排序,最后合并
- 要求: 待排序数据值范围能比较轻松划分为m个区间(桶)
- 思想: 分治思想
- 时间复杂度: O(n)
- 场景: 外部排序, 如TB级别数据,设置m个桶,将符合值区间的元素分别放入不同桶,再对桶分别排序
6. 基数排序
- 使用稳定排序算法,从最后一位开始进行排序。
- 要求: 带排序数据可以分割出独立的'位'来进行比较,而且位之间有递进关系
- 时间复杂度: O(m*n)
- 场景: 电话号码,英文字典排序等
7. 二分查找
- 待查找数据与中间数对比,若小与则在左边递归二分,若大于则在右边递归二分
- 要求:待查找集合为有序'数组'
- 时间复杂度: O(LogN)
- 场景:数据量不能太大,也不能太小
JavaScript字符串查找算法
温故而知新,最新在复习数据结构和算法,结合Chrome的V8源码,看看JS中的一些实现。
首先我们看看字符串查找在V8中是使用的哪种算法。
我们知道JS中的String是继承于Object,源码如下:
// https://github.com/v8/v8/blob/master/src/objects/string.cc
// Line942 字符串查找方法定义
int String::IndexOf(Isolate* isolate, Handle<String> receiver,Handle<String> search, int start_index) {
...
// 省略多余代码,根据返回值可见调用SearchString方法
// Line968
return SearchString<const uc16>(isolate, receiver_content, pat_vector,start_index);
}
// Line18
#include "src/strings/string-search.h"
// 根据头引入查找该文件
// https://github.com/v8/v8/blob/master/src/strings/string-search.h
// 重点来了
// Line 20
*根据以下注释我们可以知道, JS中的字符串查找使用的BM查找算法。模式串最小匹配长度为7
class StringSearchBase {
protected:
// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
// limit, we can fix the size of tables. For a needle longer than this limit,
// search will not be optimal, since we only build tables for a suffix
// of the string, but it is a safe approximation.
static const int kBMMaxShift = Isolate::kBMMaxShift;
// Bad-char shift table stored in the state. It's length is the alphabet size.
// For patterns below this length, the skip length of Boyer-Moore is too short
// to compensate for the algorithmic overhead compared to simple brute force.
static const int kBMMinPatternLength = 7;
};
// 重点的重点 Line 54
template <typename PatternChar, typename SubjectChar>
class StringSearch : private StringSearchBase {
public:
StringSearch(Isolate* isolate, Vector<const PatternChar> pattern)
: isolate_(isolate),
pattern_(pattern),
start_(Max(0, pattern.length() - kBMMaxShift)) {
if (sizeof(PatternChar) > sizeof(SubjectChar)) {
if (!IsOneByteString(pattern_)) {
strategy_ = &FailSearch;
return;
}
}
// 获取模式串字符长度
int pattern_length = pattern_.length();
// 如果小于7
// static const int kBMMinPatternLength = 7;
if (pattern_length < kBMMinPatternLength) {
if (pattern_length == 1) {
//如果待查找字符串长度为1,使用单字符查找
strategy_ = &SingleCharSearch;
return;
}
//否则使用线性查找
strategy_ = &LinearSearch;
return;
}
// 如果大于7,使用BM查找算法
strategy_ = &InitialSearch;
}
JavaScript数组排序算法
我们先看看各浏览器的排序算法是否是稳定排序
- IE6+: stable
- Firefox < 3: unstable
- Firefox >= 3: stable
- Chrome < 70: unstable
- Chrome >= 70: stable
- Opera < 10: unstable
- Opera >= 10: stable
- Safari 4: stable
- Edge: unstable for long arrays (>512 elements)
在V8 v7.0/Chrome70以后,源码不在包含/src/js目录,相应的迁移到了/src/torque
关于V8 Torque的详情可以参考V8 Torque
我们先看看Chrome70以前的Array.prototype.sort的实现
// https://github.com/v8/v8/blob/6.9.454/src/js/array.js
// Line 802
// 以下可以知道sort方法返回InnerArraySort结果
DEFINE_METHOD(
GlobalArray.prototype,
sort(comparefn) {
if (!IS_UNDEFINED(comparefn) && !IS_CALLABLE(comparefn)) {
throw %make_type_error(kBadSortComparisonFunction, comparefn);
}
var array = TO_OBJECT(this);
var length = TO_LENGTH(array.length);
return InnerArraySort(array, length, comparefn);
}
);
// Line645
// 我们接下来看InnerArraySort的定义
function InnerArraySort(array, length, comparefn) {
// In-place QuickSort algorithm.
// For short (length <= 10) arrays, insertion sort is used for efficiency.
// 原地快排算法
// 如果长度小于10,使用插入排序
function InsertionSort(a, from, to) {
for (var i = from + 1; i < to; i++) {
var element = a[i];
for (var j = i - 1; j >= from; j--) {
var tmp = a[j];
var order = comparefn(tmp, element);
if (order > 0) {
a[j + 1] = tmp;
} else {
break;
}
}
a[j + 1] = element;
}
};
// ...省略部分代码
function QuickSort(a, from, to) {
var third_index = 0;
while (true) {
// Insertion sort is faster for short arrays.
if (to - from <= 10) {
InsertionSort(a, from, to);
return;
}
// ...省略部分代码
if (to - high_start < low_end - from) {
QuickSort(a, high_start, to);
to = low_end;
} else {
QuickSort(a, from, low_end);
from = high_start;
}
}
};
if (length < 2) return array;
QuickSort(array, 0, num_non_undefined);
return array;
}
快排所带来的问题
- 众所周知快排是非稳定排序算法,由此带来了很多问题
- V8在7.0以后将JS的数组排序更改为稳定排序算法
- 在V8的博客上有一篇详细的介绍关于排序算法更改的文章
再看看Chrome70以后的排序实现
// https://github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/third_party/v8/builtins/array-sort.tq
// Line1236
transitioning macro
ArrayTimSortImpl(context: Context, sortState: SortState, length: Smi) {
if (length < 2) return;
let remaining: Smi = length;
// March over the array once, left to right, finding natural runs,
// and extending short natural runs to minrun elements.
let low: Smi = 0;
const minRunLength: Smi = ComputeMinRunLength(remaining);
while (remaining != 0) {
let currentRunLength: Smi = CountAndMakeRun(low, low + remaining);
// If the run is short, extend it to min(minRunLength, remaining).
// 当前执行长度小于最小长度
if (currentRunLength < minRunLength) {
const forcedRunLength: Smi = SmiMin(minRunLength, remaining);
//使用插入排序
BinaryInsertionSort(low, low + currentRunLength, low + forcedRunLength);
currentRunLength = forcedRunLength;
}
// Push run onto pending-runs stack, and maybe merge.
PushRun(sortState, low, currentRunLength);
MergeCollapse(context, sortState);
// Advance to find next run.
low = low + currentRunLength;
remaining = remaining - currentRunLength;
}
//其他时候使用归并排序
MergeForceCollapse(context, sortState);
assert(GetPendingRunsSize(sortState) == 1);
assert(GetPendingRunLength(sortState.pendingRuns, 0) == length);
}
// Line485
// BinaryInsertionSort is the best method for sorting small arrays: it
// does few compares, but can do data movement quadratic in the number of
// elements. This is an advantage since comparisons are more expensive due
// to calling into JS.
//
// [low, high) is a contiguous range of a array, and is sorted via
// binary insertion. This sort is stable.
//
// On entry, must have low <= start <= high, and that [low, start) is
// already sorted. Pass start == low if you do not know!.
macro BinaryInsertionSort(implicit context: Context, sortState: SortState)(
low: Smi, startArg: Smi, high: Smi) {
assert(low <= startArg && startArg <= high);
const workArray = sortState.workArray;
let start: Smi = low == startArg ? (startArg + 1) : startArg;
for (; start < high; ++start) {
// Set left to where a[start] belongs.
let left: Smi = low;
let right: Smi = start;
const pivot = workArray.objects[right];
// Invariants:
// pivot >= all in [low, left).
// pivot < all in [right, start).
assert(left < right);
// Find pivot insertion point.
while (left < right) {
const mid: Smi = left + ((right - left) >> 1);
const order = sortState.Compare(pivot, workArray.objects[mid]);
if (order < 0) {
right = mid;
} else {
left = mid + 1;
}
}
assert(left == right);
// The invariants still hold, so:
// pivot >= all in [low, left) and
// pivot < all in [left, start),
//
// so pivot belongs at left. Note that if there are elements equal
// to pivot, left points to the first slot after them -- that's why
// this sort is stable. Slide over to make room.
for (let p: Smi = start; p > left; --p) {
workArray.objects[p] = workArray.objects[p - 1];
}
workArray.objects[left] = pivot;
}
}
// Regardless of invariants, merge all runs on the stack until only one
// remains. This is used at the end of the mergesort.
transitioning macro
MergeForceCollapse(context: Context, sortState: SortState) {
let pendingRuns: FixedArray = sortState.pendingRuns;
// Reload the stack size becuase MergeAt might change it.
while (GetPendingRunsSize(sortState) > 1) {
let n: Smi = GetPendingRunsSize(sortState) - 2;
if (n > 0 &&
GetPendingRunLength(pendingRuns, n - 1) <
GetPendingRunLength(pendingRuns, n + 1)) {
--n;
}
MergeAt(n);
}
}
Web高级 JavaScript中的算法的更多相关文章
- Web高级 JavaScript中的数据结构
复杂度分析 大O复杂度表示法 常见的有O(1), O(n), O(logn), O(nlogn) 时间复杂度除了大O表示法外,还有以下情况 最好情况时间复杂度 最坏情况时间复杂度 平均情况时间复杂度 ...
- JavaScript中的算法之美——栈、队列、表
序 最近花了比较多的时间来学习前端的知识,在这个期间也看到了很多的优秀的文章,其中Aaron可能在这个算法方面算是我的启蒙,在此衷心感谢Aaron的付出和奉献,同时自己也会坚定的走前人这种无私奉献的分 ...
- 一篇文章把你带入到JavaScript中的闭包与高级函数
在JavaScript中,函数是一等公民.JavaScript是一门面向对象的编程语言,但是同时也有很多函数式编程的特性,如Lambda表达式,闭包,高阶函数等,函数式编程时一种编程范式. funct ...
- javascript数据结构与算法--高级排序算法
javascript数据结构与算法--高级排序算法 高级排序算法是处理大型数据集的最高效排序算法,它是处理的数据集可以达到上百万个元素,而不仅仅是几百个或者几千个.现在我们来学习下2种高级排序算法-- ...
- javascript数据结构与算法--高级排序算法(快速排序法,希尔排序法)
javascript数据结构与算法--高级排序算法(快速排序法,希尔排序法) 一.快速排序算法 /* * 这个函数首先检查数组的长度是否为0.如果是,那么这个数组就不需要任何排序,函数直接返回. * ...
- 用Javascript方式实现LeetCode中的算法(更新中)
前一段时间抽空去参加面试,面试官一开始让我做一道题,他看完之后,让我回答一下这个题的时间复杂度并优化一下,当时的我虽然明白什么是时间复杂度,但不知道是怎么计算的,一开局出师不利,然后没然后了,有一次我 ...
- javascript中数组的常用算法深入分析
Array数组是Javascript构成的一个重要的部分,它可以用来存储字符串.对象.函数.Number,它是非常强大的.因此深入了解Array是前端必修的功课.本文将给大家详细介绍了javascri ...
- javascript数据结构与算法--二叉树遍历(中序)
javascript数据结构与算法--二叉树遍历(中序) 中序遍历按照节点上的键值,以升序访问BST上的所有节点 代码如下: /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * ...
- javascript 中合并排序算法 详解
javascript 中合并排序算法 详解 我会通过程序的执行过程来给大家合并排序是如何排序的... 合并排序代码如下: <script type="text/javascript& ...
随机推荐
- 基于numpy的绘图
import numpy as np #import matplotlib.pyplot as plt dataset = np.loadtxt('1.csv', delimiter=",& ...
- ubuntu16.04下docker安装和简单使用(转)
ubuntu16.04下docker安装和简单使用 转自:https://www.cnblogs.com/hupeng1234/p/9773770.html 前提条件 操作系统 docker-ce ...
- [apache spark]洞见纽约车辆事故|bluemix|apache spark
今天,我们用spark 来分析 下一纽约市车辆事故的大数据. 前提条件: 1.有bluemix 帐号,并并在bluemix的dashboard里创建了一个sparck instance. 2.稳定可以 ...
- 移动端适配 rem
前置知识: 物理像素(physical pixel,device pixel) 物理像素(设备像素),显示设备中一个最微小的物理部件.每个像素可以根据操作系统设置自己的颜色和亮度. 设备独立像素(de ...
- 多个excel文件内容合并到一个excel文件的多个sheet的小程序
# -*- coding:utf-8 -*- import xlrd, xlsxwriter # 待合并excelallxls = ["D:\\excelcs\\***.xlsx" ...
- 调整数组顺序使奇数位于偶数前面(python)
题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 方法1:冒泡 O( ...
- pycharm2017版本永久激活
亲测有效,为了以后方便查看,就复制粘贴了一下 声明:转自https://blog.csdn.net/qq_34173491/article/details/81157519 目前比较好用的Python ...
- React-router的基本使用
1.安装使用 $ npm install -S react-router import { Router, Route, hashHistory } from 'react-router'; rend ...
- 浙大PAT CCCC L3-015 球队“食物链” ( 搜索 && 剪枝 )
题目链接 题意 : 有 n 个球队,给出主客场胜负图,找出一个序列 1.2.3..... 使得 1 战胜过 2 .2 战胜过 3.3 战胜过 4..... n 战胜过 1 ( 这个序列是 1~n 的其 ...
- 七牛云对象存储kodo使用体验
在这里,我使用了七牛云的对象存储Kodo,和阿里云的OSS,还有腾讯云的COS是同样的产品 oss相关术语 包依赖关系解决 unrecognized import path "golang. ...