初阶数据结构:排序(学习笔记)

目录

  • 1. 各种排序算法的分类
  • 2. 插入排序
    • 2.1 直接插入排序
    • 2.2 希尔排序
  • 3. 选择排序
    • 3.1 选择排序
    • 3.2 堆排序
    • 4. 交换排序
    • 4.1 冒泡排序
    • 4.2 快速排序
    • 4.2.1 霍尔法(hoare)
    • 4.2.2 挖坑法(hole)
    • 4.4.3 前后指针法
    • 4.4.4 补充:非递归快排
    • 4.5 快排优化
  • 5. 归并排序
  • 6. 非比较排序:计数排序
  • 7. 排序算法的稳定性

1. 各种排序算法的分类

按照排序逻辑的不同,排序大体可以分为四大类:

  1. 插入排序
  2. 选择排序
  3. 交换排序
  4. 归并排序

接下来,我们进行这些排序的学习

!注:本章内容中的动图并非原创

2. 插入排序

2.1 直接插入排序

  1. 将整个数组的元素,从起始遍历,一次向后移动一步,看作是将一个元素插入到数组中。
  2. 在"插入"的过程中,当新插入的元素小于其前面的元素时,交换两者,循环此步骤,直至前面的元素不小于新插入的元素,到此成功插入一个元素。

过程演示:
在这里插入图片描述

void InsertSort(int* a, int n)
{
	for (int i = 1; i < n; i++)
	{
		for (int j = i; a[j - 1] > a[j]; j--)
		{
			swap(&a[j - 1], &a[j]);
		}
	}
}

2.2 希尔排序

  1. 根据给定组距,进行数据的分组,组内进行插入排序。
  2. 不断减小组距,直至组距为1。
  3. 注:在组距不为1时,都是预排序,让数据更接近有序。
//多趟排
void ShellSort1(int* a, int n)
{
	int gap = n / 2;
	while (gap > 0)
	{
		for (int i = 0; i < gap; i++)
		{
			for (int j = i; j + gap < n; j += gap)
			{
				if (a[j] > a[j + gap])
				{
					swap(&a[j], &a[j + gap]);
				}
			}
		}

		gap /= 2;
	}
}

//一趟排
void ShellSort2(int* a, int n)
{
	int gap = n / 2;
	while (gap > 0)
	{
		for (int i = 0; i + gap < n; i++)
		{
			if (a[i] > a[i + gap])
			{
				swap(&a[i], &a[i + gap]);
			}
		}

		gap /= 2;
	}
}

3. 选择排序

3.1 选择排序

  1. 遍历一次,从数据中选出最小(或最大)的数据放至数据首部。
  2. 多次遍历选择,直至将最后一个数据选走。

过程演示:
在这里插入图片描述

void SelectSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		int min = i;
		for (int j = i; j < n; j++)
		{
			if (a[j] < a[min])
			{
				min = j;
			}
		}

		swap(&a[i], &a[min]);
	}
}

选择排序优化:

一次遍历选出最大值与最小值

void SelectSort2(int* a, int n)
{
	for (int i = 0; i < n / 2; i++)
	{
		int max = i;
		int min = i;
		for (int j = i; j < n - i; j++)
		{
			if (a[j] > a[max])
			{
				max = j;
			}

			if (a[j] < a[min])
			{
				min = j;
			}
		}
		swap(&a[max], &a[n - i - 1]);
		swap(&a[min], &a[i]);
	}
}

3.2 堆排序

  1. 建大堆
  2. 交换首尾,size–,向下调整,直到size为0
void AdjustDown(int* a, int n, int root)
{
	int child = root * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] < a[child + 1])
		{
			child++;
		}

		if (a[root] < a[child])
		{
			swap(&a[root], &a[child]);
		}

		root = child;
		child = root * 2 + 1;
	}
}

void HeapSort(int* a, int n)
{
	//建大堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

	//交换首尾,调整
	int size = n;
	while (size > 0)
	{
		swap(&a[0], &a[size - 1]);
		size--;
		AdjustDown(a, size, 0);
	}
}

4. 交换排序

4.1 冒泡排序

  1. 建立两个一前一后的指针,用这两个指针遍历整个数组
  2. 若后指针指向的数据大于前指针指向的数据,交换前后指针所指向的元素,之后两指针++,直至遍历完数据,得出一个最大数,需遍历的数据长度减1,此为遍历一趟。
  3. 多次遍历,当长度为0时,排序结束

过程演示:
在这里插入图片描述

void BubbleSort(int* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		int flag = 1;
		for (int j = 0; j + 1 < n - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				swap(&arr[j], &arr[j + 1]);
				flag = 0;
			}
		}

		if (flag)
		{
			break;
		}
	}
}

4.2 快速排序

4.2.1 霍尔法(hoare)

  1. 将数据的首位确定为对照key,定义两个指针left(数据首部),right(数据尾部)。
  2. 右侧指针反向遍历数组,寻找小于key的值,当找到后停止,左侧指针正向遍历数组,寻找大于key的值,找到后将两指针指向的数据交换。
  3. 重复上述步骤2,直至左右指针相遇,交换key元素与左右指针同时指向的元素,此为一趟排序。
  4. 将数据分割为[0,key - 1]与[key + 1,n],在这两个区间内再进行上述步骤2,3。直至所有元素的位置都被确认。

过程演示:
在这里插入图片描述

// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
	int key = left;
	int keyi = a[key];

	while (left < right)
	{
		while (left < right && a[right] >= keyi)
		{
			right--;
		}

		while (left < right && a[left] <= keyi)
		{
			left++;
		}

		swap(&a[left], &a[right]);
	}

	if (a[left] < keyi)
	{
		swap(&a[left], &a[key]);
		key = left;
	}

	return key;
}

4.2.2 挖坑法(hole)

  1. 选择首位数据为key,然后将数据的首位标记为hole,创建两个指针left(首位 ),right(数据尾部)。
  2. 右侧指针找寻找小于key元素的值,找到后,将所找到的元素填充至挖好的"洞"里,此元素原位置标记为新的洞,然后,移动左侧指针寻找大于key元素的值,找到后,将找到的元素填入洞中。重复上述步骤,直至左右指针相遇,将key值填入左右指针相遇的位置,此时即确定好了key的位置。
  3. 在[left,key - 1]与[key + 1, right]的区间中,重复步骤2,直至所有位置都被确定。

过程演示:

在这里插入图片描述

int PartSort2(int* a, int left, int right)
{
	int hole = left;
	int keyi = a[hole];
	
	while (left < right)
	{
		//额外检查,越界可能
		while (left < right && a[right] >= keyi)
		{
			right--;
		}

		if (a[right] < keyi)
		{
			a[hole] = a[right];
			hole = right;
		}

		while (left < right && a[left] <= keyi)
		{
			left++;
		}

		if (a[left] > keyi)
		{
			a[hole] = a[left];
			hole = left;
		}
	}

	a[hole] = keyi;

	return hole;
}

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}

	int key = PartSort2(a, left, right);
	QuickSort(a, left, key - 1);
	QuickSort(a, key + 1, right);
}

4.4.3 前后指针法

思路1:

  1. 将数据首位设置为key,创建两个指针pre(首部),cur(首部 + 1)。
  2. cur开始遍历整个数组,如果cur指针指向的值小于key,那么pre指针一同++,否则pre指针不动,直至cur再次寻找到小于key的值,此时,pre++,然后将两指针指向的值交换。如此,反复直至cur遍历完整个数组,最后,将key与pre指针指向的值交换。
  3. 在[left,key - 1]与[key + 1, right]的区间中,重复步骤2,直至所有位置都被确定。

思路2:

  1. [left + 1,pre]区间为小于key的值,[pre + 1,cur - 1]为大于key的值,[cur,right]为未遍历到的值。
  2. cur指针遍历寻找小于pre指针的数据,找到后pre++,交换两指针所指向的值。

过程演示:
在这里插入图片描述

int PartSort3(int* a, int left, int right)
{
	int pre = left;
	int cur = left + 1;
	int keyi = a[left];

	while (cur <= right)
	{
		if (a[cur] < keyi)
		{
			swap(&a[++pre], &a[cur]);
		}

		cur++;
	}

	swap(&a[left], &a[pre]);

	return pre;
}

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}

	int key = PartSort3(a, left, right);
	QuickSort(a, left, key - 1);
	QuickSort(a, key + 1, right);
}

4.4.4 补充:非递归快排

  1. 将原本递归传递的区间存储到栈中,用时从栈中取出
void QuickSortNonR(int* a, int left, int right)
{
	Stack stack;
	StackInit(&stack);

	//插入第一次遍历区间范围
	StackPush(&stack, left);
	StackPush(&stack, right);

	while (!StackEmpty(&stack))
	{
		//取出区间值进行运算
		right = StackTop(&stack);
		StackPop(&stack);
		left = StackTop(&stack);
		StackPop(&stack);

		int key = PartSort3(a, left, right);
		
		//区间遍历顺序:左区间,右区间
		if (key + 1 < right)
		{
			StackPush(&stack, key + 1);
			StackPush(&stack, right);
		}

		if (left < key - 1)
		{
			StackPush(&stack, left);
			StackPush(&stack, key - 1);
		}
	}
}

4.5 快排优化

  1. 三数取中(getmid)
  2. 当递归到小区间时,可以转而进行插入排序
//三数取中
int GetMidNum(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	
	if(a[mid] > a[left])
	{
		if(a[mid] < a[right])
		{
			return mid;
		}
		else
		{
			if(a[right] > a[left])
			{
				return right;
			}
			else
			{
				return left;
			}
		}
	}
	else
	{
		if(a[mid] > a[right])
		{
			return mid;
		}
		else
		{
			if(a[left] < a[right])
			{
				return left;
			}
			else
			{
				return right;
			}
		}
	}
}

5. 归并排序

思路1:

归并逻辑:二叉树的遍历(深度优先:左右根)

  1. 将需要进行归并的区间范围视作结点,根结点的区间为整个数组
  2. 左右孩子结点为将区间范围一分为2,左孩子为前半区间,右孩子为后半区间
  3. 对每次得到的新区间都进行上述处理,直至区间中的元素数(<=2),即视为叶子结点。
  4. 按照后序遍历二叉树的顺序,对结点区间内的数据进行插入排序。

过程演示:
在这里插入图片描述
递归法:
在这里插入图片描述

void _mergesort(int* a, int* tmp, int left, int right)
{
	//深度优先
	if (left >= right)
	{
		return;
	}

	int mid = (right + left) / 2;
	_mergesort(a, tmp, left, mid);
	_mergesort(a, tmp, mid + 1, right);

	//插入
	int i = left;
	int j = mid + 1;
	int k = left;
	while (i <= mid && j <= right)
	{
		//当存在相同的数时
		if (a[i] <= a[j])
		{
			tmp[k++] = a[i++];
		}
		else
		{
			tmp[k++] = a[j++];
		}
	}

	while (i <= mid)
	{
		tmp[k++] = a[i++];
	}

	while (j <= right)
	{
		tmp[k++] = a[j++];
	}

	memcpy(a + left, tmp + left, (right - left + 1) * sizeof(int));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(n * sizeof(int));

	_mergesort(a, tmp, 0, n - 1);

	free(tmp);
}

思路2:

  1. 将数组的元素分为两个两个一组,将每一组都使用插入排序调整为有序,遍历一遍数组
  2. 将一组中的元素数翻倍,重复步骤1,遍历完成再次翻倍,直至一组中的元素数包含整个数组,排序完成
  3. 当剩余元素不足一组时,将剩余元素也算作一组

在这里插入图片描述

非递归法:

void MergeSortNonR(int* a, int n)
{
	int* c_a = (int*)malloc(n * sizeof(int));

	int gap = 1;
	while (gap < n)
	{
		//确定初始区间
		int begin1 = 0;
		int end1 = begin1 + gap - 1;

		int begin2 = end1 + 1;
		int end2 = begin2 + gap - 1;
		//检测防止越界
		if (end2 >= n)
		{
			end2 = n - 1;
		}

		while (begin1 < n)
		{
			//插入
			int i = begin1;
			int j = begin2;
			int k = begin1;
			while (i <= end1 && j <= end2)
			{
				if (a[i] <= a[j])
				{
					c_a[k++] = a[i++];
				}
				else
				{
					c_a[k++] = a[j++];
				}
			}

			while (i <= end1)
			{
				c_a[k++] = a[i++];
			}

			while (j <= end2)
			{
				c_a[k++] = a[j++];
			}

			//拷贝回原数组
			memcpy(a + begin1, c_a + begin1, (end2 - begin1 + 1) * sizeof(int));
			
			//向后调整区间
			begin1 = end2 + 1;
			end1 = begin1 + gap - 1;

			begin2 = end1 + 1;
			end2 = begin2 + gap - 1;

			//判断是否越界
			if (end1 >= n)
			{
				end1 = n - 1;
			}

			if (end1 < n && end2 >= n)
			{
				end2 = n - 1;
			}
		}

		gap *= 2;
	}

	free(c_a);
}

优化:

void MergeSortNonR(int* arr, int n)
{
    int* tmp = (int*)malloc(n * sizeof(int));

    //确定间距
    int gap = 1;
    while (gap < n)
    {
    	//i中间记录
        for (int i = 0; i < n; i += 2 * gap)
        {
            int begin1 = i;
            int end1 = begin1 + gap - 1;
            int begin2 = end1 + 1;
            int end2 = begin2 + gap - 1;
			
			//上一趟已经遍历过
            if (end1 >= n)
            {
                break;
            }

            if (end2 >= n)
            {
                end2 = n - 1;
            }

            int index = begin1;
            while (begin1 <= end1 && begin2 <= end2)
            {
                if (arr[begin1] <= arr[begin2])
                {
                    tmp[index++] = arr[begin1++];
                }
                else
                {
                    tmp[index++] = arr[begin2];
                }
            }

            while (begin1 <= end1)
            {
                tmp[index++] = arr[begin1++];
            }

            while (begin2 <= end2)
            {
                tmp[index++] = arr[begin2++];
            }

            memcpy(arr + i, tmp + i, (end2 - i + 1) * sizeof(int));
        }

        gap *= 2;
    }
}

6. 非比较排序:计数排序

  1. 根据数据的范围,创建一个合适大小的数组。
  2. 下标对应数据,根据数据中各个数字的出现次数在对应的下标处计数++。
  3. 限制:
    <1> 数据范围不可跨度太大,会导致空间复杂度过高
    <2>只能用来处理整形数据。

过程演示:
在这里插入图片描述

void CountSort(int* a, int n)
{
	//选出最大值与最小值
	int min = a[0];
	int max = a[0];
	for (int i = 0; i < n; i++)
	{
		if (a[i] < min)
		{
			min = a[i];
		}

		if (a[i] > max)
		{
			max = a[i];
		}
	}

	//开辟空间
	int size = max - min + 1;
	int* count = (int*)malloc(size * sizeof(int));
	memset(count, 0, n * sizeof(int));

	//计数
	for (int i = 0; i < n; i++)
	{
		count[a[i] - min]++;
	}

	//读数
	int index = 0;
	for (int i = 0; i < size; i++)
	{
		while (count[i])
		{
			a[index++] = i + min;
			count[i]--;
		}
	}
}

7. 排序算法的稳定性

算法稳定性的判断标准:数据中相同数据在排序后,他们的相对位置是否变化。

在这里插入图片描述

  1. 直接插入排序(稳定,时间复杂度:O( n 2 n^2 n2))
  2. 希尔排序(不稳定,时间复杂度略小于O( n 2 n^2 n2))
  3. 选择排序(稳定,O( n 2 n^2 n2))
  4. 堆排序(不稳定,O( n n n * logn))
  5. 冒泡排序(稳定,O( n 2 n^2 n2))
  6. 快速排序(不稳定,O( n n n * logn))
  7. 归并排序(不稳定,O( n n n * logn))
  8. 计数排序(稳定,O(n,Max))

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

神经网络线性量化方法简介

可点此跳转看全篇 目录 神经网络量化量化的必要性量化方法简介线性对称量化线性非对称量化方法神经网络量化 量化的必要性 NetworkModel size (MB)GFLOPSAlexNet2330.7VGG-1652815.5VGG-1954819.6ResNet-50983.9ResNet-1011707.6ResNet-15223011.3GoogleNet271.6InceptionV38…

Codeforces Round 929 (Div. 3)- ABCDEFG

A:Turtle Puzzle: Rearrange and Negate 思路&#xff1a; 将负的元素全部排到一起&#xff0c;然后对它们符号取反&#xff0c;然后求所有元素的和&#xff0c;此时就是最大值了。 代码&#xff1a; #include<iostream> using namespace std;void solve() {int n;cin&…

资产管理系统有哪些(一体化资产管理平台推荐)

企业资产管理系统是一种关键的工具&#xff0c;旨在帮助企业有效地管理和追踪其资产。 该系统利用计算机系统和相关软件&#xff0c;通过信息化、智能化的方式&#xff0c;对资产进行全面的可视化管理&#xff0c;从而提高管理效率、降低运营成本&#xff0c;并确保资产的安全…

JVM的工作流程

目录 1.JVM 简介 2.JVM 执行流程 3. JVM 运行时数据区 3.1 堆&#xff08;线程共享&#xff09; 3.3 本地方法栈&#xff08;线程私有&#xff09; 3.4 程序计数器&#xff08;线程私有&#xff09; 3.5 方法区&#xff08;线程共享&#xff09; 4.JVM 类加载 ① 类…

【Unity】Tag、Layer、LayerMask

文章目录 层&#xff08;Layer&#xff09;什么是LayerLayer的应用场景Layer层的配置&#xff08;Tags & Layers&#xff09;Layer的数据结构LayerMaskLayer的选中和忽略Layer的管理&#xff08;架构思路&#xff09;层碰撞矩阵设置&#xff08;Layer Collision Matrix&…

搜狐新闻Hybrid AI引擎端侧离线大语言模型探索

本文字数&#xff1a;3027字 预计阅读时间&#xff1a;20分钟 01 一、导读 • LLM 以及移动平台落地趋势 • 搜狐AI引擎内建集成离线可运行的GPT模型 • Keras 定制预训练模型 • TensorFlow Lite converter 迁移到移动设备 02 二、LLM 1.1什么是LLM L…

考研复习c语言初阶(1)

本人准备考研&#xff0c;现在开始每天更新408的内容&#xff0c;目标这个月结束C语言和数据结构&#xff0c;每天更新~ 一.再次认识c语言 C语言是一门通用计算机编程语言&#xff0c;广泛应用于底层开发。C语言的设计目标是提供一种能以简易 的方式编译、处理低级存储器、产生…

【数据库-黑马笔记】基础-函数和约束

本文参考b站黑马数据库视频,总结详细全面的笔记 ,可结合视频观看27~36集 MYSQL 的基础知识框架如下 目录 一、 函数 1、字符串函数 2、数值函数 3、日期函数 4、流程函数 5、小结: 二、约束 1、概述 2、 约束演示 3、外键约束 4、外键删除更新行为 5、小结: …

【npm】前端工程项目配置文件package.json详解

简言 详细介绍了package.json中每个字段的作用。 package.json 本文档将为您介绍 package.json 文件的所有要求。它必须是实际的 JSON&#xff0c;而不仅仅是 JavaScript 对象文字。 如果你要发布你的项目&#xff0c;这是一个特别重要的文件&#xff0c;其中name和version是…

【Linux】第四十一站:线程控制

一、Linux线程VS进程 1.进程和线程 进程是资源分配的基本单位线程是调度的基本单位线程共享进程数据&#xff0c;但也拥有自己的一部分数据:线程ID一组寄存器&#xff08;上下文&#xff09;栈errno信号屏蔽字调度优先级 2.进程的多个线程共享 同一地址空间,因此Text Segment、…

【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点

文章目录 题目思路解答 题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[]示例 3&…

接口自动化测试框架搭建:基于python+requests+pytest+allure实现

众所周知&#xff0c;目前市面上大部分的企业实施接口自动化最常用的有两种方式&#xff1a; 1、基于代码类的接口自动化&#xff0c;如&#xff1a; PythonRequestsPytestAllure报告定制 2、基于工具类的接口自动化&#xff0c;如&#xff1a; PostmanNewmanJenkinsGit/svnJme…

二分算法(蓝桥杯 C++ 题目 代码 注解)

目录 模板&#xff1a; 题目一&#xff08;分巧克力&#xff09;&#xff1a; 代码&#xff1a; 题目二&#xff08;M次方根&#xff09;&#xff1a; ​编辑代码&#xff1a; 题目三&#xff08;跳石头&#xff09;&#xff1a; 代码&#xff1a; 题目四&#xff08;扫…

基于SpringBoot的CNKI数据精炼与展示

目 录 摘 要 I Abstract II 引 言 1 1 相关技术 3 1.1 SpringBoot框架 3 1.1.1 Spring框架介绍 3 1.1.2 SpringBoot框架介绍 3 1.2 MyBatis框架 4 1.3 Echarts框架 5 1.4 Bootstrap框架 5 1.5 JQuery技术 6 1.6 本章小结 6 2 系统分析 7 2.1 功能需求分析 7 2.1.1 门户模块需求…

2024最新多目标优化算法:多目标指数分布优化器MOEDO(提供MATLAB代码)

一、多目标指数分布优化器&#xff08;MOEDO&#xff09; 多目标指数分布优化算法&#xff08;Multi-objective exponential distribution optimizer &#xff0c;MOEDO&#xff09;由Kalita, K等人于2024年提出&#xff0c;其采用增强的精英非主导分类和拥挤距离机制。MOEDO集…

2024手把手教你FL Studio 20 for Mac v20.8.3 中文破解版 水果音乐制作软件

网上大部分都是Windows安装教程&#xff0c;今天给大家分享一个FL Studio 20 Mac版激活教程&#xff0c;废话不多说&#xff0c;首先上一个FL Studio 20激活成功的截图 FL Studio 20 for Mac 破解版是最容易上手的编曲工具之一&#xff0c;直观的用户操作界面&#xff0c;强大的…

基于Unity3D的AVG卡牌游戏设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 相关技术 3 1.1 C# 3 1.2 Unity3D 3 1.3 UGUI 3 1.4 XML 4 1.5 原型设计模式 4 1.6 本章小结 4 2 系统分析 5 2.1 用户需求 5 2.2 功能需求 5 2.3 非功能需求 6 2.4 本章小结 6 3 系统设计 7 3.1 系统该要设计 7 3.2 系统详细设计 7 3.2.…

DFS(深度优先搜索)C++(Acwing)

代码&#xff1a; #include <iostream>using namespace std;const int N 10;int n; int path[N]; bool st[N];void dfs(int u) {if(u n){for(int i 0; i < n; i) printf("%d ", path[i]);puts("");return;}for(int i 1; i < n; i){if(!st…

启动项目报502怎么处理呢?

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…

Qt自定义控件

自定义控件 目的&#xff1a;将多个控件或者窗口作为一个整体被多次复用。 操作方式 1.首先进行自定义的ui设计&#xff0c;以及对应的.h和.cpp文件 2.到要使用的UI界面上&#xff0c;从控件库中拖拽一个Widget控件 3.右键点击"提升为" 4.填写自定义实现的类名&…
最新文章