消失的数字和轮转数组

文章介绍了在LeetCode平台上两道编程题目:如何通过冒泡排序、位运算和数学方法找到数组中缺失的数字,以及如何处理数组轮转问题。作者详细解释了每种思路的工作原理和代码实现。

1.消失的数字

以下是题目链接:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

首先来简单看一下这道题。这道题就是告诉我们,给出一个数组nums,数组里面有n个数字,这n个数字的内容就是一个连续的0~n,这个数组不一定是按顺序来的,但一定是0~n的数字。比如0,1,2,3,4,5,6。但是在这个数组里面就缺少了一个数字。现在题目的要求就是让我们找出这个数字。

对于这道题,我目前有三个思路。

思路1

先使用冒泡排序来把这个数组变成升序的数组,然后把i丢进去遍历数组内容,用当前的数字+1,如果不等于下一个数字,那么这个数字就是我们需要寻找的数字。但是当我想用这个思路的时候,计算了一下时间复杂度,发现这个思想的时间复杂度是O(N^2),并不符合题目的要求。

思路2

使用位运算的思想。首先,先让x = 0和数组里面的内容进行按位与的操作。最后让异或完后的x跟完整的0~9来进行异或运算,最终就会得出那个缺失的数字。

关于这个思想,说实话,我觉得也是挺复杂的。这就有点像我之前写过的单身狗2,这道题的要求就是在一个数组里面找出只出现过一次的数字,大概的思路也是用0跟数组里面的数字进行异或运算的操作,相同的数字就会消失,最后只剩下两个数字,再用位运算的方法把这两个数字分别输出。

异或的操作运算是碰到相同的数字就为0。于是我总结了一个规律,在用x = 0与数组的内容进行异或操作的时候,可以把x理解为一个袋子,把数组里面的数字一个一个地往袋子里面装,如果袋子里面装了两个相同的数字,则这两个数字就会被"湮灭"掉,消失不见。最后只剩下那个只出现过一次的数字。

那这道题的代码就可以写出来了

#include <stdio.h>
int main()
{
	int i = 0;
	int arr[10] = { 1,2,3,4,5,6,7,8,10 };
	int x = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for ( i = 0; i < sz; i++)
	{
		x ^= arr[i];//首先把数组里面的数字一个个装进x这个袋子里面
	}
	for ( i = 0; i <= sz; i++)
	{
		x ^= i;//然后用x这个袋子里面的数字与完整的0~n来进行异或操作,如果有相同的
               //则就会发生"湮灭",如果没有发现相同的,就输出那个数字,而那个输出
               //的数字,恰好就是缺失的数字
	}
	printf("%d", x);
	return 0;
}

思路3

这就涉及到一个数学角度的运算。

首先把不缺失数字的数组0~n进行一个和的相加,这里就用到了等差数列的相加,((头+尾)*个数)/2来算出总和,在让总和一个个地减去数组里面的数字,最后得出的就是缺失的数字。

int missingNumber(int* nums, int numsSize){
    int sum = ((0 + numsSize)*(numsSize + 1))/2;
    int sum1=0;
    for(int i = 0;i<numsSize;i++)
    {
        sum-=nums[i];
    }
    return sum;
}

2.轮转数组

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

这个题看题目也很容易理解。

代码如下:

#include <stdio.h>
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int k = 0;
	int temp = 0;
	scanf("%d", &k);
	int count = k;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int newArr[5];
	for (int i = 0; i<sz; i++)
	{
		newArr[(i + k) % sz] = arr[i];
	}
	for (int i = 0; i < sz; i++)
	{
		arr[i] = newArr[i];
	}
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

水了水了~

在解决 LeetCode 上的轮转数组问题时,需要考虑多个关键点,包括如何处理边界情况、如何避免额外的空间使用以及如何高效地进行元素移动。以下是几种常见的解法: ### 解法一:使用临时数组 这种方法的基本思路是将数组中最后 `k` 个元素存储到一个临时数组中,然后将原数组中的前面部分向右移动 `k` 个位置,最后将临时数组中的元素放回到原数组的前面。 ```java class Solution { public void rotate(int[] nums, int k) { int length = nums.length; k = k % length; // 处理k大于数组长度的情况 int[] temp = new int[k]; // 将最后k个元素存储到临时数组中 for (int i = 0; i < k; i++) { temp[i] = nums[length - k + i]; } // 将原数组前面的元素向右移动k个位置 for (int i = length - 1; i >= k; i--) { nums[i] = nums[i - k]; } // 将临时数组中的元素放到原数组的前面 for (int i = 0; i < k; i++) { nums[i] = temp[i]; } } } ``` 这种方法的时间复杂度为 O(n),空间复杂度为 O(k)[^2]。 --- ### 解法二:三次反转 这种解法不需要额外的空间,而是通过三次反转操作来实现数组轮转。具体步骤如下: 1. 反转数组的后 `k` 个元素。 2. 反转数组的前 `n-k` 个元素。 3. 最后反转整个数组。 ```c void reverse(int* nums, int left, int right) { while (left < right) { int tmp = nums[left]; nums[left] = nums[right]; nums[right] = tmp; left++; right--; } } void rotate(int* nums, int numsSize, int k) { if (k > numsSize) { k %= numsSize; } reverse(nums, numsSize - k, numsSize - 1); // 右边倒置 reverse(nums, 0, numsSize - k - 1); // 左边倒置 reverse(nums, 0, numsSize - 1); // 整体倒置 } ``` 这种方法的时间复杂度为 O(n),空间复杂度为 O(1)[^3]。 --- ### 解法三:直接计算新索引 这种方法利用了模运算来计算每个元素的新位置,并将其复制到新的数组中。之后再将新数组的内容复制回原数组。 ```java public class Solution { public void rotate(int[] nums, int k) { int length = nums.length; int[] temp = new int[length]; // 将原数组的元素放到新数组中对应的位置 for (int i = 0; i < length; i++) { temp[(i + k) % length] = nums[i]; } // 将temp数组的内容复制回nums数组 for (int i = 0; i < length; i++) { nums[i] = temp[i]; } } } ``` 这种方法的时间复杂度为 O(n),空间复杂度为 O(n)[^4]。 --- ### 解法四:数组切片(Python) 在 Python 中,可以利用数组切片来实现轮转数组的操作。然而需要注意的是,在函数内部修改数组时,必须直接对原数组进行操作,而不是创建一个新的数组引用。 ```python class Solution: def rotate(self, nums: List[int], k: int) -> None: n = len(nums) k = k % n # 处理k大于数组长度的情况 nums[:] = nums[-k:] + nums[:-k] ``` 这种方法的时间复杂度为 O(n),空间复杂度为 O(n)[^1]。 --- ### 相关问题 1. 如何在不使用额外空间的情况下实现轮转数组? 2. 为什么在轮转数组问题中需要对 `k` 进行取模操作? 3. 在使用数组切片方法时,为什么不能直接赋值给 `nums` 而要使用 `nums[:]`? 4. 三次反转法的具体实现原理是什么? 5. 使用临时数组的方法直接计算新索引的方法有什么区别?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值