溫故而知新,最新在複習數據結構和算法,結合Chrome的V8源碼,看看JS中的一些實現。
首先咱們看看字符串查找在V8中是使用的哪一種算法。
咱們知道JS中的String是繼承於Object,源碼以下:javascript
// 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; }
咱們先看看各瀏覽器的排序算法是不是穩定排序java
在V8 v7.0/Chrome70之後,源碼不在包含/src/js目錄,相應的遷移到了/src/torque
關於V8 Torque的詳情能夠參考V8 Torquegit
// 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; }
// 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); } }