【C++数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】

简介: 快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。(第一行是元素个数,第二行是待排序的原始关键字数据。本关任务:实现快速排序算法。开始你的任务吧,祝你成功!

目录😋

任务描述

相关知识

1. 快速排序算法的基本原理

2. 快速排序算法步骤

3. 代码示例(以 C++ 为例)

4. 时间复杂度和空间复杂度

测试说明

通关代码

测试结果


任务描述

本关任务:实现快速排序算法

相关知识

为了完成本关任务,你需要掌握:

  1. 快速排序算法的基本原理
  2. 快速排序算法步骤
  3. 代码示例(以 C++ 为例)
  4. 时间复杂度和空间复杂度

1. 快速排序算法的基本原理

快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。

2. 快速排序算法步骤

  • 选择基准元素:可以有多种选择基准元素的方式。常见的有选择数组的第一个元素、最后一个元素或者中间元素等。例如,在简单的实现中,常常选择数组的第一个元素作为基准元素。
  • 划分操作(Partition)
  • 设定两个指针,一个从数组的左边开始(通常称为左指针),一个从数组的右边开始(称为右指针)。
  • 左指针向右移动,直到找到一个大于基准元素的元素;同时,右指针向左移动,直到找到一个小于基准元素的元素。
  • 当左指针和右指针都停止移动后,如果左指针小于右指针,就交换它们所指向的元素。
  • 持续进行上述操作,直到左指针和右指针相遇或者交叉。此时,将基准元素与左指针(此时左指针和右指针相遇的位置)所指向的元素交换。这样就完成了一次划分,使得基准元素左边的元素都小于等于它,右边的元素都大于等于它。
  • 递归排序:对划分后的两部分子数组(基准元素左边的和右边的)分别进行快速排序。这个过程是递归的,即对每个子数组重复选择基准元素、划分和递归排序的步骤,直到子数组的长度为 1 或者 0(这种情况下子数组已经是有序的)。

3. 代码示例(以 C++ 为例)

#include <iostream>
#include <vector>
using namespace std;
// 划分函数
int partition(vector<int>& arr, int low, int high) {
    int pivot = arr[low];
    int i = low + 1;
    int j = high;
    while (true) {
        while (i <= j && arr[i] <= pivot) i++;
        while (i <= j && arr[j] > pivot) j--;
        if (i > j) break;
        swap(arr[i], arr[j]);
    }
    swap(arr[low], arr[j]);
    return j;
}
// 快速排序函数
void quickSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        int pivotIndex = partition(arr, low, high);
        quickSort(arr, low, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, high);
    }
}
image.gif

在上述代码中:

  • partition函数实现了划分操作。它以arr[low]作为基准元素,通过两个while循环和指针ij的移动来找到需要交换的元素,最后交换基准元素和j所指元素,返回划分后的基准元素位置。
  • quickSort函数是快速排序的主函数,它首先调用partition函数进行划分,然后递归地对划分后的两部分子数组进行排序。

4. 时间复杂度和空间复杂度

  • 时间复杂度
  • 在最好情况下,每次划分都能将数组均匀地分成两部分,时间复杂度为 ,其中 是数组中的元素个数。
  • 在最坏情况下,例如数组已经是有序的(选择第一个元素作为基准元素),每次划分得到的两个子数组长度分别为 ,时间复杂度为
  • 平均时间复杂度是
  • 空间复杂度:快速排序是递归实现的,需要栈空间来保存递归调用的信息。在最好情况下,空间复杂度为 ,因为递归树的高度为。在最坏情况下,空间复杂度为 ,例如数组已经是有序的情况。平均空间复杂度是

测试说明

平台会对你编写的代码进行测试:

测试输入:(第一行是元素个数,第二行是待排序的原始关键字数据。)

10
6 8 7 9 0 1 3 2 4 5
image.gif

预期输出:

排序前:6 8 7 9 0 1 3 2 4 5 
第1次划分:  5  4  2  3  0  1  6  9  7  8
第2次划分:  1  4  2  3  0  5
第3次划分:  0  1  2  3  4
第4次划分:        2  3  4
第5次划分:           3  4
第6次划分:                       8  7  9
第7次划分:                       7  8
排序后:0 1 2 3 4 5 6 7 8 9
image.gif

开始你的任务吧,祝你成功!


通关代码

#include <malloc.h>
#include <stdio.h>
#define MAXL 100     //最大长度
typedef int KeyType; //定义关键字类型为int
typedef char InfoType;
typedef struct {
  KeyType key;   //关键字项
  InfoType data; //其他数据项,类型为InfoType
} RecType;       //查找元素的类型
void CreateList(RecType R[], KeyType keys[], int n) //创建顺序表
{
  for (int i = 0; i < n; i++) // R[0..n-1]存放排序记录
    R[i].key = keys[i];
}
void DispList(RecType R[], int n) //输出顺序表
{
  for (int i = 0; i < n; i++)
    printf("%d ", R[i].key);
  printf("\n");
}
//显示一趟划分后的结果
void disppart(RecType R[], int s, int t) {
  /********** Begin *********/
  for (int i = 0; i < s; i++)
    printf("    ");
  for (int i = s; i <= t; i++)
    printf("%3d ", R[i].key);
  printf("\n");
  /********** End **********/
}
//一趟划分
int partition(RecType R[], int s, int t) {
  /********** Begin *********/
  KeyType pivot = R[s].key; // 从 RecType 中提取 key 字段
  while (s < t) {
    while (s < t && R[t].key >= pivot)
      t--;
    R[s] = R[t];
    while (s < t && R[s].key <= pivot)
      s++;
    R[t] = R[s];
  }
  R[s].key = pivot; // 将 pivot 的值赋回 R[s].key
  return s;
  /********** End **********/
}
//对R[s..t]的元素进行递增快速排序
void QuickSort(RecType R[], int s, int t, int *count) {
  /********** Begin *********/
  int pivotpos;
  if (s < t) {
    (*count)++;                      // 增加划分次数
    printf("第%d次划分:", *count); // 输出划分次数提示信息
    pivotpos = partition(R, s, t);
    disppart(R, s, t);
    QuickSort(R, s, pivotpos - 1, count);
    QuickSort(R, pivotpos + 1, t, count);
  }
  /********** End **********/
}
int main() {
  /********** Begin *********/
  int n;
  scanf("%d", &n);
  KeyType keys[MAXL];
  RecType R[MAXL];
  for (int i = 0; i < n; i++)
    scanf("%d", &keys[i]);
  CreateList(R, keys, n);
  printf("排序前:");
  DispList(R, n);
  int count = 0; // 初始化划分次数
  QuickSort(R, 0, n - 1, &count);
  printf("排序后:");
  DispList(R, n);
  /********** End **********/
  return 0;
}

image.gif


测试结果

image.gif

image.gif

目录
相关文章
|
3月前
|
存储 缓存 监控
用 C++ 红黑树给公司电脑监控软件的日志快速排序的方法
本文介绍基于C++红黑树算法实现公司监控电脑软件的日志高效管理,利用其自平衡特性提升日志排序、检索与动态更新效率,并结合实际场景提出优化方向,增强系统性能与稳定性。
149 4
|
7月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
203 17
|
8月前
|
算法 搜索推荐
快速排序-数据结构与算法
快速排序(Quick Sort)是一种基于分治法的高效排序算法。其核心思想是通过选择基准(pivot),将数组划分为左右两部分,使得左侧元素均小于基准,右侧元素均大于基准,然后递归地对左右两部分进行排序。时间复杂度平均为 O(n log n),最坏情况下为 O(n²)(如数组已有序)。空间复杂度为 O(1),属于原地排序,但稳定性不佳。 实现步骤包括编写 `partition` 核心逻辑、递归调用的 `quickSort` 和辅助函数 `swap`。优化方法有随机化基准和三数取中法,以减少最坏情况的发生。
593 13
|
11月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
560 77
|
11月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
252 19
|
10月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
6月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
191 0
|
6月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
284 0
|
8月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
333 12
|
9月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
190 16