跟着Datawhale重学数据结构与算法(3)---排序算法

开源链接:【 教程地址 】【电子网站】
【写博客的目的是记录自己学习过程,方便自己复盘,专业课复习】

数组排序:

数组排序
冒泡排序
选择排序
插入排序
归并排序
希尔排序
快速排序
堆排序
计数排序
桶排序
基数排序

1.冒泡排序

1.1 算法思想
其实就跟线性代数里面的求逆序数的思想一样,ε=(1234),数当前数字与前面数字相比,有几个比它大的,然后加起来就是交换次数。
ε=(2143)=0+1+0+1=2,那么要成从小打到排列就要交换2次。
冒泡排序的本质也是两两交换,再用上面举例子,最坏的情况是ε=(4321)=0+1+2+3=6,我得交换6次。
step1:把最大的元素通过交换到正确的位置,我需要交换三次。
step2:把第二大元素通过俩俩交换到正确的位置,我需要交换俩次。
......
last step:最后剩的那个元素就是最小元素,无需排序,因此就完成了排序。
你可以推广到n个元素的排序。

可见,如果我们要用程序实现这个算法,就要使用双层循环,内层循环用来保证每次将一个元素放到对的位置,外层循环用来遍历整个数组。

1.2代码实现:
/* 冒泡排序 */
void swap(int &a, int &b) {int temp = a;a = b;b = temp;
}
void bubbleSort(vector<int> &nums) {for (int i = nums.size() - 1; i > 0; i--) {for (int j = 0; j < i; j++) {if (nums[j] > nums[j + 1]) {swap(nums[j], nums[j + 1]);}}}
}
1.3 优化代码

我们想这样一个问题,假如还是上面那个逆序数,如果这个数组他没有排好,那么他一定存在一个元素会发生交换操作,那反过来说,(逆否命题也成立),如果元素没有发生交换,那么说明这个数组元素是排列好了的。因此,我们就可以用一个bool变量放在内层交换函数后面,如果发生了交换,那么继续,如果没发生交换,那么直接退出for循环。

/* 冒泡排序 */
void swap(int &a, int &b) {int temp = a;a = b;b = temp;
}
void bubbleSortWithFlag(vector<int> &nums) {for (int i = nums.size() - 1; i > 0; i--) {bool flag = false; // 初始化标志位for (int j = 0; j < i; j++) {if (nums[j] > nums[j + 1]) {swap(nums[j], nums[j + 1]);flag = true;}}if (!flag)break; // 此轮“冒泡”未交换任何元素,直接跳出}
}

要掌握的一些排序算法的复杂度:
在这里插入图片描述

2.选择排序

2.1 算法思想
就是分为两个状态空间,一个是未排序的状态空间,一个是已经排序好的状态空间,原理也非常简单,就是
每轮从未排序的区间选择最小的元素放在排序好的空间的末尾。,直到未排序好的状态空间仅剩一个元素肯定为数组的最大值。
2.2 代码实现
void selectionSort(vector<int> &nums) {int n = nums.size();for (int i = 0; i < n - 1; i++) {int minIndex = i;for (int j = i + 1; j < n; j++) {if (nums[j] < nums[minIndex]) {minIndex = j;}}if (minIndex != i) {swap(nums[i], nums[minIndex]);}}
}

3.插入排序

3.1 算法思想
其实插入排序跟我们打扑克的时候整理扑克的时候很像,所以他的算法流程其实也很简单。
step1:先假设我们揭到的第一张牌为基准。
step2:从第二张牌开始,依次与基准进行比较将他插入到合适的位置,这样前两个元素就排好序了。
以此类推.......
依次将当前元素插入到已经排序好的子数组中。

插入排序的核心思想是不断地将当前元素插入到已经排序好的子数组中,以保持子数组的有序性。

3.2 代码实现:
void insertionSort(vector<int> &nums) {int n = nums.size();for (int i = 1; i < n; i++) {int key = nums[i]; // 当前待插入的元素int j = i - 1;//排序好的子序列个数// 将当前元素插入到已经排序好的子数组中的合适位置// 从后往前进行比较,不符合条件就插入到此位置中。while (j >= 0 && nums[j] > key) {nums[j + 1] = nums[j]; // 将比当前元素大的元素往后移动一个位置j--;}nums[j + 1] = key; // 插入当前元素到合适的位置}
}

4.归并排序

4.1 算法思想
分割数组:将待排序数组递归地分割成长度大约相等的两个子数组,直到每个子数组只包含一个元素为止。
合并数组:将两个有序的子数组合并成一个有序的数组。合并过程中,逐个比较两个子数组的元素,并将较小的元素放入临时数组中,直到两个子数组都被合并完毕。
重复步骤 1 和步骤 2:对分割后的子数组进行递归排序,并合并已经排序好的子数组,直到所有子数组都合并成一个完整的有序数组。

归并排序的时间复杂度为 O(n log n),其中 n 是待排序数组的大小。它的优点是稳定性好、适用于大型数据集和链表等数据结构。
我感觉这个方法很amazing。用到了递归的思想,分而治之,再合并。跟深度学习中的深度可分离卷积(Depthwise Convolution)或者说shuffleNet很像,其实就是分别卷积,然后再使用1x1卷积共享参数,保证信息的交流。算法是互通的,对于归并排序来说,先拆分着进行排序,然后再合并起来进行排序。
在这里插入图片描述

4.2 代码实现
// 合并两个有序数组
void merge(vector<int> &nums, int left, int mid, int right) {int n1 = mid - left + 1; // 左侧子数组的长度int n2 = right - mid; // 右侧子数组的长度// 创建临时数组用于存放合并后的结果vector<int> temp(n1 + n2);// 将左右两个子数组合并到临时数组中int i = left, j = mid + 1, k = 0;while (i <= mid && j <= right) {if (nums[i] <= nums[j]) {temp[k++] = nums[i++];} else {temp[k++] = nums[j++];}}// 将剩余的元素复制到临时数组中while (i <= mid) {temp[k++] = nums[i++];}while (j <= right) {temp[k++] = nums[j++];}// 将临时数组的元素复制回原数组中for (int p = 0; p < k; p++) {nums[left + p] = temp[p];}
}// 归并排序
void mergeSort(vector<int> &nums, int left, int right) {if (left < right) {int mid = left + (right - left) / 2;mergeSort(nums, left, mid); // 对左侧子数组进行排序mergeSort(nums, mid + 1, right); // 对右侧子数组进行排序merge(nums, left, mid, right); // 合并两个有序子数组}
}

5.希尔排序

5.1 算法思想

希尔排序是一种改进的插入排序算法,也称为缩小增量排序。它的基本思想是将待排序数组按一定步长进行分组,对每组进行插入排序,然后逐步减小步长,重复上述步骤,直到步长为 1,此时数组基本有序,最后再进行一次插入排序。

希尔排序的关键在于选择合适的步长序列,也就是元素间隔数gap,不同的步长序列会影响排序的效率。通常使用的步长序列有希尔原始提出的序列(逐步除以 2)、Hibbard 序列、Sedgewick 序列等。

我感觉哈其实有种层次聚类的意思,通过逐步减小步长来逐渐减小逆序对,来使得数组变得有序,来减少元素的移动次数。我记得层次聚类有种方法就是从大到小每次选择一个阈值进行聚类,逐渐减小阈值,直到所有样本都得到合理的分类即可。

5.2 代码实现
class Solution {
public:std::vector<int> shellSort(std::vector<int>& nums) {int size = nums.size();int gap = size / 2;while (gap > 0) {for (int i = gap; i < size; i++) {int temp = nums[i];int j = i;while (j >= gap && nums[j - gap] > temp) {nums[j] = nums[j - gap];j -= gap;}nums[j] = temp;}gap /= 2;}return nums;}std::vector<int> sortArray(std::vector<int>& nums) {return shellSort(nums);}
};

6.快速排序

6.1 算法思想
快速排序并不快,它使用分治策略来对一个数组进行排序。快速排序的基本思想是选择一个元素作为基准(pivot),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据要小,(左边比基准都小,右边比基准都大),然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。(由大到小)。

在这里插入图片描述

6.2 代码实现:
void quickSort(vector<int>& arr, int low, int high) {if (low < high) {// 选取基准元素为左端点int pivot = arr[low];int i = low;// 将小于基准的元素移动到分割点左侧,大于基准的元素移动到右侧for (int j = low + 1; j <= high; j++) {if (arr[j] < pivot) {i++;swap(arr[i], arr[j]);}}// 基准元素交换到中间swap(arr[low], arr[i]);// 递归排序分割点左侧和右侧的子数组quickSort(arr, low, i - 1);quickSort(arr, i + 1, high);}
}

7.堆排序

7.1 算法理解

堆得结构分为大根堆和小根堆,是一个完全二叉树(从形式上讲它是个缺失的的三角形,但所缺失的部分一定是右下角某个连续的部
分,最后那一行可能不是完整的)。大小根堆从字面意思上就可以理解为树从根节点向下排列的顺序。
在这里插入图片描述在这里插入图片描述
采用顺序结构(数组)的形式来表示完全二,能够chong分利用存储空间。

7.2 代码实现
//使用数组来存储大顶堆(左孩子和哟孩子都小于父节点)
//维护堆的性质:如果父节点不满足大顶堆的性质,那就将孩子的最大值与父节点进行交换
//这样就满足了大顶堆的性质,复杂度O(logn)
建堆:(变成大顶堆),复杂度O(n)//SO:首先将数列变成大顶堆的形式,然后自下而上,从底部与根节点进行交换,交换后底部元素
//从树中删除,删除完后放在数列的最右端,更新此时的二叉树和数组,然后继续将第二个元素与根节点
//元素进行交换,继续....直到树只剩一个元素,操作完成。
void heapify(int arr[], int n, int i){
/*数组,数组长度,待维护节点下标*/
int largest = i;
int lson = i*2 +1;
int rson = i*2 +2;if(lson < n && arr[largest] < arr[lson])largest = lson;
if(rson < n && arr[largest] < arr[rson])largest = rson;
if (largest !=i)
{swap(&arr[largest],&arr[i]);heapify(arr,n,largest);
}
}
//堆排序入口
void heap_sort(int arr[], int n){int i;//建堆for(i=n/2-1;i>=0;i--)heapify(arr,n,i);//排序for(i=n-1;i>0;i--){swap(&arr[i],&arr[0]);heapify(arr,i,0);}
}

稳定性:不稳定

8. 计数排序

8.1算法原理

通过计数而不是比较来进行排序,适用于范围比较小的整数序列
原始数组->计数数组->累计数组->最终结果
在这里插入图片描述

8.2 代码实现:
void counting_sort(int arr[], int len)
{if (len < 1) return;// 寻找最大的元素int max = arr[0];for (size_t i = 1; i < len; i++)if (arr[i] > max) max = arr[i];// 分配一个长度为max+1的数组存储计数,并初始化为0int *count = (int *)malloc(sizeof(int) * (max + 1));memset(count, 0, sizeof(int) * (max + 1));// 计数for (size_t i = 0; i < len; i++)count[arr[i]]++;// 统计计数的累计值for (size_t i = 1; i < max + 1; i++)count[i] += count[i - 1];// 创建一个临时数组保存结果int *output = (int *)malloc(sizeof(int) * len);// 将元素放到正确的位置上for (size_t i = 0; i < len; i++){output[count[arr[i]] - 1] = arr[i];count[arr[i]]--;}// 将结果复制回原数组for (size_t i = 0; i < len; i++)arr[i] = output[i];
}

9.桶排序

9.1 算法原理

将待排序的元素分到有限数量的桶中,每个桶再分别进行排序,最后将各个桶中的元素合并得到排序结果。桶排序的核心思想是将一组数据分割成一定数量的桶,然后对每个桶中的数据进行排序,最后将所有桶中的数据按照顺序合并起来。首先,初始化一个足够数量的空桶。然后遍历待排序的数组,将每个元素放入相应的桶中;对每个非空的桶中的元素进行排序(可以使用其他排序算法,如插入排序);按照桶的顺序将所有非空桶中的元素合并得到排序结果。

9.2 代码实现
// 桶排序函数
void bucketSort(vector<float>& arr) {int n = arr.size();// 创建足够数量的空桶vector<vector<float>> buckets(n);// 将每个元素放入相应的桶中for (int i = 0; i < n; ++i) {int bucket_index = n * arr[i];buckets[bucket_index].push_back(arr[i]);}// 对每个非空桶中的元素进行排序(这里使用插入排序)for (int i = 0; i < n; ++i) {sort(buckets[i].begin(), buckets[i].end());}// 合并所有非空桶中的元素得到排序结果int index = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < buckets[i].size(); ++j) {arr[index++] = buckets[i][j];}}
}

10.基数排序

10.1 算法思想

基数排序是一种非比较性的排序算法,它按照数字的位数来进行排序。基数排序可以分为 LSD最低位优先(Least Significant Digit)和 MSD最高位优先(Most Significant Digit)两种实现方式。下面我们来介绍 LSD 基数排序的算法思想和实现。

LSD 基数排序的基本思想是从数字的最低位(个位)开始,依次对数字进行排序,直到最高位(最高位数的位数)。在每一轮排序过程中,对所有数字根据当前位上的数字进行桶排序。通过这样的逐位排序过程,最终可以得到排好序的结果。
这个方法跟人类很像,我们通常来比较两个事物所有的特征(差异)来区分,LSD这个就相当于先从个位进行排序,个位排完后,在向上观察最大的特征差异,然后到第一位的比较。

10.2 代码实现
//代码也非常简单
void radixSort(vector<int>& arr) {// 获取数组中的最大值int max_num = *max_element(arr.begin(), arr.end());// LSD 基数排序for (int digit = 1; max_num / digit > 0; digit *= 10) {// 创建 10 个桶,用于存放当前位上的数字vector<vector<int>> buckets(10);// 将数组中的元素按照当前位上的数字放入相应的桶中for (int num : arr) {int digit_value = getDigit(num, digit);buckets[digit_value].push_back(num);}// 从桶中按顺序取出元素,替换原数组中的元素int index = 0;for (auto& bucket : buckets) {// auto 是为了让编译器自动推导出 bucket 的类型for (int num : bucket) {arr[index++] = num;}bucket.clear();}}
}

Leetcode之旅~:

【1】LCR.164破解闯关密码

直接偷懒,写到这累的不行了,我直接sort(快速排序)+拼接字符串。

class Solution {
public:// 自定义比较函数,用于确定两个数字拼接后的结果的大小static bool compare(const int& x, const int& y) {string xy = to_string(x) + to_string(y);string yx = to_string(y) + to_string(x);return xy < yx;}string crackPassword(vector<int>& password) {// 将数组按照自定义的比较函数排序sort(password.begin(), password.end(), compare);// 将排序后的数组拼接成字符串string result = "";for (int num : password) {result += to_string(num);}return result;}
};

【2】移动0

直接双指针遍历,移动非零元素,然后遍历完后,直接left后面全部赋0.

class Solution {
public:void moveZeroes(vector<int>& nums) {int left = 0; // left 指针int n = nums.size();// 遍历数组for (int right = 0; right < n; right++) {if (nums[right] != 0) {// 将非零元素放到 left 指针位置nums[left++] = nums[right];}}// 将 left 指针后面的所有元素置为 0while (left < n) {nums[left++] = 0;}}
};

【3】[数组排序](https://leetcode.cn/problems/sort-an-array/)

直接用前面冒泡排序的代码-》超出时间限制emmmm
快速排序也超出时间限制了,这怎么搞,我想想,数组太大了,那就小规模用插入排序更快一些,大数组用快速。

class Solution {
public:vector<int> sortArray(vector<int>& nums) {quickSort(nums, 0, nums.size() - 1);return nums;}// 快速排序函数void quickSort(vector<int>& nums, int low, int high) {if (low < high) {// 对小规模数组(<=10)使用插入排序if (high - low + 1 <= 10) {insertionSort(nums, low, high);return;}// 随机选择基准元素int pivotIndex = rand() % (high - low + 1) + low;swap(nums[pivotIndex], nums[high]);pivotIndex = partition(nums, low, high);quickSort(nums, low, pivotIndex - 1);quickSort(nums, pivotIndex + 1, high);}}// 划分函数,返回分割点的位置int partition(vector<int>& nums, int low, int high) {int pivot = nums[high]; // 基准元素int i = low - 1; // 定义分割点的位置for (int j = low; j < high; j++) {if (nums[j] < pivot) {i++;swap(nums[i], nums[j]);}}swap(nums[i + 1], nums[high]); // 将基准元素放到正确的位置return i + 1;}// 插入排序函数void insertionSort(vector<int>& nums, int low, int high) {for (int i = low + 1; i <= high; i++) {int key = nums[i];int j = i - 1;while (j >= low && nums[j] > key) {nums[j + 1] = nums[j];j--;}nums[j + 1] = key;}}
};

通过了,累了,不敲了

参考资料:

[1] 【教程地址 】[电子网站]
[2] Hello 算法教程
[3] https://blog.csdn.net/qq_39181839/article/details/109478094
[4] https://blog.csdn.net/u010452388/article/details/81283998

感谢:DataWhale社区

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/3523.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PHP项目搭建与启动

1、拉取项目 2、安装phpstudy 下载地址&#xff1a; Windows版phpstudy下载 - 小皮面板(phpstudy) (xp.cn) 软件安装&#xff1a; Apache2.4.39、Nginx1.15.11、MySQL8.0.12、 composer2.5.8 添加伪静态 将下面代码写入到伪静态配置文本域框内&#xff1a; location ~* (ru…

【Qt 学习笔记】Qt常用控件 | 输入类控件 | Text Edit的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 输入类控件 | Text Edit的使用及说明 文章编号&#xff…

VScode使用cmake编译

一&#xff1a;输入 ctrlshiftp打开用于命令执行的输入框 二&#xff1a;输入cmake&#xff0c;选择quick start 模式 三&#xff1a;选择版本最高的gcc版本 四&#xff1a;输入项目名称 选择C 五&#xff1a;选择executable 这样便创建好了最简单的cmake例程&#xff0c;一个…

企业微信代开发应用登录操作

首先声明&#xff1a;企微的文档写得真烂&#xff01;&#xff01;&#xff01;有一些问题&#xff0c;官方情愿在问答区给用户一个个解答&#xff0c;也不愿意在文档写清楚&#xff0c;生怕自己工作量不饱和被优化。 概念说明 代开发应用&#xff0c;是相对于自建应用来说的。…

[Swift]组件化开发

一、组件化开发基础 1.组件定义 在软件开发中&#xff0c;一个组件是指一个独立的、可替换的软件单元&#xff0c;它封装了一组相关的功能。组件通过定义的接口与外界交互&#xff0c;并且这些接口隔离了组件内部的实现细节。在Swift语言中&#xff0c;组件可以是一个模块、一…

车企如何利用数据技术,指导汽车全生命周期的业务运营?

引言&#xff1a;数据正作为重点&#xff0c;为行业提供不可或缺的指导 《汽车数据发展研究报告&#xff08;2023&#xff09;》指出&#xff0c;汽车行业正由传统硬件制造向“电动化、智能化、网联化”方向转变。德勤预测&#xff0c;到 2025 年&#xff0c;汽车行业 20%的利…

K8s: 部署 kubernetes dashboard

部署 Dashboard K8s 官方有一个项目叫 dashboard&#xff0c;通过这个项目更方便监控集群的状态 官方地址: https://github.com/kubernetes/dashboard 通常我们通过命令行 $ kubectl get po -n kube-system 能够查看到集群所有的组件&#xff0c;但这样的方式比较不太直观 …

【JavaScript】内置对象 ③ ( Math 内置对象 | Math 内置对象简介 | Math 内置对象的使用 )

文章目录 一、Math 内置对象1、Math 内置对象简介2、Math 内置对象的使用 二、代码示例1、代码示例 - Math 内置对象的使用2、代码示例 - 封装 Math 内置对象 一、Math 内置对象 1、Math 内置对象简介 JavaScript 中的 Math 内置对象 是一个 全局对象 , 该对象 提供了 常用的 数…

通过 QEMU 试用 ESP32-C3 的安全功能

概述 ESP32-C3 系列芯片支持可信启动、flash 加密、安全存储等多种安全功能&#xff0c;还有专用外设来支持 HMAC 和数字签名等用例。这些功能所需的私钥和配置大多存储在 ESP32-C3 的 eFuse 存储器中。 启用安全功能时需要谨慎&#xff0c;因为使用到的 eFuse 存储器是一次…

安卓studio插件开发(一)本地搭建工程

下载idea 社区版本 建立IDE Plugin工程 点击create就行&#xff0c;新建立的工程长这样 比较重要的文件 build.gradle&#xff1a;配置工程的参数 plugin.xml&#xff1a;设置插件的Action位置 build.gradle.kts内容如下&#xff1a; plugins {id("java")id(&quo…

【网络安全】安全事件管理处置 — 事件分级分类

专栏文章索引&#xff1a;网络安全 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 目录 一、安全事件分级 二、应急事件分级 三、安全事件分类 四、常见安全事件原因分析 1.web入侵 2.漏洞攻击 3.网络攻击 一、安全事件分级 在对安全事件的应急响应过程中&#xf…

6.MMD ray渲染 材质的添加及打光方法

材质 前置准备 先准备好模型和场景 将ray控制器拖入进去 添加完默认的材质以后的效果 打开插入材质页面 打开MaterialMap栏 将流萤的模型展开 自发光 现在给领带添加一个自发光效果 在自发光Emissive里&#xff0c;打开x1&#xff0c;选择albedo&#xff0c;白光 现在…

OpenMesh 计算封闭网格体积

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 思路很是简单,就是计算一些四面体的有向体积(这些四面体均是基于网格中的三角形与原点组成的) ,至于体积的符号则来自于三角形是否指向原点方向来确定,即面片的法线方向(其中三角形的法线本身取决于顶点的顺序…

80个在线小游戏源码

源码简介 搭建80个在线小游戏网站源码&#xff0c;解压即可食用&#xff0c;支持在本地浏览器打开。 安装教程 纯HTML&#xff0c;直接将压缩包上传网站目录解压即可 首页截图 源码下载 80个在线小游戏源码-小8源码屋

BGP的基本概念和工作原理

AS的由来 l Autonomous System 自治系统&#xff0c;为了便于管理规模不断扩大的网络&#xff0c;将网络划分为不同的AS l 不同AS通过AS号区分&#xff0c;AS号取值范围1&#xff0d;65535&#xff0c;其中64512&#xff0d;65535是私有AS号 l IANA机构负责AS号的分发 AS之…

【Vue】通过Axios实现异步通信(简单案例)

一、Axios介绍 1、是什么 Axios 是一个基于 promise 的 HTTP 库&#xff0c;简单的讲就是可以发送get、post请求。当然这些请求ajax和jquery也能做&#xff0c;但是由于Vue、React等框架的出现&#xff0c;促使了Axios轻量级库的出现&#xff0c;因为Vue等&#xff0c;不需要操…

LabVIEW专栏九、类的应用

一、类的应用 接上一章"类" 类在项目中&#xff0c;一般会在类的私有成员簇内&#xff0c;包含一个数据类型为参数类的队列。 例如网口类&#xff0c;里面实际会包含很多信息&#xff0c;有IP地址和端口等等参数。这些参数如果不放在队列引用中缓存下来&#xff0c;…

HarmonyOS开发实战(黑马健康系列一:欢迎页)

系列文章目录 &#xff08;零&#xff09;鸿蒙HarmonyOS入门&#xff1a;如何配置环境&#xff0c;输出“Hello World“ &#xff08;一&#xff09;鸿蒙HarmonyOS开发基础 &#xff08;二&#xff09;鸿蒙HarmonyOS主力开发语言ArkTS-基本语法 &#xff08;三&#xff09;鸿蒙…

【Qt QML】TabBar的用法

Qt Quick中的TabBar提供了一个基于选项卡的导航模型。TabBar由TabButton控件填充&#xff0c;并且可以与任何提供currentIndex属性的布局或容器控件一起使用&#xff0c;例如StackLayout或SwipeView。 import QtQuick import QtQuick.Controls import QtQuick.LayoutsWindow …