void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//选择排序O(N^2)
void SelectSort(int* arr, int n)
{
//最大最小值所在下标
int mini;
int maxi;
int begin, end;
mini = begin = 0;
end = maxi = n - 1;
//每次遍历存储找到最大最小值的位置
while (begin < end)
{
mini = begin;
maxi = end;
for (int i = begin + 1; i <= end; i++)
{
if (arr[i] < arr[mini])
{
mini = i;
}
if (arr[i] > arr[maxi])
{
maxi = i;
}
}
//交换begin和mini
Swap(&arr[begin], &arr[mini]);
//若begin本来是最大值,则maxi指向begin位置,且上述交换后,mini位置变为了最大值,begin位置变为了最小值
//重新调整maxi指向
if (begin == maxi)
maxi = mini;
Swap(&arr[end], &arr[maxi]);
++begin;
--end;
}
}
直接选择排序的特性总结:
直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不稳定
堆排序
使用数据结构堆的向上调整和向下调整算法来模拟建堆,然后用向下调整排序
cpp复制代码
//向上调整
void AdjustUp(HPDataType* a, int child)
{
assert(a);
int parent = (child - 1) / 2;
while (child > 0)
{
if (a[child] < a[parent])//小堆
{
Swap(&a[parent], &a[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
assert(a);
//默认左孩子小
int child = (parent * 2) + 1;//孩子元素的下标
while (child < n)
{
//让child指向左右孩子中小的一方
if (child + 1 < n && a[child + 1] < a[child])
{
//右孩子小
child++;
}
//判断孩子是否小于父亲节点,再交换
if (a[child] < a[parent])//<是小堆,>是大堆
{
Swap(&a[child], &a[parent]);
parent = child;
child = (parent * 2) + 1;
}
else//孩子不小于父亲,即满足堆的结构,直接跳出调整函数
{
break;
}
}
}
堆排序
降序,建小堆
原理:小堆的top一定是最小值,将top和最后的元素交换,最小值就成为了顺序结构的最后一个元素
再进行向下调整,将除开最后一个元素以外的最小值放到top位置,循环可得降序
升序,建大堆
原理:大堆的top一定是最大值,将top和最后的元素交换,最大值就成为了顺序结构的最后一个元素
再进行向下调整,将除开最后一个元素以外的最大值放到top位置,循环可得升序
cpp复制代码
//堆排序
void HeapSort(HPDataType* a,int n)
{
//降序,建小堆
// 小堆的top一定是最小值,将top和最后的元素交换,最小值就成为了顺序结构的最后一个元素
// 再进行向下调整,将除开最后一个元素以外的最小值放到top位置,循环可得降序
//升序,建大堆
//降序
//向上调整,将数组视为完全二叉树,使其成为小堆
//for (int i = 1; i < n; i++)
//{
// AdjustUp(a, i);
//}
//向下调整,从倒数第一个非叶子节点开始(父节点),向下调整,建小堆
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(a, n, i);
}
int end = n - 1;
while (end > 0)
{
Swap(&a[end], &a[0]);
AdjustDown(a, end, 0);
end--;
}
}
堆排序的特性总结:
堆排序使用堆来选数,效率就高了很多。
时间复杂度:O(N*logN)
空间复杂度:O(1)
稳定性:不稳定
冒泡排序
以升序为例:
前后两个数据比较,将大的换到后一个数位置,循环一次就将最大的数据放到了数组的位置;
循环n次,待排序的数就减少n个(最后n个数据已经排序完成);
cpp复制代码
// 冒泡排序
void BubbleSort(int* a, int n)
{
//前后交换,每一轮将最大的放到最后
//n个数循环n轮完成排序
for (int i = 0; i < n - 1; i++)//第n轮只有第一个数,不需要交换
{
int flag = 0;
for (int j = 1; j < n - i; j++)//j从1开始,所以结束的范围要比从0开始+1即n-i,而不是n-1-i;
{
if (a[j - 1] > a[j])//大的往后换
{
int tmp = a[j - 1];
a[j - 1] = a[j];
a[j] = tmp;
flag = 1;
}
}
if (flag == 0)
break;
}
//计数排序
//只适用于整数
void CountSort(int* arr, int n)
{
int max = arr[0], min = arr[0];//假设最大最小值
for (int i = 0; i < n; i++)
{
if (arr[i] > max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
//范围
int range = max - min + 1;//最大值到最小值的区间,代表count数组的空间大小
//创建count数组记录每个数的出现次数
int* count = (int*)calloc(range, sizeof(int));
if (count == NULL)
{
perror("calloc fail!");
return;
}
//相当于遍历arr数组,计数到count数组中
for (int i = 0; i < n; i++)
{
count[arr[i]-min]++;//假设min = 100,那么当arr[i]=101时相对下标就是1,即arr[i]-min
//数的大小映射count数组中的相对下标位置
}
//遍历count数组,将数据赋值返还给arr
int j = 0;
for (int i = 0; i < range; i++)
{
//每个数有多少个,就放回arr中多少个
while (count[i]--)
{
arr[j++] = i + min;
}
}
free(count);
count = NULL;
}