判断链表是否带环,若带环求交点、环的入口点【Java实现】

面试题题目如下:

1. 判断链表是否带环

2. 求带环链表中环的入口点

3. 求带环链表中环的长度

对题目1和2的分析:

1. 判断链表是否带环

分析:定义两个指针:快指针和慢指针,快指针一次走两步,慢指针一次走一步;

           如果链表带环,那么快慢指针会在环中相遇【快指针等于满指针】

          如果快指针走到头时,快慢指针没有相等的时候,那么说明链表不带环

2. 如果链表带环,如何找环的入口点

分析:如果一个链表有环,当快指针和慢指针相遇时,慢指针一定还没有遍历完链表,而此时快指针已经在环内循环走了n(n>=1)圈了。

假设此时

慢指针走了s步,快指针走了2s步,

r为快指针在环内转了一圈的步数,

a为从链表头到入口点的步数,

b为从入口点到相遇点的步数,

c为从相遇点再走c步到达入口点,

L为整个链表的长度。

那么:

慢指针走的步数:

s = a + b

快指针走的步数:

2s = s + n*r       即:s = n*r

链表的长度:

L = a + b + c = a+r

由上可知:

a + b = n*r = (n - 1)*r + r

而r = L - a

所以:

a + b = (n - 1)*r + L - a

a = (n - 1)*r + L - a - b

而L - a - b = c

所以:

a = (n -1)*r +c

综上可得:从链表头到环入口点等于(n - 1)循环内环 + 相遇点到环入口点,于是在链表头和相遇点分别设置一个指针,同时出发,每次各走一步,它们必定会相遇,且第一次相遇的点就是环入口点。


具体实现如下:

先定义节点类-Node类

public class Node {
    private int data;
    private Node next;

    Node(int data) {
        this.data = data;
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }
}

具体实现及测试代码如下:

public class LinkedList {


    //判断链表是否带环,若不带环,返回null
    public Node hasCircle(Node first) {
        if (first == null) {
            return null;
        }
        Node slow = first;
        Node fast = first;
        while (fast != null && fast.getNext() != null) {
            slow = slow.getNext();
            fast = fast.getNext().getNext();
            if (slow == fast) {
                return slow;
            }
        }
        return null;
    }

    //若链表带环,求环的入口点
    public Node getCircleEntry(Node first) {
        if (hasCircle(first) == null) {
            return null;
        }
        Node meetNode = hasCircle(first);
        Node tail = first;
        while (tail != meetNode) {
            tail = tail.getNext();
            meetNode = meetNode.getNext();
        }
        return tail;
    }

    //若链表带环,求环的长度
    public int getCircleLength(Node first) {
        if (getCircleEntry(first) == null) {
            return 0;
        }
        int len = 1;//带环的链表,环的长度至少是1
        Node entryNode = getCircleEntry(first);
        Node node = entryNode;
        while (node.getNext() != entryNode) {
            node = node.getNext();
            len++;
        }
        return len;
    }


    public static void main(String[] args) {
        LinkedList list = new LinkedList();
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        Node n5 = new Node(5);
        Node n6 = new Node(6);

        n1.setNext(n2);
        n2.setNext(n3);
        n3.setNext(n4);
        n4.setNext(n5);
        n5.setNext(n6);
        n6.setNext(n3);


        //判断链表是否带环
        System.out.println(list.hasCircle(n1));

        //求带环链表中环的入口点
        System.out.println(list.getCircleEntry(n1).getData());

        //求带环链表中环的长度
        System.out.println(list.getCircleLength(n1));

    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用以下步骤解决这个问题: 1. 将经纬度转换为直角坐标系的,这可以通过以下公式实现:x = R * cos(lat) * cos(lon),y = R * cos(lat) * sin(lon),z = R * sin(lat),其中 R 是地球半径,lat 和 lon 分别是纬度和经度。 2. 根据第一个的经纬度、方向角和距离计算出第一个的终坐标。 3. 同样的,根据第二个的经纬度、方向角和距离计算出第二个的终坐标。 4. 计算第一个到第二个的向量和交点向量,如果两个向量平行,则两条线段不相交;否则,它们相交。 5. 如果两条线段相交,则计算它们的交点。可以使用以下公式计算:交点 = P1 + (P2 - P1) * t1,其中 P1 和 P2 分别是两个线段的起和终,t1 是第一个线段上与第二个线段相交的位置。 6. 将交点的直角坐标系转换回经纬度。 下面是 Java 代码的示例,用于计算两条线段是否相交并找到它们的交点: ```java public class LineIntersection { static final double EARTH_RADIUS = 6378137; // 地球半径,单位为米 static class Point { double x, y, z; Point(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } } static Point toCartesian(double lat, double lon) { double x = EARTH_RADIUS * Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(lon)); double y = EARTH_RADIUS * Math.cos(Math.toRadians(lat)) * Math.sin(Math.toRadians(lon)); double z = EARTH_RADIUS * Math.sin(Math.toRadians(lat)); return new Point(x, y, z); } static Point move(Point p, double distance, double azimuth) { double lat1 = Math.asin(p.z / EARTH_RADIUS); // 起纬度 double lon1 = Math.atan2(p.y, p.x); // 起经度 double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distance / EARTH_RADIUS) + Math.cos(lat1) * Math.sin(distance / EARTH_RADIUS) * Math.cos(azimuth)); double lon2 = lon1 + Math.atan2(Math.sin(azimuth) * Math.sin(distance / EARTH_RADIUS) * Math.cos(lat1), Math.cos(distance / EARTH_RADIUS) - Math.sin(lat1) * Math.sin(lat2)); return toCartesian(Math.toDegrees(lat2), Math.toDegrees(lon2)); } static boolean intersect(Point p1, Point p2, Point q1, Point q2) { Point v1 = new Point(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z); // p1 -> p2 的向量 Point v2 = new Point(q2.x - q1.x, q2.y - q1.y, q2.z - q1.z); // q1 -> q2 的向量 Point v3 = new Point(q1.x - p1.x, q1.y - p1.y, q1.z - p1.z); // p1 -> q1 的向量 Point n1 = new Point(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); // v1 和 v2 的叉积 double d = n1.x * v3.x + n1.y * v3.y + n1.z * v3.z; // n1 和 v3 的积 if (Math.abs(d) < 1e-6) { return false; // 平行,不相交 } double t1 = (v2.x * v3.y - v2.y * v3.x) / d; double t2 = (v1.x * v3.y - v1.y * v3.x) / d; return t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1; // 相交当且仅当 t1 和 t2 均在 [0, 1] 内 } static Point intersection(Point p1, Point p2, Point q1, Point q2) { Point v1 = new Point(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z); // p1 -> p2 的向量 Point v2 = new Point(q2.x - q1.x, q2.y - q1.y, q2.z - q1.z); // q1 -> q2 的向量 Point v3 = new Point(q1.x - p1.x, q1.y - p1.y, q1.z - p1.z); // p1 -> q1 的向量 Point n1 = new Point(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); // v1 和 v2 的叉积 double d = n1.x * v3.x + n1.y * v3.y + n1.z * v3.z; // n1 和 v3 的积 double t1 = (v2.x * v3.y - v2.y * v3.x) / d; return new Point(p1.x + v1.x * t1, p1.y + v1.y * t1, p1.z + v1.z * t1); } public static void main(String[] args) { // 第一个的经纬度和方向角 double lat1 = 39.9042; double lon1 = 116.4074; double azimuth1 = 45; double distance1 = 10000; // 以米为单位 // 第二个的经纬度和方向角 double lat2 = 40.7128; double lon2 = -74.0060; double azimuth2 = 225; double distance2 = 10000; // 以米为单位 // 将经纬度转换为直角坐标系的 Point p1 = toCartesian(lat1, lon1); Point p2 = move(p1, distance1, azimuth1); Point q1 = toCartesian(lat2, lon2); Point q2 = move(q1, distance2, azimuth2); // 判断两条线段是否相交 if (intersect(p1, p2, q1, q2)) { // 计算交点的经纬度 Point intersection = intersection(p1, p2, q1, q2); double lat = Math.toDegrees(Math.asin(intersection.z / EARTH_RADIUS)); double lon = Math.toDegrees(Math.atan2(intersection.y, intersection.x)); System.out.println("交点经纬度:" + lat + ", " + lon); } else { System.out.println("两条线段不相交"); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值