news 2026/5/9 6:18:43

C++选择排序插入排序希尔排序快排归并排及大小根堆实现优先级队列

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++选择排序插入排序希尔排序快排归并排及大小根堆实现优先级队列

1.选择排序

(1)选择排序的实现

void ChoiceSort(int arr[], int size) { for (int i = 0; i < size - 1; i++) { int min = arr[i]; int k = i; // 记录最小值下标 for (int j = 1 + i; j < size; j++) { if (arr[j] < min) { k = j; min = arr[j]; } } if (k != i) { int tmp = arr[i]; arr[i] = arr[k]; arr[k] = tmp; } } }

这里k用来记录最小值的下标。

(2)相关知识

特点:每次选取元素中的最小值与当前元素进行交换。

缺点:相比冒泡排序交换的次数减少了,但是比较的次数仍然很多。

选择排序的平均时间复杂度是O(n*n),最好时间复杂度是O(n*n),最坏时间复杂度是O(n*n),空间复杂度O(1),稳定性(不稳定)

2.插入排序

(1)插入排序的实现

void InsertSort(int arr[], int size) { for (int i = 1; i < size; i++) { int val = arr[i]; int j = i - 1; for (; j >= 0; j--) { if (arr[j] <= val) { break; } arr[j + 1] = arr[j]; } arr[j + 1] = val; } }

(2)相关知识

特点:从第二个元素开始把前面的序列当成有序的,然后找到合适的位置进行插入。

优点:插入排序是普通排序里面效率最高的,在数据本身趋于有序的情况下插入排序是所有排序算法中效率最高的。

插入排序的平均时间复杂度O(n*n),最好时间复杂度O(n),最坏时间复杂度O(n*n),空间复杂度O(1),稳定性(稳定)

3.希尔排序

(1)希尔排序的实现

void ShellSort(int arr[], int size) { for (int gap = size; gap > 0; gap /= 2) { for (int i = gap; i < size; i++) { int val = arr[i]; int j = i - gap; for (; j >= 0; j-=gap) { if (arr[j] <= val) { break; } arr[j + gap] = arr[j]; } arr[j + gap] = val; } } }

(2)相关知识

特点:可以看作是多路的插入排序,每组的序列逐渐趋于有序,整体的序列也逐渐趋于有序,插入排序的完美体现。

希尔排序的平均时间复杂度O(n^1.3),最好时间复杂度O(n),最坏时间复杂度O(n*n),空间复杂度O(1),稳定性(不稳定)

4.快速排序

(1)快速排序的实现

//快排分割处理函数 int PartAction(int arr[],int l,int r) { //确定基准值 int val = arr[l]; //分割处理 while(l < r) { //r从右边找到第一个小于等于val的值放到l的位置,l++ while(l < r && arr[r] > val) { r --; } if(l < r) { arr[l] = arr[r]; l++; } //l从左边找到一个大于等于val的值放到r的位置上,r-- while(l < r && arr[l] < val) { l ++; } if(l < r) { arr[r] = arr[l]; r--; } } arr[l] = val; return l; } //快速排序的递归接口 void QuickSort(int arr[],int begin,int end) { //递归结束条件 if(begin >= end) { return ; } //快排分割函数 int pos = PartAction(arr,begin,end); //基准数的左边和右边再进行快排处理 QuickSort(arr,begin,pos-1); QuickSort(arr,pos+1,end); } //快速排序的递归实现 void QuickSort(int arr[],int size) { QuickSort(arr,0,size-1); }

(2)相关知识

特点:选取基准数,将小于基准数的值放到基准数的左边,大于基准数的放到基准数的右边,采用“分治思想”处理剩余的序列元素直到整个序列变成有序序列。

优化点:1.结合插入排序,当序列数据量小于某个值是采用插入排序进行排序。

2.采用”三数取中法”来进行基准数的选取。

3.随机数取基准数法

快速排序的平均时间复杂度为O(nlogn),最坏时间复杂度O(n*n),最好时间复杂度O(nlogn),空间复杂符O(logn)到O(n).稳定性(不稳定)。

5.归并排序

(1)归并排序的实现

//归并排序的排序接口 void Merge(int arr[],int l,int m,int r) { int *p = new int[r-l+1]; int pi = 0; int i = l; int j = m+1; while(i <= m && j <= r) { if(arr[i] <= arr[j]) { p[pi++] = arr[i++]; } else { p[pi++] = arr[j++]; } } while(i <= m) { p[pi++] = arr[i++]; } while(j <= r) { p[pi++] = arr[j++]; } for(i=l,j=0;i<=r;i++,j++) { arr[i] = p[j]; } delete p; } //归并排序的递归接口 void MergeSort(int arr[],int begin,int end) { //递归的结束条件 if(begin >= end) { return ; } //讲数组划分再进行递 int mid = (begin + end)/2; MergeSort(arr,begin,mid); MergeSort(arr,mid+1,end); //在归的过程中进行排序,将两个小段有序数组合成一个大段有序数组 Merge(arr,begin,mid,end); } //归并排序的实现 void MergeSort(int arr[],int size) { MergeSort(arr,0,size-1); }

(2)相关知识

特点:采用“分治思想”,先将序列进行划分,再进行合并排序

归并排序的平均时间复杂度O(nlogn),最坏时间复杂度O(nlogn),最好时间复杂度O(nlogn),空间复杂度O(n),稳定性(稳定)。

6.大小根堆优先级队列

(1)大小根堆优先级队列的实现

class PriorityQueue { public: using Comp = function<bool(int,int)>; PriorityQueue(int cap = 20,Comp comp = greater<int>()) :size_(0) ,cap_(cap) ,comp_(comp) { que_ = new int[cap_]; } PriorityQueue(Comp comp) :comp_(comp) ,cap_(20) ,size_(0) { que_ = new int[cap_]; } ~PriorityQueue() { delete []que_; que_ = nullptr; } //入堆 void push(int val) { if(size_ == cap_) { int *p = new int[cap_*2]; memcpy(p,que_,size_*sizeof(int)); delete []que_; que_ = p; cap_ *= 2; } if(size_ == 0) { que_[size_] = val; } else { //进行入堆上浮操作 siftUp(size_,val); } size_ ++; } //出堆 void pop() { if(size_ == 0) { throw "PriorityQueue is empty"; } size_ --; if(size_ == 0) { return; } else { //进行一个出堆下沉操作 siftDown(0,size_); } } //获取堆顶元素 int top() { if(size_ == 0) { throw "PriorityQueue is empty!"; } return que_[0]; } //判空 bool empty() { return size_ == 0; } //获取有效元素个数 int size() { return size_; } private: //入堆上浮操作 void siftUp(int i,int val) { while(i > 0) { //与当前父节点进行大小对比 int father = (i-1)/2; if(comp_(val,que_[father])) //注意与父节点比较的值是当前要入堆的值 { que_[i] = que_[father]; i = father; } else { break; } } que_[i] = val; } //出堆下沉操作 void siftDown(int i,int size) { while(i <= (size-2)/2) { int child = 2*i+1; if(child+1<size_ && comp_(que_[child+1],que_[child])) { child = child +1; } if(comp_(que_[child],que_[size])) { que_[i] = que_[child]; i = child; } else { break; } } que_[i] = que_[size]; } int *que_; int size_; int cap_; Comp comp_; };

在进行入堆的上浮和出堆的下沉操作时,对于和父子节点比较的值一个是要入堆的值,一个是数组最后一个元素的值。

(2)相关知识

大根堆特点:有子节点的父节点,父节点的值会大于子节点

小根堆特点:有子节点的父节点,父节点的值小于子节点

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 8:24:01

Self-Attention 为什么要做 QKV 的线性变换?又为什么要做 Softmax?

在看 Transformer 的 self-attention 结构时&#xff0c;很多人第一次见到 ( Q, K, V ) 三个矩阵都会有点疑惑&#xff1a; 明明输入就是一个向量序列&#xff0c;为什么还要多此一举做三次线性变换&#xff1f; 而且最后还要套上一个 Softmax&#xff0c;这又是在干什么&#…

作者头像 李华
网站建设 2026/5/5 12:49:22

三极管学习路径规划:零基础入门完整路线

三极管从零开始&#xff1a;一条真正能学会的实战学习路线你是不是也曾经翻开一本模电书&#xff0c;看到“载流子在PN结中的扩散与漂移”就头大&#xff1f;或者用Arduino点亮了LED&#xff0c;却始终搞不清为什么中间要加个三极管&#xff1f;别担心——这不是你的问题。是大…

作者头像 李华
网站建设 2026/5/3 19:29:01

什么是开源?小白如何快速学会开源协作流程并参与项目

大家好&#xff0c;我是虎子&#xff0c;最近开始尝试参与开源项目。一开始我完全懵&#xff1a;开源到底是什么&#xff1f;怎么贡献代码&#xff1f;为什么大佬们都热衷于此&#xff1f;折腾了几个月后&#xff0c;我从零到成功给Alibaba Sentinel提交了两个 PR&#xff08;P…

作者头像 李华
网站建设 2026/5/1 14:40:00

ARM64异常返回指令eret工作机制手把手教程

深入ARM64异常返回机制&#xff1a;ERET指令从原理到实战你有没有遇到过这样的场景&#xff1f;系统突然卡死&#xff0c;串口输出一串神秘的寄存器快照&#xff1b;内核崩溃日志里ELR_EL1的值指向一片未知内存&#xff1b;或者在写一个简单的中断处理程序时&#xff0c;发现er…

作者头像 李华
网站建设 2026/5/4 14:34:38

如何实现稳定ModbusTCP通信?工业场景操作指南

如何在工业现场构建稳定可靠的ModbusTCP通信&#xff1f;一位工程师的实战手记从一次“诡异”的超时说起上周三下午&#xff0c;某水泥厂的中控室突然报警&#xff1a;窑温监测系统连续丢点。SCADA画面上多个温度读数卡在旧值上不动&#xff0c;历史曲线断成一截一截。值班工程…

作者头像 李华