排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
希尔排序 | O(nlogn)~O(n^2) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O(n^2) | O(nlogn)~O(n) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
冒泡排序
template<typename T>
void bubbleSort( T arr[] , int n){
bool swapped;
//int newn; // 理论上,可以使用newn进行优化,但实际优化效果较差
do{
swapped = false;
//newn = 0;
for( int i = 1 ; i < n ; i ++ )
if( arr[i-1] > arr[i] ){
swap( arr[i-1] , arr[i] );
swapped = true;
// 可以记录最后一次的交换位置,在此之后的元素在下一轮扫描中均不考虑
// 实际优化效果较差,因为引入了newn这个新的变量
//newn = n;
}
//n = newn;
// 优化,每一趟Bubble Sort都将最大的元素放在了最后的位置
// 所以下一次排序,最后的元素可以不再考虑
// 理论上,newn的优化是这个优化的复杂版本,应该更有效
// 实测,使用这种简单优化,时间性能更好
n --;
}while(swapped);
}
选择排序
template<typename T>
void selectionSort(T arr[], int n){
for(int i = 0 ; i < n ; i ++){
int minIndex = i;
for( int j = i + 1 ; j < n ; j ++ )
if( arr[j] < arr[minIndex] )
minIndex = j;
swap( arr[i] , arr[minIndex] );
}
}
插入排序
template<typename T>
void insertionSort(T arr[], int n){
for( int i = 1 ; i < n ; i ++ ) {
// 寻找元素arr[i]合适的插入位置
// 写法1
// for( int j = i ; j > 0 ; j-- )
// if( arr[j] < arr[j-1] )
// swap( arr[j] , arr[j-1] );
// else
// break;
// 写法2
// for( int j = i ; j > 0 && arr[j] < arr[j-1] ; j -- )
// swap( arr[j] , arr[j-1] );
// 写法3
T e = arr[i];
int j; // j保存元素e应该插入的位置
for (j = i; j > 0 && arr[j-1] > e; j--)
arr[j] = arr[j-1];
arr[j] = e;
}
return;
}
希尔排序
template<typename T>
void shellSort(T arr[], int n){
int h = 1;
while( h < n/3 )
h = 3 * h + 1;
// 计算 increment sequence: 1, 4, 13, 40, 121, 364, 1093...
while( h >= 1 ){
// h-sort the array
for( int i = h ; i < n ; i ++ ){
// 对 arr[i], arr[i-h], arr[i-2*h], arr[i-3*h]... 使用插入排序
T e = arr[i];
int j;
for( j = i ; j >= h && e < arr[j-h] ; j -= h )
arr[j] = arr[j-h];
arr[j] = e;
}
h /= 3;
}
}
归并排序
// 将arr[l...mid]和arr[mid+1...r]两部分进行归并
template<typename T>
void __merge(T arr[], int l, int mid, int r){
// 经测试,传递aux数组的性能效果并不好
T aux[r-l+1];
for( int i = l ; i <= r; i ++ )
aux[i-l] = arr[i];
int i = l, j = mid+1;
for( int k = l ; k <= r; k ++ ){
if( i > mid ) { arr[k] = aux[j-l]; j ++;}
else if( j > r ){ arr[k] = aux[i-l]; i ++;}
else if( aux[i-l] < aux[j-l] ){ arr[k] = aux[i-l]; i ++;}
else { arr[k] = aux[j-l]; j ++;}
}
}
// 递归使用归并排序,对arr[l...r]的范围进行排序
template<typename T>
void __mergeSort(T arr[], int l, int r){
if( l >= r )
return;
int mid = (l+r)/2;
__mergeSort(arr, l, mid);
__mergeSort(arr, mid+1, r);
__merge(arr, l, mid, r);
}
template<typename T>
void mergeSort(T arr[], int n){
__mergeSort( arr , 0 , n-1 );
}
// 对arr[l...r]范围的数组进行插入排序
template<typename T>
void insertionSort(T arr[], int l, int r){
for( int i = l+1 ; i <= r ; i ++ ) {
T e = arr[i];
int j;
for (j = i; j > l && arr[j-1] > e; j--)
arr[j] = arr[j-1];
arr[j] = e;
}
return;
}
// 递归使用归并排序,对arr[l...r]的范围进行排序
template<typename T>
void __mergeSort2(T arr[], int l, int r){
// 对于小规模数组,使用插入排序
if( r - l <= 15 ){
insertionSort(arr, l, r);
return;
}
int mid = (l+r)/2;
__mergeSort2(arr, l, mid);
__mergeSort2(arr, mid+1, r);
// 对于arr[mid] <= arr[mid+1]的情况,不进行merge
// 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失
if( arr[mid] > arr[mid+1] )
__merge(arr, l, mid, r);
}
template<typename T>
void mergeSort2(T arr[], int n){
__mergeSort2( arr , 0 , n-1 );
}
//自底向上的归并
template <typename T>
void mergeSortBU(T arr[], int n){
// for( int sz = 1; sz <= n ; sz += sz )
// for( int i = 0 ; i < n ; i += sz+sz )
// // 对 arr[i...i+sz-1] 和 arr[i+sz...i+2*sz-1] 进行归并
// __merge(arr, i, i+sz-1, min(i+sz+sz-1,n-1) );
// Merge Sort Bottom Up 优化
for( int i = 0 ; i < n ; i += 16 )
insertionSort(arr,i,min(i+15,n-1));
for( int sz = 16; sz <= n ; sz += sz )
for( int i = 0 ; i < n - sz ; i += sz+sz )
if( arr[i+sz-1] > arr[i+sz] )
__merge(arr, i, i+sz-1, min(i+sz+sz-1,n-1) );
}
快速排序
// 对arr[l...r]部分进行partition操作
// 返回p,使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
template <typename T>
int __partition(T arr[], int l, int r){
swap( arr[l] , arr[rand()%(r-l+1)+l] );
T v = arr[l];
int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
for( int i = l + 1 ; i <= r ; i ++ )
if( arr[i] < v ){
j ++;
swap( arr[j] , arr[i] );
}
swap( arr[l] , arr[j]);
return j;
}
// 对arr[l...r]部分进行快速排序
template <typename T>
void __quickSort(T arr[], int l, int r){
// if( l >= r )
// return;
if( r - l <= 15 ){
insertionSort(arr,l,r);
return;
}
int p = __partition(arr, l, r);
__quickSort(arr, l, p-1 );
__quickSort(arr, p+1, r);
}
template <typename T>
void quickSort(T arr[], int n){
srand(time(NULL));
__quickSort(arr, 0, n-1);
}
双路快速排序法
template <typename T>
int _partition2(T arr[], int l, int r){
swap( arr[l] , arr[rand()%(r-l+1)+l] );
T v = arr[l];
// arr[l+1...i) <= v; arr(j...r] >= v
int i = l+1, j = r;
while( true ){
while( i <= r && arr[i] < v )
i ++;
while( j >= l+1 && arr[j] > v )
j --;
if( i > j )
break;
swap( arr[i] , arr[j] );
i ++;
j --;
}
swap( arr[l] , arr[j]);
return j;
}
三路快速排序法
template <typename T>
void __quickSort3Ways(T arr[], int l, int r){
if( r - l <= 15 ){
insertionSort(arr,l,r);
return;
}
swap( arr[l], arr[rand()%(r-l+1)+l ] );
T v = arr[l];
int lt = l; // arr[l+1...lt] < v
int gt = r + 1; // arr[gt...r] > v
int i = l+1; // arr[lt+1...i) == v
while( i < gt ){
if( arr[i] < v ){
swap( arr[i], arr[lt+1]);
i ++;
lt ++;
}
else if( arr[i] > v ){
swap( arr[i], arr[gt-1]);
gt --;
}
else{ // arr[i] == v
i ++;
}
}
swap( arr[l] , arr[lt] );
__quickSort3Ways(arr, l, lt-1);
__quickSort3Ways(arr, gt, r);
}
template <typename T>
void quickSort3Ways(T arr[], int n){
srand(time(NULL));
__quickSort3Ways( arr, 0, n-1);
}
优先队列
出队取出优先级最高的元素 入出队O(nlogn)
二叉堆 Binary Heap
完全二叉树:不大于父节点 最大堆:节点最后在左边
用数组存储二叉堆
Shift Up、Shift Down
#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
using namespace std;
template<typename Item>
class MaxHeap{
private:
Item *data;
int count;
int capacity;
void shiftUp(int k){
while( k > 1 && data[k/2] < data[k] ){
swap( data[k/2], data[k] );
k /= 2;
}
}
void shiftDown(int k){
while( 2*k <= count ){
int j = 2*k; // 在此轮循环中,data[k]和data[j]交换位置
if( j+1 <= count && data[j+1] > data[j] )
j ++;
// data[j] 是 data[2*k]和data[2*k+1]中的最大值
if( data[k] >= data[j] ) break;
swap( data[k] , data[j] );
k = j;
}
}
public:
MaxHeap(int capacity){
data = new Item[capacity+1];
count = 0;
this->capacity = capacity;
}
~MaxHeap(){
delete[] data;
}
int size(){
return count;
}
bool isEmpty(){
return count == 0;
}
void insert(Item item){
assert( count + 1 <= capacity );
data[count+1] = item;
shiftUp(count+1);
count ++;
}
Item extractMax(){
assert( count > 0 );
Item ret = data[1];
swap( data[1] , data[count] );
count --;
shiftDown(1);
return ret;
}
Item getMax(){
assert( count > 0 );
return data[1];
}
};
int main() {
MaxHeap<int> maxheap = MaxHeap<int>(100);
srand(time(NULL));
for( int i = 0 ; i < 63 ; i ++ ){
maxheap.insert( rand()%100 );
}
while( !maxheap.isEmpty() )
cout<<maxheap.extractMax()<<" ";
cout<<endl;
return 0;
}
Heapify
MaxHeap(Item arr[], int n){
data = new Item[n+1];
capacity = n;
for( int i = 0 ; i < n ; i ++ )
data[i+1] = arr[i];
count = n;
for( int i = count/2 ; i >= 1 ; i -- )
shiftDown(i);
}
原地堆排序
首尾swap,然后Shift Down 从数组0节点开始
template<typename T>
void __shiftDown(T arr[], int n, int k){
while( 2*k+1 < n ){
int j = 2*k+1;
if( j+1 < n && arr[j+1] > arr[j] )
j += 1;
if( arr[k] >= arr[j] )break;
swap( arr[k] , arr[j] );
k = j;
}
}
template<typename T>
void __shiftDown2(T arr[], int n, int k){
T e = arr[k];
while( 2*k+1 < n ){
int j = 2*k+1;
if( j+1 < n && arr[j+1] > arr[j] )
j += 1;
if( e >= arr[j] ) break;
arr[k] = arr[j];
k = j;
}
arr[k] = e;
}
template<typename T>
void heapSort(T arr[], int n){
for( int i = (n-1)/2 ; i >= 0 ; i -- )
__shiftDown2(arr, n, i);
for( int i = n-1; i > 0 ; i-- ){
swap( arr[0] , arr[i] );
__shiftDown2(arr, i, 0);
}
}
测试函数
生成随机数
// 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR]
int *generateRandomArray(int n, int rangeL, int rangeR) {
assert(rangeL <= rangeR);
int *arr = new int[n];
srand(time(NULL));
for (int i = 0; i < n; i++)
arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;
return arr;
}
//生成基本有序数组
int *generateNearlyOrderedArray(int n, int swapTimes){
int *arr = new int[n];
for(int i = 0 ; i < n ; i ++ )
arr[i] = i;
srand(time(NULL));
for( int i = 0 ; i < swapTimes ; i ++ ){
int posx = rand()%n;
int posy = rand()%n;
swap( arr[posx] , arr[posy] );
}
return arr;
}
测试算法性能
// 排序是否正确
template<typename T>
bool isSorted(T arr[], int n) {
for (int i = 0; i < n - 1; i++)
if (arr[i] > arr[i + 1])
return false;
return true;
}
// 排序消耗时间
template<typename T>
void testSort(const string &sortName, void (*sort)(T[], int), T arr[], int n) {
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
assert(isSorted(arr, n));
cout << sortName << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;
return;
}