LeetCode数组经典题目:额外题目

1. LeetCode1365. 有多少小于当前数字的数字

思路:
1.使数组排序,从小到大
2.创建哈希表记录每个元素最左的下标

class Solution {
public:
    vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
        //结果集合
        vector<int>vec=nums;
        //排序数组
        sort(vec.begin(),vec.end());
        //哈希表记录下标
        map<int,int>recorded;
        for(int i=nums.size()-1;i>=0;i--){//从右到左遍历数组,这样记录的就是最左边的下标
            recorded[vec[i]]=i;
        }
        //处理结果集
        for(int i=0;i<nums.size();i++){
            vec[i]=recorded[nums[i]];
        }
        return vec;
    }
};

2. LeetCode941. 有效的山脉数组

思路:
1.利用bool变量up判断现在是否在上升期
2.当第一次下降时,把up置为false,且之后都必须为false。如果下降或平地之后还有上升过程,说明就是无效的山脉

方法一:状态法
class Solution {
public:
    bool validMountainArray(vector<int>& arr) {
        //山脉数组先递增,所以up先置为true
        bool up=true;
        int peak=0;
        //遍历山脉数组
        for(int i=1;i<arr.size();i++){
            if(arr[i-1]==arr[i]){//不是严格递增或递减,有平地,无效山脉
                return false;
            }
            else if(arr[i-1]<arr[i]){
                if(!up){//在非上升过程中遇到上升过程,无效山脉
                    return false;
                }
                peak=i;//记录顶点坐标
            }else{
                up=false;
            }
        }
        //能出循环,顶点坐标不是0且最终趋势是下降(即up==false),就说明是有效的山脉数组
        return peak!=0&&!up;
    }
};

方法二:双指针法
class Solution {
public:
    bool validMountainArray(vector<int>& arr) {
        //左指针
        int left=0;
        //右指针
        int right=arr.size()-1;
        //注意不要越界
        while(left<arr.size()-1&&arr[left]<arr[left+1])left++;
        while(right>0&&arr[right]<arr[right-1])right--;

        //left和right必须都指向顶点,且山脉不是单纯上坡或下坡
        return left==right&&left!=0&&right!=arr.size()-1;
    }
};

3. LeetCode1207. 独一无二的出现次数

class Solution {
public:
    bool uniqueOccurrences(vector<int>& arr) {
        unordered_map<int,int>recorded;//记录每个元素出现的次数
        set<int>count;//记录出现过的词频
        
        for(int i=0;i<arr.size();i++){
            recorded[arr[i]]++;
        }

        for(auto &p:recorded){
            if(count.find(p.second)==count.end()){//如果count不曾出现当前词频
                count.insert(p.second);
            }else{
                return false;
            }
        }
        
        return true;
    }
};

4. LeetCode283. 移动零

双指针:
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        if(nums.size()==1)return;
        int left=0;//左边第一个为0的
        int right=0;//left右边第一个不为0的
        while(left<nums.size()&&right<nums.size()){
            while(left<nums.size()&&nums[left]!=0)left++;//最好把越界条件放在最前面
            right=left;
            while(right<nums.size()&&nums[right]==0)right++;//最好把越界条件放在最前面
            if(left<nums.size()&&right<nums.size())swap(nums[left],nums[right]);
        }
    }
};

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slowIndex=0;
        for(int fastIndex=0;fastIndex<nums.size();fastIndex++){
            if(nums[fastIndex]!=0){//先把不为0的值放在数组左边
                nums[slowIndex++]=nums[fastIndex];
            }
        }

        while(slowIndex<nums.size()){//后面赋0
            nums[slowIndex++]=0;
        }
    }
};

5. LeetCode189. 轮转数组

思路:
假设以原数组的状态(即所有元素相对顺序一致)定义为有序,此种状态下,nums从0到nums.size()-1都是有序的
轮转后的数组:
1. 0~k-1有序
2. k~nums.size()-1有序
3. [0,k-1][k,nums.size()-1]各自有序,但二者在一起不有序

先将整体反转,这样就使得右边的能够到左边,左边的能够到右边,再以k为界限让两边局部反转,使得两边局部有序
注意:必须先让整体反转使各元素到了正确的区域,才能够局部反转使得局部有序

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        k=k%nums.size();
        reverse(nums.begin(),nums.end());//反转整个数组
        reverse(nums.begin(),nums.begin()+k);//反转前k个元素[0,k-1]
        reverse(nums.begin()+k,nums.end());//反转后续所有元素[k,nums.size()-1]
    }
};

6. LeetCode724. 寻找数组的中心下标

class Solution {
public:
    int pivotIndex(vector<int>& nums) {
        int sum=0;//数组总和
        for(auto&val:nums){
            sum+=val;
        }
        int leftSum=0;//中心下标左边元素和
        for(int i=0;i<nums.size();i++){
            //中心左边右边元素和rightSum=sum-leftSum-nums[i],注意也要减去中心下标的值
            if(leftSum==sum-leftSum-nums[i])return i;
            leftSum+=nums[i];
        }
        return -1;
    }
};

7. LeetCode34. 在排序数组中查找元素的第一个和最后一个位置

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.size()==1){
            vector<int>vec0(2,0);
            vector<int>vec1(2,-1);
            return (nums[0]==target)?vec0:vec1;
        }
        int targetIndex=-1;
        int left=0;
        int right=nums.size()-1;
        //二分查找
        while(left<=right){
            int mid=left+((right-left)>>1);
            if(nums[mid]==target){
                targetIndex=mid;
                break;
            }else if(nums[mid]>target){
                right=mid-1;
            }else{
                left=mid+1;
            }
        }
        if(targetIndex==-1){//说明未找到目标值
            return{-1,-1};
        }else{
            left=targetIndex;
            right=targetIndex;
while(left>=0&&nums[left]==target)left--;//找到左边边界
            while(right<=nums.size()-1&&nums[right]==target)right++;//找到右边边界
        }
        return {left+1,right-1};
    }
};

8. LeetCode922. 按奇偶排序数组 II

class Solution {
public:
    vector<int> sortArrayByParityII(vector<int>& nums) {
        int evenIndex=0;//偶数下标
        int oddIndex=1;//奇数下标
        while(oddIndex<nums.size()&&evenIndex<nums.size()){
            while(oddIndex<nums.size()&&oddIndex%2==1&&nums[oddIndex]%2==1)oddIndex+=2;//元素合理就下一个奇数元素
            while(evenIndex<nums.size()&&evenIndex%2==0&&nums[evenIndex]%2==0)evenIndex+=2;//元素合理就下一个偶数元素
            if(oddIndex<nums.size()&&evenIndex<nums.size())swap(nums[oddIndex],nums[evenIndex]);//必须保证不越界
        }
        return nums;
    }
};

1.[left,right],闭区间
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        //排序数组、O(logn) -> 二分查找
        int left=0;
        int right=nums.size()-1;
        while(left<=right){//因为target在[left,right]区间,所以left==right时,区间仍然有效
            int mid=left+((right-left)>>1);
            if(nums[mid]==target){
                return mid;
            }else if(nums[mid]<target){
                left=mid+1;
            }else{
                right=mid-1;
            }
        }
        // 分别处理如下四种情况
        //目标值等于数组中某一个元素  return middle;
        // 目标值在数组所有元素之前  [0, -1],0 = -1 + 1 = right+1
        // 目标值插入数组中的位置 [left, right],return  right + 1,因为mid是向下取整的
        // 目标值在数组所有元素之后的情况 [left, right], 因为是右闭区间,所以 return right + 1
        return right+1;
    }
};

2.[left,right),左闭右开区间
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size();
        while(left<right){
            int mid=left+((right-left)>>1);
            if(nums[mid]==target){
                return mid;
            }else if(nums[mid]<target){
                left=mid+1;
            }else{
                right=mid;
            }
        }
        return right;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jomo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值