备战秋招之刷题篇 线性表之数组

本文深入探讨了一系列数组操作的算法问题,包括删除有序数组中的重复项、搜索旋转排序数组、寻找两个正序数组的中位数以及最长连续序列等。每个问题都提供了高效的解决方案,如利用二分查找、哈希表和位运算,确保在限制的时间和空间复杂度内完成任务。此外,还讨论了如何在实际应用中优化这些算法,提高程序性能。
摘要由CSDN通过智能技术生成

删除有序数组中的重复项(easy)

首先分析题目:
有序的话说明只要相邻的两个做比较即可,不需要把当前数值和后面的全都比较一遍
在这里插入图片描述
其实就是改了nums这个vector的前index个值,后面的值并没有改,根据return的修改后的长度将修改后的数组输出
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.empty())
            return 0;            
        int index=0;
        for(int i=1;i<nums.size();i++)
        {
            if(nums[index]!=nums[i])
            {
                nums[++index]=nums[i];//先index加1,再赋值
            }
        }
        return index+1;
    }
};

删除有序数组中的重复项 II(medium)

和上面一题类似,只是允许重复的次数变为2
如果说次数为3,则只需将代码中的2变为3,第一步判断size那里3变为4
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size()<3)
            return nums.size();
        int index=2;
        for(int i=2;i<nums.size();i++)
        {
            if(nums[i]!=nums[index-2])
            {
                nums[index]=nums[i];//先赋值,再index加1
                index=index+1;
            }
        }
        return index;
    }
};

搜索旋转排序数组(medium)

首先分析题目,medium题肯定不能直接全对比一遍,本题主要考二分搜索,这种情况下
时间复杂度:O(log n)
空间复杂度:O(1)
在这里插入图片描述

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int first=0;
        int last=nums.size();
        while(first!=last)
        {
            const int mid=first+(last-first)/2;
            if(nums[mid]==target)
            {
                return mid;
            }
            if(nums[first]<nums[mid])
            {
                if(nums[first]<=target && target<nums[mid])
                {
                    last=mid;
                }
                else
                    first=mid+1;
            }
            else
            {
                if(nums[mid]<target && target<=nums[last-1])
                {
                    first=mid+1;
                }
                else
                    last=mid;
            }
        }
        return -1;
    }
};

搜索旋转排序数组II(medium)

首先分析题目,和上一题乍一看似乎是一样的,想到用二分法来解决,但是由于数组中有重复的数字,就会出现例如 nums=[3,1,2,3,3,3,3],target=2,首次二分时无法判断区间 [0,3] 和区间 [4,6] 哪个是有序的。这种情况就得将左边界加1,右边界减1
在这里插入图片描述

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int size=nums.size();
        if(size==0)
            return false;
        if(size==1)
        {
            return nums[0]==target;
        }
        int first=0;
        int last=size-1;
        while(first<=last)
        {
            int mid=(last+first)/2;
            if(nums[mid]==target)
                return true;
            if(nums[first]==nums[mid]&&nums[mid]==nums[last])
            {
                ++first;
                --last;
            }
            else if(nums[first]<=nums[mid])
            {
                if(nums[first]<=target&&target<nums[mid])
                {
                    last=mid-1;
                }
                else
                    first=mid+1;
            }
            else
            {
                if(nums[mid]<target&&target<=nums[last])
                {
                    first=mid+1;
                }
                else    
                    last=mid-1;
            }
        }
        return false;
    }
};

寻找两个正序数组的中位数(hard)

题目:给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。要求时间复杂度为O(log(m+n))
分析题目:首先最简单的方法是先归并,然后找到中位数,但是这种方式的时间复杂度为O(m+n),看到log就想到二分查找
这个题目可以转化为找第k大的数
在这里插入图片描述

class Solution {
public:
    int getKthElement(vector<int>& nums1,vector<int>& nums2, int k)
    {
         /* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
         * 这里的 "/" 表示整除
         * nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
         * nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
         * 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
         * 这样 pivot 本身最大也只能是第 k-1 小的元素
         * 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
         * 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
         * 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
         */
        int m=nums1.size();
        int n=nums2.size();
        int index1=0,index2=0;
        while(true)
        {
            //边界情况
            if(index1==m)
            {
                return nums2[index2+k-1];
            }
            if(index2==n)
            {
                return nums1[index1+k-1];
            }
            if(k==1)
            {
                return min(nums1[index1],nums2[index2]);
            }
            
            //正常情况
            int newIndex1=min(index1+k/2-1,m-1);
            int newIndex2=min(index2+k/2-1,n-1);
            int pivot1=nums1[newIndex1];
            int pivot2=nums2[newIndex2];
            if(pivot1<=pivot2)
            {
                k-=newIndex1-index1+1;
                index1=newIndex1+1;
            }
            else
            {
                k-=newIndex2-index2+1;
                index2=newIndex2+1;
            }

        }
    }
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int totalLength=nums1.size()+nums2.size();
        if(totalLength%2==1)
        {
            return getKthElement(nums1,nums2,(totalLength+1)/2);
        }
        else
        {
            return (getKthElement(nums1,nums2,totalLength/2)+getKthElement(nums1,nums2,totalLength/2+1))/2.0;
        }
    }
};

最长连续序列(hard)

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
要求时间复杂度为O(n)
首先分析题目:如果允许O(nlogn)的复杂度,那么可以先排序。由于序列里的元素是无序的,又要求O(n),首先要想到使用哈希表。用哈希表记录每个元素是否使用,对每个元素,以该元素为中心,往左右扩张。对x先往左扩张,如果存在x-1,则跳过当前数,如果不存在x-1,则以这个数为起点,判断x+1,x+2…是否存在,并更新最大长度。

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> num_set;
        for(const int& num : nums)
        {
            num_set.insert(num);
        }
        int longestStreak=0;
        for(const int& num : num_set)
        {             
            if(!num_set.count(num-1))
            {
                int currentnum=num;
                int currentsteak=1;
                while(num_set.count(currentnum+1))
                {
                    currentnum += 1;
                    currentsteak += 1;
                }
                longestStreak = max(currentsteak,longestStreak);
            }            
        }
        return longestStreak;
    }
};

两数之和(easy)

在这里插入图片描述
首先分析题目,如果暴力解的话,时间复杂度O(n^2),会超时,因此可以用一个哈希表,来记录数值和下标的对应关系,这种情况下时间复杂度为O(n)。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> Map;
        for(int i=0;i<nums.size();i++)
        {
            Map[nums[i]]=i;
        }
        vector<int> res;
        for(int j=0;j<nums.size();j++)
        {
            int k=target-nums[j];
            if( (Map.count(k))&&(Map[k]!=j) )//这个地方要注意先判断它是否存在,并且要不是相同的数,比如2和2
            {
                res.push_back(j);
                res.push_back(Map[k]);
                break;
            }
        }
        return res;
    }
};

三数之和(medium)

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思路:首先将其排个序,利用三指针来做。
在这里插入图片描述

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]>0)
            {
                return ans;
            }
            if(i>0 && nums[i]==nums[i-1])//对第一个指针进行判断,看它是否和它前面一个数相等,相等就跳过
            {
                continue;
            }
            int left=i+1;
            int right= nums.size()-1;
            while(right>left)
            {
                if(nums[i]+nums[left]+nums[right]>0)
                {
                    right--;
                }
                else if(nums[i]+nums[left]+nums[right]<0)
                {
                    left++;
                }
                else
                {
                    ans.push_back(vector<int>{nums[i],nums[left],nums[right]});
                    while(right>left && nums[right]==nums[right-1])
                        right--;
                    while(right>left && nums[left]==nums[left+1])
                        left++;
                    right--;
                    left++;
                }
            }
        }
        return ans;
    }
};


最接近的三数之和(medium)

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
首先分析题目:和上面三数之和这道题做法相似,只是要加个判断差值大小。

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        int ans=1e7;
        if(nums.size()<4)
            return nums[0]+nums[1]+nums[2];
        else
        {
            sort(nums.begin(),nums.end());
            for(int i=0;i<nums.size();i++)
            {
                if(i>0 && nums[i]==nums[i-1])
                    continue;
                int left=i+1;
                int right=nums.size()-1;
                while(left<right)
                {
                    int sum=nums[left]+nums[right]+nums[i];
                    if(abs(sum-target)<abs(ans-target))
                    {
                        ans=sum;
                    }
                    if(sum==target)
                    {
                        return sum;
                    }
                    if(sum<target)
                    {
                        left++;
                    }
                    if(sum>target)
                        right--;
                }
            }
        }
        return ans;
    }
};

下一个排列

实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
分析题目:一种特殊情况就是给的排列是最大排列,此时输出应该是最小的排列,其余情况
在这里插入图片描述

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n=nums.size();
        for(int i=n-1;i>0;i--)
        {
            if(nums[i]<=nums[i-1] && i>1)
                continue;
            if(nums[i]<=nums[i-1] && i==1)
            {
                sort(nums.begin(),nums.end());
                break;
            }              
            if(nums[i-1]<nums[i])
            {
                for(int j=n-1;j>=i;j--)
                {
                    if(nums[j]>nums[i-1])
                    {
                        int temp=nums[i-1];
                        nums[i-1]=nums[j];
                        nums[j]=temp;
                        sort(nums.begin()+i,nums.end());
                        break;
                    }
                    else
                        continue;
                }
                break;
            }
        }
    }
};

有效数独(medium)

请你判断一个 9x9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
题目分析:暴力解的话,可以迭代三次,分别判断行、列、块三个部分是否有效,迭代三次耗时久,并且元素被重复访问三次,由此想到减少迭代次数,只通过一次迭代,在迭代的过程中,判断当前数是否已经存在行列块三个数组中,例如数字7,则行列块中第7个元素是否都为0,为0表示还未出现,这种情况可以将其值变为1,为1表示出现了一次,其余情况表示已经出现过,返回false

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        vector<vector<int>> row (9,vector<int>(9,0));
        vector<vector<int>> col (9,vector<int>(9,0));
        vector<vector<int>> box (9,vector<int>(9,0));
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                if(board[i][j]=='.')
                    continue;
                int val=board[i][j]-'1';
                int box_index=(i/3)*3+j/3;
                if(row[i][val]==0 && col[j][val]==0 && box[box_index][val]==0)
                {
                    row[i][val]=1;
                    col[j][val]=1;
                    box[box_index][val]=1;
                }
                else
                    return false;
            }
        }
        return true;
    }
};

接雨水(hard)

在这里插入图片描述
首先分析题目:暴力的解法就是,对于每一个柱子,都判断一下它左边和右边柱子的高度,选出其中较小的高柱子,再减去自身的高度就是当前位置能装的雨水,只是这种方式时间复杂度为O(n^2)。
时间复杂度为O(n),空间复杂度为O(1)的解法为双指针。通过一次遍历即可。
想法是:接水的高度其实是由两边高柱子中较矮的那个柱子决定的,因此在从左往右遍历的过程中,接的水是由左边柱子决定的,在从右往左遍历的过程中,接的水是由右边柱子决定的,
在这里插入图片描述

class Solution {
public:
    int trap(vector<int>& height) {
        int left=0,right=height.size()-1;
        int ans=0;
        int left_max=0, right_max=0;
        while(left<right)
        {
            if(height[left]<height[right])
            {
                if(height[left]>=left_max)
                {
                    left_max=height[left];
                }
                else
                {
                    ans+=left_max-height[left];
                }
                left++;
            }
            else
            {
                if(height[right]>=right_max)
                {
                    right_max=height[right];
                }
                else
                {
                    ans+=right_max-height[right];
                }
                right--;
            }
        }
        return ans;
    }
};


旋转图像(medium)

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
在这里插入图片描述
首先分析题目:
在这里插入图片描述
这种方法时间复杂度为O(n^2),空间复杂度为O(1)

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n=matrix.size();
        //水平翻转
        for(int i=0;i<n/2;i++)
        {
            for(int j=0;j<n;j++)
            {
                swap(matrix[i][j],matrix[n-i-1][j]);
            }
        }
        //主对角线翻转
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                swap(matrix[i][j],matrix[j][i]);
            }
        }
    }
};

加一(easy)

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
分析题目:
在这里插入图片描述

class Solution {
    public:
    vector<int> plusOne(vector<int>& digits) {
        int n=digits.size();
        for(int i=n-1;i>=0;i--)
        {
            digits[i]++;
            digits[i]=digits[i]%10;
            if(digits[i]!=0)//说明无需进位
                return digits;
        }
        vector<int> res(n+1,0);//上面的情况还没有return说明是999->1000这种情况
        res[0]=1;
        return res;
    }
};

爬楼梯(easy)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
首先分析题目:
设f(n)是爬n阶楼梯的不同方法数,为了爬到第n阶楼梯有两种选择:
从n-1阶爬一阶到n阶;
从n-2阶爬二阶到n阶;
即:f(n)=f(n-1)+f(n-2);这是一个斐波那契数列
时间复杂度O(n),空间复杂度O(1)

class Solution {
public:
    int climbStairs(int n) {
        int prev=0;
        int cur=1;
        for(int i=0;i<n;i++)
        {
            int tmp=cur;
            cur=tmp+prev;
            prev=tmp;
        }
        return cur;
    }
};

格雷编码

格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。即使有多个不同答案,你也只需要返回其中一种。
格雷编码序列必须以 0 开头。
首先分析题目:
在这里插入图片描述

class Solution {
public:
    vector<int> grayCode(int n) {
        vector<int> ans;
        int size=pow(2,n);
        for(int i=0;i<size;i++)
        {
            ans.push_back(i^i>>1);
        }
        return ans;
    } 
};

矩阵置零(medium)

在这里插入图片描述
方法一:
在这里插入图片描述

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        vector<int> row(m), col(n);
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (!matrix[i][j]) {
                    row[i] = col[j] = true;
                }
            }
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (row[i] || col[j]) {
                    matrix[i][j] = 0;
                }
            }
        }
    }
};

在这里插入图片描述
方法二
在这里插入图片描述

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m=matrix.size();
        int n=matrix[0].size();
        bool flag_col0=false, flag_row0=false;
        for(int i=0;i<m;i++)
        {
            if(matrix[i][0]==0)
                flag_row0=true;
        }
        for(int j=0;j<n;j++)
        {
            if(matrix[0][j]==0)
                flag_col0=true;
        }
        for(int i=1;i<m;i++)
        {
            for(int j=1;j<n;j++)
            {
                if(matrix[i][j]==0)
                {
                    matrix[i][0]=0;
                    matrix[0][j]=0;
                }
            }
        }
        for(int i=1;i<m;i++)
        {
            for(int j=1;j<n;j++)
            {
                if(matrix[i][0]==0 || matrix[0][j]==0)
                {
                    matrix[i][j]=0;
                }
            }
        }
        if(flag_row0)
        {
            for(int i=0;i<m;i++)
                matrix[i][0]=0;
        }
        if(flag_col0)
        {
            for(int j=0;j<n;j++)
                matrix[0][j]=0;
        }

    }
};

在这里插入图片描述

加油站

在这里插入图片描述
分析题目:
在这里插入图片描述
在这里插入图片描述
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int total=0;
        int j=-1;
        for(int i=0,sum=0;i<gas.size();i++)
        {
            sum+=gas[i]-cost[i];
            total+=gas[i]-cost[i];
            if(sum<0)
            {
                j=i;
                sum=0;
            }
        }
        return total>=0? j+1:-1;
    }
};

分发糖果(hard)

在这里插入图片描述
分析题目:
在这里插入图片描述

class Solution {
public:
    int candy(vector<int>& ratings) {
        int n=ratings.size();
        vector<int> left(n);
        for(int i=0;i<n;i++)
        {
            if(i>0 && ratings[i]>ratings[i-1])
            {
                left[i]=left[i-1]+1;
            }
            else    
                left[i]=1;
        }
        int right=0,ret=0;
        for(int i=n-1;i>=0;i--)
        {
            if(i<n-1 && ratings[i]>ratings[i+1])
            {
                right++;
            }
            else
                right=1;
            ret+=max(left[i],right);
        }
        return ret;
    }
};

只出现一次的数字(easy)

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
分析题目:
在这里插入图片描述

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int x=0;
        int n= nums.size();
        for(int i =0;i<n;i++)
        {
            x^=nums[i];
        }
        return x;
    }
};

只出现一次的数字II(medium)

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
题目分析:通过位运算的解法没有看懂,所以还是直接用两个都是O(n)的哈希表解法。键是数组中的元素值,值是出现的次数,找到出现次数为1的数,直接break。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        unordered_map<int,int> freq;
        for(int i=0;i<nums.size();i++)
        {
            freq[nums[i]]+=1;
        }
        int ans=0;
        for(int j=0;j<nums.size();j++)
        {
            if(freq[nums[j]]==1)
            {
                ans=nums[j];
                break;
            }
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值