由于二面boss时被boss的两道算法题难住了(ps:本人应聘的是ios开发岗位,oc语言),没什么好说的了,只能怪自己太年轻了。回来后开始脑补各公司的数据结构与算法面试题。
先从已有答案的面试题开始模仿及学习c++语言。点击打开链接 点击打开链接
再从没有答案的面试题开始自己写(遇事不懂问度娘~)。点击打开链接
环境:mac,xcode或者终端;
语言:c++;
潇洒的分割线~哗——————————————————————————————————————————————————
例1:求一个数组的最长递减子序列比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}
算法描述:
1. 对原序列进行递减排序(选择快速排序法);
2. 删掉重复数字;
3. 得到子序列。
c++代码:
#include <iostream>
using namespace std;
#define MAX_SIZE 9
void FastSort(int a[],int left,int right)
{
if (left < right) {
int i = left,j = right;
int temp = a[left];
while (i<j) {
while (j>i&&a[j]>temp) {
j--;
}
a[i]=a[j];
while (i<j&&a[i]<=temp) {
i++;
}
a[j]=a[i];
}
a[i]=temp;
FastSort(a, i+1, right);
FastSort(a, left, i-1);
}
}
void getChildSet(int a[],int p[],int n,int& length)
{
for (int i=0; i<n; i++) {
if (a[i]!=a[i+1]) {
p[length]=a[i];
length++;
}
}
}
int main(int argc, const char * argv[]) {
int a[MAX_SIZE] = {2,3,4,1,5,6,9,7,6};
for (int i= 0; i<MAX_SIZE; i++) {
cout<<a[i];
}
cout<<":";
FastSort(a, 0, MAX_SIZE-1);
int p[MAX_SIZE];
int length = 0;
getChildSet(a,p,MAX_SIZE,length);
for (int i=0; i<length; i++) {
cout<<p[i];
}
cout<<"\n";
return 0;
}
运行结果:234156976:12345679
收获:序列的快速排序,题目的改动,正序排列,删除重复项。
例2:将一整数序列逆序(要求递归实现)
算法描述:
1当满足first<last时直接交换序列首位和末尾位元素;
2.递归调用(注意递归退出条件:必须是变量first大于或等于last)
c++代码:
#include <iostream>
using namespace std;
#define MAX_SIZE 9
void FastSort(int a[],int left,int right)
{
if (left >= right) return;
int temp = a[left];
a[left] = a[right];
a[right] = temp;
FastSort(a, left+1, right-1);
}
int main(int argc, const char * argv[]) {
int a[MAX_SIZE] = {1,2,3,4,5,6,7,8,9};
for (int i=0; i<MAX_SIZE; i++) {
cout << a[i];
}
cout << ":";
FastSort(a, 0, MAX_SIZE-1);
for (int i=0; i<MAX_SIZE; i++) {
cout << a[i];
}
cout << "\n";
return 0;
}
收获:关键在于数组的长度奇偶性。递归思想
例3:将一整数逆序后放入一数组中(要求递归实现)如:12345逆置后为54321
算法描述:
1分割出整数的每一个位,方法n%10;
2.每一次分割都是得到整数最后一个位,数组索引值要从第0开始
c++代码:
#include <iostream>
using namespace std;
void FastSort(int a[],int n,int& length)
{
if (n == 0) return;
a[length] = n%10;
length++;
FastSort(a, n/10,length);
}
int main(int argc, const char * argv[]) {
int n = 1234567890;
int length = 0;
cout << n <<":";
int a[100];
FastSort(a, n, length);
for (int i=0; i<length; i++) {
cout << a[i];
}
cout << "\n";
return 0;
}
运行结果:1234567890:0987654321
收获:/10与%10的灵活运用。
例4:递归实现回文判断(如:abcdedbca就是回文,判断一个面试者对递归理解的简单程序)
算法描述:
与求一个序列的转置类似。
c++代码:
#include <iostream>
using namespace std;
bool FastSort(char* str,int left,int right)
{
if (left > right) {
return false;
}
if (left == right) {
return true;
}
if (str[left] != str[right]) {
return false;
}else return(FastSort(str,left+1,right-1));
}
int main(int argc, const char * argv[]) {
char* str = "abcdedcba";
cout<<str<<":";
int count = strlen(str);
if(FastSort(str, 0, count-1))
cout<<"yes"<<endl;
else cout<<"no"<<endl;
return 0;
}
运行结果:abcdedcba:yes ; abcddcba:no ;
收获:字符串也可像数组那样直接str[index]取。注意字符串最后一个是“\0”!自定义函数从void扩展到bool,注意return。
例5:分解成质因数(如435234=251*17*17*3*2)
算法描述:
1 寻找一个整数的质因数方法从2开始遍历,依次查找
c++代码:
#include <iostream>
using namespace std;
void func(int n,int i)
{
while (n%i !=0) {
i++;
if (i*i > n) {
i = n;
break;
}
}
if (i != n) {
func(n/i, i);
cout<<"*"<<i;
}else cout<<n;
}
int main(int argc, const char * argv[]) {
int n = 435234;
cout<<n<<"=";
func(n, 2);
cout<<"\n";
return 0;
}
运行结果:435234=251*17*17*3*2
收获:分解质因数,简单而有深度的题!改动的地方在于i*i>n,判断一个数是不是质数,不需要从2到n,只需从2到根号n!
例6:寻找迷宫的一条出路,1代表障碍,0代表通。
算法描述:
这里可以使用几种方法,我知道的有使用《数据结构》上“穷举求解”的方法,还有就是使用遗传算法寻找最优路径。这里我先简单描述下“穷举求解”,然后再做遗传算法的方式。
1 问题中要涉及走过路径的回溯,因为栈是先进后出,所以利于回溯,选择栈来存储走过路径
2 每一步有四个方向可以走,每到一步依次判断每一个方向,只要判断到某个方向可走就选择这个方向前进。
3 当所有方向都不能行进时,回溯到上一步,此时判断栈顶指针是否为-1,如果是,返回false失败,否则递归调用继续寻找。
c++代码:
#include <iostream>
using namespace std;
#define MAX_SIZE 10
int maze[MAX_SIZE][MAX_SIZE]=
{
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,1,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
typedef struct
{
int x;
int y;
}POINT;
typedef struct
{
POINT data[MAX_SIZE * MAX_SIZE];
int top;
}STACK;
void findWay(int maze[][MAX_SIZE],POINT start,POINT end,STACK& way)
{
POINT step;
step.x = start.x;
step.y = start.y;
if (way.data[way.top].x != end.x || way.data[way.top].y != end.y)
{
//left
if (!maze[step.y][step.x+1]) {
maze[step.y][step.x] = 1;
way.top++;
way.data[way.top] = step;
step.x++;
findWay(maze, step, end, way);
}
//down
else if (!maze[step.y+1][step.x]) {
maze[step.y][step.x] = 1;
way.top++;
way.data[way.top] = step;
step.y++;
findWay(maze, step, end, way);
}
//right
else if (!maze[step.y][step.x-1]) {
maze[step.y][step.x] = 1;
way.top++;
way.data[way.top] = step;
step.x--;
findWay(maze, step, end, way);
}
//up
else if (!maze[step.y-1][step.x]) {
maze[step.y][step.x] = 1;
way.top++;
way.data[way.top] = step;
step.y--;
findWay(maze, step, end, way);
}
else{
if (way.top == -1)
return;
maze[step.y][step.x] = 1;
way.top--;
step = way.data[way.top];
findWay(maze, step, end, way);
}
}
}
int main(int argc, const char * argv[]) {
POINT start,end;
start.x = 1;
start.y = 1;
end.x = 8;
end.y = 8;
STACK way;
way.top = -1;
findWay(maze, start, end, way);
if (way.top != -1) {
cout<<"Have fount the way!"<<endl;
for (int i = 0; i < way.top; i++) {
cout<<"("<<way.data[i].x<<" , "<<way.data[i].y<<"),";
}
}
else cout<<"Don't find the way"<<endl;
return 0;
}
运行结果:Have fount the way!(1 , 1),(2 , 1),(2 , 2),(2 , 3),(2 , 3),(1 , 3),(1 , 4),(1 , 5),(2 , 5),(3 , 5),(3 , 6),(4 , 6),(5 , 6),(5 , 7),(5 , 8),(6 , 8),(7 , 8)
收获:矩阵里的坐标跟眼睛看到的坐标反一下就好。穷举法。一个点有路就走,前脚走后脚封路,没路可走退回一步封路。用遗传算法求最优路径,待定!学过oc,对于.的用法、对象、属性还是理得清的,比较好理解。
80题-例3:求子数组的最大和
题目:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
c++代码:
#include <iostream>
using namespace std;
int func(int a[],int length)
{
int smin = 0;
int smax = 0;
int sminInmax = 0;
int sum = 0;
for (int i=0; i < length; i++) {
sum += a[i];
if (sum > smax) {
smax = sum;
smin = sminInmax;
}
if (sum < smin) {
sminInmax = sum;
}
}
return smax-smin;
}
int main(int argc, const char * argv[]) {
//int a[8] = {1,-2,3,10,-4,7,2,-5};
int a[8] = {-1,2,3,4,5,6,-10,11};
int result = func(a, 8);
cout<<result;
return 0;
}
运行结果:21
收获:这题关键在于时间复杂度要求,所以就遍历一次。思路,连续n-m个数求和=1到n个求和-1到m个求和,求最大值=1到n求和的最大值-最小值。由于可能最小值的数组长度大于最大值的数组长度,这样相减结果不是我们要的,于是引入延迟最小值!当最大值更新了,再去刷新最小值。遗憾的是主要还是用到数学思想,没用到计算机思想(毕竟本人曾是数学竞赛的0.0)
补充:计算机思想。f(n-1)表示前面已找到的最大值,若f(n-1)<0,则f(n)=a[n];否则f(n)=f(n-1)+a[n];
c++代码(终端):
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> a;
int temp;
while(cin >> temp){
a.push_back(temp);
}
if(a.size() == 0){
return -1;
}
int last = a[0];
int max = a[0];
for(int i = 1; i < a.size(); i++){
if(last > 0){
last += a[i];
}else {
last = a[i];
}
if(max < last){
max = last;
}
}
cout << max << endl;
return 0;
}
题80-例5:查找最小的k个元素
题目:输入n个整数,输出其中最小的k个。
例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4。
c++代码:
#include <iostream>
using namespace std;
#define n 8
#define k 4
void func(int a[],int left,int right)
{
int i = left, j = right;
int temp = a[left];
if (left<right) {
while (i<j) {
while (i<j&&a[j]>temp) {
j--;
}
a[i] = a[j];
while (i<j&&a[i]<=temp) {
i++;
}
a[j]=a[i];
}
a[i]=temp;
func(a, left, i-1);
func(a, i+1, right);
}
}
int main(int argc, const char * argv[]) {
//int a[n] = {1,2,3,4,5,6,7,8};
int a[n];
for (int i=0; i<n; i++) {
cout<<"a["<<i<<"]=";
cin>>a[i];
}
func(a, 0, n-1);
for (int i = 0; i<k; i++) {
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
运行结果:1,2,3,4
收获:数组的快速排列又巩固一次。
题80-例10:翻转句子中单词的顺序。
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student. a am I”。
c++代码:
#include <iostream>
using namespace std;
void func(string str,string comparestr,int right)
{
int i = right-1;
while (i)
{
if (str.compare(i, 1, comparestr) == 0)
{
for (int j = i+1; j < right ; j++)
{
cout<<str[j];
}
cout<<comparestr;
func(str,comparestr,i);
break;
}
i--;
}
if (i == 0)
{
for (int j = i; j < right ; j++)
{
cout<<str[j];
}
}
}
int main(int argc, const char * argv[]) {
string str = "I am a student.";
string str1 = " ";
cout<<str<<":";
func(str,str1,str.length());
cout<<endl;
return 0;
}
运行结果:I am a student.:student. a am I
收获:string字符串比较。
题80-例12:题目:求1+2+…+n,
要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。
c++代码:
#include <iostream>
using namespace std;
#define N 10
int func(int n,int &sum)
{
n && func(n-1, sum);
return sum+=n;
}
int main(int argc, const char * argv[]) {
int sum = 0;
cout<<func(N, sum);
return 0;
}
运行结果:55;
收获:这题抄袭网上大牛的,用&&特性跳出循环,膜拜orz!get☑️。
题80-例14:题目:输入一个已经按升序排序过的数组和一个数字,
在数组中查找两个数,使得它们的和正好是输入的那个数字。
要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。
例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。
c++代码:
#include <iostream>
using namespace std;
#define n 6
#define SUM 15
void func(int a[],int length,int right)
{
while (length!=right) {
if ((a[length]+a[right])>SUM) {
right--;
}
else if ((a[length]+a[right])<SUM) {
length++;
}
else {
cout<<a[length]<<"+"<<a[right]<<"="<<SUM<<endl;
break;
}
}
if (length == right) {
cout<<"no result"<<endl;
}
}
int main(int argc, const char * argv[]) {
int a[n] = {1,2,3,4,11,15};
func(a, 0, n-1);
return 0;
}
运行结果:4+11=15
收获:重要题干“已经正序排列”“两个数”。省了很多事了。头尾两指针,sum大了尾针移,小了头针移。
题80-例18:题目:n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,
每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。
当一个数字删除后,从被删除数字的下一个继续删除第m个数字。
求出在这个圆圈中剩下的最后一个数字。
c++代码:
#include <iostream>
using namespace std;
#define N 17
#define M 5
int LastRemaining_Solution2(int n, unsigned int m)
{
if(n <= 0 || m < 0)
return -1;
int lastinteger = 0;
for (int i = 2; i <= n; i ++)
lastinteger = (lastinteger + m) % i;
return lastinteger;
}
int main(int argc, const char * argv[]) {
cout<<LastRemaining_Solution2(N, M);
return 0;
}
运行结果:10.
收获:约瑟夫环,数学分析一番,递推一番,得出公式:f(n,m)=[f(n-1,m)+m]%n n>1。第一个删除的是M-1.
题80-例19:题目:定义Fibonacci数列如下:
/ 0 n=0
f(n)= 1 n=1
\ f(n-1)+f(n-2) n=2
输入n,用最快的方法求该数列的第n项。
c++代码:
#include <iostream>
using namespace std;
#define N 6
int func(int n)
{
if (n <= 0) {
return 0;
}
if (n == 1 || n == 2) {
return n;
}
return func(n-1)+func(n-2);
}
int main(){
cout<<func(N)<<endl;
return 0;
}
运行结果:13
收获:斐波那契数列。
题80-例20:题目:输入一个表示整数的字符串,把该字符串转换成整数并输出。
例如输入字符串"345",则输出整数345。
c++代码:
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
char* str = "0345";
int length = strlen(str);
int sum = 0;
for (int i = 0 ; i < length; i++) {
sum = sum*10 + str[i]-48;
}
cout<<sum;
return 0;
}
运行结果:345
收获:直接通过字符的ASCII码计算。“0”对应48。
题80-例21:2010年中兴面试题
编程求解:
输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,
使其和等于 m ,要求将其中所有的可能组合列出来.
c++代码:
#include <iostream>
using namespace std;
#define M 8
#define N 20
void func(int n, int m,int a[])
{
if (m == 0) {
for (int i = 1; i < M+1; i++) {
if (a[i] == 1) {
cout<<i<<" ";
}
}
cout<<endl;
}
if (n <= 0 || m <= 0) {
return;
}
a[n] = 0;
func(n-1, m,a);
a[n] = 1;
func(n-1, m-n,a);
a[n] = 0;
}
int main(){
int a[M+1];
int m = M;
int n = N;
if (n > m) {
n = m;
}
if (m > n*n-n+1) {
cout<<"Don't find!"<<endl;
return 0;
}
func(n, m,a);
return 0;
}
运行结果:1 3 4 ,1 2 5 ,3 5 ,2 6 ,1 7 ,8
收获:0/1背包问题。从右往左,没拿标0,f(n-1,m,a[]);拿了标1,f(n-1,m-n,a[])。看m是否为零。
题80-例27:跳台阶问题
题目:一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。
求总共有多少总跳法,并分析算法的时间复杂度。
这道题最近经常出现,包括MicroStrategy等比较重视算法的公司
都曾先后选用过个这道题作为面试题或者笔试题。
c++代码:
#include <iostream>
using namespace std;
#define N 6
int func(int n)
{
if (n <= 0) {
return 0;
}
if (n == 1 || n == 2) {
return n;
}
return func(n-1)+func(n-2);
}
int main(){
cout<<func(N)<<endl;
return 0;
}
运行结果:6
收获:Fibonacci数列。
题80-例28:整数的二进制表示中1的个数
题目:输入一个整数,求该整数的二进制表达中有多少个1。
例如输入10,由于其二进制表示为1010,有两个1,因此输出2。
分析:
这是一道很基本的考查位运算的面试题。
包括微软在内的很多公司都曾采用过这道题。
c++代码:
#include <iostream>
using namespace std;
#define N 10
int func(int n)
{
if (n == 1) {
return 1;
}
if (n%2) {
return func(n/2)+1;
}
return func(n/2);
}
int main(){
cout<<func(N)<<endl;
return 0;
}
运行结果:2
收获:递归,/2%2.