十大经典排序算法之C++实现

起因

虽然C++的STL给我们直接提供了快速排序算法(std::sort()),刷题的时候大多数题也不要求自己手写排序算法,但是熟悉它们的原理以及自己动手实现一遍(以非递减序列为例),能够加深理解以及更好地应用。特别是对于那些时间复杂度要求严苛,进阶类的题目,理解好这些排序算法将会写出更高效的代码。

万一在面试时被问到也不至于懵逼

总览

图来自参考链接[1]

在这里插入图片描述
P.S.

  1. 其中排序方法的稳定性指的是当两个相同数值的数在排序前后的相对位置是否一致;
  2. in-place 占用常数内存,不占用额外内存;
  3. out-place 占用额外内存

1. 冒泡排序

关键点:将剩余数组中的最大(最小)的元素交替冒泡到尾部(首部)

算法详细原理见:https://www.runoob.com/w3cnote/bubble-sort.html

以将最大数值冒泡到尾部为例:

template<typename T>

void bubble_sort(vector<T>& arr, int len){
    bool swapped = false;
    for(int i=0; i<len; i++){
        for(int j=1; j<len-i; j++){
            if(arr[j-1] > arr[j]){
                swap(arr[j-1], arr[j]);
                swapped = true;
            }
        }
        if(!swapped) break;	// 如果没有产生交换,证明目前数组已有序
    }
}

2. 选择排序

关键点:将剩余数组中的最大值(最小值)交换到尾部(首部)

详细原理见https://www.runoob.com/w3cnote/selection-sort.html

此处以将最小元素交换到首部为例:

template<typename T>

void selection_sort(vector<T>& arr, int len){
    for(int i=0; i<len; i++){
        for(int j=i+1; j<len; j++){
            if(arr[i] > arr[j])
                swap(arr[i], arr[j]);
        }
    }
}

3. 插入排序

关键点:数组分成已排序 / 未排序两部分,将未排序部分的元素插入已排序部分。

详细原理见https://www.runoob.com/w3cnote/insertion-sort.html

template<typename T>

void insert_sort(vector<T>& arr, int len){
    for(int i=1; i<len; i++){
        for(int j=i; j>0; j--){
            if(arr[j-1]>arr[j])	// 以交换的方式进行插入
                swap(arr[j-1], arr[j]);
            else
                break;
        }
    }
}

4. 希尔排序

关键点:数组按照某个增量分成子序列,每个子序列进行插入排序,逐步减少增量值。

详细原理见https://www.runoob.com/w3cnote/shell-sort.html以及参考链接【3】

template<typename T>
void shell_sort(vector<T>& arr, int len){
    int gap = 1;    // 增量间隔
    while(gap<len/3)  gap = gap*3+1;    // 为3的倍数,也可以为其他值
    while(gap>=1){
        // 分组进行插入排序
        for(int i=gap; i<len; i++){
            for(int j=i; j>=gap && arr[j-gap]>arr[j]; j-=gap)
                swap(arr[j-gap], arr[j]);
        }
        gap = gap/3;	// 缩小增量
    }
}

5. 归并排序

关键点:递归的思想,将两个有序数组归并为一个有序数组。

详细原理见https://www.runoob.com/w3cnote/merge-sort.html

template<typename T>
// 将两个有序数组归并为一个有序数组 (arr[left,mid] arr[mid+1, right])
void merge(vector<T>& arr, int left, int right, int mid){
	// 额外开辟存储空间
    vector<T> left_arr(arr.begin()+left, arr.begin()+mid+1);
    vector<T> right_arr(arr.begin()+mid+1, arr.begin()+right+1);
    int leftIdx = 0, rightIdx = 0;
    for(int i=left; i<=right; i++){
        if((left_arr[leftIdx]<=right_arr[rightIdx] && leftIdx<left_arr.size()) || rightIdx>=right_arr.size()){
            arr[i] = left_arr[leftIdx];
            leftIdx++;
        }
        else{
            arr[i] = right_arr[rightIdx];
            rightIdx++;
        }
    }
}

template<typename T>
void merge_sort(vector<T>& arr, int left, int right){
    if(left>=right) return;
    int mid = (right-left)/2+left;
    merge_sort(arr, left, mid);
    merge_sort(arr, mid+1, right);
    merge(arr, left, right, mid);
}

6. 快速排序

关键点:利用递归的思想,找到一个参考点(基准),将小于基准的值放置左边,大于基准的值放置在右边。

详细原理见https://www.runoob.com/w3cnote/quick-sort-2.html

template<typename T>
 // 写得很妙,值得回味
int quick(vector<T>& arr, int low, int high){
    int pivot=arr[low];	// 选择基准点
    while(low<high){	
    	// 移动high指针
        while(arr[high]>=pivot && high>low)
            high--;
        arr[low]=arr[high];	// 交换
        // 移动low指针
        while(arr[low]<=pivot && high>low)
            low++;
        arr[high]=arr[low];	// 交换
    }
    arr[low]=pivot;  // 最后的空位放上基准值
    return low;
}

template<typename T>
void quick_sort(vector<T>& arr, int low, int high){
    if(low>=high) return;
    int pivot = quick(arr, low, high);
    // 经过一次quick arr[low:pivot-1]全小于arr[pivot]
    quick_sort(arr, low, pivot-1);
    // arr[low:pivot-1]全大于arr[pivot]
    quick_sort(arr, pivot+1, high);
}

7. 堆排序

关键点:构建大顶/小顶堆,然后将堆顶元素与末尾元素交换后(类似堆删除操作),再继续将剩余元素堆化。

详细原理见https://www.runoob.com/w3cnote/heap-sort.html
大顶堆:每个节点的值都大于或等于其子节点的值

此处以大顶堆为例:

template<typename T>
// 构造大顶堆
void max_heapify(vector<T>& arr, int i, int len){
    int j = i*2+1;
    while(j < len){
		// 选择最大的子节点
        if(j+1<len && arr[j]<arr[j+1])
            j++;
    	// 如果当前节点比子节点大,结束
        if(arr[i]>arr[j])
            break;
        // 比子节点小,交换后,继续与子节点比较
        swap(arr[i], arr[j]);
        i = j;
        j = 2*i+1;
    }
}

template<typename T>
void heap_sort(vector<T>& arr, int len){
	// 自底向上构造大顶堆
    for(int i=len/2-1; i>=0; i--)
        max_heapify(arr, i, len);
    // 堆排序
    for(int i=0; i<len; i++){
        // 将当前堆顶元素移除(最大元素)与数组末尾元素交换
        swap(arr[0], arr[len-1-i]);
        // 重新构造大顶堆,找到剩余堆中的最大元素
        max_heapify(arr, 0, len-1-i);
    }
}

8. 计数排序

关键点:构造计数数组,统计出现频次。为了稳定排序:需要将统计数组进行变形。

详细原理见https://www.runoob.com/w3cnote/counting-sort.html 以及参考链接【4】

template<typename T>
vector<T> count_sort(vector<T>& arr, int len){
    if(len < 0)
        return {0};
    // 1. 构建计数数组
    int _min, _max, d;
    _min=_max=arr[0];
    for(int i=1; i<len; i++){
        if(arr[i] > _max)   _max=arr[i];
        if(arr[i] < _min)   _min=arr[i];
    }
    d = _max-_min+1;
    vector<T> countArr(d, 0);
    // 2. 计数
    for(int i=0; i<len; i++){
        countArr[arr[i]-_min]++;
    }
    // 3. 稳定排序: 数组变形
    for(int i=1; i<d; i++){
        countArr[i] += countArr[i-1];
    }
    // 4. 得到稳定的排序结果
    vector<T> res(len, 0);
    for(int i=len-1; i>=0; i--){
        res[countArr[arr[i]-_min]-1] = arr[i];
        countArr[arr[i]-_min]--;
    }

    return res;
}

9. 桶排序

关键点:基于计数排序的改进算法

详细原理https://www.runoob.com/w3cnote/bucket-sort.html

template<typename T>
void bucket_sort(vector<T>& arr, int len){
    if(len < 0) return;
    // 1. 找到最大最小值
    int _min, _max, d;
    _min=_max=arr[0];
    for(int i=1; i<len; i++){
        if(arr[i] > _max)   _max=arr[i];
        if(arr[i] < _min)   _min=arr[i];
    }
    d = _max-_min+1;
    // 2. 将元素分到5个桶中
    vector< vector<T> > bucket(5);
    int step = d/5+1;

    for(int i=0; i<len; i++){
        int idx = (arr[i]-_min)/step;
        bucket[idx].insert(bucket[idx].end(), arr[i]);
    }
    // 3. 排序每个桶内元素 - 这里采用快速排序,也可采用其他排序方法
    for(int i=0; i<5; i++){
        sort(bucket[i].begin(), bucket[i].end());
    }
    // 4. 组合
    int idx = 0;
    for(int i=0; i<5; i++){
        for(int j=0; j<bucket[i].size(); j++){
            arr[idx++] = bucket[i][j];
        }
    }
}

10. 基数排序

关键点:将元素按位数进行切分,每次对每一位进行排序。比较完所有位之后,数组即有序。

详细原理见https://www.runoob.com/w3cnote/radix-sort.html以及链接[5]
计数排序、桶排序、以及基数排序通常适用于整数排序。
分为LSD(Least significant digital)或MSD(Most significant digital)两种算法,一种是从最低位开始,另一种从最高位开始。

这里以LSD算法为例(用到了计数排序中变形数组的思想):

template<typename T>
 // 辅助函数:求数组中最大值的位数
int maxbit(vector<T>& arr, int len){
    int maxData = arr[0];
    for(int i=1; i<len; i++){
        if(maxData < arr[i])    maxData = arr[i];
    }

    int bit = 1;
    while(maxData > 9){
        bit++;
        maxData /= 10;
    }
    return bit;
}

template<typename T>
void radix_sort(vector<T>& arr, int len){
    int Bit = maxbit(arr, len);	// 确定最大位数
    vector<T> tmp(arr);	// 构造临时数组

    int radix = 1; // 从最低为开始
    for(int i=0; i<Bit; i++){ // 进行Bit次排序
        vector<int> count(10, 0); // 计数器 0~9
        for(int j=0; j<len; j++){
            count[(arr[j]/radix)%10]++;	// 找到相应的位数,计数
        }
        for(int j=1; j<10; j++){	// 转换为变形数组,确保每一次排序均为稳定排序
            count[j] += count[j-1];
        }
        for(int j=len-1; j>=0; j--){	// 从最后一位元素开始放置
            int k = (arr[j]/radix)%10;
            tmp[count[k]-1] = arr[j];
            count[k]--;
        }
        arr.assign(tmp.begin(), tmp.end()); // 交换临时数组
        radix *= 10;	// 往前进一位
    }
}

参考

[1] 菜鸟教程 https://www.runoob.com/w3cnote/ten-sorting-algorithm.html
[2] LeetCode 101 - A LeetCode Grinding Guide (C++ Version)
[3] 图解排序算法(二)之希尔排序 https://www.cnblogs.com/chengxiao/p/6104371.html
[4] 漫画:什么是计数排序? https://www.cxyxiaowu.com/5437.html
[5] 【算法】排序算法之基数排序
https://zhuanlan.zhihu.com/p/126116878

智能网联汽车的安全员高级考试涉及多个方面的专业知识,包括但不限于自动驾驶技术原理、车辆传感器融合、网络安全防护以及法律法规等内容。以下是针对该主题的一些核心知识解析: ### 关于智能网联车安全员高级考试的核心内容 #### 1. 自动驾驶分级标准 国际自动机工程师学会(SAE International)定义了六个级别的自动驾驶等级,从L0到L5[^1]。其中,L3及以上级别需要安全员具备更高的应急处理能力。 #### 2. 车辆感知系统的组成与功能 智能网联车通常配备多种传感器,如激光雷达、毫米波雷达、摄像头和超声波传感器等。这些设备协同工作以实现环境感知、障碍物检测等功能[^2]。 #### 3. 数据通信与网络安全 智能网联车依赖V2X(Vehicle-to-Everything)技术进行数据交换,在此过程中需防范潜在的网络攻击风险,例如中间人攻击或恶意软件入侵[^3]。 #### 4. 法律法规要求 不同国家和地区对于无人驾驶测试及运营有着严格的规定,考生应熟悉当地交通法典中有关自动化驾驶部分的具体条款[^4]。 ```python # 示例代码:模拟简单决策逻辑 def decide_action(sensor_data): if sensor_data['obstacle'] and not sensor_data['emergency']: return 'slow_down' elif sensor_data['pedestrian_crossing']: return 'stop_and_yield' else: return 'continue_driving' example_input = {'obstacle': True, 'emergency': False, 'pedestrian_crossing': False} action = decide_action(example_input) print(f"Action to take: {action}") ``` 需要注意的是,“同学”作为特定平台上的学习资源名称,并不提供官方认证的标准答案集;建议通过正规渠道获取教材并参加培训课程来准备此类资格认证考试
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值