本篇博客只介绍冒泡排序,插入排序,选择排序,以及堆排序详解
冒泡排序、插入排序、选择排序
因为这几种排序都相对比较简单,其他的博主都写得很完善,我就只给上我的代码,连同后面的堆排序给出测试结果截图:
void Bubblesort(int *a, int n) //冒泡排序
{
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (a[j] > a[j + 1])
{
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
void Selectsort(int *a, int n) //选择排序 不断选出每一躺中最小的元素
{
int temp = 0;
for (int i = 0; i < n; i++)
{
temp = i;
for (int j = i + 1; j < n; j++)
{
if (a[j] < a[temp])
{
temp = j;
}
}
int ret = a[i];
a[i] = a[temp];
a[temp] = ret;
}
}
void insertSort(int *a, int begin, int end)
{
for (int i = 1; i < end; i++)
{
int temp = a[i];
int j = i - 1;
for (; j >= 0; j--)
{
if (temp < a[j])
{
a[j + 1] = a[j];
}
else
break;
}
a[j + 1] = temp;
}
}
堆的定义
每个节点的值都大于或者等于其左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或者等于其左右孩子节点的值,称为小顶堆。
- 升序采用大顶堆,降序采用小顶堆
堆排序的基本思想
如果要升序,则将待排序的序列构成一个大顶堆,(反之就是一个小顶堆)此时,整个序列的最大值就是对顶的根结点,将它与堆数组的末尾元素进行交换,此时末尾元素就是最大值,然后将剩下的n-1个数字再重新构造一个堆,这样就得带n个元素中的次小值。如此反复执行,便可以得到一个有序序列。
如图所示:假设要对4,6,8,5,9进行排序
- 第一步,我们从第一个非叶子节点开始,也就是6这个节点,从左到右,从下岛、到上进行调整。
- 再找第二个非叶子节点,也就是4,由于[4,9,8]中,9最大,因此4和9进行交换
- 第三步,交换后导致[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换,4和6
此时我们就将一个无序序列构建成了一个大顶堆。
接下来就是第二个大的步骤,将堆顶元素9和末尾元素4进行交换,然后根据上述办法对剩下的n-1个元素进行调堆,如此反复进行交换,重建。
继续调堆,因为倒数第一个非叶子节点6比它的孩子大,不用调堆,再继续倒数第二个非叶子节点4比它的右孩子8小,因此再次进行交换
堆顶元素8和末尾元素5进行交换,得到第二大元素8
以此种方法反复进行,最终得到的序列为:
堆排序的代码:
void HeapAjustUp(int* a,int i,int n) //升序需要向上调堆,最后得出的堆是大顶堆
{ //n表示的是堆的大小,i表示开始从i这个位置开始调堆
int parent = i;
int child = 2 * i + 1;
while (child < n)
{
if (a[child] < a[child + 1] && child + 1 < n)
child++;
if (a[parent] < a[child])
{
swap(&a[parent], &a[child]);
parent = child;
}
child = child * 2 + 1;
}
}
void BuidHeap(int *a,int heapsize)
{
for (int i = heapsize / 2 - 1; i >= 0; i--)
{
HeapAjustUp(a, i, heapsize);
}
}
void HeapSort(int *a, int heapsize)
{
BuidHeap(a, heapsize);
for (int i = heapsize-1; i > 0; i--)
{
swap(&a[0], &a[i]); //将第一个和最后一个元素交换
HeapAjustUp(a,0,i);
}
}
这几种排序的结果截图:
建堆的时间复杂度:O(n)
调整堆:n(logn)(因为每一次都是从根结点往下循环查找,所以每一次时间都是logn,总时间:logn(n-1)=nlogn-logn);
最优的情况:所有叶子铺满最底层
最差的情况:所有叶子铺满半层的时候