一. 时空复杂度
转自:http://blog.chinaunix.net/uid-21457204-id-3060260.html
二、实现代码
http://blog.csdn.net/touch_2011/article/details/6767673 漫谈经典排序算法 对《数据结构》上的算法总结得很好,主要对着实现了一遍。
与书一致,为了叙述方便,以下源代码中均不考虑A[0],默认下标从1开始。
//-------------插播一段交换int a, b两个数据值的方法------------------------------------
方法一:使用异或^操作符。
void swap(int &a, int &b)
{
a = a^b;
b = a^b;
a = a^b;
}
方法二:使用加法
a = a+b; // 可能溢出,方法一则不会,且位操作要比加减法 操作要快些
b = a - b;
a = a -b;
//-------------end--------------------------------------
(1) 插入排序、希尔 排序 --- 基于插入思想
// 直接插入排序 // 最好 O(n), 最差 O(n^2), 平均 O(n^2), 稳定 void Sort::insertSort(int* a, int n) { if(a == NULL || n < 1) return; for(int i = 2; i <= n; i++) { a[0] = a[i]; // 哨兵 int j; for(j = i - 1; j > 0; j--) { if(a[0] < a[j]) // move back { a[j+1] = a[j]; } else break; } a[j+1] = a[0]; } } int Sort::binarySearch(int *a, int start, int end, int value) { if(start > end) // 没找到 return start; int mid = (start + end) / 2; if(a[mid] == value) return mid; else if( a[mid] < value) return binarySearch(a, mid + 1, end, value); else return binarySearch(a, start, mid -1, value); } // 二分查找插入排序 // 最好 O(nlogn), 最差 O(n^2), 平均 O(n^2), 非稳定 // 只在数据量较大,且 比较无序的 情况下 优于 直接插入排序 void Sort::binaryInsertSort(int *a, int n) { if(a == NULL || n < 1) return; int pos; for(int i = 2; i <= n; i++) { a[0] = a[i]; pos = binarySearch(a, 1, i - 1, a[0]); for(int j = i - 1; j >= pos; j--) a[j+1] = a[j]; a[pos] = a[0]; } } // 表插入排序 // 最好 O(n), 最差 O(n^2), 平均 O(n^2), 稳定 // 只是用改变存储结构,用改变连接关系代替实际移动,比较次数 // 没有变化; 且无法随机访问,比较无用。(用双向链表, 循环数组会好一点) void Sort::tableInsertSort(PNode list, int n) { int p, ahead; // head 插入位置的前一个结点 int i; // 初始化 list[0].next = 1; list[1].next = 0; // 逐个插入 for(i = 2; i <= n; i++) { ahead = 0; p = list[0].next; // 从表头遍历 // 遍历链表,寻找插入位置 while(p != 0 && list[p].key <= list[i].key) { ahead = p; p = list[p].next; } list[ahead].next = i; list[i].next = p; } arrange(list, n); } // 将表中元素按增序重排到a[1,n] void Sort::arrange(PNode list, int n) { int p = list[0].next; int q; Node tmp; for(int i = 1; i < n; i++) { while(p < i) p = list[p].next; // [1, i-1] 有序,得到原来在[1, i-1]间元素的新位置 q = list[p].next; if(p != i) // 交换到当前位置i { tmp.key = list[p].key; tmp.next = list[p].next; list[p] = list[i]; list[i] = tmp; list[i].next = p; // 指回到原来的位置p } p = q; // 下一个 元素i+1的位置 } cout << "tableInsertSort" << endl; for(int i = 1; i <= n; i++) { cout << list[i].key << " "; } cout << endl; } // 希尔排序 // 与增量递减序列的取值有关, 基本 O(n^2), 不稳定 void Sort::shellSort(int* a, int n) { int shellTable[] = {5, 3, 1};// 增量递减序列(序列中的值互质),末尾元素为1 for(int i = 0; i < sizeof(shellTable) / sizeof(int); i++) { shellInsertSort(a, n, shellTable[i]); } } // 希尔排序中所采用的插入排序,元素间隔为dk void Sort::shellInsertSort(int* a, int n, int dk) { if(dk >n) throw std::exception("invalid input"); for(int t = 1; t <= dk; t++) { for(int i = dk + t; i <= n; i+= dk) { a[0] = a[i]; // 哨兵暂存 int j; for(j = i - dk; j > 0; j -= dk) { if(a[0] < a[j]) a[j + dk] = a[j]; else break; } a[j+dk] = a[0]; } } }
(2) 简单选择排序,堆排序--- 基于选择思想
// 简单选择排序 // 最好、最差、平均 都是 O(n^2) 稳定 void Sort::selectSort(int* a, int n) { int index; for(int i = 1; i < n; i++) // 进行n-1次,每次选出最小的 { index = i; int j; for(j = i + 1; j <= n; j++) { if(a[index] > a[j]) { index = j; // 记录最小的下标为index } } if(index != i) // 交换 { swap(a[index], a[i]); } } } // 递归选择排序 // 最好、最差、平均 都是 O(n^2) 稳定 void Sort::recursionSelectSort(int* a, int n) { if(n == 1) // 退出 return ; // 1. 选择最小的元素 int index = 1; for(int i = 1; i <= n; i++) { if(a[index] > a[i]) index = i; } // 2. 交换 1 和index if(index != 1) { swap(a[index], a[1]); } // 3. 子序列[a+i, a+n]递归 recursionSelectSort(a + 1, n - 1); } // 堆调整 a[i, n]为除 i 外,其他符合堆性质的序列,本质上是使用堆结构,逐次从中选择最大的元素,是选择排序 // 最好、最差、平均 都是 O(nlog(n)) 不稳定 void Sort::heapAdjust(int* a, int i, int n) // 调整堆,保持大顶堆的性质,参数i指向根结点 { int tmp = a[i]; int left, right, largest; // 记录 根左孩子右孩子 这三者中的最大者index largest = left = i * 2; if(left > n) return; right = left + 1; if(right <= n && a[right] > a[left]) largest = right; if(a[largest] > a[i]) // 根结点小于子结点的最大值,需交换根与最大子结点 {
swap(a[largest], a[i]); heapAdjust(a, largest, n); // 调整子堆 } } // 创建堆, 即对[n/2, n]进行调整,得到大顶堆 void Sort::createHeap(int* a, int n) { // n/2表示,由n个元素构建的完全二叉树,最后一个非叶子结点的下标为n/2(取下整) // 若取大于 n/2的结点,都是叶子结点不用adjust for(int i = n/2; i >= 1; i--) { heapAdjust(a, i, n); } } // 堆排序 void Sort::heapSort(int* a, int n) { createHeap(a, n); // 构建大顶堆 // 排序,每次选最大的数 a[1](大顶堆的根结点)交换放到序列最后一个位置上 for(int i = n; i >= 2; i--) // 元素数量 { // 堆顶元素 和 最后一个位置 交换 swap(a[1], a[i]); heapAdjust(a, 1, i - 1); } }
(3) 冒泡排序, 快速排序--- 基于交换思想
// 冒泡排序 // 最好、最差、平均 都是 O(n^2) 稳定 void Sort::bubbleSort(int* a, int n) { for(int i = 1; i < n; i++) for(int j = 1; j < n - i + 1; j++) { if(a[j + 1] < a[j]) // swap {
swap(a[j+1], a[j]); }
// 快速排序 不稳定 // 最好(递归树,每次划分都对称)的复杂O(nlog(n)), 最坏时(每次划分都不对称,如顺序和逆序时,退化为冒泡)O(n^2), 平均O(nlogn) void Sort::quickSort(int* a, int low, int high) { if(low < high) { int position = partition(a, low, high); quickSort(a, low , position - 1); quickSort(a, position + 1, high); } } int Sort::partition(int* a, int low, int high) { int pivotkey = a[low]; // 枢轴 // 注意:low < high 的条件很重要 while(low < high) { while(low < high && a[high] >= pivotkey) high--; a[low] = a[high]; // low=high 或 a[high] < pivotkey 时,将小于枢轴值放于低位 while(low < high && a[low] <= pivotkey) low++; a[high] = a[low]; // 放于高位 } // 结束时,有 low = high 为枢轴的下标 a[low] = pivotkey; // 赋值 枢轴 return low; // 返回枢轴下标 } // 非递归版 void Sort::quickSortStack(int* a, int low, int high) { if(low < high) { std::stack<int> callStackLow;
std::stack<int> callStackHigh;
callStackLow.push(low);
callStackHigh.push(high);
while(!callStackLow.empty()) { int curLow = callStackLow.top(); int curHigh = callStackHigh.top(); callStack.pop(); // 求 partition if(curLow < curHigh) { int position = partition(a, curLow, curHigh); callStackLow.push(curLow);
callStackHigh.push(position - 1);
callStackLow.push(position+1); // 二者无顺序性 callStackHigh.push(j); } } } }
(4) 归并排序
//----------------------基于分制思想---------------------------------------- // 归并排序 递归,先自顶向下分解,再由递归退栈,自底向上合并 // 最好、最坏、平均 复杂度 都为 O(nlog(n)) (递归树) void Sort::mergeSort(int* a, int start, int end) { if(start < end) { int mid = (start + end) / 2 ; // 分成两部分 // 先分解 mergeSort(a, start, mid); mergeSort(a, mid + 1, end); // 再合并 merge(a, start, mid, end); } } //将有序的src[start, mid] 和 src[mid+1,..n] 归并为有序的 src[start, end] void Sort::merge(int* a, int start, int mid, int end) { int i, j, k; // 申请辅助数组 int length1 = mid - start + 1; int length2 = end - mid; int* array1 = new int[length1]; // int* array2 = new int[length2]; // // 把内容copy到辅助数组中 std::memcpy(array1, a + start, sizeof(int) * length1); std::memcpy(array2, a + mid + 1, sizeof(int) * length2); // 归并 for(i = 0, j = 0, k = start; i < length1 && j < length2;) { if(array1[i] <= array2[j]) a[k++] = array1[i++]; else a[k++] = array2[j++]; } if(i < length1) std::memcpy(a + k, array1 + i, sizeof(int) * (length1 - i)); // 复制剩余的src[start, mid] 到 tr if(j < length2) std::memcpy(a + k, array2 + j, sizeof(int) * (length2 - j)); // 复制剩余的src[mid+1, end] 到 tr delete[] array1; delete[] array2; } // 归并排序 非递归 ---直接自底向上合并 void Sort::mergeSortNoRecusion(int* a ,int n) { int step = 1, i; // 要合并的组长度为step while(step < n) { i = 1; while(i + 2 * step - 1 <= n) // 查看是否可以合并 a[i,i+step-1] , a[i+step, i+2*step-1] { merge(a, i, i + step - 1, i + 2 * step -1); i = i + 2 * step; // 下一组 } // 处理尾部剩余部分 if(i < n) { merge(a, i, i + step/2 -1, n); //有序数列 a[i, i+step/2-1], a[i+step/2, n] 合并 } step *= 2; // 2倍增长 } }
(5) 计数排序、基数排序、桶排序
/------------------------线性时间排序-------------------------------------- // 计数排序,要求a中的元素都为非负整数 // 最快、最差、平均 时间复杂度 o(n+k), n为元素个数,k为a中的最大值。需辅助空间 n+k 稳定 void Sort::countingSort(int* a, int n) { if(a == NULL || n < 1) return; // 找到最大的元素 int maxValue = a[1]; for(int i = 2; i <= n; i++) { if(maxValue < a[i]) maxValue = a[i]; } // 初始化计数数组 count int* count = new int[maxValue + 1]; memset(count, 0, (maxValue + 1) * sizeof(int)); // 计算等于 a[i] 的记录个数 for(int i = 1; i <= n; i++) { count[a[i]]++; } // 计算 小于等于 a[i] 的记录个数,确定其顺序 for(int i = 1; i <= maxValue; i++) { count[i] += count[i-1]; } // 从后往前扫描a数组(为了保持稳定性),把各个元素放在临时数组对应的位置上 int* tmp = new int[n+1]; for(int i = n; i > 0; i--) { tmp[count[a[i]]] = a[i]; count[a[i]]--; } // 把有序数组的元素拷贝到a中 memcpy(a, tmp, (n+1) * sizeof(int)); delete[] count; delete[] tmp; } // 简单整数基数排序, 每一位都用一次计数排序 // 最坏、最好、平均都是 O(n), 稳定排序, 辅助空间k+n void Sort::radixSort(int* a, int n) { int i; int maxValue = a[1]; // 统计最大数 for(i = 2; i <= n; i++) { if(maxValue < a[i]) maxValue = a[i]; // 最大值 } int d = 0; //统计最大数的位数 if(maxValue > 0) d = 1; while(maxValue / 10 != 0) { d++; maxValue/=10; } for(int i = 1; i <= d; i++) countingSort(a, n, 9, i); // 从最低位开始排序 } // 计数排序, n为数组a的记录个数,radix为记录中最大值,按第d为排序 void Sort::countingSort(int* a, int n, int radix ,int d) // radix表示基数的取值最大值,十进制默认为9. [0,9] { int* count = new int[radix + 1]; int* b = new int[n + 1]; memset(count, 0, (radix + 1) * sizeof(int)); // a[i] 在 d 位 的值为 a[i] / (int)(pow(10, d-1) % 10, 在count中记录其出现的个数 for(int i = 1; i <= n; i++) { int v = a[i]/(int)(std::pow(10.0, d-1)) % 10; count[v]++; } // 统计 小于等于 该值的个数 for(int i = 1; i <= radix; i++) { count[i] += count[i-1]; } // 从后往前 扫描数组,把各原始放在有序序列中相应的位置上 for(int i = n; i > 0; i--) { int v = a[i]/(int)std::pow(10.0, d-1) % 10; b[count[v]] = a[i]; count[v]--; } // 排序结果 拷回 a中 memcpy(a, b, (n+1) * sizeof(int)); delete[] count; delete[] b; } // 桶排序,感觉和基数排序挺相似,不过基数排序的radix为每位的取值范围,有多位, // 而桶则是直接根据取值范围进行等间隔划分 // 复杂度为O(n + k), k为桶的个数 最好情况(数均匀分布于各桶中) O(n), 最坏情况O(n^2), void Sort::bucketSort(int* a, int n, int k) { // 建立链表 (也可以用 k * n 的二维数组,但比较耗费空间) // 链表结点描述 typedef struct Node { double key; struct Node* next; }; // 辅助数组元素描述 typedef struct Head { Node* next; }; int i, j; Node* p; Node* q; Node* node; // 求出最大、最小值,对桶范围进行划分 int maxValue = a[1]; int minValue = a[1]; for(int i = 2; i <= n; i++) { if(maxValue < a[i]) maxValue = a[i]; if(minValue > a[i]) minValue = a[i]; } Head* heads = new Head[k]; for(int i = 0; i < k; i++) heads[i].next = NULL; for(int i = 1; i <= n; i++) { node = new Node(); node->key = a[i]; node->next = NULL; // 判断a[i]该进入哪个桶 int index; if(a[i] == maxValue) index = k - 1; else index = (double)(a[i] - minValue) / (maxValue - minValue) * k; p = q = heads[index].next; if(p == NULL) // 该桶中尚未有元素 { heads[index].next = node; continue; } else { // 对已有元素进行排序(也可以全部分配完在排序) // 这里直接使用进行插入排序 while(p) { if(a[i] < p->key) break; q = p; // 记录前一个结点 p = p->next; } if(p == NULL) q->next = node; // 插到尾部 else { node->next = p; q->next = node; } } } int t = 1; // 将排序后的结果拷到a中 for(int i = 0; i < k; i++) { p = heads[i].next; while(p) { a[t++] = p->key; q = p; p = p->next; delete q; // 删除链表 } } delete[] heads; }
鸽巢原理及应用