本文用 JavaScript 實現六大基礎算法與單鏈表數據結構,代碼簡潔、註釋詳細,適合前端工程師快速建立算法直覺。
function linearSearch(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) return i;
}
return -1;
}
適用於無序、小規模數組;算法界的 Hello World。
function binarySearch(arr, target) {
let low = 0, high = arr.length - 1;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
if (arr[mid] === target) return mid;
if (target < arr[mid]) high = mid - 1;
else low = mid + 1;
}
return -1;
}
前提:數組已按升序排列。每輪排除一半,極快。
function bubbleSort(arr) {
for (let i = 0; i < arr.length; i++) {
let sorted = true;
for (let j = arr.length - 1; j > i; j--) { // 內層倒序,大元素「冒泡」到右側
if (arr[j] < arr[j - 1]) {
[arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]; // ES6 解構交換
sorted = false;
}
}
if (sorted) break; // 本輪無交換,提前結束
}
}
function selectionSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
let minIdx = i;
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIdx]) minIdx = j;
}
if (minIdx !== i) [arr[i], arr[minIdx]] = [arr[minIdx], arr[i]];
}
return arr;
}
每輪選最小,交換次數最少(O(n)),但比較次數仍 O(n²)。
function insertionSort(arr) {
for (let i = 1; i < arr.length; i++) {
const x = arr[i];
let j = i - 1;
while (j >= 0 && arr[j] > x) {
arr[j + 1] = arr[j]; // 向右移動
j--;
}
if (j + 1 !== i) arr[j + 1] = x; // 插入空位
}
return arr;
}
類似整理撲克牌:假設左側有序,將當前牌插入合適位置。近似有序數組極快(O(n))。
function reverseString(str) {
const arr = str.split('');
let i = 0, j = arr.length - 1;
while (i < j) {
[arr[i], arr[j]] = [arr[j], arr[i]];
i++; j--;
}
return arr.join('');
}
雙指針原地交換,空間 O(1)(不計輸出)。
| 算法 | 穩定性 | 備註 |
|---|---|---|
| 冒泡、插入、選擇 | 穩定 | 基於比較的簡單排序通常穩定 |
| 歸並、堆、快速、桶 | 不穩定 | 進階算法(O(n log n))通常不穩定,除非特殊實現 |
穩定**:相等元素相對順序不變。對多關鍵字排序至關重要。
// 節點類
class Node {
constructor(val) {
this.data = val;
this.next = null;
}
}
// 單鏈表類
class SinglyLinkedList {
constructor() {
this.head = new Node(null); // 哑節點,僅佔位
}
// 尾插
insert(val) {
let p = this.head;
while (p.next) p = p.next;
p.next = new Node(val);
}
// 刪除第 n 個節點(頭節點為第 0 位)
removeAt(n) {
if (n <= 0) return;
const pre = this.getNodeByIndex(n - 1);
if (pre.next) pre.next = pre.next.next;
}
// 獲取第 n 個節點
getNodeByIndex(n) {
let p = this.head, i = 0;
while (p.next && i < n) { p = p.next; i++; }
return p;
}
// 按值查找(返回首個匹配節點)
getNodeByValue(val) {
let p = this.head;
while (p.next) {
p = p.next;
if (p.data === val) return p;
}
return null;
}
// 打印所有節點
print() {
let p = this.head, out = '';
while (p.next) { p = p.next; out += p.data + ' '; }
console.log(out.trim());
}
}
// 測試
const list = new SinglyLinkedList();
['A','B','C','D'].forEach(v => list.insert(v));
list.print(); // A B C D
// 檢測重複元素
function hasDuplicate(list) {
let i = list.head;
while (i.next) {
i = i.next;
let j = i;
while (j.next) {
j = j.next;
if (i.data === j.data) return true;
}
}
return false;
}
// 鏈表反轉(借助數組)
function reverseList(list) {
const arr = [];
let p = list.head;
while (p.next) { p = p.next; arr.push(p.data); }
const newList = new SinglyLinkedList();
for (let i = arr.length - 1; i >= 0; i--) newList.insert(arr[i]);
return newList;
}
const reversed = reverseList(list);
reversed.print(); // D C B A
Array.sort()(V8 採用 Timsort,O(n log n) 穩定)。