编程之旅-Day31

目录

Day31-学习内容:

1.剑指Offer

面试题:字符流中第一个不重复的字符

面试题13:机器人的运动范围

面试题:滑动窗口的最大值

 2.Leetcode

例1:字符串是否为有效数字

例2:返回所有回文字符串

 3.2017年腾讯秋招编程题2

例1:[编程题] 游戏任务标记


1.剑指Offer

面试题:字符流中第一个不重复的字符

题目描述:

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

输出描述:

如果当前字符流没有存在出现一次的字符,返回#字符。

代码:

class Solution
{
public:
  //Insert one char from stringstream
    string s;
    char hash[256]={0};
    void Insert(char ch)
    {
        s+=ch;
        hash[ch]++;         
    }
  //return the first appearence once char in current stringstream
    char FirstAppearingOnce()
    {
        int len=s.length();
        for(int i=0;i<len;i++){
            if(hash[s[i]]==1){
                return s[i];
            }
        }
        return '#';
    }
};

面试题13:机器人的运动范围

题目描述:地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

思路:回溯法

代码:

class Solution {
public:
    int movingCount(int threshold, int rows, int cols)
    {
        if(threshold<0||rows<=0||cols<=0){
            return 0;
        }
        bool *visited=new bool[rows*cols];
        //memset(visited,rows*cols,false);memset用于初始化慎用,否则程序不通过,
        for(int i=0;i<rows*cols;i++){
            visited[i]=false;
        }
        
        int count=movingCountCore(threshold,rows,cols,0,0,visited);
            
        delete[] visited;
        return count;
    }
    int movingCountCore(int threshold, int rows, int cols, int row, int col, bool* &visited){
        int count=0;
        if(check(threshold,rows,cols,row,col,visited)){
            visited[row*cols+col]=true;
            count=1+movingCountCore(threshold,rows,cols,row-1,col,visited)
                +movingCountCore(threshold,rows,cols,row+1,col,visited)
                +movingCountCore(threshold,rows,cols,row,col-1,visited)
                +movingCountCore(threshold,rows,cols,row,col+1,visited);
        }
        return count;
    }
    bool check(int threshold, int rows, int cols, int row, int col, bool* &visited){
        if(row>=0&&row<rows&&col>=0&&col<cols&&!visited[row*cols+col]&&(getDigitSum(row)+getDigitSum(col)<=threshold)){
            return true;
        }
        return false;
    }
    int getDigitSum(int number){
        int sum=0;
        while(number>0){
            sum+=number%10;
            number/=10;
        }
        return sum;
    }
};

解析:

memset
原型:extern void *memset(void *buffer, int c, int count);
用法:#i nclude
功能:把buffer所指内存区域的前count个字节设置成字符c。
说明:返回指向buffer的指针。

面试题:滑动窗口的最大值

题目描述:给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路:时间复杂度o(n),空间复杂度为o(n),采用双端队列,队列中的头节点保存的数据比后面的要大,比如当前假如的数据比队尾的数字大,说明当前这个数字最起码在从现在起到后面的过程中可能是最大值,而之前队尾的数字不可能最大了,所以要删除队尾元素。此外,还要判断队头的元素是否超过size长度,由于存储的是下标,所以可以计算得到;特别说明,我们在双端队列中保存的数字是传入的向量的下标;

代码:

//deque s中存储的是num的下标
class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int> res;
        deque<int> s;
        for(unsigned int i=0;i<num.size();++i){
            while(s.size() && num[s.back()]<=num[i])//从后面依次弹出队列中比当前num值小的元素,同时也能保证队列首元素为当前窗口最大值下标
                s.pop_back();
            while(s.size() && i-s.front()+1>size)//当当前窗口移出队首元素所在的位置,即队首元素坐标对应的num不在窗口中,需要弹出
                s.pop_front();
            s.push_back(i);//把每次滑动的num下标加入队列
            if(size&&i+1>=size)//当滑动窗口首地址i大于等于size时才开始写入窗口最大值
                res.push_back(num[s.front()]);
        }
        return res;
    }
};

 2.Leetcode

例1:字符串是否为有效数字

题目描述:

Validate if a given string is numeric.

Some examples:
"0"=>true
" 0.1 "=>true
"abc"=>false
"1 a"=>false
"2e10"=>true

Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one.

代码:

class Solution {
public:
    bool isNumber(const char *s) {
        string str(s);
        int k=str.find_first_not_of(' ');
        if(str[k]=='+'||str[k]=='-'){
            k++;
        }
        int p=0,num1=0;
        for(;str[k]>='0'&&str[k]<='9'||str[k]=='.';k++){
            str[k]=='.'?p++:num1++;
        }
        if(p>1||num1<1){
            return false;
        }
        
        if(str[k]=='e'||str[k]=='E'){
            k++;
            if(str[k]=='+'||str[k]=='-'){
                k++;
            }
            int num2=0;
            for(;str[k]>='0'&&str[k]<='9';k++){
                num2++;
            }
            if(num2==0){
                return false;
            }
        }
        for(;str[k]==' ';k++)
            ;
        return str[k]=='\0';
        
    }
};

解析:

find_first_of()

在源串中从位置pos起往后查找,只要在源串中遇到一个字符,该字符与目标串中任意一个字符相同,就停止查找,返回该字符在源串中的位置;若匹配失败,返回npos。

find_first_not_of()

在源串中从位置pos开始往后查找,只要在源串遇到一个字符,该字符与目标串中的任意一个字符都不相同,就停止查找,返回该字符在源串中的位置;若遍历完整个源串,都找不到满  足条件的字符,则返回npos。

例2:返回所有回文字符串

题目描述:

Given an array of strings, return all groups of strings that are anagrams.

Note: All inputs will be in lower-case.

思路:Anagram(回文构词法)。Anagrams是指由颠倒字母顺序组成的单词,“tea”会变成“eat”。

回文构词法有一个特点:单词里的字母的种类和数目没有改变,只是改变了字母的排列顺序。

For example:

Input:  ["tea","and","ate","eat","den"]

Output: ["tea","ate","eat"]  

代码:

class Solution {
public:
    vector<string> anagrams(vector<string> &strs) {
        vector<string> res;
        int len=strs.size();
        if(len<=0) return res;
        unordered_map<string,vector<string>> anamap;
        for(string temp:strs){
            string key=temp;
            sort(key.begin(),key.end());
            anamap[key].push_back(temp);
        }
        for(auto it=anamap.begin();it!=anamap.end();it++){
            if(it->second.size()>1){
                res.insert(res.end(),it->second.begin(),it->second.end());
            }
        }
        return res;
    }
};

解析:

(1)Anagram(回文构词法):是指由颠倒字母顺序组成的单词,比如“dormitory”颠倒字母顺序会变成“dirty room”,“tea”会变成“eat”,回文构词法有一个特点:单词里的字母的种类和数目没有改变,只是改变了字母的排列顺序。

(2)unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。不同的是unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的,而map中的元素是按照二叉搜索树存储,进行中序遍历会得到有序遍历。

所以使用时map的key需要定义operator<。而unordered_map需要定义hash_value函数并且重载operator==。但是很多系统内置的数据类型都自带这些,那么如果是自定义类型,那么就需要自己重载operator<或者hash_value()了。

结论:如果需要内部元素自动排序,使用map,不需要排序使用unordered_map。

 3.2017年腾讯秋招编程题2

例1:[编程题] 游戏任务标记

题目描述:

游戏里面有很多各式各样的任务,其中有一种任务玩家只能做一次,这类任务一共有1024个,任务ID范围[1,1024]。请用32个unsigned int类型来记录着1024个任务是否已经完成。初始状态都是未完成。 输入两个参数,都是任务ID,需要设置第一个ID的任务为已经完成;并检查第二个ID的任务是否已经完成。 输出一个参数,如果第二个ID的任务已经完成输出1,如果未完成输出0。如果第一或第二个ID不在[1,1024]范围,则输出-1。

输入描述:

输入包括一行,两个整数表示人物ID.

输出描述:

输出是否完成

输入例子1:

1024 1024

输出例子1:

1

思路:解释:1024=32*32,因此可用32个整数表示1024位(因为每个整数32位),因为任务ID范围是1~1024,所以减1转化为0~1023,然后任务ID除以32,商为存到哪个整数,余数为该整数对应位(置1即可)。

注:除以32相当于直接右移5位,对32取余相当于"与31"(这个技巧只对2的次方数有效)。

拓展:大数据处理,可自行查找1-bitmap和2-bitmap。

代码:

#include <iostream>
using namespace std;
    
unsigned int arr[32];
    
int main()
{
    int id1, id2;
    while(cin>>id1>>id2)
    {
        if(!(id2>=1 && id2<=1024))
        {
            cout<<-1<<endl;
            continue;
        }
        arr[(id1-1)>>5] |= (1<<(id1&31));
        cout<<( (arr[(id2-1)>>5] & (1<<(id2&31))) != 0)<<endl;
    }
    return 0;
}
#include <iostream>
using namespace std;

int main(){
    int id1,id2;
    while(cin>>id1>>id2){
        if(id1<1||id1>1024||id2<1||id2>1024){
            cout<<-1<<endl;
            continue;
        }
        unsigned int tag[32];
        int index=(id1-1)/32;
        int temp=(id1-1)%32;
        tag[index]|=1<<temp;
        index=(id2-1)/32;
        temp=(id2-1)%32;
        if((tag[index])&(1<<temp)){
            cout<<1<<endl;
        }
        else{
            cout<<0<<endl;
        }
    }
    return 0;
}

解析:

&比较实用的例子:
比如我们经常要用的是否被2整除,一般都写成  if(n % 2 == 0)

可以换成 if((n&1) == 0)

位运算参考:https://www.linuxidc.com/Linux/2014-03/98362.htm

猜你喜欢

转载自blog.csdn.net/linyuhan3232/article/details/89311051