链表经典面试题

93 阅读3分钟

构造链表

通过倒叙 arr[len-1]然后不断 curNode进行赋值next是当前curNode(倒推思想)

class Node {
  constructor(value) {
    this.val = value
    this.next = undefined
  }
}
class NodeList  {
  constructor(arr) {
    if (arr.length) {
      const len = arr.length
      let curNode = {
        value: arr[len - 1]
      }
      for (let i = len - 2; i >= 0; i--) {
        curNode = {
          value: arr[i],
          next: curNode
        }
      }
      return curNode
    }
  }
}

链表排序

  • swap交互位置
  • sort函数双指针思想-找出目标位置-不断进行左右互换
  • 递归终止标志 if (begin !== end) 进行结束
  • 内部partion函数进行内部 while循环 大小比较 swap交换位置 返回p
var swap = (p, q) => {
  let val = p.val
  p.val = q.val
  q.val = val
}
var partion = (begin, end) => {
  let val = begin.val
  let p = begin
  let q = begin.next
  while (q !== end) {
    if (q.val < val) {
      swap(p.next, q)
      p = p.next
    }
    q = q.next
  }
  swap(p, begin)
  return p
}
var sort = (begin, end) => {
  if (begin !== end) {
    let part = partion(begin, end)
    sort(begin, part)
    sort(part.next, end)
  }
}

var head = new NodeList([4, 1, 3, 2, 7, 9, 9, 10, 12, 6])
sort(head)
var res = []
var next = head
while (next) {
  res.push(next.val)
  next = next.next
}

核心代码解析

  • 首先有一个基准值进行大小比较-一般是第一个值 begin.val
  • p慢指针-一般从第一个开始-用户和快指针交换-作为最后的基准点返回
  • q块指针一般从第二个开始-和一开始的基准值val进行比较
  • q指针小就和p指针交换那个值的位置
  • 交换之后我们就可以保证在p指针左边都是小于一开始的基准值 val的
var partion = (begin, end) => {
  let val = begin.val
  let p = begin
  let q = begin.next
  while (q !== end) {
    if (q.val < val) {
      swap(p.next, q)
      p = p.next
    }
    q = q.next
  }
  swap(p, begin)
  return p
}

环形链表

image.png

var isCircle = (head) => {
  let slow = head
  let fast = head.next
  while(1) {
    if(!fast||!fast.next) {
      return false
    } else if (fast===slow || fast.next === slow) {
      return true
    } else {
      slow = slow.next
      fast = fast.next.next
    }
  }
}
var head = new NodeList([6,1,2,5,7,9])
head.next.next.next.next.next.next = head.next
isCircle(head)

核心思想解析

  • fast指针走的快 slow指针走的慢
  • fast指针先进入这个位置 碰上或者说fast在slow后面说明有环
  • fast走完都没有相等的时候说明没环

开挂

var isCircle = (head) => {

  try {

    JSON.stringify(head)

    return false

  } catch(e) {

    return true

  }

}

js反转链表

// 

function reverseLink (link) {
  let prevNode = undefined
  let curNode = undefined
  let nextNode = link
  while (nextNode) {
    if (curNode && !prevNode) {
      delete curNode.next
    }

    if (curNode && prevNode) {
      curNode.next = prevNode
    }

    prevNode = curNode
    curNode = nextNode
    nextNode = nextNode?.next
  }
  curNode.next = prevNode
  return curNode
}

核心解析

  • 每次把当前指针指向前一个指针完成链表反转
  • 定义三个指针 前指针 先指针 后指针
  • 首先每一次这三个指针都会按顺序进行替换前进一个指针

临界值处理

  • 遍历到最后nextNode为undefined的时候
  • 当前指针 curNode到最后一个位置还没有反指prevNode
  • 如果!prevNode为false 说明curNode才来到第一个
  • 这部分的之前指向的prevNode是undefined-应当删除避免死循环
  • 解析来有值的时候就进行正常的反指curNode.next = prevNode

删除链表中的重复节点


function deleteNode(pHead) {
  if (pHead == null || pHead.next == null) return pHead;
  const head = {  next: pHead };
  let pre = head;
  let last = head.next;
  while (last) {
    if (last.next && last.val === last.next.val) {
      while (last.next && last.val === last.next.val) {
        last = last.next;
      }
      pre.next = last.next;
      last = last.next;
    } else {
      pre = pre.next;
      last = last.next;
    }
  }
  return head.next;
}

核心解析

  • 删除链表节点-就是让这个节点失去引用被垃圾回收
  • 指向不重复的那个节点-那么前面的那个重复节点就会被垃圾回收
  • 创建pre和last指针
  • last指针不断往后后进行比较last.val和last.next.val进行比较是否相等-
  • 如果相等是重复节点-一直循环找不到不重复节点
  • prev每次都指向不重复的节点last

临界值

  • last&&last.next
  • 最后return的时候注意reutrn head.next才是我们要返回的节点