简单

21.合并两个有序链表

方法一:递归

// m和n分别为两个链表的长度
// 时间复杂度:O(m + n)
// 空间复杂度:O(m + n),递归调用时存在栈空间开销
var mergeTwoLists = function(list1, list2) {
  if(list1 === null) {
    return list2
  } else if (list2 === null) {
    return list1
  } else if (list1.val <= list2.val) {
    list1.next = mergeTwoLists(list1.next, list2)
    return list1
  } else {
    list2.next = mergeTwoLists(list1, list2.next)
    return list2
  }
}

方法二:迭代

// m和n分别为两个链表的长度
// 时间复杂度:O(m + n)
// 空间复杂度:O(1),仅需要常量存储空间
var mergeTwoLists = function(list1, list2) {
  const dummy = new ListNode(0)
  let curr = dummy
  while(list1 !== null && list2 !== null) {
    if (list1.val <= list2.val) {
      curr.next = list1
      list1 = list1.next 
    } else {
      curr.next = list2
      list2 = list2.next
    }
    curr = curr.next
  }

  // while循环结束后,list1和list2中还有未合并的节点,直接使用
  curr.next = list1 === null ? list2 : list1
  return dummy.next
};

141.环形链表

方法一:哈希表

// n为链表的长度
// 时间复杂度:O(n),最坏情况遍历全部节点
// 空间复杂度:O(n),为哈希表的内存开销
var hasCycle = function(head) {
  if (head === null) {
    return false
  }
  const set = new Set()
  while(head !== null) {
    // 如果哈希表中存在相同的节点,则表示有环
    if(set.has(head)) {
      return true
    }
    set.add(head)
    head = head.next
  }
  return false
};

方法二:快慢指针

// n为链表的长度
// 时间复杂度:O(n),最坏情况遍历全部节点
// 空间复杂度:O(1),仅定义了两个快慢指针变量
var hasCycle = function(head) {
  if (head === null) {
    return false
  }
  // slow和fast不在同一个位置,是为了执行while循环,如果是do-while,可设置为同一个位置
  let slow = head
  let fast = head.next
  while(slow !== fast) {
    // 快指针遍历完毕时,还没有相等,则表示无环
    if (fast === null || fast.next === null) {
      return false
    }
    slow = slow.next
    fast = fast.next.next
  }
  return true
}

160.相交链表

方法一:哈希表

// m,n分别为两个链表的长度
// 时间复杂度:O(m + n),需要分别遍历两个链表
// 空间复杂度:O(m),需要把其中一个链表的节点存储到哈希表中
var getIntersectionNode = function(headA, headB) {
  const set = new Set()
  let curr = headA
  // 把第一个链表中的节点存储到哈希表中
  while(curr !== null) {
    set.add(curr)
    curr = curr.next
  }
  // 遍历第二个链表,如果存在一个节点在哈希表中,则代表其为相交节点
  curr = headB
  while(curr !== null) {
    if (set.has(curr)) {
      return curr
    }
    curr = curr.next
  }
  return null
};

方法二:双指针

// m,n分别为两个链表的长度
// 时间复杂度:O(m + n),两个链表都需要遍历一遍
// 空间复杂度:O(1),仅需要定义两个指针变量
var getIntersectionNode = function(headA, headB) {
  if (headA === null || headB === null) {
    return null
  }
  let pA = headA
  let pB = headB
  // headA遍历完毕时,从headB重新开始遍历
  // headB遍历完毕时,从headA重新开始遍历
  // while循环终止时,有相交节点的情况下,pA或者pB即为相交节点;否的话,表示两个链表已遍历完毕
  while(pA !== pB) {
    pA = pA === null ? headB : pA.next
    pB = pB === null ? headA : pB.next
  }
  return pA
}

206.反转链表

方法一:双指针

// n为链表的长度
// 时间复杂度:O(n),需要遍历链表
// 空间复杂度:O(1),定义双指针变量
var reverseList = function(head) {
  let prev = null
  let curr = head
  while(curr !== null) {
    const next = curr.next
    curr.next = prev
    prev = curr
    curr = next
  }
  return prev
};

234.回文链表

方法一:数组 + 双指针

// n为链表的长度
// 时间复杂度:O(n) + O(n/2) = O(n),一次链表遍历 + 一半数组遍历
// 空间复杂度:O(n),定义数组存储链表信息
var isPalindrome = function(head) {
  const list = []
  while(head !== null) {
    list.push(head.val)
    head = head.next
  }
  for(let i = 0, j = list.length - 1; i < j; i++, j--) {
    if (list[i] !== list[j]) {
      return false
    }
  }
  return true
};
最后更新时间:
贡献者: wangtunan