惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
V2EX - 技术
V2EX - 技术
T
Troy Hunt's Blog
TaoSecurity Blog
TaoSecurity Blog
Attack and Defense Labs
Attack and Defense Labs
SecWiki News
SecWiki News
M
MIT News - Artificial intelligence
N
News and Events Feed by Topic
Help Net Security
Help Net Security
IT之家
IT之家
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
The Last Watchdog
The Last Watchdog
Martin Fowler
Martin Fowler
Hacker News: Ask HN
Hacker News: Ask HN
酷 壳 – CoolShell
酷 壳 – CoolShell
人人都是产品经理
人人都是产品经理
H
Heimdal Security Blog
B
Blog
Blog — PlanetScale
Blog — PlanetScale
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Threat Research - Cisco Blogs
I
InfoQ
腾讯CDC
L
LangChain Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Know Your Adversary
Know Your Adversary
Cloudbric
Cloudbric
Project Zero
Project Zero
T
Tor Project blog
小众软件
小众软件
博客园 - 司徒正美
www.infosecurity-magazine.com
www.infosecurity-magazine.com
H
Help Net Security
Webroot Blog
Webroot Blog
量子位
NISL@THU
NISL@THU
Schneier on Security
Schneier on Security
Google Online Security Blog
Google Online Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
月光博客
月光博客
宝玉的分享
宝玉的分享
V
V2EX
T
Tailwind CSS Blog
Spread Privacy
Spread Privacy
G
Google Developers Blog
K
Kaspersky official blog

Blog Feed

锐评简笔记/某站商店/某彦 - 字节星球 浅谈 DDD 领域驱动设计架构 - 字节星球 浅谈分布式事务#2 - 字节星球 浅谈分布式事务 - 字节星球 聊聊大学里面的奇葩 - 字节星球 MySQL 加锁机制分析与死锁排查 - 字节星球 Golang pprof 案例实战 - 字节星球 Golang 手写一个 Channel - 字节星球 字节星球终于全栈上新! - 字节星球 欢迎试用 TodoList - 字节星球 记一次“说走就走”的成都行 - 字节星球 Docker Desktop修改默认存储路径 - 字节星球 流程中心使用指南 - 字节星球 微分方程小手册 - 字节星球 近期小记 - 字节星球 字节星球(肥柴之家)搬家了! - 字节星球 如何顺利注册ChatGPT? - 字节星球 披着CLion的外衣实则在讲CMake - 字节星球 守望之墓/电子骨灰盒 - 字节星球 Python笔记 第三章 - 字节星球 WePlanet现已发布! - 字节星球 希尔排序 - 字节星球 插入排序 - 字节星球 快速排序 - 字节星球 Python 笔记 第二章 - 字节星球 Python笔记 第一章 - 字节星球 Web使用HarmonyOS字体的压缩方案 - 字节星球 字节星球关于在评论区等位置展示IP属地的公告 - 字节星球 MATLAB简明教程#1 - 字节星球 解决Qt5无法连接MySQL数据库的问题 - 字节星球 时隔多年,终于摆脱了控制台 - 字节星球 论内卷 - 字节星球 堆排序 - 字节星球 连通块中点的数量 - 字节星球 合并集合(并查集) - 字节星球 Trie字符串统计 - 字节星球 KMP字符串 - 字节星球
简单选择排序和堆排序 - 字节星球
2022-08-05 · via Blog Feed

简单选择排序和堆排序

2022/8/5 22:22:00admin4717 阅读1 点赞2 评论

最近在全面学习数据结构,常用算法记录:简单选择排序和堆排序,简单选择排序的基本思想是每一趟在待排序元素中选取关键字最小的元素加入有序子序列,直到所有元素有序,总共进行 n−1n-1 趟。
堆排序的基本思想见文末图片。

简单选择排序为不稳定排序。
堆排序为不稳定排序。

简单选择排序时间复杂度:

时间复杂度:O(n2)O(n^2)
空间复杂度:O(1)O(1)

堆排序时间复杂度:

一个节点每下降一层,最多只需要比较两次关键字。若树高度为 hh,某节点在第 ii 层,则将这个节点向下调整最多只需要下降 h−ih-i 层,那么对比次数不超过 2(h−i){2}(h-i)nn 个节点的完全二叉树树高 h=⌊log⁡2n⌋+1h = \left\lfloor {{{\log }_2}n} \right\rfloor + 1

将整棵树调整为大根堆,关键字比较次数不超过:

∑i=h−112i−12(h−i)=∑i=h−112i(h−i)=∑j=1h−12h−jj≤2n∑j=1h−1j2j≤4n \sum\limits_{{\rm{i}} = h - 1}^1 {{2^{i - 1}}2(h - i) = } \sum\limits_{{\rm{i}} = h - 1}^1 {{2^i}(h - i) = } \sum\limits_{j = 1}^{h - 1} {{2^{h - j}}j \le 2n\sum\limits_{j = 1}^{h - 1} {\frac{j}{{{2^j}}}} } \le 4n

建堆的过程关键字的对比次数不超过 4n{4}n,建堆的时间复杂度:O(n)O(n)

heapSort总共需要 n−1n-1 趟,每一趟完成后都需要将根节点下坠,根节点最多下降 h−1h-1 层,因此,每一趟排序的复杂度不超过 O(h)=O(log⁡2n)O(h)=O({{{\log }_2}n}),总共 n−1n-1 趟,故总时间复杂度:O(nlog⁡2n)O(n{{{\log }_2}n})

故堆排序的时间复杂度:O(n)+O(nlog⁡2n)=O(nlog⁡2n)O(n)+O(n{{{\log }_2}n})=O(n{{{\log }_2}n})
空间复杂度:O(1)O(1)

CPP

#include <iostream>

using namespace std;

void swap(int &a, int &b);
void selectSort(int arr[], int n);  //简单选择排序

void buildMaxHeap(int arr[], int len);  //建立大根堆
void headAdjust(int arr[], int k, int len);  //调整节点,使其较小节点下坠,使其符合大根堆的特性

void heapSort(int arr[], int len);  //堆排序(基于大根堆)

int main()
{
    int arr[] = {5, 7, 12, 6, 2, 0, 8, 15, 1, 11}, heap_arr[] = {-1, 5, 7, 12, 6, 2, 0, 8, 15, 1, 11};
    int length = (int)(sizeof(arr) / sizeof(int));  //数组长度
    selectSort(arr, length);
    for(auto item:arr)
        cout << item << " ";
    cout << endl;

    heapSort(heap_arr, length);
    for(int i = 1; i < length + 1; i++)
        cout << heap_arr[i] << " ";

    return 0;
}

void swap(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

void selectSort(int arr[], int n)
{
    for (int i = 0; i < n - 1; i++)     //进行n-1次即可,最后一个数必然是最大的
    {
        int min = i;
        for (int j = i + 1; j < n; j++)     //从i后面一个数开始,找到一个最小的数
        {
            if (arr[j] < arr[min])
                min = j;    //记录最小数的下标
        }
        swap(arr[i], arr[min]);     //将最小数与i位置的数交换
    }
}

void buildMaxHeap(int arr[], int len)
{
    for (int i = len / 2; i > 0; i--)  //从最后一个分支节点开始调整,0为暂存节点
        headAdjust(arr, i, len);
}

void headAdjust(int arr[], int k, int len)
{
    arr[0] = arr[k];  //将当前节点暂存给arr[0]
    for (int i = 2 * k; i <= len; i *= 2)   //沿值较大的节点向下查找
    {
        if(i < len && arr[i] < arr[i + 1])  //i < len 保证当前节点有右孩子
            i++;    //记录左右子节点中较大的节点
        if(arr[0] >= arr[i])
            break;    //如果当前节点大于等于左右子节点,则不需要调整
        else{
            arr[k] = arr[i];   //将左右子节点中较大的节点放入当前双亲节点
            k = i;  //替换为较大的节点,进入下一次循环,看是否满足大于左右孩子的条件
        }
    }
    arr[k] = arr[0];  //将待调整的节点的值放入最终位置
}

void heapSort(int arr[], int len)
{
    buildMaxHeap(arr, len);     //建立大根堆
    for (int i = len; i > 1; i--)
    {
        swap(arr[1], arr[i]);   //堆顶和堆底交换,使堆底元素最大,注意堆顶是arr[1],arr[0]是暂存节点
        headAdjust(arr, 1, i - 1);  //调整剩余的堆(i及其后面的序列已经有序),让堆顶元素下坠
    }
}

image.png