算法-链表

66 阅读3分钟

含义

多个元素组成的列表,元素存储不连续,通过 next 指针来链接, 最底层为 null

就类似于 父辈链接关系 吧, 比如: 你爷爷的儿子是你爸爸,你爸爸的儿子是你,而你假如目前还没有结婚生子,那你就暂时木有儿子

链表的优缺点

因为链表是一种 松散 的结构体,所以当你想要找到其中的某一个节点时,只能够从 头节点 一级一级的往下找,但也因为这种松散的结构使得其进行 插入删除 时只需要改变其 指针域 的指向即可

优点:适合动态插入和删除的应用场景 缺点:不能快速的定位和随机访问数据

数组和链表的对比总结

  • 数组和链表都是线性数据结构
  • 数组为静态结构,静态分配内存。链表支持动态分配内存
  • 数组在数据储存时是一段连续的内存空间,链表是非连续的通过指针来串联
  • 数组可以根据下标定位快速查找,链表则需要遍历查找
  • 数组在插入和删除时会有大量的数据移动补位,链表只需要改变指针指向

js中类似于链表的典型就是原型链, 但是js中没有链表这种数据结构,我们可以通过一个object来模拟链表

const a = {
  val: "a"
}

const b = {
  val: "b"
}

const c = {
  val: "c"
}

const d = {
  val: "d"
}

a.next = b;
b.next = c;
c.next = d;

// const linkList = {
//    val: "a",
//    next: {
//        val: "b",
//        next: {
//            val: "c",
//            next: {
//                val: "d",
//                next: null
//            }
//        }
//    }
// }

// 遍历链表
let p = a;
while (p) {
  console.log(p.val);
  p = p.next;
}

// 插入
const e = { val: 'e' };
c.next = e;
e.next = d;


// 删除
c.next = d;

合并两个有序链表

// 时间复杂度O(n) n为链表1和链表2的长度之和
// 空间复杂度O(1)
var mergeTwoLists = function (list1, list2) {
  const res = {
    val: 0,
    next: null
  }
  // 指向新链表的指针
  let p = res;
  // 建立两个指针
  let p1 = list1;
  let p2 = list2;
  while(p1 && p2) {
      if(p1.val < p2.val) {
          p.next = p1
          p1 = p1.next
      }else {
          p.next = p2
          p2 = p2.next
      }
    // p永远要往后移动一位
    p = p.next;
  }
  if(p1) {
      p.next = p1
  }
  if(p2) {
      p.next = p2
  }
  return res.next;
};

删除排序链表中的重复元素

// 1 -> 1 -> 2 -> 3 -> 3 
// 1 -> 2 -> 3 -> null

// 时间复杂度 O(n) n为链表的长度
// 空间复杂度 O(1)
const deleteDuplicates = (head) => {

  // 创建一个指针
  let p = head;

  // 遍历链表
  while (p && p.next) {

    // 如果当前节点的值等于下一个节点的值
    if (p.val === p.next.val) {

      // 删除下一个节点
      p.next = p.next.next
    } else {

      // 否则继续遍历
      p = p.next
    }
  }

  //  最后返回原来链表
  return head
}

反转链表

// 1 -> 2 -> 3 -> 4 -> 5 -> null
// 5 -> 4 -> 3 -> 2 -> 1 -> null

// 时间复杂度 O(n) n为链表的长度
// 空间复杂度 O(1)
var reverseList = function (head) {

  // 创建一个指针
  let p1 = head;

  // 创建一个新指针
  let p2 = null;

  // 遍历链表
  while (p1) {

    // 创建一个临时变量
    const tmp = p1.next;

    // 将当前节点的下一个节点指向新链表
    p1.next = p2;

    // 将新链表指向当前节点
    p2 = p1;

    // 将当前节点指向临时变量
    p1 = tmp;
  }

  // 最后返回新的这个链表
  return p2;
}

reverseList(list)

删除链表中的节点

// 时间复杂和空间复杂度都是 O(1)
const deleteNode = (node) => {
  // 把当前链表的指针指向下下个链表的值就可以了
  node.val = node.next.val;
  node.next = node.next.next
}

instanceOf

const myInstanceOf = (A, B) => {
  // 声明一个指针
  let p = A;
  
  // 遍历这个链表
  while (p) {
    if (p === B.prototype) return true;
    p = p.__proto__;
  }

  return false
}

myInstanceOf([], Object)

单向链表

class neNode {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

// 实现一个单项链表
class singleLinkedList {
  constructor() {
    this.head = null;
  }
  // 添加节点
  add(data) {
    let node = new neNode(data);
    if (this.head === null) {
      this.head = node;
    } else {
      let current = this.head;
      while (current.next) {
        current = current.next;
      }
      current.next = node;
    }
  }
  // 插入节点
  insert(data, target) {
    let node = new neNode(data);
    let current = this.head;
    while (current.next) {
      if (current.data === target) {
        node.next = current.next;
        current.next = node;
        break;
      }
      current = current.next;
    }
  }
  // 查找节点
  find(data) {
    let current = this.head;
    while (current) {
      if (current.data === data) {
        return current;
      }
      current = current.next;
    }
    return null;
  }
  // 删除节点
  remove(data) {
    let current = this.head;
    let previous = null;
    while (current) {
      if (current.data === data) {
        if (previous === null) {
          this.head = current.next;
        } else {
          previous.next = current.next;
        }
        return true;
      }
      previous = current;
      current = current.next;
    }
    return false;
  }
}
const list = new singleLinkedList();
list.add(1);
list.add(2);
list.add(3);
list.insert(4, 2);
console.dir(list, { depth: null });