LinkedList与链表(1万多字超级详细版本)

        一. ArrayList的缺陷

        上个博客已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素:

public class ArrayList < E > extends AbstractList < E >
implements List < E > , RandomAccess , Cloneable , java . io . Serializable
{
// ...
// 默认容量是 10
private static final int DEFAULT_CAPACITY = 10 ;
//...
// 数组:用来存储元素
transient Object [] elementData ; // non-private to simplify nested class access
// 有效元素个数
private int size ;
public ArrayList ( int initialCapacity ) {
if ( initialCapacity > 0 ) {
this . elementData = new Object [ initialCapacity ];
} else if ( initialCapacity == 0 ) {
this . elementData = EMPTY_ELEMENTDATA ;
} else {
throw new IllegalArgumentException ( "Illegal Capacity: " +
initialCapacity );
}
}
// ...
        由于其底层是一段连续空间,当 ArrayList 任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后 搬移,时间复杂度为 O(n) ,效率比较低,因此 ArrayList 不适合做任意位置插入和删除比较多的场景 。因此: java 集合中又引入了LinkedList ,即链表结构。

二. 链表

2.1 链表的概念及结构

        链表是一种物理存储结构上非连续 存储结构,数据元素的 逻辑顺序 是通过链表中的 引用链接 次序实现的 。
        实际中链表的结构非常多样,以下情况组合起来就有8 种链表结构:
1. 单向或者双向

2. 带头或者不带头


3. 环或者非循环

        虽然有这么多的链表的结构,但是我们重点掌握两种:
        
        1.无头单向非循环链表 结构简单 ,一般不会单独用来存数据。实际中更多是作为 其他数据结构的子结构 ,如哈希桶、图的邻接表等等。另外这种结构在笔试面试 中出现很多。
        2.无头双向链表 :在 Java 的集合框架库中 LinkedList 底层实现就是无头双向循环链表。

2.2 链表的实现

        


2.2.1 先创建一个LinkedList链表

        

        一般的都是通过一个内部类来实现链表的创建的,显得更加的整齐。

        

        自己写一个接口来实现存放这些方法。

        

        这是实例化来创建了内容对象,通过next来实现链表的连接。head是头。

        

        就是通过如图形式连接的。


        2.2.2 display方法

        

        通过创建一个singerLinkedlist对象来指向head,也就是第一个元素,通过while语句让origal指针指向空值时结束。origal=origal.next,是用来移动指针的。

        结果:


2.2.3 addFirst方法

        

        通过cur创建一个singerLinkedlist对象,存入你输入的数据,把head的地址放在cur.next当中,如下图:

        

        然后再改变一下head指针,让它指向增加之后的第一个元素。


2.2.4 addLast方法

        

        先判断一下,指针指向的内容是否为null,如果为null说明链表中不存在元素,第一个也就是最后一个,所以直接创建一个对象,让head指针指向它们。

        如果不为空,先创建一个对象来存储你要增加的值,然后再创建一个指针也指向head指针指向的内容。通过while语句先指向最后一个位置,然后让最后一个位置的next原来为空现在把你增加的数据的地址。

        只是执行完while语句的效果,然后cur1.next=cur,就是下图的效果。

        


        2.2.5addIndex方法

        

        在指定的位置插入你要插入的元素。

        fushuyichang是我们自己写的一个异常。

        

        如图,如果你输入一个不合法的数,就会报出输入异常的提醒。如果你要在首尾位置插入,就直接调用我们原来写的方法。

        下面是我们从第一个位置到达倒数第二个位置插入的方法,先通过一个len方法来记录下第二个元素的下标,通过while语句来找到我们要插入的位置。然后通过一个temp来存下我们原来的位置的next,然后把你添加元素的地址放到当前位置的next当中,让后再把原来位置的next放到你添加的这个元素的next当中。

        或者通过下面这个方式:

        

        这样也可以成功的插入元素,不用引入第三个变量。


2.2.6 contains方法        

        不要在意len没有什么用,我忘记删除了,这个很简单能看懂,就是通过cur=cur.next的方式来移动指针,通过cur.val的值与key的值进行比较来判断是否找到该值,没有找到就返回false。


        2.2.7 remove方法
        第一种

        通过双指针,一个就按照正常的顺序往后走,一个是指向它的前一个位置,当找到了该元素后,通过prev是指向前一个元素的,当cur指针找到咱要删除的元素,通过prev.next=cur.next的方式删除掉它,很巧妙,当你删除的是第一个元素时,进去prev.next就是空值了,因为prev谁也没指向,它就是一个空指针,然后直接改变head也就是头元素就删除了,删除后面的就是prev永远就不会为null了,然后通过prev.next=cur.next的方式删除了。

        prev.next=cur.next的过程如下图:

        原来的

        当我们要删除18时,如图所示,cur找到了18,cur找到了该值,prev指向它的前一个位置,然后进行我们的操作。

        执行完之后的。

        这样就直接把0x77就跳过去了。


第二种

        

        先找到你要删除的前一个,改变你要删除的前一个的next,把你要删除那个元素的下一个元素的地址放进去,也可以达到删除的效果,和上面的一样,就是它定义了两个变量我们这里定义了一个。

        大家思考一下这个是否可以达到删除的效果?

        答案是不能的,你的prev看似是存放着cur.next,看似和上面的一样,但是这是指针,你这相当于把prev指针指向了cur.next那个地址的值,并不是改变了,你的cur的next的值,下面的表示的是有改变了prev的指向的地址,并没有断开连接。


两种方法的区别:        

        第一段代码中,当满足 key - 1 == len 的条件时,执行的删除操作是 prev = cur.next.next; ,这种写法存在错误,因为它只是将 prev 指向了 cur.next.next ,但并没有将 cur.next 与链表中的后续节点正确断开连接,无法实现删除节点的效果。

        第二段代码中,当满足条件时,执行的删除操作是 cur.next = cur.next.next; ,这是正确的删除节点操作,将当前节点(cur)的下一个节点(cur.next)指向其下下个节点(cur.next.next),从而实现了删除 cur.next 节点的效果。


        这里的cur.next直接使用和赋值给prev到底有什么区别

        

        当将 cur.next 赋值给 prev 时(如第一段代码):

 
singerLinkedlist prev = cur.next;
prev = cur.next.next;

        这里只是改变了 prev 的引用,让它指向了 cur.next.next ,但并没有改变链表的结构。原来 cur.next 所指向的节点仍然在链表中,没有被真正删除或断开连接。

        

而直接对 cur.next 进行赋值(如第二段代码):

 
cur.next = cur.next.next;
 

        这直接修改了链表中当前节点(cur)的下一个节点的引用,将其指向了下下个节点,从而实现了删除当前节点的下一个节点(cur.next)的效果,改变了链表的结构。

 

        总的来说,第一段代码中的操作没有对链表进行有效的删除节点操作,而第二段代码中的操作正确地实现了删除节点。

        


        第三种

        

        这是通过输入你要删除的值来删除的,不是通过下标。

        


        2.2.8 removeAllKey方法

        

        通过双指针,先把除了第一项的其他符合要求的项数全部删除。最后再判断一下第一项是否符合删除的条件。


        2.2.9 size方法

        

        


        2.2.10 clear方法

        

        这种是破坏了链表结构打印的,通过cur指向head,然后cur1指向cur,通过cur=cur.next的形式来使指针向后移动,通过cur1.next=null的形式使表的结构断开,只剩下第一个了,让head=null就行了。

        原来的链表结构。

        while执行后的

        就可以了。

       

        或者

        

        咱是通过head指针打印的,直接让head指针变为0就可以了。


        2.3 面试题

        2.3.1 删除链表中等于给定值 val 的所有节点。

        要求就是

        

        答案

        通过两个指针的形式,如果一直相等,就让prev一直指向第一个元素的位置不懂,直到最后一个元素的next为null赋值给prev,此时就剩一个头head了,如果中间有不相等的,就让prev指向那个不相等的,把他保留下来。

        源代码:

    public ListNode removeElements(ListNode head, int val) {

        if(head==null){

            return head;

        }

        ListNode cur=head.next;

        ListNode cur1=head;

        while(cur!=null){

            if(cur.val==val){

               cur1.next=cur.next;

               cur=cur.next;

               }

                else{

                    cur1=cur;

                    cur=cur.next;

                }

            }

            if(head.val==val){

                head=head.next;

            }

        return head;

    }


        2.3.2      翻转链表

        

        cur和cur1的作用都是记录原来它的next的值,防止原来的next发生变化,你再让cur=cur.next,他会陷入无限循环。

        源代码:

public ListNode reverseList(ListNode head) {

        if(head==null){

            return  head;

        }

        ListNode cur1=head.next;

        head.next=null;

        while(cur1!=null){

            ListNode prev=cur1.next;

            cur1.next=head;

            head=cur1;

            cur1=prev;

        }

        return head;

    }


2.3.3 链表的中间结点

        

要求:一次遍历就要完成,不能用len来记录次数。

        此时我们就要定义两个引用了。

        

        一个fast引用,走得快,slow引用走的慢,路程是一样的,fast是slow的二倍,所以fast到终点的时候,slow才走到中间位置。

        为什么要有两个限制条件呢?

        第二个限制条件是,当有5个数的时候,要打印后三个,fast到第三个时,fast到第二个了,判断还是符合,fast走到第五个位置了,此时slow走到第三个位置,此时就要打印了,如果只有第一个条件的话,此时还要向后走,slow就会走到第四个位置了,导致打印结果出错。

        第一个限制条件的作用,光有第二个限制条件也是不行的,也不能交换位置,例如,当有四个元素时,fast走到第三个位置,slow到了第二个位置,fast走到第五个位置,此时为null,你要是直接用fast.next此时fast为空,无法访问它的next会报空指针异常。

        源代码:

public ListNode middleNode(ListNode head) {

        if(head==null){

            return head;

        }

        ListNode fast=head;

        ListNode slow=head;

        while(fast!=null&&fast.next!=null){

            fast=fast.next.next;

            slow=slow.next;

        }

        return slow;

    }


2.3.4 打印倒数的节点

        要求:输入一个链表,输出该链表中倒数第k个结点及其往后的节点。

        要求用一个while语句遍历一次解决。

        

        通过len控制先不遍历slow,直到我们走到合适的位置,你要打印倒数后两个的话,fast指向最后一个的时候,slow要指向倒数第二个,也就是图中的效果。

        源代码:
public void findlast(int k){singerLinkedlist fast=head;singerLinkedlist slow=head;int len=1;while (fast!=null&&fast.next!=null){if(len<k){len++;fast=fast.next;continue;}fast=fast.next;slow=slow.next;}System.out.println(slow.val);
}}

        2.3.5 判断回文

        题目要求:

        答案

        先把这个链表中的值加入放到数组当中,通过数组来判断是否是回文。

        源代码1:
public boolean check(singerLinkedlist head){int[] arr=new int[10];singerLinkedlist cur=head;int i=0;int sum=0;while (cur!=null){arr[i]=cur.val;cur=cur.next;sum++;i++;}for (int j = 0; j < sum/2; j++) {if (arr[j]==arr[sum-1]){}else {return false;}sum--;}return true;
}

解法二:

                通过快慢指针,快指针指向最后,慢指针指到中间位置,此时再通过下面的while循环翻转一下后半部分的内容,如果它是奇数,slow指针和cur指针会在中间位置相遇,此时就结束循环了,如果是偶数,通过if语句结束循环,此时到中间cur.next就是slow。

源代码2:
public boolean panduan(singerLinkedlist head){singerLinkedlist fast=head;singerLinkedlist slow=head;int len=0;while (fast!=null&&fast.next!=null){fast=fast.next.next;slow=slow.next;}singerLinkedlist cur=head;singerLinkedlist cur1=slow.next;while (cur1!=null){singerLinkedlist curn=cur1.next;cur1.next=slow;slow=cur1;cur1=curn;}while (slow!=cur){if (cur.next==slow){break;}if (cur.val!=slow.val){return false;}slow=slow.next;cur=cur.next;}return true;
}

2.3.6 拼接链表

        

        

        答案:

        

        先创建一个对象,它的头是-1,让tmp指向newlist,通过while判断谁的小,就把谁接在tmp的后面,最后head和head2的长度不一定一样,所以,判断二者谁不为空就把谁后面的全部拼在tmp的后面。

源代码:
public singerLinkedlist hebing(singerLinkedlist head,singerLinkedlist head2){singerLinkedlist newlist=new singerLinkedlist(-1);singerLinkedlist tmp=newlist;while (head!=null&&head2!=null){if (head.val<head2.val){tmp.next=head;head=head.next;tmp=tmp.next;}else {tmp.next=head2;head2=head2.next;tmp=tmp.next;}}if (head!=null){tmp=head;}if (head2!=null){tmp=head2;}return newlist.next;
}

2.3.7 链表分割

        答案:

        解法一:

        

        创建两个对象一个是放小于x的一个放大于等于x的,通过while循环把它们分别放在不同的对象当中,可能全部都大于不存在小的,此时就直接返回prev1的next因为它的第一个是-1,不能要,如果小于的,可以再判断一下bigcur是否为空,如果是空的话就直接返回prev 的next,二者都不是空值再拼接,最后一个if语句是把最后一个元素的next设置为空值,因为如果最后一个数小于x,放在了前面,此时的最后一个next中放的还是原来的next值,此时就会形成一个闭环,无限循环输出。

源代码1:

public singerLinkedlist paixu(singerLinkedlist pHead,int x){// write code heresingerLinkedlist littlecur=new singerLinkedlist(-1);singerLinkedlist bigcur=new singerLinkedlist(-1);singerLinkedlist prev=littlecur;singerLinkedlist prev1=bigcur;singerLinkedlist cur=pHead;while(cur!=null){if(cur.val<x){littlecur.next=cur;cur=cur.next;littlecur=littlecur.next;}else{bigcur.next=cur;cur=cur.next;bigcur=bigcur.next;}}if (littlecur==null){return prev1.next;}littlecur.next=prev1.next;if (littlecur!=null){bigcur.next=null;}return prev.next;}

解法二:

        

        先创建四个空指针。

        然后把两种值一种大于等于x的,一种小于x的分别用不同的指针指向它们,首先就是先判断s1是否为空,如果不为空,说明执行了else的语句,此时s1就指向了这个小于x的头元素,作用就是找头的,不为空执行上面的,让s2动,s1不动,结束之后,s2就指向了小于x的最后一个元素,同理s4也指向了最大值的最后一个元素,判断s2是否为空,为空证明没有小于x的值,此时直接返回s4,否则直接让s2的next拼上大于等于x的第一个,就是如图效果,通过最后一个if语句将最后一个元素弄为空值。

源代码2:

public singerLinkedlist paixu2(singerLinkedlist pHead,int x){singerLinkedlist s1=null;singerLinkedlist s2=null;singerLinkedlist s3=null;singerLinkedlist s4=null;singerLinkedlist cur=pHead;while (cur!=null){if(cur.val<x){if (s1!=null){s2.next=cur;cur=cur.next;s2=s2.next;}else {s1=s2=cur;cur=cur.next;}}else {if (s4!=null){s3.next=cur;cur=cur.next;s3=s3.next;}else {s3=s4=cur;cur=cur.next;}}}if (s2==null){return s3;}//访问next了就要考虑他是否为空。s2.next=s4;if (s2!=null&&s3!=null){s3.next=null;}//因为当s2为空时说明全部都大于x,此时也就不存在什么问题,当s3为空时,说明全是小于x的,也不会存在问题,但是当s2不为空的时候,当最后一个是小于x的,// 它的前面有大于x的,此时最后一个去前面了,但是此时的最后一个还是有next的此时就会陷入循环当中。return s1;
}
2.3.8 相交链表

        

        先讲一下这里的相交不是我们创痛意义上的相交。

        

        不是有交点的意思,而是只要有交点两个链表的后面全部得相等,是Y字型相交而不是我们的X型相交。

源代码:

public class Solution {

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

        ListNode cur1=headA;

        ListNode cur2=headB;

        int len1=0;

        int len2=0;

        while(cur1!=null){

            len1++;

            cur1=cur1.next;

        }

        while(cur2!=null){

            len2++;

            cur2=cur2.next;

        }

        cur1=headA;

        cur2=headB;

        int len=len1-len2;

        if(len<0){

            cur1=headB;

            cur2=headA;

            len=len2-len1;

        }

        while(len!=0){

            cur1=cur1.next;

            len--;

        }

        while(cur1!=cur2){

            cur1=cur1.next;

            cur2=cur2.next;

        }

        if(cur1==null){

            return null;

        }

        return cur1;

    }

}

思路:

        思路就是,因为后面的要全部相等,两个链表的长度不一定相等,我们得先保证两个链表后面走的长度是一样的,所以我们就先让那个长的链表先走,保证它俩的长度一样,我们让cur1指向长链表让cur2指向短链表,我们不知道谁长,可以通过len1和len2来分别记录长度,再通过差值判断谁长,让cur1指向长的,再让cur1先走差值步,此时才可以判断是否相交,我们要的是相交的那个点,通过cur1!=cur2,如果相等了,说明就找到相交点了,下面的if语句如果cur1也就是长的为空了,说明没有找到相交点,此时返回null,否则就返回cur1说明找到了相交点。


2.3.9:环形链表

        答案:

        

        还是通过快慢指针的方式,让快指针走两步,慢指针走一步,此时二者的距离一直在缩小,如果是环形的话,总是会相遇的,如果fast==slow说明相遇了,就返回true否则就返回false。

        可能会有疑问,fast能不能一次走三步,slow走一步,这是不能的,例如一个两个元素呈环形的链表,它就一直会错过,如果不成环形链表,还会发生越界情况。

源代码:

    public boolean hasCycle(ListNode head) {

        ListNode fast=head;

        ListNode slow=head;

        if(head==null||head.next==null){

            return false;

        }

        while(fast!=null&&fast.next!=null){

            fast=fast.next.next;

            slow=slow.next;

            if(fast==slow){

                return true;

            }

        }

        return false;

    }

2.3.10 环形链表 II

        

        答案:

        

        思路:

        

源代码:

public ListNode detectCycle(ListNode head) {

        ListNode fast=head;

        ListNode slow=head;

        ListNode cur=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 null;

        }

        while(cur!=slow){

            cur=cur.next;

            slow=slow.next;

        }

        return cur;

    }


        三.双向链表

        3.1 什么是双向链表

        双向链表(Doubly Linked List)是链表的一种。

        在双向链表中,每个节点不仅包含数据和指向下一个节点的指针(称为“后继指针”),还包含指向前一个节点的指针(称为“前驱指针”)。

        这使得双向链表在某些操作上比单向链表更具优势。例如,在双向链表中,可以直接从尾部向头部遍历,而单向链表只能从头部向尾部遍历。

        3.2 双向链表的优点

        它解决了单向链表只能访问后一个元素的缺点,加了一个prev来表示前面的元素。

        如下图所示:

        加了一个prev来表示前一个变量,和一个last表示指向最后一个变量,使它既可以向前访问也可以向后访问,更加的灵活。

        3.3 双向链表中的方法

        3.3.1 display方法

        

        和上面的单项链表差不多一样。

         源代码:

        

public void display() {
shuangxiang cur=head;
while (cur!=null){System.out.print(cur.val+"  ");cur=cur.next;
}System.out.println();
}


3.3.2 size方法

        

        和单项链表的一样

        源代码:
public int size() {int count=0;shuangxiang cur=head;while (cur!=null){count++;cur=cur.next;}return count;
}


3.3.3 addfrist方法

        先判断是否为空,为空就直接返回,然后创建两个指针分别指向cur和cur1,直接改变第一个元素的next和prev即可,再移动一下头指针head即可。

        源代码:
public void addFirst(int data) {shuangxiang l6=new shuangxiang(data);if (head==null){head=l6;last=l6;return;}shuangxiang cur=head;shuangxiang cur1=last;cur.prev=l6;l6.next=cur;l6.prev=null;head=l6;
}


3.3.4 addlast方法

                还是先判断是否为空,不为空就对最后一个元素进行操作。

        源代码:
public void addLast(int data) {
shuangxiang q6=new shuangxiang(data);
if (head==null){head=last=q6;return;
}
last.next=q6;
q6.prev=last;
last=q6;
}


3.3.5 addIndex方法

        

        还是先判断是否为空,然后如果他要在第一个元素处添加,就直接调用addfrist方法,如果最后处添加,就调用addlast方法,然后用len来记录index的值,然后让prev指向原来位置的元素,让cur指向原来的前面的哪个元素,然后cur3和cur2的作用分别是记录原来的next和prev的值,使他接在后面。

        源代码:
public void addIndex(int index, int data) {
shuangxiang l6=new shuangxiang(data);
shuangxiang cur=head;
shuangxiang prev=last;
if (index>size()-1||index<0){throw new fushuyichang("输入异常,超出范围");
}
if (index==size()-1){addLast(data);return;
}
if (index==0){addFirst(data);return;
}
int len=index;
while (len!=0){prev=prev.prev;len--;
}
while (len+1!=index){cur=cur.next;len++;
}
shuangxiang cur2=cur.next;
shuangxiang cur3=prev.prev;
cur.next=l6;
l6.next=cur2;
prev.prev=l6;
l6.prev=cur3;
}


3.3.6 remove方法

        

        删除元素第一次出现的位置,然后先判断首尾是否为要删除的元素,然后再找中间的元素,通过找到要删除的元素的前一个元素,然后对他进行操作,即可删除该元素。

        源代码:
public void remove(int key) {
shuangxiang cur=head;
shuangxiang prev=last;
if (prev.val==key){shuangxiang prev2=prev.prev;prev.prev=null;prev2.next=null;return;
}
if (head.val==key){shuangxiang cur5=cur.next;cur.next=null;cur5.prev=null;head=cur5;return;
}
while (cur!=null){if (cur.next.val==key){cur.next=cur.next.next;cur.next.prev=cur;break;}cur=cur.next;
}
}


3.3.7 removeAllkey方法

        

        删除出现的全部元素,首先设立几个指针分别指向不同的对象,然后通过while语句先找到你要删除的前一个元素,然后对它进行操作,使其删除它,最后还要判断头元素是否相等,相等就直接让他等于后一个删除掉它。

        源代码:
public void removeAllKey(int key) {shuangxiang cur=head;shuangxiang cur3=head.next;while (cur3!=null){if (cur3.val==key){cur.next=cur.next.next;cur3=cur3.next;cur.next.prev=cur;}else {cur=cur3;cur3=cur3.next;}}if (head.val==key){head=head.next;}
}


2.3.8 contains方法

        

        和单向的一样,就是找是否存在这个元素。

        

        源代码:
public boolean contains(int key) {shuangxiang cur=head;while (cur!=null){if (cur.val==key){return true;}cur=cur.next;}return false;
}


四.ArrayListLinkedList的区别

        

五.结束语

        感谢大家的查看,希望可以帮助到大家,做的不是太好还请见谅,其中有什么不懂的可以留言询问,我都会一一回答。  感谢大家的一键三连。

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

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

相关文章

国际奥委会主席巴赫:阿里AI技术将巴黎奥运转播带到新高度

7月26日&#xff0c;巴黎奥运会开幕在即&#xff0c;国际奥委会主席巴赫在国际转播中心举行的活动中表示&#xff0c;2024巴黎奥运会展现了奥运转播的未来&#xff0c;广泛应用的阿里AI技术正在将巴黎奥运转播带到新的高度。 &#xff08;国际奥委会主席巴赫&#xff09; 在巴黎…

SQLite基础:简介、安装和命令

文章目录 1. SQLite简介1.1 SQL 和 SQLite 之间的差异1.2 SQLite 特性 2. SQLite安装3. SQLite命令4. SQLite点命令 1. SQLite简介 SQLite 是嵌入式关系数据库管理系统。它是自包含、无服务器、零配置和事务性 SQL 数据库引擎。其与大多数其他 SQL 数据库不同&#xff0c;SQLi…

视觉SLAM第一讲

第一讲-预备知识 SLAM是什么&#xff1f; SLAM&#xff08;Simultaneous Localization and Mapping&#xff09;是同时定位与地图构建。 它是指搭载特定传感器的主体&#xff0c;在没有环境先验信息的情况下&#xff0c;于运动过程中建立环境的模型&#xff0c;同时估计自己…

长效IP有哪些应用场景?

不同于经常重置的短效IP&#xff0c;长效IP以其长久稳定的特性&#xff0c;为各行各业提供了更为广阔的应用舞台。今天&#xff0c;就让我们一起探索极光http这一长效IP服务的魅力&#xff0c;看看它是如何成为众多领域首选的。 1. 远程工作的稳定后盾 在远程办公日益普及的今…

昇思25天学习打卡营第23天|LSTM+CRF序列标注

Mindspore框架CRF条件随机场概率图模型实现文本序列命名实体标注|&#xff08;一&#xff09;序列标注与条件随机场的关系 Mindspore框架CRF条件随机场概率图模型实现文本序列命名实体标注|&#xff08;二&#xff09;CRF模型构建 Mindspore框架CRF条件随机场概率图模型实现文本…

【Beyond Compare】Beyond Compare下载、安装与使用详细教程

目录 &#x1f33a;1 概述 &#x1f384;2 Beyond Compare 安装包下载 &#x1f33c;3 安装详细教程 &#x1f342;4 免费注册 &#x1f30d;5 使用详情 &#x1f33a;1 概述 Beyond Compare 是一款强大的文件和文件夹比较工具&#xff0c;广泛应用于软件开发、文档管理和…

Web动画(lottie篇)

一、Lottie简介 Lottie是一个库&#xff0c;可以解析使用AE制作的动画&#xff08;需要用bodymovin导出为json格式&#xff09;&#xff0c;支持web、ios、android和react native。在web侧&#xff0c;lottie-web库可以解析导出的动画json文件&#xff0c;并将其以svg或者canva…

electron调试

electron 调试 electron 的调试分两步&#xff0c;界面的调试&#xff0c;和主进程的调试。 界面调试类似浏览器F12&#xff0c;可是调试不到主进程。 主进程调试有vscode、命令行提示和外部调试器调试。 本篇记录的练习是vscode调试。命令行和外部调试器的方式可以参考官网&a…

CJS与ESM:CJS

模块化方案 历史上&#xff0c;JavaScript 一直没有模块&#xff08;module&#xff09;体系&#xff0c;无法将一个大程序拆分成互相依赖的小文件&#xff0c;再用简单的方法拼装起来。其他语言都有这项功能&#xff0c;比如 Ruby 的require、Python 的import&#xff0c;甚至…

PEFT LoRA 介绍(LoRA微调使用的参数及方法)

一 PEFT LoRA 介绍 官网简介如下图&#xff1a; 翻译过来是&#xff1a;低秩自适应(LoRA)是一种PEFT方法&#xff0c;它将一个大矩阵在注意层分解成两个较小的低秩矩阵。这大大减少了需要微调的参数数量。 说的只是针对注意力层&#xff0c;其实我自己平时微调操作注意力层多…

nacos2.x作为配置中心和服务注册和发现以及springcloud使用

目录 一、nacos是什么 二、windows下安装配置nacos 1、准备 2、安装nacos 3、配置nacos 4、启动并且访问nacos 三、springcloud使用nacos作为配置中心 四、springcloud使用nacos进行服务注册与发现 五、springcloud使用nacos进行服务消费 六、nacos的一些高级配置 1…

Ubuntu上编译多个版本的frida

准备工作 Ubuntu20(WSL) 略 安装依赖 sudo apt update sudo apt-get install build-essential git lib32stdc-9-dev libc6-dev-i386 -y nodejs 去官网[1]下载nodejs&#xff0c;版本的话我就选的20.15.1&#xff1a; tar -xf node-v20.15.1-linux-x64.tar.xz 下载源码 …

AbutionGraph时序(流式)图数据库开发文档地址

AbutionGraph-时序(流式)图数据库&#xff0c;官方开发文档(API)地址&#xff1a; http://www.thutmose.cn

JavaSE从零开始到精通(九) - 双列集合

1.前言 Java 中的双列集合主要指的是可以存储键值对的集合类型&#xff0c;其中最常用的包括 Map 接口及其实现类。这些集合允许你以键值对的形式存储和管理数据&#xff0c;提供了便捷的按键访问值的方式。 2. HashMap HashMap 是基于哈希表实现的 Map 接口的类&#xff0c…

java算法day23

java算法day23 121买卖股票的最佳时机55 跳跃游戏45 跳跃游戏Ⅱ763划分子母区间 121买卖股票的最佳时机 最容易想的应该就是两个for暴力枚举。但是超时 本题用贪心做应该是最快的。 先看清楚题&#xff0c;题目要求在某一天买入&#xff0c;然后在某一天卖出&#xff0c;要求…

MarkTool集合篇

MarkTool目前包含以下几种工具 1、TCP客户端 2、TCP服务端 3、UDP客户端 4、Web客户端 5、Web服务端 6、串口网口 7、PLC 8、获取本机设备 9、Log 10、密钥 11、系统设置 11-1、基本设置 11-2、角色设置 11-3、用户设置 11-4、log记录 开启软件需要找我解密&#…

S7-1200PLC通过111报文和EPOS模式实现位置轴轴控功能(FB284封装)

EASY_SINA_POS的详细使用介绍请参考下面文章链接: S7-1200PLC使用标准报文111和EPOS模式实现V90 PN总线伺服定位(Easy_SINA_Pos)_西门子sinapos-CSDN博客文章浏览阅读132次。文章浏览阅读7k次。先简单说下如何获取FB284,一般有2种方法,Startdrive软件可以操作大部分西门子的…

PostgreSQL使用(四)——数据查询

说明&#xff1a;对于一门SQL语言&#xff0c;数据查询是我们非常常用的&#xff0c;也是SQL语言中非常大的一块。本文介绍PostgreSQL使用中的数据查询&#xff0c;如有一张表&#xff0c;内容如下&#xff1a; 简单查询 --- 1.查询某张表的全部数据 select * from tb_student…

【Qt】QLabel常用属性相关API

QLabel是Qt框架中用于显示文本或图案的小部件。在Qt应用程序中&#xff0c;QLabel是用来呈现静态文本或图像给用户的重要部分 QLabel属性陈列 属性说明textQLabel中的文本内容textFormat 文本的格式 Qt::PlainText 纯文本Qt::RichText 富文本Qt::MarkdownText markdown…

【数据结构】排序算法(冒泡排序、插入排序、希尔排序、选择排序、堆排序、计数排序)

生命不可能有两次&#xff0c;但许多人连一次也不善于度过。&#x1f493;&#x1f493;&#x1f493; 目录 ✨说在前面 &#x1f34b;知识点一&#xff1a;排序的概念和应用 • &#x1f330;1.排序及其概念 • &#x1f330;2.排序的应用 • &#x1f330;3.常见的排序算…