【算法】—— 数组常见算法
1.1 问题描述
给你一个数组nums
和一个值val
,你需要原地移除所有数值等于val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
1.2 实现思路
使用快慢指针,快指针遍历数组,慢指针构成移除指定元素后的数组
fast
与慢指针slow
从0下标位置开始遍历fast
遍历数组,遇到不是val
值的,fast
处的数值赋值给slow
处,slow
加一
fast
遇到val
值,slow
不动,fast
继续遍历
slow
指向新数组的最后一个元素,slow+1
就是新数组的长度1.3 代码实现
int removeElement(int* nums, int numsSize, int val){
int fast = 0;
int slow = 0;
while (fast < numsSize)
{
if (nums[fast] != val)
{
nums[slow] = nums[fast];
slow++;
}
fast++;
}
return slow;
}
2.1 问题描述
给你一个 升序排列 的数组nums
,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的相对顺序应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums
的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么nums
的前 k 个元素应该保存最终结果。
将最终结果插入nums
的前 k 个位置后返回 k 。
不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例:
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
2.2 实现思路
依然使用快慢指针的方式实现,快指针遍历数组,慢指针构成新数组
fast
与slow
进行对比,若是相等,则fast
加一slow
加一,并得到fast
的值slow
指向新数组的最后一个元素,slow+1
就是新数组的长度2.3 代码实现
int removeDuplicates(int* nums, int numsSize){
int fast = 1;
int slow = 0;
while (fast < numsSize)
{
if (nums[fast] != nums[slow])
{
nums[slow+1] = nums[fast];
slow++;
}
fast++;
return slow+1;
}
3.1 问题描述
给你两个升序排列的整数数组nums1
和nums2
,另有两个整数 m 和 n ,分别表示nums1
和nums2
中的元素数目。
请你合并nums2
和nums1
中,使合并后的数组同样按升序排列。
示例:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
3.2 实现思路
方法一(开辟空间)
创建m+n大小的数组,依次对nums1
和nums2
中的元素遍历对比,将较小值存入新数组,比较完成后将剩余的数组全部复制到新数组中,并将新数组返回。很好理解,代码如下,这里就不细嗦了。
代码实现
int* merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int* nums = (int*)malloc((m+n)*sizeof(int));
int i = 0;
int j = 0;
int k = 0;
while (i<m && j<n)
{
if (nums1[i] < nums2[j])
{
nums[k] = nums1[i];
i++;
}
else
{
nums[k] = nums2[j];
j++;
}
k++;
}
while (i<m)
{
nums[k] = nums1[i];
i++;
k++;
}
while (j<n)
{
nums[k] = nums2[j];
j++;
k++;
}
return nums;
}
方法二(原地合并数组)
若是从前往后遍历,则每一次插入都要移动大量数据,所以我们不走寻常路,从后往前遍历,大的往后放
k
,让他指向合并数组的末尾,就是m+n-1
下标处(m、n分别是两个数组的长度),指针i
和j
指向两数组末尾i
和j
处数据的大小,将大的挪到k
处,并指向下一个要比较的数,k
也向前挪动一格num1
遍历完毕num2
还没遍历完,将剩余的数据一定是最小的,全部插入到num1
前nums1
剩余则已经是最小值在数组中了,不用处理,最后将数组长度返回即可代码实现
int* merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int i = m - 1;
int j = n - 1;
int k = m + n - 1;
while (i>=0 && j>=0)
{
if (nums1[i] > nums2[j])
{
nums1[k] = nums1[i];
i--;
}
else
{
nums1[k] = nums2[j];
j--;
}
k--;
}
while (j>=0)
{
nums1[k] = nums2[j];
k--;
j--;
}
return nums1;
}
4.1 问题描述
给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
4.2 实现思路
方法一(一个一个移)
一次移动一个元素,一共移动k次,代码如下
void rotate(int* nums, int numsSize, int k)
{
k = k % numsSize;
int i = 0;
for (i=0; i<k; i++) //进行k次移动
{
//所有元素移动一格
int temp = nums[numsSize-1];
int j = 0;
for (j=0; j<numsSize-1; j++)
{
nums[j+1] = nums[j];
}
nums[0] = temp;
}
}
方法二(三步反转法)
将0 ~ k-1
的数据进行反转,再对k ~ numsSize-1
的数据进行反转,最后对整个数组来个反转,就得到了旋转数组
当k=3时,对 [1, 2, 3, 4, 5, 6, 7, 8] 反转:
代码实现
//反转数组
void reverse(int *nums, int left, int right)
{
while (left < right)
{
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
++left;
--right;
}
}
//旋转数组
void rotate(int* nums, int numsSize, int k){
if (numsSize < 2)
{
return;
}
k = k % numsSize;
reverse(nums, 0, numsSize-1-k);
reverse(nums, numsSize-k, numsSize-1);
reverse(nums, 0, numsSize-1);
}