Java集合框架:链表和LinkedList详解

news2024/11/23 17:07:50

目录

一、ArrayList的缺陷

二、链表(主要介绍不带头的非循环的 单链表 / 双链表)

    注:

三、模拟链表的实现:

 MySingleList(单链表)

 MyLinkedList(双链表)

四、LinkedList的使用

1. LinkedLis的构造

2. LinkedList的常用方法

3. LinkedList的遍历

五、ArrayList和LinkedList的区别

六、链表的oj练习

oj链接:

解析:


前言

    上篇文章总结了ArrayList的底层实现和方法的基本使用,可以了解ArrayList底层是使用数组来实现的,ArrayList的优点就是可以支持数据的随机访问,但是ArrayList也有缺点,所以每个集合都有自己特定的使用场景。

一、ArrayList的缺陷

1. 不适合做任意位置插入和删除操作(插入或删除元素时,需要将后序的元素往前或者往后挪动,时间复杂度O(N))

2. 开辟的空间是连续的,开辟的空间不够时需要动态扩容

    所以此时也就引出了链表这种数据结构;

二、链表(主要介绍不带头的非循环的 单链表 / 双链表)

    

    如上图所示链表的结构:是一种物理存储结构上不连续的存储结构,数据元素的逻辑顺序是通过链表节点中的引用连接下一个数据元素的。

    注:

1. 如上图:链表结构在逻辑上是连续的,但是在物理上不一定连续
2. 节点一般都是从堆上申请出来的
3. 从堆上申请的空间,是按照一定的策略分配的,申请的空间可能是连续的,也可以不是连续的

三、模拟链表的实现:

1. MySingleList --- 单链表的实现 (只有一个next域,有定义好的head)

2. MyLinkedList --- 双链表的实现 (有next和prev 域,有定义好的head 和 last)(head引用头节点,last引用最后一个节点)

 MySingleList(单链表)

//单链表的实现
/*Ctrl + R 选中类中相同的变量名称然后,改名*/
public class MySingleList {
    public int val;
    //内部类如果是static的,生成对象的时候是不依赖于外部类对象的
    //不加static,生成对象的时候依赖于外部类对象
    static class ListNode {
        public int val;//存储值
        public ListNode next;//存储next域
        public ListNode(int val) {
            this.val = val;
        }
    }
    public ListNode head;//head引用的是当前列表的头节点

    public void createLink() {
        ListNode listNode1 = new ListNode(1);
        ListNode listNode2 = new ListNode(2);
        ListNode listNode3 = new ListNode(3);
        ListNode listNode4 = new ListNode(4);
        listNode1.next = listNode2;
        listNode2.next = listNode3;
        listNode3.next = listNode4;
        head = listNode1;
        //当这个方法走完,node1,2,3,4....都被回收了,
        //他们都是局部变量
    }

    //遍历打印链表
    public void display() {
        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    //从指定位置开始打印链表
    public void display(ListNode newHead) {
        ListNode cur = newHead;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
    //查找是否包含关键字key,是否在单链表中
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) return true;
            cur = cur.next;
        }
        return false;
    }
    //得到单链表的长度
    public int size() {
        ListNode cur = head;
        int size = 0;
        while (cur != null) {
            size++;
            cur = cur.next;
        }
        return size;
    }

    //头插法
    public void addFirst(int data){
        ListNode listNode = new ListNode(data);
        listNode.next = head;
        head = listNode;
    }
    //尾插法(考虑如果当前链表没有节点)
    public void addLast(int data) {
        ListNode listNode = new ListNode(data);
        if (head == null) {
            head = listNode;
            return;//不要忘了return
        }
        ListNode cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = listNode;
    }
    //指定下标位置插入节点
    public void addIndex(int index, int data) {
        checkIndex(index);
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        ListNode listNode = new ListNode(data);
        ListNode cur = findIndexSubOne(index);//cur走到下标的前一个位置
        listNode.next = cur.next;
        cur.next = listNode;
    }
    //找到index-1位置的节点的地址
    private ListNode findIndexSubOne(int index) {
        ListNode cur = head;
        int count = 0;
        while (count != index - 1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }
    private void checkIndex(int index) {
        if (index < 0 || index > size()) {
            throw new ListIndexOutOfException("下标位置不合法!");
        }
    }

 MyLinkedList(双链表)

package Review;
/*和单链表不一样,双链表引入了一个last引用来指向最后一个节点(一直是指向最后一个节点)
* 也就是说在开始没有节点时,head和last节点都是指向头节点的*/
/*所有的插入都是先绑后边*/
public class MyLinkedList {
    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;
    //打印遍历链表
    public void display() {
        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
    //头插法O(1)
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;
            last = node;
        }else {
            node.next = head;
            head.prev = node;
            head = node;
        }
    }
    //尾插法O(1):因为省去了找尾巴的过程
    public void addLast(int data) {
        //如果链表中没有元素
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;
            last = node;
        }else {
            last.next = node;
            node.prev = last;
            last = node;
        }

    }

    //任意位置插入,第一个数据节点为0号的下标
    public void addIndex(int index, int data) {
        if (index < 0 || index >size()) {
            throw  new ListIndexOutOfException();
        }
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        //不是头插不是尾插,在中间插入一个元素
        ListNode cur = head;
        while (index != 0) {
            cur = cur.next;
            index--;
        }
        ListNode node = new ListNode(data);
        node.next = cur;
        cur.prev.next = node;
        node.prev = cur.prev;
        cur.prev = node;
    }
    //查找是否包含关键字key在链表中
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    // 删除关键字为key的节点
    public void remove(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                //1.删除的是头节点
                if (cur == head) {
                    head = head.next;
                    //如果head.next是空的(只有一个节点),此时空指针异常
                    if (head != null) {
                        head.prev = null;
                    }
                } else {
                    //中间  尾巴
                    cur.prev.next = cur.next;
                    //不是尾巴节点
                    if (cur.next != null) {
                        cur.next.prev = cur.prev;
                    } else {
                        //是尾巴节点
                        last = last.prev;
                    }
                }
                return;
            }
            cur = cur.next;
        }
    }
    //删除所有值为key的节点
    public void removeKeyAll(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                //1.删除的是头节点
                if (cur == head) {
                    head = head.next;
                    //如果head.next是空的(只有一个节点),此时空指针异常
                    if (head != null) {
                        head.prev = null;
                    }
                } else {
                    //中间  尾巴
                    cur.prev.next = cur.next;
                    //不是尾巴节点
                    if (cur.next != null) {
                        cur.next.prev = cur.prev;
                    } else {
                        //是尾巴节点
                        last = last.prev;
                    }
                }
            }
            cur = cur.next;
        }
    }
    //求长度
    public int size() {
        int count = 0;
        ListNode cur = head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }
    //将双向链表置空
    public void clear() {
        ListNode cur = head;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.prev = null;
            cur.next = null;
            cur = curNext;
        }
        //最后让这俩引用也要置空
        head = null;
        last = null;
    }
}

四、LinkedList的使用

1. LinkedList实现了List接口(ArrayList也实现了List接口)
2. LinkedList的底层就是一个双向链表的结构
3. LinkedList没有实现RandomAccess接口,所以不支持随机访问

4. LinkedList适合插入和删除的场景,时间复杂度都为O(1)

1. LinkedLis的构造

LinkedList()无参构造
public LinkedList(Collection<? extends E> c)传一个实现了Collection接口类的具体对象的参数(<>中是泛型参数的上界,这个类型必须继承了E或者是E本身)

构造器的演示:

//源码中的LinkedList在new对象时的参数只要是实现了Collection接口的
        //具体类都可以作为参数
        /*Collection<? extends E> c 代表的要么是String,要么是String的子类
        * c代表的是泛型参数的上界*/
        ArrayList<String> arrayList = new ArrayList<>();
        LinkedList<String> list = new LinkedList<>(arrayList);
        list.add("hello");
        list.add("ok");
        list.add("world");
        System.out.println(list);

2. LinkedList的常用方法

add(E e)添加e数据元素(尾插法)
add(int index, E element)将e插入到index位置
addAll(Collection<? extends E> c)插入集合c中的元素(c类必须实现了Collection接口)
remove(int index)删除index位置元素
get(int index) set(int index,E elment)获取和设置index下标位置的元素
contains(int key)判断是否包含key元素
List<E> subList(int fromIndex, int toIndex)截取部分list,注:返回值是List<E>

3. LinkedList的遍历

1. 直接遍历(因为重写了toString方法)
2. for循环或者for each循环遍历
3. 迭代器遍历
public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        LinkedList<String> list = new LinkedList<>(arrayList);
        list.add("hello");
        list.add("ok");
        list.add("world");
        for (String str : list) {
            System.out.print(str + " ");
        }
        System.out.println();
        ListIterator<String> it = list.listIterator(list.size());
        //listIterator()括号中可以给参数或者没有参数,如果有参数代表的是
        //list中的下标的位置,要从哪一个下标位置开始遍历
        while (it.hasPrevious()) {
            //此时就是反向遍历list中的元素
            System.out.print(it.previous() + " ");
        }

五、ArrayList和LinkedList的区别

  1. ArrayList的物理存储空间是连续的,LinkedList物理上不一定连续。
  2. ArrayList支持随机访问,LinkedList不支持随机访问。
  3. ArrayList在插入和删除的时候时间复杂度O(N),LinkedList时间复杂度O(1);所以LinkedList适合频繁插入和删除的场景。
  4. ArrayList空间不够需要动态扩容,LinkedList不需要。

 

六、链表的oj练习

oj链接:

1. 删除链表中等于给定值 val 的所有节点。力扣
2. 反转一个单链表。 力扣
3. 返回链表的中间节点。力扣
4. 输入一个链表,输出该链表中倒数第k个结点。链表中倒数第k个结点_牛客题霸_牛客网
5. 合并两个有序的链表。力扣
6. 以给定值x为基准将链表分成两部分,所有小于x的结点排在大于或等于x的结点之前 。链表分割_牛客题霸_牛客网
7. 判断链表是否是回文结构。链表的回文结构_牛客题霸_牛客网
8. 输出链表的公共节点。力扣
9. 判断链表是否有环。力扣

解析:

//链表的反转
    public ListNode ReverseList() {
        if (head == null) return null;
        if (head.next == null) return head;
        ListNode cur = head.next;
        head.next = null;
        while (cur != null) {
            ListNode curNext = cur.next;
            //头插法
            cur.next = head;
            head = cur;
            cur = curNext;
        }
        return head;
    }
    //返回链表的中间节点
    public ListNode middleNode(ListNode head) {
        if (head == null) return null;
        ListNode s = head;
        ListNode f = head;
        //此时要注意,f走一步就需要判断是否为空,否则可能走一步就
        //空指针异常了
        while (f != null && f.next != null) {
            f = f.next.next;
            s = s.next;
        }
        //结束的情况: fast = null && fast.next = null
        return s;
    }
    //返回链表的倒数第k个节点
    public ListNode FindKthToTail (ListNode pHead, int k) {
        // write code here
        //快指针先走k-1步,然后和慢指针一起走,此时返回的慢指针就是倒数第k个节点
        if (k < 0 || pHead == null) return null;
        ListNode cur = pHead;
        int len = 0;
        while (cur != null) {
            cur = cur.next;
            len++;
        }
        ListNode fast = pHead;
        ListNode slow = pHead;
        while (k !=  1) {
            fast = fast.next;
            if (fast == null) return null;
            k--;
        }
        //fast和slow一起走
        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
    //合并两个有序的链表
    public ListNode Merge(ListNode list1, ListNode list2) {
        ListNode newHead = new ListNode(-1);
        ListNode tmp = newHead;
        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                tmp.next = list1;
                list1 = list1.next;
                tmp = tmp.next;
            }else {
                tmp.next = list2;
                list2 = list2.next;
                tmp = tmp.next;
            }
        }
        //如果list1为空了
        if (list2 != null) {
            tmp.next = list2;
        }
        if (list1 != null) {
            tmp.next = list1;
        }
        return newHead.next;
    }
    //判断链表是否是回文结构
    public boolean isPail (ListNode head) {
        if (head ==null) return false;
        if (head.next == null) return true;
        //1.找中间节点
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        //2.反转
        ListNode cur = slow.next;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.next = slow;
            slow = cur;
            cur = curNext;
        }
        //3.一个从头往后走,一个从后往前走
        //这里不要和fast指针的值进行比较,fast偶数情况下走到最后已经空指针了
        //也不能和cur进行比较,因为最后cur在什么情况下都会空指针
        while (slow != head) {
            if (head.val != slow.val) {
                return false;
            }
            //偶数的情况
            if (head.next == slow) {
                return true;
            }
            slow = slow.next;
            head = head.next;
        }
        return true;
    }
    //链表分割
    /*思路:分成两个段,第一个段放小于x的节点,第二个段放大于x的节点,然后把两个段
    * 连起来,此时要考虑是否同时有小于x的和大于x的数据,之后要考虑串起来之后的最后
    * 一个节点next是否需要置空*/
    public ListNode partition(int x) {
        ListNode bs = null;
        ListNode be = null;
        ListNode as = null;
        ListNode ae = null;
        ListNode cur = head;
        while (cur != null) {
            if (cur.val < x) {
                if (bs == null) {
                    bs = cur;
                    be = cur;
                }else {
                    be.next = cur;
                    be = be.next;
                }
            }else {
                if (as == null) {
                    as = cur;
                    ae = cur;
                } else {
                    ae.next = cur;
                    ae = ae.next;
                }
            }
            cur = cur.next;
        }
        //有可能不会同时存在小于x和大于等于x的数据
        //如果第一个段中没有数据,就返回第二个段,如果第二个段还没有数据
        //此时直接返回null,
        if (bs == null) return as;
        //第一个段不为空
        be.next = as;
        //如果第二个段不为空,也就是有>x的数据,此时需要把最后一个节点next域置空
        if (as != null) {
            ae.next = null;
        }
        return bs;
    }
    //相交链表
    public ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
        //1.先求长度
        ListNode cur1 = headA, cur2 = headB;
        int len1 = 0, len2 = 0;
        while (cur1 != null) {
            len1++;
            cur1 = cur1.next;
        }
        while (cur2 != null) {
            len2++;
            cur2 = cur2.next;
        }
        //2.让比较长的链表的cur 走差值的步数
        cur1 = headA;//这个地方一定要注意:让cur1和cur2指回来
        cur2 = headB;//因为求完长度之后,cur1 和 cur2 都已经是空的了
        int len = Math.abs(len1 - len2);
        while (len != 0) {
            if (len1 < len2) {
                cur2 = cur2.next;
            }else {
                cur1 = cur1.next;
            }
        }
        //2.然后再让cur1 和 cur2 一起走
        while (cur1 != cur2) {
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode pl = headA,ps = headB;
        int len1 = 0,len2 = 0;
        while (pl != null) {
            len1++;
            pl = pl.next;
        }
        while (ps != null) {
            len2++;
            ps = ps.next;
        }
        //2.让pl和ps 指回来
        pl = headA;
        ps = headB;
        int len = len1 - len2;
        //3.根据len的值修改pl 和 ps 的指向
        if (len < 0) {
            pl = headB;
            ps = headA;
            len = len2 - len1;
        }//此时len一定是一个正数 pl一定指向的是最长的  ps一定指向的是最短的
        while (len != 0) {
            pl = pl.next;
            len--;
        }
        while (pl != ps) {//这里可以不用判断pl和ps是否是空的,如果是空的
            //此时返回的是ps或者pl,返回的也是null
            pl = pl.next;
            ps = ps.next;
        }
        return ps;
    }
    //判断链表是否有环
    //快慢指针为啥不能走三步,走一步:如果有链表有环且只有两个节点,此时会错过,
    //永远也不会相遇
    //但是如果是走两步和走一步的情况,此时fast和slow最多差一个环的长度,此时
    //fast和slow是一次追击一步,如果存在环一定会相遇
    public boolean hasCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }
    public boolean hasCycle2(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;
            }
        }
        //或者走到这里肯定有一个是空的了
        if (fast == null || fast.next == null) {
            return false;
        }
        return true;
    }
    //创建一个环
    public void createLoop() {
        ListNode cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = head.next.next;
    }
    //环形链表:返回链表开始入环的第一个节点
    //结论:让一个指针从链表的起始位置开始遍历链表,同时让一个指针从相遇点开始绕环遍历
    //两个指针每次都是走一步,最终一定会在入口点相遇
    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;
            }
        }
        //或者走到这里肯定有一个是空的了
        if (fast == null || fast.next == null) {
            return null;
        }
        //走到这里一定是有环
        slow = head;
        while (slow != fast) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }

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

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

相关文章

HNU计算机体系结构-实验一:RISC-V指令理解

HNU计算机体系结构-实验一 前言1.实验目的2.实验步骤1.安装模拟器Ripes2.生成汇编指令3.思考问题1&#xff09;指令add x15, x14, x152&#xff09;指令bge x15 x14 -683&#xff09;指令lw x15, -20 x84&#xff09;指令sw x15, -20 x85&#xff09;简述BranchE信号的作用6&am…

通达信筹码集中度计算公式,解决了结果不同的问题

筹码分布图基本上是国内股票交易软件的标配&#xff0c;一般在K线图窗口的右侧。通达信软件中&#xff0c;在右下角有个“筹”&#xff0c;点击之后就可以看到“筹码分布图”。&#xff08;如下图&#xff09;筹码分布图是根据一定的模型计算出的流通股票持仓成本分布情况&…

postman接口关联实战解析

在使用postman做接口测试时&#xff0c;有时候后面的接口需要获取前面接口的某一个返回值做为请求参数&#xff0c;这时就可以使用关联。 如从A接口提取出a字段的值&#xff0c;供B接口的b字段使用。 一个接口的返回报文如下&#xff1a; {"retCode": "0&quo…

【数据库原理与实践】知识点归纳(上)

第1章 数据库系统概述 &#xff08;数据处理技术的发展 数据库的基本概念和特点&#xff09; 一&#xff1a;数据、信息、知识、决策 对应&#xff1a;Data、Information、Knowledge、Decision 二&#xff1a;数据库技术的产生与发展 产生原因&#xff1a;数据管理任务的…

如何正确使用Postman变量?又该如何灵活设置变量?

目录 引言 理解变量 postman的变量类型 变量作用域 变量覆盖规则 创建变量 创建全局变量 创建环境变量 创建集合变量 使用变量 引言 Postman变量可以帮助你快速生成测试数据、模拟不同的场景和环境。 但是&#xff0c;如何正确使用Postman变量&#xff1f;又该如何…

Android的服务Service

Android学了太久了&#xff0c;都忘了。复习下四大组件之一的Service。 介绍 Android的Service是一种在后台执行长时间运行操作的组件&#xff0c;它可以在没有用户界面的情况下执行任务&#xff0c;并且可以与应用程序的其他组件进行通信。Service通常用于处理网络请求、音乐…

快速创建Django项目

环境搭建 安装Django之前需要先安装python环境和pycharm。 目前 Django 1.6.x 以上版本已经完全兼容 Python 3.x。 python安装教程&#xff1a;https://blog.csdn.net/David_house/article/details/100110542 项目创建 打开pycharm&#xff0c;选择文件–>新建项目 创建D…

MySQL操作基础

文章目录 一、Mysql的基本概念1.1数据库有哪些组成数据表数据库 1.2数据库系统和数据库管理系统1.3数据库的发展史 二、数据库的分类三、数据库的数据类型三、数据库的操作3.1查看数据库的结构3.2创建及删除数据库和表3.3管理表中的数据记录3.4修改表名和表结构 Linux操作系统&…

11.JavaWeb-Spring框架

1.Spring概念 Spring框架是一个轻量级开源的应用程序框架&#xff0c;用于开发Java企业级应用程序。它提供了一种综合的编程和配置模型&#xff0c;可以帮助开发人员构建灵活、可扩展且易于维护的应用程序。 Spring的根本使命&#xff1a;简化Java开发 【Spring给予了Java新生】…

文心一言 VS 讯飞星火 VS chatgpt (40)-- 算法导论5.4 4题

四、一次聚会需要邀请多少人&#xff0c;才能让其中 3 人的生日很可能相同? 文心一言&#xff1a; 为了解决这个问题&#xff0c;我们需要先计算出所有可能的生日组合数&#xff0c;然后计算出其中生日相同的组合数。 已知需要邀请的人数为&#xff1a;23人 根据组合数公式…

大数据开发之Hive案例篇12:HDFS rebalance 一例

文章目录 一. 问题描述二. 解决方案2.1 增加节点2.2 rebalance2.3 rebalance引发的问题 一. 问题描述 公司的离线数仓是CDH集群&#xff0c;19个节点&#xff0c;HDFS存储空间大约400TB左右&#xff0c;使用量在200TB左右。 由于历史遗留的问题&#xff0c;数据仓库需要重构&a…

将Qtcreator编译器、调试器等配置为MSVC2017/2019

目录 1. 前言 2. 工具/原料 2.1. 安装Windows下的软件开发调试器 3. 配置Qtcreator 1. 前言 安装Qt或Qtcreator时&#xff0c;一般选择MinGW 64-bit或MSVC编译器。对于MinGW 64-bit很简单&#xff0c;一般安装后会自动在Qtcreator配置好&#xff0c;不用费心就可以进行开发…

一种对不同类型齐格勒-尼科尔斯 P-I-D 控制器调谐算法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

基于NIM_DUILIB_FRAMEWORK框架的网易云信demo:NIM_PC_DEMO

NIM_PC_DEMO 网易云信 PC IM Demo 是基于网易云信 PC SDK 制作的即时通讯示例程序&#xff0c;UI 库使用 NIM Duilib 制作。 github地址:。 1、预览 2、最低要求 CMake 3.10 或以上版本。Visual Studio 2017 或以上版本。Git。 3、开发步骤 NIM Demo 从 8.4.0 版本开始使…

selenium面试题总结

今天有同学问到seleinum面试的时候会问到的问题&#xff0c;随便想了想&#xff0c;暂时纪录一下。欢迎大家在评论中提供更多问题。 1.selenium中如何判断元素是否存在&#xff1f; selenium中没有提供原生的方法判断元素是否存在&#xff0c;一般我们可以通过定位元素异常捕获…

Java网络开发(Tomcat同步数据增删改查)—— 用Jsp语法实现同步请求的 增删改查

目录 引出显示所有数据到前端&#xff08;1&#xff09;前端代码&#xff1a;list.jsp&#xff08;2&#xff09;后端代码&#xff1a;CompanyListServlet.java 新增数据---转发类型信息---新增信息业务&#xff08;1&#xff09;在list.jsp页面点击添加&#xff08;2&#xff…

don‘t have write permissions for the /System/Library/Frameworks/Ruby.framework

don’t have write permissions for the /System/Library/Frameworks/Ruby.framework sudo gem install sigh或sudo gem install -n /usr/local/bin cocoapods --pre出现&#xff1a; appleCQIMAC-L1A9Q05R ~ % sudo gem update --system Password: Updating rubygems-update F…

社会心理学(1) 社会心理学的定义

今天开始 我们一起学习一门课程 社会心理学 社会心理学 他是 应用心理学 或者 心理学专业的一个必修课 吴江霖教授说过 心理学应该分为两大分支 生理心理学 和 社会心理学 如果认同他的观点 那么 社会心理学可谓是相当重要了 社会心理学的定义之广可以说 有多少社会心理学教…

书山有路勤为径 学海无涯苦作舟(AI引领时代浪潮)

书山有路勤为径 学海无涯苦作舟 AI模型是如何训练的&#xff1f; 数据准备 在训练AI模型之前&#xff0c;需要准备大量的数据集。数据集的质量和多样性直接影响模型训练的效果。数据集准备的主要工作包括&#xff1a; 数据清洗&#xff1a;清除数据中的噪声、错误、重复等不…

STM32单片机(六)TIM定时器 -> 第一节:TIM定时中断

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…