链表题目总结 -- 双指针技巧

news2024/11/24 9:58:54

文章目录

      • 一. 合并两个有序链表
        • 1. 思路简述
        • 2. 代码
        • 3. 总结
      • 二. 分隔链表
        • 1. 思路简述
        • 2. 代码
        • 3. 总结
      • 三. 合并K个升序链表
        • 1. 思路简述
        • 2. 代码
        • 3. 总结
      • 四. 单链表的倒数第 k 个节点
        • 1. 思路简述
        • 2. 代码
        • 3. 总结
      • 五. 链表的中间结点
        • 1. 思路简述
        • 2. 代码
        • 3. 总结
      • 六. 环形链表(链表是否有环)
        • 1. 思路简述
        • 2. 代码
        • 3. 总结
      • 七. 环形链表 II(如何计算环的起点)
        • 1. 思路简述
        • 2. 代码
        • 3. 总结
      • 八. 相交链表(求两个链表相交的起始点)
        • 1. 思路简述
        • 2. 代码
          • I. 法一
          • II. 法二
          • III. 法三
          • IV. 法四
        • 3. 总结

一. 合并两个有序链表

  • 题目链接:
    https://leetcode.cn/problems/merge-two-sorted-lists/

1. 思路简述

  • 整一个虚拟头指针(构造一个新链表)
  • 两个给出的链表一个节点一个节点的向后比较,val小的那个挂在新链表上(像是一个简单选择排序)
  • 如果一个链表比另一个链表长,把多余的那个部分直接挂在新链表后面(因为题目给的是两个有序链表)。

2. 代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode p = new ListNode();
        ListNode p1 = p;

        while(list1 != null && list2 != null){
            if(list1.val > list2.val){
                p1.next = list2;
                list2 = list2.next;
            }
            else{
                p1.next = list1;
                list1 = list1.next;
            }
            p1 = p1.next;
        }
        if(list1 != null){
            p1.next = list1;
            //这句,完全不需要写,只要list1没完直接整体挂在后面就行了
            //p1 = p1.next;
        }
        if(list2 != null){
            p1.next = list2;
            //这句和上面一样,也不用写
            //p1 = p1.next;
        }
        return p.next;

    }
}

3. 总结

  • 设立p节点,是为了简化边界判断的操作。
  • 一般的题目,都是没有头结点的,所以最后返回的是p.next。
  • ListNode p = new ListNode();申请空间的意义,是由于它必须存在next指针,否则没法往后面挂。
  • 两个给定的链表不需要再申请额外的工作指针,因为比一次,对应的节点就已经挂在新链表上了,不需要考虑回溯的问题。
  • 虽然说这代码看起来很烂,有很多冗余的部分,但可读性强啊,也有利于去编写。
  • 时间复杂度:O(len(A) + len(B))
  • 空间复杂度:O(1)

二. 分隔链表

  • 题目链接:
    https://leetcode.cn/problems/partition-list//

1. 思路简述

  • 建立两个子链表,比x值小的节点挂一个链表上,另一些节点挂在另外一个链表上。
  • 保存挂在链表上节点的后继节点,同时将该节点的next清空,保证那条子链是一个独立的链。
  • 最后将两个链表拼接在一起。

2. 代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode partition(ListNode head, int x) {
    //建立两个虚拟的头结点,用于引导两个子链表
        ListNode list1 = new ListNode();
        ListNode list2 = new ListNode();
    //p1,p2分别为两个子链表上的工作指针    
        ListNode p1 = list1, p2 = list2;
    //p为初始链表的工作指针
        ListNode p = head;
    //temp为初始链表的临时指针,用于保存挂在list1或list2上节点的后继节点,使原链表中每一个节点都能访问到
        ListNode temp = null;

        while(p != null){
            if(p.val < x){
                p1.next = p;
                p1 = p;
            }
            else{
                p2.next = p;
                p2 = p;
            }
            temp = p.next;
            p.next = null;
            p = temp;
        }
    //将list2挂在list1上,由于链表默认是没有虚拟节点的,所以使用p1.next来接收值
        p1.next = list2.next;
    //同理,list1为虚拟头结点,返回list1.next
        return list1.next;
    }
}

3. 总结

这道题其实不是很难,但是由于引入了很多指针,所以显得很绕,博主第一次做这个题,就把自己绕进去了,卡在了temp指针哪一步,不知道怎么才能保证所有节点都能访问到。

  • 凡是需要申请空间的变量,最好就不要动了,引入新的变量来充当工作指针,要不然很乱很乱。
  • 这道题和上道题很像,都是需要比较。第一个题为1 -> 2;这个题而是:1 -> 2 -> 1。
  • 最难的就是temp那一块,需要双指针暂存后继节点,同时清空子链表的末尾(最后节点的.next = null)。
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

三. 合并K个升序链表

  • 题目链接:https://leetcode.cn/problems/merge-k-sorted-lists/

1. 思路简述

  • 处理边界情况。
  • 建立优先队列,将所有链表的头节点放入队列,形成一个小根堆。
  • 不断的筛选出最小的节点,挂在新的链表node上。

2. 代码

import java.util.*;

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
    //判断极端情况
        if(lists.length == 0)
            return null;
    //建立虚拟头节点
        ListNode node = new ListNode();
        ListNode p = node;
    //创建优先级队列
        PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length,new Comparator<ListNode>(){
             public int compare(ListNode a, ListNode b){
                return a.val - b.val;
            }
        });
   /* 
        法二:也可以这么创建优先队列
        PriorityQueue<ListNode> pq = new PriorityQueue<>(
        lists.length, (a, b)->(a.val - b.val));
   */
   
    /*
		把所有链表的头结点放入优先队列中,形成一个小根堆。因为各个子列表是升序的,
		所以不用担心,中间会有比它更小的值。
    */

        for(ListNode node1 : lists){
            if(node1 != null)
                pq.add(node1);
        }
	/*
		不断筛选最小值输出。以第一个为例:如果头结点被筛选走了,就第二个节点加入小根堆。
		因为每个子链表都是升序的,所以保证了,每次进入小根堆的其实都是当前链表当中的最小值。
		一直筛选,直到所有的链表当中的节点都搞完,结束。
	*/
        while(!pq.isEmpty()){
            ListNode min = pq.poll();
            p.next = min;
            p = min;
            if(p.next != null)
                pq.add(p.next);

        }

        return node.next;
    }
}

3. 总结

  • 时间复杂度:O(Nlog(k)) (k为链表的个数,N为结点的总个数,找到最小结点O(1),调整堆顺序O(logk))
  • 空间复杂度:O(n)(创建一个新的链表开销为O(n);优先队列的开销为O(k),远比大多数情况的n要小)
  • 为什么创建好一个数组,默认就有length属性呢?
    • 因为数组创建的时候,需要分配空间,长度是一个定值(类型为:public final int ),所以只要数组创建好了,就可以使用.length属性。
    • 具体的.length和.length()两种方法对比,请看下面这篇博客:
    • https://blog.csdn.net/qq_39671159/article/details/128584905
  • 遇见下面两种情况,直接记住:数组名.length == 0为条件(eg:lists.length == 0)
    • 如果使用lists[0] == null,会直接报错(空指针异常)

输入:lists = [ ]
输出:[ ]

输入:lists = [[ ]]
输出:[ ]

  • PriorityQueue创建中,会使用二叉堆对自身进行排序。想要知道它内部是怎么实现的,请参考https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-daeca/er-cha-dui-1a386/和算法4这本书中优先队列这一小节。
  • 创建优先队列的时候,为什么对比的方法是前大于后,也就是说,为什么这样创建出来的是小根堆???
    • 算法4里面关于这个问题,有一句话很有意思:MaxPQ(基于大根堆的优先队列)的任意实现都能很容易地转化为MinPQ(基于小根堆的优先队列)的实现,反之亦然,只需要改变一下less()比较的方向即可。
    • 我们再去研究一下swim()方法
      在这里插入图片描述在这里插入图片描述
    • 上面两个图说的是基于大根堆的优先队列,K/2就是父节点,K则是子节点。我们带入less()方法,父节点的值如果比子节点的值小,那我们交换父子节点(也就是说父节点的值一定要大于子节点的值),这明显是一个大根堆的有序化过程。同理,看本题当中重写的compare方法(return a.val - b.val;),显然是一个小根堆的有序化的比较方法。
  • 本质上还是一个排序的问题,前面两个题都是两个链表进行排序,而这个题则是多个链表进行排序,以后可以直接联想到优先队列,使用二叉堆来处理问题。

四. 单链表的倒数第 k 个节点

  • 题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/

1. 思路简述

  • 先找到倒数 k + 1个节点,因为我们要知道它的前一个是谁,才能将删除后的链表连在一起。
  • 倒数第k + 1个节点,不就相当于,这个节点到链尾的距离是k + 1吗,那我们,让p1 = head,过k+1个距离后,设置p2 = head;(使两个指针中间的距离刚好使k + 1)
  • 两个指针一直向前同步移动,知道p1 = null;p2则是我们要删除的节点。

2. 代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode findFromEnd(ListNode head, int n){
        ListNode p1 = head;
        while(n != 0){
            p1 = p1.next;
            n--;
        }
        ListNode p2 = head;
        while(p1 != null){
            p2 = p2.next;
            p1 = p1.next;
        }
        return p2;
    }
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode list = new ListNode(-1);
        //引入头结点,完全是为了,让头结点和其他结点一样,便于处理
        list.next = head;
		//如果我们单单是为了就出倒数第K个节点,就没有引入头结点的必要,因为根本涉及不到对头结点的操作
		//这里一定是 list而不是 head,考虑边界情况([1],1 )这种情况,如果找倒数n + 1的结点,会出现空指针异常
        ListNode x = findFromEnd(list, n + 1);
        x.next = x.next.next;

        return list.next;
    }
}

3. 总结

  • 这个题非常的巧妙,将本来看似逆向的问题(求逆向第k个),转换成了正向问题,用双指针同步移动(k个距离,直到快指针为null)解决了问题。
  • 注意边界问题,很容易出现空指针异常。
  • 常常具备构造虚拟头结点的思想,会简便很多。
  • 这道题虽然也是双指针,但是和上面的几个题不同,不再是排序的问题。
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

五. 链表的中间结点

  • 题目链接:https://leetcode.cn/problems/middle-of-the-linked-list/

1. 思路简述

  • 设两个指针,一个一次跑一格子,一个一次跑两格子。
  • 让两个指针一起向后跑,快指针跑到末尾,慢指针指的就是

2. 代码

  • 带头结点
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode list = new ListNode(-1);
        list.next = head;
        ListNode p1 = list;
        ListNode p2 = p1;
	/*
		下面这个while循环完全可以改成:
		while(p2 != null && p2.next != null){
            p1 = p1.next;
            p2 = p2.next.next;
        }
	*/
        while(p2 != null){
            if(p2.next == null)
                break;
            p1 = p1.next;
            p2 = p2.next.next;
        }
        if(p2 == null)
            return p1;
        else 
            return p1.next;
    }
}
  • 不带头结点
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode p1 = head;
        ListNode p2 = p1;

        while(p2 != null && p2.next != null){
            p1 = p1.next;
            p2 = p2.next.next;
        }

        return p1;
    }
}

3. 总结

  • 快慢指针的问题, 巧妙的将求解中间结点的问题转换成了:求两个指针中间距离的问题,和上一题有异曲同工之妙。
  • 有一点落入了设置头结点的固定陷阱中,面试的时候,还是先考虑没有虚拟头结点的情况,如果不好算,那么再加虚拟头结点。笔试就无所谓。
  • 写上面while循环的时候,我一直再想或,或,或的问题,一直报空指针,所以才改成那个样子,以后,或想不出来,可以想想与运算。
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

六. 环形链表(链表是否有环)

  • 题目链接:https://leetcode.cn/problems/linked-list-cycle/

1. 思路简述

  • 其实这问题和上题(链表的中间结点)的求法大致相似,只要稍稍改一下就可以。
  • 快慢指针同时从head往后面跑,上道题是快指针跑到链的末尾就停止,而这道题是一直跑,如果有环,那么快慢指针一定相遇;如果没环,一定能出这个循环。

2. 代码

public boolean hasCycle(ListNode head){
	ListNode p1 = head;
	ListNode p2 = p1;
	
	while(p2 != null && p2.next != null){
	//其实还是上面题的那个循环,如果它能从这个循环中走出来,那么它一定没环;如果有环,快慢指针肯定会相遇,且快指针比慢指针多走了一圈
		p1 = p1.next;
		p2 = p2.next.next;
	
		if(p1 == p2)
			return true;
	}
	return false;
	
}

如果有环,快慢指针肯定会相遇,且快指针比慢指针多走了一圈(慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步),盗一张东哥的图:
在这里插入图片描述
实在不理解就用笔画一画,用2个结点的环链表和3个结点的环链表做实验就可以。

3. 总结

  • 上道题是快指针跑到链的末尾就停止,而这道题是一直跑,如果有环,那么快慢指针一定相遇;如果没环,一定能出这个循环。
  • 时间复杂度:O(n)(n为链表长度,不存在环时,遍历一遍链表就结束;使用相对速度来理解,快指针的相对速度为1,慢指针原地不动,那么也就是考虑快指针前往相遇点要花的时间,相遇点最靠后,也就在这个链表环起点的前一个结点,不会超过n)
  • 空间复杂度O(1)

七. 环形链表 II(如何计算环的起点)

  • 题目链接:https://leetcode.cn/problems/linked-list-cycle-ii/

1. 思路简述

  • 设置快慢指针,让它们第一次相遇。
  • 相遇之后将快指针(或者慢指针)放到起点,然后同步移动,再一次相遇的地点,就是环的起点。

这道题和上道题也差不多,在上道题的基础上做了一点点改动,下面解释一下具体的细节:

  • 首先,分析题意,判断环的起点,那么这个链表就一定存在环,那么就要解决有环的问题,也就是上个题所处理的问题。
  • 盗一张东哥的图,嘿嘿嘿。假设快慢指针在图中的相遇点相遇,我们知道:慢指针 slow 走了 x 步,那么快指针 fast 一定走了 2x步。那么,慢指针跑到了相遇点,快指针就一定多走了一倍的路程。相当于:慢指针在相遇点没动,快指针从相遇点,走了慢指针从head到相遇点这么长的路程。他们最终又相遇了,就是说快指针从相遇点又走到了相遇点(一直在转圈圈),那么快指针走的路程一定是环的长度的整数倍
  • 假设快慢指针已经在相遇点相遇,现在把慢(快)指针放到开始位置(slow == head; or fast == head;),另一个指针不动,开始同步向前。
  • 解释一下为什么第二次要同步向前?
    • 根据上面的分析,我们知道:慢指针从head到相遇点这么长的路程,相当于快指针从相遇点到相遇点跑的路程,就是在转圈圈。如果是按照原来的方法(快指针走的路是慢指针走的路的2倍),那么他们最终还是会在相遇点相遇,而不是一定是环起点(环起点和相遇点有可能不重合)。
    • 假设环的长度是k,假设快指针走的路程一定是环的长度的1倍,相遇点离环起点的有m的距离。
    • 若是同步向前,大家看图,当快慢指针同时走k - m的距离之后,它们会在环起点相遇。
      在这里插入图片描述

2. 代码

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode p1 = head;
        ListNode p2 = p1;

        while(p2 != null && p2.next != null){
            p1 = p1.next;
            p2 = p2.next.next;
            
            if(p1 == p2)
                break;
        }

        if(p2 == null || p2.next == null)
            return null;

        p1 = head;
        while(p1 != p2){
        //一定是同步移动
            p1 = p1.next;
            p2 = p2.next;
        }
        return p1;
    }
}

3. 总结

  • 博主自己做的时候,画了画图,认为环起点和相遇点一定是同一个点,最后发现不是这个样子的。当把慢指针放到head处后,又天真的认为按原来的快慢指针方法(速度不同)一定行,结果g了。
  • 代码量虽然不大,思维要求还是很高的,尤其是这一点:快指针走的路程一定是环的长度的整数倍。想了很久。
  • 时间复杂度:O(N),其中 N 为链表中节点的数目。在最初判断快慢指针是否相遇时,慢指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(N)+O(N)=O(N)
  • 空间复杂度:O(1)。我们只使用了p1,p1两个指针。

八. 相交链表(求两个链表相交的起始点)

  • 题目链接:https://leetcode.cn/problems/intersection-of-two-linked-lists/

1. 思路简述

求两个链表相交的起始点,首先要考虑的是两个链表是否相交的问题。如果我们能保证两个链表同时到达相交的区域,就能进行判断了。
在这里插入图片描述
法一

  • 将链表A和链表B连接起来。(一边遍历,一边连接,不存在专门的连接步骤
    • 将A,B链表连接起来,形成新链表p1;将B,A链表连接起来,形成新链表p2。
    • 这样不管A,B谁长,最终合起来的链表(p1和p2)长度是一致的,那么也就可以同时到达相交区域,如下图所示。
  • 如果相交,输出的是相交结点;不相交,输出的是null。
    在这里插入图片描述

2. 代码

I. 法一
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1 = headA;
        ListNode p2 = headB;

        while(p1 != p2){
            if(p1 == null)
     		//A完了接B
                p1 = headB;
            else
                p1 = p1.next;
                
            if(p2 == null)
           	//B完了接A
                p2 = headA;
            else
                p2 = p2.next;
        }
        return p1;
    }
}
II. 法二
  • 用了两个变量lenA,lenB来存储A,B表的长度。
  • 让相对长的链表先走,直到两个链表剩余的长度相同,再开始同时遍历、比较。
  • 如果相交,输出的是相交结点;不相交,输出的是null。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1 = headA;
        ListNode p2 = headB;
        int lenA = 0;
        int lenB = 0;

        while(p1 != null){
            lenA++;
            p1 = p1.next;
        }
        while(p2 != null){
            lenB++;
            p2 = p2.next;
        }

        p1 = headA;
        p2 = headB;

        if(lenA > lenB)
            for(int i = 0; i < lenA - lenB; i++)
                p1 = p1.next;
        else
            for(int i = 0; i < lenB - lenA; i++)
                p2 = p2.next;

        while(p1 != p2){
            p1 = p1.next;
            p2 = p2.next;
        }
        return p1;
    }
}
III. 法三
  • 将A的末尾和B连接起来,将问题转换成求链表的成环起始点问题(也就是问题七)。
  • 一定要注意,题目中强调要不能改变链表的结构,那么在输出之前一定记得变回来,本题使用的是temp指针来处理。
    在这里插入图片描述
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
        public ListNode detectCycle(ListNode head) {
        ListNode p1 = head;
        ListNode p2 = p1;

        while(p2 != null && p2.next != null){
            p1 = p1.next;
            p2 = p2.next.next;
            
            if(p1 == p2)
                break;
        }

        if(p2 == null || p2.next == null)
            return null;

        p1 = head;
        while(p1 != p2){
        //一定是同步移动
            p1 = p1.next;
            p2 = p2.next;
        }
        return p1;
    }
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1 = headA;
        ListNode p2 = headB;
        //临时指针,处理链表结构改动问题
        ListNode temp = null;
        while(p1.next != null)
            p1 = p1.next;
        p1.next = headB;
        
        temp = p1;
        p1 = detectCycle(headA);
        //将结构改回去
        temp.next = null;
        
        return p1;
    }
}
IV. 法四
  • 使用HashSet数据结构,往里面加链表A。之后遍历链表B,判断结点是否在里面。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        Set<ListNode> hashset = new HashSet<ListNode>();
        ListNode p = headA;

        while(p != null){
            hashset.add(p);
            p = p.next;
        }

        p = headB;
        while(p != null){
            if(hashset.contains(p))
                return p;
            p = p.next;
        }
        return null;
    }
}

3. 总结

  • 最好的解法还是第一个解法,空间复杂度为 O(1),时间复杂度为 O(N)。算法十分新颖。
  • 解法二,虽然代码多一些,空间复杂度仍为 O(1),时间复杂度仍为 O(N)。
  • 算法三,将问题转化成了问题七,挺有意思的,空间复杂度仍为 O(1),时间复杂度仍为 O(N)。
  • 解法四,时间复杂度O(len(A) + len(B)),空间复杂度O(len(A))。

参考:
https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-8f30d/shuang-zhi-0f7cc/
https://leetcode.cn/problems/intersection-of-two-linked-lists/solution/xiang-jiao-lian-biao-by-leetcode-solutio-a8jn/
https://leetcode.cn/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/

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

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

相关文章

docker一个容器内部署多个服务

原因是&#xff0c;我有一个springBoot服务需要写入httpd的目录&#xff0c;然后httpd提供链接给别人下载。之前的方法是&#xff0c;httpd和springBoot各一个容器&#xff0c;但是我们将镜像是部署在腾讯云上的&#xff0c;腾讯云会自动对每个容器分离不同的虚拟机&#xff0c…

pytorch简单自定义Datasets

前言 本文记录一下如何简单自定义pytorch中Datasets&#xff0c;官方教程文件层级目录如下&#xff1a; images 1.jpg2.jpg…9.jpg annotations_file.csv 数据说明 image文件夹中有需要训练的图片&#xff0c;annotations_file.csv中有2列&#xff0c;分别为image_id和labe…

Python的23种设计模式(完整版带源码实例)

作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; Python的23种设计模式 一 什么是设计模式 设计模式是面对各种问题进行提炼和抽象而形成的解决方案。这些设计方案是前人不断试验&…

【入门篇】1 # 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?

说明 【数据结构与算法之美】专栏学习笔记。 什么是复杂度&#xff1f; 复杂度也叫渐进复杂度&#xff0c;包括时间复杂度和空间复杂度&#xff0c;用来分析算法执行效率与数据规模之间的增长关系&#xff0c;可以粗略地表示&#xff0c;越高阶复杂度的算法&#xff0c;执行…

时域脉冲通信采用高斯脉冲且使用PAM调制的Matlab简易演示仿真

时域脉冲通信采用高斯脉冲且使用PAM调制的Matlab简易演示仿真 环境 matlab 2016a 指标 1 将声音信号转为二进制码 2 PAM调制 3 采用高斯脉冲 流程 代码 [OriginVoice,fs]audioread(voice.m4a) ; OriginVoiceOriginVoice(:,2); Nlength(OriginVoice); % 计算信号x的长度 …

算法训练营 day15 二叉树 层序遍历 翻转二叉树 对称二叉树

算法训练营 day15 二叉树 层序遍历 翻转二叉树 对称二叉树 层序遍历 102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。…

标签设计打印软件:LabelJoy 6.23.0 Crack

LabelJoy 专业条码软件 生成25种条形码 从数据源导入条码 计算自动校验 商业条形码标签软件 兼容 Excel、Access、MySQL、Oracle 11.000 个预装的纸张布局 支持任何打印机 通过 3 个步骤创建和打印标签&#xff1a; 选择布局 创建您的标签 开始打印 最好的标签打印软件&#xf…

kafka-1

文章目录1.启动2.创建主题3.发送消息4.消费消息5.使用kafka connect将现有的数据导入到kafka中6.使用kafka streams处理kafka中的events6.终止服务集群配置要点创建主题要点主题分区变更主题副本可变更吗&#xff1f;创建生产者要点> tar -xzf kafka_2.12-3.3.1.tgz1.启动 …

Mac生成和查看ssh key

从 git 上拉取或者提交代码每次都需要输入账号密码&#xff0c;这样很麻烦。我们可以在电脑上生成一个 ssh key&#xff0c;然后把ssh key添加到 git 中&#xff0c;就可以不用每次去输账号密码了。下面就介绍一下怎么在自己的 Mac 中生成和查看 ssh key。一、Mac生成SSH Key打…

【环境】idea远程debug

工作中&#xff0c;遇到问题的时候&#xff0c;想知道上下文中对应的参数值是什么&#xff1f;这时候&#xff0c;1、我们可以全靠逻辑分析。费脑&#xff0c;不一定对。2、打印日志&#xff0c;打印的信息不一定全&#xff0c;换包&#xff0c;麻烦3、远程debug。 1、配置ide…

pytorch二维码识别

二维码图片的生成 利用captcha可以生成二维码图片 # -*- coding: UTF-8 -*- from captcha.image import ImageCaptcha # pip install captcha from PIL import Image import random import time import os # 验证码中的字符 # string.digits string.ascii_uppercase NUMBER…

整理了一周近万字讲解linux基础开发工具vim,gdb,gcc,yum等的使用

文章目录 前言一、yum的使用二、vim的使用三 . gcc/g的使用四 . gdb的使用总结前言 想用linux开发一些软件等必须要会的几种开发工具是必不可少的&#xff0c;在yum vim gcc gdb中指令繁杂的是vim和gdb这两个工具&#xff0c;至于yum和gcc的指令就比较简单了。 一、yum的使用…

【SpringMVC】拦截器

目录 一、概念 二、自定义拦截器的三个实现方法 三、自定义拦截器执行流程 四、使用 五、拦截器和过滤器 相关文章&#xff08;可以关注我的SpringMVC专栏&#xff09; SpingMVC专栏SpingMVC专栏一、概念在学习拦截器之前&#xff0c;我们得先了解一下它是个什么❓ SpringMVC…

SAP ABAP调用标准事务码

这里介绍常见的几种在开发中常用到的事务代码跳转功能。 1、最常用到的是“SET PARAMETER”语句赋值&#xff0c;然后再使用“CALL TRANSACTION”语句跳转屏幕。 比如采购订单、销售订单、交货单、采购发票、销售发票等事务代码&#xff0c;均可以利用给参数赋值来直接跳转&am…

零售及仓储数字化整理解决方案

价格管控 皮克价格管控方案可实现门店与企业信息管理平台的数据同步&#xff0c;强化零售企业对终端的控制。同时为企业销售决策提供支持&#xff0c;优化门店经营活动的效率和频率。陈列管理 皮克陈列管理方案通过电子价签产品使商品陈列得到固化。 同时实现了陈列可视化&am…

ArcGIS水文分析提取河网及流域

在进行某些研究或者一些论文插图显示的时候&#xff0c;有时我们会碰到在部分资料中找不到一些小的河流或者流域的数据的情况&#xff0c;这里讲述通过DEM数据生成河网及流域。 一、数据来源 四川省高程数据来源于中国科学院资源环境科学与数据中心&#xff08;中国科学院资源环…

Vue3学习之深度剖析CSS Modules和Scope

Css Modules 是通过对标签类名进行加装成一个独一无二的类名&#xff0c;比如.class 转换成.class_abc_123,类似于symbol&#xff0c;独一无二的键名 Css Scope 是通过为元素增加一个自定义属性&#xff0c;这个属性加上独一无二的编号&#xff0c;而实现作用域隔离。 原理 …

爬虫必备抓包工具——Fiddler【认识使用】

目录&#xff1a;1.fiddler &#xff08;抓包工具&#xff09;1.1 引入&#xff1a;HTTP/https代理&#xff08;正向代理&#xff09;1.2 拓展&#xff1a;反向代理&#xff1a;1.2 初识Fiddler①什么是抓包&#xff1f;抓包有什么用&#xff1f;②浅谈fiddler&#xff1a;③fi…

Unity_Skybox自定义插件可实现日夜更替Polyverse Skies | Low Poly

又又一个天空盒,不过这个做的还是比较完善的。。。不会出现买家秀和买家秀差别大问题 此Skybox插件特色提供: 可扩展,自定义很多的Skybox Shader预制体几个,虽然都是夜晚样式(缺白天)若干预设值</

对NIO和BIO的进一步理解

疑问 在之前的学习中&#xff0c;只提到BIO是阻塞IO&#xff0c;在建立连接和读写事件时会阻塞线程。NIO是非阻塞IO&#xff0c;基于事件注册&#xff0c;通过Selector进行切换Channel&#xff0c;不会阻塞线程。对于这种解释&#xff0c;还是带有一些疑问的。Selector进行Cha…