寻找数组中第k小的数:平均情况下时间复杂度为O(n)的快速选择算法

又叫线性选择算法,这是一种平均情况下时间复杂度为O(n)的快速选择算法,用到的是快速排序中的第一步,将第一个数作为中枢,使大于它的所有数放到它右边,小于它的所有数放到它左边。之后比较该中枢的最后位置i与k的大小,若i比k小,说明第k小的元素在i的右半段,之后对i的右半段进行快速选择;若i比k大,说明第k小的元素在i的左半段,之后对i的左半段进行快速选择;若i正好等于k,则直接返回......。

下面是具体的算法步骤:

1,选择中枢:如下,为了提高快速选择的效率,最好尽可能的选择数值居中的数作为中枢。如1 7 2 8 4 3,则取头尾以及中间位置,然后选择(1, 2, 3)中的2

int median3(int nums[], int left, int right){
	int center = (left + right) / 2;
	if(nums[left] > nums[center])
		swap(nums[left], nums[center]);
	if(nums[left] > nums[right])
		swap(nums[left], nums[right]);
	if(nums[center] > nums[right])
		swap(nums[center], nums[right]);//此时nums[left] <= nums[center] <= nums[right]
	swap(nums[center], nums[right-1]);//交换中枢和a[right-1],此时上面三个值的居中的数在下标right-1处,然后返回中枢,由于a[right]必定比中枢大,故只需对区间[left, right-1]以中枢a[right-1]进行一次快速排序即可。
	return nums[right-1];
}

2,对区间[left, right-1]以中枢nums[right-1]进行一次快速排序,之后大于nums[right-1]的数全在它的右边,小于nums[right-1]的数全在它的左边

int pivot = nums[right-1], i = left, j = right-1;
while(i < j){
	while(nums[i] < pivot && i < j) ++i;
	nums[j] = nums[i];
	while(nums[j] >= pivot && i < j) --j;
	nums[i] = nums[j];
}
nums[i] = pivot;

3,比较i和k的大小:若求序列中的第k大的数,现在由步骤2已经知道一次选择排序后的中枢下标为i,说明前面i个数比pivot大,后面right-i个数比pivot大。
如果k < i,说明第k大的数在前面i个数中;如果k > i,说明第k大的数在后面的right-i中;如果i==k,直接返回答案。对于前两种情况只需要对pivot的左半段或者右半段中寻找即可,即:

if(k < i)
	QuickSelect(nums, k, 0, i-1);
else if(k > i)
	QuickSelect(nums, k, i+1, right);
else
	return nums[i];

完整代码如下:

#include <iostream>
using namespace std;
int median3(int nums[], int left, int right){
	int center = (left + right) / 2;
	if(nums[left] > nums[center])
		swap(nums[left], nums[center]);
	if(nums[left] > nums[right])
		swap(nums[left], nums[right]);
	if(nums[center] > nums[right])
		swap(nums[center], nums[right]);//此时nums[left] <= nums[center] <= nums[right]
	swap(nums[center], nums[right-1]);//交换中枢和a[right-1],此时上面三个值的居中的数在下标right-1处,然后返回中枢,由于a[right]必定比中枢大,故只需对区间[left, right-1]以中枢a[right-1]进行一次快速排序即可。
	return nums[right-1];
}

int QuickSelect(int nums[], int k, int left, int right){
	int pivot = median3(nums, left, right), i = left, j = right-1;
	while(i < j){
		while(nums[i] < pivot && i < j) ++i;
		nums[j] = nums[i];
		while(nums[j] >= pivot && i < j) --j;
		nums[i] = nums[j];
	}
	nums[i] = pivot;
	if(k < i)
		QuickSelect(nums, k, 0, i-1);
	else if(k > i)
		QuickSelect(nums, k, i+1, right);
	else
		return nums[i];
}

int main(){
	int k, nums[12] = {4, 5, 23, 12, 89, 20, 14, 23, 54, 66, 47, 23};
	while(cin >> k)
		cout << QuickSelect(nums, k-1, 0, 11) << endl;//第k大的数下标应为k-1
	return 0;
}

运行结果如下:



时间复杂度:每次尽可能的遍历数组的一半,故平均情况下的时间复杂度为O(n/2) + O(n/4) + O(n/8) + ... +O(1) = O(n)

<think>好的,我现在需要向用户介绍MATLAB中的数组据。首先,我得回想一下自己了解的MATLAB数组知识,确保信息准确。MATLAB作为值计算软件,数组是其核心据结构,这点很重要,应该首先提到。 用户可能刚接触MATLAB,所以需要从基础开始。首先解释数组的定义,包括元素、索引、维度这些概念。然后,分类数组类型,比如一维、二维、多维,以及特殊类型如单元数组和结构体数组。这部分需要结构清晰,分点说明。 接下来是创建数组的方法。要涵盖方括号法、冒号运算符、linspace和logspace函,还有zeros、ones这些常用函。每种方法需要简单举例,比如1:2:10生成什么,方便用户理解。 然后,数组操作部分很重要,用户经常需要这些操作。索引和切片是关键,包括不同维度的访问方式。矩阵运算和元素运算的区别,比如*和.*的不同,必须强调清楚,避免混淆。还需要提到reshape和转置,这些在据处理中常用。 应用场景部分,需要举一些实际的例子,比如据处理、图像处理、学建模,这样用户能明白数组的实际用途。这部分可以稍微详细,帮助用户联想使用场景。 注意事项方面,MATLAB的索引从1开始,这点和某些其他语言不同,容易出错,必须提醒。还有内存问题,数组的处理方法,比如预分配,这对性能优化很重要。 最后,确保整个回答符合系统级指令:行内公式用$...$,独立公式用$$...$$,LaTeX正确,中文回答。检查是否有遗漏的部分,比如是否需要更多例子或更详细的步骤。可能用户需要的是实用信息,所以重点放在操作和应用上,避免过于理论化。 现在整理结构,先定义,再分类,创建方法,操作,应用,注意事项。每个部分用标题分开,保持清晰。例子用代码块显示,但用户要中没有提到代码格式,可能需要注意。不过通常MATLAB代码会用等宽字体,这里可能用反引号即可。不过原例中用户的问题里没有使用代码块,所以可能需要用中文说明,但适当加入语法示例。 最后通读一遍,确保符合所有要,没有格式错误,LaTeX正确,比如矩阵的表示是否正确使用$$...$$。例如,独立公式如矩阵应使用$$...$$,例如: $$A = \begin{bmatrix}1 & 2 \\ 3 & 4\end{bmatrix}$$ 确认所有要点都已涵盖,没有遗漏重要内容,比如单元数组和结构体数组的简要说明。确保语言简洁,步骤明确,帮助用户逐步理解MATLAB数组的相关知识。</think>### MATLAB数组据详解 #### 一、数组定义 MATLAB中的**数组**是用于存储值、字符或逻辑据的基础据结构,具有以下特点: - **元素**:数组由有序排列的据单元构成 - **索引**:从1开始(与Python等语言不同),支持正负索引 - **维度**:支持1维(向量)、2维(矩阵)、多维数组(三维及以上) #### 二、数组分类 | 类型 | 描述 | 示例 | |------|------|------| | 数组 | 存储据 | `[1 2; 3 4]` | | 字符数组 | 存储文本据 | `'MATLAB'` | | 逻辑数组 | 存储布尔值 | `[true false]` | | 单元数组 | 存储异构据 | `{1, 'text', [2 3]}` | | 结构体数组 | 键值对存储 | `struct('name','Alice','age',25)` | #### 三、数组创建方法 1. **直接赋值** ```matlab A = [1 2 3; 4 5 6]; % 2×3矩阵 B = 1:0.5:3; % 生成[1 1.5 2 2.5 3] ``` 2. **专用函** ```matlab zeros(3) % 3×3零矩阵 ones(2,4) % 2×4全1矩阵 eye(3) % 3×3单位矩阵 rand(1,5) % 包含5个随机的行向量 ``` 3. **特殊序列生成** ```matlab linspace(0,10,5) % 生成[0 2.5 5 7.5 10] logspace(1,3,3) % 生成[10 100 1000] ``` #### 四、核心操作技巧 1. **索引与切片** ```matlab A(2,3) % 访问第2行第3列元素 A(:,1) % 获取第一列所有元素 A(end-1,:) % 获取倒第二行 ``` 2. **维度操作** ```matlab size(A) % 获取维度信息 reshape(A,3,2) % 改为3×2矩阵 permute(A,[2 1]) % 维度置换 ``` 3. **运算规则** - **矩阵运算**:`A * B`(矩阵乘法) - **元素运算**:`A .* B`(逐元素相乘) - **广播机制**:自动扩展维度进行运算 #### 五、应用场景 1. **信号处理** ```matlab t = 0:0.001:1; y = sin(2*pi*50*t) + randn(size(t))*0.1; ``` 2. **图像处理** ```matlab img = imread('image.jpg'); red_channel = img(:,:,1); % 提取红色通道 ``` 3. **矩阵运算** ```matlab A = [1 2; 3 4]; eigenvalues = eig(A); % 计算特征值 ``` #### 六、性能优化建议 1. **预分配内存** ```matlab data = zeros(1000,1000); % 预先分配空间 ``` 2. **向量化运算** ```matlab % 差实践(循环) for i = 1:1000 y(i) = x(i)^2 + 2*x(i) end % 好实践(向量化) y = x.^2 + 2*x; ``` 3. **内存管理** ```matlab whos % 查看工作区变量 clear var % 删除指定变量 save data.mat -v7.3 % 存储文件 ``` #### 七、特殊数组类型 1. **单元数组** ```matlab C = {1, 'text', [2 3]; true, struct(), magic(3)}; ``` 2. **表格数组** ```matlab data = table([1:3]', {'A';'B';'C'}, 'VariableNames', {'ID','Category'}); ``` 3. **时间序列** ```matlab t = datetime(2023,1,1:10); values = rand(1,10); ts = timeseries(values,t); ``` #### 八、调试技巧 1. **数组检查** ```matlab ismatrix(A) % 判断是否为矩阵 isnumeric(A) % 判断是否为数组 isempty(A) % 判断是否为空数组 ``` 2. **可视化查看** ```matlab disp(A(1:3,:)) % 显示前3行 spy(A) % 可视化非零元素分布 ``` 附:常用学表达式示例 矩阵乘法公式: $$C_{ij} = \sum_{k=1}^{n} A_{ik}B_{kj}$$ 傅里叶变换: $$X_k = \sum_{n=0}^{N-1} x_n \cdot e^{-i2\pi kn/N}$$ 掌握这些核心知识后,您将能高效处理MATLAB中的各类据运算任务。建议通过实际项目实践加深理解,如进行矩阵运算练习或信号处理仿真。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值