算法学习(七)—— 分治

news2024/11/5 18:33:28

关于分治

分治,就是“分而治之”的意思,就是把一个大问题,转化为若干个相同或者相似的几个子问题,然后在子问题的基础上再进行划分,直到能够快速一个子问题时停止划分

我们的快速排序和归并排序就是典型的分治思想

部分OJ题详解

分治-快排

75. 颜色分类

75. 颜色分类 - 力扣(LeetCode)

通过这个题目我们要来学习下“把数组分三段”这个思想,这个题目就是这样,以1为“基准元素”,左边全是小于1的,右边全是大于1的。我们来分析下这道题:

  •  这道题其实就是在本系列第一道题 “ 移动0 ” 的双指针基础上多了一个指针,所以这道题的解法可以说是“三指针”
  • 我们把一个数组分成三部分,0,1和2各自一部分,定义第一个指针i,用来遍历整个数组;定义第二个指针left,作用是标定0这个区域的最侧;定义第三个指针right,作用是标记2这个区域的最侧,正确的结果是,i走到数组结尾,left和right分别在1区域的左右位置
class Solution {
public:
    void sortColors(vector<int>& nums) 
    {
        int n = nums.size();
        int left = -1, right = n, i = 0;
        while(i < right)
        {
            if(nums[i] == 0)
            {
                swap(nums[++left], nums[i++]);
            }
            else if(nums[i] == 1)
            {
                i++;
            }
            else
            {
                swap(nums[--right], nums[i]);
            }
        }
    }
};

 912. 快速排序数组

 912. 排序数组 - 力扣(LeetCode)

这道题就是快排,把数组排成升序,下面来分析下这道题:

  • 快排的思想就是,选一个中间值key,使key左边的值全都小于key,右边的值全都大于key,然后再在左右两边的区域采用相同的策略,就能达到快速排序;但是这种办法有个缺点,当数组里的数字全部是一样的时候,时间复杂度就会退化到(N ^ 2)
  • 所以我们用分治的“数组分三块”的思想,来实现并优化快排。我们依旧把数组分成三个区域,左边的区域<key,中间区域==key,右边区域>key;然后就是和上面颜色划分一样,定义三个指针,每个指针的作用相同
  • 优化:有很多,比如三数取中,但是快排算法时间复杂度要接近O(N * logN),需要用随机的方式选择基准元素,这个在“算法导论:概率求期望”里面有证明
  • r = rand(); 然后我们获得随机基准元素的方式就是return nums[r % (right - left + 1) + left];

然后我们就可以用短短30行代码实现之前差不多100行的快排算法:

class Solution {
public:
vector<int> sortArray(vector<int>& nums) 
    {
        srand(time(nullptr)); //种随机数种子
        qsort(nums, 0, nums.size() - 1);
        return nums;
    }
    
    void qsort(vector<int>& nums, int l, int r)
    {
        if(l >= r) return; //递归出口
        //数组分三份
        //1,随机选择基准元素
        int a = rand();
        int key = nums[a % (r - l + 1) + l];
        //2,定义三指针
        int i = l, left = l - 1, right = r + 1;
        //3,分类讨论
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }
        //走到这里,就分成了三段区域
        //[l, left]  [left + l, right - l]  [right, r],其中中间已经排好了,只需要递归去排左边和右边
        qsort(nums, l, left);
        qsort(nums, right, r);
    }
};

215. 数组中第k个最大元素

215. 数组中的第K个最大元素 - 力扣(LeetCode)

这个题目其实就是Top K 问题,这种问题有四种问法:①第K大  ②第K小  ③前K大  ④前K小

可以使用堆排序解决,另一个就是可以使用快速选择算法,前者时间复杂度为O(NlogN),后者为O(N),《算法导论》对这些时间复杂度有证明,这里我们主要讲后者 

下面我们来解释下这道题:

  • 先选择基准元素key,也是分成三段,和上一题一样;由于题目是要找第K大的元素,所以我们只需要保证这个“第K大的元素”落在>key的区域,这样左边两个区域就不用再考虑了,只需要在>key的区域找就行了
  • 所以,我们只需要确定“第K大的元素”在三个区域的哪一个区域,就能排除另外两个区域了

 使用快速选择算法:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k)
    {
        return qsort(nums, 0, nums.size() - 1, k);
    }

    int qsort(vector<int>& nums, int l, int r, int k)
    {
        if(l == r) return nums[l];
        //1,随机选择基准元素
        int a = rand();
        int key = nums[a % (r - l + 1) + l];
        //2,根据基准元素将数组排好序并分为三类
        int left = l - 1, right = r + 1, i = l;
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }
        //3,分情况讨论
        int c = r - right + 1, b = right - left - 1;
        if(c >= k) //落在右边区域
            return qsort(nums, right, r, k);
        else if(b + c >= k) //落在中间区域
            return key;
        else //落在左边区域
            return qsort(nums, l, left, k - b - c);
    }
};

 另一种解法,使用优先级队列搞:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k)
    {
        priority_queue<int> p(nums.begin(),nums.end());
        for(int i=0;i<k-1;i++)
        {
            p.pop();
        }
        return p.top();
    }
};

 剑指offer 40. 最小的k个数

LCR 159. 库存管理 III - 力扣(LeetCode)

 题目很简单,就是给我们一个数组和一个整数k,找出最小的k个数字,下面我们来分析下这道题:

  • 解法一:直接排序,然后找k个最小的数,O(NlogN)
  • 解法二:利用堆,我们创建一个大小为k的大堆,然后把数组扔这个堆里去,然后返回 O(NlogK)
  • 解法三:快速选择算法,随机选择基准元素,数组分三块,步骤和前面一样
class Solution {
public:
    vector<int> inventoryManagement(vector<int>& stock, int cnt)
    {
        srand(time(nullptr));
        qsort(stock, 0, stock.size() - 1, cnt);
        return {stock.begin(), stock.begin() + cnt};
    }
    void qsort(vector<int>& nums, int l, int r, int k)
    {
        if(l >= r) return;
        //1,选择基准元素
        int s = rand();
        int key = nums[s % (r - l + 1) + l];
        //2,根据基准元素划分数组
        int left = l - 1, right = r + 1, i = l;
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }
        //3,分情况讨论
        int a = left - l + 1, b = right - left - 1; //得出左边和中间区域的元素个数
        if(a > k) qsort(nums, l, left, k);
        else if(a + b >= k) return;
        else qsort(nums, right, r, k - a - b);
    }
};

分治-归并

912. 排序数组

912. 排序数组 - 力扣(LeetCode)

这个题目和上面 “912. 快速排序数组”,的那个题目是一样的,这里只是单纯的复习下归并排序,下面我们来用归并的思想解一下这道题:

  • 先来复习下归并排序的基本步骤:
  • 当把归并和快排作比较时,可以发现这两个是非常相似的算法,快排就是搞一个基准元素k,然后根据k把数组分成三份,这个和归并是一样的,并且二者都是当分成的数组只剩一个元素时停止分块,所以快排和归并的思想很像,只是处理数组的时机不一样
  • 归并排序也很像二叉树的后续遍历(后续遍历就是左右中遍历,把底层搞完再来搞上层);而快排恰恰相反,先在本层先把数组分成两块,先把本层的搞完,然后再去搞左边,左边搞完后再去搞右边,这就是二叉树前序遍历(中左右
class Solution {
public:
    vector<int> tmp;
vector<int> sortArray(vector<int>& nums) 
    {
        tmp.resize(nums.size());
        mergeSort(nums,0 , nums.size() - 1);
        return nums;
    }
    
    void mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return;
        //1,先选择中间点,划分区间
        int mid = (right + left) / 2;
        //划分好的区间为[left, mid], [mid + 1, right]
        //2,把左右区间拆分
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        //3,合并小区间,并实现排序
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] <= nums[cur2]) tmp[i++] = nums[cur1++];
            else tmp[i++] = nums[cur2++];
        }
        //处理还没有遍历完的数组
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        //4,还原数组
        for(int i = left; i <= right; i++)
            nums[i] = tmp[i - left];
    }
};

剑指offer 51. 数组中的逆序对

LCR 170. 交易逆序对的总数 - 力扣(LeetCode)

题目其实不难看懂,只是有个地方需要注意,就是组成的逆序对中的两个数字的前后顺序是要和原数组对应的,下面来分析下这道题:

  •  一般看到这道题最先想到的就是暴力枚举法,而这个题也刚好就是经典的暴力枚举例子,但是不用说,O(N ^ 2)肯定会超时
  • 我们来重新搞一个策略,我们把数组分成两部分,然后我们在左边区域挑出来a个逆序对,再去右边挑b个逆序对,然后左边取一个数,右边取一个数,这样“一左一右”挑出来c个逆序对,然后a+b+c的结果也是数组的逆序对(因为这个本质还是暴力枚举,因为每个逆序对的两个数都是挑出来的)
  • 再扩展一下上面的策略:左半部分挑完后排个序,右半也一样,两者有序之后,再“一左一右”,这也是正确的,因为左边挑一个再去右边挑的时候,是不管这两个数的顺序的,只需要先从左边挑出来一个数,然后去右边挑比这个数小的就行了;同理,先挑右边的,然后只需要去看看左边有多少个数比我大(左右个自排序不影响“一左一右”的结果)
  • 解法:利用归并排序解决。结合归并思想和上面的策略,可以发现“左半部分找逆序对+排序”和“右半部分找逆序对+排序”这两步都是可以在递归中搞的,我们主要的目的就是实现“一左一右 + 排序
  • 策略一:找出该数之前,,有多少个cur1比cur2大

扩展:上面的策略是“找出该数之前,有多少个cur1比cur2大”,所以我们用的升序如果用降序能否解决问题?

  • 策略二:找出该数之后,有多少个数比我小,用这个就只能用降序了

策略一,升序: 

class Solution {
public:
    int tmp[50001] = { 0 };
    int reversePairs(vector<int>& record) 
    {
        return mergeSort(record, 00, record.size() - 1);
    }
    int mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0;
        int ret = 0;

        //1,找中间点,将数组分成两部分
        int mid = (left + right) >> 1;
        //2,分别左右区间的逆序对,并排序
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);
        //3,一左一右的逆序对的个数 —— 策略一:升序
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] <= nums[cur2])
            {
                tmp[i++] = nums[cur1++]; //完成指针移动和排序功能和归并是一样的
            }
            else
            {
                ret += mid - cur1 + 1;
                tmp[i++] = nums[cur2++];
            }
        }
        //4,处理未遍历完的指针
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];
        //5,覆盖原数组
        for(int j = left;j <= right; j++)
            nums[j] = tmp[j - left];
        
        return ret; //返回逆序对结果数量
    }
};

策略二,降序

 其实就改了下24,28和29行

class Solution {
public:
    int tmp[50001] = { 0 };
    int reversePairs(vector<int>& record) 
    {
        return mergeSort(record, 00, record.size() - 1);
    }
    int mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0;
        int ret = 0;

        //1,找中间点,将数组分成两部分
        int mid = (left + right) >> 1;
        //2,分别左右区间的逆序对,并排序
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);
        //3,一左一右的逆序对的个数 —— 策略一:升序
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] <= nums[cur2])
            {
                tmp[i++] = nums[cur2++]; //完成指针移动和排序功能和归并是一样的
            }
            else
            {
                ret += right - cur2 + 1;
                tmp[i++] = nums[cur1++];
            }
        }
        //4,处理未遍历完的指针
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];
        //5,覆盖原数组
        for(int j = left;j <= right; j++)
            nums[j] = tmp[j - left];
        
        return ret; //返回逆序对结果数量
    }
};

315. 计算右侧小于当前元素的个数

315. 计算右侧小于当前元素的个数 - 力扣(LeetCode)

这个题目和我们上面那个逆序对的题是很相似的,根据示例一就能理解,下面我们来分析下这道题:

  • 解法:归并排序(分治),和上一题一样,把数组分成两半,左右各自找逆序对个数,然后“一左一右”找的话用上一题的策略二:当前元素和后面,有多少个比我小,
  • 所以这道题和前面那道题的不同点就是:当我们找到这个元素右边有多少个数比我小的时候,我还要找到这个元素的原始下标(因为我们把数组分成两份之后是排了序的)

  • 如何找到排完序后的这个元素的原下标是多少呢?我们搞一个同等大小的数组index,这个数组里存的都是下标。然后两个数组绑定移动:

class Solution 
{
    vector<int> ret; //返回用
    vector<int> index; //记录 nums 中各元素的原始下标
    int tmpNums[500010];
    int tmpIndex[500010]; //合并数组时的辅助数组
public:
    vector<int> countSmaller(vector<int>& nums) 
    {
        int n = nums.size();
        ret.resize(n);
        index.resize(n);
        //初始化index
        for(int i = 0; 
        i < n; i++)
            index[i] = i;
        mergeSort(nums, 0, n - 1);
        return ret;
    }
    void mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return;
        //1,划分区间
        int mid = (left + right) / 2;
        //2,处理左右区间的
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        //3,处理“一左一右”
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            //如果小于就正常归并,如果大于就记录结果
            if(nums[cur1] <= nums[cur2]) //降序,谁大先移动谁
            {
                tmpNums[i] = nums[cur2];
                tmpIndex[i++] = index[cur2++];
            }
            else //统计结果
            {
                ret[index[cur1]] += right - cur2 + 1; 
                //用+=不能用=,因为左右区间算的时候,里面的值已经更新过了,用=的话会覆盖原先的值
                tmpNums[i] = nums[cur1];
                tmpIndex[i++] = index[cur1++];
            }
        }
        //4,处理剩下的排序过程
        while(cur1 <= mid)
        {
            tmpNums[i] = nums[cur1];
            tmpIndex[i++] = index[cur1++];
        }
        while(cur2 <= right)
        {
            tmpNums[i] = nums[cur2];
            tmpIndex[i++] = index[cur2++];
        }
        //5,还原nums和index
        for(int j = left; j <= right; j++)
        {
            nums[j] = tmpNums[j - left];
            index[j] = tmpIndex[j - left];
        }
    }
};

493. 翻转对

493. 翻转对 - 力扣(LeetCode)

这个题目可以说就是逆序对的变种,就是把逆序对的“前面大于后面”这个条件变成了“前面大于两倍的后面”,下面我们来分析下这道题:

  • 暴力枚举我们就不多说了。因为这个题和逆序对非常相似,所以我们依旧可以按照逆序对的那个策略来分析,解法二:分治
  • 老样子把数组分两份,,先求左边和右边的翻转对,再一左一右;逆序对那里是直接将比较大小和归并重合了,就是 nums[i] > nums[j],但是这道题不一样,这道题是 nums[i] > 2*nums[j],所以不能按照归并排序的流程求翻转对,我们得重新想一个策略来求
  • 我们需要在归并排序之前计算翻转对,因为我们可以利用左右两个数组有序的性质,就可以在一次归并中用O(N)的时间复杂度搞定一堆翻转对个数
  • 策略一:计算当前元素后面有多少个元素的两倍比我小,用降序,cur1在left,cur2在mid,cur1先不动,cur2往右移动,当cur2的两倍第一次比cur1小时,cur2后面的就都比cur1小了;但是cur1++后,cur2先别回退,因为此时的cur1是最后一次比cur2大,当cur1++后,cur1就变小了,那么此时cur2的两倍是铁定比cur1大的,所以此时我们的cur2不用退回到mid,直接在现在的位置继续判断即可,ret += right - cur2 + 1; (这个策略就是利用单调性,使用同向双指针)
  • 策略二:计算当前元素之前,有多少元素的一半比我大,用升序,也可以利用数组单调性解决问题,cur2不动,如果cur1的一半比cur2大,cur1就继续++,当cur1的一半第一次比cur2小,那么cur1的后面就都比cur2小了,此时ret += mid - cur1 + 1; cur2++; 然后一样的,cur1不必回退到left,而是在当前位置继续判断
  • 最后计算完逆序对后,我们还得排序,所以之后还得合并两个数组

策略一,降序:

class Solution
{
    int tmp[50001];
public:
    int reversePairs(vector<int>& nums) 
    {
        return mergeSort(nums, 0, nums.size() - 1);    
    }
    int mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0;
        int ret = 0;
        //1,先划分左右数组
        int mid = (left + right) / 2;
        //2,先计算左右两侧的反转对
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);
        //3,计算翻转对的数量
        int cur1 = left, cur2 = mid + 1, i = left;
        while(cur1 <= mid)
        {
            while(cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) cur2++;
            if(cur2 > right) break;
            ret += right - cur2 + 1; //统计出一左一右的翻转对数量
            cur1++;
        }
        //4,排序
        cur1 = left, cur2 = mid + 1;
        while(cur1 <= mid && cur2 <= right) //降序,谁大谁先移
        {
            if(nums[cur1] <= nums[cur2])
            {
                tmp[i++] = nums[cur2++];
            }
            else
            {
                tmp[i++] = nums[cur1++];
            }
        }
        //5,处理剩余的
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        //6,合并
        for(int j = left; j <= right; j++)
            nums[j] = tmp[j];
        return ret;
    }
};

 策略二,升序:

class Solution
{
    int tmp[50001];
public:
    int reversePairs(vector<int>& nums) 
    {
        return mergeSort(nums, 0, nums.size() - 1);    
    }
    int mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0;
        int ret = 0;
        //1,先划分左右数组
        int mid = (left + right) / 2;
        //2,先计算左右两侧的反转对
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);
        //3,计算翻转对的数量
        int cur1 = left, cur2 = mid + 1, i = left;
        while(cur2 <= right)
        {
            while(cur1 <= mid && nums[cur2] >= nums[cur1] / 2.0) cur1++;
            if(cur1 > mid) break;
            ret += mid - cur1 + 1; //统计出一左一右的翻转对数量
            cur2++;
        }
        //4,排序
        cur1 = left, cur2 = mid + 1;
        while(cur1 <= mid && cur2 <= right) //升序,谁小谁先移
        {
            if(nums[cur1] <= nums[cur2])
            {
                tmp[i++] = nums[cur1++];
            }
            else
            {
                tmp[i++] = nums[cur2++];
            }
        }
        //5,处理剩余的
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        //6,合并
        for(int j = left; j <= right; j++)
            nums[j] = tmp[j];
        return ret;
    }
};

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

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

相关文章

2-141 怎么实现ROI-CS压缩感知核磁成像

怎么实现ROI-CS压缩感知核磁成像&#xff0c;这个案例告诉你。基于matlab的ROI-CS压缩感知核磁成像。ROI指在图像中预先定义的特定区域或区域集合&#xff0c;选择感兴趣的区域&#xff0c;通过减少信号重建所需的数据来缩短信号采样时间&#xff0c;减少计算量&#xff0c;并在…

C++ 实现俄罗斯方块游戏

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

VS+Qt解决提升控件后,包含头文件格式不对问题处理

一、前言 VSQt 提升控件后&#xff0c;在uic目录下会生成ui相关的初始化文件&#xff0c;对于提升的控件头文件包含的格式为#include<> 而非 #include “ ” 导致无法找到头文件。如果手动修改为 #include “ ”相当麻烦&#xff0c;甚至每次编译都要修改一遍&#xff0c…

02- 模块化编程-002 DS1302数码显示时间与日期

1、数码显示时间日期的电路 2、电路原理简介 电路组件与功能 单片机&#xff08; PIC16F887&#xff09;&#xff1a; 作为系统的主控芯片&#xff0c;处理所有输入输出&#xff0c;进行时间控制和显示信息更新。 DS1302&#xff08;实时时钟芯片&#xff09;&#xff1a; 用于…

java计算机毕设课设—Java聊天室(附源码、文章、相关截图、部署视频)

这是什么系统&#xff1f; 资源获取方式再最下方 java计算机毕设课设—Java聊天室(附源码、文章、相关截图、部署视频) Java聊天室系统是一个基于Java语言开发的在线即时通讯平台&#xff0c;旨在为用户提供一个简单、易用的实时交流环境。该系统支持多用户同时在线交流&…

编译原理第一次实验报告

源代码及附件&#xff1a;编译原理实验一源程序及附件资源-CSDN文库实验题目 实验要求 实验设计 前两部分指出了实验的宏观把控&#xff0c;为了具体实施实验&#xff0c;我们需要预先为实验做出如下设计&#xff1a; 本次实验我选取了C语言的一个子集进行设计词法分析器&…

Llama 3.2 Vision Molmo:多模态开源生态系统基础

编者按&#xff1a; 视觉功能的融入对模型能力和推理方式的影响如何&#xff1f;当我们需要一个既能看懂图像、又能生成文本的 AI 助手时&#xff0c;是否只能依赖于 GPT-4V 这样的闭源解决方案&#xff1f; 我们今天为大家分享的这篇文章&#xff0c;作者的核心观点是&#xf…

C++_day01

目录 0. 课前须知 1. C发展历史&#xff08;了解&#xff09; 2. C特点&#xff08;熟悉&#xff09; 3. 面向对象核心术语&#xff08;熟悉&#xff09; 4. 开发环境 5. 新建项目 4. 开发环境 5. 新建项目 0. 课前须知 C的思维与C语言完全不同&#xff0c;不能生搬硬套。 C偏向…

安娜的档案(Anna’s Archive) 镜像网站/国内最新可访问入口(持续更新)

安娜的档案&#xff08;Anna’s Archive&#xff09;是一个颇受关注的资源库。它涵盖了广泛的内容&#xff0c;可能包括各类文献、资料等。其特色在于丰富的信息储备和一定的系统性。安娜的档案&#xff08;Anna’s Archive&#xff09;用户可以从中获取多样的知识和数据&#…

Linux 下执行定时任务之 Systemd Timers

不知道 ECS 因为什么缘故&#xff0c;上面安装的 MySQL 服务老是不定期挂掉&#xff0c;本来想通过 Linux 得 Cron 配置个半小时的定时检测任务&#xff0c;结果一直没有执行&#xff0c;因此又尝试使用了 Systemd Timers 进行了重新配置&#xff0c;简要做个记录。 Systemd Ti…

【星闪EBM-H63开发板】AT固件的接口简介

引言 前面介绍了星闪EBM-H63开发板的透传固件&#xff0c;现在介绍一下AT固件。AT固件比透传固件要复杂的多&#xff0c;都让功能也多很多&#xff0c;可以配置很多星闪相关的参数。AT固件没有AT命令模式和数据模式切换的问题&#xff0c;因为收发的数据是出现在AT命令中的。 …

51单片机教程(四)- 点亮LED灯

1、项目分析 让输入/输出口的P1.0连接一盏LED灯进行点亮。 2、技术准备 1 LED组成 ​ 说明 二极管有 P型 和 N型材料构成&#xff0c;通常是&#xff1a;硅/锗 掺杂其他元素&#xff08;硼、磷等&#xff09; 电子是带负电的&#xff0c;是负电荷的载体&#xff0c;电子流…

开源库 FloatingActionButton

开源库FloatingActionButton Github:https://github.com/Clans/FloatingActionButton 这个库是在前面这个库android-floating-action-button的基础上修改的&#xff0c;增加了一些更强大和实用的特性。 特性&#xff1a; Android 5.0 以上点击会有水波纹效果 可以选择自定义…

Ubuntu 24.04上启用 root 用户通过 SSH 和图形界面进行登录

一、启用 root 用户的密码登录 设置 root 用户密码&#xff1a; 在终端中输入以下命令为 root 用户设置一个密码&#xff1a; testtest-virtual-machine:~$ sudo passwd root [sudo] test 的密码&#xff1a; 新的密码&#xff1a; 无效的密码&#xff1a; 密码是一个回文…

深度学习基础知识-损失函数

目录 1. 均方误差&#xff08;Mean Squared Error, MSE&#xff09; 2. 平均绝对误差&#xff08;Mean Absolute Error, MAE&#xff09; 3. Huber 损失 4. 交叉熵损失&#xff08;Cross-Entropy Loss&#xff09; 5. KL 散度&#xff08;Kullback-Leibler Divergence&…

基于vue框架的的考研信息共享平台v0eyp(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;国家政策,用户,院校政策,院校信息,考研资料,资料分类,考研论坛 开题报告内容 基于Vue框架的考研信息共享平台开题报告 一、研究背景与意义 随着考研人数的逐年增长&#xff0c;考研学生对高效、便捷、个性化的信息获取需求愈发强烈。…

区别:矩阵合同和矩阵酉相似

矩阵的合同和酉相似在许多方面具有相似性&#xff0c;但也有明显的区别。 定义 矩阵合同&#xff1a;给定矩阵 A 和 B &#xff0c;若存在一个非奇异矩阵 P &#xff0c;使得 则称矩阵 A 和 B 是合同的。合同变换常用于实对称矩阵的特征问题等。 酉相似&#xff1a;给定矩阵 …

海的回忆:海滨学院班级记忆录技术实现

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

半个月,练完这50个Python实战项目你就牛了!

今日精选50个Python实战项目&#xff0c;边做边学&#xff0c;让Python技能突飞猛进&#xff01; 好记性不如烂笔头&#xff0c;实践是提升技能的王道&#xff01;这70个项目涵盖广泛&#xff0c;难度亲民&#xff0c;特别适合Python新手入门与进阶。它们不仅实用性强&#xf…

贪心算法习题其三【力扣】【算法学习day.20】

前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&am…