秋招突击——7/5——复习{}——新作{跳跃游戏II、划分字母区间、数组中的第K个大的元素(模板题,重要)、前K个高频元素}

文章目录

引言

  • 今天就开始的蛮早的,现在是九点多,刚好开始做算法,今天有希望能够将项目的内容整理一下,然后再修改丰富一下我的简历,这个已经拖了很久了,加把劲,累点就累点吧。

正文

贪心------45 跳跃游戏II

题目链接

注意

  • 所有测试用例都是可到达,所有不用考虑不能到达最终目标的情况
  • 边界存在的条件为1的情况,需要考虑一下
  • 返回的是最小跳数
  • f[i] = f[i -1] + 1,如果f[i-1]到达时已经是最小跳数,那么f[i]的最小跳数就是上一个状态的最小跳数+1
个人实现
  • 想着使用动态规划实现,因为上一个状态最小的情况下,到当前状态的最小就是默认加1,想想看怎么做的。
  • f[i]表示从0到i的最小跳数,当前是在节点i,那么就遍历在当前nums[i]范围内的所有的跳数,更新一下对应的数组就行了。
sql 复制代码
class Solution {
public:
    int jump(vector<int>& nums) {
        vector<int> f(nums.size() + 1,INT_MAX);
        f[0] = 0;
        for(int i = 0;i < nums.size();i ++){
            for(int j = 1;j <= nums[i];j ++){
                if(i + j < nums.size()) f[i + j] = min(f[i + j],f[i] + 1);
            }
        }
        return f[nums.size() - 1];
    }
};
  • 意料之外,居然没有超时,我靠,太意料之外了。这个计算量得是10的7次方最大,居然没有超时,没超时就没超时把!
参考实现


  • 这里是维护跳数的区间数组,在某一个区间内的最小跳数始终是固定的,而且随着往后遍历,区间的跳数是递增的,根据上述推论实现代码如下
sql 复制代码
class Solution {
public:
    int jump(vector<int>& nums) {
    vector<int> f(nums.size() + 1,0);
    for(int i = 1,j = 0;i < nums.size();i ++){
        while(j + nums[j] < i)  j ++;       // 不断将j向后移动,保证当前跳数范围包括了对应i
        f[i] = f[j] + 1;// 更新每一个节点所属的最少跳数段落信息,维护对应的数组
    }
    return f[nums.size() - 1];
    }
};

确实很巧妙,学到了,这个跳数这里应该不会再出问题了

这个可以看看之前的做的跳跃游戏原始版本,题目链接

  • 那道题也是使用类似方法,主要是通过最远距离和i之间的关系,判定能否到达最远距离,中间会不会出现断链的情况。

划分字母区间

注意

  • 同一个字母最多出现在一个片段中,每一个字母只能用一次
  • 片段数最多的情况
  • 所有划分结果顺序拼接,最终仍然是s
  • 小写英文字母组成
  • 长度最小是1
    保证每一个片段的字母是彼此不同的,而且要保证最终的片段数尽可能多
个人实现
  • 这里有一个约束,就是每一个字母只能在一个片段出现,不能横跨两个片段,这个怎么实现?
  • 这个题也是类似横跨区间的问题,保证一个字母出现的第一个索引和最后一个索引都是在同一个片段内部,不然就不满足约束条件,所以需要记录所有的字母的出现的两个位置。
  • 然后就是怎么合理的安排区间的问题。是否需要进行二次遍历,保证区间的相互包含,从而实现最多的划分。
  • 这里的时间复杂度目测是O(n),我需要遍历两次
sql 复制代码
class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> res;
        
        // 更新记录每一个元素出现的最早的位置和
        vector<pair<int,int>> word(27,{s.size() - 1,0});
        for(int i = 0;i < s.size();i ++){
            word[s[i] - 'a'].first = min(i,word[s[i] - 'a'].first);
            word[s[i] - 'a'].second = max(i, word[s[i] - 'a'].second);
        }
        cout<<word[s[0] - 'a'].first<<endl;
        cout<<word[s[0] - 'a'].second<<endl;


        // 再次遍历计算区间的长度
        int beg = word[s[0] - 'a'].first , end = word[s[0] - 'a'].second;
        for(int i = 0;i < s.size();i ++){
            end = max(word[s[i] - 'a'].second,end);
            if(i == end){
                // 遍历到尾节点,直接添加结果
                res.push_back(end - beg + 1);
                beg = i + 1;
                if(i + 1 < s.size())    end = word[s[i + 1] - 'a'].second;
            }
        }
        return res;
    }
};
  • 这个方法中规中矩,没有任何异常,代码量也挺多的。不过做出来了。
参考实现

序列是无序的

  • 从前往后和从后往前效果是一样的
  • 是否需要进行排序,保证他是有序的,降低问题的难度

正式思路

  • 思路和我的差不多,只不过 他是仅仅记录了每一个字母出现的最终位置,而且使用的是hashmap,这里就得讨论一下,使用数组和hashmap哪个更快。理论上来时数组更快,但是写起来比较难看。

  • 具体实现代码如下,基本上都是一致的

sql 复制代码
class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> res;
        
        // 更新记录每一个元素出现的最早的位置和
        vector<int> word(27,0);
        for(int i = 0;i < s.size();i ++){
            word[s[i] - 'a'] = max(i, word[s[i] - 'a']);
        }
        cout<<word[s[0] - 'a']<<endl;


        // 再次遍历计算区间的长度
        int beg = 0 , end = word[s[0] - 'a'];
        for(int i = 0;i < s.size();i ++){
            end = max(word[s[i] - 'a'],end);
            if(i == end){
                // 遍历到尾节点,直接添加结果
                res.push_back(end - beg + 1);
                beg = i + 1;
                if(i + 1 < s.size())    end = word[s[i + 1] - 'a'];
            }
        }
        return res;
    }
};

数组中的第K个最大元素

题目链接

注意

  • 规定了时间复杂度,O(n)只能遍历一次
  • 第K大的元素,是排序只有第K大的元素
  • 数组长度[1, 1 0 5 10^5 105]
  • 每一个元素范围是正负 1 0 4 10^{4} 104
  • 元素与元素之家存在重复的情况
个人实现
  • 这里是制定了,只能通过遍历来实现,想想看怎么做。
  • 转换一下问题思路,如果是找最大的元素,就是完整的遍历并且比较一遍,然后找最小的元素也使完整的遍历一遍,然后在比较一遍,确定一个最大值。
  • 如果是确定第二大的数字,就是如果一个数字比最大的大的话,就默认往后进行顺。
  • 所以这里想办法维护一个队列,每次都是从和队首元素进行比较,然后固定长度是K,如果超过了固定长度直接弹出最后一个元素。
  • 这样不一定是目标值。

对于队列的使用有点问题了,然后返回的是队首的元素。

sql 复制代码
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        queue<int> q;
        for(auto x :nums){
            //队列是空的,直接添加
            if(q.empty())   q.push(x);
            // 如果大于队首元素,直接入队
            if(q.back() < x )   q.push(x);
            while(q.size() > k)    q.pop();
        }
       
        return q.front();

    }
};
  • 这里只能通过一半的样例,如果一开始就给我最大值,通不过测试样例,所以不行!
  • 这个方法根本就不行!
  • 不会做!
  • 使用堆排序,肯定不对呀,是logN的操作复杂度
参考做法
  • 这里是一道模板题,是建立在快排的基础上实现的,所以需要背一下快排的模板,具体如下
cpp 复制代码
void quick_sort(int q[],int l,int r){
	if(l >= r)	return ;
	// 确定中间值、左边界、右边界
	// 中间元素不参加排序,i是从x的左侧一个开始,j是从x的右侧开始
	int i = l - 1,j = r + 1,x = q[l + r >> 1];
	while(i < j){
		do i ++; while(q[i] < x);
		do j -- ;while(q[j] > x);
		if(i < j)	swap(q[i],q[j]);
	}
	quick_sort(q,l,j),quick_sort(q,j + 1,r);
}
  • 这里的j就是最终的区分索引,所以k应该也是和j进行比较,然后再进行判定的是左边进行快排,还是右边进行快排。
  • 这是一道经典的模板题,需要好好背一下。
  • 根据模板题,好好做一下
cpp 复制代码
class Solution {
public:
    int quickSort(vector<int> &nums,int l,int r,int k){
        if(l == r)  return  nums[k];
        int i = l - 1,j = r + 1 ,x = nums[(l + r) >> 1];
        while(i < j){
            do i ++ ;while(nums[i] > x);
            do j -- ; while(nums[j] < x);
            if(i < j) swap(nums[i],nums[j]);
        }   
        if(k <= j)   return quickSort(nums,l,j,k);
        else  return quickSort(nums,j + 1,r,k);
    }

    int findKthLargest(vector<int>& nums, int k) {
        return quickSort(nums,0,nums.size() - 1,k-1);
    }
};
  • 这是一个模板题,只能说是超过了我的知识范围,所以还是得不断补充完善。

前K个高频元素

题目链接


注意

  • 出现频率前K高的元素
  • 按照任意顺序返回
  • 数据保证答案唯一
个人实现
  • 这道题没有说数据是有序的,并不能从顺序进行考虑。
  • 最直白的做法, 统计每一个元素出现的次数,然后使用出现的次数进行排序,然后返回前k高地元素,也就是返回阈值之前的所有的元素。
cpp 复制代码
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int>  counts;
        for(auto x : nums)  counts[x] ++;
        vector<pair<int,int>>   ct;
        for(auto item : counts) ct.push_back({item.first,item.second});
        sort(ct.begin(),ct.end(),[](auto a,auto b){
            return a.second > b.second;
        });
        vector<int> res;
        for(int i = 0;i < k;i ++)   res.push_back(ct[i].first);
        return res;
       
    }
};
  • 纯硬做,直接模拟思路,完全照搬!
参考实现
  • 前半部分思路是一样的,就是要统计每一个元素的出现的次数,然后形成一个key-value键值对,然后使用计数排序,实现前k个频率最高的元素的计算。
  • 具体代码如下
cpp 复制代码
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int>  counts;
        for(auto x : nums)  counts[x] ++;
        int m = nums.size();
        vector<int>   ct(m + 1,0);
        // 实现计数排序
        for(auto [k,v]:counts)  ct[v] ++;
        // 遍历前k个元素,计算一个边界次数
        int edg = m,s = 0;
        while(s < k)   s+= ct[edg --];
        // 遍历获取的满足条件的k
        vector<int> res;
        for(auto [k,v]:counts)  {
            if(v > edg)   res.push_back(k);
        }
        return res;
    }
};

遍历map的好方法

  • 使用[a,b]然后加上auto实现
cpp 复制代码
for(auto [k,v] : map)

总结

  • 大概测了一下,发现自己做一道题,加上修改的总结的时间是超过了50分钟的,有点吓人,一天得花多少时间是用来做算法题。还是得快一点。
  • 可以,今天的效率蛮快的,在十一点就完成了算法题的内容,下面再补充一下关于设计模式的相关知识,然后下午就看一下我们的项目了。加油,冲 !
  • 剑走偏锋呀,感觉自己的路子不对,很多东西都没有专门走过,所以就会有很多问题,现在得转换一下思路,项目的代码我看的不是很懂,那就要从不是很懂的地方一点点开始看,一点点开始弄。现在欠缺了太多东西,后续还要增加每天一样的知识补充。其实很多东西,都是要花时间去弄的,现在就是知道MySQL的基础的东西,但是对于高可用的东西,并不了解。然后Java的多线程编程也不知道,感觉还是得花大时间。
  • 现在应该专心去弄什么?有点乱,感觉有点自暴自弃,觉得完蛋了,但是其实那几个项目并不难,跟着往后做就行了。加油吧。如果有不懂的,就去找SSM中学过的,然后就是哪里不行,补充哪里。
  • 我得调整一下自己的计划,现在欠缺的是项目还有简历,得想办法结合项目,把简历整理出来,后续再根据简历上的东西进行一点点补充。
  • 看项目有点吃力,是因为我从来没有跟着一个东西,从头到尾敲过一个项目,所以看的很吃力。
  • 晚上有点摆烂了,学了一天了,太累了,晚上注意力难以集中!!
  • 明天加油吧!
相关推荐
windwind20001 小时前
游戏关卡设计方法的杂感
游戏·关卡设计
白乐天_n14 小时前
腾讯游戏安全移动赛题Tencent2016A
安全·游戏
这是我5814 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
tealcwu15 小时前
【游戏设计原理】21 - 解谜游戏的设计
游戏·游戏策划
清梦202016 小时前
经典问题---跳跃游戏II(贪心算法)
算法·游戏·贪心算法
tealcwu17 小时前
【游戏设计原理】22 - 石头剪刀布
游戏·游戏策划
l1384942745119 小时前
Java每日一题(2)
java·开发语言·游戏
孤留光乩1 天前
从零搭建纯前端飞机大战游戏(附源码)
前端·javascript·游戏·html·css3
王大队长1 天前
Epic游戏使用mod
游戏
tealcwu2 天前
【游戏设计原理】20 - 囚徒困境
游戏