算法经典面试题(1)——数组

*题目编号为Leetcode中对应的题号。
某位大佬的Leetcode题解参考链接

数组

  1. 二分查找
    template<typename T>
    int binarySearch(int arr[], int n, T target){
    
    
        int l = 0, r = n - 1;// 在[l...r]范围内查找target
        while(l <= r){
    
    // 当l==r时,[l...r]依然是有效的
            int mid = l + (r - l) / 2;
            if(arr[mid] == target)
                return mid;
            else if(arr[mid] > target)
                r = mid - 1;// target在[l...mid-1]
            else// arr[mid] < target
                l = mid + 1;// target在[mid+1...r]
        }
        return -1;
    }
  • 要清楚地定义变量的含义,保证循环不变量
  1. (283 移动零) 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

    示例:

    输入: [0,1,0,3,12]
    输出: [1,3,12,0,0]
    

    说明:

    1. 必须在原数组上操作,不能拷贝额外的数组。
    2. 尽量减少操作次数。
 // 暴力版
    void moveZeroes(vector<int>& nums) {
    
    
    	vector<int> nums2;
        for(int i=0;i<nums.size();i++){
    
    
            if(nums[i])
                nums2.push_back(nums[i]);
        }
        for(int i=0;i<nums2.size();i++){
    
    
            nums[i]=nums2[i];
        }
        for(int i=nums2.size();i<nums.size();i++){
    
    
            nums[i]=0;
        }
    }
    
    // 优化版
    void moveZeroes(vector<int>& nums){
    
    
        int k=-1;// [0,k]为不含0的元素
    	for(int i=0;i<nums.size();i++){
    
    
            if(nums[i])
                if(i!=k)// 如果数组几乎不含0,即i==k,则不进行交换
                	swap(nums[i],nums[++k]);
            	else
                    k++;
        }
    }
  1. (27 移除元素) 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

    不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

    元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

    示例:

    给定 nums = [0,1,2,2,3,0,4,2], val = 2,

    函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

    注意这五个元素可为任意顺序。

    扫描二维码关注公众号,回复: 12686112 查看本文章

    你不需要考虑数组中超出新长度后面的元素。

// 基础版
    int removeElement(vector<int>& nums, int val) {
    
    
    	int k=-1;// [0,k]为不含val的元素
    	for(int i=0;i<nums.size();i++){
    
    
            if(nums[i]!=val){
    
    
                if(i!=k)
                	nums[++k]=nums[i];
            	else
                    k++;
            }
        }
        return k+1;
    }
    
    // 优化版
    int removeElement(vector<int>& nums, int val) {
    
    
        int n=nums.size();
        for(int i=0;i<n;){
    
    
            if(nums[i]==val){
    
    
            	nums[i]=nums[--n];
            }
            else
                i++;
        }
        return n;
    }
  1. (26 删除排序数组中的重复项) 给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

    不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
    
    示例 :
    
    给定 nums = [0,0,1,1,1,2,2,3,3,4],
    
    函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
    
    你不需要考虑数组中超出新长度后面的元素。
    
 // 基础版
    int removeDuplicates(vector<int>& nums) {
    
    
    	vector<int> nums2;
        int k=-1;
        for(int i=0;i<nums.size();i++){
    
    
            if(k==-1 || nums[i]!=nums2[k]){
    
    
            	nums2.push_back(nums[i]);
                k++;
            }
        }
        for(int i=0;i<=k;i++){
    
    
            nums[i]=nums2[i];
        }
        return k+1;
    }
    
    // 优化版
    int removeDuplicates(vector<int>& nums) {
    
    
        int k=-1;// [0,k]为不含重复元素
        for(int i=0;i<nums.size();i++){
    
    
    		if(k==-1 || nums[i]!=nums[k])
                if(i!=k)
                	nums[++k]=nums[i];
            	else
                    k++;
        }
        return k+1;
    }
  1. (80 删除排序数组中的重复项II) 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。

    不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

    示例 :

    给定 nums = [1,1,1,2,2,3],

    函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。

    你不需要考虑数组中超出新长度后面的元素。

int removeDuplicates(vector<int>& nums) {
    
    
    	int k=-1;// [0,k]为符合要求的数组
        int num=0;// nums[k]已经出现的次数
        for(int i=0;i<nums.size();i++){
    
    
            if(k==-1){
    
    // 第一个元素
            	k++;
                num++;
            }
    		else if(num==1&&nums[i]==nums[k]){
    
    // 未到2个
                nums[++k]=nums[i];
                num++;
            }
            else if(nums[i]!=nums[k]){
    
    // 新元素
                nums[++k]=nums[i];
                num=1;
            }
        }
        return k+1;
    }
  1. (75 颜色分类、计数排序) 给定一个有n个元素的数组,数组中元素的取值只有0,1,2三种可能,为这个数组排序

    示例:

    输入: [2,0,2,1,1,0]
    输出: [0,0,1,1,2,2]
    
// 计数排序
    void sortColors(vector<int>& nums) {
    
    
    	int count[3]={
    
    0};// 存放0,1,2三个元素的个数
        for(int i=0;i<nums.size();i++){
    
    
        	assert(nums[i]>=0 && nums[i]<=2);
            count[nums[i]]++;
        }
        
        int index=0;
        for(int i=0;i<sizeof(count)/sizeof(int);i++)
        for(int j=0;j<count[i];j++)
            nums[index++]=i;
    }
    
    // 三路快排
    void sortColors(vector<int>& nums) {
    
    
        int k1=-1;// [0,k1]==0
        int k2=nums.size();// [k2,n-1]==2
        for(int i=0;i<k2;){
    
    
            if(nums[i]==1)
                i++;
            else if(nums[i]==0){
    
    
    			swap(nums[i++],nums[++k1]);
            }
            else{
    
    
                assert(nums[i]==2);
                swap(nums[i],nums[--k2]);
            }
        }
    }
  1. (215数组中的第K个最大元素) 在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

    示例 2:

    输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
    输出: 4
    
int findKthLargest(vector<int>& nums, int k) {
    
    
    	int index=-1;
        int l=0, r=nums.size()-1;// 在[l,r]范围内找
        while(l<=r){
    
    
            index=partition(nums, l, r);
            if(index<(nums.size()-k)){
    
    
                l=index+1;
            }
            else if(index>(nums.size()-k)){
    
    
                r=index-1;
            }
            else
                return nums[index];
        }
       	return -1;
    }
    
    // 对nums[l,r]部分进行partition
    // 返回p,使得nums[l,p-1]<nums[p],nums[p+1,r]>nums[p]
    int partition(vector<int>& nums, int l, int r){
    
    
        int target=nums[l];
        
        // nums[l+1,j]<target; nums[j+1,i)>target
        int j=l;
        for(int i=l+1;i<=r;i++){
    
    
            if(nums[i]<target)
    			swap(nums[i],nums[++j]);
        }
        // nums[l,j-1]<target; nums[j+1,r]>target
    	swap(nums[l],nums[j]);
        return j;
    }
  1. (167 两数之和II-输入有序数组) 给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。

    函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

    说明:

    返回的下标值(index1 和 index2)不是从零开始的。
    你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

    示例:

    输入: numbers = [2, 7, 11, 15], target = 9
    输出: [1,2]
    解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

// 基础版:二分搜索
    // 其中一个数遍历整个数组,两一个数在剩余的数组中二分搜索
    vector<int> twoSum(vector<int>& numbers, int target) {
    
    
    	vector<int> ret;
        for(int i=0;i<numbers.size()-1;i++){
    
    
            int searchTarget=target-numbers[i];
            int idx2=binarySearch(numbers, i+1, numbers.size()-1, searchTarget);
            if(idx2!=-1){
    
    
                int idx[2]={
    
    i+1,idx2+1};
                ret=vector<int>(idx, idx+2);
            }
        }
        return ret;
    }
    
    int binarySearch(vector<int>& nums, int l, int r, int target){
    
    
        while(l<=r){
    
    
            int mid=l+(r-l)/2;
            if(nums[mid]>target)
                r=mid-1;
            else if(nums[mid]<target)
                l=mid+1;
            else
                return mid;
        }
        return -1;
    }
    
    // 优化版:对撞指针
    vector<int> twoSum(vector<int>& numbers, int target) {
    
    
        int j=0, k=numbers.size()-1;
        vector<int> ret;
        while(j<k){
    
    
            int sum=numbers[j]+numbers[k];
    		if(sum<target)
    			j++;
            else if(sum>target)
                k--;
            else{
    
    
            	ret.push_back(++j);
                ret.push_back(++k);
                break;
            }
        }
        return ret;
    }
  1. (125 验证回文串) 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

    **说明:**本题中,我们将空字符串定义为有效的回文串。

    示例 1:

    输入: "A man, a plan, a canal: Panama"
    输出: true
    

    示例 2:

    输入: "race a car"
    输出: false
    
 bool isPalindrome(string s) {
    
    
    	int l=0, r=s.size()-1;
        while(l<r){
    
    
            if(!isalnum(s[l])){
    
    
                l++;
                continue;
            }
            if(!isalnum(s[r])){
    
    
    			r--;
                continue;
            }
            if(tolower(s[l++])!=tolower(s[r--]))
    			return false;
                
        }
        return true;
    }
  1. (344 反转字符串) 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

    不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

    你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

    示例 2:

    输入:["H","a","n","n","a","h"]
    输出:["h","a","n","n","a","H"]
    
     void reverseString(vector<char>& s) {
    
    
     	int l=0, r=s.size()-1;
         while(l<r){
    
    
             swap(s[l++],s[r--]);
         }
     }
  1. (345 反转字符串中的元音字母) 编写一个函数,以字符串作为输入,反转该字符串中的元音字母。

    示例 :

    输入: "leetcode"
    输出: "leotcede"
    

    说明:
    元音字母不包含字母"y"。

string reverseVowels(string s) {
    
    
     	int l=0, r=s.size()-1, i=0;
         string ret_s(s);
         while(l<r){
    
    
             if(!isaeiou(s[l])){
    
    
                 ret_s[l]=s[l];
                 l++;
                 continue;
             }
             if(!isaeiou(s[r])){
    
    
                 ret_s[r]=s[r];
                 r--;
     			continue;
             }
             ret_s[l]=s[r];
             ret_s[r]=s[l];
             l++;
             r--;
         }
         return ret_s;
     }
     
     bool isaeiou(char s){
    
    
     	if(s=='A'||s=='E'||s=='I'||s=='O'||s=='U'||
            s=='a'||s=='e'||s=='i'||s=='o'||s=='u')
             return true;
         return false;
     }
  1. (11 盛最多水的容器) 给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

    说明: 你不能倾斜容器,且 n 的值至少为 2。

    示例:

    输入:[1,8,6,2,5,4,8,3,7]
    输出:49
    
 int maxArea(vector<int>& height) {
    
    
     	int l=0, r=height.size()-1;
         int maxarea=0;
         while(l<r){
    
    
             int V=(r-l)*min(height[l],height[r]);
             if(V > maxarea)
                 max=V;
             if(height[l]>height[r])
                 r--;
             else
                 l++
         }
         return max;
     }
  1. 子数组:一般来说不一定连续

  2. 一旦使用[]对数组取值时,就要保证是否越界

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

    示例:

    输入: s = 7, nums = [2,3,1,2,4,3]
    输出: 2
    解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
    

    进阶:

    如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。

    滑动窗口

    首先计算滑动窗口nums[i,j]内元素的和是否大于s,若否,则j++,直到sum>s

    此时,尝试让i++,直到sum<s,记录最短子数组的长度

 int minSubArrayLen(int s, vector<int>& nums) {
    
    
     	int l=0, r=-1;// nums[l,r]为滑动窗口
         int sum=0;
         int len=nums.size()+1;// 之后会逐渐取最小值
         while(l<nums.size()){
    
    
             if(r+1<nums.size() && sum<s)// 对右边界判断是否越界
                 sum+=nums[++r];
             else
                 sum-=nums[l++];
             if(sum>=s)
                 len=min(len,r-l+1);
         }
         if(len==nums.size()-1)
             return 0;
         return len;
     }
  1. (剑指offer48 最长不含重复字符的子字符串) 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

    示例 1:

    输入: "abcabcbb"
    输出: 3 
    解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
    

    示例 2:

    输入: "bbbbb"
    输出: 1
    解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
    
int lengthOfLongestSubstring(string s) {
    
    
     	int l=0, r=-1;// [l,r]为无重复字符的子串
         bool ascii[256]={
    
    false};// 标记子串中含有的字符
         int maxlen=0;// 最长子串的长度
         while(l<s.size()){
    
    
             if(r+1<s.size() && ascii[s[r+1]]==false)// 子串中不含有下一个字符
                 ascii[s[++r]]=true;
             else// 子串中含有下一个字符
                 ascii[s[l++]]=false;
             maxlen=max(maxlen,r-l+1);
         }
         return maxlen;
     }
  1. (438 找到字符串中所有字母异位词) 给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。字符串只包含小写英文字母,并且字符串 sp 的长度都不超过 20100。

    示例 1:

    输入:
    s: "cbaebabacd" p: "abc"
    输出:
    [0, 6]
    
    解释:
    起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。
    起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。
    
vector<int> findAnagrams(string s, string p) {
    
    
     	int ascii_s[26] = {
    
     false };
             int ascii_p[26] = {
    
     false };
             vector<int> ret;
             int l = 0, r = -1;
     
             for (char c : p)
                 ascii_p[c - 'a'] ++;
     
     
             while (r + 1 < s.size()) {
    
    
                 r++;
                 ascii_s[s[r] - 'a'] ++;
     
                 if (r - l + 1 > p.size())
                     ascii_s[s[l++] - 'a'] --;
     
                 if (r - l + 1 == p.size() && same(ascii_s, ascii_p))
                     ret.push_back(l);
         }
         return ret;
     }
     
         bool same(int ascii_s[26], int ascii_p[26]) {
    
    
             for (int i = 0; i < 26; i++) 
                 if (ascii_s[i] != ascii_p[i])
                     return false;
             return true;
         }
  1. (76 最小覆盖子串) 给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

    示例:

    输入: S = "ADOBECODEBANC", T = "ABC"
    输出: "BANC"
    

    说明:

    • 如果 S 中不存这样的子串,则返回空字符串 ""
    • 如果 S 中存在这样的子串,我们保证它是唯一的答案。
string minWindow(string s, string t) {
    
    
     		int l = 0, r = -1;// Searching ing [l,r]
     		vector<int> ascii_t(256, 0);// 记录目标字符串中每个字母的个数
     		vector<int> ascii_s(256, 0);// 记录待查字符子串中每个字母的个数
     		int minLen = s.size() + 1;
     		int start = -1, end = -1;// 记录包含子串的起始和终止位置
     		int count = 0;// 记录子串中包含有多少t中的字符
     
     		if (s.size() < t.size())
     			return string("");
     
     		for (char c : t)
     			ascii_t[c]++;
     
     		while (l < s.size()) {
    
    
     
     			if (count == t.size()) {
    
    
     				if ((r - l + 1) < minLen) {
    
    
     					minLen = r - l + 1;
     					start = l, end = r;
     				}
     
     				if (ascii_s[s[l]] <= ascii_t[s[l]])
     					count--;
     
     				ascii_s[s[l]]--;
     				l++;
     			}
     
     			else if (r + 1 < s.size()) {
    
    
     				if (ascii_s[s[r + 1]] < ascii_t[s[r + 1]]) 
     					count++;
     
     				ascii_s[s[r + 1]]++;
     				r++;
     			}
     			else
     				break;
     		}
     	if (start == -1)
     			return string("");
     		string ret = s.substr(start, end - start + 1);
     		return ret;
     }

猜你喜欢

转载自blog.csdn.net/qq_34731182/article/details/113564856
今日推荐