数据结构与算法--链表二

207 阅读4分钟

文章简要概述

  • 本文主要是进行链表相关的算法题刷题题解记录,带你进一步了解链表相关算法如何解。
  • 将接着上一篇文章数据结构与算法--链表一的内容;
  • 这篇文章主要有以下六个题目的刷题题解,K个一组翻转链表旋转链表两两交换链表中的节点删除链表的倒数第 N 个结点删除排序链表中的重复元素删除排序链表中的重复元素 II

与链表相关算法

K个一组翻转链表

K个一组翻转链表--leetcode

题目大意:

  给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
  k 是一个正整数,它的值小于或等于链表的长度。
  如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

reverse_ex1.jpeg

输入: head = [1,2,3,4,5], k = 2
输出: [2,1,4,3,5]

解题思路:

  • 首先这道题和之前反转链表的题中有相同部分,特别点在与指定了链表区间进行反转。
  • 我首先将链表按照k的数量进行切分,然后反转,再与原链表切分的首尾部分链表。
  • 需要借助一个虚拟头节点的概念,在原链表头节点之前在加一个我们自己定义的节点进行链接。
  • 定义虚拟头节点hair,prev指针,从虚拟头节点开始,tail指针是待反转k个节点的位置,nex是tai指针的下一个节点,即k个节点反转区域为head到tail节点,prev和nex为反转区域的首尾连接节点。

9.png

代码:

 function reverse (prev, cur) {
     if (!cur) return prev;
     const tem = cur.next;
     cur.next = prev;
     return reverse(cur, tem);
 }
 function reverseKGroup (head, k) {
     const VNode = new ListNode(-1);
     VNode.next = head;
     let prev = VNode;
     while (head) {
         let tail = prev;
         for (i = 0; i < k; i++) {
             tail = tail.next;
             if (!tail) {
                 return VNode.next;
             }
         }
         const succ = tail.next;
         const leftNode = prev.next;
         prev.next = null;
         tail.next = null;
         reverse(null, leftNode);
         prev.next = tail;
         leftNode.next = succ;
         head = succ;
         prev = leftNode;
     }
     return VNode.next;
  };

旋转链表

旋转链表--leetcode

题目大意: 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k **个位置。

rotate1.jpeg

输入: head = [1,2,3,4,5], k = 2
输出: [4,5,1,2,3]

解题思路:

  • 假设链表的长度为n,向右移动n的倍数相当于在原位置(没有移动)
  • 当移动k >= n时,我们仅需要向右移动k%n次即可。
  • 将链表首尾相连形成闭合的环,再将链表向右移动(n-1)-(k%n)个节点就是链表的最后一个节点,然后断开链接即可得到。

代码:

  function rotateRight (head, k) {
      if (!head || !head.next || k === 0) return head;
      let n = 1;
      let cur = head;
      while(cur.next) {
          cur = cur.next;
          n++;
      }
      let removeNum = n - k % n;
      if (removeNum === n) {
          return head;
      }
      cur.next = head;
      while(removeNum) {
          cur = cur.next;
          removeNum--;
      }
      const result = cur.next;
      cur.next = null;
      return result;
  };

两两交换链表中的节点

两两交换链表中的节点--leetcode

题目大意: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

swap_ex1.jpeg

输入: head = [1,2,3,4]
输出: [2,1,4,3]

解题思路:

  • 这道题和前面的指定k个一组反转链表其实是一样的,只要将k写成2就可以实现了
  • 这道题可以采用简单的写法,因为是固定的数字。如下图,先定义一个虚拟头节点,指向head,定义一个中间节点tem,指向node2,node1的next指向node2的next,node2的next再指向node1,反复进行上面步骤,直到node1和node2不存在为止即可得到答案

6.png

**代码: **

   function swapPairs (head) {
       const VNode = new ListNode(-1);
       VNode.next = head;
       let prev = VNode;
       while(prev.next && prev.next.next) {
           const node1 = prev.next;
           const node2 = prev.next.next;
           prev.next = node2;
           node1.next = node2.next;
           node2.next = node1;
           prev = node1;
       }
       return VNode.next;
    };

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

删除链表的倒数第 N 个结点 -- leetcode

题目大意: 给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点

remove_ex1.jpeg

输入: head = [1,2,3,4,5], n = 2
输出: [1,2,3,5]

解题思路:

  • 方法一: 求的链表的长度L,倒数n的位置等于 L-n+1的位置,将这个位置指针向下next.next位,即可删除倒数n位的节点
  • 方法二:设置两个指针,两个指针相隔n个节点,当first指针到达链表的尾部时,second节点到达待删除节点前一位,设置其指针位second.next.next,即可删除节点。

p3.png

代码:

    // 方法一: 链表长度 - n + 1的位置就是要删除的节点
    function getLength (node) {
        let n = 0;
        while (node.next) {
            node = node.next
            n++;
        }
        return n;
    }
    var removeNthFromEnd = function(head, n) {
        const l = getLength(head);
        const VNode = new ListNode(-1);
        VNode.next =head;
        let prev = VNode;
        for (let i = 0; i < l - n + 1; i++) {
           prev = prev.next;
        }
        prev.next = prev.next?.next;
        return VNode.next;

    };
    
    // 方法二 双指针,两指针间隔n个节点
    function removeNthFromEnd (head, n) {
        const VNode = new ListNode(-1);
        VNode.next =head;
        let first = VNode;
        let second = VNode;
        for (let i = 0; i < n; i++) {
           second = second.next;
        }
        while(second?.next) {
            first = first.next;
            second = second.next;
        }
        first.next = first.next.next;
        return VNode.next;
    };

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

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

题目大意: 存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 

list1.jpeg

输入: head = [1,1,2]
输出: [1,2]

解题思路:

  • 链表是升序的,因此相同的数值时连续出现的
  • 只要比较cur指针与cur.next指针的值,如果值相同,将cur的next指向cur.next.next,然后重复当前操作,直到cur的next不存在为止,即可得到。

截屏2021-12-12 下午10.06.18.png 代码:

    function deleteDuplicates (head) {
        if (!head) return head;
        let cur = head;
        while(cur.next) {
            if (cur.val === cur.next.val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return head;
    };

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

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

题目大意: 存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 **的数字。

linkedlist1.jpeg

输入: head = [1,2,3,3,4,4,5]
输出: [1,2,5]

解题思路:

  • 链表是升序的,因此相同的数值时连续出现的
  • 这道题与上一题的区别是,重复数值本身也会被删除,因此,头节点也可能被删除,需要借助虚拟头节点。
  • 只要比较cur指针与cur.next指针的值,如果值相同,记录当前值,将cur的指针向后移,继续比较,如果仍相同,next指向cur.next.next。如果不相同,cur指向向后移动,然后重复当前操作,直到cur.next和cur.next.next不存在为止,即可得到。

代码:

   function deleteDuplicates (head) {
       const VNode = new ListNode(-1, head);
       let cur = VNode;
       while(cur?.next?.next) {
           if (cur.next.val === cur.next.next.val) {
               const val = cur.next.val;
               while(cur?.next?.val === val) {
                   cur.next = cur.next.next;
               }
           } else {
               cur = cur.next;
           }
       }
       return VNode.next;
   };

结束语

数据结构与算法相关的练习题会持续输出,一起来学习,当前是链表部分,后期还会有其他类型的数据结构,题目来源于leetcode。

往期文章: 数据结构与算法-链表一

如果文章对你有帮助,欢迎点赞,关注!

文中图片资源来源于leetcode