对LinkedList ,单链表和双链表的理解

news2024/11/14 20:51:31
 一.ArrayList的缺陷
 二.链表
 三.链表部分相关oj面试题
     四.LinkedList的模拟实现
     五.LinkedList的使用
     六.ArrayList和LinkedList的区别
一.ArrayList的缺陷:
1. ArrayList底层使用 数组 来存储元素,如果不熟悉可以来再看看: ArrayList与顺序表-CSDN博客
由于其底层是一段连续空间,当 ArrayList 任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后 搬移,时间复杂度为 O(n) ,效率比较低,因此 ArrayList 不适合做任意位置插入和删除比较多的场景 。因此: java集合中又引入了 LinkedList,即链表结构
二.链表
1.链表的概念及结构:链表是一种 物理存储结构上非连续 存储结构,数据元素的 逻辑顺序 是通过链表中的引用链接次序实现的就像一个火车。
注意:1链表在逻辑上是连续的,在物理结构上不一定连续。
            2.节点一般都是从堆上申请出来的
          3.从堆上申请出来空间,是有它的分配规律和策略的,两次申请出来的可能连续也可能不续
2.链表的分类
单向或者双向循环和非循环带头和不带头就可以组合出8种类型的链表
虽然有这么多的链表的结构,但是我们重点掌握两种:
(1) 无头单向非循环链表:结构简单, 一般不会单独用来存数据。实际中更多是 作为其他数据结构的子结构,哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
(2)无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表
 

3.无头单向非循环链表实现
自己定义的类和包:


这里可以把方法先写在一个接口中,再通过MyLinkList实现接口,这样写可能更好,代码好复用。
我们
MyLinkList类中:我们要先抽象出每一个节点,每一个节点就是一个对象,我们可以写一个产生节点对象的模板:(这里可以定义一个静态 内部类,来抽象出节点模板):
 
public class MyLinkList {


    public int data;
    public MyLinkList.Node next;

    //静态内部类
     public static class Node {
        public int data;//0
        public Node next;//引用类型默认值为NULL

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

        }
    }
}

(1)头插方法:
public void addFirst(int data) {

        //第一次插入节点(链表为空)
        if (this.head == null) {
            Node node = new Node(data);//链表头为空时(head == null),整了链表的头引用为 node
           this.head = node;
           return;
        }


        //链表不为空,单链表插入要先绑后面
        Node node = new Node(data);
        node.next = this.head;
        head = node;//把node的引用给head,然head变成新的头
    }

(2)尾插法:

public void addList(int data) {

        //第一次插入时
        if (this.head == null) {
            Node node = new Node(data);
            head = node;
            return;
        }


        Node node = new Node(data);
        Node cur = this.head;//cur从头开始

        /*这里注意cur不可以先走到空,如果cur走到null,那么cur的next就是cull*/
        while (cur.next != null) {
            cur = cur.next;
        }

        //出来时cur==null,就尾插
        cur.next = node;
    }

(3)打印单链表:这里我们可以写一个,重载方法display2,可以让链表从返回的某个节点开始打印;

    //打印单链表
    public void display2(Node nodeH) {
        Node cur = this.head;//cur从头开始
        cur = nodeH;
        while (cur != null) {
            System.out.print(cur.data + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    public void display() {
        Node cur = this.head;//cur从头开始
        while (cur != null) {
            System.out.print(cur.data + " ");
            cur = cur.next;
        }
        System.out.println();
    }

(4)查找链表中是否包含某一数据节点:

 //查找是否包含关键字Key,是否在链表中
    public boolean contains(int key) {
        Node cur = this.head;
        while (cur != null) {
            if (cur.data == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

 
(5)清空链表:
 
public void clear() {
        Node cur = head;
        while (cur != null) {
            //注意定义一个,变量记住置为空的,后驱节点
           Node curN = cur.next;
           cur.next =null;//引用类型必须制空
           cur = curN;
        }

        //最后把头节点手动置为null
        head = null;
    }

(6).返回链表的长度:

public int size() {
        Node cur = this.head;
        int count = 0;//count不能为1,如果是空链表,count=1返回就,寄了
        while (cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

(7)任意位置插入:这里我画了个图来理解:

//任意位置插入(第一个数据节点为0号下标)
    public void addIndex(int index, int data) {


        //相当于头插
        if (index == 0) {
            addFirst(data);
            return;
        }

        //相当于尾插
        if (index == this.size()) {
            addList(data);
            return;
        }

        //正常插入方法:

        /**
         *    1. 先找到index前一个节点的地址->定义一个cur走index-1步
         *    2.画图插入
         */

        //先找到index前一个节点的地址
        Node cur = searchIndex(index);

        //插入
        Node node = new Node(data);

        /**
         * 这里注意,先绑后面(node = cur.next;),因为单链表前一个节点负责,单独的维护后一个节点,前一个节点的引用被覆盖(cur节点)
         * 那么原本和cur节点连接的节点就找不到了
         */

        node.next = cur.next;
        cur.next = node;

    }


    //找到index前一个节点的地址的方法
    private Node searchIndex(int index) {
        //index下标位置检验
        if (index < 0 || index > this.size()) {
            throw new RuntimeException("下标位置不合法");
        }

        Node cur = this.head;
        while (index-1 != 0/*走index-1步*/) {
            cur = cur.next;
            index--;
        }
        return cur;//返回走index-1步后的,cur类型地址
    }

(8)删除指定位置节点:
 //找key节点的前驱
    private Node searchPrev(int key) {
        Node prev = this.head;
        while(prev.next != null) {
            if (prev.next.data == key) {
                return prev;
            }else {
                prev = prev.next;//继续往后走
            }
        }
        return null;
    }


    //删除第一次出现关键字为key的节点
    public void remove(int key) {
        /** 1. 找到,要删除节点del的前驱
         *  2. 找到要删除的节点del
         *  3. 删除节点
         */

        //空节点直接返回
        if (this.head == null) {
            return;
        }

        //头节点直接删除
        if (this.head.data == key) {
            head = head.next;
            return;//这里注意别忘记了
        }

        //1. 找到,要删除节点del的前驱
        Node prev = searchPrev(key);
        if (prev == null) {
            throw new RuntimeException("没有你要删除的节点,请考量要删除的节点");
        }

        //2. 找到要删除的节点del
        Node del = prev.next;
        //3. 删除节点
        prev.next = del.next;
    }

(9)只遍历一遍链表,删除所有指定的节点:这里我画了一个图可以帮助理解:定义一个一直往后走快指针,和一个,不需要时往后走判断是否要删除慢指针

 //遍历单链表一遍,删除所有值为key的节点
    public void removeAllKey(int key) {
        /** 1.定义一个快指针 cur : cur指针一直往后走;
         *  2.定义一个慢指针 prev: prev指针,只有cur遇到要删除的数据时,prev指针才往后走,不然保持不动
         *  3.注意最后不要漏了,head头节点
         */

       // 1.定义一个 cur指针 : cur指针一直往后走
       //  2.定义一个 prev指针: prev指针,只有cur遇到要删除的数据时,prev指针才往后走,不然保持不动
        Node cur = this.head.next;//
        Node prev = this.head;

        while (cur != null) {
            if (cur.data == key) {
                //cur.data == key,时只有cur指针都在走,因为要遍历删除数据
                prev.next = cur.next;
                cur = cur.next;
            }else {
                //cur.data != key,两个指针都在动,prev指针,指向cur指针
                prev = cur;
                cur = cur.next;
            }
        }

       // 3.注意最后不要漏了,head头节点
        if (this.head.data == key) {
            this.head = this.head.next;
        }
    }

 三.链表部分相关oj面试题:(分享一些我认为比较重要的)
1.  反转一个单链表:我录了视频方便理解: 反转一个链表-CSDN直播

反转一个链表

class Solution {
    public ListNode reverseList(ListNode head) {

        ListNode cur = head;
        
        if(head == null) {
            return null;
        }

        head = null;
        while(cur != null) {
         ListNode curN = cur.next;
         cur.next = head;
         head = cur;
         cur = curN;
        }

        return head;
    }
}


2.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点:

理解视频:找到链表中间节点-CSDN直播

找到链表中间节点

class Solution {
    public ListNode middleNode(ListNode head) {
        if(head == null) {
            return null;
        }

        ListNode fast = head;//快指针一次走2步
        ListNode slow = head;//慢指针一次走一步
//条件不可以交换:(fast != null && slow.next != null),fast可能开始就为null
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }

        return slow;
    }
}

3.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的:

理解视频:合并两个有序链表-CSDN直播

合并两个有序链表


 

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode headH = new ListNode(-1);
        ListNode tmp = headH;//tmp用来遍历两个链表
        while(list1 != null && list2 != null) {
            //哪个节点数据小,就接在tmp后面
            if(list1.val < list2.val) {
                tmp.next = list1;
                list1 = list1.next;
                tmp = tmp.next;
            }else {
                tmp.next = list2;
                list2 = list2.next;
                tmp = tmp.next;
            }
        }

        //当其中一个链表遍历完,就直接接上另一个链表的后半部分
        if(list1 != null) {
            tmp.next = list1;
        }

        if(list2 != null) {
            tmp.next = list2;
        }

        return headH.next;
    }
}

4.链表的回文结构:

这里有两个点要注意:1.从后往前用slow走,因为偶数节点,fast指针会走到null,无法往前走

 2.回文时偶数情况下,A的下一个节点是slow节点,并且两个节点的val相等。这个时候就要直接返回ture

理解视频:链表的回文结构-CSDN直播

链表的回文结构

public class PalindromeList {
    public boolean chkPalindrome(ListNode A) {
        // write code here
        if (A == null) {
            return true;
        }

        // write code here
        ListNode fast = A;
        ListNode slow = A;

        //1.找到中间节点
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }


        //2.翻转链表
        ListNode cur = slow.next;
        while (cur != null) {
            ListNode curN = cur.next;
            cur.next = slow;
            slow = cur;
            cur = curN;
        }


        //3.判断回文
        //让A往后走,slow往前走直到;A.val==slow.val
        //注意:回文时会有偶数情况下,A的下一个节点是slow节点,并且两个节点的val相等。这个时候就要直接返回ture
        while (A != slow) {
            if (A.val != slow.val) {
                return false;
            }
            //到这里A.val == slow.val

            //A.val == slow.val前提下,偶数情况下,A的下一个节点是slow节点,并且两个节点的val相等。这个时候就要直接返回ture
            if (A.next == slow) {
                return true;
            }

            A = A.next;
            slow = slow.next;
        }
        return true;
    }
}


 

5.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前:

注意:这里我的方法是,改完后,链表数据从小到大的,而做题在牛客网是,要求反过来(但是方法都一样)

理解视频:链表分割-CSDN直播

链表分割

//链表的分割
    public Node partition(Node pHead, int x) {
        Node as = null;
        Node ae = null;
        Node bs = null;
        Node be = null;
        Node cur = pHead;

        while (cur != null) {
            if (cur.data > x) {
                //第一次插入
                if (as == null) {
                    as = ae = cur;
                }else {//第N次插入

                    ae.next = cur;
                    ae = ae.next;
                }
            } else {
                //第一次插入
                if (bs == null) {
                    bs = be = cur;
                }else{//第N次插入
                    be.next = cur;
                    be = be.next;
                }
            }

            cur = cur.next;
        }

        //当一个链表为空时,返回
        if(as == null) {
            return bs;
        }

        //如果到这里as!= null
        //连接两部分
        ae.next = bs;

        //注意,第二部分结尾不为空时,要手动把第二部分最后一个节点,手动制空
        if(bs != null) {
            be.next = null;
        }

        //最后返回as
        return bs;
    }

6.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环返回空 :

方法是:第一次相遇点,到入口点的距离,等于起始点到入口点的距离

这里我画了这个图的推到:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

//  方法:第一次相遇点,到入口点的距离,等于起始点到入口点的距离
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
             if(fast == slow) {
                break;
            }
        }
      
     
      /**

      1.走到这里,要么不满足{(fast != null && fast.next != null)}
       就是没有环;
      2. 要么就是有环
       */
       
       //没有环
       if(fast == null || fast.next == null) {
        return null;
       }

       /**
        有环:让slow以和fast以相同的速度,从起始点到入口点,
       fast从第一次相遇的成环点走到入口点
        */
    
        slow = head;//把slow返回起始点
        while(slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }

        return slow;
    }
}


7.输入两个链表,找出它们的第一个公共结点:

方法:先找到哪个链表长,再让长的链表他们的差值步最后两个链表一起走,直到他们第一次相遇。

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
       //1.先分别求出两个链表的长度
        ListNode pl = pHead1;
        ListNode ps = pHead2;
        int lenA = 0;
        int lenB = 0;

        while (pl != null) {
            lenA++;
            pl = pl.next;
        }
        while (ps != null) {
            lenB++;
            ps = ps.next;
        }
        //注意pl和ps,指向了null,要赋值回来
        pl = pHead1;
        ps = pHead2;

        //2.求差值
        int len = lenA - lenB;
        
        if (len < 0) {
            pl = pHead2;
            ps = pHead1;
            len = lenB - lenA;//len变为为正数
        }

        //现在知道pl指向长的链表,ps指向短的链表

        //3.操作两个链表pl和ps,长的链表(pl)先走链表的差值,然后再一起走直到相交
        while (len != 0) {
            pl = pl.next;
            len--;
        }

        //两个链表分别都走,直到他们相遇
        while (pl != ps) {
            pl = pl.next;
            ps = ps.next;
        }

        if (pl == null) {
            //pl,ps为空,也不可能相交
            return null;
        }
        

        return pl;
    }
}


 

 四.LinkedList的模拟实现:无头双向链表实现
 
1.写的类和包:
其实 无头双向链表,就比单链表多了一个,可以指向前一个节点的引用域,并且尾节点也被一个引用记录着。这样任意位置插入就不用记录节点了。
2.实现:
这里注意一下删除双链表指定位置Remove的节点 :可以优化一下代码,先删除头节点,之后尾节点和中间任意位置节点,有重复代码,(cur.prev.next = cur.next)可以共用;
public class MyLinkList implements  IList{


   static class ListNode{
       public int val;
       public ListNode prev;
       public ListNode next;

       public ListNode(int val) {
           this.val = val;
       }
   }

   public ListNode head;//头节点
   public ListNode last;//尾节点

    @Override
    public void addFirst(int data) {
        ListNode node = new ListNode(data);

        if (head == null) {
            head = last = node;
        }else {
            //所有的插入优先绑定后面
            node.next = head;
            head.prev = node;
            head = node;
        }
    }

    @Override
    public void addLast(int data) {
        ListNode node = new ListNode(data);

        if (head == null) {
            head = last = node;
        }else {
            last.next = node;
            node.prev = last;
            last = last.next;
        }
    }

    @Override
    public void addIndex(int index, int data) {
        int len = size();
        if (index > len || index < 0) {
            return;
        }

        if (index == len) {
            addLast(data);
        }

        if (index == 0) {
            addFirst(data);
        }

        ListNode cur = findIndex(index);
        ListNode node = new ListNode(data);
        node.next = cur;
        cur.prev.next = node;
        node.prev = cur.prev;
        cur.prev = node;
    }


    private ListNode findIndex(int index) {
        ListNode cur = head;
        while (index != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    @Override
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }



    @Override
    public void remove(int key) {
        ListNode cur = head;

        while (cur != null) {
            if (cur.val == key) {
            if (cur == head) {

                //当只有一个节点要删除时
                if (head == null) {
                    cur.next.prev = null;
                }
                head = head.next;
                //删除就走人
                return;
            }else {
                cur.prev.next = cur.next;//优化后,删除中间和尾巴的代码
                if (cur == last) {
                    last = cur.prev;
                }else {
                    cur.next.prev = cur.prev;
                }
                //删除就走人
                return;
            }
            }
            cur = cur.next;

        }
    }

    @Override
    public void removeAllKey(int key) {
        ListNode cur = head;

        while (cur != null) {
            if (cur.val == key) {
                if (cur == head) {

                    //当只有一个节点要删除时,cur.next.prev = null会为空,所以加上if判断
                    if (head == null) {
                        cur.next.prev = null;
                    }
                    head = head.next;
                    //删除不能走人,接着删除后面。

                }else {
                    cur.prev.next = cur.next;//优化后,删除中间和尾巴的代码
                    if (cur == last) {
                        last = cur.prev;
                    }else {
                        cur.next.prev = cur.prev;
                    }
                    //删除不能走人,接着删除后面。
                }
            }
            cur = cur.next;
        }
    }

    @Override
    public int size() {
        ListNode cur = head;
        int len = 0;
        while (cur != null) {
            len++;
            cur = cur.next;
        }
        return len;
    }

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

    @Override
    public void clear() {
        ListNode cur = head;
        while (cur != null) {
            ListNode curN = cur.next;
            cur.next = null;
            cur.prev = null;
            cur = curN;
        }
        //注意head和last节点在链表中还被引用着
        head = last = null;
    }
}

五.LinkedList的使用:
1.什么是LinkedList:
LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。
2.  在集合框架中,LinkedList也实现了List接口,具体如下:

总结
1. LinkedList实现了List接口
2. LinkedList的底层使用了双向链表
3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问
4. LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)
5. LinkedList比较适合任意位置插入的场景

 
3. LinkedList也有有参数和二无参数的构造方法:
4.方法的使用表参考:
public class Test {
    public static void main(String[] args) {

        List<Integer> list = new LinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        System.out.println(list);

        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(11);
        list1.add(12);
        list1.add(13);

        System.out.println("==============");
        list.addAll(list1);
        System.out.println(list);

    }
}

输出:

5.LinkedList的遍历:ListIteratorIterator的一个子类,可以专门用来打印链表

代码如下:

public class Test {
    public static void main(String[] args) {

        List<Integer> list = new LinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        System.out.println(list);

        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(11);
        list1.add(12);
        list1.add(13);



        System.out.println("foreach遍历");
        for (Integer x:list) {
            System.out.print(x + " ");
        }
        System.out.println();

        System.out.println("迭代器遍历历");
        Iterator<Integer> it = list.iterator();
        while (it.hasNext()) {
            System.out.print(it.next() + " ");
        }

        /**
         * ListIterator是Iterator的一个子类,可以专门用来打印链表
         */

        System.out.println();
        System.out.println("使用迭代器遍历---正向遍历");
        ListIterator<Integer> it1 = list.listIterator();
        while (it1.hasNext()) {
            System.out.print(it1.next() + " ");
        }



        System.out.println();
        System.out.println("使用反向迭代器---反向遍历");

        ListIterator<Integer> it2 = list.listIterator(/*这里要传链表的长度*/ list.size());
        while (it2.hasPrevious()) {
            System.out.print(it2.previous() + " ");
        }
    }
}


 

 六.ArrayList和LinkedList的区别:

                                                                                 

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

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

相关文章

zephyr BLE创建自定义服务

目录 LBS服务介绍实现过程 以创建LBS服务为例&#xff0c;在蓝牙标准里面没有这个服务&#xff0c;但是nordic有定制这个服务。 LBS服务介绍 实现过程 定义 GATT 服务及其特性的 128 位 UUID。包括服务UUID&#xff0c;特征的UUID。 #define BT_UUID_LBS_VAL BT_UUID_128_EN…

【BUG】已解决:ValueError: Expected 2D array, got 1D array instead

已解决&#xff1a;ValueError: Expected 2D array, got 1D array instead 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉…

“论软件维护方法及其应用”精选范文,软考高级论文,系统架构设计师论文

论文真题 软件维护是指在软件交付使用后&#xff0c;直至软件被淘汰的整个时间范围内&#xff0c;为了改正错误或满足 新的需求而修改软件的活动。在软件系统运行过程中&#xff0c;软件需要维护的原因是多种多样的&#xff0c; 根据维护的原因不同&#xff0c;可以将软件维护…

【Linux】线程——线程互斥的概念、锁的概念、互斥锁的使用、死锁、可重入和线程安全、线程同步、条件变量的概念和使用

文章目录 Linux线程4. 线程互斥4.1 线程互斥的概念4.2 锁的概念4.2.1 互斥锁的概念4.2.2 互斥锁的使用4.2.3 死锁4.2.4 可重入和线程安全 5. 线程同步5.1 条件变量的概念5.2 条件变量的使用 Linux线程 4. 线程互斥 我们之前使用了线程函数实现了多线程的简单计算模拟器。 可以…

3D问界—在MAYA中使用Python脚本进行批量轴居中

问题提出&#xff1a;MAYA中如何使用Python脚本 今天不是一篇纯理论&#xff0c;主要讲一下MAYA中如何使用Python脚本&#xff0c;并解决一个实际问题&#xff0c;文章会放上我自己的代码&#xff0c;若感兴趣欢迎尝试&#xff0c;当然&#xff0c;若有问题可以见文章末尾渠道&…

防火墙--带宽管理

目录 核心思想 带宽限制 带宽保证 连接数的限制 如何实现 接口带宽 队列调度 配置位置 在接口处配置 带宽策略配置位置 带宽通道 配置地方 接口带宽、带宽策略和带宽通道联系 配置顺序 带块通道在那里配置 选项解释 引用方式 策略独占 策略共享 重标记DSCP优先…

C# 中IEnumerable与IQuerable的区别

目的 详细理清IEnumerator、IEnumerable、IQuerable三个接口之间的联系与区别 继承关系&#xff1a;IEnumerator->IEnumerable->IQuerable IEnumerator&#xff1a;枚举器 包含了枚举器含有的方法&#xff0c;谁实现了IEnuemerator接口中的方法&#xff0c;就可以自定…

【坑】微信小程序开发wx.uploadFile和wx.request的返回值格式不同

微信小程序 使用wx.request&#xff0c;返回值是json&#xff0c;如下 {code:200,msg:"更新用户基本信息成功",data:[]} 因此可以直接使用如 res.data.code获取到返回值中的code字段 但是&#xff0c;上传图片需要使用wx.uploadFile&#xff0c;返回的结果如下 …

【知识图谱】【红楼梦】

参考链接 安装、使用教程&#xff08;知乎&#xff09;&#xff1a;https://zhuanlan.zhihu.com/p/634006024Git &#xff1a;https://github.com/chizhu/KGQA_HLM 注&#xff1a;原项目为 【 重庆邮电大学&#xff0c;2018 林智敏 的毕业设计 】。【 感谢大佬的分享 】。 jav…

Web渗透:Shiro550漏洞(CVE-2016-4437)

Apache Shiro 是一个强大且易于使用的Java安全框架&#xff0c;提供了身份验证&#xff08;Authentication&#xff09;、授权&#xff08;Authorization&#xff09;、会话管理&#xff08;Session Management&#xff09;和密码学支持等功能。Apache Shiro 550反序列化漏洞&a…

set类和map类介绍和简单使用

目录 set类介绍与简单使用 set类 multiset类 map类介绍与简单使用 map类 multimap类 set类介绍与简单使用 set类是一种关联式容器&#xff0c;在数据检索时比序列式容器效率更高。本质是一个常规的二叉搜索树&#xff0c;但是为了防止出现单支树导致效率下降进行了相关优…

188数码管轮询扫描

前言 最近用到了188数码管&#xff0c;总结一下。 188数码管&#xff0c;用5个IO&#xff0c;在不借助外部驱动芯片的情况下&#xff0c;可以点亮20个灯。188数码管广泛应用于电子烟、充电器、充电宝、DVD、高级音响、工业设备控制面板、医疗器械等多个领域&#xff0c;满足不…

FPGA FIR fdatool filter designer MATLAB

位数问题 fdatool 先确定输入信号的位宽&#xff0c;比如17位在fdatool中&#xff0c;选set quantization parameters 选input/output 设置input word length 为17bit(not confirmed) fir compiler implementation 注意&#xff1a; 当设置输入位宽为16位时&#xff0c;ip核…

Java 快速入门学习 -- Day 2

Java 快速入门 Ⅱ maven&#xff08;图书管理员&#xff09;IDEA使用 maven框架 maven&#xff08;图书管理员&#xff09; maven 仓库&#xff0c;图书馆。要看书的化先从家里找&#xff08;本地仓库&#xff09;&#xff0c;本地找不到就去中央仓库或者镜像仓库找&#xff0c…

CSA笔记3-文件管理命令(补充)+vim+打包解包压缩解压缩命令

grep(-i -n -v -w) [rootxxx ~]# grep root anaconda-ks.cfg #匹配关键字所在的行 [rootxxx ~]# grep -i root anaconda-ks.cfg #-i 忽略大小写 [rootxxx ~]# grep -n root anaconda-ks.cfg #显示匹配到的行号 [rootxxx ~]# grep -v root anaconda-ks.cfg #-v 不匹配有…

记录些MySQL题集(8)

ACID原则、事务隔离级别及事务机制原理 一、事务的ACID原则 什么是事务呢&#xff1f;事务通常是由一个或一组SQL组成的&#xff0c;组成一个事务的SQL一般都是一个业务操作&#xff0c;例如聊到的下单&#xff1a;「扣库存数量、增加订单详情记录、插入物流信息」&#xff0…

AQS详解

文章目录 AQS 是什么&#xff1f;AQS 的原理是什么&#xff1f;AQS 资源共享方式总结 AQS 是什么&#xff1f; AQS 的全称为 AbstractQueuedSynchronizer &#xff0c;翻译过来的意思就是抽象队列同步器。这个类在 java.util.concurrent.locks 包下面。 AQS是一个用来构建锁和…

【操作系统】定时器(Timer)的实现

这里写目录标题 定时器一、定时器是什么二、标准库中的定时器三、实现定时器 定时器 一、定时器是什么 定时器也是软件开发中的⼀个重要组件.类似于⼀个"闹钟".达到⼀个设定的时间之后,就执行某个指定 好的代码. 定时器是⼀种实际开发中⾮常常用的组件. ⽐如⽹络通…

base SAS programming学习笔记13(Array)

1.Array array-name{dimension} <elements> array-name&#xff1a;向量名称 dimension&#xff1a;向量长度&#xff0c;默认为1&#xff1b; elements:列出变量名&#xff0c;变量名要么全是数值变量或者全是字符变量 array-name和variable不能相同&#xff1b;也不能和…

【BUG】已解决:java.lang.IllegalStateException: Duplicate key

已解决&#xff1a;java.lang.IllegalStateException: Duplicate key 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市…