内排序及时间复杂度分析-插入排序选择排序交换排序归并排序分配和索引排序对比...
基本概念
什么是排序?
- 排序
- 将序列中的记录按照排序码顺序排列起来
- 排序码域的值具有不减(或不增)的顺序
- 内排序
- 整个排序过程在内存中完成
- 给定一个序列 R = { r1, r2, ...,rn }
- 其排序码分别为 k = { k1, k2, ...,kn }
- 排序的目的:将记录按排序码重排
- 形成新的有序序列R’={r’1,r’2,...,r’n}
- 相应排序码为 k’= { k’1, k’2, ...,k’n }
- 排序码的顺序
- 其中 k’1 ≤ k’2 ≤ ... ≤ k’n,称为不减序
- 或 k’1 ≥ k’2 ≥ ... ≥ k’n ,称为不增序
排序的稳定性
- 稳定性
- 存在多个具有相同排序码的记录
- 排序后这些记录的相对次序保持不变
- 排序稳定性的意义
- 保持第一关键字相同的数据排序前后顺序不变,若不稳定的排序要做到这一点,则需要增加第二个关键字。
插入排序
直接插入排序算法
template <class Record>
void ImprovedInsertSort (Record Array[], int n){ //Array[] 为待排序数组,n 为数组长度
Record TempRecord; // 临时变量
for (int i=1; i<n; i++){ // 依次插入第 i 个记录
TempRecord = Array[i];
//从 i 开始往前寻找记录 i 的正确位置
int j = i-1;
//将那些大于等于记录 i 的记录后移
while ((j>=0) && (TempRecord < Array[j])){
Array[j+1] = Array[j];
j = j - 1; }
//此时 j 后面就是记录 i 的正确位置,回填
Array[j+1] = TempRecord; }
}
复制代码
算法分析
- 稳定
- 空间代价:Θ(1)
- 时间代价:
- 最佳情况:n-1次比较,2(n-1)次移动,Θ(n)
- 最差情况:Θ(n2)
- 比较次数为n(n-1)/2 = O(n2)
- 移动次数为 (n-1)(n-4)/2 = O(n2)
- 平均情况:Θ(n2)
- 性质:
- 序列本身有序的话时间代价为O(N)
- 对于短序列比较有效
Shell排序
算法思想
- 先将序列转化为若干小序列,然后在小序列里面插入排序
- 逐步增加小序列规模,从而减少小序列个数
- 最后对整个序列扫尾直接插入排序
“增量每次除以2”的Shell排序
template <class Record>
void ShellSort(Record Array[], int n) {
// Shell排序,Array[]为待排序数组,n为数组长度
int i, delta;
// 增量delta每次除以2递减
for (delta = n/2; delta>0; delta /= 2)
for (i = 0; i < delta; i++)
// 分别对delta个子序列进行插入排序 //“&”传 Array[i]的地址,数组总长度为n-i ModInsSort(&Array[i], n-i, delta);
// 如果增量序列不能保证最后一个delta间距为1 // 可以安排下面这个扫尾性质的插入排序
// ModInsSort(Array, n, 1);
}template <class Record> // 参数delta表示当前的增量 void ModInsSort(Record Array[], int n, int delta) {
int i, j;
// 对子序列中第i个记录,寻找合适的插入位置 for (i = delta; i < n; i += delta)// j以dealta为步长向前寻找逆置对进行调整 for(j=i;j>=delta;j-=delta) {
if (Array[j] < Array[j-delta]) // 逆置对 swap(Array, j, j-delta);// 交换
else break; }}
复制代码
算法分析
- 不稳定
- 空间代价O(1)
- 时间代价O(n2)
- 选取的增量并不互质,实际上上一轮子序列都排序过了,导致效率过低
- 选取合适的增量序列,可以是时间接近于O(n)
Hibbard 增量序列
- Hibbard 增量序列
- {2k -1,2k-1 -1,...,7,3,1}
- Shell(3) 和 Hibbard 增量序列的 Shell 排序的效率可以达到 Θ(n3/2)
Shell最好的代价
- 呈 2p3q 形式的一系列整数: – 1, 2, 3, 4, 6, 8, 9, 12
- Θ(nlog2n)
选择排序
- 直接选择排序
- 堆排序
直接选择排序
每次从剩下的堆中选择最小的
template <class Record>
void SelectSort(Record Array[], int n) {
// 依次选出第i小的记录,即剩余记录中最小的那个
for (int i=0; i<n-1; i++) {
// 首先假设记录i就是最小的 int Smallest = i;
// 开始向后扫描所有剩余记录 for (int j=i+1;j<n; j++)
// 如果发现更小的记录,记录它的位置 if (Array[j] < Array[Smallest])
Smallest = j;
// 将第i小的记录放在数组中第i个位置(从而导致不稳定)
swap(Array, i, Smallest);
} }
复制代码
算法分析
- 不稳定(因为会交换位置)
- 空间代价O(1)
- 时间代价
- 比较次数O(n2)
- 交换次数n-1
- 总时间代价O(n2)
堆排序
- 选择类内排序 由于直接选择排序是从剩余记录中线性地去查找最大记录所以效率较低,我们可以采用最大堆来实现,效率会更高
- 选择类外排序(这一部分后面讨论)
- 置换选择排序
- 赢者树、败方树
template <class Record>
void sort(Record Array[], int n){
int i;
// 建堆
MaxHeap<Record> max_heap
= MaxHeap<Record>(Array,n,n); // 算法操作n-1次,最小元素不需要出堆 for (i = 0; i < n-1; i++)
// 依次找出剩余记录中的最大记录,即堆顶
max_heap. RemoveMax(); }
复制代码
算法分析
- 不稳定(Siftdown的过程会有可能导致相同元素出入序列不一致)
- 建堆O(n)
- 删除堆顶O(log n)
- 总时间代价O(nlog n)
- 空间代价O(1)
- 在只需得到最大或最小的一部分元素时有优势(部分排序)
交换排序
- 冒泡排序
- 快速排序
冒泡排序
- 算法思想
- 不停地比较相邻的记录,如果不满足排序要求,就 交换相邻记录,直到所有的记录都已经排好序
- 检查每次冒泡过程中是否发生过交换,如果没 有,则表明整个数组已经排好序了,排序结束
- 避免不必要的比较
template <class Record>
void BubbleSort(Record Array[], int n) {
bool NoSwap; // 是否发生了交换的标志
int i, j;
for (i = 0; i < n-1; i++) {NoSwap = true;for (j = n-1; j > i; j--){if (Array[j] < Array[j-1]) {swap(Array, j, j-1); NoSwap = false}if (NoSwap)return; }
} }
复制代码
算法分析
- 稳定
- 空间代价O(1)
- 时间代价
- 比较次数
- 最少O(n)
- 最多O(n2)
- 交换次数 平均O(n2)
- 比较次数
- 结论
- 最大平均为O(n2)
- 最小为O(n)
- 冒泡排序和直接选择排序从时间复杂度上来讲是一样的,都是O(n^2)的排序算法,但是因为冒泡排序是每次交换相邻两个,所以在常数上可能要稍微大一点。冒泡排序是稳定的,直接选择排序是不稳定的。
快速排序
- 思想(分治策略)
- 选择轴值(pivot)
- 将序列划分为两个子序列L和R,使得L中所有记录都小于或等于轴值,R中记录都大于轴值
- 对子序列L和R递归进行快速排序
- 轴值选择
- 尽可能使 L, R 长度相等
- 选择策略:
- 选择最左边记录
- 随机选择
- 选择平均值
template <class Record>
void QuickSort(Record Array[], int left, int right) {if (right <= left) // 只有0或1个记录,就不需排序 return;int pivot = SelectPivot(left, right); // 选择轴值swap(Array, pivot, right); // 轴值放到数组末端 pivot = Partition(Array, left, right); // 分割后轴值正确 QuickSort(Array, left, pivot-1); // 右子序列递归快排 QuickSort(Array, pivot +1, right); // 右子序列递归快排
}
int SelectPivot(int left, int right) {return (left+right)/2;// 选中间记录作为轴值
}//分割函数
template <class Record>
int Partition(Record Array[], int left, int right) { // 分割函数,分割后轴值已到达正确位置int l = left; // l 为左指针int r = right; // r 为右指针Record TempRecord = Array[r]; // 保存轴值while (l != r) { // l, r 不断向中间移动,直到相遇// l 指针向右移动,直到找到一个大于轴值的记录 while (Array[l] <= TempRecord && r > l)l++;if (l < r) { // 未相遇,将逆置元素换到右边空位Array[r] = Array[l];r--; // r指针向左移动一步 }// r 指针向左移动,直到找到一个大于轴值的记录 while (Array[r] >= TempRecord && r > l)r--;if (l < r) { // 未相遇,将逆置元素换到左空位Array[l] = Array[r];l++; // l 指针向右移动一步 }} //end whileArray[l]=TempRecord; //把轴值回填到分界位置l上return l;// 返回分界位置l
}
复制代码
时间代价
- 不稳定(轴值的选择会影响到)
- 最差情况:
- 时间代价:Θ(n2)
- 空间代价:Θ(n)
- 最佳情况:
- 时间代价:Θ(nlog n)
- 空间代价:Θ(log n)
- 平均情况:
- 时间代价:Θ(nlog n)
- 空间代价:Θ(log n)
快速排序优化
- 轴值选择 RQS
- 小子串不递归 (比如阈值 28, 采用直接插入排序等)
- 消除递归(用栈,队列)
- 更多参见这篇文章
- 快速排序比大部分排序算法都要快。尽管我们可以在某些特殊的情况下写出比快速排序快的算法,但是就通常情况而言,没有比它更快的了。快速排序是递归的,对于内存非常有限的机器来说,它不是一个好的选择。
归并排序
归并排序思想
- 划分为两个子序列
- 分别对每个子序列归并排序
- 有序子序列合并
两路归并排序
#include<iostream>
using namespace std;// 两个有序子序列都从左向右扫描,归并到新数组
template <class Record>
void Merge(Record Array[], Record TempArray[], int left ,int right,int middle){int i,j,index1,index2;for(j=left;j<=right;j++)TempArray[j] = Array[j]; //放入临时数组i = left;index1 = left;index2 = middle+1;while(index1<=middle && index2<=right){if(TempArray[index2] < TempArray[index1])Array[i++] = TempArray[index2++];elseArray[i++] = TempArray[index1++];}while(index1<=middle) //复制剩下的左序列Array[i++] = TempArray[index1++];while(index2<=right) //复制剩下的右序列Array[i++] = TempArray[index2++];
}template <class Record>
void MergeSort(Record Array[], Record TempArray[], int left, int right) {
// Array为待排序数组,left,right两端int middle;if (left < right) { // 否则序列中只有0或1个记录,不用排序middle = (left + right) / 2; // 平分为两个子序列 // 对左边一半进行递归 MergeSort(Array,TempArray,left,middle);// 对右边一半进行递归MergeSort(Array, TempArray,middle+1,right);Merge(Array, TempArray,left,right,middle); // 归并 }
}int main(){int arr[] = {1,9,7,10,5,8,4,2,11,6};int tempArr[10];MergeSort(arr,tempArr,0,9);for(int i:arr)cout<<i<<" ";cout<<endl;return 1;
}
复制代码
归并算法优化
- 同优化的快速排序一样,对基本已排序序 列直接插入排序
- R.Sedgewick优化:归并时从两端开始 处理,向中间推进,简化边界判断
#include<iostream>
using namespace std;
#define THRESHOLD 28template <class Record>
void InsertSort(Record *p,int len){Record TempRecord; // 临时变量for (int i=1; i<len; i++){ // 依次插入第 i 个记录TempRecord = *(p+i);//从 i 开始往前寻找记录 i 的正确位置int j = i-1;//将那些大于等于记录 i 的记录后移while ((j>=0) && (TempRecord < *(p+j))){*(p+j+1) = *(p+j);j = j - 1; }*(p+j+1) = TempRecord; }
}template <class Record>
void ModMerge(Record Array[],Record TempArray[],int left,int right,int middle) {int index1,index2; // 两个子序列的起始位置 int i,j,k ;for (i = left; i <= middle; i++)TempArray[i] = Array[i]; // 复制左边的子序列 for (j = 1; j <= right-middle; j++) // 颠倒复制右序列TempArray[right-j+1] = Array[j+middle];for (index1=left, index2=right, k=left; k<=right; k++)if (TempArray[index1] <= TempArray[index2])Array[k] = TempArray[index1++];elseArray[k] = TempArray[index2--];
}template <class Record>
void ModMergeSort(Record Array[], Record TempArray[], int left, int right) { // Array为待排序数组,left,right两端int middle;if (right-left+1 > THRESHOLD) { // 长序列递归middle = (left + right) / 2; // 从中间划为两个子序列 ModMergeSort(Array, TempArray ,left,middle); // 左 ModMergeSort(Array, TempArray ,middle+1,right);// 右 // 对相邻的有序序列进行归并ModMerge(Array, TempArray ,left,right,middle); // 归并 }else InsertSort(&Array[left],right-left+1); // 小序列插入排序
}int main(){int arr[] = {1,9,7,10,5,8,4,2,11,6,11,12,13,14,15,16,17,18,19,28,17,14,12,13,16,36,27,29,20,4,2,11,6,11,12,13,14,15,16,17,18,19,28};int tempArr[43];ModMergeSort<int>(arr,tempArr,0,42);for(int i:arr)cout<<i<<" ";cout<<endl;return 1;
}
复制代码
算法分析
- 空间代价O(n)
- 最大,最小,平均时间代价O(nlog n)
- 普通归并排序是稳定的,因为合并的时候是按照顺序合并。Sedgewick采用一个正序一个逆序合并所以相同值会颠倒顺序。所以是不稳定的。
- Sedgewick算法需要更多地元素之间的比较,但是普通算法需要判断边界,所以比较次数更多。总体来说Sedgewick 算法更优。
分配排序和基数排序
- 不需要进行纪录之间两两比较
- 需要事先知道记录序列的一些 具体情况
桶排序
- 事先知道序列中的记录都位于某个小区间段 [0, m) 内
- 将具有相同值的记录都分配到同一个桶中,然后依次按照编号从桶中取出记录,组成一个有序序列
template <class Record>
void BucketSort(Record Array[], int n, int max) { Record *TempArray = new Record[n]; // 临时数组int *count = new int[max]; int i;for (i = 0; i < n; i++)TempArray[i] = Array[i]; for (i = 0; i < max; i++)count[i] = 0;for (i = 0; i < n; i++)count[Array[i]]++; for (i = 1; i < max; i++)count[i] = count[i-1]+count [i]; // c[i]记录i+1的起址for (i = n-1; i >= 0; i--) // 尾部开始,保证稳定性 Array[--count[TempArray[i]]] = TempArray[i];
}
复制代码
算法分析
- 数组长度为 n, 所有记录区间 [0, m) 上
- 时间代价:
- 统计计数:Θ(n+m) , 输出有序序列时循环 n 次
- 总的时间代价为 Θ(m+n)
- 适用于m相对于n很小的情况,也就是适用于小区间,但m不要超过nlogn比较好,因为桶式排序的算法复杂度为O(n+m),如果m过大则换用其它方法比较好.
- 空间代价:
- m 个计数器,长度为 n 的临时数组,Θ(m+n)
- 稳定
基数排序
- 桶式排序只适合 m 很小的情况
- 基数排序:当m很大时,可以将一个记录的值即排序码拆分为多个部分来进行比较
例子
例如:对 0 到 9999 之间的整数进行排序
- 将四位数看作是由四个排序码决定,即千、百 、十、个位,其中千位为最高排序码,个位为 最低排序码。基数 r=10。
- 可以按千、百、十、个位数字依次进行4次桶 式排序
- 4趟分配排序后,整个序列就排好序了
低位优先法
- LSD,Least Significant Digit first
- 从最低位 k0 开始排序
- 对于排好的序列再用次低位 k1 排序;
- 依次重复,直至对最高位 kd-1 排好序后, 整个序列成为有序的
- 分、收;分、收;...;分、收的过程 – 比较简单,计算机常用
基数排序的实现
- 主要讨论 LSD(低位优先法)
- 基于顺序存储
- 基于链式存储
- 原始输入数组R的长度为n,基数为 r,排序码个数为 d
基于数组的基数排序
template <class Record>
void RadixSort(Record Array[], int n, int d, int r) {Record *TempArray = new Record[n];int *count = new int[r]; int i, j, k;int Radix = 1; // 模进位,用于取Array[j]的第i位 for(i=1;i<=d;i++) { //对第i个排序码分配for (j = 0; j < r; j++)count[j] = 0; // 初始计数器均为0for(j=0;j<n;j++){ //统计每桶记录数 k = (Array[j] / Radix) % r; // 取第i位 count[k]++; // 相应计数器加1}for (j = 1; j < r; j++) // 给桶划分下标界 count[j] = count[j-1] + count[j];for(j=n-1;j>=0;j--) { //从数组尾部收集 k=(Array[j]/Radix)%r; //取第i位 count[k]--; // 桶剩余量计数器减1 TempArray[count[k]] = Array[j]; // 入桶}for (j = 0; j < n; j++) // 内容复制回 Array 中Array[j] = TempArray[j];Radix *= r; // 修改模Radix}
}
复制代码
顺序基数排序代价分析
- 空间代价:
- 临时数组, n
- r 个计数器
- 总空间代价 Θ(n+r)
- 时间代价
- 桶式排序:Θ(n+r)
- d 次桶式排序
- Θ(d·(n+r))
基于静态链的基数排序
- 将分配出来的子序列存放在 r 个 (静 态链组织的) 队列中
- 链式存储避免了空间浪费情况
链式基数排序算法代价分析
- 空间代价
- n 个记录空间
- r 个子序列的头尾指针
- Θ(n + r)
- 时间代价
- 不需要移动记录本身, 只需要修改记录的 next 指针
- Θ(d·(n+r))
template <class Record>
void RadixSort(Record *Array, int n, int d, int r) {int i, first = 0; // first指向第一个记录 StaticQueue *queue = new StaticQueue[r];for (i = 0; i < n-1; i++)Array[i].next = i + 1; // 初始化静态指针域 Array[n-1].next = -1; // 链尾next为空// 对第i个排序码进行分配和收集,一共d趟 for(i=0;i<d;i++) {Distribute(Array, first, i, r, queue);Collect(Array, first, r, queue); }delete[] queue;AddrSort(Array, n, first); // 整理后,按下标有序
}template <class Record>
void Distribute(Record *Array, int first, int i, int r, StaticQueue *queue) {int j, k, a, curr = first;for (j = 0; j < r; j++) queue[j].head = -1;while (curr != -1) { // 对整个静态链进行分配k = Array[curr].key;for (a = 0; a < i; a++) // 取第i位排序码数字kk = k / r; k = k % r;if (queue[k].head == -1) // 把数据分配到第k个桶中 queue[k].head = curr;else Array[queue[k].tail].next = curr; queue[k].tail = curr;curr = Array[curr].next; // curr移动,继续分配}
}template <class Record>
void Collect(Record *Array, int& first, int r, StaticQueue *queue) {int last, k=0; // 已收集到的最后一个记录 while (queue[k].head == -1) k++; // 找到第一个非空队 first = queue[k].head; last = queue[k].tail;while (k < r-1) { // 继续收集下一个非空队列k++;while (k < r-1 && queue[k].head == -1)k++;if(queue[k].head!=-1) {//试探下一个队列Array[last].next = queue[k].head;last = queue[k].tail; } }Array[last].next = -1;
}
复制代码
线性时间整理静态链表
template <class Record>
void AddrSort(Record *Array, int n, int first) {int i, j;j = first; // j待处理数据下标Record TempRec;for(i=0;i<n-1;i++) {//循环,每次处理第i个记录TempRec = Array[j]; // 暂存第 i 个的记录 Array[j] swap(Array[i], Array[j]);Array[i].next = j;j = TempRec.next; while (j <= i)j = Array[j].next; }
}
复制代码
基数排序效率
- 时间代价为Θ(d·n), 实际上还是 Θ(nlog n)
- 基数排序中应该是d>=logrm,m为排序码中的最大数。实际测试的时候当n>=10亿时,在64bit系统下基数排序还是有优势的。甚至比introsort还要快一些。只有排序码中元素两两不相同时,才有m=n。
索引排序
在排序时,若是数据很复杂,对数据的移动显然是费时的。若把数据移动改为索引(或指针)移动,则减少了操作复杂度。索引排序,也叫地址排序,就是这种排序思想。
索引含义
根据索引的含义不同,索引排序的算法上也主要分为两种。
- index[i]为array[i]最终在有序序列中的位置。
- index[i]为位置i上最终应存放元素的下标。即最终元素按array[index[0]]、array[index[1]]……有序。
索引排序的适用性
- 一般的排序方法都可以
- 那些赋值(或交换)都换成对index 数组的赋值(或交换)
- 举例:插入排序
插入排序的索引地址排序版本
template<class Record>
void AddrSort(Record Array[], int n) {//n为数组长度
int *IndexArray = new int[n], TempIndex; int i,j,k;Record TempRec;for (i=0; i<n; i++)//只需一个临时空间IndexArray[i] = i; for (i=1; i<n; i++)// 依次插入第i个记录// 依次比较,发现逆置就交换for (j=i; j>0; j--)if ( Array[IndexArray[j]] <Array[IndexArray[j-1]])swap(IndexArray, j, j-1);else break;//此时i前面记录已排序for(i=0;i<n;i++) { // 调整为按下标有序j= i;TempRec = Array[i];while (IndexArray[j] != i) {k=IndexArray[j]; Array[j]=Array[k]; IndexArray[j] = j; j = k;}Array[j] =TempRec; IndexArray[j] = j;}
}
复制代码
总结
- 时间复杂度(试验时间)
- 稳定性
- 对于稳定的排序算法,在排序前将数据随机打乱,就可以达到不稳定的效果了。
- 对于不稳定的排序算法,可以将数据扩充出一个域,用于记录初始下标,当数据关键值相同时,比较下标大小,这样排序算法就是稳定的了。
内排序及时间复杂度分析-插入排序选择排序交换排序归并排序分配和索引排序对比...相关推荐
- js排序数组并返回排序后原数组的索引排序
背景:最近一朋友面试,二面机试题,题目大意是: 从百度天气的一个api取得天气数据,前端展示天气数据,穿衣指数,点击温度栏进行一个升温降温排序,以及不排序 接口地址:http://api.map.ba ...
- 堆排序的python实现及时间复杂度分析
堆是一种形如二叉树的抽象数据结构,要求根节点的元素大于(小于)左右子树的节点,左右子树也需满足上述条件,是一种递归定义. 堆排序的python实现,主要分为两大部分: 1.根据原始数组建立初始堆,只需 ...
- 数据结构中常见的时间复杂度分析题目
数据结构中常见的时间复杂度分析题目 时间复杂度基本概念 例如上面的代码,我们可以表示成T(n) = O(f(n)),而f(n) = 2n + 3.我们使用大O表示法可以写为T(n) = O(n). 分 ...
- python排序算法——归并排序(附代码)
python排序算法 --归并排序 文章目录 python排序算法 --归并排序 一.前言 二.算法描述 三.代码实现 总结 一.前言 相关知识来自<python算法设计与分析>.初级排序 ...
- mysql排序1 10 11_MySQL中的排序
在编写SQL 语句时常常会用到 order by 进行排序,那么排序过程是什么样的?为什么有些排序执行比较快,有些排序执行很慢?又该如何去优化? 索引排序 索引排序指的是在通过索引查询时就完成了排序, ...
- Mysql如何才能走索引排序?
一条SQL到底能不能走索引排序? 实际遇到的场景比较多,总结记录到下表,后面不断补充. 一些结论 1.in查询排序:与范围查询的区别在于,in后面的等值查询依然可以走索引,范围查询不可以.排序行为与范 ...
- 希尔排序选择排序时间复杂度分析
#include <stdio.h> #include <stdlib.h>void shellsort(char array[],int len); void selects ...
- 排序算法-插入排序的时间复杂度分析
插入排序的原理是,将数组分为已排序区间和未排序区间两部分,从未排序区间中依次取元素跟已排序区间的元素一一对比,找到适合插入的位置. 拿数组[4,5,6,1,3,2]来举例,如图所示是排序的图解过程.这 ...
- 直接插入排序 希尔排序 冒泡排序 快速排序 直接选择排序 堆排序 归并排序 基数排序的算法分析和具体实现 ...
排序分为内部排序和外部排序 内部排序是把待排数据元素全部调入内存中进行的排序. 外部排序是因数量太大,把数据元素分批导入内存,排好序后再分批导出到磁盘和磁带外存介质上的排序方法. 比较排序算法优劣的标 ...
最新文章
- Linux进程退出详解(do_exit)--Linux进程的管理与调度(十四)
- iOS 关于.tbd与.dylib用法
- CentOS6.6上进程挂起的诡异问题和处理
- kettle、Oozie、camus、gobblin
- linux系统下用到的小知识点积累
- Leetcode每日一题:1002.find-common-characters(查找常用字符串)
- 十分钟带你理解Kubernetes核心概念
- cocos-2d iphone入门(二) cocos2d源代码生成查询文档
- PHP中“简单工厂模式”实例讲解(转)
- ExtJS应用架构设计(二)
- PHP使用Apache中的ab测试网站的压力性能及mpm介绍
- Kaggle Tabular Playground Series - Jan 2022 学习笔记1(数据分析)
- mysql基础01 创建表 修改表
- tl-wn821n无线网卡驱动 linux,tlwn821n 300m无线网卡驱动 官方最新版
- 详解动态代理及其实现原理
- 狂神Mybatis笔记
- 验证本地或远程网址是否HTTP/2站点
- ptp精准时间协议_精确时间协议PTP研究
- 【富文本】2019年上半年英语四、六级成绩查询时间(附不需要验证码的查询网站)
- Layui 后台ajax 腾讯地图 多点标记mark
热门文章
- 201621123037 《Java程序设计》第10周学习总结
- Python流程控制语句
- Linux下对SVN的相关操作命令
- 关于Window的快捷方式,图标缓存的清理
- 数据结构——队列(C语言实现)
- spring+hibernate的配置
- 瑞友虚拟服务器网页登录,瑞友云端虚拟专网系统
- hdu4284 dfs+floyd
- 【Linux 内核】进程优先级与调度策略 ② ( 获取调度策略对应的进程优先级函数 | sched_get_priority_max 函数 | sched_get_priority_min 函数 )
- 【五线谱】符干朝向与连音线 ( 符干朝向 | 第三线以下符干朝上 | 第三线以上符干朝下 | 连音线 )