计算机基础学习:数据结构之线性数据结构

93 阅读2分钟

文章内容主要为小册《程序员的必修课》的学习笔记以及个人理解,仅供个人学习使用。详细内容请支持正版小册。

不同的业务场景,适用不同的技术。运用合理的数据结构,降低时空复杂度,约等于成功了一半。

数据结构可以分为两大类:

  • 线性数据结构:数据元素之间的关系是一对一的。
  • 非线性数据结构:数据元素之间的关系不是一对一的。

线性数据结构

  • 顺序表:紧密相邻。便于查找元素,不便于插入和删除元素。
  • 链表:便于插入和删除,不便于查找
  • 栈:FILO 先进后出,历史记录,撤销回退
  • 队列:FIFO 先进先出,上传、下载队列

非线性数据结构

  • 数:树是一对多的数据结构。适合有层级关系的场景。比如组织架构、目录
  • 图:图是多对多的数据结构。适合没有层级的网状关系。比如通讯录,朋友圈好友信息

顺序表

class ArrayList {
  private arr: number[] = new Array(10).fill(0);

  private curIndex: number = -1;

  public put(num: number): void {
    if (++this.curIndex >= this.arr.length) {
      throw new Error('Array index out of bounds');
    }
    this.arr[this.curIndex] = num;
  }

  public get(index: number): number {
    if (index < 0 || index > this.curIndex) {
      throw new Error('Array index out of bounds');
    }
    return this.arr[index];
  }

  public add(num: number, index: number): void {
    for (let i = this.curIndex; i >= index; i--) {
      this.arr[i + 1] = this.arr[i];
    }

    this.arr[index] = num;
    this.curIndex++;
  }

  public remove(index: number): void {
    for (let i = index; i < this.curIndex; i++) {
      this.arr[i] = this.arr[i + 1];
    }
    this.curIndex--;
  }
}

const list = new ArrayList();

list.put(10);
list.put(20);
list.put(30);

console.log(list);
console.log(list.get(0));
console.log(list.get(2));

list.add(15, 1);
console.log(list);

console.log(list.get(1));

list.remove(1);

console.log(list.get(1));

image.png

链表

// 定义元素Node
class Node {
  value: number;
  next: Node | null;
  constructor(value: number, next: Node | null = null) {
    this.value = value;
    this.next = next;
  }
}

class LinkedList {
  private first: Node | null;

  constructor() {
    this.first = null;
  }

  put(num: number): void {
    const node = new Node(num, null);

    if (this.first == null) {
      this.first = node;
    } else {
      let curNode = this.first;
      while (curNode.next != null) {
        curNode = curNode.next;
      }

      curNode.next = node;
    }
  }

  get(index: number): number {
    let i = index;
    let curNode = this.first;
    while (i > 0 && curNode !== null) {
      curNode = curNode.next;
      i--;
    }
    if (curNode === null) {
      throw new Error('Index out of bounds');
    }
    return curNode.value;
  }

  add(num: number, index: number): void {
    let node = this.first;
    while (--index > 0 && node !== null) node = node.next;

    if (node === null) {
      throw new Error('Index out of bounds');
    }

    const newNode = new Node(num);
    newNode.next = node.next;
    node.next = newNode;
  }

  remove(index: number): void {
    if (index === 0) {
      if (this.first === null) {
        throw new Error('List is empty');
      }
      this.first = this.first.next;
      return;
    }

    let node = this.first;
    while (--index > 0 && node !== null) node = node.next;

    if (node === null || node.next === null) {
      throw new Error('Index out of bounds');
    }

    node.next = node.next.next;
  }
}

const linkedList = new LinkedList();
linkedList.put(3);
linkedList.put(5);
linkedList.put(7);
linkedList.put(9);
console.log('linkedList => ', linkedList);

image.png

遵循后进先出(LIFO)的原则,非常常见,这里就不举例了

队列

先进先出,也是非常常见的结构,一般用数组去实现,也可以用链表,之前有一篇文章介绍过一个小而美的库,大家可以去看一下yocto-queue源码解读:链表、迭代器与高效队列