八大排序——选择排序/堆排序

八大排序——选择排序/堆排序

一、选择排序

每一趟从待排序序列中找到其最小值,然后和待排序序列的第一个值进行交换,则此时这个值变有序,待排序序列个数-1,继续下一趟,直到待排序序列个数为1时结束

void Select_Sort(int arr[], int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		int min = i;
		for (int j = i; j < len; j++)//找最小值 用min保存下标
		{
			if (arr[j] < arr[min])//j指向的值比min的值更小,则min更新一下,更新为j
			{
				min = j;
			}
		}

		if (min != i)
		{
			int tmp = arr[min];
			arr[min] = arr[i];
			arr[i] = tmp;
		}
	}
}

时间复杂度O(n^2) 循环嵌套都是

空间复杂度O(1)

稳定性:不稳定 

二、堆排序

:n个节点组成的有限集合,树有且只有一个根节点“ROOT”,树除了根节点之外剩余节点还可以组成m个互不相交的有限集合(称为子树)

 二叉树:每个节点最多有俩个孩子节点

满二叉树:一颗二叉树层数为k,且有2^-1和节点。换句话说,一个二叉树,有多少层就把多少层摆满。

完全二叉树:相较于满二叉树,可以少节点,但只能从最后一层,从右至左少节点

节点的度:孩子的个数

 叶子节点:度为0的节点,最外的一层

 非叶子节点:度不为0的节点

2.1 大顶堆(升序)

是一个特殊的完全二叉树,父节点都大于等于孩子节点

2.1.1 步骤

每次确定一个最大值,然后和最后一个位置进行交换

为什么每次都能获取一个最大值,因为堆排序的核心时维护一个大顶堆(默认升序),大顶堆的唯一特征是根节点是所有值中的最大值

1. 首先将给的数组,想象成一个完全二叉树

2.将这颗完全二叉树调整成一个大顶堆(首次调整是由内到外的调整:从最后一个非叶子节点作为根节点的分支二叉树开始调整,然后从右至左,从下之上

3. 将这课大顶堆的根节点(最大的值)和其最后一个节点进行交换,交换完成后,让此时这个节点断开连接,不参与后续排序、

4. 此时这颗完全二叉树不再是大顶堆,我们需要重新调整为大顶堆,不需要从上面那样从内到外调整,只需要调整最外层的

5. 反复执行三四直到大顶堆中剩下一个节点

2.1.2 代码实现

#include <stdio.h>

// 函数声明,用于显示排序后的数组
void Show(int arr[], int len);

// 调整堆的函数,使其满足大顶堆的性质
void Adjust_Heap(int arr[], int start, int end) {
    int tmp = arr[start]; // 保存当前起始节点的值
    int max_child = start * 2 + 1; // 初始化最大子节点索引为起始节点的左子节点
    // 循环,直到最后一个节点
    for (; max_child <= end; max_child = start * 2 + 1) {
        // 如果右子节点存在且大于左子节点,则更新最大子节点索引
        if (max_child + 1 <= end && arr[max_child + 1] > arr[max_child]) {
            max_child++;
        }
        // 如果最大子节点的值大于保存的起始节点的值,则交换它们
        if (arr[max_child] > tmp) {
            arr[start] = arr[max_child];
            start = max_child; // 更新起始节点为最大子节点
        } else {
            break; // 如果最大子节点的值不大于保存的起始节点的值,则结束循环
        }
    }
    arr[start] = tmp; // 将保存的起始节点的值放到最终位置
}

// 堆排序函数
void Heap_Sort(int arr[], int len) {
    // 构建大顶堆
    for (int i = ((len - 1) - 1) / 2; i >= 0; i--) {
        Adjust_Heap(arr, i, len - 1); // 从最后一个非叶子节点开始调整堆
    }
    // 排序过程
    for (int i = 0; i < len - 1; i++) {
        int tmp = arr[0]; // 保存堆顶元素
        arr[0] = arr[len - 1 - i]; // 将堆顶元素与未排序部分的最后一个元素交换
        arr[len - 1 - i] = tmp; // 完成交换

        Adjust_Heap(arr, 0, len - 1 - i - 1); // 重新调整堆
    }
}

// 主函数
int main() {
    int arr[] = { 10000, 20000, -100, 195, 71, 89, 42, 8, 902, 894, 194, 80, 194, 128, 90, 18, 490, 18, 409, 180, 95, 12, 489, 404 };
    int len = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
    Heap_Sort(arr, len); // 调用堆排序函数对数组进行排序
    Show(arr, len); // 显示排序后的数组
    return 0;
}

// 显示数组的函数
void Show(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]); // 打印数组元素
    }
    printf("\n"); // 打印换行符
}

2.2 小顶堆(降序)

是一个特殊的完全二叉树,父节点都小于等于孩子节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值