目录
1.剑指Offer
面试题29:顺时针打印矩阵
题目描述:
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
思路:1.每次打印一个矩形用循环实现,矩形的起始位置总是左上角的点,坐标:(start,start)即行列标相等,循环条件:rows>start*2&&columns>start*2。
2.矩形打印分四步,第一步从左到右,总是需要至少会打印一步;第二步从上到下,应满足start<endR即起始行号小于终止行号;第三步从右到左,应满足start<endR&&start<endC即起始行号小于终止行号且起始列号小于终止列号;第四步从下到上,应满足start<endC&&start<endR-1即终止行号要比起始行号至少大2且起始列号小于终止列号。
代码:
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
int rows=matrix.size();
int columns=matrix[0].size();
vector<int> res(rows*columns);
res.clear(); //一定要清空,否则会有默认的0出现
int start=0;
while(rows>start*2&&columns>start*2){ //print a rectangle
printMatrixCore(matrix,rows,columns,start,res);
++start;
}
return res;
}
void printMatrixCore(vector<vector<int> > &matrix, int rows, int columns, int start, vector<int> &res){
int endC=columns-1-start;
int endR=rows-1-start;
for(int i=start;i<=endC;i++){ //print from left to right
res.push_back(matrix[start][i]);
}
if(start<endR){ //print top to bottom
for(int i=start+1;i<=endR;i++){
res.push_back(matrix[i][endC]);
}
}
if(start<endR&&start<endC){ //print right to left
for(int i=endC-1;i>=start;i--){
res.push_back(matrix[endR][i]);
}
}
if(start<endC&&start<endR-1){ //print bottom to top
for(int i=endR-1;i>=start+1;i--){
res.push_back(matrix[i][start]);
}
}
}
};
面试题21:调整数组顺序使奇数位于偶数前面
题目描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
代码:
方法1:
class Solution {
public:
void reOrderArray(vector<int> &array) {
if(array.empty()||array.size()==0){
return;
}
Reorder(array,isEven);
}
static void Reorder(vector<int> &array, bool (*func)(int)){
int i=0;
int j=array.size()-1;
int temp;
while(i<j){
while(i<j&&!func(array[i])){
++i;
}
while(i<j&&func(array[j])){
--j;
}
if(i<j)
temp=array[i];
array[i]=array[j];
array[j]=temp;
}
}
static bool isEven(int n){
return (n&1)==0;
}
};
结果:您的代码已保存
答案错误:您提交的程序没有通过所有的测试用例
case通过率为0.00%
用例:
[1,2,3,4,5,6,7]
对应输出应该为:
[1,3,5,7,2,4,6]
你的输出为:
[1,7,3,5,4,6,2]
原因在于:没有保证奇数和奇数,偶数和偶数之间的相对位置不变。
方法2:
思路:类似冒泡算法,前偶后奇数就交换
class Solution {
public:
void reOrderArray(vector<int> &array) {
for(int i=0;i<array.size();i++){
for(int j=array.size()-1;j>i;j--){
if((array[j]%2==1)&&(array[j-1]%2==0)){
swap(array[j],array[j-1]);
}
}
}
}
};
2.Leetcode
例1:求字符串最后一个单词的长度
题目描述:
Given a string s consists of upper/lower-case alphabets and empty space characters' ', return the length of last word in the string.
If the last word does not exist, return 0.
Note: A word is defined as a character sequence consists of non-space characters only.
For example,
Given s ="Hello World",
return5.
思路:反向查找,末尾空格忽略,行中出现空格就终止循环,注意一定要考虑末尾有空行的情况。
解析:strlen函数
格式:strlen (字符数组名)
功能:计算字符串s的(unsigned int型)长度,不包括'\0'在内。
strlen(char*)函数求的是字符串的实际长度,它求得方法是从开始到遇到第一个'\0',如果你只定义没有给它赋初值,这个结果是不定的,它会从aa首地址一直找下去,直到遇到'\0'停止。
char aa[10];cout<<strlen(aa)<<endl; //结果是不定的
char aa[10]={'\0'}; cout<<strlen(aa)<<endl; //结果为0
char aa[10]="jun"; cout<<strlen(aa)<<endl; //结果为3
而sizeof()返回的是变量声明后所占的内存数,不是实际长度,此外sizeof不是函数,仅仅是一个操作符,strlen是函数。
sizeof(aa) 返回10
int a[10]; sizeof(a) 返回40 (根据语言int型 c 是两个字节 c++是四个 java 是两个)
⒈sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。
该类型保证能容纳实现所建立的最大对象的字节大小。
⒉sizeof是操作符(关键字),strlen是函数。
参考:https://blog.csdn.net/smf0504/article/details/51372351
代码:
class Solution {
public:
int lengthOfLastWord(const char *s) {
int count=0;
int len=strlen(s);
for(int i=len-1;i>=0;i--){
if(s[i]==' '){
if(count){ //忽略末尾空行
break;
}
}
else{
++count;
}
}
return count;
}
};
例2:求二进制字符串的和
题目描述:
Given two binary strings, return their sum (also a binary string).
For example,
a ="11"
b ="1"
Return"100".
解析:1.字符转数字a[l1--]-'0',数字转字符char(s%2+'0')
代码:
class Solution {
public:
string addBinary(string a, string b) {
int l1=a.length()-1;
int l2=b.length()-1;
int s=0,c=0;
string sum="";
while(l1>=0||l2>=0||c){
int num1=(l1>=0?a[l1--]-'0':0); //不足位自动补全
int num2=(l2>=0?b[l2--]-'0':0);
s=num1+num2+c;
c=s/2; //等价于s>>2
sum=char(s%2+'0')+sum;
}
return sum;
}
};
- C++字符串补充:
- <string>
- string str;
- 1. 字符串长度
- len = str.length();
- len = str.size();
- 2. 字符串比较
- 可以直接比较
- 也可以:
- str1.compare(str2);
- str1.compare(pos1,len1,str2,pos2,len2); 值为负,0 ,正。
- nops 长度到完。
- 3. 附加
- str1 += str2;
- 或
- str1.append(str2);
- str1.append(str2.pos2,len2);
- 4. 字符串提取
- str2 = str1.substr();
- str2 = str1.substr(pos1);
- str2 = str1.substr(pos1,len1);
- string a=s.substr(0,4); //获得字符串s中 从第0位开始的长度为4的字符串
- 5. 字符串搜索
- where = str1.find(str2);
- where = str1.find(str2,pos1); pos1是从str1的第几位开始。
- where = str1.rfind(str2); 从后往前搜。
- 6. 插入字符串
- 不是赋值语句。
- str1.insert(pos1,str2);
- str1.insert(pos1,str2,pos2,len2);
- str1.insert(pos1,numchar,char); numchar是插入次数,char是要插入的字符。
- 7. 替换字符串
- str1.replace(pos1,str2);
- str1.replace(pos1,str2,pos2,len2);
- 8. 删除字符串
- str.erase(pos,len)
- str.clear();
- 9. 交换字符串
- swap(str1,str2);
- 10. C --> C++
- char *cstr = "Hello";
- string str1;
- cstr = cstr;
- string str2(cstr);
- 对于ACMer来说,C的字符串处理要比C++的方便、简单,尽量用C的字符串处理函数。
- C++中string类常用算法
- string类的构造函数:
- string(const char *s); //用c字符串s初始化
- string(int n,char c); //用n个字符c初始化
- 此外,string类还支持默认构造函数和复制构造函数,如string s1;string
- 参考:https://www.cnblogs.com/lidabo/p/3487043.html
3.2018年校招编程题
例1:今日头条-判断三只球队能否打平
题目描述:
有三只球队,每只球队编号分别为球队1,球队2,球队3,这三只球队一共需要进行 n 场比赛。现在已经踢完了k场比赛,每场比赛不能打平,踢赢一场比赛得一分,输了不得分不减分。已知球队1和球队2的比分相差d1分,球队2和球队3的比分相差d2分,每场比赛可以任意选择两只队伍进行。求如果打完最后的 (n-k) 场比赛,有没有可能三只球队的分数打平。
输入描述:
第一行包含一个数字 t (1 <= t <= 10) 接下来的t行每行包括四个数字 n, k, d1, d2(1 <= n <= 10^12; 0 <= k <= n, 0 <= d1, d2 <= k) 输出描述:
每行的比分数据,最终三只球队若能够打平,则输出“yes”,否则输出“no”
示例1
输入
2 3 3 0 0 3 3 3 3 输出
yes no 说明
case1: 球队1和球队2 差0分,球队2 和球队3也差0分,所以可能的赛得分是三只球队各得1分 case2: 球队1和球队2差3分,球队2和球队3差3分,所以可能的得分是 球队1得0分,球队2得3分, 球队3 得0分,比赛已经全部结束因此最终不能打平。
思路:
题目中只说队伍之间相差的分数,并没有说哪支队伍得分多,哪支队伍得分少。所以,本题应该分4种情况讨论。假设球队1得分为m (m >= 0) ,至少需要need 场比赛才能持平。
当 球队1< 球队2,球队2<球队3 时,得分情况:
球队1:m
球队2:m + d1
球队3:m + d1 + d2 。此时有3 * m = k - d1 - d1 - d2 need = d1 + d2 + d2(此时球队3得分最多,所以球队1还需要赢d1 + d2场,球队2还需要赢d2场)
当 球队1< 球队2,球队2>球队3 时,得分情况:
球队1:m
球队2:m + d1
球队3:m + d1 - d2 。此时有3 * m = k - d1 - d1 + d2 need = d1 + d2 (此时球队2得分最多,所以球队1还需要赢d1场,球队3还需要赢d2场)
当 球队1> 球队2,球队2>球队3 时,得分情况:
球队1:m
球队2:m - d1
球队3:m - d1 - d2 。此时有3 * m = k + d1 + d1 + d2 need = d1 + d1 + d2 (此时球队1得分最多,所以球队2还需要赢d1场,球队3还需要赢d1 + d2场)
当 球队1> 球队2,球队2<球队3 时,得分情况:
球队1:m
球队2:m - d1
球队3:m - d1 + d2 。
此时有3 * m = k + d1 + d1 - d2 。这时不能确定哪个球队得分最多,还要分情况:
当 d1 >= d2 时,球队1得分最多need = d1 + d1 - d2
当 d1 < d2 时,球队3得分最多need = d2 - d1 + d2
但是写代码时,不能用if else if else if....这种结构,因为同一组数据可能满足两种以上的情况,所以要把这几种情况都判断一遍,如果都不能输出yes,那么最后输出no
代码:
错误代码:
#include <iostream>
using namespace std;
int main(int argc, char *argv[]){
int t;
int n,k,d1,d2,temp,left;
cin>>t;
while(t){
t--;
cin>>n>>k>>d1>>d2;
temp=k+d1+d1+d2; //1>2>3
if(temp>=0&&temp%3==0){
left=(n-k)-(d1+d1+d2);
if(left>=0&&left%3==0){
cout<<"yes"<<endl;
continue;
}
}
temp=k-d1-d1-d2; //1<2<3
if(temp>=0&&temp%3==0){
left=(n-k)-(d1+d2+d2);
if(left>=0&&left%3==0){
cout<<"yes"<<endl;
continue;
}
}
temp=k-d1-d1+d2; //1<2>3
if(temp>=0&&temp%3==0){
left=(n-k)-(d1+d2);
if(left>=0&&left%3==0){
cout<<"yes"<<endl;
continue;
}
}
temp=k+d1+d1-d2; //1>2<3
if(temp>=0&&temp%3==0){
if(d1>=d2){
left=(n-k)-(d1+d1-d2);
}
else{
left=(n-k)-(d2+d2-d1);
}
if(left>=0&&left%3==0){
cout<<"yes"<<endl;
continue;
}
}
cout<<"no"<<endl;
}
return 0;
}
您的代码已保存
答案错误:您提交的程序没有通过所有的测试用例
case通过率为60.00%
错误原因:可能某个判断条件没有写正确,暂未找出错误,后续再修改!
正确代码:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int t;
long int n, k, d1, d2, tmp ,left;
cin >> t;
while(t > 0)
{
t--;
cin >> n >> k >> d1 >> d2;
tmp = k - d1 - d1 - d2; //球队1< 球队2,球队2<球队3
if(tmp >= 0 && tmp % 3 == 0) //球队1的得分有解是大前提
{
left = (n - k) - (d1 + d2 + d2);
if(left >= 0 && left % 3 == 0)//剩下的场次不小于need,并且可以均分3场。
{
cout << "yes" << endl;
continue;
}
}
tmp = k - d1 - d1 + d2; //球队1< 球队2,球队2>球队3
if(tmp >= 0 && tmp % 3 == 0)
{
left = (n - k) - (d1 + d2);
if(left >= 0 && left % 3 == 0)
{
cout << "yes" << endl;
continue;
}
}
tmp = k + d1 + d1 + d2;//球队1> 球队2,球队2>球队3
if(tmp >= 0 && tmp % 3 == 0)
{
left = (n - k) - (d1 + d1 + d2);
if(left >= 0 && left % 3 == 0)
{
cout << "yes" << endl;
continue;
}
}
tmp = k + d1 + d1 - d2;//球队1>球队2,球队2<球队3
if(tmp >= 0 && tmp % 3 == 0)
{
if(d1 >= d2)
{
left = (n - k) - (d1 + d1 - d2);
}
else
{
left = (n - k) - (d2 + d2 - d1);
}
if(left >= 0 && left % 3 == 0)
{
cout << "yes" << endl;
continue;
}
}
cout << "no" << endl;
}
return 0;
}
例2:今日头条-求最大连续相同字符的子串长度
题目描述:
有一个仅包含’a’和’b’两种字符的字符串s,长度为n,每次操作可以把一个字符做一次转换(把一个’a’设置为’b’,或者把一个’b’置成’a’);但是操作的次数有上限m,问在有限的操作数范围内,能够得到最大连续的相同字符的子串的长度是多少。
输入描述:
第一行两个整数 n , m (1<=m<=n<=50000),第二行为长度为n且只包含’a’和’b’的字符串s。 输出描述:
输出在操作次数不超过 m 的情况下,能够得到的 最大连续 全’a’子串或全’b’子串的长度。
示例1
输入
8 1 aabaabaa 输出
5 说明
把第一个 'b' 或者第二个 'b' 置成 'a',可得到长度为 5 的全 'a' 子串。
思路:1.用两个数组分别存字符a和b在原来数组中的位置,分别求两个数组中反转字符的最大长度。
代码:
错误代码:(自己实现)
int main(){
int n,m;
cin>>n>>m;
string str;
getline(cin,str);
int i=0;
int count=0,maxlen=0,time=0;
while(i<=str.length()-2){
if(str[i]==str[i+1]){
count++;
if(time<=m&&maxlen<count){
maxlen=count;
}
}
else{
++time;
if(time>m){
count=0;
time=0;
}
}
i++;
}
return maxlen;
}
错误原因:没有考虑可以反转a或者b两种情况
正确代码:
#include<iostream>
#include<string>
using namespace std;
const int maxn=50040;
void longstr(string str,int k) //输出最长连续序列函数
{
int len=str.length();
int arraya[maxn]={0}; //字符串中所有元素'a'的下标组成的数列
int arrayb[maxn]={0}; //字符串中所有元素'b'的下标组成的数列
int J=1; //计数符
int K=1;
int maxa=0; //记录最大长度
int maxb=0;
for(int i=0;i<len;i++) //遍历字符串,生成目标数组
{
if(str[i]=='a')
{
arraya[J++]=i+1;
}
if(str[i]=='b')
{
arrayb[K++]=i+1;
}
}
arraya[J]=len; //添加首末元素
arrayb[K]=len;
arraya[0]=1;
arrayb[0]=1;
int temp1,temp2; //记录每一个遍历的连续字符串长度
for(int i=1;i<=J-k;i++)
{
if(i==1 || i==J-k) //如果要反转的字符在第一个或者最后一个
{
temp1=arraya[i+k]-arraya[i-1]; //不需要减去1
}
else
{
temp1=arraya[i+k]-arraya[i-1]-1; //否则需要减去1
}
if(temp1>maxa)
{
maxa=temp1; //将最大值保存
}
}
for(int i=1;i<=K-k;i++)
{
if(i==1 || i==K-k)
{
temp2=arrayb[i+k]-arrayb[i-1];
}
else
{
temp2=arrayb[i+k]-arrayb[i-1]-1;
}
if(temp2>maxb)
{
maxb=temp2;
}
}
int a=(maxa>maxb)?maxa:maxb; //找出翻转a和b中最长的序列
cout<<a<<endl;
}
int main()
{
string str;
int n,m;
while(cin>>n>>m)
{
cin>>str;
int len=str.length();
longstr(str,m);
}
}
#include <iostream>
#include <string>
using namespace std;
const int maxn=50040;
void MaxLengthofSubstr(string str, int m){
int len=str.length();
int a[maxn]={0};
int b[maxn]={0};
int j=1,k=1;
int maxlen_a=0,maxlen_b=0;
int temp_a=0,temp_b=0;
for(int i=0;i<len;i++){
if(str[i]=='a'){
a[j++]=i+1;
}
if(str[i]=='b'){
b[k++]=i+1;
}
}
a[j]=len;
a[0]=1;
b[k]=len;
b[0]=1;
for(int i=1;i<j-m;i++){
if(i==1||i==j-m){
temp_a=a[i+m]-a[i-1];
}
else{
temp_a=a[i+m]-a[i-1]-1;
}
if(temp_a>maxlen_a){
maxlen_a=temp_a;
}
}
for(int i=1;i<k-m;i++){
if(i==1||i==k-m){
temp_b=b[i+m]-b[i-1];
}
else{
temp_b=b[i+m]-b[i-1]-1;
}
if(temp_b>maxlen_b){
maxlen_b=temp_b;
}
}
int maxlen=maxlen_a>maxlen_b?maxlen_a:maxlen_b;
cout<<maxlen<<endl;
}
int main(){
string str;
int n,m;
while(cin>>n>>m){
cin>>str;
MaxLengthofSubstr(str,m);
}
}
4.2017年阿里巴巴秋招笔试题
例2:在关系型是数据库中,有两个不同的事务同时操作数据库中同一表的同一行,不会引起冲突的是: (F)
A.其中一个DELETE操作,一个是SELECT操作 B.其中两个都是UPDATE C.其中一个是SELECT,一个是UPDATE D.其中一个SELECT E.其中一个是DELETE,另一个是UPDATE F.两个都是DELETE
解析:当要删除的记录在数据库中不存在的时候,是不会报错的。
例3:众所周知我们所处的宇宙的质能公式是E=mc 2 ,其中c是真空中的光速。和我们的宇宙平行的另一个宇宙meta,研究显示他们使用的质能公式是E=(2+ √3) m ,当一个物体的质量很大的时候,对应的能量E非常大,数据也非常的长。但meta宇宙里面的智慧生物非常的懒,他们只愿意把E取整,然后记录对应的能量E的最后一位整数,比如m=0时,他们会记录1,m=1时,他们会记录3,m=2时,他们会记录3.现在请问当m=80时,他们会记录多少?(C)
A.1 B.2 C.3 D.4 E.5 F.6
解析:
m=0,记录1;m=1,记录3;m=2,记录3;
m=3,记录1;m=4,记录3;m=5,记录3;
... ...
... ...
... ...
m=78,记录1,m=79,记录3;m=80,记录3
规律就是从零开始,三个数为一个周期,一个周期当中记录是1,3,3。从0到80一共是81个数,正好能整除3,也就是说m=80落在一个周期里的第三个数,记录3。
例4:页高速缓存是Linux kerne使用的主要的磁盘缓存技术。它允许系统把存放在磁盘上的一些数据保留在内存中,以便减少对磁盘的访问。进程对页高速缓存区中的数据修改之后,数据页被标记为“脏数据”在下列哪些条件下,脏数据不会被写入磁盘?(BE)
A.页高速缓存空间不足
B.突然断电
C.变脏以来,太久没有过更新
D.通过系统调用(sync(),fsync(),fdataasync())来强行对将对快设备的更新同步到磁盘
E.内存足够大
F.磁盘足够大
正确答案:B E
解析:进程对页高速缓存区中的数据修改之后只要还没有写入磁盘就是脏数据,突然断电会导致页高速缓存区中的数据丢失,不会写入磁盘,当内存(页高速缓存区)足够大,也暂时不会写入磁盘