力扣刷题之路-----链表问题

好久没有更新力扣刷题的一些收获了,最近换成了labuladong的算法小抄来学习算法,在此还是只记录自己觉得有难度以及有启发的题目!

涉及题目:

 23. 合并K个升序链表

       

  题目仍然是很好理解,在两个链表进行排序的时候还比较简单。多个链表的时候,首先考虑到题目的要求是排好序,那么就需要使用到可以排序的数据结构。使用队列可以很方便地存取节点,且可以进行排序,则考虑使用队列来存储链表节点。使用队列来存储节点数据,根据节点的next获取该节点的下一个节点。

   public ListNode mergeKLists(ListNode[] lists) {
      if(lists.length==0) return null;
      ListNode head = new ListNode(-1);
      ListNode p = head;
      PriorityQueue<ListNode> queue = new PriorityQueue<>(lists.length, new Comparator<ListNode>() {
        @Override
        public int compare(ListNode o1, ListNode o2) {
          return o1.val-o2.val;
        }
      });
      for (ListNode list :lists){
        if(list!=null) queue.add(list);
      }
      while (!queue.isEmpty()){
        ListNode temp = queue.poll();
        p.next = temp;
        if(temp.next!=null){
          queue.add(temp.next);
        }
        p=p.next;
      }
      return head.next;
    }

 160. 相交链表

 

   如果只用两个指针完成这个题的话,存在一个问题是指针什么时候往后,那个指针往后才能同时到相交的节点。这个题解看不懂,只能背思路了。

  把两个链表拼接起来,假设p1和p2两个指针分别在两条链表上前进,让p1遍历完链表1之后开始遍历链表2,让p2遍历完链表2后开始遍历链表1。这样二者相遇的地方就是两个链表相交的地方。图示↓

 

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode fastA=headA,fastB=headB;
        while (fastA!=fastB){
            if(fastA==null) fastA=headB;
            else fastA=fastA.next;
            if(fastB==null) fastB=headA;
            else fastB=fastB.next;
        }
        return fastA;
    }

 

 206. 反转链表

 

   链表的反转可以有两种方法实现:迭代和递归

  迭代方法的思想很简单也很容易实现,要对链表进行反转,那么后一个节点的next就要指向前一个节点,此时需要有一个指针来记录前一个节点,同时也需要链表按照原来的顺序前进。原来的头结点反转后是最后一个节点,该节点的next是null,所以刚开始的时候初始化“前节点”为null。

  public ListNode reverseList(ListNode head) {
        ListNode pre = null;//指向前一个节点
        ListNode curr = head;//指向当前节点
        while (curr!=null){
            ListNode next = curr.next;
            curr.next = pre;
            pre = curr;
            curr = next;
        }
        return pre;
    }

  递归方法的实现(结合图示看)首先找到最后一个节点,即head.next=null的节点,由于递归的函数参数是head.next,返回最后head时相当于返回的是4,此时将5的next指向4,即head.next.next=head;每次向前返回的时候cur都是5。单纯的文字解释比较困难,此处放一个题解的链接,感觉讲的是比较容易懂的。动画演示+多种解法 206. 反转链表 - 反转链表 - 力扣(LeetCode)

    public ListNode reverseList(ListNode head) {
        if(head==null || head.next==null){
            return head;
        }
        ListNode cur = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return cur;
    }    

 92. 反转链表 II

 

   当需要反转一部分链表时,需要记录反转部分的前一个节点和反转部分的后一个节点,即图上2节点前的节点和4节点后的节点。当left=1时,相当于反转前right个节点,这个反转的算法使用递归法来写的话比较好些,即

    public ListNode reverseList(ListNode head,int n){
        if(n==1){
            successor = head.next;
            return head;
        }
        ListNode cur = reverseList(head.next,n-1);
        head.next.next = head;
        head.next = successor;
        return cur;
    }

  当n等于1的时候,已经走到了图上4所在的节点,这个时候需要记录4后的节点是什么,使用successor来记录,并且每次都讲head.next赋值为successor,这个赋值和null是一样的,只不过把null替换成了另一节点。

  当left != 1时。我们只需要找到开始位置,即忽略前面的多余节点,仍旧按照left=1来计算。图示如下。left不等于1时,需要先向后遍历,将head.next的索引视为1的时候,反转的区间是从left-1开始的,即相对于2节点,left=2;相对于3节点,left=1。由于最后需要将反转链表和原链表连接起来,需要记录开始的前一个节点,即图中的2节点,所以设置head.next满足left=1的条件时,则可以直接按照上边的代码反转。代码如下。

 

 

class Solution {
    ListNode successor = null;//后驱节点
    public ListNode reverseBetween(ListNode head, int left, int right) {
        if(left==1){
            return reverseList(head,right);
        }
        head.next = reverseBetween(head.next,left-1,right-1);
        return head;

    }
    public ListNode reverseList(ListNode head,int n){
        if(n==1){
            successor = head.next;
            return head;
        }
        ListNode cur = reverseList(head.next,n-1);
        head.next.next = head;
        head.next = successor;
        return cur;
    }
}

 

总结:链表的题感觉好难啊 很多题目都比较难转过来弯。多多练习 多写几遍打算背一下了

posted @ 2022-05-19 21:47  啵啵ray  阅读(63)  评论(0编辑  收藏  举报