Leetcode(一):数组、链表部分经典题目详解(JavaScript版)

数组、链表部分算法题

  • 一、数组
    • 1. 二分查找
    • 2. 移除数组元素
    • 3. 有序数组的平方
    • 4. 长度最小的子数组
    • 5. 螺旋矩阵
  • 二、链表
    • 1. 删除链表元素
    • 2. 设计链表
    • 3.反转链表
    • 4.两两交换链表中的节点
    • 5.删除链表倒数第n个节点
    • 6.环形链表

提前声明:本博客内容均为笔者为了方便个人理解而发布,题目思路参考自代码随想录,如果大家有觉得写的不清晰的地方,欢迎在评论区提问。

一、数组

1. 二分查找

点击跳转到力扣题目

注意题目的前提条件:有序数组、无重复元素

解法:双指针

这道题目的难点在于边界的确定,[left,right][left,right)的边界处理是不一样的,前者right应该为nums.length-1,后者right应为nums.length

第一种:左闭右闭,即从[left,right]区间搜索,以[1,2,3,4],找1为例子带进去看看第一轮while循环

var search = function(nums, target) {
   let left = 0; //左指针闭区间,指向第一个位置0
   let right = nums.length - 1; //右指针闭区间,指向最后一个位置3

   //由于是左闭右闭,那么left=right就是有意义的,代表一直找到了最后一个元素
   while(left <= right) {
       let middle = Math.floor((right - left)/2) + left //寻找中间位置1
       //开始二分查找,不断缩小边界
       if(target < nums[middle]) {
      	   //缩小边界,由于right是闭区间,判断条件已经确定target<middle位置的值
      	   //由于是闭区间,下一次搜索一定会搜索<=middle-1处的值,right要变为middle-1=0
           right = middle - 1; 
       } else if (target > nums[middle]) {
           left = middle + 1;//缩小边界,此时已确定target>middle位置的值,left要变为middle+1
       } else {
           return middle //如果中间位置这个数刚好对上,就返回,否则就会一直到循环的最后一轮
       }
   }
   //如果没有return,就return-1
   return -1
};

第一轮循环结束后,搜索区间变成了[0,0],也就是左右指针都指向了位置0的元素1,此时左右指针相撞,left=right就是有意义的,所以while循环的条件就是<=,等于号的情况代表一直找到了最后一个元素

第二种:左闭右开,即从[left,right)区间搜索,以[1,2,3,4],找1为例子带进去看看第一轮while循环

var search = function(nums, target) {
   let left = 0; //左指针闭区间,指向第一个位置0
   let right = nums.length; //右指针开区间,指向最后一个位置3的后面4

   //由于是左闭右开,那么left=right就是没有意义的
   while(left < right) {
       let middle = Math.floor((right - left)/2) + left //寻找中间位置1
       //开始二分查找,不断缩小边界
       if(target < nums[middle]) {
      	   //缩小边界,由于right是开区间,判断条件已经确定target<middle位置的值
      	   //由于是开区间,下一次搜索一定会搜索<middle的值,right要变为middle=1
           right = middle; 
       } else if (target > nums[middle]) {
      	   //缩小边界,由于left是闭区间,判断条件已经确定target>middle位置的值
      	   //由于是闭区间,下一次搜索一定会搜索>=middle+1的值,left要变为middle+1	       
           left = middle + 1;
       } else {
           return middle //如果中间位置这个数刚好对上,就返回,否则就会一直到循环的最后一轮
       }
   }
   //如果没有return,就return-1
   return -1
};

第一轮循环结束后,搜索区间变成了[0,1),也就是左指针指向了位置0的元素1,右指针指向了位置1的元素2,此时必然会锁定0位置的元素,所以while循环的条件就是<

这个题有点不太好描述,感觉容易乱,先这样吧……我觉得最好按第一种方法来记

2. 移除数组元素

点击跳转到力扣题目
做这道题必须要知道数组的特点,那就是删除元素时只能覆盖,不是原地删除

解法:快慢指针法

想要用快慢指针法解这个问题,最关键的点就是理解slow和fast都代表什么
我们可以把慢指针理解为新数组的每个位置,快指针的作用是遍历整个数组,这样的话快指针只需要找到不用删除的元素,然后依次覆盖就可以了。
可以仔细看下注释,以removeElement([1,2,3,4],3)理解一下指针的移动过程

var removeElement = function(nums, val) {
    let slow = 0; //慢指针,指向新数组的每个位置
    for(let fast = 0; fast < nums.length; fast++) { //快指针fast遍历数组
        if(nums[fast] !== val) {    
            //如果快指针指向的不是要删除的元素,那么就让快指针位置的值覆盖慢指针位置的值
            nums[slow] = nums[fast]
            slow++ //然后慢指针先移动,快指针再移动
        }
        //如果是要删除的元素,那么只有快指针移动(fast++),慢指针不动
        //这样可以做到后面的值覆盖到当前慢指针指向的位置
    }
    //移除后数组长度,就是当前slow指向的这个索引
    return slow //因为如果覆盖了当前元素,slow会先往后移,此时指向的是没有元素的位置
    //比如[1,2,3,4],删除3时,slow指向3,fast指向4,4覆盖3后slow指向4,此时索引为3,正好是数组长度
};

3. 有序数组的平方

点击跳转到力扣题目

解法:双指针法

这个题目相较于前面两个,更好理解一些,其实也可以先平方再快排,但是更好的是用空间换时间,使用双指针法,依次找最大的元素unshift,当两个指针相撞时,就是最小的那个元素。

//双指针法,空间换时间,时间复杂度O(N)
 var sortedSquares = function(nums) {
    let i = 0;   //一个指针指向头部
    let j = nums.length - 1;  //一个指针指向尾部
    let result = []; //开辟一个新的空间
   //注意循环停止的条件,当i = j时,指针相撞,指向的应该是最小的元素,循环应继续
    while(i<=j) {
       let left = Math.pow(nums[i],2)
       let right = Math.pow(nums[j],2)
       if( left > right) {
           result.unshift(left)
           i++
       } else {
           result.unshift(right)
           j--
       }
    }
    return result
};

4. 长度最小的子数组

点击跳转到力扣题目

解法:暴力or滑动窗口

这道题目其实也可以用暴力求解,那就是双重for循环把所有可能的长度组合枚举出来,然后去找哪个是满足条件的长度最小子数组,并返回它的长度。

var minSubArrayLen = function(target, nums) {
    let result = Number.MAX_VALUE; //获取已知的最大值
    //外层循环,以每一个元素作为起点去找目标值
    for(let i = 0; i < nums.length; i++) {
        let sum = 0;
        //内层循环,寻找目标值
        for(let j = i; j < nums.length; j++) {
            sum += nums[j];
            if (sum >= target) {
                result = Math.min(result, j - i + 1); //和上一个长度比较,取较小的值
                break;
            }
        }
    }
    // 如果最后result没变化,那么说明没有比target大的组合
    return result === Number.MAX_VALUE ? 0 : result;
};

这里有很妙的一个方法,可以把时间复杂度降低到O(n):

为了方便理解,直接以target = 101, nums = [1,1,1,100,1]为例,走一遍下面的逻辑
滑动窗口:先确定终止的位置(因为如果先确定开始位置,那么终止位置的确定和暴力解法就一样了),确定的方式就是去遍历数组,找到第一个符合sum >= target的位置,作为第一个滑动窗口的终止位置,开始位置直接从0开始,然后第一个符合条件的长度就是 j - i + 1 此时窗口开始缩短(i向前移动),移动前要先把之前位置的数值从sum中删掉。之后一直循环直到条件不成立,则j开始移动,就这样不断滑动,可以过一遍所有可能的情况。

为什么时间复杂度是O(n)呢?因为每个元素只被过了2次,那就是O(2n),也就是O(n)

var minSubArrayLen = function(target, nums) {
    let result = Number.MAX_VALUE; //找到最大值,方便下面对result进行取小
    let sum = 0; //初始化sum
    let i = 0; //开始位置是第一个
    for(let j = 0; j < nums.length; j++) {
        sum = sum + nums[j]; //通过前几次循环,先找到符合条件的j值,作为滑动窗口终止位置
        while (sum >= target) { //i开始移动
            result = Math.min(result, j - i + 1) //如果目标长度比上一个小,就替换一下子
            sum = sum - nums[i]; //替换之后,i继续蠢蠢欲动,移动前,先把旧i位置的值从sum删掉
            i++; //窗口左侧开始滑动,看一下下一个值是否是符合条件的值,如果是,则继续循环移动开始位置
        }
    }
    return result === Number.MAX_VALUE ? 0 : result;
};

5. 螺旋矩阵

点击跳转到力扣题目

这道题的难点在于各种边界的确定,我们需要哪些状态来协助我们进行数组的更新。

  1. 第一个能想到的状态是count,因为要记录每次要填的数,每填一个就++
  2. 第二个就是每一圈x轴和y轴的坐标,第一圈从00开始,第二圈从11开始
  3. 第三个就是要转几圈,通过循环填充数字,那么转几圈是很重要的,偶数就是n/2圈,奇数则是向下取整圈,比如3那就转1圈,中间的数字单独考虑,5则转两圈,中间的数组单独考虑
  4. 第四个就是填充的时候这个偏移量offset,每一圈开始和结束的位置是不一样的,比如第一圈的第一行是从startX=0开始,到n-1结束,第二圈的第一行是从startX=1开始,到n-2结束的,所以offset每轮都要递增一下
  5. 仔细看下注释,你会明白的
var generateMatrix = function(n) {
    //1.初始化各种需要的变量,其中二维数组所有元素补0(因为js中无法直接通过索引添加元素)
    let result = new Array(n).fill(0).map(() => new Array(n).fill(0));
    let count = 1; //从1开始计数,一直到n,依次填充到n方
    let startX = 0; //初始化每一圈的x轴坐标
    let startY = 0; //初始化每一圈的y轴坐标
    let loop = Math.floor(n/2); //转几圈
    let offset = 1;
    
    //每一个while就是转一圈,转几圈取决于是奇数还是偶数,偶数就是n/2圈,奇数则最后一圈不转,单独填充下就行了
    while(loop > 0) {
        let row = startX; //初始化每一圈的行坐标
        let col = startY; //初始化每一圈的列坐标

        //填充上行从左到右,左闭右开,临界值是n-offset
        for(; col < n - offset; col++) {
            result[row][col] = count;
            count++;
        }

        //此时col停在了右列,row开始移动
        //填充右列从上到下
        for(; row < n - offset; row++) {
            result[row][col] = count;
            count++;
        }

        //此时row停在了下列,col开始移动
        //填充下行从右到左
        for(; col > startX; col--) {
            result[row][col] = count;
            count++;
        }

        //此时col停在了左列,row开始移动
        //填充左列从下到上
        for(; row > startY; row--) {
            result[row][col] = count;
            count++;
        }

        loop--; //每转完一圈,loop减1
        startX++; //每转完一圈,开始位置要移动,为下一圈做准备
        startY++; //每转完一圈,开始位置要移动,为下一圈做准备
        offset++; //每转完一圈,填充的临界值(n-offset)都要缩小
    }

    //如果是奇数n,那么需要单独给矩阵中间的位置赋值,索引就是迭代到最后一圈时x和y轴的坐标位置
    if (n % 2 === 1) {
        result[startX][startY] = count
    }

    return result;
};

二、链表

1. 删除链表元素

点击跳转到力扣题目

解法:添加虚拟头节点,双指针

这个其实比较简单,方法是添加一个虚拟头节点,然后删除时,让上一个节点的next指向当前节点的next就可以了:

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */
var removeElements = function(head, val) {
    let dummyHead = new ListNode(0, head) //创建一个虚拟头节点
    let pre = dummyHead //指针1,指向虚拟头节点
    let current = head //指针2,指向头节点

    //循环移动指针,一直到current指向null,所有链表元素遍历完毕
    while(current) {
        if(current.val === val) {
            //如果找到要删除的元素
            pre.next = current.next //pre指向current后面,这样被跳过的元素会被自动回收
            current = current.next //指针current移动
        } else {
            //否则,移动两个指针继续寻找
            pre = current
            current = current.next
        }
    }

    return dummyHead.next
};

2. 设计链表

点击跳转到力扣题目

这里其实不需要虚拟头节点,我没有用虚拟头节点,双指针pre默认为null,current默认为第一个节点,然后依次迭代

几个注意点:1.不用虚拟头节点 2.插入删除到0位置时单独判断 3.小心空指针情况(比如尾部插入时)

class LinkNode {
    constructor(val) {
        this.val = val;
        this.next = null;
    }
}

var MyLinkedList = function() {
    this.head = null;
    this.size = 0;
};

/** 
 * @param {number} index
 * @return {number}
 */
MyLinkedList.prototype.get = function(index) {
    if(index < 0 || index > this.size - 1) return -1;
    let current = this.head;
    for(let i = 0; i < index; i++) {
        current = current.next
    }
    return current.val;
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtHead = function(val) {
    const Node = new LinkNode(val);
    Node.next = this.head;
    this.head = Node;
    this.size++;
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtTail = function(val) {
    const Node = new LinkNode(val);
    let current = this.head;
    if(this.head) {
        while(current.next) {
            current = current.next;
        }
        current.next = Node;
    } else {
        this.head = Node;
    }
    this.size++;
};

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function(index, val) {
    if(index < 0 || index > this.size) return false
    const Node = new LinkNode(val);
    let pre = null;
    let current = this.head;
    for(let i = 0; i < index; i++) {
        pre = current;
        current = current.next;
    }
    if(index === 0) {
        Node.next = this.head;
        this.head = Node;
    } else {
        pre.next = Node;
        Node.next = current;
    }

    this.size++;
};

/** 
 * @param {number} index
 * @return {void}
 */
MyLinkedList.prototype.deleteAtIndex = function(index) {
    if(index < 0 || index > this.size - 1) return false
    let pre = null;
    let current = this.head;
    for(let i = 0; i < index; i++) {
        pre = current;
        current = current.next;
    }
    if(index === 0) {
        this.head = current.next
    } else {
        pre.next = current.next
    }
    this.size--;
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * var obj = new MyLinkedList()
 * var param_1 = obj.get(index)
 * obj.addAtHead(val)
 * obj.addAtTail(val)
 * obj.addAtIndex(index,val)
 * obj.deleteAtIndex(index)
 */

3.反转链表

点击跳转到力扣题目

解法1:双指针迭代

双指针迭代就是定义两个指针pre和current,依次改变指针的指向,具体看下面的代码和注释:

function reverseList(head) {
    let pre = null;  //定义一个空指针在head的前面
    let current = head;  //定义另一个指针指向头节点
    while(current) {    //current指针为空的时候说明指到尾节点了,就停止循环
        let temp = current.next;  //定义一个temp来保存当前节点的下一个节点
        current.next = pre;  //关键一步,改变指针指向,把向后指的指针改为向前指
        pre = current;    //pre指针向后移,指向current
        current = temp;   //current指针向后移,指向当前节点的下一个节点(提前保存了)
    }
    return pre;   //最后返回新的头指针(current指向了null)
}

解法2:递归

递归的实现思路和双指针迭代是类似的,也是要有两个指针向后指

function reverseList(head) {
    return reverse(null,head); //初始 pre = null, current = head
}

//定义递归函数,作用其实就是让指针向后移
function reverse(pre,current) {     //定义两个指针,一个pre指向前面,一个current指向当前
    if(current == null) return pre; //递归停止的条件(最后一层递归开始出栈,返回头指针)
    let temp = current.next;  //保存后面的节点,方便后面让指针向后移
    current.next = pre;  //改变指向
    return reverse(current,temp);  //返回一个递归的调用
}

4.两两交换链表中的节点

点击跳转到力扣题目

 /**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var swapPairs = function(head) {
    //1.创建虚拟头节点
    const dummyhead = new ListNode(0);
    dummyhead.next = head;
    //2.创建双指针
    let pre = dummyhead;
    let current = head;
    //3.奇数和偶数项的结束条件,先判断current有没有,再判断curren.next有没有(逻辑中断)
    while(current && current.next) {
        //4.依次改变指针指向,注意不要出现空指针的情况
        let temp = current.next; //测试中发现current.next会丢失,先保存一下
        //4.1调转两个节点
        pre.next = temp; 
        current.next = temp.next;
        temp.next = current;
        //4.2指针向后移动
        pre = current;
        current = current.next;
    }
    //综上如果无法在脑中模拟,画个图其实就明白了
    return dummyhead.next;
};

5.删除链表倒数第n个节点

点击跳转到力扣题目

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    //使用头节点就是为了:不需要单独考虑删除头节点的情况
    const dummyhead = new ListNode(0);
    dummyhead.next = head;
    //快慢指针法
    let fast = dummyhead;
    let slow = dummyhead;
    n = n + 1;
    //快指针先走n+1下,慢指针不动
    while(n-- > 0) {
        fast = fast.next;
    }
    //快慢指针一起移动,当快指针指向null时,慢指针指向的就是要删除的节点的前一个节点,仔细想想是不是
    while(fast) {
        fast = fast.next;
        slow = slow.next;
    }
    slow.next = slow.next.next;
    //这里返回的不能是head,因为如果删除的是head,那么dummyhead指针指向了第二个节点
    //但此时head还是有值的,通过head仍然能获取链表所有值,相当于head并没有被删掉(仔细想想删除头节点时指针的改变)
    return dummyhead.next; 
};

6.环形链表

点击跳转到力扣题目

解法:快慢指针
在这里插入图片描述
这里可以看代码随想录的证明,其实快慢指针相遇的点到环入口的距离和头节点到环入口的距离是相同的(快指针走两步慢指针走一步的情况下,慢指针第一圈内一定会和快指针相遇)
在这里插入图片描述

搞清楚这一点,我们只需要找到几个位置就可以了,首先是相遇的节点a,相遇后让两个指针分别指向head和a,同时按每次一个节点的速度移动,当两个指针相遇时,指针指向的节点就是环的入口。

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function(head) {
    //1.定义快慢指针指向头节点
    let slow = head;
    let fast = head;
    //2.循环移动快慢指针,慢指针每次走一步,快指针每次走两步
    while(fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
        //3.快慢指针相遇时一定在环上,此时指针距离环入口的距离和head距离环入口的距离相同
        if(slow === fast) {
            let index1 = head;
            let index2 = fast;
            //4.同时移动两个指针直至相撞,那么撞的位置就是环的入口
            while(index1 !== index2) {
                index1 = index1.next;
                index2 = index2.next;
            }
            return index1;
        }
    }
    //如果return null,只有两种情况,一种是链表只有1个节点或没有节点,这种情况不可能有环
    //另一种情况就是链表没有环(不然在上面代码中已经return出去了)
    return null;
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DbVisualizer Pro Crack

DbVisualizer Pro Crack DbVisualizer是适用于开发人员、DBA和分析师的通用数据库工具。它是最终的解决方案&#xff0c;因为相同的工具可以在访问各种数据库的所有主要操作系统上使用。支持的数据库Amazon Redshift、DB2 LUW、Exasol、H2、Informix、JavaDB/Derby、Microsoft …

深度解剖动态内存管理

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大一&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 望小伙伴们点赞&#x1f44d;收藏✨加关注哟&#x1f495;&#x1…

汽车过户时,怎么选到理想的好车牌?

在汽车过户的过程中&#xff0c;选到一副理想的好车牌就像买彩票中大奖一样令人兴奋。但是&#xff0c;怎样找到这样一块车牌呢&#xff1f;这就是本文要探讨的问题。 首先&#xff0c;我们来聊聊选车牌的技巧。很多人喜欢选择有特别数字的车牌&#xff0c;如“8888”、“6666”…

语义检索系统【二】:基于无监督训练SimCSE+In-batch Negatives策略有监督训练的语义索引召回

搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术…

数字化时代,如何做好用户体验与应用性能管理

引言 随着数字化时代的到来&#xff0c;各个行业的应用系统从传统私有化部署逐渐转向公有云、行业云、微服务&#xff0c;这种变迁给运维部门和应用部门均带来了较大的挑战。基于当前企业 IT 运维均为多部门负责&#xff0c;且使用多种运维工具&#xff0c;因此&#xff0c;当…

爬虫原理详解及requests抓包工具用法介绍

文章目录 一、什么是爬虫&#xff1f;二、爬虫的分类三、网址的构成四、爬虫的基本步骤五、动态页面和静态页面六、伪装请求头七、requests库介绍1. 概念&#xff1a;2. 安装方式&#xff08;使用镜像源&#xff09;&#xff1a;3. 基本使用&#xff1a;4. response对象对应的方…

IDEA的基础使用——【初识IDEA】

IDEA的基础使用——【初识IDEA】 文章目录 IDEA简介前言官网 IDEA的下载与安装选择下载路径勾选自己需要的其余按默认选项进行即可 目录简介安装目录简介 运行Hello WorldIDEA快捷键常用模板模板一&#xff1a;psvm&#xff08;main&#xff09;模板二&#xff1a;模板三&#…

215. 数组中的第K个最大元素

题目链接&#xff1a;力扣 解题思路&#xff1a; 方法一&#xff1a;基于快速排序 因为题目中只需要找到第k大的元素&#xff0c;而快速排序中&#xff0c;每一趟排序都可以确定一个最终元素的位置。 当使用快速排序对数组进行降序排序时&#xff0c;那么如果有一趟排序过程…

frida学习及使用

文章目录 安装frida安装python3.7设置环境变量安装pycharm和nodejs 使用frida将frida-server push到手机设备中端口转发安装apk使用jadx查看java代码运行frida-server frida源码阅读frida hook方法Frida Java层hoookJavaHook.javaJavaHook.js Frida native层hook 一NativeHook.…

深度学习——划分自定义数据集

深度学习——划分自定义数据集 以人脸表情数据集raf_db为例&#xff0c;初始目录如下&#xff1a; 需要经过处理后返回 train_images, train_label, val_images, val_label 定义 read_split_data(root: str, val_rate: float 0.2) 方法来解决&#xff0c;代码如下&#xff1a…

局域网内电脑ping不通(防火墙惹的祸)

明明是同一网段&#xff08;同一局域网&#xff09;的电脑&#xff0c;却ping不通&#xff0c;这种情况大概率就是防火墙惹得祸了。除了把防火墙关掉&#xff0c;我们还可以采取如下解决方案。 解决方案&#xff1a; 1. 打开&#xff1a;控制面板\系统和安全\Windows Defende…

matlab多线程,parfor循环进度,matlab互斥锁

一. 内容简介 matlab多线程&#xff0c;parfor循环进度&#xff0c;matlab互斥锁 二. 软件环境 2.1 matlab 2022b 2.2代码链接 https://gitee.com/JJW_1601897441/csdn 三.主要流程 3.1 matlab多线程 有好几种&#xff0c;最简单的&#xff0c;最好理解的就是parfor&am…

面试热题(无重复字符的最长子串)

无重复字符的最长子串 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。 解法一&#xff1a; public int lengthOfLonge…

css 动画之旋转视差

序&#xff1a;网上看到的一个例子&#xff0c;做一下 效果图&#xff1a; 代码&#xff1a; <style>.content{width: 300px;height: 300px;margin: 139px auto;display: grid;grid-template-columns: repeat(3,1fr);grid-template-rows: repeat(3,1fr);grid-template:…

4年测试工程师,常用功能测试点总结,“我“不再走弯路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 输入框测试 1、字…

MySQL的JSON操作

官网地址 1. MySQL json介绍 As of MySQL 5.7.8, MySQL supports a native JSON data type defined by RFC 7159 that enables efficient access to data in JSON (JavaScript Object Notation) documents. Automatic validation of JSON documents stored in JSON columns. …

【消息中间件】原生PHP对接Uni H5、APP、微信小程序实时通讯消息服务

文章目录 视频演示效果前言一、分析二、全局注入MQTT连接1.引入库2.写入全局连接代码 二、PHP环境建立总结 视频演示效果 【uniapp】实现买定离手小游戏 前言 Mqtt不同环境问题太多&#xff0c;新手可以看下 《【MQTT】Esp32数据上传采集&#xff1a;最新mqtt插件&#xff08;支…

PHP: 开发入门macOS系统下的安装和配置

安装Homebrew 安装 ~~友情提示&#xff1a;这个命令对网络有要求&#xff0c;可能需要翻墙或者用你的手机热点试试&#xff0c;或者把DNS换成&#xff08;114.114.114.114 和 8.8.8.8&#xff09; /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebr…

【【胎教级51单片机智能小车设计】】

胎教级51单片机智能小车设计 从现在开始开一个新坑 称为创意工坊 主要更新一些有意思的设计 第一次手把手更新51单片机智能小车 胎教级教学人人都会 单片机实现的功能是通过蓝牙APP 控制小车前后左右移动 先讲明白这个小车 后续再在这个小车上更新其他的设计 成品图 第一步…

分库分表之基于Shardingjdbc+docker+mysql主从架构实现读写分离(一)

说明&#xff1a;请先自行安装好docker再来看本篇文章&#xff0c;本篇文章主要实现通过使用docker部署mysql实现读写分离&#xff0c;并连接数据库测试。第二篇将实现使用Shardingjdbc实现springboot的读写分离实现。 基于Docker去创建Mysql的主从架构 #创建主从数据库文件夹…