[线性表–数组OJ] (C/C++实现)
文章目录
- [线性表--数组OJ] (C/C++实现)
- @[toc]
- 1.[leetcode 17.04. 消失的数字](https://leetcode-cn.com/problems/missing-number-lcci/)
- 2.[ 数组中数字出现的次数(leetcode)](https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/)
- 3.[ 二分查找(leetcode)](https://leetcode-cn.com/problems/binary-search/)
- 4.[移除元素(leetcode)](https://leetcode-cn.com/problems/remove-element/)
- 5.[ 删除有序数组中的重复项(leetcode)](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/)
- 6.[合并两个有序数组(leetcode)](https://leetcode-cn.com/problems/merge-sorted-array/)
- 7.[轮转数组(leetcode)](https://leetcode-cn.com/problems/rotate-array/)
1.leetcode 17.04. 消失的数字
题目描述:
示例:nums=[3,0,4,5,7,1,2],n=7 输出:6
解法一:扫一遍数组求出实际总和sum1
,再利用==等差数列求和公式==,求出期望的总和sum2
。
sum2
-sum1
即为缺失的数字。
代码实现:
class Solution {
public:
int missingNumber(vector<int>& nums) {
int sum1=0;
int sum2=(nums.size()+1)*nums.size()/2;
for(int i=0;i<nums.size();i++)
sum1+=nums[i];
return sum2-sum1;
}
};
解法二(位运算):
首先,我们先来介绍一下异或(^)运算
借助位运算我们可以通过如下操作把这道题解决:
- 把
nums
数组中的所有元素异或一遍的结果存在ret
中 - 把
ret
再同1到n的所有数异或一遍,ret
即为缺失的数字
因为,经历了上述操作,1-n出现的数字都异或了两遍,缺失的数字仅异或了一遍
代码实现:
class Solution {
public:
int missingNumber(vector<int>& nums) {
int ret=0;
for(int i=0;i<nums.size();i++)
ret^=nums[i];
for(int i=1;i<=nums.size();i++)
ret^=i;
return ret;
}
};
2. 数组中数字出现的次数(leetcode)
题目描述:
一个整型数组 nums
里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例: nums=[1,1,2,3,3,4,5,5] 输出:[2,4]
代码实现:
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int val=0,flag=1;
for(int i=0;i<nums.size();i++)
val^=nums[i];
while((val&flag)==0)
{
flag<<=1;
}
int a=0,b=0;
for(int i=0;i<nums.size();i++)
{
if(nums[i]&flag)
a^=nums[i];
else
b^=nums[i];
}
return {a,b};
}
};
3. 二分查找(leetcode)
题目描述:
3-1.target 定义在[left,right] (左右都是闭区间的写法)
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
while(left<=right)
{
int mid=(left+right)/2;
if(nums[mid]>target)
right=mid-1;
else
{
if(nums[mid]<target)
left=mid+1;
else
return mid;
}
}
return -1;
}
};
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
3.2. target定义在[left,right)的开区间中(左开右闭区的写法)
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size();//开区间
while(left<right)//等于的情况没有意义
{
int mid=(left+right)/2;//可写成防止溢出的写法
if(nums[mid]>target)//左区间
right=mid;
else
{
if(nums[mid]<target)
left=mid+1;
else
return mid;
}
}
return -1;
}
};
解释:
①.while循环中判断条件等于就没有意义
②.target落在左区间内mid-1可能取到而mid不能取到,所以right=mid
4.移除元素(leetcode)
题目描述:
解法一:暴力循环,移动覆盖 O(N^2)
代码实现:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int sz=nums.size();
for(int i=0;i<sz;i++)
{
if(val==nums[i])
{
for(int j=i;j<sz-1;j++)
nums[j]=nums[j+1];
i--;
sz--;
}
}
return sz;
}
};
**解法二:双指针 **O(N)
这里我们介绍两种双指针实现方式来完成这一题
-
快慢指针
思路:
- 我们开始时使得
slow
与fast
都为0 - 如果
nums[fast] != val
和那么fast
和slow
一起走,如果fast
指的值等于val
,fast
走而slow
不走 - 每次
fast
和slow
实现一起走的时候,即为把fast
指的的值给slow
这样我们可以证明:
fast
走完一遍时,0~slow
所有的值都不等于val
class Solution { public: int removeElement(vector<int>& nums, int val) { int slow=0; for(int fast=0;fast<nums.size();fast++) if(nums[fast]!=val) nums[slow++]=nums[fast]; return slow; } };
- 我们开始时使得
-
左右指针
思路:
代码实现:
class Solution { public: int removeElement(vector<int>& nums, int val) { int left=0,right=nums.size()-1; while(left<=right) { if(val==nums[left]) { nums[left]=nums[right]; right--; } else left++; } return left; } };
5. 删除有序数组中的重复项(leetcode)
题目描述:
思路:
-
升序排列:说明数组中重复出现的元素一定是连续的,不可能间断
-
我们用k表示删除元素(重复元素)的个数,
val
记录==上一层循环的nums[i]== -
在当前循环层中如果
val==nums[i]
,说明该层循环nums[i]
为重复元素,根据我们在2中给的定义,k
需要加1,val
可以更新(但没必要) -
当前循环层中的值不等于
val
,说明这个不是重复元素,i-k
位置上的元素即为在[0,i)这个区间中第一个出现重复的元素(1中连续性结合2中定义得出) -
扫完一遍后,
nums.size()-k
即为剩下元素的个数,且满足[0,nums.size()-k]区间上·不存在重复元素。
代码实现:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k=0,val=nums[0];
for(int i=1;i<nums.size();i++)
{
if(val==nums[i])
{
k++;
}
else
{
val=nums[i];
nums[i-k]=nums[i];
}
}
return nums.size()-k;
}
};
6.合并两个有序数组(leetcode)
题目描述:
思路:
因为两个数组都排好序,只需同时扫两个数组,每次把小的放到ans
数组中
若m==n
扫完一遍后,两数组按序插入到ans
中
如果m != n
最后只需把长的数组后面的元素统统放到ans
中即可。
(这也是我们后续要写的==归并排序==的归并操作)
代码实现:
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
vector<int> ans;
int i=0,j=0;
while(i<m&&j<n)
{
if(nums1[i]<nums2[j])
ans.push_back(nums1[i++]);
else
ans.push_back(nums2[j++]);
}
while(i<m)
ans.push_back(nums1[i++]);
while(j<n)
ans.push_back(nums2[j++]);
nums1=ans;
}
};
7.轮转数组(leetcode)
解法一(原数组与轮转数组间的下标关系):
代码实现:
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int size=nums.size();
vector<int> ans(size);
for(int i=0;i<size;i++)
ans[(i+k)%size]=nums[i];
nums=ans;
}
};
解法二:三次逆置(个人觉得这个方法超秀😍😍)
代码实现:
class Solution {
public:
void reverse(vector<int>& nums, int begin, int end)
{
int i=begin,j=end;
while(i<j)
{
int tmp=nums[i];
nums[i]=nums[j];
nums[j]=tmp;
i++,j--;
}
}
void rotate(vector<int>& nums, int k) {
int size=nums.size();
reverse(nums,0,size-k%size-1);
reverse(nums,size-k%size,size-1);
reverse(nums,0,size-1);
}
};