数组先小于等于再大于等于的调整

本文介绍了一种在O(N)时间复杂度和O(1)额外空间复杂度下,将数组调整成交替上升下降顺序的算法。通过示例展示了如何使用此算法实现数组的调整,并提供了具体的代码实现。

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

给定数组 arr,请将数组调整成 a <= b >= c <= d >= e <= f...的样子

例如,arr = {3,1,2}
调整之后,arr 可以是{1,3,2}. 1 <= 3 >=2
调整之后,arr 也可以是{2,3,1}. 2 <= 3 >=1
arr = {3,1,2,6}
调整之后,arr 可以是{1,6,2,3}. 1 <= 6 >= 2 <= 3
调整之后,arr 也可以是{3,6,1,2}. 3 <= 6 >= 1 <= 2

1,如果 arr 长度为 N,要求时间复杂度为 O(N),额外空间复杂度为 O(1)。
2,arr 可能会不止一种调整方案,但只要满足要求即可。


算法原形:完美洗牌算法


public static void shuffle(int[] arr, int l, int r){
	while(r - l + 1 > 0){
		int lenAndOne = r - l + 2;
		int bloom = 3;
		int k = 1;
		while(bloom <= lenAndOne / 3){
			bloom += 3;
			k++;
		}
		int m = (bloom - 1) / 2;
		int mid = (l + r) / 2;
		rotate(arr, l + m, mid, mid+m);
		cycles(arr, l - 1, bloom, k);
		l = l + bloom - 1;
	}
}

public static void cycles(int[] arr, int base, int bloom, int k){
	for(int i = 0, trigger = 1; i < k; i++, trigger += 3){
		int next = (2*trigger) % bloom;
		int cur = next;
		int record = arr[next + base];
		int tmp = 0;
		arr[next + base] = arr[trigger + base];
		while(cur != trigger){
			next = (2 * cur) %bloom;
			tmp = arr[next + base];
			arr[next + base] = record;
			cur = next;
			record = tmp;
		}
	}
}

public static void rotate(int[] arr, int l, int m, int r){
	reverse(arr, l, m);
	reverse(arr, m + 1, r);
	reverse(arr, l ,r);
}

public static void reverse(int[] arr, int l, int r){
	while(l < r){
		int tmp = arr[l];
		arr[l++] = arr[r];
		arr[r--] = tmp;
	}
}


<think>我们需要在数组中找到第一个大于等于某个值的元素的索引。这类似于用户之前的问题,但之前是查找大于(不包括等于),现在是大于等于(包括等于)。对于有序数组,我们可以使用二分查找来提高效率;对于无序数组,只能顺序遍历。根据用户的问题,他可能更关注有序数组的情况,但我们也提供顺序遍历的方法作为通用解。方法1:顺序遍历(适用于任意数组)遍历数组,找到第一个大于等于给定值的元素,返回其索引。方法2:二分查找(适用于升序排序的数组)在升序数组中,我们可以使用二分查找来快速定位。与之前查找大于的情况类似,但条件改为大于等于时,我们其实是在寻找下界(lowerbound)。注意:在二分查找中,我们要找的是第一个大于等于给定值的元素。步骤如下:-初始化low=0,high=数组长度-1,结果初始为-1(表示未找到)。-当low<=high时循环:mid=(low+high)向下取整如果中间元素大于等于给定值,那么记录这个位置(可能是候选结果),然后继续在左半边查找(因为可能有更小的索引满足条件),即high=mid-1否则,说明中间元素小于给定值,那么应该去右半边查找,即low=mid+1-最后返回记录的结果。但是,上述步骤在遇到等于的情况也会记录,并且继续向左寻找是否有更早的等于或者大于的值。这样,最终结果就是第一个大于等于给定值的索引。然而,还有一种常见的写法,是不使用result变量,而是最后返回low。因为当循环结束(low>high)时,low指向的就是第一个大于等于目标值的元素位置(前提是数组是升序的)。但要注意,如果所有元素都小于目标值,那么low会等于数组长度(即超出数组边界),此时表示未找到。我们可以选择其中一种。为了清晰,我们使用result变量的方式。但是,实际上有一个更简洁的方法:二分查找寻找下界的标准算法。它返回的是第一个大于等于目标值的位置,如果不存在,返回数组长度(或者-1,根据需要调整)。我们这里采用标准的下界二分查找(lower_bound),并返回索引。如果不存在,返回-1。标准写法:letlow=0;lethigh=arr.length;//注意这里high取数组长度,而不是length-1,这样当目标值大于所有元素时,返回arr.length(即数组末尾的下一个位置)while(low< high){constmid=Math.floor((low+high)/2);if(arr[mid]>=value){high=mid;}else{low=mid+1;}}//循环结束时,low就是第一个大于等于value的位置,但如果low在数组范围内(low>=0且low<arr.length)则满足,否则表示没有大于等于value的元素returnlow< arr.length?low:-1;或者,我们也可以使用传统的二分查找方式,但是要注意边界。为了与之前回答的二分查找结构类似,我们使用一个result变量来记录,但这里其实不需要,因为上面的标准写法更简洁。我们提供两种二分查找的实现供用户选择。根据用户上一次的问题,我们之前对于“大于”的情况使用了如下二分查找(升序数组):functionfindFirstIndexGreaterThanSorted(arr,value){letlow=0;lethigh=arr.length-1;letresult=-1;while(low<=high){constmid=Math.floor((low+high)/2);if(arr[mid]>value){result=mid;//记录当前满足条件的索引high=mid-1;//继续向左查找}else{low=mid+1;//向右查找}}returnresult;}现在要改成大于等于,我们只需要将条件改为`arr[mid]>=value`,然后做同样的事情:记录mid,然后继续向左查找(因为可能有更小的索引满足条件)。所以可以直接修改:functionfindFirstIndexGreaterThanOrEqualSorted(arr,value){letlow=0;lethigh=arr.length-1;letresult=-1;while(low<=high){constmid=Math.floor((low+high)/2);if(arr[mid]>=value){result=mid;//记录当前满足条件的索引high=mid-1;//继续向左查找更小的索引}else{low=mid+1;//向右查找}}returnresult;}注意:这个函数返回的是第一个大于等于value的元素的索引,如果没有则返回-1。但是,这个函数在数组小于value时,result不会被赋值,所以返回-1。符合要求。但是,上面的标准写法(lowerbound)更加高效,循环次数更少(因为每次循环只需要判断一次,而上述方法在找到候选结果后还需要继续缩小左边界)。但两种方法都是正确的,且时间复杂度相同。我们这里按照用户上一次的写法,方便用户对比。对于无序数组,顺序遍历的方法修改条件即可:functionfindFirstIndexGreaterThanOrEqual(arr,value){for(leti=0;i< arr.length;i++){if(arr[i]>=value){returni;}}return-1;}现在,我们按照用户要求,提供两种方法的代码。同时,根据用户要求,在回答中引用的部分添加引用标识(但这个问题中,我们主要提供代码,引用部分不多,但我们可以引用用户提到的引用[1]关于indexOf的说明,强调我们的方法和indexOf的不同)。另外,用户还提到引用[2]和[3]和[4],但这些似乎与问题无关,我们主要考虑引用[1]:indexOf只能用于查找相等,而我们需要的是范围查找。最后,生成相关问题。</think> 在 JavaScript 中查找数组中第一个大于等于指定值的元素索引,可以根据数组是否有序选择以下两种方法实现: ### 📌 方法1:顺序遍历(适用于任意数组) ```javascript function findFirstIndexGreaterThanOrEqual(arr, value) { for (let i = 0; i < arr.length; i++) { if (arr[i] >= value) { return i; // 返回第一个大于等于指定值的索引 } } return -1; // 未找到时返回-1 } // 示例 const nums = [2, 5, 9, 1, 8]; console.log(findFirstIndexGreaterThanOrEqual(nums, 5)); // 输出: 1 (元素5的索引,5>=5) console.log(findFirstIndexGreaterThanOrEqual(nums, 7)); // 输出: 2 (元素9的索引,9>=7) ``` ### 🔍 方法2:二分查找(适用于升序数组) ```javascript function findFirstIndexGreaterThanOrEqualSorted(arr, value) { let low = 0; let high = arr.length - 1; let result = -1; while (low <= high) { const mid = Math.floor((low + high) / 2); if (arr[mid] >= value) { result = mid; // 记录符合条件的索引 high = mid - 1; // 继续向左查找更小索引 } else { low = mid + 1; // 向右查找 } } return result; } // 示例(升序数组) const sortedNums = [1, 3, 5, 7, 9]; console.log(findFirstIndexGreaterThanOrEqualSorted(sortedNums, 5)); // 输出: 2 (元素5的索引,5>=5) console.log(findFirstIndexGreaterThanOrEqualSorted(sortedNums, 6)); // 输出: 3 (元素7的索引,7>=6) ``` ### ✨ 关键差异对比 | 特性 | 顺序遍历 | 二分查找 | |----------------|--------------------------|--------------------------| | **时间复杂度** | $O(n)$ | $O(\log n)$ | | **空间复杂度** | $O(1)$ | $O(1)$ | | **数组要求** | 任意顺序 | 必须升序排序 | | **最佳场景** | 小型/无序数组 | 大型有序数组 | | **查找逻辑** | 逐个检查元素直到找到目标 | 通过折半搜索快速定位目标 | > ⚠️ 注意: > 1. `Array.prototype.indexOf()`只能查找完全匹配的值,无法实现范围查询[^1] > 2. 二分查找的数组**必须预排序**,否则结果不可靠 > 3. 当元素值恰好等于目标值时,两种方法都会返回该元素的索引 ### 🚀 性能优化建议 1. 对**高频查询**的数据进行预排序,后续查询使用二分法提升效率 2. 当数组规模超过 10,000 个元素时,二分查找比顺序遍历快数百倍 3. 使用 `TypedArray` 处理数值型大数据集可获得额外性能提升
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值