编程导航算法通关村第 1关 | 两个链表的第一个公共节点

编程导航算法通关村第 1关 | 白银挑战

剑指 Offer 52. 两个链表的第一个公共节点

编程导航算法通关村第 1关 | 两个链表的第一个公共节点_第1张图片

集合/map

  • 将headA中的链表,放在一个set集合中, 依次遍历headB, headB中第一个包含在set集合中的节点就是第一个公共子节点
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 将headA压入集合中
        Set<ListNode> statckA = new HashSet<>();
        ListNode temp = headA;
        while (temp != null) {
            statckA.add(temp);
            temp = temp.next;
        }

        ListNode tempB = headB;
        while (tempB != null) {
            if (statckA.contains(tempB)) {
                return tempB;
            }

            tempB = tempB.next;
        }
        return null;
    }

  • 将两个链表的节点分别压入栈中,然后同时出栈,当两个栈出栈的元素相等是,说明这个节点是公共子节点,当最后一个相等的元素出栈时,就是第一个公共子节点
 /**
     * 方法二:将两个链表的节点分别压入栈中,然后同时出栈,
     * 当两个栈出栈的元素相等是,
     * 说明这个节点是公共子节点,当最后一个相等的元素出栈时,就是第一个公共子节点
     */
    ListNode getIntersectionNodeByStack(ListNode headA, ListNode headB) {
        //    同时压入两个栈中
        ListNode tempA = headA;
        ListNode tempB = headB;

        Stack<ListNode> stackA = new Stack<>();
        Stack<ListNode> stackB = new Stack<>();

        while (tempA != null) {
            stackA.push(tempA);
            tempA = tempA.next;
        }


        while (tempB != null) {
            stackB.push(tempB);
            tempB = tempB.next;
        }
        ListNode result = null;
        while (stackA.size() != 0 && stackB.size() != 0) {
            ListNode popA = stackA.pop();
            ListNode popB = stackB.pop();
            if (popA == popB) {
                result = popA;
            } else {
                break;
            }
        }

        return result;
    }

拼接两个字符串

编程导航算法通关村第 1关 | 两个链表的第一个公共节点_第2张图片

 /**
     * 分别遍历两个链表
     * headA+headB;
     * headB+headA
     * 找到的第一个相等的点,就是两个的第一个公共子节点
     */
    ListNode getIntersectionNodeByString(ListNode headA, ListNode headB) {
        ListNode tempA = headA;
        ListNode tempB = headB;

//        考虑链表为空的情况
        if (headA == null) {
            return null;
        }
        if (headB == null) {
            return null;
        }
        while (tempA != tempB) {
            tempA = tempA.next;
            tempB = tempB.next;

            if (tempA != tempB) {
                if (tempA == null) {
                    tempA = headB;
                }
                if (tempB == null) {
                    tempB = headA;
                }
            }
        }

        return tempA;
    }

差和双指针

  • 先各自统计链表的长度
  • 计算长度的差值
  • 较长的链表移动差值距离
  • 同时向后移动,第一个相等节点便是第一个公共子节点

 ListNode getIntersectionNodeBysubA(ListNode headA, ListNode headB) {
        ListNode tempA = headA;
        ListNode tempB = headB;

//        考虑链表为空的情况
        if (headA == null) {
            return null;
        }
        if (headB == null) {
            return null;
        }
//       计算长度
        int lengthA = 0;
        int lengthB = 0;
        while (tempA != null) {
            tempA = tempA.next;
            lengthA++;
        }

        while (tempB != null) {
            tempB = tempB.next;
            lengthB++;
        }

//        计算差值
        int sub = Math.abs(lengthA - lengthB);

        tempA = headA;
        tempB = headB;
        if (lengthA > lengthB) {
            int i = 0;
            while (i < sub) {
                tempA = tempA.next;
                i++;
            }
        } else if (lengthB > lengthA) {
            int i = 0;
            while (i < sub) {
                tempB = tempB.next;
                i++;
            }
        }
//        同时向后移动
        while (tempA != null && tempB != null) {
            if (tempA == tempB) {
                return tempA;
            }
            tempA = tempA.next;
            tempB = tempB.next;
        }
        return null;
    }

判断是否是回文序列

使用栈

  • 将链表中的元素全部压入栈中,遍历链表,与栈中弹出的元素是否相等,如何全部相等,则为回文序列
    public boolean isPalindrome(ListNode head) {
        Stack<ListNode> listNodes = new Stack<>();

        ListNode temp = head;

        while (temp != null) {
            listNodes.push(temp);
            temp = temp.next;
        }

//
        temp = head;
        while (temp != null) {

            ListNode pop = listNodes.pop();
            if (temp.val != pop.val) {
                return false;
            }
            temp=temp.next;
        }

        return true;


    }

优化:只遍历一半,将前一半与后一半进行对比

    public boolean isPalindrome(ListNode head) {
        Stack<ListNode> listNodes = new Stack<>();

        ListNode temp = head;

        int length = 0;
        while (temp != null) {
            listNodes.push(temp);
            temp = temp.next;
            length++;
        }

//
//        计算一半的值

        int cout = length / 2;
        temp = head;
        int i = 0;
        while (temp != null && i < cout) {

            ListNode pop = listNodes.pop();
            if (temp.val != pop.val) {
                return false;
            }
            temp = temp.next;
            i++;
        }

        return true;


    }

快慢指针+一半反转法

  • 首先,如果链表为空或者只有一个节点,那么它肯定是回文的,所以直接返回true。

  • 创建两个指针slow和fast,都指向链表的头部。slow指针每次移动一步,fast指针每次移动两步。这样,当fast指针到达链表的末尾时,slow指针就会在链表的中间。

  • 同时,我们还创建了pre和prepre两个指针,用于反转链表的前半部分。pre指针始终指向slow指针的前一个节点,prepre指针始终指向pre指针的前一个节点。在每次循环中,我们都将pre指针的next指向prepre,然后将prepre和pre分别更新为pre和slow,这样就可以反转链表的前半部分。

  • 如果链表的长度是奇数,那么fast指针会停在最后一个节点上,此时slow指针指向的是链表的中间节点,我们需要将slow指针向后移动一步,跳过中间节点。

  • 最后,我们同时遍历反转后的前半部分链表(从pre指针开始)和后半部分链表(从slow指针开始),如果发现有节点的值不相等,那么就返回false。如果所有节点的值都相等,那么就返回true。

  • 这个函数的时间复杂度是O(n),空间复杂度是O(1),其中n是链表的长度。

    public static boolean isPalindromeByFastAndSlow(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

//        当fast到达末尾时,slow正好到达中间
        ListNode pre = null;
        while (fast != null && fast.next != null) {
            ListNode next = slow;
            fast = fast.next.next;
            slow = slow.next;

//            进行翻转
            next.next = pre;
            pre = next;
//            next = slow;

        }
        System.out.println(pre.val);
        System.out.println(slow.val);
        if (fast != null) {
            slow = slow.next;
        }
//        此时pre就是翻转后链表的头结点,
//        slow是后半部分的头结点

        while (pre != null && slow != null) {
            if (pre.val != slow.val) {
                return false;
            }
            pre = pre.next;
            slow = slow.next;
        }

        return true;

//
    }

删除倒数第N个元素

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz

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

Related Topics 链表 双指针 2586 0

public ListNode removeNthFromEnd(ListNode head, int n) {

        ListNode listNode = new ListNode(0);
        listNode.next = head;
        if (head == null) {
            return null;
        }

//        if ( head.next == null &&  n == 1 ) {
//            return null;
//        }

        ListNode fast = head;
        ListNode slow = listNode;

//
        int i = 0;
        while (i < n) {
            fast = fast.next;
            i++;
        }

        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
//        slow.ne


//        删除slow 下一个节点的位置
        slow.next = slow.next.next;

        return listNode.next;

    }

删除重复元素

  • 设置虚拟头结点,防止出现删除head节点的情况
  • 保留重复的值,直接删除
 /**
     *
     * 删除重复元素
     * */
    public ListNode deleteDuplicatesNo(ListNode head) {
        ListNode doumoNode = new ListNode(0);
        doumoNode.next = head;
        ListNode temp = doumoNode;
        while (temp != null && temp.next != null && temp.next.next != null) {
            if (temp.next.val == temp.next.next.val) {
                int x = temp.next.val;
                while (temp.next != null && temp.next.val == x) {
                    temp.next = temp.next.next;
                }
            } else {
                temp = temp.next;
            }
        }
        return doumoNode.next;
    }

你可能感兴趣的:(算法,链表,数据结构)