数据结构与算法再探(五)贪心-双指针-滑动窗口

贪心算法

贪心算法是一种常用的算法设计策略,旨在通过局部最优选择来构建全局最优解。它的基本思想是:在每一步选择中,都选择当前看起来最优的选项,而不考虑后续的影响。贪心算法通常用于解决最优化问题,尤其是在某些特定条件下能够得到全局最优解的问题

1、分发饼干

455. 分发饼干 - 力扣(LeetCode)

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是满足尽可能多的孩子,并输出这个最大数值。输入: g = [1,2,3], s = [1,1]: 输出: 1

贪心策略是,给剩余孩子里最小饥饿度的孩子分配最小的能饱腹的饼干:

C++双指针

class Solution {
public:
    int findContentChildren(vector& g, vector& s) {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int j=0;
        for(int i=0;i=g[j])//满足条件的饼干,才能让饥饿度后移
             {
                j++;
             }
        }
        return j;
    }
};

python3

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        g.sort()
        s.sort()
        n=len(g)
        i=0
        for x in s:
            if i=g[i]:
                i+=1
        return i

2、分发糖果

135. 分发糖果 - 力扣(LeetCode)

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。你需要按照以下要求,给这些孩子分发糖果: 每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

肯定需要遍历,但是一次遍历考虑左右有些复杂化,可以从左到右和从右到左遍历,都是O(N)

C++

class Solution {
public:
    int candy(vector& ratings) {
        int size = ratings.size();
        if (size < 2) {
            return size;
        }
        vector num(size, 1);
        for (int i = 1; i < size; ++i) {
            if (ratings[i] > ratings[i - 1]) {
                num[i] = num[i - 1] + 1;
            }
        }
        for (int i = size - 1; i > 0; --i) {
            if (ratings[i] < ratings[i - 1])
              //在第一次遍历的时候也许已经满足条件要考虑相等,所以用max
                num[i - 1] = max(num[i - 1], num[i] + 1);
        }
        return accumulate(num.begin(), num.end(), 0);
    }
};

python3

class Solution:
    def candy(self, ratings: List[int]) -> int:
        n = len(ratings)
        if n == 0: return 0
        candy_nums = [1] * n
        for i in range(1, n):
            if ratings[i] > ratings[i - 1]:
                candy_nums[i] = candy_nums[i - 1] + 1
        for i in range(n - 1, 0, -1):
            if ratings[i - 1] > ratings[i]:
                candy_nums[i - 1] = max(candy_nums[i - 1], candy_nums[i] + 1)
        return sum(candy_nums)

3、无重叠区域

435. 无重叠区间 - 力扣(LeetCode)

给定多个区间,计算让这些区间互不重叠所需要移除区间的最少个数。起止相连不算重叠,在选择要保留区间时,区间的结尾十分重要:选择的区间结尾越小,余留给其它区间的空间就越大,就越能保留更多的区间。因此采取的贪心策略为,优先保留结尾小且不相交的区间。

C++

class Solution {
public:
    int eraseOverlapIntervals(vector>& intervals) {
       sort(intervals.begin(),intervals.end(),[](vectora, vectorb){return a[1]intervals[i][0]){
            res++;
        }else{
            j=i;
        }
       }
       return res;
    }
};

python3

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        intervals.sort(key=lambda x:x[1])
        n=len(intervals)
        if n<=1:
            return 0
        j=0
        sum=0
        for i in range(1,n):
            if intervals[j][1]>intervals[i][0]:
                sum+=1
            else:
                j=i
        return sum

双指针

双指针是一种常用的算法技巧,双指针技术通常涉及两个指针在同一数据结构上移动,以达到特定的目的。特别适用于处理数组或链表等线性结构的问题,通过使用两个指针,可以有效地减少时间复杂度,简化代码逻辑。

左右指针:两个指针分别从数组的两端向中间移动。
快慢指针:一个指针移动较快,另一个指针移动较慢,常用于链表中查找中间节点或检测环。
滑动窗口:用于处理子数组或子串问题,动态调整指针以满足特定条件。

常见应用场景
查找特定元素:如在排序数组中查找两个数的和。
反转字符串或数组:通过左右指针交换元素。
合并两个有序数组:使用两个指针遍历两个数组。
滑动窗口问题:如最长无重复子串、最小覆盖子串等。

1、两数之和

167. 两数之和 II - 输入有序数组 - 力扣(LeetCode)

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列  ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

C++

使用左右指针,左指针从数组的开始位置,右指针从数组的结束位置,逐步逼近

class Solution {
public:
    vector twoSum(vector& numbers, int target) {
        int l=0,r=numbers.size()-1,sum;
        while (l{l+1,r+1};
    }
};

python3

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        l,r=0,len(numbers)-1
        while ltarget:
                r-=1
            else:
                l+=1
        return l+1,r+1

2、合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序排列。

数组合并到nums1中,两数组都是非递减的,两个指针指向两个数组末尾,第三个指针指向nums1需要填充的位置。

88. 合并两个有序数组 - 力扣(LeetCode)

C++

class Solution {
public:
    void merge(vector& nums1, int m, vector& nums2, int n) {
        int pos=m-- +n-- -1;
        while (m>=0&&n>=0)
        {
            if(nums1[m]>nums2[n]){
                nums1[pos--]=nums1[m--];
            }else{
               nums1[pos--]=nums2[n--];
            }
            //如果 nums2 的数字已经复制完,剩余nums1 的数字不需要改变,因为它们已经被排好序
        }
        while (n>=0)
        {//nums2 的数字有可能没有处理完
            nums1[pos--]=nums2[n--];
        }
    }
};

滑动窗口

释义:滑动窗口的核心思想是使用两个指针(通常称为左指针和右指针)来表示一个窗口的范围。随着右指针的移动,窗口逐渐扩大;当满足某些条件时,左指针也会移动,从而缩小窗口。这个过程可以在一次遍历中完成,因此时间复杂度通常为 O(n)。

滑动窗口的类型:1)固定大小窗口:窗口的大小是固定的,适用于需要计算固定长度子数组的情况。2)动态大小窗口:窗口的大小是动态变化的,适用于需要满足特定条件的子数组或子串。

应用场景:1)最长无重复子串:找出字符串中最长的无重复字符的子串。2)最小覆盖子串:在一个字符串中找到包含另一个字符串所有字符的最小子串。3)固定大小的子数组:计算固定大小窗口的和、平均值等。4)子数组的最大/最小值:在给定范围内查找最大或最小值。

1、无重复的最长字串

使用动态大小窗口,维护一个字符集合,动态调整左右指针

3. 无重复字符的最长子串 - 力扣(LeetCode)

C++

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
      unordered_map res;
      int len=0;
      int left=0;
      int right=0;
      while(right

python3

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        ans=left=0
        winds=set()
        for right,c in enumerate(s):
            while c in winds:
                winds.remove(s[left])
                left+=1
            winds.add(c)
            ans=max(ans,right-left+1)
        return ans

2、最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

76. 最小覆盖子串 - 力扣(LeetCode)

class Solution {
public:
    string minWindow(string s, string t) {
        vector chars(128,0);
        vector flag(128,false);
        for(int i=0;i=0){
                    ++cnt;
                }
                while(cnt==t.size()){
                    if(r-l+10)){
                        --cnt;
                    }
                    ++l;
                }
            }
        }
        return mini_size>s.size()?"":s.substr(min_l,mini_size);
    }
};

你可能感兴趣的:(数据结构与算法,算法)