冒泡排序与动态数组vector简单应用——c++

本文详细解释了冒泡排序算法的工作原理,并展示了如何在C++中使用std::vector动态数组进行操作,包括构造、初始化、元素访问和排序。还介绍了模板编程和引用的概念在排序函数中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

冒泡排序详解

Vector——c++动态数组标准库

源代码及其详解


冒泡排序详解

什么是冒泡排序呢?

        它是通过多次遍历和比较,将最大(或最小)的元素逐步冒泡到最右(或最左)的位置,类似于水泡上升的过程,因此得名"冒泡"排序。

 具体步骤是这样的:

  1. 从数组的第一个元素开始,依次比较相邻的两个元素。
  2. 如果前一个元素大于后一个元素,交换它们的位置,使得较大的元素排在后面。
  3. 继续向下遍历,对剩余的元素进行相同的比较和交换,直到达到倒数第二个元素。
  4. 重复以上步骤,直到整个数组排序完成,即没有发生任何交换操作。

但是这样子还是不太好理解怎么办,可以继续往下看,理解冒泡的过程:

初始状态:

5 *****

3 ***

8 ********

4 ****

2 **

第一轮遍历,比较并交换元素:(经过第一轮遍历,最长的8被冒到了最后一位)

3 ***

5 *****

4 ****

2 **

8 ********

第二轮遍历,比较并交换元素:(经过第二轮遍历,第二长的5被冒到了倒数第二位)

3 ***

4 ****

2 **

5 *****

8 ********

第三轮遍历,比较并交换元素:(第三轮是4)

3 ***

2 **

4 ****

5 *****

8 ********

最终排序完成:(最后一轮是3)

2 **

3 ***

4 ****

5 *****

8 ********

 如此一来,长度为5的序列经过四轮遍历后被成功排序

那么在每一轮遍历中,经过了几次比较呢?

我们发现,第一轮遍历需要比较的次数是4次,第二轮是三次,第三轮是两次,第四轮是一次

简单总结是,遍历次数i,比数字个数n小一,也就是i= n-1;

每一轮遍历时比较的次数是未被排序的数字个数(n-i)*减一,也就相当于是i-1

*:每一次遍历过后,都有一个数字归位!所以未排列的数字个数是n-i

那么上源代码:

int temp = 0;//这个用于后续进行换位时,临时存储其中一个元素
for (int i = 0; i < n - 1; i++) 
{
	for (int j = 0; j < n - i - 1; j++) 
	{
		if (arr[j] > arr[j + 1]) 
		{
            //如果前一个数比后一个数大,就交换他们的位置
			temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
		}
	}
}

使用for循环嵌套完成核心代码的书写(也可以使用别的) 

如果实在看不懂,可以找一找动画过程,或者看看视频,b站上黑马程序员的c++视频有解释,讲的很细。

Vector——c++动态数组标准库

额,为什么会用到这个呢?

好问题,我在开始写的时候,就想着我能直接在控制台直接输入数字,所以就有了它!!!

        Vector是 C++ 标准库中的一个类模板,用于实现动态数组,提供了许多便捷的函数和方法来处理数组。

下面是 std::vector 常用的一些函数和方法:

  1. 构造和初始化:

    • std::vector<T> v:创建一个空的 std::vector,其中 T 是存储在 vector 中的元素类型。
    • std::vector<T> v(n):创建一个包含 n 个默认构造的元素的 std::vector
    • std::vector<T> v(n, value):创建一个包含 n 个值为 value 的元素的 std::vector
  2. 大小和容量:

    • v.size():返回 v 中元素的数量。
    • v.empty():检查 v 是否为空。
    • v.capacity():返回 v 可以容纳的元素数量。
    • v.reserve(n):请求将 v 的容量增加到至少 n
  3. 访问元素:

    • v[index]:返回位于索引 index 处的元素的引用。
    • v.at(index):返回位于索引 index 处的元素的引用,并会进行越界检查。
    • v.front():返回 v 的第一个元素的引用。
    • v.back():返回 v 的最后一个元素的引用。
  4. 添加和删除元素:

    • v.push_back(value):在 v 的末尾添加一个值为 value 的元素。
    • v.pop_back():从 v 的末尾移除最后一个元素。
    • v.insert(iterator, value):在 iterator 所指向的位置之前插入一个值为 value 的元素。
    • v.erase(iterator):从 v 中移除由 iterator 指定的元素。
  5. 其他常用操作:

    • v.clear():移除 v 中的所有元素。
    • v.swap(other):交换 v 与 other 中的元素。

 这只是一些最基础的常用函数,差不多够用。

源代码及其详解

#include<iostream>
#include<vector>//引用c++动态数组标准库
using namespace std;

/*
冒泡排序算法
*/

template<typename T>
void bubble_sort(vector<T>& arr) 
{
    int n = arr.size();  // 数组大小
    bool swapped;  // 标记是否发生过交换
    for (int i = 0; i < n - 1; i++) 
    {
        swapped = false;  // 每一轮开始时,标记为未发生交换
        for (int j = 0; j < n - i - 1; j++) 
        {
            if (arr[j] > arr[j + 1])  // 如果前一个元素大于后一个元素
            {
                swap(arr[j], arr[j + 1]);  // 交换两个元素的位置
                swapped = true;  // 有发生交换,标记为已交换
            }
        }
        if (!swapped) break;  // 如果本轮没有发生交换,提前结束排序
    }
}

int main() 
{
    vector<int> arr;  // 存储输入数字的向量
    int num = 0;

    cout << "请输入一串数字(以空格或换行分隔,输入非数字结束):" << endl;

    while (cin >> num)
    {
        arr.push_back(num);  // 将输入的数字添加到向量中
    }

    cout << "输入的数字为:";
    for (auto x : arr)
    {
        cout << x << " ";  // 输出输入的数字
    }
    cout << endl;

    bubble_sort(arr);  // 对输入的数字进行冒泡排序

    cout << "排序后的数字为:";
    for (auto x : arr)
    {
        cout << x << " ";  // 输出排序后的数字
    }
    cout << endl;

    return 0;
}

template<typename T>

函数模板的声明语法,其中 typename T 是函数模板的类型参数,表示在函数定义中可以使用一个名为 T 的类型。这是一种泛型编程技术,也称为模板编程。

通过使用函数模板,我们可以编写一次代码,然后用不同类型的参数调用该函数,从而对多种类型的数据都进行相同的操作。在冒泡排序的例子中,我们使用了模板类型 T,以便让函数可以处理任意类型的数组(整数、浮点数、字符等)。

在函数模板的定义中,我们使用类型参数 T 代替实际的类型,但是在函数模板调用时,需要将实际类型作为参数传递给函数。例如,在冒泡排序的例子中,我们可以这样调用函数:

std::vector<int> arr = {3, 5, 2, 8, 4, 7};

bubble_sort<int>(arr);

这里的 <int> 指定了类型参数 T 的实际类型为 int,从而在函数调用中对 arr 数组进行排序。

使用函数模板可以增加代码的重用性和灵活性,减少代码的冗余,提高程序的可读性和可维护性。


void bubble_sort(vector<T>& arr) 

& 是引用符号。它表示函数的参数 arr 是一个对类型为 std::vector<T> 的对象的引用。

通过将参数声明为引用,可以避免在函数调用时进行对象的复制,从而提高程序的效率。引用允许我们在函数内部直接操作传递给函数的原始对象,而不是副本。

在冒泡排序的例子中,std::vector<T>& arr 表示 arr 是一个引用,指向类型为 std::vector<T> 的对象。当我们在函数内部修改 arr 时,实际上就是在修改传递给函数的原始数组。

使用引用作为函数参数还可以使得函数能够修改传入的对象,而不仅仅是对副本进行操作。这样,函数执行后,原始对象的内容也会发生变化。

总而言之,& 是引用符号,用于将参数声明为对原始对象的引用,从而避免对象的复制,并使函数能够修改传入的对象。

这个举个例子说就是:

int& element = v[2]; 这个语句将 v[2] 的引用赋值给了变量 element,而不是将其值赋值给 element

在这里使用 int& 是为了声明 element 为一个整数的引用,也就是说 element 将成为指向 v[2] 的引用。通过这样的声明,对 element 的修改将直接影响到 v[2]。反之亦然,对 v[2] 的修改也会影响到 element

以下是一个示例:

#include <iostream>
#include <vector>

int main() 
{
    std::vector<int> v = {1, 2, 3, 4, 5};
  
    int& element = v[2];
    element = 10;  // 修改 element,也同时修改了 v[2]
  
    std::cout << v[2] << std::endl;  // 输出修改后的值,即 10
    std::cout << element << std::endl;  // 输出修改后的值,即 10

    return 0;
}

输出结果将会是:

10

10

因此,引用的声明可以让我们通过改变引用来直接修改底层变量的值。在这种情况下,element 成为了 v[2] 的别名。


最后插一嘴,vs还是安装在c盘比较好,我换成其他盘之后,每次调式都要卡好一会,之前一下子就好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值