冒泡排序:每一趟循环两两比较,大的向后挪动,最终最大值放在最后,n个数据需要跑n-1趟。
代码如下:
//冒泡排序 时间复杂度O(n^2) 空间复杂度O(1) 稳定性:稳定
void BubbleSort(int* arr, int len)
{
assert(arr != NULL);
for (int i = 0; i < len-1; ++i)
{
for (int j = 0; j < len - 1 - i; ++j)
{
if (arr[j+1] < arr[j])
{
int tmp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = tmp;
}
}
}
}
我想大多数人刚开始写的代码是上边的类型,但之后有人对冒泡进行优化,但优化的可以说是优化很小
//冒泡排序 时间复杂度O(n^2) 空间复杂度O(1) 稳定性:稳定
void BubbleSort(int* arr, int len)
{
assert(arr != NULL);
bool tag = true;//标记 首先赋初值为真
//从头到尾遍历一般,没有交换操作,则可以认定完全有序
//反过来说:只要有一次交换操作,则不能认定完全有序
for (int i = 0; i < len-1; ++i)
{
tag = true;
for (int j = 0; j < len - 1 - i; ++j)
{
if (arr[j+1] < arr[j])
{
int tmp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = tmp;
tag = false;
}
}
if (tag)
{
break;
}
}
}
就是加了一个bool类型,只要头到尾遍历一般,没有交换操作,则可以认定完全有序,直接退出。
堆排序:
先搞清楚什么是大顶堆,小顶堆
大顶堆:父节点的值大于孩子节点(从小到大排序用大顶堆)
小顶堆:父节点的值小于孩子节点(从大到小排序用小顶堆)
1。我们需要先将原始数据想象成一个完全二叉树
2.从最后一个非叶子节点开始调整,调整为大顶堆
此时我们会发现根节点的值是数组中最大的那个值
3.将根节点和最后一个节点的值进行交换,然后将这个尾结点踢除我们的排序数组即可
4.重复2,3步骤
代码如下:
//一次调整函数 时间复杂度O(logn)
void HeapAdjust(int arr[], int start, int end)
{
//assert
int tmp = arr[start];
for(int i=start*2+1; i<=end; i=start*2+1)//start*2+1 相当于是start这个节点的左孩子
{ //i<end 退出for循环 触发的是情况1
if(i<end && arr[i+1] > arr[i])//i<end 代表存在右孩子,且右孩子的值还大于左孩子
{
i++;//则此时,让i指向右孩子
}
//此时i肯定已经指向较大的那个孩子
if(arr[i] > tmp)//子大于父
{
arr[start] = arr[i];
start = i;
}
else
{
break;//退出for循环,触发情况2
}
}
arr[start] = tmp;
}
//堆排序:时间复杂度O(n*logn) 空间复杂度O(1) 稳定性:不稳定
void HeapSort(int *arr, int len)
{
//1.整体从最后一个非叶子节点开始由内到外调整一次
//首先需要知道最后一个非叶子节点的下标
for(int i=(len-1-1)/2; i>=0; i--)//因为最后一个非叶子节点肯定是 最后一个叶子节点的父节点
{
HeapAdjust(arr, i, len-1);//调用我们一次调整函数 //这里第三个值比较特殊,没有规律可言,则直接给最大值len-1
}
//此时,已经调整为大顶堆了
//接下来,根节点的值和当前最后一个节点的值进行交换,然后将尾结点剔除掉
for(int i=0; i<len-1; i++)
{
int tmp = arr[0];
arr[0] = arr[len-1-i];//len-1-i 是我们当前的尾结点下标
arr[len-1-i] = tmp;
HeapAdjust(arr, 0, (len-1-i)-1);//len-1-i 是我们当前的尾结点下标,然后再给其-1则相当于将其剔除出我们的循环
}
}