目录
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)