《算法通关村第一关——链表经典算法题》

两个链表第一个公共子节点

链表只能有一个后继节点

在这里插入图片描述

哈希和集合

遍历第一个链表,将所有元素全部存到Map中。遍历第二个方法 当Hash中存在了每个元素,那么就找到了公共子节点。

/**
     * 方法1:通过Hash辅助查找
     *
     * @param pHead1
     * @param pHead2
     * @return
     */
    public static ListNode findFirstCommonNodeByMap(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }
        ListNode current1 = pHead1;
        ListNode current2 = pHead2;

        HashMap<ListNode, Integer> hashMap = new HashMap<ListNode, Integer>();
        while (current1 != null) {
            hashMap.put(current1, null);
            current1 = current1.next;
        }

        while (current2 != null) {
            if (hashMap.containsKey(current2)){
                return current2;
            }
            current2 = current2.next;
        }

        return null;
    }

双指针

由图可知,两个链表的后方是相同的,那么如果我链表长度长的先出发,走到长度差之后,再一起走,是不是就能遇到公共子节点了。

/**
     * 方法5:通过差值来实现
     *
     * @param pHead1
     * @param pHead2
     * @return
     */
    public static ListNode findFirstCommonNodeBySub(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }
        ListNode current1 = pHead1;
        ListNode current2 = pHead2;
        int l1 = 0, l2 = 0;
        while (current1 != null) {
            current1 = current1.next;
            l1++;
        }

        while (current2 != null) {
            current2 = current2.next;
            l2++;
        }
        current1 = pHead1;
        current2 = pHead2;

        int sub = l1 > l2 ? l1 - l2 : l2 - l1;

        if (l1 > l2) {
            int a = 0;
            while (a < sub) {
                current1 = current1.next;
                a++;
            }
        }

        if (l1 < l2) {
            int a = 0;
            while (a < sub) {
                current2 = current2.next;
                a++;
            }
        }

        while (current2 != current1) {
            current2 = current2.next;
            current1 = current1.next;
        }

        return current1;
    }

拼接字符串方法

原理:AB BA字符串 如果有公共子节点,一定会在后方出现相同元素
A: 0 - 1 - 2 - 3 - 4 - 5
B: a- b - 4 - 5
AB:0-1-2-3-4-5-a-b-4-5
BA:a-b-4-5-0-1-2-3-4-5

right_a 和 right_b 是一样的
在这里插入图片描述

/**
     * 寻找第一个公共子节点 拼接
     *
     * @param head  第一个链表
     * @param head2 第二个链表
     */
    public static ListNode findFirstWithStr(ListNode head, ListNode head2) {

        if (head == null || head2 == null) return null;

        ListNode p1 = head;
        ListNode p2 = head2;

        while (p1 != p2) {
            p1 = p1.next;
            p2 = p2.next;

            if (p1 != p2) {
                if (p1 == null) {
                    p1 = head2;
                }

                if (p2 == null) {
                    p2 = head;
                }
            }

        }
        return p1;
    }

注意:为什么if (p1 != p2),因为如果出现两个不相交的链表,会出现死循环,但是如果加了该判断,两个链表一定会出现都为null的情况

双指针专题

寻找倒数第K个节点

输入一个链表,输出该链表中倒数第k个节点。本题从1开始计数,即链表的尾节点是倒数第1个节点。
1 -> 2 -> 3 -> 4 -> 5, k=2

通过快慢指针,fast指针先走k个单位,但实际位置在k+1,此时slow指针和fast指针同时移动,因为fast相单于多移动了一步,所以在后面时也需要多走一步,所以while中的条件为fast != null

public static ListNode getKthFromEnd(ListNode head, int k) {
   ListNode fast = head;
   ListNode slow = head;

   while (fast != null && k > 0) {
       fast = fast.next;
       k--;
   }
   while (fast != null) {
       fast = fast.next;
       slow = slow.next;
   }
   return slow;
}

链表旋转

在这里插入图片描述
在这里插入图片描述

倒数第k个元素的方式

public static ListNode rotateRight(ListNode head, int k) {
      if (head == null || k == 0) {
          return head;
      }
      ListNode temp = head;
      ListNode fast = head;
      ListNode slow = head;
      int len = 0;
      while (head != null) {
          head = head.next;
          len++;
      }
      if (k % len == 0) {
          return temp;
      }
      while ((k % len) > 0) {
          k--;
          fast = fast.next;
      }
      while (fast.next != null) {
          fast = fast.next;
          slow = slow.next;
      }
      ListNode res = slow.next;
      slow.next = null;
      fast.next = temp;
      return res;
  }

注意:第二次while (fast.next != null)的时候,因为我们要遍历到最后一个节点 而不是最后一个节点的后一个节点,所以要注意这里的判断条件,包括后面的res节点取的是slow节点的next节点,因为此时相当于是倒数第k-1个节点

注意点:

什么时候是 node != null 什么时候是 node.next != null

如果是node != null:

  1. 节点通常会遍历到最后一个节点的后一个几点,最后的节点为null。如果是快慢指针,慢指针会走到中间节点。如寻找倒数第K个节点时。
  2. 对于偶数寻找到中间节点。

如果是node.next != null:

  1. 节点会遍历到最后一个节点,此时节点为lastNode。如果是快慢指针,慢指针会停留在最后一个节点的前一个节点。如翻转链表:找倒数第K个节点时。
  2. 对于技术寻找到中间节点时。
    当快慢指针的快指针每次走两格时,一般会将两个条件一起添加。如果出现对next.next先赋值在做操作时,记得判空。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林深时见璐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值