算法-约瑟夫环问题

题目: 给定两个可能有环也可能无环的单链表,头结点 head1 和 head2。 请实现一个函数,如果两个链表相交,请返回相交的 第一个节点 。如果不相交,返回 null 【要求】 如果两个链表的长度之和为 N ,时间复杂度请达到 O(N) , 额外空间复杂度请达到 O(1) 。

使用容器定位

  • 思路一 (空间复杂度无法达到要求)使用容器完成约瑟夫环:

    比如无环链表: head->A->B->C->D->E->null 与 有环链表: head->A->B->C->D->B->E->null 可以使用 hash 表进行存储,当因为 hash 表中存储的元素都是唯一的,也就是说当我们在遍历链表的过程当中, 将元素置入 hash 表当中,如果链表遍历但最后一个 null 都没有出现重复的元素,则表示该链表不存在环。 如果在遍历的过程当中,一但出现重复的元素,则表示该链表已经形成了一个环。

kotlin 复制代码
    /**
     * 利用容器实现环的查找
     *
     * @param elements
     * @param <E>
     * @return
     */
    public static <E> E vesselScreen(LinkedList<E> elements) {
        HashSet<E> screen = new HashSet<E>();
        for (int i = 0; i < elements.getSize(); i++) {
            E data = elements.get(i);
            if (screen.contains(data))
                return data;
            screen.add(data);
        }
        return null;
    }

通过快慢指针定位

  • 思路二 (空间复杂度 O(1))使用快慢指针:

    通过快慢两个指针的移动,完成对该链表的遍历。如果慢指针最终指向空,则表示该链表是不存在环的,如果慢指针最终追上了快指针,则表示必定有环,因为当没有环的时候,本身快指针就比慢指针走的快,是不可能相遇的。

虽然是相遇,但是却无法定位到入环节点。当两个指针相遇的时候,快指针回到开头节点,快指针走一步,而慢指针走两步,而当快慢指针再次相遇的时候,便是入环的节点。

附录-代码

  • 节点元素

    kotlin 复制代码
    package com.peppa.common;
    ​
    /**
     * 自定义对的节点元素
     * @param <T>
     */
    public class Node<T> {
        T data;
        Node<T> next;
        public Node(T data){
            this.data=data;
            this.next=null;
        }
    }
  • 链表结构

    ini 复制代码
    package com.peppa.common;
    ​
    /**
     * 自定义链表结构
     */
    public class LinkedList<T> {
        private Node<T> head; // 头结点
        private int length; // 链表长度
    ​
        // 添加元素到尾巴节点
        public void append(Node<T> data) {
            if (head == null) {
                head = data;
                length = 1;
                return;
            }
            Node<T> current = head;
            while (current.next != null) { // 定位到最后的一个元素
                current = current.next;
            }
            current.next = data;// 追加到尾部
            length++;
        }
    ​
        //打印链表元素
        public void printList() {
            Node<T> current = head;
            int coutn = 0;
            while (current != null && coutn < length) {
                System.out.print(current.data + " ");
                current = current.next;
                coutn++;
            }
            System.out.println();
        }
    ​
        //根据元素下标获取元素数据
        public T get(int index) {
            Node<T> current = head;
            if (index > length << 1) {
                return current.data;
            }
            for (int i = 0; i < index; i++) {
                current = current.next;
            }
            return current.data;
        }
    ​
        public int getSize() {
            return length;
        }
    ​
    }
  • main

    ini 复制代码
    /**
         * 运行程序
         *
         * @param args
         */
        public static void main(String[] args) {
            LinkedList<Integer> linkedList = new LinkedList();
            Node<Integer> integerNode_1 = new Node<Integer>(1);
            Node<Integer> integerNode_2 = new Node<Integer>(2);
            Node<Integer> integerNode_3 = new Node<Integer>(3);
            Node<Integer> integerNode_4 = new Node<Integer>(4);
            Node<Integer> integerNode_5 = new Node<Integer>(5);
            Node<Integer> integerNode_6 = new Node<Integer>(6);
            Node<Integer> integerNode_7 = new Node<Integer>(7);
            linkedList.append(integerNode_1);
            linkedList.append(integerNode_2);
            linkedList.append(integerNode_3);
            linkedList.append(integerNode_4);
            linkedList.append(integerNode_5);
            linkedList.append(integerNode_6);
            linkedList.append(integerNode_7);
            linkedList.append(integerNode_3);
            linkedList.printList();
    ​
            Integer data = vesselScreen(linkedList);
            if (data != null)
                System.out.println("有环-相交元素为: " + data);
            else
                System.out.println("无环-无相交元素");
        }

    算法文件地址

    约瑟夫环

相关推荐
鳄鱼儿4 分钟前
密码算法的OID查阅
算法
lxh011334 分钟前
螺旋数组题解
前端·算法·js
czlczl200209251 小时前
算法:二叉树的公共祖先
算法
小白程序员成长日记2 小时前
2025.11.23 力扣每日一题
算法·leetcode·职场和发展
16_one3 小时前
autoDL安装Open-WebUi+Rag本地知识库问答+Function Calling
人工智能·后端·算法
散峰而望4 小时前
C++数组(三)(算法竞赛)
开发语言·c++·算法·github
q***95224 小时前
SpringMVC 请求参数接收
前端·javascript·算法
初级炼丹师(爱说实话版)5 小时前
多进程与多线程的优缺点及适用场景总结
算法
hetao17338375 小时前
2025-11-25~26 hetao1733837的刷题记录
c++·算法
历程里程碑5 小时前
各种排序法大全
c语言·数据结构·笔记·算法·排序算法