链表操作

337 阅读7分钟

链表

链表常用技巧大多数可以通过画图来更直观的解决。

206. 反转链表

题目描述

反转一个单链表。

例子1

Input: 1->2->3->4->5->NULL

output: 5->4->3->2->1->NULL

进阶:
进阶: 你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

思考

1 比较简单,直接反转就可以

参考实现1

实现1

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */

// Input: 1->2->3->4->5->NULL
// Output: 5->4->3->2->1->NULL

// Runtime: 84 ms, faster than 67.00% of JavaScript online submissions for Reverse Linked List.
// Memory Usage: 40.6 MB, less than 37.61% of JavaScript online submissions for Reverse Linked List.

const resver = (head, newHead) => {
  if (head === null) {
    return newHead;
  }
  const temp1 = head.next;
  head.next = newHead;
  newHead = head;
  head = temp1;
  return resver(head, newHead);
};

var reverseList = function (head) {
  let newHead = null;
  return resver(head, newHead);
};
export default (head) => {
  let newHead = null;
  //return resver(head, newHead);
  while (head) {
    const temp1 = head.next;
    head.next = newHead;
    newHead = head;
    head = temp1;
  }
  return newHead;
};

21. 合并两个有序链表

题目描述

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

例子1

Input: l1 = [1,2,4], l2 = [1,3,4]

output: [1,1,2,3,4,4]

例子2

Input: l1 = [], l2 = []

output: []

思考

1 比较简单

参考实现1

2 递归实现

参考实现2

实现1

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
function ListNode(val, next) {
  this.val = val === undefined ? 0 : val;
  this.next = next === undefined ? null : next;
}
// Runtime: 84 ms, faster than 92.56% of JavaScript online submissions for Merge Two Sorted Lists.
// Memory Usage: 40.8 MB, less than 10.66% of JavaScript online submissions for Merge Two Sorted Lists.
export default (l1, l2) => {
  let newHead = new ListNode();
  let head = newHead;
  while (l1 && l2) {
    let val;
    if (l1.val <= l2.val) {
      val = l1.val;
      l1 = l1.next;
    } else {
      val = l2.val;
      l2 = l2.next;
    }
    const temp = new ListNode(val);
    newHead.next = temp;
    newHead = temp;
  }
  if (l1) {
    newHead.next = l1;
  }
  if (l2) {
    newHead.next = l2;
  }
  return head.next;
};

实现2

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
function ListNode(val, next) {
  this.val = val === undefined ? 0 : val;
  this.next = next === undefined ? null : next;
}

const rescur = (l1, l2, newHead) => {
  if (l1 && l2) {
    let val;
    if (l1.val <= l2.val) {
      val = l1.val;
      l1 = l1.next;
    } else {
      val = l2.val;
      l2 = l2.next;
    }
    const temp = new ListNode(val);
    newHead.next = temp;
    newHead = temp;
    rescur(l1, l2, newHead);
  }
  if (l1 && !l2) {
    newHead.next = l1;
  }
  if (l2 && !l1) {
    newHead.next = l2;
  }
};

export default (l1, l2) => {
  let newHead = new ListNode();
  let head = newHead;
  rescur(l1, l2, newHead);
  return head.next;
};

24. 两两交换链表中的节点

题目描述

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

例子1

Input: head = [1,2,3,4]

output: [2,1,4,3]

例子2

Input: l1 = [], l2 = []

output: []

思考

1 比较简单

参考实现1

实现1

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */

function ListNode(val, next) {
  this.val = val === undefined ? 0 : val;
  this.next = next === undefined ? null : next;
}
const rescrue = (pre, head) => {
  if (head) {
    let next = head.next;
    if (!next) {
      return;
    }
    pre.next = next;
    head.next = next.next;
    next.next = head;
    rescrue(head, head.next);
  }
};
// Runtime: 76 ms, faster than 79.57% of JavaScript online submissions for Swap Nodes in Pairs.
// Memory Usage: 39 MB, less than 12.95% of JavaScript online submissions for Swap Nodes in Pairs.
export default (head) => {
  if (!head || !head.next) {
    return head;
  }
  let pre = new ListNode();
  const currentHead = pre;

  rescrue(pre, head);
  return pre.next;
};

160. 相交链表

题目描述

编写一个程序,找到两个单链表相交的起始节点。

例子1

Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3


output: 8

提示: 1 不能修改链表的结构

思考

1 本来想先反转A和B链表,然后从反转之后的开头开始寻找,但是提示不能修改链表结构,所以不行。

后来想起来龟兔赛跑,好像不能用

后来看了下题解,原来是使用两个指针,分别遍历A和B两个链表,当一个指针走到一个链表的末尾之后,指针重新指向另外一个链表的开头,这样到最后两个指针走过的距离就是相同的。

参考实现1

实现1

// Runtime: 128 ms, faster than 18.76% of JavaScript online submissions for Intersection of Two Linked Lists.
// Memory Usage: 46 MB, less than 77.97% of JavaScript online submissions for Intersection of Two Linked Lists.
export default (headA, headB) => {
  let copyA = headA;
  let copyB = headB;

  let count = 0;
  while (headA && headB && count < 3) {
    if (headA === headB) {
      return headA;
    }
    headA = headA.next;
    headB = headB.next;
    if (!headA) {
      headA = copyB;
      ++count;
    }
    if (!headB) {
      headB = copyA;
      ++count;
    }
  }

  return null;
};


时间复杂度O(m+n) 空间复杂度O(1)

234. 回文链表

题目描述

请判断一个链表是否为回文链表。

例子1
Input: 1->2
output: false

例子2
Input: 1->2->2->1
output: true

思考

1 找到中间节点,然后使用栈对比就可以了
参考实现1

实现1

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {boolean}
 */
// Runtime: 100 ms, faster than 23.11% of JavaScript online submissions for Palindrome Linked List.
// Memory Usage: 42 MB, less than 65.81% of JavaScript online submissions for Palindrome Linked List.
export default (head) => {
  let count = 0;
  let copyHead = head;
  while (head) {
    ++count;
    head = head.next;
  }
  let half;
  if (count % 2 === 0) {
    half = count / 2 + 1;
  } else {
    half = Math.ceil(count / 2) + 1;
  }
  let curHead = copyHead;
  const stack = [];
  while (half > 1) {
    --half;
    stack.push(curHead.val);
    curHead = curHead.next;
  }
  if (count % 2 !== 0) {
    stack.pop();
  }
  while (curHead) {
    if (curHead.val !== stack.pop()) {
      return false;
    }
    curHead = curHead.next;
  }
  return true;
};

83. 移除链表中的重复元素

题目描述

移除链表中的重复元素。

例子1
Input: head = [1,1,2]
output: [1,2]

例子2
Input: head = [1,1,2,3,3]
output: [1,2,3]

思考

1 直接删除就可以了
参考实现1

实现1

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
//  Runtime: 84 ms, faster than 94.39% of JavaScript online submissions for Remove Duplicates from Sorted List.
//  Memory Usage: 40.7 MB, less than 24.89% of JavaScript online submissions for Remove Duplicates from Sorted List.
export default (head) => {
  let copyHead = head;
  while (head) {
    const next = head.next;
    if (!next) {
      return copyHead;
    }
    if (head.val === next.val) {
      head.next = next.next;
    } else {
      head = next;
    }
  }
  return copyHead;
};

时间复杂度O(n),空间复杂度O(1)

328. 奇偶链表

题目描述

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

例子1
Input: 1->2->3->4->5->NULL
output: 1->3->5->2->4->NULL

例子2
Input: 2->1->3->5->6->4->7->NULL
output: 2->3->6->7->1->5->4->NULL

说明:
应当保持奇数节点和偶数节点的相对顺序。
链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推

思考

1 链接偶数和奇数就可以,不过细节很多,需要注意细节
参考实现1

实现1

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
// Runtime: 96 ms, faster than 36.86% of JavaScript online submissions for Odd Even Linked List.
// Memory Usage: 41.3 MB, less than 19.30% of JavaScript online submissions for Odd Even Linked List.
export default (head) => {
  let copyOddHead = head;
  if (!head) {
    return head;
  }
  let copytEvenHead = head.next;
  let temp = copytEvenHead;

  while (copytEvenHead) {
    const next = copytEvenHead.next;
    if (!next) {
      head.next = temp;
      break;
    }
    head.next = next;
    head = next;

    copytEvenHead.next = head.next;
    copytEvenHead = head.next;

    if (!head.next) {
      head.next = temp;
      break;
    }
  }

  return copyOddHead;
};

19. 删除链表的倒数第 N 个结点

题目描述

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:你能尝试使用一趟扫描实现吗?

例子1
Input: head = [1,2,3,4,5], n = 2
output: [1,2,3,5]

例子2
Input: head = [1], n = 1
output: []

思考

1 使用快慢指针先找出倒数第n个的位置,这里有个技巧,通过增加一个头结点,你会发现逻辑清晰很多。
参考实现1

实现1

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
function ListNode(val, next) {
  this.val = val === undefined ? 0 : val;
  this.next = next === undefined ? null : next;
}
// Runtime: 104 ms, faster than 11.59% of JavaScript online submissions for Remove Nth Node From End of List.
// Memory Usage: 39.9 MB, less than 91.59% of JavaScript online submissions for Remove Nth Node From End of List.
export default (head, n) => {
  const tempHead = new ListNode();
  tempHead.next = head;
  head = tempHead;

  const copyHead = head;
  let p = head;
  while (n > 0) {
    --n;
    p = p.next;
  }
  while (p && p.next) {
    p = p.next;
    head = head.next;
  }

  const temp = head.next;
  head.next = temp.next;
  return copyHead;
};

148. 排序链表

题目描述

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

例子1
Input: head = [4,2,1,3]
output: [1,2,3,4]

例子2
Input: head = [-1,5,3,4,0]
output: [-1,0,3,4,5]

思考

1 直接使用归并排序就可以。
参考实现1

实现1

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
function ListNode(val, next) {
  this.val = val === undefined ? 0 : val;
  this.next = next === undefined ? null : next;
}
const merge = (l1, l2) => {
  let head = new ListNode();
  let p = head;
  while (l1 && l2) {
    if (l1.val <= l2.val) {
      p.next = l1;
      l1 = l1.next;
    } else {
      p.next = l2;
      l2 = l2.next;
    }
    p = p.next;
  }
  if (l1) {
    p.next = l1;
  }
  if (l2) {
    p.next = l2;
  }
  return head.next;
};
// Runtime: 140 ms, faster than 86.37% of JavaScript online submissions for Sort List.
// Memory Usage: 54.6 MB, less than 37.62% of JavaScript online submissions for Sort List.
const sortList = (head) => {
  if (head === null || head.next === null) {
    return head;
  }
  let pre;
  let slow = head;
  let fast = head;

  while (fast && fast.next != null) {
    pre = slow;
    slow = slow.next;
    fast = fast.next.next;
  }
  pre.next = null;
  let l1 = sortList(head);
  let l2 = sortList(slow);
  return merge(l1, l2);
};

export default sortList;