插入排序
时间复杂: n2
最好 n
空间复杂: 1
稳定
原理:将待排值依次与前面的有序序列比较并插入。
实现:两个循环,第一个循环从第2位开始往后遍历到末尾,第二个循环从第一个循环的当前位,依次往前遍历比较。若小于则被比较值后移一位,若大于则退出第二个循环,第一个循环当前位的值插入第二个循环中断位。
void insertSort(int* const arr, const int len){
for (int i = 1; i < len; i++)
{
int tmp = arr[i];
int j = i - 1;
for (; j >= 0; j--)
{
if (tmp < arr[j])
{
arr[j + 1] = arr[j];
}else{
break;
}
}
arr[j + 1] = tmp;
}
}
希尔排序
时间复杂度:n1.3到n2,与gap设定挂钩
空间复杂度:1
不稳定
原理:设定一个gap,序列按gap的间隔进行插入排序,即(数列分为len/gap组进行插入排序)。然后依次减少gap的值,直至进行最后一次gap为1的排序(即插入排序)
void shellSort(int* const arr, const int len){
for(int gap = len / 2; gap > 0; gap /= 2){
for (int i = gap; i < len; i++){
int tmp = arr[i];
int j = i - gap;
for(; j >= 0; j -= gap){
if (tmp < arr[j]){
arr[j + gap] = arr[j];
}else{
break;
}
}
arr[j + gap] = tmp;
}
}
}
选择排序
时间复杂度:n2
空间复杂度:1
不稳定(比如数组A[3,4,3,2]。第一次找到最小的2和3换了一下,那第一个3和第二个3位置顺序就变化了)
原理:选择最小或最大数放起始位置
实现:两个循环:第一个循环从0开始,第二个循环从第一个循环位+1开始递增,拿第一个循环位的值跟第二个循环的所有值比较,找到最小值交换
void selectSort(int* const arr, const int len){
for(int i = 0; i < len; i++){
int tmpIdx = i;
for(int j = i + 1; j < len; j++){
if(arr[j] < arr[tmpIdx]){
tmpIdx = j;
}
}
swap(arr+tmpIdx, arr+i);
}
}
堆排序
时间复杂度:nlogn
空间复杂度:1
不稳定
原理:将序列构造为大顶堆,堆顶根节点为最大值,与末尾交换。剩余元素重复这个步骤
实现:第一次完全无序数组要从最后一个非叶子节点往上开始建堆。之后对于交换过最大值与末尾值的树,除根节点外,下面的节点是遵循最大堆规则的的。只需从根节点递归往下建堆即可
void maxHeap(int* const arr, const int i, const int len){
int leftIdx = 2*i + 1;
int rightIdx = 2*i + 2;
int max = i;
if(leftIdx < len && arr[leftIdx] > arr[max])
max = leftIdx;
if(rightIdx < len && arr[rightIdx] > arr[max])
max = rightIdx;
if(max != i){
swap(arr+max, arr+i);
//对被交换的叶子节点及其子节点递归调整为最大堆
maxHeap(arr, max, len);
}
}
void heapSort(int* const arr, const int len){
//从第一个非叶子节点开始往前遍历所有非叶子节点,建堆
for(int i = len/2 - 1; i >= 0; i--){
maxHeap(arr, i, len);
}
for(int i = len - 1; i >= 0; i--){
swap(arr, arr+i);
maxHeap(arr, 0, i);
}
}
冒泡排序
时间复杂度 :n2
空间复杂度:1
稳定
原理:从头开始循环,两两比较是否交换,是最大/小反在最后
void bubbleSort(int* const arr, const int len){
for(int i = len; i > 0; i--){
for (int j = 0; j < i - 1; j++){
if(arr[j] > arr[j+1]){
swap(arr+j, arr+j+1);
}
}
}
}
快速排序
时间复杂度:nlogn
空间复杂度:logn
不稳定
原理:以一个数为基数,小于基数的放基数左边,大于基数的放右边,形成左右两个序列。左右两个序列重复操作
void quickSort(int* const arr, const int begin, const int end){
if(begin >= end)
return;
int mediumNum = arr[begin];
int left = begin;
int right = end;
while(left < right){
while(right > left && arr[right] >= mediumNum)
right--;
while(left < right && arr[left] <= mediumNum)
left++;
if(left != right){
swap(arr+left, arr+right);
}
}
swap(arr+begin, arr+left);
quickSort(arr, begin, left-1);
quickSort(arr, right+1, end);
}
优化:
1.头中尾,选取中间值作key与首元素换位
2.递归到数据量少时,用插入排序,减少递归次数
3.非递归快排:将需要快排区间的头尾索引保存起来,根据快排返回的中间值,计算保存新的头尾索引数据。已快排的头尾索引删掉。根据是否有头尾索引数据判断快排是否结束。
归并排序
时间复杂度:nlogn
空间复杂度:n
稳定
原理:将序列递归对半分,直到无法分割,再从最小数组向上排序
void _mergeSort(int* const arr, const int begin, const int center, const int end){
int left = begin;
int right = center;
int tmpIdx = 0;
int tmp[end - begin + 1];
while(left < center && right <= end)
{
if(arr[left] < arr[right]){
tmp[tmpIdx++] = arr[left++];
}else{
tmp[tmpIdx++] = arr[right++];
}
}
while(left < center){
tmp[tmpIdx++] = arr[left++];
}
while(right <= end){
tmp[tmpIdx++] = arr[right++];
}
for(int i = 0; i < tmpIdx; i++){
arr[begin+i] = tmp[i];
}
}
void mergeSort(int* const arr, const int begin, const int end){
if(begin >= end)
return;
int center = (begin + end) / 2;
mergeSort(arr, begin, center);
mergeSort(arr, center+1, end);
_mergeSort(arr, begin, center+1, end);
}