王道408数据结构——第八章 排序

一、排序定义

重新排列表中的元素,使表中的元素满足按关键字有序。
算法的稳定性:带排序表中关键字相同的元素,其相对次序在排序前后不变,这称这个排序算法是稳定的。算法是否稳定并不能衡量一个算法的优劣。如果带排序表中的关键字均不重复,则排序结果是唯一的,算法的稳定性就无关紧要。

大部分的内部排序都需要执行比较和移动操作。通过比较两个关键字的大小,确定对应元素的前后关系;然后通过移动元素以达到有序。

在基于比较的排序方法中,每次比较两个关键字大小之后,仅出现两种可能的转移。因此可以用一棵二叉树来描述比较判定过程,由此可以证明:当文件的 n 个关键字随机分布时,任何借助比较的排序算法,至少需要O(nlog⁡2n)O(n\log_2n)O(nlog2n)的时间。

二、插入排序——直接插入排序

1. 描述

插入排序是一种简单直观的排序方法,其基本思想是将一个待排序的记录按其关键字大小插入前面已排好序的子序列。

插入排序通常采用就地排序,在从后向前的比较过程中,需要反复把已排序元素逐步向后挪位,为新元素腾出插入空间。

2. 代码和示例

下面是直接插入排序的代码

void insertSort(ElemType A[], int n){int i, j;for(i = 2; i <= n; i++){  // 依次对A[2]到A[n]进行插入排序if (A[i] < A[i-1]){  // 如果当前元素小于其前驱,将其插入前面序列A[0] = A[1];  // 哨兵,A[0]不存放元素for(j = i-1; A[0] < A[j]; j--)  // 从后往前查找待插入位置A[j+1]  = A[j];  // 元素挨个往后挪位A[j+1] = A[0];  // 最后一次循环,j指向待插入点的前一个位置。}}
}

直接插入排序示例
这里哨兵的作用是防止数组下标越界,提高查找效率

3. 空间效率

就地排序,仅使用了常数个辅助单元,空间复杂度为O(1)O(1)O(1)

4. 时间效率

排序过程中,向有序子表中逐个插入元素的操作进行了n-1趟;每趟操作都分为比较关键字和移动元素,次数取决于待排序表的初始状态。

在最好情况下,表中元素已经有序,此时每插入一个元素,都只需一次比较而不需要移动元素。时间复杂度为O(n)O(n)O(n)
在最坏情况下。表中元素的顺序刚好与排序结果相反(逆序),总的比较次数达到最大,为∑i=2ni\sum^n_{i=2}ii=2ni,总的移动次数也达到最大,为∑i=2n(i+1)\sum^n_{i=2}(i+1)i=2n(i+1)
在平均情况下,考虑待排序表中的元素是随机的,此时取最好与最坏情况的平均值作为平均情况的时间复杂度。总的比较次数和总的移动次数均为n24\frac{n^2}44n2
因此,直接插入排序的时间复杂度为O(n2)O(n^2)O(n2)

5. 稳定性

每次插入元素总是从后往前先比较在移动,算法是稳定的

6. 适用性

直接插入排序算法时使用与顺序储存(大部分排序算法仅适用于顺序储存的线性表)和链式储存。

其更适用于基本有序、数据量不大的排序表。

三、插入排序——折半插入排序

1. 描述

对于顺序表,对插入位置的查找过程可以采用折半查找来实现。确定待插入位置后,再统一向后移动元素。

2. 时间效率

折半插入排序减少了比较元素的次数,总比较次数约为O(nlog⁡2n)O(n\log_2n)O(nlog2n),该比较次数与表的初始状态无关,仅取决于表中元素的个数;
而元素的移动次数并未改变,仍依赖于表的初始状态。
因此,时间复杂度仍为O(n2)O(n^2)O(n2)

但对于数据量不是很大的排序表,折半插入排序往往表现出很好的性能。

3. 稳定性

折半插入排序是一种稳定的排序方法。

四、插入排序——希尔排序(缩小增量排序)

1. 描述

基本思想:先将待排序表分割成若干形如[i,i+d,i+2d,⋯,i+kd][i,i+d,i+2d,\cdots,i+kd][i,i+d,i+2d,,i+kd]的特殊子表,即把相隔某个增量的记录组成一个子表,对各个子表进行直接插入排序;当整个表中的元素已呈“基本有序”时,再对全体记录进行一次直接插入排序。

希尔排序的过程如下:

  1. 先取一个小于 n 的步长 d1d_1d1
  2. 把表中的全部记录分成d1d_1d1组:所有距离为d1d_1d1倍数的记录放在同一组;
  3. 在各组内进行直接插入排序;
  4. 取第二个步长d2(d2<d1)d_2(d_2<d_1)d2(d2<d1),重复第二、三步。
  5. 不断取更大步长,直到dt=1d_t=1dt=1,即所有记录已放在同一组中,再进行一次直接插入排序。由于此时已经具有了较好的局部有序性,故可以很快得到最终结果。

通常步长did_idi的取值为d1=n2,di+1=⌊di2⌋,d最后一个=1d_1=\frac n2,d_{i+1}=\lfloor \frac {d_i}2\rfloor,d_{最后一个}=1d1=2n,di+1=2di,d=1

2. 代码和示例

希尔排序的代码如下

void shellSort(Elemtpye A[], int n){int dk, i, j;for(dk = n/2; dk >= 1; dk = dk/2){// 将各个子表的直接插入排序分解,合并为一个循环,一次循环过程仅按顺序处理一个元素// 从第1个子表的第二个元素开始,第二次循环进行第2个子表的第二个元素for(i = dk+1; i < n; ++i){if(A[i] >= A[i-dk])  // 仅在当前元素无序时进行排序continue;A[0] = A[i];  // A[0]不是哨兵,仅暂存元素// j小于等于0时,说明该元素在该子表的已排序序列中最小for(j = i-dk; j >= 0 && A[0] < A[j]; j -= dk)  A[j+dk] = A[j];A[j+dk] = A[0];}}
}

希尔排序示例

3. 空间效率

仅使用了常数个辅助单元,空间复杂度为O(1)O(1)O(1)

4. 时间效率

当n在某个特定范围时,希尔排序的时间复杂度约为O(n1.3)O(n^{1.3})O(n1.3)
在最坏情况下,希尔排序的时间复杂度为O(n2)O(n^2)O(n2)

5. 稳定性

当相同关键字的记录被划分到不同子表时,可能会改变它们之间的相对次序,因此希尔排序是一种不稳定的排序方法。

6. 适用性

希尔排序对较大规模的排序都可以达到很高的效率。

仅适用于顺序存储的线性表。

五、交换排序——冒泡排序

1. 描述

所谓交换,是指根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置。

基本思想:从后往前(或从前往后)两两比较相邻元素的值,若为逆序,则交换它们,直到序列比较完,这称为一趟冒泡。一趟冒泡的结果是将最小(或最大)元素交换到待排序的第一个位置。下一趟冒泡时,已确定位置的元素不再参与比较。这样最多做n-1趟冒泡就可以把所有元素排好。

冒泡排序中所产生的有序子序列一定是全局有序的,每趟排序都会将一个元素放在其最终位置。

2. 代码和示例

冒泡排序算法代码如下

void bubbleSort(ElemType A[], int n){for(i = 0; i < n-1; i ++){flag = false;  // 标志位,记录本趟冒泡是否发生交换for(int j = n-1; j > i; j--){  // 一趟冒泡,顺序与外层循环相反if(A[j-1] > A[j]){ swap(A[j-1], A[j]);flag = true;}}if( !flag )  // 若某趟冒泡过程没有发生交换,说明表已经有序return;}
}

在这里插入图片描述

3. 空间效率

仅使用了常数个辅助单元,空间复杂度为O(1)O(1)O(1)

4. 时间效率

最好情况下,当初始序列有序时,第一趟冒泡标志位flag仍为false,直接跳出循环,比较次数为n-1,移动次数为0,时间复杂度为O(n)O(n)O(n)
最坏情况下,当初始序列逆序时,需要进行n-1趟排序,第 i 趟排序要进行 n-i 次关键字的比较,而且每次比较后都必须进行三次移动来交换元素的位置。比较次数=∑i=1n−1(n−i)=n(n−1)2,移动次数=∑i=1n−13(n−i)=3n(n−1)2比较次数=\sum^{n-1}_{i=1}(n-i)=\frac{n(n-1)}2,移动次数=\sum^{n-1}_{i=1}3(n-i)=\frac{3n(n-1)}2=i=1n1(ni)=2n(n1)=i=1n13(ni)=23n(n1)从而最坏情况下时间复杂度为O(n2)O(n^2)O(n2)
平均情况下,时间复杂度为O(n2)O(n^2)O(n2)

5. 稳定性

冒泡排序时一种稳定的排序方法。
如果把代码中判断是否逆序的条件由“>”改为“≥”,则算法变得不稳定。

六、交换排序——快速排序

1. 描述

快速排序的基本思想是基于分治法的:在待排序表中选取一个元素,称为枢轴(或称基准,常取首元素)。通过一趟排序,将待排序表分成两部分,一部分中所有元素均小于枢轴,另一部分元素均大于枢轴,两部分分别位于枢轴元素的两侧,这个过程称为一趟快速排序(或一次划分)。然后递归地分别对两个子表重复上述过程,直到每部分只有一个元素或空为止,此时所有元素都放在了最终位置。

快速排序并不产生有序子序列,但每趟排序后会将枢轴元素放在最终位置上。

2. 代码和示例

一趟快速排序是一个交替搜索和交换的过程,算法如下

void quickSort(ElemType A[], int low, int high){if(low >= high)  // 递归跳出条件。只有一个元素或为空return;// 一趟快排,将表划分为两个子表,返回枢轴位置int pivotpos = partition(A, low, high); quickSort(A, low, pivotpos-1);  // 对左子表进行递归quickSort(A, pivotpos+1, high);  // 对右子表进行递归
}// 一趟划分,交替进行搜索交换
int partition(ElemType A[], int low, int high){ElemType pivot = A[low];  // 设为枢轴while(low < high){  // 从high往前搜索,找到最近的小于枢轴的元素,// 将其置入枢轴或者上一次交换留出空位中while(low < high && A[high] > pivot) high--;A[low] = A[high];// 从low往后搜索,找到最近的大于枢轴的元素,// 将其置入上一次交换留出的空位中。while(low < high && A[low] < pivot)low++;A[high] = A[low];}A[low] = pivot;  // 将枢轴元素置入交替搜索后留出的空位中。return low;  // 返回枢轴位置
}

在这里插入图片描述

3. 空间效率

快排是递归地,需要借助一个递归工作栈来保持每层递归调用的必要信息,容量与递归调用的最大深度一致。
最好情况下,空间复杂度为O(log⁡2n)O(\log_2n)O(log2n)
最坏情况下,因为要进行n-1次递归调用,栈的深度为O(n)O(n)O(n)
平均情况下,栈的深度为O(log⁡2n)O(\log_2n)O(log2n)

4. 时间效率

快速排序的运行时间和划分是否对称有关。
最好情况下,partition()可以做到最平衡的划分,得到的两个子问题大小都不大于n/2,这种情况下快速排序的运行速度将大大提升,此时时间复杂度为O(nlog⁡2n)O(n\log_2n)O(nlog2n)
最坏情况下,划分的两个区域分别包含n-1个元素和0个元素。若初始表基本有序基本逆序时,每层递归都出现最坏情况。此时时间复杂度为O(n2)O(n^2)O(n2)
平均情况下的快速排序与其最佳情况的运行时间很接近,时间复杂度为O(nlog⁡2n)O(n\log_2n)O(nlog2n)

有很多方法可以提升算法的效率:一种方法时尽可能选择一个可以将数据平分的枢轴,如从序列的头尾和中间选取三个元素,选择三个元素的中间值作为枢轴;或随机从表中选取一个枢轴,这样做可以时最坏情况几乎不会出现。

快速排序是所有内部排序中平均性能最优的排序算法

5. 稳定性

某一趟中,两个关键字相同的元素,从一个区间被交换到另一个区间的过程中,相对位置会发生变化。快速排序是一种不稳定的排序方法。

七、选择排序——简单选择排序

1. 描述

基本思想:每一趟排序后,将剩余待排序元素中选择关键字最小(或最大)的元素,放在其最终位置,最终位置的原有元素与其交换位置。

2. 代码

简单选择排序代码如下

void selectSort(ElemType A[], int n){for(int i = 0; i < n-1; i++){  // 到第n-1趟,待排元素只剩一个,就不用再选了int min = i;for(int j = i+1; j < n; j++){  // 选择待排序列中的最小元素if(A[j] > A[i])min = j;}if(min != i)swap(A[i], A[min]);}
}

3. 空间效率

仅使用常数个辅助单元,空间效率为O(1)O(1)O(1)

4. 时间效率

在简单选择排序中,元素移动的次数很少,不会超过3(n−1)3(n-1)3(n1)次,最好情况是移动0次(此时对应表已有序)。但元素的比较次数和序列的初始状态无关,始终是n(n−1)2\frac{n(n-1)}22n(n1)次,因此时间复杂度始终O(n2)O(n^2)O(n2)

5. 稳定性

在第 i 趟把最小元素和第 i 个元素进行交换时,可能导致第 i 个元素与其后含有相同关键字元素的相对位置发生变化。简单选择排序是不稳定的。

八、选择排序——堆排序

1. 堆的定义

若n个关键字序列满足以下任一条件:
{L(i)⩾L(2i)∧L(i)⩾L(2i+1)(1⩽i⩽⌊n2⌋)L(i)⩽L(2i)∧L(i)⩽L(2i+1)\left\{ \begin{array}{l} L(i)\geqslant L(2i)\wedge L(i)\geqslant L(2i+1)&(1\leqslant i\leqslant\lfloor\frac n2\rfloor)\\ L(i)\leqslant L(2i)\wedge L(i)\leqslant L(2i+1) \end{array} \right. {L(i)L(2i)L(i)L(2i+1)L(i)L(2i)L(i)L(2i+1)(1i2n)
该序列称为

堆可以视为一棵顺序存储的完全二叉树,满足第一个条件的称为大根堆,其最大元素放在根节点,且堆的非叶结点的值均大于其左右子树。,满足第二个条件的称为小根堆。

2. 建堆(构造初始堆)

n个结点的完全二叉树,其最后一个非叶结点序号是⌊n2⌋\lfloor \frac n2\rfloor2n。从最后一个非叶结点开始往前一次遍历结点,对每一个以当前结点为根的子树进行检查:对于大根堆,若根结点关键字小于左右孩子,将左右孩子中较大者与之交换,小根堆反之;交换后可能破坏下一级的堆,因此对下一级的堆重复进行检查和交换,直到以当前结点为根的子树构成堆为止。

下面是建立大根堆的算法

void buildMaxHeap(ElemType A[], int len){for(int i = len/2; i > 0; i--)  // 从最小非叶结点开始,反复调整堆headAdjust(A, i, len);
}void headAdjust(ElemType A[], int k, int len){A[0] = A[k];  // 暂存子树根节点for(int i = 2*k; i <= len; i *= 2){  // 沿key值较大的结点往下if(i < len && A[i+1] > A[i])  // 左右子树在顺序表中总是相邻i++;  // 选择key值较大的结点if(A[0] >= A[i])  // 当前堆已满足性质break;A[k] = A[i];  // 调整结点k = i;  // 进入较大叶结点的子树}A[k] = A[0];
}

调整的时间与数高h有关,为O(h)O(h)O(h)
在建含 n 个元素的堆时,关键字的比较总次数不超过4n,时间复杂度为O(n)O(n)O(n),这说明可以在线性时间将一个无序数组建成一个堆。

3. 删除结点

堆的删除通常在根节点处,此时需要重新调整结构以保持性质。

输出堆顶元素后,将堆底元素移到堆顶。再从堆顶元素开始向下调整,使其保持大堆顶的性质。

4. 插入结点

对堆进行插入时,先将新结点放在堆的末端,再对这新结点向上执行调整操作。

5. 算法与示例

下面给出堆排序算法,即依次删除根节点的算法

void heapSort(ElemType A[], int len){buildMaxHeap(A, len);  // 建立初始堆// 进行n-1趟交换和建堆过程。当i=1时,仅剩根节点,此时数组已经有序for(int i = len; i > 1; i--){  // 输出堆顶元素(和堆底元素进行交换),此时数组中i~len的元素已经是全局有序的了swap(A[i], A[1]);  headAdjust(A, 1, i-1);  // 把剩余i-1个元素元素整理成堆}
}

建立初始堆的示例如下
在这里插入图片描述

输出根节点87,将最后一个叶节点09置于根的位置,将剩余元素调整成新堆调整
在这里插入图片描述
在堆的末端插入新结点,重新调整堆
在这里插入图片描述

6. 空间效率

仅使用了常数个辅助单元,空间复杂度为O(1)O(1)O(1)

7. 时间效率

建堆时间为O(n)O(n)O(n),之后有n-1次向下调整操作,每次调整时间复杂度为O(h)。故在最好、最坏和平均情况下,堆排序的时间复杂度O(nlog⁡2n)O(n\log_2n)O(nlog2n)

8. 稳定性

在进行筛选时,有可能把后面相同关键字的元素调整到前面,所以堆排序时一种不稳定的排序方法。

9. 适用性

堆排序适合关键字较多的情况。

九、归并排序

1. 描述

假定待排序表含有n 个记录,则可将其视为n个有序的子表,每个子表长度为1。然后两两归并(称为2路归并排序),再将得到的长度为2或1的有序表两两归并,如此重复,直到合并成一个长度为n的有序表为止。

2. 代码和示例

递归形式的归并排序算法时基于分治的,分为分解和合并两个步骤

void mergeSort(ElemType A[], int low, int high){if(low < heigh){  // 递归退出条件,当low==heigh时,子表长度为1,停止分解int mid = (low + high) / 2;  // 分解:划分两个子表,对两个子表递归地进行排序mergeSort(A, low, mid); mergeSort(A, mid+1, high);merge(A, low, mid, high);  // 归并:合并两个已经排序的子表得到新的排序结构}
}// 设立一个辅助数组
ElemType *B = (ElemType *)malloc( (n+1)*sizeof(ElemType) );  // 归并两个子表的过程与合并两个有序链表的算法过程类似
void merge(ElemType A[], int low, int mid, int high){int i, j, k;for(i = low; i <= high; i++)  // 将两个子表中所有元素复制到B中的对应位置B[i] = A[i];for(i = low, j = mid+1, k = low; i <= mid && j <= high; k++){if(B[i] <= B[j])A[k] = B[i++];elseA[k] = B[j++];}while(i <= mid)A[k++] = B[i++];while(j <= high)A[k++] = B[j++];}

在这里插入图片描述

3. 空间效率

merge()操作中,辅助空间刚好为n个单元,空间复杂度为O(n)O(n)O(n)

4. 时间效率

每趟归并的时间复杂度为O(n)O(n)O(n),需要进行⌈log⁡2n⌉\lceil\log_2n\rceillog2n趟归并,所以算法的时间复杂度为O(nlog⁡2n)O(n\log_2n)O(nlog2n)

一般而言,对于N个元素进行 k 路归并排序时,排序的趟数 m 满足km=Nk^m=Nkm=N,从而m=⌈log⁡kN⌉m=\lceil\log_kN\rceilm=logkN

从单个记录起进行两两归并并不值得提倡,通常将它和直接插入排序结合。改进后的归并排序仍是稳定的。

5. 稳定性

merge()操作并不会改变相同关键字记录的相对次序,算法是稳定的。

十、基数排序

1. 描述

基数排序十一种很特别的排序方法,它基于比较和移动进行排序,而基于关键字各位的大小进行排序。技术排序是一种借助多关键字排序的思想以对单逻辑关键字进行排序的方法。

假设长度为 n 的线性表中每个结点aja_jaj的关键字由 d 元组(kjd−1,kjd−2,⋯,kj1,kj0)(k_j^{d-1},k_j^{d-2},\cdots,k_j^1,k_j^0)(kjd1,kjd2,,kj1,kj0)组成,满足0≤kji≤r−10\leq k_j^i\leq r-10kjir1。其中kjd−1k_j^{d-1}kjd1称为最主位关键字kj0k_j^0kj0称为最次位关键字

为实现多关键字排序,通常由两种方法:

  • 最高位优先(MSD):按关键字位权重递减依次逐层划分成若干更小的子序列,最后将所有子序列依次连接成一个有序序列。
  • 最低位优先(LSD):按关键字位权重递增依次进行排序,最后形成一个有序子序列。

2. 算法和示例

下面描述以 r 为基数的最低位优先基数排序的过程
在排序过程中,使用 r 个队列Q0,Q1,⋯,Qr−1Q_0,Q_1,\cdots,Q_{r-1}Q0,Q1,,Qr1。基数排序的过程如下:
从i=0开始(数字最低位),依次做一次“分配”和“收集”。

  • 分配:开始时,把Q0,Q1,⋯,Qr−1Q_0,Q_1,\cdots,Q_{r-1}Q0,Q1,,Qr1各个队列置空,然后依次考察线性表中的每个结点aj(j=0,1,⋯,n−1)a_j(j=0,1,\cdots,n-1)aj(j=0,1,,n1),若aja_jaj关键字kji=kk_j^i=kkji=k,就把aja_jaj放进QkQ_kQk队列中。
  • 收集:把Q0,Q1,⋯,Qr−1Q_0,Q_1,\cdots,Q_{r-1}Q0,Q1,,Qr1各个队列首位相接,得到新的结点序列,从而组成新的线性表。

通常采用链式基数排序,假设对如下10个记录进行排序:
在这里插入图片描述
每个关键字是1000以下的正整数,由3位子关键字K1K2K3K^1K^2K^3K1K2K3构成,分别代表百位、十位、个位,一共需要进行三趟分配-收集操作。基数r=10,在排序过程中需要借助10个链队列。

第一趟分配用最低位关键字(个位)K3K^3K3进行。将所有K3K^3K3相等的记录分配到同一个队列,然后进行收集。
在这里插入图片描述
第二趟分配用次低位关键字(十位)K2K^2K2进行,将所有K2K^2K2相等的记录分配到同一个队列,然后进行收集。
在这里插入图片描述
第三趟分配用最高位关键字(百位)K1K^1K1进行,将所有K3K^3K3相等的记录分配到同一个队列,然后进行收集。自此整个排序结束
在这里插入图片描述

3. 空间效率

一趟排序需要辅助空间为 r(r个队列,r个队头指针和队尾指针)。空间复杂度为O(r)O(r)O(r)

4. 时间效率

基数排序需要进行 d 趟分配和收集,一趟分配需要O(n)O(n)O(n),一趟收集需要O(r)O(r)O(r)。所以基数排序的时间复杂度为O(d(n+r))O(d(n+r))O(d(n+r)),其与序列的初始状态无关。

5. 稳定性

基数排序是稳定的。

十一、排序算法的比较

算法种类时间复杂度空间复杂度是否稳定
最好情况最坏情况平均情况
直接插入排序
(插入排序)
O(n)O(n2)O(n2)O(1)稳定
冒泡排序
(交换排序)
O(n)O(n2)O(n2)O(1)稳定
简单选择排序
(选择排序)
O(n2)O(n2)O(n2)O(1)不稳定
希尔排序
(插入排序)
依赖增量函数O(1)不稳定
快速排序
(交换排序)
O(n2)
最坏情况下是O(n)
不稳定
堆排序
(选择排序)
O(1)不稳定
2路归并排序O(n)稳定
基数排序O(d(n+r))O(d(n+r))O(d(n+r))O(r)稳定
  • 在实际应用中,快速排序往往可以优于其他算法,被认为是目前基于比较的内部排序中最好的方法。
  • 冒泡排序和堆排序每趟处理后都能产生当前的最大值或最小值。
  • 快速排序一趟处理就能确定一个元素的最终位置。
  • 若 n 较小,可以采用直接插入排序或简单选择排序;
    • 由于直接插入排序所需的记录移动次数较简单选择排序多,当记录本身信息量较大时,选用简单选择排序较好。
  • 若文件的初始状态已经按关键字基本有序,选用直接插入排序或冒泡排序。
  • 若 n 较大,则应采用时间复杂度为Olog⁡2n)O\log2n)Olog2n)的排序方法:快速排序、推排序或归并排序;
    • 当关键字随机分布时,快速排序平均时间最短;
    • 堆排序所需的辅助空间少于快速排序,且不会出现快速排序可能出现的最坏情况;
    • 若要求排序稳定,则可选用归并排序。
  • 若 n 很大、记录的关键字位数较少且可以分解时,采用基数排序较好
  • 当记录本身信息量较大时,为避免耗费大量时间移动记录,可以采用链表作为存储结构。

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

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

相关文章

Avalonia跨平台入门第二十一篇之玩耍CEF

在前面分享的几篇中咱已经玩耍了Popup、ListBox多选、Grid动态分、RadioButton模板、控件的拖放效果、控件的置顶和置底、控件的锁定、自定义Window样式、动画效果、Expander控件、ListBox折叠列表、聊天窗口、ListBox图片消息、窗口抖动、语音发送、语音播放、语音播放问题;今…

golang实现自定义驱动的Cache

近期在写 ActivedRouter项目的时候需求一个缓存模型&#xff0c;要求缓存模型支持不同驱动,例如:memory、file、redis、mysql&#xff0c;实现思路代码如下: cache.go文件,定义缓存对外接口 //ActivedRouter //Author:usher.yue //Amail:usher.yuegmail.com //TencentQQ:422366…

【自定义控件】c#winform自定义控件实现标签控件

介绍首先我们设计这个控件的时候要明白控件是怎样交互的&#xff0c; 熟悉b站的小伙伴应该知道 &#xff0c;我们上传视频的时候会去选择标签 &#xff0c;我们输入标签文本 按下回车就代表该标签已经添加成功了&#xff0c;效果图如下&#xff01;控件拆分我们首先拆分一下该控…

ASP.NET 使用Ajax(转)

之前在Ajax初步理解中介绍了对Ajax的初步理解&#xff0c;本文将介绍在ASP.NET中如何方便使用Ajax&#xff0c;第一种当然是使用jQuery的ajax&#xff0c;功能强大而且操作简单方便&#xff0c;第二种是使用.NET封装好的ScriptManager。 $.ajax向普通页面发送get请求 这是最简单…

fir.im 持续集成技术实践

互联网时代&#xff0c;人人都在追求产品的快速响应、快速迭代和快速验证。不论是创业团队还是大中型企业&#xff0c;都在探索属于自己的敏捷开发、持续交付之道。fir.im 团队也在全面实施敏捷&#xff0c;并推出新持续集成服务— flow.ci &#xff0c;以帮助企业将开发测试流…

宇宙最強的IDE - Visual Studio 25岁生日快乐

每位开发者从入门开始或多或少都会接触过 Visual Studio &#xff0c; 现今的 Visual Studio 除了支持传统的 C , C# , Visual Basic.NET ,F# 的编程语言外&#xff0c;还可以做 Python , Node.js 的开发。在应用场景上也从单一的桌面应用&#xff0c;延伸到 Web &#xff0c; …

有没有一段代码,让你觉得人类的智慧也可以璀璨无比?【转】

转自&#xff1a;https://www.zhihu.com/question/30262900 作者&#xff1a;烧茄子链接&#xff1a;https://www.zhihu.com/question/30262900/answer/48741026来源&#xff1a;知乎著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。当然是这个…

如何使用 abp 创建 module 并应用单独的数据库迁移

最近在学习使用 abp 来做一些小程序。abp 是一个功能丰富的 .NET 开发框架&#xff0c;完全开源&#xff0c;遵循 DDD&#xff08;领域驱动&#xff09;设计模式&#xff0c;支持微服务开发&#xff0c;集成了 Identity、角色权限、本地化、动态代理、后台任务、分布式消息、审…

MinGW安装和使用基础教程

MinGW全称Minimalist GNU For Windows&#xff0c;是个精简的Windows平台C/C、ADA及Fortran编译器&#xff0c;相比Cygwin而言&#xff0c;体积要小很多&#xff0c;使用较为方便。MinGW提供了一套完整的开源编译工具集&#xff0c;以适合Windows平台应用开发&#xff0c;且不依…

px,em,rem,vw单位在网页和移动端的应用

px&#xff1a; 是网页设计中最常用的单位&#xff0c;然而1px到底是多大长&#xff0c;恐怕没有人能回答上来 它用来表示屏幕设备物理上能显示的最小的一个点&#xff0c;这个点不是固定宽度的&#xff0c;不同设备上点的长度、比例有可能会不同。 假设&#xff1a;你现在用的…

cs-Panination

ylbtech-Unitity: cs-PaninationPager.cs IPagingOption.cs IPagedList.cs PagingOption.cs PagedList.cs PagingExtensions.cs 1.A,效果图返回顶部 1.B,源代码返回顶部1.B.1,Pager.cs using System; using System.Collections.Generic; using System.Linq; using System.Text…

SignalR的使用

什么是 SignalR&#xff1f;ASP.NET Core SignalR 是一个开放源代码库&#xff0c;可用于简化向应用添加实时 Web 功能。实时 Web 功能使服务器端代码能够将内容推送到客户端。适合 SignalR 的候选项&#xff1a;需要从服务器进行高频率更新的应用。示例包括游戏、社交网络、投…

NHibernate之旅(7):初探NHibernate中的并发控制

本节内容 什么是并发控制&#xff1f; 悲观并发控制(Pessimistic Concurrency)乐观并发控制(Optimistic Concurrency)NHibernate支持乐观并发控制实例分析结语什么是并发控制&#xff1f; 当很多人试图同一时候改动数据库中的数据时&#xff0c;必须实现一个控制系统&#xff0…

期望DP

期望DP的一般做法是从末状态開始递推&#xff1a; Problem DescriptionAkemi Homura is a Mahou Shoujo (Puella Magi/Magical Girl).Homura wants to help her friend Madoka save the world. But because of the plot of the Boss Incubator, she is trapped in a labyrinth …

神奇的[Caller*]属性

前言上次&#xff0c;我们《使用 CallerArgumentExpression 检查弃元参数》&#xff0c;它实际是利用编译器编译时将变量名称传入。其实&#xff0c;.NET中提供了多个[Caller*]属性&#xff0c;帮助我们轻松获取调用者信息。CallerFilePathAttribute允许获取包含调用方的源文件…

java dateTime + long

2019独角兽企业重金招聘Python工程师标准>>> public static void main(String[] args) throws Exception{SimpleDateFormat sdfnew SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // long timeStartsdf.parse("2011-09-20 12:30:45").getTime();l…

.NET Core中异常过滤器ExceptionFilter的使用介绍

介绍实现需要继承IExceptionFilter 或 IAsyncExceptionFilter。可用于实现常见的错误处理策略。使用场景首先讲一下我们为什么要使用异常过滤器 &#xff0c;如果业务场景复杂&#xff0c;只使用HttpStatusCode&#xff0c;抛出异常后,后期要加很多字段来描述。那么这种就比较不…

程序一启动检查网络,如果没有网络就退出程序

转载于:https://www.cnblogs.com/songxing10000/p/4823812.html

看小说的这些年

从大一开始&#xff0c;就开始看起了小说&#xff0c;不是那种名著类型&#xff0c;而是快餐小说&#xff0c;玄幻、都市、言情、科幻&#xff0c;什么都会看&#xff0c;因为看多了&#xff0c;就会发现&#xff0c;已经没什么可以看的。 谈起快餐小说&#xff0c;已经有很多被…

COMA(一): Learning to Communicate with Deep Multi-Agent Reinforcement Learning 论文讲解

Learning to Communicate with Deep Multi-Agent Reinforcement Learning 论文讲解 论文链接&#xff1a;https://papers.nips.cc/paper/6042-learning-to-communicate-with-deep-multi-agent-reinforcement-learning.pdf &#xff08;这篇论文是COMA三部曲中的第&#xff08…