直接插入排序
- 思路
在待排列的元素中,在前n-1项有序序列中,插入第n项元素,使n项元素有序,这样依次插入直至序列有序。
由于不能确定有序部分,所以从第一个元素开始,视第一个元素为有序,将之后的元素依次插入。
- 代码
//直接插入排序(升序)
void InsertSort(int* arr, int n)
{
for (int i = 1; i < n; i++)
{
int end = i - 1;
int tmp = arr[i];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + 1] = arr[end];
--end;
}
else
{
break;
}
}
arr[end + 1] = tmp;
}
}
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:稳定
希尔排序
希尔排序是对直接插入排序的优化,先进行预排序,将序列接近有序,再进行直接插入排序,使序列有序。
- 思路
选定小于n的整数gap,将待排序列分为gap组,所有间隔为gap的元素为一组,每组进行直接插入排序,然后选定小于上一次gap的整数作为gap循环操作;当gap=1时,相当于整个待排序列为一组,进行依次直接插入排序后有序。
代码
//希尔排序(升序)
void ShellSort(int* arr, int n)
{
//设置组
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
//共gap组,循环gap组 //每组循环n / gap - 1次,间隔为gap的数据为一组,每次循环插入一个数据
for (int i = 0; i < n - gap; i++)
{
int end = i;
//保存新插入的数据
int tmp = arr[i + gap];
//和组内数据比较
while (end >= 0)
{
//如果新插入的数据小于上一个数据
if (tmp < arr[end])
{
//将上一个数据向后挪动
arr[end + gap] = arr[end];
//再次对比上上个数据
end -= gap;
}
else
break;
}
//写入新插入的数据
arr[end + gap] = tmp;
}
}
}
时间复杂度:O(N^1.3),特殊序列 O(N^2)
空间复杂度:O(1)
稳定性:不稳定
选择排序
- 思路
每一次从待排序的数据元素中选出最小和最大的一个元素,存放在序列的起始位置和结束位置,直到全部待排序的
数据元素排完
代码
//选择排序
void SelectSort(int* arr, int n)
{
for (int i = 0; i < n / 2; i++)
{
int mini = i;
int max = i;
for (int j = i+1; j < n-i; j++)
{
if (arr[j] > arr[max])
max = j;
if (arr[j] < arr[mini])
mini = j;
}
Swap(&arr[i], &arr[mini]);
// 如果i和maxi重叠,交换后修正一下
if (max == i)
max = mini;
Swap(&arr[n-i-1], &arr[max]);
}
}
堆排序
- 思路(升序)
将待排序列向下调整建成大堆,根据大堆的特性 根节点的元素是最大的,将根节点的元素和最后一位元素交换位置,再利用向下调整将剩下元素中次大的调整到根,重复操作直至序列有序(序列从后向前依次有序)
// 堆排序
void AdjustDwon(int* arr, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && arr[child + 1] > arr[child])
child++;
if (arr[child] > arr[parent])
{
Swap(&arr[child], &arr[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void HeapSort(int* arr, int n)
{
//建大堆
for (int parent = (n - 1 - 1) / 2; parent >= 0; parent--)
{
AdjustDwon(arr, n, parent);
}
//排序
for (int end = n-1; end > 0; end--)
{
Swap(&arr[0], &arr[end]);
AdjustDwon(arr, end, 0);
}
}
时间复杂度:O(N*logN)
空间复杂度:O(1)
稳定性:不稳定
快速排序
时间复杂度:O(N*logN)
空间复杂度:O(logN)
稳定性:不稳定
hoare版本
- 思路
将序列开始位置选做key。
从序列最右侧(定义为right)向左找比key小的值,找到后停止。
右侧找到后从序列最左侧(定义为left)向左找比key大的值,找到后left和right交换元素,继续从right开始找。
当right和left相遇时,将相遇点元素和key的元素交换。
此时key的左侧都是小于key的数,key的右侧都是大于key的数。
将key的左右侧如此单趟排序,直到左右序列只有一个元素,或是左右序列不存在,递归结束。
代码
// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
//设置key值的下标
int keyi = left;
while (left < right)
{
//右侧找比key小的值
while (left < right && a[right] >= a[keyi])
{
right--;
}
//左侧找比key大的值
while (left < right && a[left] <= a[keyi])
{
left++;
}
//交换两个值的位置
Swap(&a[left], &a[right]);
}
//与key交换位置
Swap(&a[keyi], &a[left]);
return left;
}
void _QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
if (right - left + 1 <= 10)
{
InsertSort(a + left, right - left + 1);
return;
}
int mid = PartSort1(a, left, right);
//int mid = PartSort2(a, left, right);
//int mid = PartSort3(a, left, right);
_QuickSort(a, left, mid - 1);
_QuickSort(a, mid + 1, right);
}
挖坑法
- 思路
类似hoare版本
将序列开始位置的值选做key值。
右侧先走,找到比key小的停止,将元素放入left(最开始就是key的位置)中。
left找到比right大的停止,将元素放入right中,继续从right开始找。
当right和left相遇时,将key直接放入相遇点。
代码
// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
//设置key值
int key = a[left];
while (left < right)
{
//右侧找比key小的值
while (left < right && a[right] >= key)
{
right--;
}
//将小的保存在左侧
a[left] = a[right];
//左侧找比key大的值
while (left < right && a[left] <= key)
{
left++;
}
//将大的保存在右侧
a[right] = a[left];
}
//保存key的值
a[left] = key;
return left;
}
void _QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
if (right - left + 1 <= 10)
{
InsertSort(a + left, right - left + 1);
return;
}
//int mid = PartSort1(a, left, right);
int mid = PartSort2(a, left, right);
//int mid = PartSort3(a, left, right);
_QuickSort(a, left, mid - 1);
_QuickSort(a, mid + 1, right);
}
前后指针法
- 思路
将序列开始位置选做key。
prev指针指向序列起始位置,cur指针指向prev+1。
当cur指向的元素小于key,则prev先向后移动一位,然后交换prev和cur指针的元素,然后cur指针++;若cur指向的元素大于key,则cur指针直接++。
当cur越界时,交换key和prev指针的元素。
// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
//取最小值的下标
int min = GetMid(a, left, right);
//如果和left不同,交换值
if (min != left)
Swap(&a[min], &a[left]);
//设置key值的下标
int keyi = left;
int cur = left+1;
int prev = left;
while (cur <= right)
{
if (a[cur] < a[keyi])
{
if (cur != ++prev)
Swap(&a[cur], &a[prev]);
}
cur++;
}
Swap(&a[keyi], &a[prev]);
return prev;
}
void _QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
if (right - left + 1 <= 10)
{
InsertSort(a + left, right - left + 1);
return;
}
//int mid = PartSort1(a, left, right);
//int mid = PartSort2(a, left, right);
int mid = PartSort3(a, left, right);
_QuickSort(a, left, mid - 1);
_QuickSort(a, mid + 1, right);
}
非递归
非递归类似二叉树的前序遍历,配合栈来模拟实现,将单趟排序的左右区间入栈,根据栈 先进后出的特性实现前序遍历。
// 快速排序 非递归实现
void _QuickSortNonR(int* a, int left, int right)
{
Stack* s = NULL;
StackInit(&s);
if (left < right)
{
StackPush(s, right);
StackPush(s, left);
}
while (!StackEmpty(s))
{
//取起始位置
int _left = StackTop(s);
StackPop(s);
//取结束位置
int _right = StackTop(s);
StackPop(s);
if (_right - _left + 1 <= 10)
{
InsertSort(a + _left, _right - _left + 1);
}
else
{
int mid = PartSort1(a, _left, _right);
//入右侧
if (mid + 1 < _right)
{
StackPush(s, _right);
StackPush(s, mid + 1);
}
//入左侧
if (_left < mid - 1)
{
StackPush(s, mid - 1);
StackPush(s, _left);
}
}
}
StackDestroy(&s);
}
归并排序
时间复杂度:O(N*logN)
空间复杂度:O(N)
稳定性:稳定
- 思路
归并排序类似二叉树的后序遍历。
将序列二分为左区间和右区间,如果左右区间有序,就可以合并左右区间,此时序列有序。
左右区间递归继续二分直至二分到一个元素或不存在后递归结束,此时这一个元素单独有序。
合并已经有序的左区间和右区间后返回直至到根部。
代码
// 归并排序递归实现
void _MergeSort(int* a, int* tmp, int left, int right)
{
if (left >= right)
return;
//二分序列
int mid = (left + right) / 2;
_MergeSort(a, tmp, left, mid);
_MergeSort(a, tmp, mid+1, right);
//合并有序序列
int begin_l = left, end_l = mid;
int begin_r = mid + 1, end_r = right;
int index = left;
while (begin_l <= end_l && begin_r <= end_r)
{
if (a[begin_l] <= a[begin_r])
{
tmp[index++] = a[begin_l++];
}
else
{
tmp[index++] = a[begin_r++];
}
}
while (begin_l <= end_l)
{
tmp[index++] = a[begin_l++];
}
while (begin_r <= end_r)
{
tmp[index++] = a[begin_r++];
}
memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
_MergeSort(a, tmp, 0, n-1);
//_MergeSortNonR(a, tmp, n);
}
非递归
- 思路
非递归思路上就没有递归的模拟后序遍历麻烦,不需要二分序列,直接视每个元素单独有序开始操作。
设值gap=1,每gap个元素为一组(每个元素为一组),每两个gap组两两合并(每两个元素合并,就是合并左右区间),直至合并到序列尾部,合并后gap*=2(之后就和递归版差不多,每组元素个数翻倍)。
代码
// 归并排序非递归实现
void _MergeSortNonR(int* a, int* tmp, int n)
{
int grap = 1;
while (grap < n)
{
//grap组归并
for (int i = 0; i < n; i += grap * 2)
{
int begin_l = i, end_l = i + grap - 1;
int begin_r = i + grap, end_r = i + grap*2 - 1;
//左侧结束位置或右侧开始位置越界,不归并,直接下次循环
if (end_l >= n || begin_r >= n)
continue;
//右侧结束位置越界,结束位置改为n-1
if (end_r >= n)
end_r = n - 1;
int index = i;
while (begin_l <= end_l && begin_r <= end_r)
{
if (a[begin_l] <= a[begin_r])
{
tmp[index++] = a[begin_l++];
}
else
{
tmp[index++] = a[begin_r++];
}
}
while (begin_l <= end_l)
{
tmp[index++] = a[begin_l++];
}
while (begin_r <= end_r)
{
tmp[index++] = a[begin_r++];
}
memcpy(a + i, tmp + i, sizeof(int) * (end_r - i + 1));
}
grap *= 2;
}
}
完整代码
sort.h
#pragma once
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <assert.h>
#include "stack.h"
void PrintArray(int* a, int n);
void InsertSort(int* a, int n);
void ShellSort(int* arr, int n);
void SelectSort(int* arr, int n);
void HeapSort(int* arr, int n);
void BubbleSort(int* a, int n);
// 快速排序实现
void QuickSort(int* a, int n);
// 归并排序实现
void MergeSort(int* a, int n);
void CountSort(int* a, int n);
sort.c
#pragma once
#include "Sort.h"
//打印数组
void PrintArray(int* arr, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
//交换两个数
void Swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//直接插入排序(升序)
void InsertSort(int* arr, int n)
{
for (int i = 1; i < n; i++)
{
int end = i - 1;
int tmp = arr[i];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + 1] = arr[end];
--end;
}
else
{
break;
}
}
arr[end + 1] = tmp;
}
}
//希尔排序(升序)
void ShellSort(int* arr, int n)
{
//设置组
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
共gap组,循环gap组
//for (int i = 0; i < gap; i++)
//{
// //每组循环n / gap - 1次,每组数据内的间隔为gap,每次循环插入一个数据
// for (int j = i; j < n - gap; j += gap)
// {
// int end = j;
// //保存新插入的数据
// int tmp = arr[j + gap];
// //和组内数据比较
// while (end >= 0)
// {
// //如果新插入的数据小于上一个数据
// if (tmp < arr[end])
// {
// //将上一个数据向后挪动
// arr[end + gap] = arr[end];
// //再次对比上上个数据
// end -= gap;
// }
// else
// break;
// }
// //写入新插入的数据
// arr[end+gap] = tmp;
// }
//}
//共gap组,循环gap组 //每组循环n / gap - 1次,间隔为gap的数据为一组,每次循环插入一个数据
for (int i = 0; i < n - gap; i++)
{
int end = i;
//保存新插入的数据
int tmp = arr[i + gap];
//和组内数据比较
while (end >= 0)
{
//如果新插入的数据小于上一个数据
if (tmp < arr[end])
{
//将上一个数据向后挪动
arr[end + gap] = arr[end];
//再次对比上上个数据
end -= gap;
}
else
break;
}
//写入新插入的数据
arr[end + gap] = tmp;
}
}
}
//选择排序
void SelectSort(int* arr, int n)
{
for (int i = 0; i < n / 2; i++)
{
int mini = i;
int max = i;
for (int j = i+1; j < n-i; j++)
{
if (arr[j] > arr[max])
max = j;
if (arr[j] < arr[mini])
mini = j;
}
Swap(&arr[i], &arr[mini]);
// 如果i和maxi重叠,交换后修正一下
if (max == i)
max = mini;
Swap(&arr[n-i-1], &arr[max]);
}
}
// 堆排序
void AdjustDwon(int* arr, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && arr[child + 1] > arr[child])
child++;
if (arr[child] > arr[parent])
{
Swap(&arr[child], &arr[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void HeapSort(int* arr, int n)
{
//建大堆
for (int parent = (n - 1 - 1) / 2; parent >= 0; parent--)
{
AdjustDwon(arr, n, parent);
}
//排序
for (int end = n-1; end > 0; end--)
{
Swap(&arr[0], &arr[end]);
AdjustDwon(arr, end, 0);
}
}
// 冒泡排序
void BubbleSort(int* a, int n)
{
for (int i = n-1; i > 0; i--)
{
int count = 0;
for (int j = 0; j < i; j++)
{
if (a[j + 1] < a[j])
{
Swap(&a[j + 1], &a[j]);
count++;
}
}
if (!count)
return;
}
}
// 快速排序递归实现
// 三数取中
int GetMid(int* a, int left, int right)
{
int mid = (left + right) / 2;
if (a[left] > a[mid])
{
if (a[mid] > a[right])
return mid;
else
return right;
}
else
{
if (a[left] > a[right])
return left;
else
return right;
}
}
// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
//取最小值的下标
int min = GetMid(a, left, right);
//如果和left不同,交换值
if (min != left)
Swap(&a[min], &a[left]);
//设置key值的下标
int keyi = left;
while (left < right)
{
//右侧找比key小的值
while (left < right && a[right] >= a[keyi])
{
right--;
}
//左侧找比key大的值
while (left < right && a[left] <= a[keyi])
{
left++;
}
//交换两个值的位置
Swap(&a[left], &a[right]);
}
//与key交换位置
Swap(&a[keyi], &a[left]);
return left;
}
// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
//取最小值的下标
int min = GetMid(a, left, right);
//如果和left不同,交换值
if (min != left)
Swap(&a[min], &a[left]);
//设置key值
int key = a[left];
while (left < right)
{
//右侧找比key小的值
while (left < right && a[right] >= key)
{
right--;
}
//将小的保存在左侧
a[left] = a[right];
//左侧找比key大的值
while (left < right && a[left] <= key)
{
left++;
}
//将大的保存在右侧
a[right] = a[left];
}
//保存key的值
a[left] = key;
return left;
}
// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
//取最小值的下标
int min = GetMid(a, left, right);
//如果和left不同,交换值
if (min != left)
Swap(&a[min], &a[left]);
//设置key值的下标
int keyi = left;
int fast = left+1;
int slow = left;
while (fast <= right)
{
if (a[fast] < a[keyi])
{
if (fast != ++slow)
Swap(&a[fast], &a[slow]);
}
fast++;
}
Swap(&a[keyi], &a[slow]);
return slow;
}
void _QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
if (right - left + 1 <= 10)
{
InsertSort(a + left, right - left + 1);
return;
}
//int mid = PartSort1(a, left, right);
//int mid = PartSort2(a, left, right);
int mid = PartSort3(a, left, right);
_QuickSort(a, left, mid - 1);
_QuickSort(a, mid + 1, right);
}
// 快速排序 非递归实现
void _QuickSortNonR(int* a, int left, int right)
{
Stack* s = NULL;
StackInit(&s);
if (left < right)
{
StackPush(s, right);
StackPush(s, left);
}
while (!StackEmpty(s))
{
//取起始位置
int _left = StackTop(s);
StackPop(s);
//取结束位置
int _right = StackTop(s);
StackPop(s);
if (_right - _left + 1 <= 10)
{
InsertSort(a + _left, _right - _left + 1);
}
else
{
int mid = PartSort1(a, _left, _right);
//入右侧
if (mid + 1 < _right)
{
StackPush(s, _right);
StackPush(s, mid + 1);
}
//入左侧
if (_left < mid - 1)
{
StackPush(s, mid - 1);
StackPush(s, _left);
}
}
}
StackDestroy(&s);
}
void QuickSort(int* a, int n)
{
//_QuickSort(a, 0, n - 1);
_QuickSortNonR(a, 0, n - 1);
}
// 归并排序递归实现
void _MergeSort(int* a, int* tmp, int left, int right)
{
if (left >= right)
return;
int mid = (left + right) / 2;
_MergeSort(a, tmp, left, mid);
_MergeSort(a, tmp, mid+1, right);
int begin_l = left, end_l = mid;
int begin_r = mid + 1, end_r = right;
int index = left;
while (begin_l <= end_l && begin_r <= end_r)
{
if (a[begin_l] <= a[begin_r])
{
tmp[index++] = a[begin_l++];
}
else
{
tmp[index++] = a[begin_r++];
}
}
while (begin_l <= end_l)
{
tmp[index++] = a[begin_l++];
}
while (begin_r <= end_r)
{
tmp[index++] = a[begin_r++];
}
memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}
// 归并排序非递归实现
void _MergeSortNonR(int* a, int* tmp, int n)
{
int grap = 1;
while (grap < n)
{
//grap组归并
for (int i = 0; i < n; i += grap * 2)
{
int begin_l = i, end_l = i + grap - 1;
int begin_r = i + grap, end_r = i + grap*2 - 1;
//左侧结束位置或右侧开始位置越界,不归并,直接下次循环
if (end_l >= n || begin_r >= n)
continue;
//右侧结束位置越界,结束位置改为n-1
if (end_r >= n)
end_r = n - 1;
int index = i;
while (begin_l <= end_l && begin_r <= end_r)
{
if (a[begin_l] <= a[begin_r])
{
tmp[index++] = a[begin_l++];
}
else
{
tmp[index++] = a[begin_r++];
}
}
while (begin_l <= end_l)
{
tmp[index++] = a[begin_l++];
}
while (begin_r <= end_r)
{
tmp[index++] = a[begin_r++];
}
memcpy(a + i, tmp + i, sizeof(int) * (end_r - i + 1));
}
grap *= 2;
}
}
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
//_MergeSort(a, tmp, 0, n-1);
_MergeSortNonR(a, tmp, n);
}
// 计数排序
void CountSort(int* a, int n)
{
int max = 0;
for (int i = 0; i < n; i++)
{
if (a[i] > max)
max = a[i];
}
max++;
int* tmp = (int*)malloc(sizeof(int) * max);
memset(tmp, 0, sizeof(int) * max);
for (int i = 0; i < n; i++)
{
tmp[a[i]]++;
}
int index = 0;
for (int i = 0; i <= max-1; i++)
{
while (tmp[i]--)
{
a[index++] = i;
}
}
free(tmp);
}