面试经典 150 题 -- 数组 / 字符串 (总结)

总的链接

面试经典 150 题 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台

88.合并两个有效数组

因为有序,直接设置双指针置于两个数组的末尾,从后往前直接模拟就好了,贪心的比较两个指针所指元素,优先挑出大的,放入答案数组中去;

class Solution {
public:
    void merge(vector& nums1, int m, vector& nums2, int n) {
        int p1 = m - 1, p2 = n - 1;
        int tail = m + n - 1;
        int cur;
        while (p1 >= 0 || p2 >= 0) {
            if (p1 == -1) {
                cur = nums2[p2--];
            } else if (p2 == -1) {
                cur = nums1[p1--];
            } else if (nums1[p1] > nums2[p2]) {
                cur = nums1[p1--];
            } else {
                cur = nums2[p2--];
            }
            nums1[tail--] = cur;
        }
    }
};

27.移除元素

题意 : 在nums中原地移除值=val的数,然后返回新数组的长度;

思路 : 模拟即可,设置双指针,一个j指向新数组的尾下标,一个i指向原数组,一遍遍历,当nums[i]!=val时,将其加入新数组中,否则直接跳过即可;

class Solution {
public:
    int removeElement(vector& nums, int val) {
        int n = nums.size() ;
        int i=0,j=0;
        for(;i

26.删除有序数组中的重复项

思路 : 还是双指针模拟,思路与上题类似; 

class Solution {
public:
    int removeDuplicates(vector& nums) {
        int j=0;
        int n = nums.size() ;
        
        for(int i=0;i

80 . 删除有序数组中的重复项 II

思路 : 上一题多了一个保留两个重复元素,那么在枚举的过程中,发现重复的元素数量>=2,那么只将两个加入新数组即可;

class Solution {
public:
    int removeDuplicates(vector& nums) {
        int j=0;
        int n = nums.size() ;
        for(int i=0;i=2){
                nums[j++] = nums[i];
                nums[j++] = nums[i];
            }else{
                nums[j++] = nums[i];
            }
            
            i = k - 1 ; 
        }
        return j ;
    }
};

169.多数元素

法一(众数理论) : 

思路 : 众数理论,由于在题目当中的众数定义为出现次数>=n/2的元素,那么在排好序之后,该元素其中的一个一定会出现在数组最中间 ;

class Solution {
public:
    int majorityElement(vector& nums) {
     sort(nums.begin(),nums.end());
     return nums[nums.size()/2];
    }
};

法二 (哈希表暴力枚举): 

思路 : 利用hash表统计每个元素出现次数,然后枚举哈希表,找到次数最大的元素;这里优化为边遍历边统计;

class Solution {
public:
    int majorityElement(vector& nums) {
        int n = nums.size();
        unordered_map counts;
        int cnt = 0;
        int ma;
        for(int num : nums){
            ++counts[num];
            if(counts[num] > cnt){
                ma = num;
                cnt = counts[num];
            }
        }
        return ma;
    }
};

法三(摩尔投票法)

利用摩尔投票法,来解决此题,详情请看代码 : 

class Solution {
    public int majorityElement(int[] nums) {
        int x = 0 , votes = 0 ;
        for(int num : nums){
            if(votes == 0) x = num  ;
            votes += num == x ? 1 : -1 ;
        }
        return x ;
    }
}

189.轮转数组

思路 : 首先先复制nums数组到res中,保留元素组的情况,对于右移k位,那么也就是nums[(i+k)%n] = res[i] ,详细请看代码 : 

class Solution {
public:
    void rotate(vector& nums, int k) {
    int n = nums.size();
    vector res(n);
    for(int i=0;i

121 . 买卖股票的最佳时机 

思路 : 一遍遍历,在遍历的过程中,用pre存前面的最小元素,那么对于第i天如果要卖股票的话,那么最大收益就是prices[i]-pre ; 一遍遍历,循环更新答案就好 ;

class Solution {
public:
    int maxProfit(vector& prices) {
        int n = prices.size() ;
        int mi = prices[0] ;
        int ans = 0 ;
        for(int i=1;i

122 . 买卖股票的最佳时机 II

这题就直接遍历就好了,举个例子 :

3 1 5 , 那么ans = 5 - 1

1 2 4 , 那么ans = 4 - 1 = 4-2+2-1

用贪心的思路,相邻两个, 只要p[i]>p[i-1],那么就在前一天买,第i买,就会造成最优的结果 ;

class Solution {
public:
    int maxProfit(vector& prices) {
        int ans = 0;
        for(int i=1;i0 ? tmp : 0;
        }
        return ans;
    }
};

55.跳跃游戏

一遍遍历 ,记录当前能够达到的最远距离k,如果 i > k 表示到不了,直接返回false即可,在循环的过程中更新k即可;

class Solution {
public:
    bool canJump(vector& nums) {
        int k = 0;
        for(int i=0;ik) return false;
            k = max(k,i+nums[i]);
        }
        return true;
    }
};

45 . 跳跃游戏 II

题目 : 

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i] 
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

法一 (逆向) : 

反方向查找能够跳到当前位置且距离当前位置最远的点;

class Solution {
    public int jump(int[] nums) {
        // 反方向查找能跳到当前位置且距离当前位置最远的距离
        int p = nums.length - 1 ;
        int ans = 0 ;
        while(p>0){
            for(int i=0;i=p){
                    p = i ;
                    ans ++;
                    break;
                }
            }
        }
        return ans ;
    }
}

法二 : (正向) : 

法一逆向过来,能够优化到O(n)

class Solution {
    public int jump(int[] nums) {
        // 反方向查找能跳到当前位置且距离当前位置最远的距离
        int p = nums.length - 1 ;
        int ans = 0 ;
        int ma = 0 ;
        int r = 0;
        for(int i=0;i

274 . H指数 

直接贪心 : 

先排序,从后往前,如果c[i] >= n-i , 那么表示 H = n - i 是可行的,在遍历的过程中更新答案即可 ;

class Solution {
    public int hIndex(int[] c) {
        int ans = 0 ;
        int n = c.length ;
        Arrays.sort(c);
        for(int i=n-1;i>=0;i--){
            if(c[i]>=n-i) {
                ans = Math.max(ans , n-i);
            }
        }
        return ans ;
    }
}

380 . O(1)时间插入,删除和获取随机元素

思路 : 利用边长数组和哈希表实现,对于随机选取,利用rand函数随机生成一个数组下标,满足选取的随机性 ;

class RandomizedSet {
private:
    vector a ;
    unordered_map mp ;// key -> 值 , val -> 在a中的下标
public:
    RandomizedSet() {
        srand((unsigned)time(NULL));
    }
    
    bool insert(int val) {
        if(mp.count(val)){
            return false;
        }
        int idx = a.size() ;
        a.push_back(val);
        mp[val] = idx ;
        return true ;
    }
    
    bool remove(int val) {
        if(!mp.count(val)){
            return false;
        }
        int idx = mp[val] ;
        int last = a.back() ;
        a[idx] = last ;
        mp[last] = idx ;
        mp.erase(val);
        a.pop_back() ;
        return true ; 
    }
    
    int getRandom() {
        int randIdx = rand() % a.size() ;
        return a[randIdx];
    }
};

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet* obj = new RandomizedSet();
 * bool param_1 = obj->insert(val);
 * bool param_2 = obj->remove(val);
 * int param_3 = obj->getRandom();
 */

238 . 除自身以外数组的乘积

由于可能有0的出现,那么就否定了,先求整体的乘积,然后除以自身来得到答案的可能 ;

这里采用前后缀和的思想来解决 ;

class Solution {
public:
    typedef long long LL;
    vector productExceptSelf(vector& nums) {
        int n = nums.size();
        vector front(n,0);
        vector back(n,0);
        front[0]=1;
        back[n-1]=1;
        for(int i=1;i=0;j--) back[j]=nums[j+1]*back[j+1];
        vector ans(n,0);
        for(int i=0;i

134 . 加油站

本题关键在于 : 

// 假设从x加油站出发经过z加油站最远能到达y加油站,

// 那么从z加油站直接出发,不可能到达y下一个加油站。

// 因为从x出发到z加油站时肯定还有存储的油,或者刚好用完,这都到不了y的下一站,

// 而直接从z出发刚开始是没有存储的油的,所以更不可能到达y的下一站。

// 所以如果 i 最远能够到达 j ,根据上边的结论 i + 1 到 j 之间的节点都不可能绕一圈了。

// 所以直接从 j + 1 开始考虑即可

// 想象成一个圆,所以 i 后边的节点就都不需要考虑了,直接返回 -1 即可。

class Solution {
public:
    int canCompleteCircuit(vector& gas, vector& cost) {
        int n = gas.size() ;
        for(int i=0;i=0){
                remain = remain - cost[j] + gas[(j+1)%n];
                j = (j+1)%n;
                if(j==i){
                    return i ;
                }
            }
            if(j

135.分发糖果

贪心 , 只用考虑左边和右边相邻且向中心递增且rating小于自己的个数 ;

class Solution {
public:
    int candy(vector& ratings) {
        int n = ratings.size();
        vector left(n,1);
        vector right(n,1);
        int ans = 0;
        for(int i=1;i ratings[i-1]){
                left[i] = left[i-1]+1;
            }
        }

        for(int i=n-2;i>=0;i--){
            if(ratings[i]>ratings[i+1]) right[i] = right[i+1]+1;
        }

        for(int i=0;i

你可能感兴趣的:(leetcode,算法学习,leetcode,算法,面试)