# 数组

数组(Array)是一种分配一块连续内存空间存储相同类型元素的线性表数据结构。

优点是允许通过索引(下标)随机访问元素。时间复杂度为 O(1)。

那么数组为什么能够快速随机访问到元素呢?因为计算机可将数组索引通过以下寻址公式映射为真实的内存地址,从而直接访问到存储在内存中的数据。

数组元素内存地址 = 首个数组内存地址 + 索引 * 数据类型大小

缺点是插入和删除操作低效。

在数组末尾插入或删除元素时,其时间复杂度为 O(1);在数组开头插入元素时,如果数组是有序的,首先需要将数组所有元素往后移动一位,然后将插入元素插入到数组第一位,时间复杂度为 O(n)。如果数组是无序的,则可将插入位置的元素搬移到最后一位,然后将新元素插入到之前元素搬移留下的位置,时间复杂度为 O(1)。

在数组开头删除元素时,首先需要将删除元素删除,然后将删除元素后面的所有元素都往前移动一位,时间复杂度为 O(n)。同时删除很多元素时,为避免大量重复搬移,我们可以标记需要删除的元素,当数组没有更多空间存储数据时,才真正删除所以被标记的元素,这样删除的效率将会得到提升。

插入和删除任意位置元素的平均时间复杂度为 O(n)。

数组作为最基础的原始数据结构之一,可以用来实现栈、队列、哈希表等其他高级的数据结构,也可以用来实现编程语言中的数组数据类型。

在 JavaScript 语言中,数组数据类型的底层实现原理并未遵循数组数据结构的定义,而是根据存储数据的不同,选择不同的实现方式。如果数组储存的是相同类型的数据,会分配一块连续的内存空间来存储数据;如果是不同类型的数据,并不会连续存储在内存中,而是使用类似哈希表的结构存储不同类型的数据。

# 动态扩容数组

class DynamicArray {
  constructor(n) {
    this.arr = new Array(n).fill(0);
    this.size = n;
    this.length = 0;
  }
  insert(v) {
    if (this.length >= this.size) { // 扩容
      const temp = this.arr;
      this.size *= 2;
      this.arr = new Array(this.size).fill(0);
      for (let i = 0; i < temp.length; i++) {
        this.arr[i] = temp[i];
      }
    } 
    this.arr[this.length++] = v;
  }
}

const arr = new DynamicArray(10);
for (let i = 1; i <= 12; i++) {
  arr.insert(i);
}

# 固定有序数组

class FixedOrderedArray {
  constructor(capacity) {
    this.arr = new Array(capacity).fill(0);
    this.count = 0;
    this.size = capacity;
  }
  insert(element) {
    if (this.count >= this.size) return -1; // 处理越界
    
    // 保证有序性
    let j = this.count;
    for (let i = 0; i < j; i++) {
      if (this.arr[i] > element) {
        let temp = element;
        while (i < j) {
          this.arr[j] = this.arr[j - 1];
          j--;
        }
        this.arr[j] = temp;
        this.count++;
      }
    }

    if (this.count <= j)
      this.arr[this.count++] = element;
  }
  remove(element) {
    for (let i = 1; i <= this.count; i++) {
      if (this.arr[i - 1] === element) {
        while (i < this.count) {
          this.arr[i - 1] = this.arr[i];
          i++;
        }
        this.arr[i - 1] = 0;
        this.count--;
      }
    }
  }
  modify(index, element) {
    if (index < 0 || index >= this.count) return -1; // 处理越界
    this.arr[index] = element;

    // 保证有序性
    for (let i = index + 1; i < this.count; i++) {
      if (this.arr[i - 1] > this.arr[i]) {
        [this.arr[i - 1], this.arr[i]] = [this.arr[i], this.arr[i - 1]];
      }
    }
  }
}

const orderedArray = new FixedOrderedArray(5);
orderedArray.insert(2);    // => [2, 0, 0, 0, 0]
orderedArray.insert(4);    // => [2, 4, 0, 0, 0]
orderedArray.insert(1);    // => [1, 2, 4, 0, 0]
orderedArray.insert(5);    // => [1, 2, 4, 5, 0]
orderedArray.insert(3);    // => [1, 2, 3, 4, 5]
orderedArray.insert(6);    // => [1, 2, 3, 4, 5]
orderedArray.remove(3);    // => [1, 2, 4, 5, 0]
orderedArray.remove(1);    // => [2, 4, 5, 0, 0]
orderedArray.remove(5);    // => [2, 4, 0, 0, 0]
orderedArray.modify(0, 6); // => [4, 6, 0, 0, 0]

# 合并两个有序数组

function merge(arr1, arr2) {
  const m = arr1.length, n = arr2.length;
  const arr = [];
  let count = 0;

  let i = 0, j = 0;
  while (i < m || j < n) {
    if (arr1[i] === undefined) {
      arr[count++] = arr2[j];
      j++;
    } else if (arr2[j] === undefined) {
      arr[count++] = arr1[i];
      i++;
    } else if (arr1[i] <= arr2[j]) {
      arr[count++] = arr1[i];
      i++;
    } else {
      arr[count++] = arr2[j];
      j++;
    }
  }

  return arr;
}
    
const arr1 = [1,3,5], arr2 = [0,2,4,6];
merge(arr1, arr2); // => [0,1,2,3,4,5,6]

# 参考

更新时间: 7/4/2022, 9:30:01 PM