选择排序
1.直接选择排序
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 算法思想:进行N趟排序,每一趟都从未排序序列中选择一个最大或最小的值放入排序序列中
- 稳定性:不稳定
template<class KeyType>
int min_key(KeyType A[], int low, int high) //从数据集合中选择最小的关键字,并返回数组下标
{
int min_pos = low;
for(int pos=low+1; pos<high; pos++)
{
if (A[pos]<A[min_pos])
{
min_pos = pos;
}
}
return min_pos;
}
template<class KeyType>
void selection_sort(KeyType A[], int size)
{
int position;
for (int i=0; i<size-1; i++) // 最后一个不用选了
{
position = min_key<KeyType>(A, i, size); //从数据集合中寻找最小的数据
if (position!=i)
{
swap<KeyType>(A[i], A[position]); //交换两个数据
}
}
}
2.堆排序
- 时间复杂度:O(N*logN)
- 空间复杂度:O(1)
- 算法思想:建立一个最大堆或最小堆(堆:以二叉树为数据模型,根结点的数据大于子结点的数据叫做大根堆,反之即为小根堆),每次从堆顶取出一个元素放入目标数组中
- 稳定性:不稳定
template<class KeyType>
void insert_heap(KeyType A[], KeyType current, int low, int high) //插入元素到堆中
{
int large = 2*low + 1; //计算左边孩子结点的下标
while(large<high)
{
if (large+1<high && A[large]<A[large+1])//比较两个孩子结点的元素大小
{
large++;
}
if (current<A[large]) //将插入的元素和较大的孩子结点的元素进行比较
{
A[low] = A[large];
low = large;
large = 2*low + 1;
}
else
{
break;
}
}
A[low] = current;
}
template<class KeyType>
void build_heap(KeyType A[], int size) //将一棵完全二叉树建立成大根堆
{
for (int low=size/2-1; low>=0; low--)
{
insert_heap(A, A[low], low, size);
}
}
template<class KeyType>
void heap_sort(KeyType A[], int size)
{
build_heap(A, size);//建立大根堆
int current;
for (int i=size-1; i>0; i--)
{
current = A[i];//叶子结点的值
A[i] = A[0]; //将根结点的值放到叶子结点处
insert_heap(A, current, 0, i);//将叶子结点的值重新插入到堆中
}
}
插入排序
1.直接插入排序
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 算法思想:遍历数组,将当前元素插入到已排序序列中
- 算法特点:适合于大部分数据为有序状态的数组排序
- 稳定性:稳定
template<class KeyType>
void insertion_sort(KeyType A[], int size)
{
int first_unsorted; //第一个待排序数据位置
KeyType current; //第一个待排序数据
int high, low;
for (first_unsorted=1; first_unsorted<size; first_unsorted++)
{
if (A[first_unsorted]<A[first_unsorted-1])
{
/*current = A[first_unsorted];
position = first_unsorted;
do
{
A[position] = A[position-1];
position--;
} while (position>0&¤t<A[position-1]);
A[position] = current;*/
//采用二分插入排序,减少比较次数
current = A[first_unsorted];
low = 0;
high = first_unsorted-1;
while(low<high) //寻找插入位置
{
int mid = low+(high-low)/2;
if (A[mid]<=current)
{
low = mid+1;
}
else
{
high = mid-1;
}
}
//移动元素
for (int i=first_unsorted; i>low; i--)
{
A[i] = A[i-1];
}
A[low] = current;
}
}
}
2.希尔排序
- 时间复杂度:
- 空间复杂度:O(1)
- 算法思想:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高
- 算法关键点:如何选择初始增量,以及如何缩减增量
- 稳定性:稳定
template<class KeyType>
void shell_sort(KeyType A[], int size)
{
int increment = size;
int first_unsorted, position;
KeyType current;
do
{
increment = increment/3 + 1; //计算增量,算法的关键点
for (int i=0; i<increment; i++) //根据增量分割待排序的元素
{
for (first_unsorted=i+increment; first_unsorted<size; first_unsorted+=increment)
{
if (A[first_unsorted]<A[first_unsorted-increment]) //在内部用直接插入排序算法进行排序
{
position = first_unsorted;
current = A[first_unsorted];
do
{
A[position] = A[position-increment];
position -= increment;
} while ((position-increment>=0)&&(current<A[position-increment]));
A[position] = current;
}
}
}
} while (increment>1);
}
交换排序
1.快速排序
- 时间复杂度:平均情况O(NlogN),最差情况(N^2)
- 空间复杂度:O(1)
- 算法思想:选择一个枢纽元,数组中小于枢纽元的数据放一边,大于枢纽元的数据放另外一边。不断递归的分割直至数组大小为1
- 稳定性:不稳定
template<class KeyType>
KeyType median(KeyType A[], int low, int high)
{
int center = low + (high-low)/2;
// A[low] <= A[center] <= A[high]
if (A[center]>A[high]) Swap(A[center], A[high]);
if (A[low]>A[center])
{
Swap(A[low], A[center]);
if (A[center]>A[high]) Swap(A[center], A[high]);
}
Swap(A[center], A[high-1]);
return A[high-1];
}
template<class KeyType>
int partion(KeyType A[], int low, int high)
{
KeyType pivot = median(A, low, high); // 三数中值法取枢纽元
int left = low;
int right = high-1;
while (true)
{
while(A[++left]<pivot); // 从左往右找大于pivot的元素
while(A[--right]>pivot); // 从右往左找小于pivot的元素
if (left<right)
Swap(A[left], A[right]);
else
break;
}
Swap(A[left], A[high-1]);
return left;
}
template<class KeyType>
void recursive_quick_sort(KeyType A[], int low, int high)
{
while (low+1<high)
{
int p = partion(A, low, high);
recursive_quick_sort<KeyType>(A, low, p-1);
low = p+1;
}
}
template<class KeyType>
void quick_sort(KeyType A[], int size)
{
recursive_quick_sort<KeyType>(A, 0, size-1);
insertion_sort(A, size);
}
2.冒泡排序
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 算法思想:进行N趟排序,每一趟从底部将最大的元素交换到顶部
- 稳定性:稳定
template<class KeyType>
void bubble_sort(KeyType A[], int size)
{
bool flag = true; //标记每一趟是否交换过,决定是否提前结束循环
for (int i=1; i<size&&flag; i++)
{
flag = false; //每一趟的开始,设置交换标记为false
for (int j=0; j<size-i; j++)
{
if (A[j]>A[j+1])
{
swap<KeyType>(A[j], A[j+1]);
flag = true; //设置交换标记为true
}
}
}
}
分配排序
1.基数排序
- 时间复杂度:O(MN)(M为最大位数)
- 空间复杂度:O(N)
- 算法思想:直接看代码
- 算法局限性:1.数据只能是非负的整数;2.得知道数据的最大位数(十进制)
- 稳定性:稳定
int maxBit(int A[], int size)//用来求数组中元素的最大位数
{
int bit = 1;
int num = 10;
for (int i=0; i<size; ++i)
{
while(A[i]/num>0)
{
++bit;
num *= 10;
}
}
return bit;
}
void radix_sort(int A[], int size)
{
int bit = maxBit(A, size);//计算数组中元素的最大位数
int count[10];//计数器
int * temp = new int[size];//辅助数组
int i, j, k;
int radix = 1;
for (i=1; i<=bit; i++)//进行bit趟排序
{
for(j=0; j<10; ++j)
{
count[j] = 0;
}
for (j=0; j<size; ++j) //统计每个桶中的记录数
{
k = A[j] / radix % 10; //计算所在位上的数字
++count[k];
}
for (j=1; j<10; ++j)
{
count[j] += count[j-1];
}
for (j=size-1; j>=0; --j)// 将A中的数据存到temp中,从后往前,保持稳定性
{
k = A[j] / radix % 10; //计算所在位上的数字
--count[k];
temp[count[k]] = A[j];
}
for (j=0; j<size; ++j)//将temp中的数据复制到A中
{
A[j] = temp[j];
}
radix *= 10;
}
delete [] temp;
}
2.位图排序
- 时间复杂度:O(N)
- 空间复杂度:O(MaxValue/32)
- 算法思想:将每个数据映射到某个数组元素的某一位
- 算法局限性:1.数据只能是非负的整数;2.数据不能重复;3.须知道最大的数据
- 算法特点:在数据集合满足条件的情况下,适合查询数据有没有存在
- 稳定性:数据没有重复,自然是稳定的
//sort the array by bitmap, it demand all elements of src can't be repeated.
const int WORD = 32;
const int SHIFT = 5; //left shift 5 bits equal to mutiply 32
const int MASK = 0x1f;//31,M%N: if N%2=0, M%N = M&(N-1)
const int MAX_VALUE = 10000000; //The maximum value of all elements of src.
int bitmap[MAX_VALUE/WORD+1]; //The array of bitmap.
// set the bit
// i right shift 5 bit to calculate the index
// 1<<(i&0x1f) to calculate the bit
void set(int i)
{
bitmap[i>>SHIFT] |= (1<<(i&MASK));
}
//clear the bit
void clear(int i)
{
bitmap[i>>SHIFT] &= ~(1<<(i&MASK));
}
//return the result of the bit
int test(int i)
{
return (bitmap[i>>SHIFT] & (1<<(i&MASK)));
}
void bitmap_sort(int src[], int size)
{
int i;
for (i=0; i<MAX_VALUE; ++i)
{
clear(i);
}
for (i=0; i<size; ++i)
{
set(src[i]);
}
int j=0;
for (i=0; i<MAX_VALUE; ++i)
{
if (test(i))
{
src[j++] = i;
}
}
}
3.计数排序
- 时间复杂度:O(N)
- 空间复杂度:O(MaxValue)
- 算法思想:将每个数据映射到某个数组元素的某一位
- 算法局限性:数据只能是整数
- 算法特点:在数据集合满足条件的情况下,适合数据集合跨度(Max-Min)不是很大的情况
- 稳定性:稳定
void count_sort(int A[], size_t size)
{
if (size>0)
{
int max, min;
max = min = A[0];
size_t i;
for(i=1; i<size; ++i)
{
if (A[i]>max)
max = A[i];
else if (A[i]<min)
min = A[i];
}
max = max - min + 1;
size_t* Count = new size_t[max]; // min as offset value
memset(Count, 0, max*sizeof(size_t)); // initialize
for (i=0; i<size; ++i) // count
++Count[A[i]-min];
size_t pos = -1; // index of A[]
// sort
for (i=0; i<max; ++i)
{
while (Count[i]>0)
{
A[++pos] = i + min;
--Count[i];
}
}
delete [] Count;
}
}
归并排序
1.归并排序
- 时间复杂度:O(N*logN)
- 空间复杂度:O(N)
- 算法思想:分而治之。分:把数组想像成二叉数的根结点,先将该根结点向下逐层分裂成叶结点只含一个元素,这颗树的高度即为logN;治:再从叶结点向上逐层合并,并且在合并的过程中同时排序
- 稳定性:稳定
template<class KeyType>
void two_merge(KeyType dest[], KeyType src[], int low, int m, int high) //归并两个有序序列
{
int i = low;
int j = m;
while(i<m&&j<high) //两个序列中都存在未归并元素
{
if (src[i]<=src[j])
{
dest[low++] = src[i++];
}
else
{
dest[low++] = src[j++];
}
}
while(i<m) //第一个有序序列中存在未归并元素
{
dest[low++] = src[i++];
}
while(j<high) //第二个有序序列中存在未归并元素
{
dest[low++] = src[j++];
}
}
template<class KeyType>
void merge_pass(KeyType dest[], KeyType src[], int len, int size)
{
int p = 0; //p指向每一对待归并的第一个元素下标
while (p+2*len<size)//两两归并长度均为len的有序子序列
{
two_merge<KeyType>(dest, src, p, p+len, p+2*len);
p += 2*len;
}
if (p+len<size) //归并最后两个长度不等的有序子序列
{
two_merge<KeyType>(dest, src, p, p+len, size);
}
else
{
for (int i=p; i<size; i++)
{
dest[i] = src[i]; //把剩下的最后一个有序子序列复制到swap中
}
}
}
template<class KeyType>
void merge_sort(KeyType A[], int size)
{
KeyType* temp = new KeyType[size];//构造一个临时数组
int len = 1; //有序序列的长度
while(len<size)
{
merge_pass<KeyType>(temp, A, len, size);
len = len*2;
merge_pass<KeyType>(A, temp, len, size);
len = len*2;
}
delete [] temp;
}