leetcode 算法题(链表):删除排序链表中的重复元素、旋转链表、反转链表 II、删除链表的倒数第n个节点

545 阅读3分钟

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

image.png

解题思路

  • 定义pre和cur两个相邻的指针
  • 每个指针向后移动一位,判断两个节点的值
  • 如果不相等,同时向后移动一位
  • 如果相等,cur指针向后移动一位,pre节点next指向新的cur节点
/**
 * 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}
 */
var deleteDuplicates = function(head) {
    if(!head || !head.next) {
        return head;
    }
    let pre = head;
    let cur = head.next;
    while(cur) {
        if(cur.val === pre.val) {
            cur = cur.next;
            pre.next = cur;
        } else {
            cur = cur.next;
            pre = pre.next;
        }
    }
    return head;
};

61. 旋转链表

image.png

解题思路
  • 将链表尾节点连接到头节点,组成一个环
  • 找到旋转后的链表头节点的前一个节点
  • 拆环,返回新的头节点
/**
 * 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} k
 * @return {ListNode}
 */
var rotateRight = function(head, k) {
 if (!head || !head.next) {
    return head;
  }
  let tail = head;
  let len = 1;
  // 查找链表的最后一个节点,将tail 的next 指向head
  while (tail.next) {
    tail = tail.next;
    len++;
  }
  // 此时tail 指向最后一个节点,将tail的next属性指向head
  tail.next = head;
 // 将向右旋转变为向左旋转
  let n = len - (k % len);
  let pre = head;
  let ret = head;

  // 用户--那目的少旋转一个,便于操作
  while (--n) {
    pre = pre.next;
  }
  // ret 指向新的头节点
  ret = pre.next;
  // 将环拆开
  pre.next = null;
  return ret;
};

92. 反转链表 II

image.png

解题思路
  • 找到需要翻转的头节点的前一个节点
  • reverve函数: 翻转以头节点开始的k个节点 返回包含后面未翻转部分的新链表
  • 链接前半部分和部分新的链表
/**
 * 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} left
 * @param {number} right
 * @return {ListNode}
 */
var reverseBetween = function(head, left, right) {
    if(!head || !head.next) {
        return head;
    }
    if(right -left ===0) {
        return head;
    }
    // 虚拟头节点
    const ret = new ListNode(null,head);

    // 查找left 的前一个节点
    let l = left;
    let pre = ret;
    while(--l) {
        pre = pre.next;
    }

    const reserve = (head,n) => {
        if(!head) {
            return null;
        }
        let pre = null;
        let cur = head;
        // 翻转前k个节点
        while(cur && n-- > 0) {
            const next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        // head: 翻转部分的尾部节点,pre: 翻转部分的头节点
        // cur: 剩余部分的头节点
        head.next = cur;
        return pre;
    }
    // 此时pre为需要翻转节点的前一个节点
    // reserve 返回之后部分的链表,且left 到 right 部分已经被翻转
    pre.next = reserve(pre.next,right - left + 1);
    return ret.next;
};

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

image.png

解题思路

如何找到倒数第n个节点

  • 定义第一个指针 front,先向后走n步,
  • 定义第二个指针 after, front 和 after 一同向后走
  • 当 front走到链表尾部节点时,after 指向的就是倒数第n个节点 技巧: 当节点有很大概率会改变时,需要用到虚拟头节点的技巧, 就是在头部定义一个虚拟的链表节点,next指针指向head
/**
 * 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}
 */
var removeNthFromEnd = function(head, n) {
      if (!head) {
    return null;
  }
  // 定义虚拟头节点
  let ret = new ListNode(null, head);
  let front = ret;
  while (n--) {
    front = front.next;
  }

  // 定义after指针,然后两个指针一起向后走
  let after = ret;
  while (front.next) {
    after = after.next;
    front = front.next;
  }
  // 此时after,指向倒数第n个节点的前一个节点
  after.next = after.next.next;
  return ret.next;
};