<使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理>
文章目录
前言:在笔者学习的不同时期阶段,曾遇到多种冒泡排序算法的实现形式,这些形式纷繁多样,在一定时期阶段带给笔者些许疑惑,然而通过深入学习理解之后,发现其本质却都满足冒泡排序算法的思想和基本定义。对其归纳总结所得四种冒泡排序算法实现形式使用生成式AI进行分析解释探讨整理。
1.冒泡排序实现形式总结
cpp
//冒泡排序实现1------不常用(这个版本两层for循环分别是i++、j++)
void bubbleSort(int A[], int n) {
int i, j, temp; // 声明三个整型变量i、j和temp用于循环控制和交换元素
for (i = 0; i < n - 1; i++) { // 外层循环,从数组的第一个元素开始遍历到倒数第二个元素
for (j = i+1; j < n; j++) { // 内层循环,从当前外层循环位置的下一个元素开始遍历到最后一个元素
if (A[i] > A[j]) { // 如果当前元素大于下一个元素
temp = A[i]; // 将当前元素存储到临时变量temp中
A[i] = A[j]; // 将下一个元素赋值给当前元素
A[j] = temp; // 将临时变量temp的值(原当前元素的值)赋值给下一个元素
}
}
}
}
//冒泡排序实现2------王道考研指导书及课件(这个版本两层for循环分别是i++、j--)
//交换
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
//冒泡排序
void bubbleSort(int A[], int n) {
for (int i = 0; i < n - 1; i++) {
bool flag = false; // 表示本趟冒泡是否发生交换的标志
for (int j = n - 1; j > i; j--) { // 一趟冒泡过程
if (A[j - 1] > A[j]) { // 若为逆序
swap(A[j - 1], A[j]); // 交换
flag = true;
}
}
if (flag == false)
return; // 本趟遍历后没有发生交换,说明表已经有序
}
}
//冒泡排序实现3------不常用(这个版本两层for循环分别是i--、j++)
void bubbleSort(int A[], int n) {
for (int i = n - 1; i > 1; i--) {
for (int j = 1; j < i; j++) {
if (A[j] > A[j + 1]) {
int temp = A[j];
A[j] = A[j + 1];
A[j + 1] = temp;
}
}
}
}
//冒泡排序实现4------典型(或标准)冒泡排序,未优化(这个版本两层for循环分别是i++、j++)
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
// 一趟冒泡排序的过程
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
//注:上述除了实现2之外,均为未优化的冒泡排序
//实现2 在冒泡排序的基础上增加了一个优化,即通过标志位 flag 来判断在某一趟排序过程中是否发生了元素交换。如果在某趟排序过程中没有发生任何交换,说明数组已经有序,可以提前结束排序过程。这种优化在最好情况下(输入数组已经有序)可以将时间复杂度降低到 O(n),但在最坏情况下(逆序数组)时间复杂度仍然是 O(n^2)。即总结:A.包含了一个早期退出机制(flag变量),可以在数组已经有序的情况下提前结束排序。B:减少了不必要的比较和交换操作。
//相比之下,实现1、3和4都是一般的冒泡排序或其变种实现形式,它们的时间复杂度在所有情况下都是 O(n^2)。因此,从理论上讲,实现2在最好情况下比实现1、3和4更优。
//然而,在实际应用中,这种优化带来的性能提升可能并不明显,因为大多数情况下输入数据不会是完全有序的。此外,这种优化也增加了代码的复杂性。因此,是否使用实现2 取决于具体的应用场景和需求。在某些情况下,简单的标准冒泡排序可能已经足够满足需求。
//空间复杂度:所有的实现都具有O(1)的空间复杂度,因为它们只使用了常数级的额外空间。
1.1关于冒泡排序实现形式1的来源:
通义千问回答:
该解释比较有说服力。
讯飞星火回答:
该解释有些不全面。
ChatGPT-4o回答:
该解释比较有说服力(与通义千问解释近似)。
1.2对四种排序实现形式使用AI进行无引导分析:
cpp//排序实现1 void Sort(int A[], int n) { int i, j, temp; // 声明三个整型变量i、j和temp用于循环控制和交换元素 for (i = 0; i < n - 1; i++) { // 外层循环,从数组的第一个元素开始遍历到倒数第二个元素 for (j = i+1; j < n; j++) { // 内层循环,从当前外层循环位置的下一个元素开始遍历到最后一个元素 if (A[i] > A[j]) { // 如果当前元素大于下一个元素 temp = A[i]; // 将当前元素存储到临时变量temp中 A[i] = A[j]; // 将下一个元素赋值给当前元素 A[j] = temp; // 将临时变量temp的值(原当前元素的值)赋值给下一个元素 } } } } //排序实现2 //交换 void swap(int &a, int &b) { int temp = a; a = b; b = temp; } //排序 void Sort(int A[], int n) { for (int i = 0; i < n - 1; i++) { bool flag = false; for (int j = n - 1; j > i; j--) { if (A[j - 1] > A[j]) { // 若为逆序 swap(A[j - 1], A[j]); // 交换 flag = true; } } if (flag == false) return; // 本趟遍历后没有发生交换,说明表已经有序 } } //排序实现3 void Sort(int A[], int n) { for (int i = n - 1; i > 1; i--) { for (int j = 1; j < i; j++) { if (A[j] > A[j + 1]) { int temp = A[j]; A[j] = A[j + 1]; A[j + 1] = temp; } } } } //排序实现4 void Sort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } 请详细分析上述四个算法实现,分别逐行解释?分别属于哪种排序?并分析时间复杂度和空间复杂度?以及各自的优缺点,总结和对比四种算法实现。
ChatGPT-4o:
该分析较为有说服力!但对算法类型的总结有误!
讯飞星火:
该解释尚可!主要不够详细!
通义千问:
该解释尚可!但其认为实现1为选择排序,然后询问其"是否满足选择排序的定义"之后,又给出前后矛盾的解释!
对于上述四种实现形式三种AI给出的解释分析总结:
(1)通义千问和ChatGPT-4o首先认为实现1为选择排序的变种,在质问"符合哪种排序定义"之后,两者均改称其为冒泡排序的变种,给出的解释前后矛盾,多次从不同角度询问,其回答左右摇摆!
(2)讯飞星火坚称四种实现均属于冒泡排序,只是实现的形式不同。(注:这些实现形式是笔者在不同的学习阶段遇到,在此汇总探讨整理,笔者较认可讯飞星火的解释)
一些关于冒泡排序不同实现形式在不同AI的分析回答详情:(链接如下)
ChatGPT-4o(科学上网打开)-关于冒泡排序几种不同实现分析2024/10/01
注:上述问答是笔者在进行多次实验之后汇总整理(即经过多次实验),可以感受到笔者被其分析解释的前后矛盾,左右摇摆带来的无奈与折腾。
1.3AI(通义千问)给出的传统冒泡排序和优化后的冒泡排序
cpp
//传统冒泡排序
//传统冒泡排序的基本思想是通过多次遍历数组,每次将相邻的两个元素进行比较并交换,使得每一轮遍历后最大的元素"冒泡"到数组的末尾。
#include <iostream>
using namespace std;
// 传统冒泡排序
void BubbleSortTraditional(int A[], int n) {
for (int i = 0; i < n - 1; i++) { // 外层循环,从第一个元素到倒数第二个元素
for (int j = 0; j < n - 1 - i; j++) { // 内层循环,从第一个元素到未排序部分的倒数第二个元素
if (A[j] > A[j + 1]) { // 如果当前元素大于下一个元素
// 交换
int temp = A[j];
A[j] = A[j + 1];
A[j + 1] = temp;
}
}
}
}
// 主函数,用于测试排序功能
int main() {
int A[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(A) / sizeof(A[0]);
cout << "Original array: ";
for (int i = 0; i < n; ++i) {
cout << A[i] << " ";
}
cout << endl;
BubbleSortTraditional(A, n);
cout << "Sorted array: ";
for (int i = 0; i < n; ++i) {
cout << A[i] << " ";
}
cout << endl;
return 0;
}
//优化后的冒泡排序
//优化后的冒泡排序增加了早期退出机制。如果在某一轮遍历中没有发生任何交换,说明数组已经有序,可以提前结束排序。
#include <iostream>
using namespace std;
// 交换函数
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
// 优化后的冒泡排序
void BubbleSortOptimized(int A[], int n) {
for (int i = 0; i < n - 1; i++) { // 外层循环,从第一个元素到倒数第二个元素
bool swapped = false; // 标志变量,用于检测本轮是否有交换
for (int j = 0; j < n - 1 - i; j++) { // 内层循环,从第一个元素到未排序部分的倒数第二个元素
if (A[j] > A[j + 1]) { // 如果当前元素大于下一个元素
swap(A[j], A[j + 1]); // 交换
swapped = true; // 设置标志变量
}
}
if (!swapped) { // 如果本轮没有发生交换,说明数组已经有序
break; // 提前退出
}
}
}
// 主函数,用于测试排序功能
int main() {
int A[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(A) / sizeof(A[0]);
cout << "Original array: ";
for (int i = 0; i < n; ++i) {
cout << A[i] << " ";
}
cout << endl;
BubbleSortOptimized(A, n);
cout << "Sorted array: ";
for (int i = 0; i < n; ++i) {
cout << A[i] << " ";
}
cout << endl;
return 0;
}
观察发现主要算法逻辑是不是同实现4一样? 因为即使是其它AI的解释答案,也是建立在网络搜索基础之上...
总结:应该按照各种排序的定义区分相应排序类型。俗话说:"尽信书,不如无书",迁移到此处就是"尽信AI,不如多理解"。在当今,其实应该辩证的使用AI工具,不能全然依托AI,而是将其作为辅助,同时自身也要多去学习,提升自身的判断和理解。
后记:由于时间仓促,上述探讨在整理过程中亦有思维逻辑有误之处,然而在计算机这类学科中,"定义"不是绝对的衡量标尺,笔者更是在计算机研究生全国统考408的《数据结构》、《操作系统》、《计算机网络》、《计算机组成原理》的学习中就有深刻体会。因此,世间纷繁、事事接踵绵延,应抓住主要矛盾,有时切不能再耗费大量精力在一些琐事之上了...此刻已是10月1日凌晨3点33分。今天正值祖国成立75周年,笔者在此向祖国致以最诚挚最衷心的祝福与敬意!向无数前辈致以崇高的敬意!中华人民共和国万岁!世界人民大团结万岁!