Java牛客编程刷算法记录--2022-12-7+2023-2-19

news2024/9/25 11:20:20

https://www.nowcoder.com/ta/classic-code 牛客上经典必刷题库

https://www.nowcoder.com/practice/e08819cfdeb34985a8de9c4e6562e724?tpId=46&tqId=29030&rp=1&ru=/ta/classic-code&qru=/ta/classic-code&difficulty=&judgeStatus=&tags=/question-ranking

题目CC1:(知识点:树,难度:较难)
求给定二叉树的最小深度。最小深度是指树的根结点到最近叶子结点的最短路径上结点的数量。

示例1
输入:
{1,2,3,4,5}

返回值:
2

代码:

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @return int整型
     */
    public int run (TreeNode root) {
        // write code here
        if (root == null) {
            return 0;
        }
        int left = run(root.left);
        int right = run(root.right);
        if (left * right > 0){
            return (left > right ? right : left) + 1;
        } else {
            // 理解最小深度出现偏差,对根节点,左右节点两个为空(均返回0),或其中一个为空(则left或right为0,此时要取较大值,为0的值没有叶子节点不予考虑。根节点到最近叶子节点路径上的节点数。这个概念要理解清楚,否则编程出大问题。)
            return (left > right ? left : right) + 1;
        }

    }
}

https://www.nowcoder.com/practice/22f9d7dd89374b6c8289e44237c70447?tpId=46&rp=1&ru=%2Fta%2Fclassic-code&qru=%2Fta%2Fclassic-code&difficulty=&judgeStatus=&tags=&title=&sourceUrl=&gioEnter=menu

题目CC2:(知识点:栈, 难度:较难)
计算逆波兰式(后缀表达式)的值
运算符仅包含"+“,”-“,”*“和”/",被操作数是整数

保证表达式合法,除法时向下取整。

数据范围:表达式的长度满足: n<10000
进阶:空间复杂度 O(n) , 时间复杂度 O(n)
例如:
[“20”, “10”, “+”, “30”, “*”] -> ((20 + 10) * 30) -> 900
[“40”, “130”, “50”, “/”, “+”] -> (40 + (130 / 50)) -> 42

示例1
输入:
[“20”,“10”,“+”,“30”,“*”]

返回值:
900

示例2
输入:
[“20”]

返回值:
20

代码:

import java.util.*;


public class Solution {
    /**
     * 
     * @param tokens string字符串一维数组 
     * @return int整型
     */
    public int evalRPN (String[] tokens) {
        // write code here
        Stack<Integer> stack = new Stack<>();
        int num1, num2;
        for (int i = 0; i < tokens.length; i++) {
            switch (tokens[i]) {
                case "+":
                    num2 = stack.pop();
                    num1 = stack.pop();
                    stack.push(num1 + num2);
                    break;
                case "-":
                    num2 = stack.pop();
                    num1 = stack.pop();
                    stack.push(num1 - num2);
                    break;
                case "*":
                    num2 = stack.pop();
                    num1 = stack.pop();
                    stack.push(num1 * num2);
                    break;
                case "/":
                    num2 = stack.pop();
                    num1 = stack.pop();
                    stack.push(num1 + num2);
                    break;
                default:
                    stack.push(Integer.parseInt(tokens[i]));
            }
        }
        return stack.pop();
    }
}

https://www.nowcoder.com/practice/bfc691e0100441cdb8ec153f32540be2?tpId=46&rp=1&ru=%2Fta%2Fclassic-code&qru=%2Fta%2Fclassic-code&difficulty=&judgeStatus=&tags=&title=&sourceUrl=&gioEnter=menu

题目CC3:(知识点:穷举,难度:困难)
对于给定的n个位于同一二维平面上的点,求最多能有多少个点位于同一直线上
示例1
输入:
[(0,0),(0,1)]

返回值:
2

示例2
输入:
[(2,3),(3,3),(-5,3)]

返回值:
3

代码:

import java.util.*;

/*
 * public class Point {
 *   int x;
 *   int y;
 * }
 */

public class Solution {
    /**
     * 
     * @param points Point类一维数组 
     * @return int整型
     */
    public int maxPoints (Point[] points) {
        // write code here
        if (points == null || points.length == 0) {
            return 0;
        }
        if (points.length <= 2) {
            return points.length;
        }
        //上面考虑特殊情况。下面就是通用情况考虑
        int max = 0;  // max存储最大值sum
        for (int i = 0; i < points.length; i++) {
            for (int j = i + 1; j < points.length; j++) {
                // 两点形成一条直线,穷尽两个点的举例, 但会存在重复的直线,这里存在这个重复计算的问题,理论上可以优化。
                //points[i] 和 points[j] 构成一条直线,重新遍历所有点,有多少点在该直线上,并将数据存在sum中。
                int sum = 0;
                if (points[i].x == points[j].x) {
                    // 考虑直线斜率无穷大的情况,所有点横坐标相同。
                    for (int k = 0; k < points.length; k++) {
                        if (points[k].x == points[i].x) {
                            sum++;
                        }
                    }
                } else {
                    // 如何判断点在一条有斜率的直线上???
                    // y = kx + b // k, b体现直线的特征(不适用垂直于x轴的直线)
                    // 在程序中直接求k值(除不尽为小数)会存在精度损失的情况,所以必须采用某种表达式判断点是否在直线上。假设两个个点(x1,y1),(x2,y2),判断第三个点(x3,y3)(不同于前面两个点)是否在(x1,y1),(x2,y2)构成的直线上??我们知道(y2-y1)/(x2-x1) = (y3 - y2)/(x3 -x2)成立则第三个点肯定在直线上,对这个表达式变换一下:(y2-y1)*(x3-x2) = (y3-y2)*(x2-x1),程序中判断这个表达式就不会存在精度问题。本题中横纵坐标均是整型。若(x3,y3)为(x1,y1),(x2,y2)中的一个点,表达式依然成立
                    for (int m = 0; m < points.length; m++) {
                        if ((points[j].y - points[i].y) * (points[m].x - points[j].x) == (points[m].y - points[j].y) * (points[j].x - points[i].x)){
                            sum++;
                        }
                    }

                }

                // 遍历所有的每条直线产生的sum值。
                if (sum > max) {
                    max = sum;
                }

            }
        }
        return max;
    }
}

总结:穷举类型的题目在于用程序模拟求解的过程。本题要求最多有多少个点在一条直线上。所以考虑穷举所有的直线,并求每条直线上的点的个数,即可求得本题的解。

https://www.nowcoder.com/practice/d75c232a0405427098a8d1627930bea6?tpId=46&rp=1&ru=%2Fta%2Fclassic-code&qru=%2Fta%2Fclassic-code&difficulty=&judgeStatus=&tags=&title=&sourceUrl=&gioEnter=menu

题目CC4:(知识点:链表,排序,难度:较难)
在O(n log n)的时间内使用常数级空间复杂度对链表进行排序。
示例1
输入:
{30,20,40}

返回值:
{20,30,40}

本题思路:采用插入排序,但插入排序的时间复杂度为O(n^2),但本题采用插入排序,可以通过牛客网的全部测试用例。对于单链表而言,肯定采用插入排序更好。快排时间复杂度可以达到O(nlog2).对于排序,各种方法还需要好好复习,加以体会。

代码:

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode sortList (ListNode head) {
        // write code here
        if (head == null || head.next == null) {
            return head;
        }
        //插入排序
        ListNode p = head.next;//遍历剩下的链表元素
        head.next = null;  //用于构建插入排序链表,寻找合适位置并插入
        while (p != null) {
            ListNode r = p.next; //保存p的后续,以免链断了。
            if (p.val < head.val) { //考虑特殊情况,首结点,p作为首节点,//这里若引入头结点就不用考虑特殊情况,头结点不存储任何数据,只存储指向链表首结点的指针。单链表插入操作必须考虑前驱节点。
                p.next = head;
                head = p;
                p = r;
                
            } else {
                ListNode q = head;
            
                //后面比较除head节点之外的节点
                while (q.next != null && p.val >= q.next.val) {
                    q = q.next;
                }
                //找到合适位置并插入,p的值比q的下一个值小则p插入到q的后面, 或者遍历到最后q.next=null,则q插入到q的后面.  这里是单链表的插入操作,我竟然都忘记了,要维护两个指针,而我只写了q.next = p.
                p.next = q.next;
                q.next = p; 
                p = r;
            }

            

        }
        return head;
    }
}

本题第二种思路:为满足时间为O(nlogn),采用归并排序。冒泡排序、插入排序、选择排序这三种算法的时间复杂度都为O(n^2) ,只适合小规模的数据。两种时间复杂度为nlogn的排序算法——归并排序(Merge Sort)和快速排序(Quick Sort),他们都用到了分治思想,非常巧妙。参考网址:https://zhuanlan.zhihu.com/p/95080265
归并排序算法时间复杂度分析,参考网址:https://zhuanlan.zhihu.com/p/341225128
像这种排序问题的时空复杂度分析,还是要作比较深入的理解分析,对于自己的算法编程肯定大有益处。
使用快慢指针拆分两个链表,对两个链表分别递归排序,最终将排好序的两个链表归并排序成一个有序的链表,归并 排序需要额外的空间O(n)

代码:

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode sortList (ListNode head) {
        // write code here
        if (head == null) {
            return null;
        }
        if (head.next == null) {
            return head;
        }

        ListNode preSlow = head;  // 慢指针指向元素的前一个元素
        ListNode slow = head; // 慢指针
        ListNode fast = head; // 快指针
        while (slow != null && fast != null && fast.next != null) {
            preSlow = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        // 循环结束后,slow指向链表中间的元素(元素个数为奇数),中间两个元素的后一个(元素个数为偶数)
        ListNode listA = head;  //指向前一个半链
        ListNode listB = slow;  //指向后一个半链。
        preSlow.next = null;  //将两个链断开。
        listA = sortList(listA);  // 对前一个半链递归分解合并排序
        listB = sortList(listB);  // 对后一个半链递归分解合并排序
        return merge(listA, listB); // 将有序的listA 和有序的listB合并排序
    }

    private ListNode merge(ListNode listA, ListNode listB) {
        ListNode dummy = new ListNode(0); // 作为临时链表的头节点,不存储实际内容
        ListNode nodeA = listA;
        ListNode nodeB = listB;
        ListNode nodeR = dummy;  // 指向临时链表的尾结点。

        while (nodeA != null && nodeB != null) {
            if (nodeA.val < nodeB.val) {
                nodeR.next = nodeA;
                nodeA = nodeA.next;
            } else {
                nodeR.next = nodeB;
                nodeB = nodeB.next;
            }
            //每次插入一个,维护nodeR指针指向尾结点。
            nodeR = nodeR.next;
        }

        if (nodeA != null) {
            // nodeA节点后面有剩余元素,则一并拼接为一条链,不需要逐个添加。
            nodeR.next = nodeA;
        }
        if (nodeB != null) {
            nodeR.next = nodeB;
        }
        return dummy.next;

    }
}

https://www.nowcoder.com/practice/152bc6c5b14149e49bf5d8c46f53152b?tpId=46&rp=1&ru=%2Fta%2Fclassic-code&qru=%2Fta%2Fclassic-code&difficulty=&judgeStatus=&tags=&title=&sourceUrl=&gioEnter=menu

题目CC5:(知识点:链表,排序,难度:较难)
使用插入排序对链表进行排序。
示例1
输入:
{30,20,40}

返回值:
{20,30,40}

代码:

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode insertionSortList (ListNode head) {
        // write code here
        if (head == null || head.next == null) {
            return head;
        }
        // 至少两个节点
        ListNode p = head.next;  // 遍历p链表的每个元素,在有序链表head中寻找插入位置并插入。
        head.next = null; //初始化head链表,只有一个链表有序,后面扩充该有序表。

        

        while(p != null) {
            ListNode q = head; // q用来遍历有序链表
            ListNode r = p.next; // p插入有序表中会修改p.next,所以这里要保存。
            if (p.val < q.val) {
                //考虑特殊情况,插入位置为首结点前面。
                p.next = q;
                head = p;
                p = r;
            } else {
                while (q.next != null && p.val >= q.next.val) {
                    q = q.next;
                }
                p.next = q.next;
                q.next = p;
                p = r;
            }
        }
        return head;
    }
}

题目CC6:(知识点:二叉树,难度:中等)
用递归的方法对给定的二叉树进行后序遍历。
例如:
给定的二叉树为{1,#,2,3},

返回[3,2,1].

本题思路:后序遍历,针对每一个子树始终左子树、右子树、根递归遍历。

代码:

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @return int整型ArrayList
     */
    public ArrayList<Integer> postorderTraversal (TreeNode root) {
        // write code here
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (root == null) {
            return list;
        }
        ArrayList<Integer> leftList = new ArrayList<Integer>();
        ArrayList<Integer> rightList = new ArrayList<Integer>();
        
        leftList = postorderTraversal(root.left);
        
        rightList = postorderTraversal(root.right);
        
        list.addAll(leftList);
        list.addAll(rightList);
        list.add(root.val);
        return list;
    }
}

题目CC7:(知识点:栈,树,难度:简单)
求给定的二叉树的前序遍历。
例如:
给定的二叉树为{1,#,2,3},

返回:[1,2,3].

代码:

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @return int整型ArrayList
     */
    public ArrayList<Integer> preorderTraversal (TreeNode root) {
        // write code here
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (root == null) {
            return list;
        }

        list.add(root.val);

        ArrayList<Integer> leftList = new ArrayList<Integer>();
        ArrayList<Integer> rightList = new ArrayList<Integer>();
        
        leftList = preorderTraversal(root.left);
        
        rightList = preorderTraversal(root.right);
        
        list.addAll(leftList);
        list.addAll(rightList);
        
        return list;
    }
}

题目CC8:重排链表(知识点:链表,难度:中等)
将给定的单链表L:L0–>L1–>…–>Ln-1–>Ln
重新排序为:L0–Ln–>L1–>Ln-1–>L2–>Ln-2->…
要求使用原地算法,不能只改变节点内部的值,需要对实际的节点进行交换。

数据范围:链表长度 0≤n≤20000 ,链表中每个节点的值满足 0≤val≤1000

要求:空间复杂度 O(n) 并在链表上进行操作而不新建链表,时间复杂度 O(n)
进阶:空间复杂度 O(1) , 时间复杂度 O(n)

示例1
输入:
{1,2,3,4}

返回值:
{1,4,2,3}

说明:
给定head链表1->2->3->4, 重新排列为 1->4->2->3,会取head链表里面的值打印输出 1

示例2
输入:
{1,2,3,4,5}

返回值:
{1,5,2,4,3}

说明:
给定head链表1->2->3->4->5, 重新排列为 1->5>2->4->3,会取head链表里面的值打印输出

示例3
输入:
{}

返回值:
{}

本题思路一:时间复杂度O(N):N是链表的节点数量,遍历链表,重组链表,空间复杂度O(1):使用常数级空间指针。
1.使用快慢指针,快指针一次走两步,慢指针一次走一步,当快指针走到终点时,慢指针指向中点,若节点个数为偶数,则慢指针指向中间两个节点中的左端节点。
2.通过第一步拆分两个链表,对后一半链表逆序,用头插法
3.遍历逆序后的链表插入到第一个链表的对应位置。
代码:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public void reorderList(ListNode head) {
        if (head == null || head.next == null || head.next.next == null) {
            return;
        }
        // 用快慢指针寻找中间节点,将链表分为两个
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode newHead = slow.next;
        slow.next = null;

        // 第二个链表反转,这里考察单链表反转
        newHead = reverseList(newHead);

        // 链表节点依次连接
        while(newHead != null) {
            ListNode temp = newHead.next;
            newHead.next = head.next;
            head.next = newHead;
            head = newHead.next;
            newHead = temp;
        }
    }

    private ListNode reverseList(ListNode head) {
        if (head == null) {
            return null;
        }
        ListNode tail = head;
        head = head.next;
        tail.next = null;
        while (head != null) {
            ListNode temp = head.next;
            head.next = tail;
            tail = head;
            head = temp;
        }
        return tail;
    }
}

总结1:考察快慢指针,链表逆序(头插法),单链表插入操作等。

本题思路二:将链表放到一个ArrayList中,利用前后双指针,依次按题目要求插入链表。时间复杂度O(N),空间复杂度O(N)
代码:

import java.util.*;
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public void reorderList(ListNode head) {
        if (head == null || head.next == null || head.next.next == null) {
            return;
        }
        // 用快慢指针寻找中间节点,将链表分为两个
        List<ListNode> list = new ArrayList<>();
        while (head != null) {
            list.add(head);
            head = head.next;
        }
        // 设置两个指针
        int i = 0;  //前指针
        int j = list.size() - 1; //后指针,增加索引好寻找最后一个元素,以便从后往前遍历
        while (i < j) {
            list.get(i).next = list.get(j);
            i++;
            if (i == j) { // 链表节点只有两个时才会出现这种情况
                break;
            }
            list.get(j).next = list.get(i);
            j--;
        }
        list.get(i).next = null;
    }
}

总结2:这里关键是给单链表的每个元素增加了下标索引,使用ArrayList增加了O(N)的空间。

题目CC9:(知识点:链表,哈希,双指针,难度:中等)
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

数据范围: 0<=n<=10000,1<=结点值<=10000
要求:空间复杂度 O(1),时间复杂度 O(n)

例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
在这里插入图片描述

可以看到环的入口结点的结点值为3,所以返回结点值为3的结点。
输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回值描述:
返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。

示例1
输入:
{1,2},{3,4,5}

返回值:
3

说明:
返回环形链表入口结点,我们后台程序会打印该环形链表入口结点对应的结点值,即3

示例2
输入:
{1},{}

返回值:
“null”

说明:
没有环,返回对应编程语言的空结点,后台程序会打印"null"

示例3
输入:
{},{2}
复制
返回值:
2

说明:
环的部分只有一个结点,所以返回该环形链表入口结点,后台程序打印该结点对应的结点值,即2

本题思路一:hash法,使用HashSet存储已经遍历过的节点,当第一次出现重复的节点时,即为入口节点。时间复杂度:O(N),需要将链表的所有节点遍历一次;空间复杂度:需要额外的O(N)空间HashSet存储节点。
代码:

import java.util.*;
/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        HashSet<ListNode> set = new HashSet<>();
        if (pHead == null) {
            return null;
        }
        while (pHead != null) {
            // 当set中包含节点时,说明第一次出现重复的节点,即环的入口节点。
            if (set.contains(pHead)) {
                return pHead;
            }
            // set中加入未重复的节点
            set.add(pHead);
            pHead = pHead.next;
        }

        return null;
    }
}

本题思路二:快慢指针很容易判断是否有环,进入环中,二者间的相对距离每次减1,最终会相遇。假设从头节点到环的入口节点的前一个节点一共有a个,环中的节点有b个,设fast指针走过的节点数为f,slow指针走过的节点数为s,则:
(1) f=2s
(2) f=s+nb (f走的节点数一定与s走的节点数相差整数圈即nb)
由此得到s=nb.两指针相遇时满指针走了nb步。已知指针走到环的入口节点需要走a+kb步(k为整数),而slow指针走了nb步,则其只要走a步即可到环的入口节点,这时将fast指针赋值为头节点,也移动a步后则fast和slow指针在环的入口节点相遇(编程的具体实现思想)。
时间复杂度:O(n),n为链表节点数,以满指针为基准,遍历次数约为2次
空间复杂度:O(1),只使用了两个指针变量,空间复杂度为常数。
代码:

import java.util.*;
/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        if (pHead == null) return null;
        // 定义快慢指针
        ListNode slow = pHead;
        ListNode fast = pHead;
        while (fast != null && fast.next != null) {
            // 快指针是慢指针的两倍速度
            fast = fast.next.next;
            slow = slow.next;
            // 记录快慢指针第一次相遇的节点
            if (slow == fast) break;
        }
        // 若快指针指向null,则环不存在
        if (fast == null || fast.next == null) return null;
        // 将快指针重新指向头节点
        fast = pHead;
        // 与第一次相遇的节点相同速度出发    这里是这道题的核心点
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }
}

题目CC10 判断链表中是否有环
知识点:链表、哈希、双指针(快慢指针)
描述
判断给定的链表中是否有环。如果有环则返回true,否则返回false。
数据范围:链表长度 0 \le n \le 100000≤n≤10000,链表中任意节点的值满足 |val| <= 100000∣val∣<=100000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
输入分为两部分,第一部分为链表,第二部分代表是否有环,然后将组成的head头结点传入到函数里面。-1代表无环,其它的数字代表有环,这些参数解释仅仅是为了方便读者自测调试。实际在编程时读入的是链表的头节点。
例如输入{3,2,0,-4},1时,对应的链表结构如下图所示:
在这里插入图片描述
可以看出环的入口结点为从头结点开始的第1个结点(注:头结点为第0个结点),所以输出true。
示例1
输入:{3,2,0,-4},1
返回值:true
说明:第一部分{3,2,0,-4}代表一个链表,第二部分的1表示,-4到位置1(注:头结点为位置0),即-4->2存在一个链接,组成传入的head为一个带环的链表,返回true
示例2
输入:{1},-1
返回值:false
说明:第一部分{1}代表一个链表,-1代表无环,组成传入head为一个无环的单链表,返回false
示例3
输入:{-1,-7,7,-4,19,6,-9,-5,-2,-5},6
返回值:true

此题与CC9题相关为其一部分,解题简单。
代码如下:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null) return false;
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            // 若有环,则快慢指针终会相遇
            if (fast == slow) break;
        }
        // 无环
        if (fast == null || fast.next == null) return false;

        // 有环
        return true;

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/356296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android自动化配置

1 搭建APPIUM环境 1.1 安装node.js Appium是使用nodejs实现的&#xff0c;所以node是解释器&#xff0c;需要第一步安装好 node.js的安装包下载地址&#xff1a; https://nodejs.org/en/download/ 注意&#xff1a;node.js的安装包的下载在官网有两种版本&#xff0c;建议大…

基于FFmpeg实现的无声音屏幕录制

UI自动化测试时&#xff0c;有时需要进行录屏操作&#xff0c;这时我们是不需要声音的&#xff0c;我们可以通过FFmpeg进行简单的录制工作。 以下是在windows10环境下&#xff0c;基于FFmpeg实现的简单录制&#xff1a; Ffmpeg简介&#xff1a; 功能&#xff1a;有非常强大的…

Spring Cloud Nacos实战(六)- 集群架构说明与持久化配置切换

目录Nacos集群架构说明Nacos支持三种部署模式集群部署说明预备环境Nacos持久化切换配置Nacos持久化配置Nacos默认derby切换MySql测试Nacos集群架构说明 ​ 到目前为止&#xff0c;我们已经对Nacos的一些基本使用和配置已经掌握&#xff0c;但是这些还不够&#xff0c;我们还需…

算法导论【时间复杂度】—排序算法、图算法、动态规划、字符串匹配等时间复杂度小结

算法导论【时间复杂度】—排序算法、图算法、DP等小结排序快速排序堆排序计数排序基数排序桶排序SELECT算法RANDOMIZED-SELECTSELECT图算法广度优先搜索深度优先搜索Kruskal算法Prim算法Bellman-Ford算法Dijkstra算法Floyd-Warshall算法Johnson算法Ford-Folkson方法Edmonds-Kar…

基于Redis实现的分布式锁

基于Redis实现的分布式锁什么是分布式锁分布式锁主流的实现方案Redis分布式锁Redis分布式锁的Java代码体现优化一&#xff1a;使用UUID防止误删除优化二&#xff1a;LUA保证删除原子性什么是分布式锁 单体单机部署中可以为一个操作加上锁&#xff0c;这样其他操作就会等待锁释…

网络安全-FOFA资产收集和FOFA API调用

网络安全-FOFA资产收集和FOFA API调用 前言 一&#xff0c;我也是初学者记录的笔记 二&#xff0c;可能有错误的地方&#xff0c;请谨慎 三&#xff0c;欢迎各路大神指教 四&#xff0c;任何文章仅作为学习使用 五&#xff0c;学习网络安全知识请勿适用于违法行为 学习网络安全…

Android OTA升级常见问题的解决方法

1.1 多服务器编译 OTA 报错 Android7 以后引入了 jack-server 功能&#xff0c;也导致在公共服务器上 编译 Android7 以上的版本时&#xff0c;会出现 j ack-server 报错问题。 在多用户服务器上 编译 dist 时 会出现编译过程中 会将 port_service 和 port_admin 改为 默认的 …

Go语言Web入门之浅谈Gin框架

Gin框架Gin简介第一个Gin示例HelloworldRESTful APIGin返回数据的几种格式Gin 获取参数HTTP重定向Gin路由&路由组Gin框架当中的中间件Gin简介 Gin 是一个用 Go (Golang) 编写的 web 框架。它是一个类似于 martini 但拥有更好性能的 API 框架&#xff0c;由于 httprouter&a…

MySQl----- 单表查询

表名&#xff1a;worker-- 表中字段均为中文&#xff0c;比如 部门号 工资 职工号 参加工作 等 CREATE TABLE worker ( 部门号 int(11) NOT NULL, 职工号 int(11) NOT NULL, 工作时间 date NOT NULL, 工资 float(8,2) NOT NULL, 政治面貌 varchar…

【Spark分布式内存计算框架——Spark SQL】11. External DataSource(中)parquet 数据

6.3 parquet 数据 SparkSQL模块中默认读取数据文件格式就是parquet列式存储数据&#xff0c;通过参数【spark.sql.sources.default】设置&#xff0c;默认值为【parquet】。 范例演示代码&#xff1a;直接load加载parquet数据和指定parquet格式加载数据 import org.apache.s…

事物发展的不同阶段会有不同的状态

之前讨论过一个话题&#xff0c;有人问“股票交易稳定盈利很难么&#xff1f;” 我的回答&#xff1a;不难&#xff0c;难在之前。 这几天我又想到经常看到论坛里有人pk观点&#xff0c;最后甩出一句话&#xff1a;“证明你说得对&#xff0c;你先赚一个亿再说。否则&#xf…

写代码犹如写文章: “大师级程序员把系统当故事来讲,而不是当做程序来写” | 如何架构设计复杂业务系统? 如何写复杂业务代码?...

“大师级程序员把系统当故事来讲&#xff0c;而不是当做程序来写”写代码犹如写文章好的代码应该如好文章一样表达思想&#xff0c;被人读懂。中心思想: 突出明确程序是开发者用编程语言写成的一本书&#xff0c;首先应该是记录开发者对业务需求分析、系统分析&#xff0c;最终…

并发编程底层原理

并发编程 文章目录并发编程线程知识点回顾多线程并行和并发什么是并发编程&#xff1f;并发编程的根本原因&#xff1f;Java内存模型&#xff08;JMM&#xff09;并发编程的核心问题-可见性、有序性、原子性可见性有序性原子性并发问题总结volatile关键字volatile的底层原理如何…

K8s学习(二)Kubernetest的资源管理及五大资源介绍

文章目录前言1.kubernetes的资源管理系统资源查看2.资源管理方式3.资源管理实战3.1 Namespace3.2 Pod3.3 Label3.4 Deployment3.5 Service3.5.1创建集群内部可访问的Service3.5.2创建集群外部可访问的Service前言 本文是k8s学习系列文章&#xff0c;前后串起来是一个完整的课程…

一招鉴别真假ChatGPT,并简要介绍ChatGPT、GPT、GPT2和GPT3模型之间的区别和联系

以下内容除红色字体部分之外&#xff0c;其他均来源于ChatGPT自动撰写。 ChatGPT是基于GPT模型的对话生成模型&#xff0c;旨在通过对话模拟实现自然语言交互。它是为了改善人机对话体验而设计的&#xff0c;主要应用于聊天机器人、智能客服等场景。 与GPT模型相比&#xff0c;…

全栈之路-前端篇 | 第一讲.基础前置知识【浏览器内核与网络知识】学习笔记

欢迎关注「全栈工程师修炼指南」公众号 点击 &#x1f447; 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习&#xff01; 涉及 企业运维、网络安全、应用开发、物联网、人工智能、大数据 学习知识 “ 花开堪折直须折&#xff0c;莫待无花…

内大-oj练习题(1期)

用于存储内大oj练习题 1. 排序题2. 实数输出3. 字符串比较大小4. 1055 找最小放表头,找最大放表尾5. 通过反转实现数据移动6. 破圈报数7. 通话记录8. 用栈实现进制转换9. 判断升序10. 金额的中文大写11. 生日组成的素数12. 判断是否属于一个子网13 统计字符个数14. 求前n项和1…

LeetCode02.07面试题 链表相交 带有输入和输出的链表相交

题目&#xff1a; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c; 函…

Git的基本操作

文章目录1.git的工作流程2.git的工作环境3.git的基本操作(1)git init(2)git status(3)git add(4)git commit4.版本控制(1)git reflog与git log(2)再增加两个版本(3)git reset --hard 版本号(4)两个指针4.分支管理(1)对分支的理解(2)git branch和git branch -v(3)git checkout 分…

基于matlab的斜视模式下SAR建模

一、前言此示例说明如何使用线性 FM &#xff08;LFM&#xff09; 波形对基于聚光灯的合成孔径雷达 &#xff08;SAR&#xff09; 系统进行建模。在斜视模式下&#xff0c;SAR平台根据需要从宽侧斜视一定角度向前或向后看。斜视模式有助于对位于当前雷达平台位置前面的区域进行…