堆排、快速排序、归并排序等总结

news2024/11/26 21:35:03

十大经典排序算法大梳理 (动图+代码)(动态图参考)

排序算法平均时间复杂度最差时间复杂度空间复杂度数据对象稳定性
冒泡排序O(n2)O(n2)O(1)稳定
选择排序O(n2)O(n2)O(1)数组不稳定、链表稳定
插入排序O(n2)O(n2)O(1)稳定
快速排序O(n*log2n)O(n2)O(log2n)不稳定
堆排序O(n*log2n)O(n*log2n)O(1)不稳定
归并排序O(n*log2n)O(n*log2n)O(n)稳定
希尔排序O(n*log2n)O(n2)O(1)不稳定
计数排序O(n+m)O(n+m)O(n+m)稳定
桶排序O(n)O(n)O(m)稳定
基数排序O(k*n)O(n2)稳定

堆排序

基本概念总结

堆的性质

  • 堆中的某个节点一定是不大于或者不小于其父节点的
  • 堆总是一个完全二叉树

 

排序实现 

向下调整(从父亲到儿子)

// 堆的向下调整(建立大根堆)
void AdjustDown(vector<int>& arr, int parent)
{
	int n = arr.size() - 1;
	int child = parent * 2 + 1;//正常父节点即是下标为0的节点

	while (child < n)
	{
		//找到数值最大的孩子
		if (child + 1 < n && arr[child + 1] > arr[child]) {
			++child;
		}
		if (arr[child] > arr[parent]) {
			swap(arr[child], arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {
			break;
		}
	}
}

 向上调整(从儿子到父亲)

//堆的向上调整
void AdjustUp(vector<int>& arr, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0) {
		if (arr[child] > arr[parent]) {
			swap(arr[child], arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

使用STL实现大根堆和小根堆的方法 

void head()
{
	//默认大根堆
	priority_queue<int> heap_max;

	//小根堆
	priority_queue<int, vector<int>, greater<int>> heap_min;
}

STL优先级队列实现堆

priority_queue

  • 默认是大堆,每次top()方法的时候,都会返回当前队列中最高优先级元素
  • 可以通过自定义排序,实现小堆,或者按照自己的需求进行排序

 大堆使用演示

#include <iostream>
#include <queue>
#include <vector>

int main() {
    // 定义一个最大堆优先队列(默认情况下)
    std::priority_queue<int> maxHeap;

    // 定义一个最小堆优先队列
    std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;

    // 插入元素
    maxHeap.push(10);
    maxHeap.push(20);
    maxHeap.push(15);

    // 获取并移除最大元素
    std::cout << "Max element: " << maxHeap.top() << std::endl;  // 输出 20
    maxHeap.pop();

    std::cout << "Max element after pop: " << maxHeap.top() << std::endl;  // 输出 15

    return 0;
}

自定义小根堆的方法

  • 通过函数重载,重写函数对象
  • lambda表达式实现
  •  函数指针实现自定义排序
  • 复杂对象中可以自定义排序规则
#include <iostream>
#include <queue>
#include <vector>

// 自定义比较器函数对象
struct Compare {
    bool operator()(int a, int b) {
        return a > b;  // 实现小顶堆,即a<b时返回false,表示a优先级更高
    }
};

int main() {
    // 使用自定义比较器创建小顶堆
    std::priority_queue<int, std::vector<int>, Compare> minHeap;

    // 插入元素
    minHeap.push(30);
    minHeap.push(10);
    minHeap.push(20);

    // 获取并移除优先级最高(值最小)的元素
    std::cout << "Min element: " << minHeap.top() << std::endl;  // 输出 10
    minHeap.pop();
    
    std::cout << "Next min element: " << minHeap.top() << std::endl;  // 输出 20

    return 0;
}
#include <iostream>
#include <queue>
#include <vector>

int main() {
    // 使用lambda表达式作为比较器
    auto compare = [](int a, int b) {
        return a > b;  // 实现小顶堆
    };

    // 使用自定义比较器创建小顶堆
    std::priority_queue<int, std::vector<int>, decltype(compare)> minHeap(compare);

    // 插入元素
    minHeap.push(40);
    minHeap.push(15);
    minHeap.push(25);

    // 获取并移除优先级最高(值最小)的元素
    std::cout << "Min element: " << minHeap.top() << std::endl;  // 输出 15
    minHeap.pop();

    std::cout << "Next min element: " << minHeap.top() << std::endl;  // 输出 25

    return 0;
}
#include <iostream>
#include <queue>
#include <vector>

// 自定义比较函数
bool compare(int a, int b) {
    return a > b;  // 实现小顶堆
}

int main() {
    // 使用函数指针作为比较器
    std::priority_queue<int, std::vector<int>, bool(*)(int, int)> minHeap(compare);

    // 插入元素
    minHeap.push(50);
    minHeap.push(20);
    minHeap.push(30);

    // 获取并移除优先级最高(值最小)的元素
    std::cout << "Min element: " << minHeap.top() << std::endl;  // 输出 20
    minHeap.pop();

    std::cout << "Next min element: " << minHeap.top() << std::endl;  // 输出 30

    return 0;
}
#include <iostream>
#include <queue>
#include <vector>

// 定义一个结构体
struct Person {
    std::string name;
    int age;

    Person(std::string n, int a) : name(n), age(a) {}
};

// 自定义比较器,按年龄排序
struct Compare {
    bool operator()(const Person& p1, const Person& p2) {
        return p1.age > p2.age;  // 按年龄升序排序
    }
};

int main() {
    // 使用自定义比较器
    std::priority_queue<Person, std::vector<Person>, Compare> pq;

    // 插入元素
    pq.emplace("Alice", 30);
    pq.emplace("Bob", 25);
    pq.emplace("Charlie", 35);

    // 获取并移除优先级最高(年龄最小)的元素
    std::cout << "Youngest person: " << pq.top().name << " (" << pq.top().age << ")" << std::endl;
    pq.pop();

    std::cout << "Next youngest person: " << pq.top().name << " (" << pq.top().age << ")" << std::endl;

    return 0;
}

 

快速排序

时间复杂度

  • 平均时间复杂度: O(n log n)
  • 最坏时间复杂度: O(n^2) (当数组已经是有序的,或者所有元素相等时)
  • 空间复杂度: O(log n) (主要是递归栈的深度)

    int getRandom(vector<int>&nums , int left , int right)
    {
        int r  = rand();
        return nums[r%(right-left+1)+left];
    }

    vector<int> sortArray(vector<int>& nums) 
    {
        srand(time(NULL));
        qsort(nums,0,nums.size()-1);
        return nums;
    }

    void qsort(vector<int>&nums,int l, int r)
    {
        if(l>=r) return;

        //1. 处理该区间内的三块数组
        int key = getRandom(nums,l,r);
        int i = l,left = l-1,right = r+1;//初始位置很重要
        while(i<right)
        {
            //left和right指针的移动需要着重注意
            if(nums[i]<key){
                swap(nums[++left],nums[i++]);
            }else if(nums[i]==key){
                i++;
            }else{
                //i不++的原因:因为换过来的right位置还没有进行判断过
                swap(nums[--right],nums[i]);
            }
        }

        //2. 继续处理以key为基准的其他两区间的数组
        qsort(nums,l,left);
        qsort(nums,right,r);
    }

完整代码

#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib> // 包含rand和srand函数

using namespace std;

// 获取区间内的随机元素
int getRandom(vector<int>& nums, int left, int right) {
    int r = rand();
    return nums[r % (right - left + 1) + left];
}

// 快速排序的辅助函数
void qsort(vector<int>& nums, int l, int r) {
    if (l >= r) return;

    // 处理该区间内的三块数组
    int key = getRandom(nums, l, r);
    int i = l, left = l - 1, right = r + 1; // 初始位置
    while (i < right) {
        if (nums[i] < key) {
            swap(nums[++left], nums[i++]);
        } else if (nums[i] == key) {
            i++;
        } else {
            swap(nums[--right], nums[i]);
        }
    }

    // 继续处理以key为基准的其他两区间的数组
    qsort(nums, l, left);
    qsort(nums, right, r);
}

// 快速排序的主函数
vector<int> sortArray(vector<int>& nums) {
    srand(time(NULL));  // 设置随机数种子
    qsort(nums, 0, nums.size() - 1);
    return nums;
}

// 测试函数
int main() {
    vector<int> nums = {3, 6, 8, 10, 1, 2, 1, 5, 7, 9};
    
    cout << "Original array: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;

    nums = sortArray(nums);

    cout << "Sorted array: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

归并排序

实现逻辑

归并排序,可以简单理解为将大数组拆分成小数组,然后小数组有序后合并成大数组

  • 具体实现是借助递归,递归最终实现有序数组
  • 注意,下图在代码执行层面,都是在原数组上直接进行变动数据,而不是另外使用数组存储对应数据

代码实现

//归并排序(稳定,最好O(n*log2n),最差O(n*log2n))
void mergeSort(vector<int>& arr, vector<int>& tmp, int left, int right)
{
	if (left >= right) return;

	//2.1 选择中间点,利用位运算判断中间点
	int mid = (left + right) >> 1;

	//2.2 将分好的左右区间,递归向下进行排序
	mergeSort(arr,tmp, left, mid);
	mergeSort(arr, tmp,mid + 1, right);

	//2.3 合并两个有序数组
	int cur1 = left, cur2 = mid + 1, i = 0;
	while (cur1 <= mid && cur2 <= right) {
		tmp[i++] = arr[cur1] <= arr[cur2] ? arr[cur1++] : arr[cur2++];
	}
	//处理其中可能没有处理到的数组
	while (cur1 <= mid) tmp[i++] = arr[cur1++];
	while (cur2 <= right) tmp[i++] = arr[cur2++];

	//2.4 还原:将排序的数字放回到arr中
	for (int i = left; i <= right; ++i) {
		arr[i] = tmp[i - left];
	}
}

冒泡排序

#include <iostream>
#include <vector>

void bubbleSort(std::vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; ++i) {
        for (int j = 0; j < n - i - 1; ++j) {
            if (arr[j] > arr[j + 1]) {
                std::swap(arr[j], arr[j + 1]);
            }
        }
    }
}

int main() {
    std::vector<int> arr = {64, 34, 25, 12, 22, 11, 90};
    bubbleSort(arr);
    for (int i : arr) std::cout << i << " ";
    return 0;
}

选择排序

#include <iostream>
#include <vector>

void selectionSort(std::vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; ++i) {
        int minIdx = i;
        for (int j = i + 1; j < n; ++j) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        std::swap(arr[i], arr[minIdx]);
    }
}

int main() {
    std::vector<int> arr = {64, 25, 12, 22, 11};
    selectionSort(arr);
    for (int i : arr) std::cout << i << " ";
    return 0;
}

 插入排序

#include <iostream>
#include <vector>

void insertionSort(std::vector<int>& arr) {
    int n = arr.size();
    for (int i = 1; i < n; ++i) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            --j;
        }
        arr[j + 1] = key;
    }
}

int main() {
    std::vector<int> arr = {12, 11, 13, 5, 6};
    insertionSort(arr);
    for (int i : arr) std::cout << i << " ";
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2100139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《省间中长期市场连续运营下考虑耦合安全约束的电力交易联合优化出清模型》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

Docker笔记-Docker容器使用

Docker笔记-Docker容器使用 1、Docker 客户端 docker 客户端非常简单&#xff0c;我们可以直接输入 docker 命令来查看到 Docker 客户端的所有命令选项。 $ docker可以通过命令 docker command --help 更深入的了解指定的 Docker 命令使用方法。 例如我们要查看 docker sta…

用vs附加运行的软件并打断点

需求&#xff1a;给一个代码文件A的第n行打断点。 前提&#xff1a;目标软件已经build完成。并且生成了对应的pdb文件P。 0 将要打断点的代码&#xff0c;所在生成的pdb文件放到exe相同目录下。 1 运行程序。 2 打开vs&#xff0c;并打开文件A。并在n行打上断点。&#xff…

视觉SLAMch4——李群和李代数

一、李群和李代数在SLAM中的定位&#xff08;如何解决SLAM中的问题&#xff09; 在视觉SLAM中&#xff0c;我们的目标之一是估计传感器&#xff08;通常是摄像头&#xff09;在每一时刻的位置和姿态。为了量化估计的好坏&#xff0c;我们需要定义一个误差函数&#xff0c;该函数…

Apache Arrow简介

是什么&#xff1f; (1) Apache Arrow是内存分析开发平台&#xff0c;是Apache的顶级项目。 (2) Apache Arrow是一个开源的跨平台数据层开发框架&#xff0c;主要提供高效的、硬件加速的、内存中数据计算的能力。Apache Arrow的设计初衷是作为“新一代大数据系统的共享基础”&a…

3.5 算术运算指令

&#x1f393; 微机原理考点专栏&#xff08;通篇免费&#xff09; 欢迎来到我的微机原理专栏&#xff01;我将帮助你在最短时间内掌握微机原理的核心内容&#xff0c;为你的考研或期末考试保驾护航。 为什么选择我的视频&#xff1f; 全程考点讲解&#xff1a;每一节视频都…

11、Django Admin启用对计算字段的过滤

重新定义admin.py中的Hero管理模型如下&#xff1a; admin.register(Hero) class HeroAdmin(admin.ModelAdmin):list_display ("name", "is_immortal", "category", "origin", "is_very_benevolent")list_filter ("…

(第四十天)配置完整的考试系统,完成前端和后端并优化,mysql后端,nginx前端,java连接

回顾 1 、使用 harbor 仓库 python --version yum -y update yum -y install python2-pip pip install --upgrade pip 20 .3 -i ....... pip install docker-compose -i ....... sh docker.sh 解压 harbor vim harbor.yml ./prepare ./installed 使用…

智慧储能柜构造揭秘:安科瑞2000ES解决方案打造智慧储能大脑

随着全球能源结构的转型和可再生能源的快速发展&#xff0c;储能技术成为调节能源供需、提高能源利用效率的关键。储能柜作为储能技术的核心组成部分&#xff0c;其构造和功能显得尤为重要。本文将为您详细解析储能柜的构造&#xff0c;并展示其如何成为高效能源管理的智慧核心…

ArcGIS Pro高级技巧:制作带地类编码文字的用地图例

欢迎关注同名微信公众号&#xff0c;更多文章推送&#xff1a; 01 需求分析 这个方法由群友【Erik】提供。 很有意思&#xff0c;看到最后一定会帮你打开思路。 直接上图比较&#xff0c;这是一般情况下简单的用地图例&#xff0c;只有色块&#xff1a; 下面这个是我们想要…

4、Django Admin对自定义的计算字段进行排序

通常&#xff0c;Django会为模型属性字段&#xff0c;自动添加排序功能。当你添加计算字段时&#xff0c;Django不知道如何执行order_by&#xff0c;因此它不会在该字段上添加排序功能。 如果要在计算字段上添加排序&#xff0c;则必须告诉Django需要排序的内容。你可以通过在…

有了这款AI代码神器Coursor,学习Rust不再是难题!

大家好&#xff0c;我是渔夫。 工欲善其事&#xff0c;必先利其器。AI 是未来十年生产力的核心工具&#xff0c;要让 AI 真正转化为生产力&#xff0c;而不仅仅是围观一时的热潮。 今天来聊聊最近又火爆AI圈的AI代码神器 Cursor&#xff0c;它其实是一款 VS Code 的一个分支&am…

在C#中使用Redis

NoSql NoSql概念 NoSQL&#xff0c;泛指非关系型的数据库。随着互联网web2.0网站的兴起&#xff0c; 历史中—中国的网站----马云--- 中国黄页&#xff0c;只能展示&#xff1b;用户只能看到 传统的关系数据库在处理web2.0网站&#xff08;可以看&#xff0c;也可以做到写&am…

华为云CCE集群创建loadBalancer

目录 一、目的 二、创建应用 三、创建服务 loadBalancer 四、域名解析 五、验证 一、目的 1、为CCE容器应用创建loadBalancer服务&#xff0c;并且绑定https协议的域名 2、公网访问域名: https://test.******.com 3、CCE创建用于公网域名访问的loadBalancer&#xff0c;不…

IAR9.X printf串口重定向方法,或提示Linker Error: “no definition for __write“的解决方案

一、问题现象&#xff1a; 1、Error[Li005]:no definition for"__write" [referenced from flush.o(dl7M_tlf.a)] 2、串口重映射代码没问题&#xff0c;但是串口工具接收不到数据 3、复现环境&#xff1a;IAR9.40.1 二、操作方法: 1、[工程项目]->[Options]…

【负载均衡式在线OJ】Compile_server 模块

文章目录 程序源码compile_server整体思路编译(compile.hpp)运行模块编译运行模块编译运行服务 程序源码 https://gitee.com/not-a-stupid-child/online-judge compile_server 整体思路 这个服务要对oj_server 发送过来的代码进行编译和运行&#xff0c;最后把结果返回给oj…

理想二极管

原理图 mos管选型 参考链接 很实用&#xff01;用MOS管制作一个理想中的二极管_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Xi421r7K8/?spm_id_from333.1007.tianma.2-1-4.click&vd_sourcee821a225c7ba4a7b85e5aa6d013ac92e 特此记录 anlog 2024年9月3日

JS设计模式之“单孑独立” - 单例模式

image.png 引言 在JavaScript开发中&#xff0c;单例模式是一种常见且实用的设计模式一。 单例模式的核心思想是&#xff1a;确保一个类只有一个实例对象&#xff0c;并且该对象可以在应用程序的任何地方被共享和访问。通过使用单例模式&#xff0c;我们可以简化代码、节省资…

SpringCloud开发实战(一):搭建SpringCloud框架

本系列文章主要是记录在工作中用到的微服务的各个组件&#xff0c;以及学习新的微服务组件~如有问题&#xff0c;欢迎大家批评指导。如果本文对你有帮助&#xff0c;还请点个收藏和关注。感谢支持&#xff0c;希望大家写的代码都没有BUG&#xff01;&#xff01; 前言 下面是我…

Linux下编译安装SuperLU

SuperLU用于求解大规模稀疏线性方程组&#xff0c;本文记录在远程Linux服务器下编译安装SuperLU的流程。 一、配置VS Code 2.1 安装VS Code Extensions 在本地打开VS Code, 安装以下扩展插件&#xff0c; Task Explorer Output Colorizer Git Extension Pack Remote Develop…