当前位置: 首页 > news >正文

深入刨析数据结构之排序(下)

目录

1.内部排序

1.5选择排序

1.5.1简单选择排序

1.5.2树形选择排序

1.6堆排序

1.7归并排序

1.7.1递归归并

1.7.2非递归归并

1.8计数排序

1.9基数排序

常见内部排序的总结:


1.内部排序

1.5选择排序

   选择排序(Selection Sort)的基本思想是:每一趟在n-i+1(i=1,2,⋯,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。其中最简单且为读者最熟悉的是简单选择排序(Simple Selection Sort)。


1.5.1简单选择排序

   一趟简单选择排序的操作为:通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1≤i≤n)个记录交换之。
   显然,对L.r[1..n]中记录进行简单选择排序的算法为:令i从1至n-1,进行n-1趟选择操作,容易看出,简单选择排序过程中,所需进行记录移动的操作次数较少,其最小值为“0”,最大值为3(n-1)。然而,无论记录的初始排列如何,所需进行的关键字间的比较次数相同,均为n(n-1)/2。因此,总的时间复杂度也是O(n^2)。

具体过程太过于抽象,我们来看看选择排序的动图加以理解:

步骤及思路:(按照升序排序)

1.设置外层循环,将循环次数设置为0-n-2次,因为我们还需要留出最后一个数进行比较

2.设置内层循环,从当前下标的下一个元素开始寻找比这个元素小的元素

3.如果较小元素与参照元素下标不相同,就进行交换

下面是代码实现:

void SelectSort1(int* arr, int n)
{int i = 0;for (i = 0; i < n - 1; i++){int min = i;int j = 0;for (j = i + 1; j < n; j++){if (arr[j] < arr[min]){min = j;}}if (min != i){int tmp = arr[min];arr[min] = arr[i];arr[i] = tmp;}}
}

   那么,能否加以改进呢?
   从上述可见,选择排序的主要操作是进行关键字间的比较,因此改进简单选择排序应从如何减少“比较”出发考虑。显然,在n个关键字中选出最小值,至少进行n-1次比较,然而,继续在剩余的n-1个关键字中选择次小值就并非一定要进行n-2次比较,若能利用前n-1次比较所得信息,则可减少以后各趟选择排序中所用的比较次数。因此我们来介绍一下树形选择排序

1.5.2树形选择排序

   树形选择排序(Tree Selection Sort),又称锦标赛排序(Tournament Sort),是一种按照锦标赛的思想进行选择排序的方法。首先对n个记录的关键字进行两两比较,然后在其中[n/2]个较小者之间再进行两两比较,如此重复,直至选出最小关键字的记录为止,这个过程可用一棵有n个叶子结点的完全二叉树表示。例如,图10.9(a)中的二叉树表示从8个关键字中选出最小关键字的过程。8个叶子节点依次存放排序之前的8个关键字,每个非终端结点中的关键字均等于其左、右孩子结点中较小的关键字,则根结点中的关键字即为叶子节点中的最小关键字。在输出最小关键字之后,根据关系的可传递性,欲选出次小关键字,仅需将叶子结点中的最小关键字(13)改为“最大值”,然后从该叶子结点开始。和其左(或右)兄弟的关键字进行比紋,修改从叶子节点到根的路径各个结点的关键字,则根结点的关键字即为次小关键字。同理,可依次选出从小到大的所有关键字(参见图10.9(b)和(c))。由于含有n个叶子结点的完全二叉树的深度为[logn]+1,则在树形选择排序中,除了最小关键字之外,每选择一个次小关键字仅需进行[logn]次比较,因此,它的时间复杂度为O(nlogn)。但是,这种排序方法尚有辅助存储空间较多、和“最大值”进行多余的比较等缺点。为了弥补,威洛姆斯(J.willioms)在1964年提出了另一种形式的选择排序堆排序

1.6堆排序

   堆排序(Heap Sort)只需要一个记录大小的辅助空间,每个待排序的记录仅占有一个存储空间。

   什么是堆?堆的定义如下:n个元素的序列{k₁,k₂,…,kₙ}当且仅当满足下关系时,称之为堆。       

若将和此序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列{k₁,k₂,…, kn} 是堆,则堆顶元素(或完全二叉树的根)必为序列中 n个元素的最小值(或最大值)。例如,下列两个序列为堆,对应的完全二叉树如图10.10所示。

若在输出堆顶的最小值之后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。由此,实现堆排序需要解决两个问题:

(1)如何由一个无序序列建成一个堆?

(2)如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?

下面先讨论第二个问题。例如,图10.11(a)是个堆,假设输出堆顶元素之后,以堆中最后一个元素替代之,如图10.11(b)所示。此时根结点的左、右子树均为堆,则仅需自上至下进行调整即可。首先以堆顶元素和其左、右子树根结点的值比较之,由于右子树根结点的值小于左子树根结点的值且小于根结点的值,则将27和97交换之;由于97 替代了27之后破坏了右子树的“堆”,则需进行和上述相同的调整,直至叶子结点,调整后的状态如图10.11(c)所示,此时堆顶为 n-1个元素中的最小值。重复上述过程,将堆顶元素27和堆中最后一个元素97 交换且调整,得到如图10.11(d)所示新的堆。

我们称这个自堆顶至叶子的调整过程为“筛选”。

从一个无序序列建堆的过程就是一个反复“筛选”的过程。若将此序列看成是一个完全二叉树,则最后一个非终端结点是第[n/2]个元素,由此“筛选”只需从第[n/2]个元素开始。例如,图10.12(a)中的二叉树表示一个有8个元素的无序序列

{49,38,65,97,76,13,27,49}

则筛选从第 4个元素开始,再于97>49,则交换之,交换后的序列如图10.12(b)所示,同理、在第3个元素65被筛选之后序列的状态如图10.12(c)所示,由于第2个元素38不大于其左、右子树根的值,则筛选后的序列不变。图10.12(e)所示 筛选根元素49之后建成的堆:

步骤及思路:(按照升序排序)

1.我们先来进行建堆的操作,我们有两种方法可以选择,向上建堆和向下建堆

2.然后我们可以定义end来代表数组最后一个元素的下标,在end>=1的条件下设置循环

3.循环内部先交换数组第一个元素和最后一个元素,因为第一个元素是堆中最大的元素,将则个元素放到最后一个,再重新建堆,就可以形成升序数组,重复这个过程就可以完成排序

向上和向下建堆:

1.向上建堆:传入孩子结点的下标,根据父亲结点=(孩子结点-1)/2,我们将父亲结点与孩子结点进行比较,如果父亲结点小于孩子结点,交换他们,将父亲结点作为新的孩子结点,继续比较,直到不再小于孩子结点为止。

2.向下建堆:传入父亲结点的下标,和数组的总元素个数,根据左孩子结点=父亲结点*2+1,我们先将左孩子结点与右孩子结点的数据进行比较,选出较大的孩子结点,与父亲结点比较,同样,如果父亲结点小于孩子结点,交换他们,将孩子结点作为新的父亲结点,继续比较,直到不再小于孩子结点为止或是孩子结点的下标大于元素个数为止。

下面是代码实现:

void AdjustUp(int* arr, int child)
{int parent = (child - 1) / 2;while (child > 0){if (arr[child] > arr[parent]){swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
void AdjustDown(int* arr, int n, int parent)
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && arr[child] < arr[child + 1]){child++;}if (arr[child] > arr[parent]){swap(&arr[parent], &arr[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}
//最坏:o(nlogn)
void HeapSort(int* arr, int n)
{int i = 0;for (i = 0; i < n; i++){AdjustUp(arr, i);}int end = n - 1;while (end > 0){//将最大值与最后一个交换一下,end--,继续排前面的元素swap(&arr[0], &arr[end]);AdjustDown(arr, end, 0);end--;}
}

堆排序对记录数较少的文件并不值得提倡,但对n较大的文件还是很有效的,因为其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上,由此,堆排序在最坏的情况下,其时间的复杂度为O(nlogn),相对于快速排序来说,这是堆排序的最大优点,此外,堆排序仅需一个记录大小供交换用的辅助空间。

1.7归并排序

   归并排序(Merging Sort)是又一类不同的排序方法。“归并”的含义是将两个或两个以上的有序表组合成一个新的有序表。它的实现方法早已为读者所熟悉,无论是顺序存储结构还是链表存储结构,都可在(的时间量级上实现。利用归并的思想容易实现排序。

   假设初始序列含有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到 个长度为2 或1的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2-路归并排序。例如图10.13为2-路归并排序的一个例子。

1.7.1递归归并

步骤及实现:(按照升序排序)

1.先开辟一个与原数组所占空间相同的新数组,(建议使用malloc函数),传入第一个元素下标left,最后一个元素下标right

2.取数组的中间下标数mid,调用递归,将left和mid传入,再次调用递归,传入mid和right

3.类似于后序遍历,先调用递归两次,再进行归并,将数组划分为begin和mid,mid+1和end,其中begin=left,end=right,begin和mid作为第一个归并数组的begin1和end1,mid+1和end作为第二个归并数组的begin2和end2

4.在begin1<=end1&&begin2<=end2的条件下归并,之后再分别合并

5.使用memcpy将排好序的数据拷贝回原数组

下面是代码实现:

void _MergeSort(int* arr, int left,int right,int* tmp)
{if (left >= right)return;int mid = (left + right) / 2;//类似于后序遍历_MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid + 1, right, tmp);int begin1 = left;int end1 = mid;int begin2 = mid + 1;int end2 = right;int i = left;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[i++] = arr[begin1++];}else if (arr[begin1] > arr[begin2]){tmp[i++] = arr[begin2++];}else{tmp[i++] = arr[begin1++];tmp[i++] = arr[begin2++];}}while (begin1 <= end1){tmp[i++] = arr[begin1++];}while (begin2 <= end2){tmp[i++] = arr[begin2++];}memcpy(arr + left, tmp + left, sizeof(int) * (right - left + 1));
}
//N*logN
void MergeSort(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc failed");return;}_MergeSort(arr, 0, n - 1,tmp);free(tmp);tmp = NULL;
}

1.7.2非递归归并

步骤及思路(按照升序排序):

1.还是先开辟出一块空间与原数组空间大小相同,做好拷贝排序好的数据的准备

2.设置一个gap,使gap的初始值为1,每次循环后*=2,这样就可以实现从小区间归并到大区间归并

3.设置内层循环,设置两个归并数组的begin和end分别为i和i+gap-1,i+gap和i+2*gap-1,这里我们对边界值进行一些处理:如果end1或者begin2越界就退出循环,不进行拷贝,如果是end2越界,就修改为n-1,再进行与递归归并相同的操作

4.将有序的数据拷贝回原数组

下面是代码实现:

void MergeSortNonR(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc failed");return; }int gap = 1;while (gap < n){int i = 0;for (i = 0; i < n; i += 2 * gap){int begin1 = i;int end1 = i + gap - 1;int begin2 = i + gap;int end2 = i + 2 * gap - 1;int j = i;//画图理解,边界值的处理if (end1 >= n || begin2 >= n){break;}if (end2 >= n){end2 = n - 1;}while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[j++] = arr[begin1++];}else if (arr[begin1] > arr[begin2]){tmp[j++] = arr[begin2++];}else{tmp[j++] = arr[begin1++];tmp[j++] = arr[begin2++];}}while (begin1 <= end1){tmp[j++] = arr[begin1++];}while (begin2 <= end2){tmp[j++] = arr[begin2++];}memcpy(arr + i , tmp + i, sizeof(int) * (end2-i+1));//这里排完数据就拷贝,不要到时候一把梭哈}gap *= 2;}free(tmp);tmp = NULL;
}

1.8计数排序

步骤及思路:(按照升序排序)

1.这里我们先遍历一遍数组,选出最大值和最小值

2.通过最大值和最小值开辟出范围数组

3.再次遍历原数组,采用相对映射,将数组中的数据-最小值对应的下标++,这样新开辟的数组记录的是相对映射数据出现的次数

4.遍历新数组对原数组进行还原

下面是代码实现:

void CountSort(int* arr, int n)
{int max = arr[0];int min = arr[0];int i = 0;for (i = 0; i < n; i++){if (arr[i] > max){max = arr[i];}if (arr[i] < min){min = arr[i];}}int range = max - min + 1;int* countA = (int*)calloc(1,sizeof(int) * (range));if (countA == NULL){perror("malloc failed");return;}for (i = 0; i < n; i++){countA[(arr[i] - min)]++;}int j = 0;for (i = 0; i < range; i++){while (countA[i] > 0){arr[j++] = i + min;countA[i]--;}}free(countA);countA = NULL;
}

1.9基数排序

   基数排序(Radix Sorting)是和前面所述各类排序方法完全不相同的一种排序方法。
   从前几节的讨论可见,实现排序主要是通过关键字间的比较和移动记录这两种操作,而实现基数排序不需要进行记录关键字间的比较。基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。

书上对于多关键字的排序介绍:

有点抽象,我们直接来看例子:

步骤及实现:

1.这里需要用到队列这一数据结构,由于已经介绍过,这里就直接引用,我们建立一个队列数组,队列数组的下标就代表每一次基数排序的关键字

2.先将队列数组初始化,先统计数据,分别入队列,注意这里循环的次数,是按照数据的最高位的位数来确定的,使用collect和destribute分别来收集,分发数据

3.按照最低位优先原则,使用GetKey函数来获得数据指定位数的数字,来入对应数字的队列

4.分发数据,完成第一次基数排序,重复这个过程

下面来看代码实现:

int Getkey(int num, int index)
{int ret = 0;while (index > 0){ret = num % 10;num /= 10;index--;}return ret;
}
void Collect(Queue* quarr, int* arr, int index, int sz)
{int i = 0;for (i = 0; i < sz; i++){int judge = Getkey(arr[i], index);QueuePush(&(quarr[judge]), arr[i]);}
}
void Destribute(Queue* quarr, int* arr, int sz)
{int i = 0;for (i = 0; i < sz; i++){int j = 0;int put = 0;for (j = 0; j < 10; j++){if (!QueueEmpty(&quarr[j])){put = QueueFront(&quarr[j]);QueuePop(&quarr[j]);break;}}arr[i] = put;}
}
//基数排序,按个位十位百位分别入队列,出队列循环排序
void RadixSort(int* arr, int n)
{Queue quarr[10];int i = 0;for (i = 0; i < 10; i++){QueueInit(&quarr[i]);}for (i = 1; i <= 3; i++){Collect(quarr, arr, i, n);//统计数据,分别入队列Destribute(quarr, arr, n);//按照队列标号从小到大依次出队列,直到队列为空}for (i = 0; i < 10; i++){QueueDestroy(&quarr[i]);}
}

常见内部排序的总结:

(1)从平均时间性能而言,快速排序最佳,其所需时间最省,但快速排序在最坏情况下的时间性能不如堆排序和归并排序。而后两者相比较的结果是,在n较大时,归并排序所需时间较堆排序省,但它所需的辅助存储量最多。
(2)上表中的“简单排序”包括除希尔排序之外的所有插人排序,起泡排序和简单选择排序,其中以直接插入排序为最简单,当序列中的记录“基本有序”或n值较小时,它是最佳的排序方法,因此常将它和其他的排序方法,诸如快速排序、归并排序等结合在一起使用。
(3)基数排序的时间复杂度也可写成O(d•n)。因此,它最适用于n值很大而关键宇较小的序列。若关键字也很大,而序列中大多数记录的“最高位关键字”均不同,则亦可先按“最高位关键字”不同将序列分成若干“小”的子序列,而后进行直接插入排序
(4)从方法的稳定性来比较,基数排序是稳定的内排方法,所有时间复杂度为O(n^2)的简单排序法也是稳定的,然而,快速排序、堆排序和希尔排序等时间性能较好的排序方法都是不稳定的。一般来说,排序过程中的“比较”是在“相邻的两个记录关键字”间进行的排序方法是稳定的。值得提出的是,稳定性是由方法本身决定的,对不稳定的排序方法而言,不管其描述形式如何,总能举出一个说明不稳定的实例来。反之,对稳定的排序方法,总能找到一种不引起不稳定的描述形式。由于大多数情况下排序是按记录的主关键字进行的,则所用的排序方法是否稳定无关紧要。若排序按记录的次关键字进行,则应根据问题所需慎重选择排序方法及其描述算法。

1.稳定的排序方法有冒泡排序,直接插入排序,归并排序,其他都是不稳定的,如:选择排序,5,2,5...........2,会将2的顺序颠倒,希尔排序,相同数值的数据可能会被分到不同组,堆排序,堆顶元素会被换到数组末尾,顺序颠倒,快速排序,key的元素会和left的元素交换,有可能会颠倒顺序

2.在数组接近有序的时候,这时最最有效的排序方法是直接插入排序,快速排序会退化成O(n^2)

3.在空间复杂度中,归并排序需要O(n)的辅助空间来用开辟新数组,来拷贝排序好的数据,快速排序需要一个栈空间,如果对数组进行相对有序的划分时,则需要logn的辅助空间,而关键字为第一个元素或者为最后一个元素时,则需要n的辅助空间,其他排序方法都只需要单个辅助空间即可。

相关文章:

深入刨析数据结构之排序(下)

目录 1.内部排序 1.5选择排序 1.5.1简单选择排序 1.5.2树形选择排序 1.6堆排序 1.7归并排序 1.7.1递归归并 1.7.2非递归归并 1.8计数排序 1.9基数排序 常见内部排序的总结&#xff1a; 1.内部排序 1.5选择排序 选择排序&#xff08;Selection Sort&#xff09;的基…...

特殊数据类型的深度分析:JSON、数组和 HSTORE 的实用价值

title: 特殊数据类型的深度分析:JSON、数组和 HSTORE 的实用价值 date: 2025/1/4 updated: 2025/1/4 author: cmdragon excerpt: 随着数据管理需求的多样化,许多现代数据库系统开始支持特殊数据类型,以满足更多复杂应用场景的需求。在 PostgreSQL 中,JSON、数组和 HSTOR…...

PCA降维算法详细推导

关于一个小小的PCA的推导 文章目录 关于一个小小的PCA的推导1 谱分解 (spectral decomposition)2 奇异矩阵(singular matrix)3 酉相似(unitary similarity)4 酉矩阵5 共轭变换6 酉等价7 矩阵的迹的计算以及PCA算法推导8 幂等矩阵(idempotent matrix)9 Von Neumanns 迹不等式 [w…...

NS4861 单灯指示独立耳锂电池充放电管理 IC

1 特性  最大 500mA 线性充电电流&#xff0c;外部可调节  内部预设 4.2V 充电浮充电压  支持 0V 电池充电激活  支持充满 / 再充功能  内置同步升压放电模块&#xff0c;输出电压 5.1V  同步升压 VOUT 最大输出电流 500mA  VOL/OR 独…...

编写可复用性的模块

在生活中&#xff0c;重复的机械劳动会消耗我们的时间和精力&#xff0c;提高生产成本&#xff0c;降低工作效率。同样&#xff0c;在代码世界中&#xff0c;编写重复的代码会导致代码的冗余&#xff0c;页面性能的下降以及后期维护成本的增加。由此可见将重复的事情复用起来是…...

2025年1月4日CSDN的Markdown编辑器

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...

广域网连接PPP

广域网连接PPP PPP协议是一种应用广泛的点到点链路协议&#xff0c;主要用于点到点连接的路由器间的通信。PPP协议既可以用于同步通信&#xff0c;也可以用于异步通信&#xff0c;本部分只讨论同步接口上的PPP配置。 锐捷路由器的同步串行口默认封装Cisco HDLC&#xff0c;所…...

【pyqt】(四)Designer布局

布局 之前我们利用鼠标拖动的控件的时候&#xff0c;发现一些部件很难完成对齐这些工作&#xff0c;pyqt为我们提供的多种布局功能不仅可以让排版更加美观&#xff0c;还能够让界面自适应窗口大小的变化&#xff0c;使得布局美观合理。最常使用的三种布局就是垂直河子布局、水…...

【从零开始入门unity游戏开发之——C#篇40】C#特性(Attributes)和自定义特性

文章目录 前言一、特性&#xff08;Attributes&#xff09;基本概念二、自定义特性1、自定义特性代码示例&#xff1a;2、应用自定义特性&#xff1a;3、解释3.1 **AttributeUsage 特性**3.2 特性的命名3.3 **构造函数**&#xff1a;3.4 **属性**&#xff1a; 4、使用反射获取特…...

DES密码的安全性分析(简化版本)

DES仍是世界上使用最广的&#xff08;DES发行后20年&#xff0c;互联网的兴起&#xff0c;人们开始觉得DES不安全了&#xff0c;但DES的实现成本也越来越低&#xff09; 宏观分析&#xff1a; 密钥空间方面&#xff1a; 密钥长度&#xff1a;DES 算法使用 56 位的密钥对数据…...

引入三方jar包命令

mvn install:install-file \ -Dfile本地磁盘路径 \ -DgroupId组织名称 \ -DartifactId项目名称 \ -Dversion版本号 \ -Dpackagingjar 例如 假设你的 JAR 文件路径是 /home/user/common-pojo-1.0-SNAPSHOT.jar&#xff0c;组织名称是 com.example&#xff0c;项目名…...

机器学习基础-机器学习的常用学习方法

半监督学习的概念 少量有标签样本和大量有标签样本进行学习&#xff1b;这种方法旨在利用未标注数据中的结构信息来提高模型性能&#xff0c;尤其是在标注数据获取成本高昂或困难的情况下。 规则学习的概念 基本概念 机器学习里的规则 若......则...... 解释&#xff1a;如果…...

在控制领域中如何区分有效性、优越性、稳定性和鲁棒性?

在控制领域中&#xff0c;区分有效性、优越性、稳定性和鲁棒性可以通过具体的控制器设计实例来更好地理解。以下以经典的质量-弹簧-阻尼系统的PID控制器设计为例&#xff0c;展示如何区分这四个性能指标。 经典质量-弹簧-阻尼系统的PID控制器设计 质量-弹簧-阻尼系统模型 考…...

美国宏观经济基础框架梳理

玩转币圈和美股&#xff0c;最关键的是理解美国宏观经济。以下是核心逻辑&#xff1a;美国经济数据→政策调整→资金流动→资产价格变化。掌握这些因素的关系&#xff0c;才能在市场中立于不败之地。 一、核心变量及其意义 1. GDP&#xff08;国内生产总值&#xff09; • …...

装饰器模式详解

装饰器模式&#xff08;Decorator Pattern&#xff09;是一种设计模式&#xff0c;属于结构型模式之一。它允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。这种模式创建了一个装饰类&#xff0c;用来包装原有类的一个实例&#xff0c;从而扩展该实例的功能。…...

[最新] SIM卡取出后还能找到我的iPhone吗?

您是否曾在任何地方丢失过 SIM 卡&#xff1f;或者您是否已移除 SIM 卡&#xff0c;现在无法在任何地方找到您的 iPhone&#xff1f;在这篇博客中&#xff0c;您将了解即使 SIM 卡被移除&#xff0c;“查找我的 iPhone”也能正常工作。 在某些情况下&#xff0c;您必须取出 SIM…...

数据分析思维(六):分析方法——相关分析方法

数据分析并非只是简单的数据分析工具三板斧——Excel、SQL、Python&#xff0c;更重要的是数据分析思维。没有数据分析思维和业务知识&#xff0c;就算拿到一堆数据&#xff0c;也不知道如何下手。 推荐书本《数据分析思维——分析方法和业务知识》&#xff0c;本文内容就是提取…...

谷歌2025年AI战略与产品线布局

在2024年12月的战略会议上,谷歌高层向员工描绘了2025年的宏伟蓝图,特别是在人工智能(AI)领域。这一年被定位为AI发展的关键转折点,谷歌计划通过一系列新产品和创新来巩固其在全球科技领域的领导地位。本文将深入探讨谷歌的2025年AI战略、重点产品以及竞争策略。 一、整体…...

登录的几种方式

使用Session完成登录 1. 手机号发送验证码 逻辑步骤&#xff1a; 校验手机号格式是否正确。生成验证码&#xff08;例如使用Hutool工具类&#xff09;。将手机号和验证码存入Session。返回验证码发送成功的响应。 2. 用户登录逻辑 逻辑步骤&#xff1a; 从Session中获取存…...

Scala_【5】函数式编程

第五章 函数式编程函数和方法的区别函数声明函数参数可变参数参数默认值 函数至简原则匿名函数高阶函数函数作为值传递函数作为参数传递函数作为返回值 函数闭包&柯里化函数递归控制抽象惰性函数友情链接 函数式编程 面向对象编程 解决问题时&#xff0c;分解对象&#xff…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

大数据治理的常见方式

大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法&#xff0c;以下是几种常见的治理方式&#xff1a; 1. 数据质量管理 核心方法&#xff1a; 数据校验&#xff1a;建立数据校验规则&#xff08;格式、范围、一致性等&#xff09;数据清洗&…...

【若依】框架项目部署笔记

参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作&#xff1a; 压缩包下载&#xff1a;http://download.redis.io/releases 1. 上传压缩包&#xff0c;并进入压缩包所在目录&#xff0c;解压到目标…...

高保真组件库:开关

一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...