leetcode数组问题

数组相关题目总结以及算法思想

一、移动元素类型

此类问题的相关的解决思路是引入一个索引变量,不同的数将其保存到数组中,并将其索引加1,此次可以实现空间复杂度为O(1)的方法。相关的题目如下

/**
 * 283.移动0
 * 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序
 * */
class Solution {
public:
    void moveZeroes(vector<int>& nums) {

        int index = -1;
        for( int i = 0; i < nums.size();i++){
            if(nums[i] != 0 )
                nums[++index] = nums[i];
        }
        for( int i = index+1 ; i < nums.size();i++)
            nums[i] = 0;
    }
};

/**
 * 27.移除元素
 * 给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
 * */
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        if(nums.size() == 0)
            return 0;
        int index = -1;
        for(int i = 0; i < nums.size();i++){
            if(nums[i] != val)
                nums[++index] = nums[i];
        }
        return index+1;
    }
};

/**
 * 26.删除排序数组的重复选项
 * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
 * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
 * */
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if( nums.empty())
            return 0;
        int index = 0;
        for(int i = 0; i < nums.size(); i++){
            if(nums[index] != nums[i])
                nums[++index] = nums[i];
        }
        return index+1;
    }
};

/**
 * 80.删除排序数组中的重复项二
 *给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
 * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
 * */

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if( nums.size()<=2)
            return nums.size();
        int index = 2;
        for(int i = 2 ;i < nums.size(); i++){
            if(nums[i]!= nums[index-2])
                nums[index++]= nums[i];

        }
        return index;
    }
};

二、与排序思想相结合的问题

应用比较广泛的几种排序思想主要有:快速排序(荷兰国旗问题、三路快排),计数排序(适用于数据比较少的状况,比如荷兰国旗问题)、归并排序(引申出多路归并问题,原来的归并为二路归并)

/**
 * 88.合并另个有序数组,归并排序的一部分
 * 此处即为归并排序的归并部分
 * */
void merge(vector<int> &arr,int &L,int &mid, int &R) {
/*
 * 关于此函数遇到的问题
 * 数组下表赋值必须提前分配数组空间,如果没有提前分配空间就必须使用push_back
 * 这是因为push_back会自动new,new出来的会保存在堆上,否自在全栈上
 * */
    vector<int> help(R - L + 1);
    //vector<int>help;//此时的数组大小为空 因此help[i]没有任何意义
    int k = 0;
    int i = L ;
    int j = mid +1;
    while (i <= mid&&j <= R)
    {
        help[k++] =  arr[i] < arr[j] ?  arr[i++] : arr[j++];
    }

    while (i <= mid) {
        help[k++] = arr[i++];
    }
    while (j <= R) {
        help[k++] = arr[j++];
    }
    for (k = 0; k < help.size(); k++) {
        arr[L + k] = help[k];
    }
}

三、双索引

双索引主要用与遍历数组,两个指针指向指向不同的元素,从而协同完成任务。一般情况主要是一指针指向左一指针在最右,向里面对撞(快排也用到了这样的思想)

/**
 * 167.有序数组的两数之和
 * 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
 * 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
 * tip:解法一:暴力解法,没用到有序这条件
 *   解法二:有序问题应该想到二分搜索
 *   解法三:双索引
 * */

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int i = 0;
        int j = numbers.size()-1;
        while( i<j){
            if( numbers[i] + numbers[j] > target)
                j--;
            else if(numbers[i] + numbers[j] < target)
                i++;
            else
            {
                vector<int> res = {i+1,j+1};
                return res;
            }
        }
    }
};

/**
 * 11.盛水最多的容器
 * 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。
 * 在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两
 * 条线,使得它们与 x 轴共同构成的容器可以容纳最多的水
 * */
class Solution {
public:
    int maxArea(vector<int>& height) {
        int max = 0;
		//i,j两个对撞指针从两边移动
        for(int i = 0, j = height.size() - 1; i < j ; ){
            int minHeight = height[i] < height[j] ? height[i ++] : height[j --];
            max = max(max, (j - i + 1) * minHeight);
        }
    }
};

四、滑动窗口技术

滑动窗口技术通常适用于求满足一定条件连续子集的问题,一般通过窗口[left,right]移动已达到满足的条件。

/**
 * 209.长度最小的子数组
 * 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。
 * 如果不存在符合条件的连续子数组,返回 0。
 * */

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int l = 0;
        int r = -1;//nums[l..r]滑动窗口,此时滑动窗口中不应该保留任何值
        int sum = 0;
        int length = nums.size()+1;

        while( l<nums.size())
        {
            if(r+1 <nums.size() && sum< s)
                sum +=nums[++r];
            else
                sum -= nums[l++];
            if(sum >= s)
                length = min(length,r-l+1);
        }
        if( length == nums.size()+1)
            return 0;
        return length;
    }
};

/**
 * 3.无重复字符的最长子串
 * 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
 * */
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int freq[256] = {0};
        int l = 0, r = -1;
        int res = 0;
        int len = s.length();
        while (l < len) {

            if ( r+1 <len &&  freq[s[r+1]] == 0) {
                r++;
                freq[s[r]] ++;
            } else {
                freq[s[l]] --;
                l++;
            }

            res = max(res, r-l+1);
        }

        return res;
    }

};

与滑动窗口相关的leetcode题目还有438题与76题

/**
 * 438.找到字符串中所有字母异位词
 * 给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
 * 字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
 * */
/*我写的超时了,扎心
 * class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> res;
        if(s.length() == 0 || p.length() == 0)
        { return res; }

        unordered_map<char,int> mymap;

        for(int i = 0; i < p.size(); i++)
            mymap[q[i]]++;
        int l = 0;
        int r = p.size()-1;
        int count = 0;

        while( r < s.size()){
            for(int i = l; i <= r; i++){
             if(mymap.find(s[i])!= mymap.end()){
                 count++;
             }
             else{
                 break;
             }
             if(count == p.size()){
                 res.push_back(l);
                 l++;
                 r++;
             } else{
                 r++;
                 l++;
             }
            }
        }

        return res;
    }
};*/
class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> res;
        if(s.empty() || s.size() < p.size())
            return res;
        int left = 0;
        int right = p.size() - 1;
        int freqS[26] = { 0 };  //只包含小写字母a~z,数组中保存的是S的子串儿中每个字符出现的次数
        int freqP[26] = { 0 };
        for (int i = 0; i < p.size(); i++) {
            freqS[(int)s[i] - (int)'a']++;
            freqP[(int)p[i] - (int)'a']++;
        }

        while (right < s.size()) {
            int i = 0;
            for ( ; i < 26; i++) {
                if (freqS[i] != freqP[i])
                    break;
            }
            if(i == 26)
                res.push_back(left);
            if(right + 1 == s.size())
                return res;
            freqS[(int)s[left] - (int)'a']--;
            freqS[(int)s[right + 1] - (int)'a']++;
            left++;
            right++;
        }
        return res;

    }
};

//哈希表实现
class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int plen=p.size(),slen=s.size();
        vector<int> res;
        if(slen < plen) {return res;}
        map<char,int> mp;
        for(auto c:p) {
            mp[c]++;
        }
        for(int i=0;i<slen-plen+1;i++){
            map<char,int> temp;
            string substri=s.substr(i,plen);
            for(auto c:substri) temp[c]++;
            if(mapequal(temp,mp))
                res.push_back(i);
        }
        return res;
    }

    bool mapequal(map<char,int>& a,map<char,int>& b){
        if(a.size()!=b.size()) return false;
        map<char,int>::iterator iter;
        for(iter=a.begin(); iter!=a.end(); iter++){
            if(b.count(iter->first)==0||b[iter->first]!=iter->second)
                return false;
        }
        return true;
    }
};

/**
 * 76.最小覆盖子串
 * 给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串
 * */
class Solution {
public:
    string minWindow(string s, string t) {
        string minString;
        if ((s.empty() || t.empty()) || s.size() < t.size())
            return minString;
        map<char, int> mymap;
        for (auto c : t) 
            mymap[c]++;
        int l = 0;
        int r = t.size() - 1;
        int minLength = s.size() + 1;

        while (l < s.size()) {
            map<char, int> mymap2;
            for (int i = l; i < r + 1; i++)
                mymap2[s[i]]++;
            if (mapequal(mymap, mymap2)) {
                minLength = min(minLength,r-l+1);
                minString = s.substr(l,minLength);
                l++;
            } else
                r++;
        }
        return minString;
    }

    bool mapequal(map<char,int>& a,map<char,int>& b) {
        if (a.size() != b.size()) return false;
        map<char, int>::iterator iter;
        for (iter = a.begin(); iter != a.end(); iter++) {
            if (b.find(iter->first) != b.end() && b.count(iter->first) == iter->second)
                return true;
            return false;
        }
    }
};

猜你喜欢

转载自blog.csdn.net/qq_40028201/article/details/89452167