目录
1.剑指Offer
面试题30:包含min函数的栈
题目描述:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
思路:使用一个辅助栈,记录数据栈中最小的元素
代码:
class Solution {
public:
stack<int> stack1,stack2;
void push(int value) {
stack1.push(value);
if(stack2.empty()||value<stack2.top()){
stack2.push(value);
}
}
void pop() {
if(stack1.top()==stack2.top()){
stack2.pop();
}
stack1.pop();
}
int top() {
return stack1.top();
}
int min() {
return stack2.top();
}
};
面试题57:和为s的数字
题目描述:
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。
思路:定义两个指针,一前一后开始查找,时间复杂度为O(n).
代码:
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> res;
if(array.empty()||array.size()<2){
return res;
}
int ahead=0; //从头开始的指针,向后移动
int behind=array.size()-1; //从尾开始的指针,向前移动
while(ahead<behind){
int cursum=array[ahead]+array[behind];
if(cursum==sum){
res.push_back(array[ahead]); //注意输入顺序,先push_back小的元素
res.push_back(array[behind]);
break;
}
else if(cursum<sum){
ahead++;
}
else{
behind--;
}
}
return res;
}
};
解析:
1.push对应的stack与queue,push_back相对应的vector和list.
2.push_back 方法
vector::void push_back (value_type&& val);
该函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素,新的元素的值是val的拷贝(或者是移动拷贝)。
面试题57:和为s的连续整数序列
题目描述:
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
输出描述:
对应每个测试案例,输出两个数,小的先输出。
思路:定义两个指针,一前一后开始查找累加求和。求连续序列和时为了减少不必要的运算,提高代码效率,总是在前一个序列和的基础上求操作之后序列的和。
代码:
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > res;
vector<int> temp;
if(sum<3) return res;
int small=1;
int big=2;
int middle=(sum+1)/2;
int cursum=small+big;
while(small<middle){
if(cursum==sum){
PrintContinuousSequence(temp,small,big);
res.push_back(temp);
temp.clear();
}
while(cursum>sum&&small<middle){
cursum-=small;
small++;
if(cursum==sum){
PrintContinuousSequence(temp,small,big);
res.push_back(temp);
temp.clear();
}
}
big++;
cursum+=big;
}
return res;
}
vector<int> PrintContinuousSequence(vector<int> &temp,int small, int big){
for(int i=small;i<=big;i++){
temp.push_back(i);
}
return temp;
}
};
2.Leetcode
例1:插入间隔
题目描述:
Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).
You may assume that the intervals were initially sorted according to their start times.
Example 1:
Given intervals[1,3],[6,9], insert and merge[2,5]in as[1,5],[6,9].
Example 2:
Given[1,2],[3,5],[6,7],[8,10],[12,16], insert and merge[4,9]in as[1,2],[3,10],[12,16].
This is because the new interval[4,9]overlaps with[3,5],[6,7],[8,10].
思路:定义Interval结构体类型,通过比较间隔的起点和终点大小判断间隔是否应该合并或是插入。
代码:
/**
* Definition for an interval.
* struct Interval {
* int start;
* int end;
* Interval() : start(0), end(0) {}
* Interval(int s, int e) : start(s), end(e) {}
* };
*/
class Solution {
public:
vector<Interval> insert(vector<Interval> &intervals, Interval newInterval) {
vector<Interval> res;
int i=0;
for(;i<intervals.size();i++){
if(newInterval.end<intervals[i].start){
break;
}
else if(newInterval.start>intervals[i].end){
res.push_back(intervals[i]);
}
else{
newInterval.start=min(newInterval.start,intervals[i].start);
newInterval.end=max(newInterval.end,intervals[i].end);
}
}
res.push_back(newInterval);
for(;i<intervals.size();i++){
res.push_back(intervals[i]);
}
return res;
}
};
例2:最长回文子串
题目描述:
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
思路:从回文串的对称点开始,依次向左向右比较,不相同的时候停止遍历,直到找出最大的长度的回文子串。
(1)回文子串长度为奇数:对称点只有一个字符
(2)回文子串长度为偶数:对称点有两个字符
时间复杂度为O(n^2):对称点的数量为O(n),每次查找的时间也为O(n),所有总时间复杂度为O(n^2)
代码:
class Solution {
public:
string longestPalindrome(string s) {
//字符串的长度
int len = s.size();
if (len == 0) return s;
//保留最长回文串
string resultStr = "";
//回文子串长度为奇数,:对称点只有一个字符的情况
for (int i=0; i<len; ++i){
// i 为对称点
int left = i;//左
int right = i;//右
//向左向右遍历,直到发现不相同的时候停止
while (left > 0 && right < len - 1 && s[left - 1] == s[right + 1]){
--left;
++right;
}
//比较,更新最长回文串
if (right - left + 1 > resultStr.size()){
resultStr = s.substr(left, right - left + 1);
}
}
//回文子串长度为偶数:对称点有两个字符
for (int i = 0; i < len - 1; ++i){
if (s[i] == s[i+1]){//两个对称点相同,才有继续遍历的意义
int left = i;
int right = i+1;
//向左向右遍历,直到发现不相同的时候停止
while (left > 0 && right < len - 1 && s[left - 1] == s[right + 1]){
--left;
++right;
}
//比较,更新最长回文串
if (right - left + 1 > resultStr.size()){
resultStr = s.substr(left, right - left + 1);
}
}
}
return resultStr;
}
};
3.2017年腾讯暑期实习编程题-复习
编程题1-构造回文
题目描述:
给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢?
输出需要删除的字符个数。
思路:关键在于如何求最长公共回文子串,可采用动态规划或者找对称点(参考本文Leetcode部分 例2求最长回文子串)
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1010;
int Max[N][N];
int MaxLen(string s1, string s2){//动态规划求最长回文子串
int len1=s1.length();
int len2=s2.length();
memset(Max,N*N,0); //初始化
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(s1[i-1]==s2[j-1]){
Max[i][j]=Max[i-1][j-1]+1;
}
else{
Max[i][j]=max(Max[i-1][j],Max[i][j-1]);
}
}
}
return Max[len1][len2];
}
int main(){
string s;
while(cin>>s){
int len=s.length();
if(len==0){
printf("%d\n",1);
continue;
}
string s2=s;
reverse(s2.begin(),s2.end()); //反转字符串
int maxlen=MaxLen(s,s2); //求最长公共回文子串
printf("%d\n",len-maxlen);
}
return 0;
}
解析:
一、 fill和fill_n函数的应用:
fill函数的作用是:将一个区间的元素都赋予val值。
函数参数:fill(first,last,val);//first为容器的首迭代器,last为容器的尾迭代器,
替换元素的区间为[first,last),val为将要替换的值。
eg:
vector <int> V;
fill(V.begin(),V.end(),val);
二、fill_n函数的作用是:
给你一个起始点,然后再给你一个数值count和val。把从起始点开始依次赋予 count个元素val的值。
注意: 不能在没有元素的空容器上调用fill_n函数。
三、关于memset()函数:
这个 函数是按字节覆盖,批量初始化内存空间
参考:C++中fill()、fill_n()与memset()函数的区别
https://blog.youkuaiyun.com/xs18952904/article/details/75195412
编程题2:算法基础-字符移位
题目描述:
小Q最近遇到了一个难题:把一个字符串的大写字母放到字符串的后面,各个字符的相对位置不变,且不能申请额外的空间。
你能帮帮小Q吗?
思路:使用lastUp指针记录大写字母应该存放的位置,依次从后往前遍历,每找到一个大写字母就将该字母到lastUp指针之间的字母依次往前移动一位,并将该大写字母放到lastUp位置。
代码:
#include <bits/stdc++.h>
using namespace std;
int main(){
string s;
while(cin>>s){
int len=s.length();
if(len<=1){
cout<<s<<endl;
continue;
}
int lastUp=len-1; //大写字母的存放位置
for(int i=len-1;i>=0;i--){
if(isupper(s[i])){//判断是否为大写字母
int temp=s[i];
for(int j=i+1;j<=lastUp;j++){
s[j-1]=s[j]; //依次向前移动一位
}
s[lastUp]=temp;
--lastUp;
}
}
cout<<s<<endl;
}
return 0;
}
解析:
#include <cctype>
的函数
c++中应该是#include <cctype>
c中应该是#include <ctype.h>
以下为字符函数库中常用的函数:
函数名称 返回值
isalnum() 如果参数是字母数字,即字母或数字,该函数返回true
isalpha() 如果参数是字母,该函数返回true
isblank() 如果参数是空格或水平制表符,该函数返回true
isdigit() 如果参数是数字(0~9),该函数返回true
islower() 如果参数是小写字母,该函数返回true
isupper() 如果参数是大写字母,该函数返回true
isxdigit() 如果参数是十六进制的数字,即0~9、a~f、A~F,该函数返回true
tolower() 如果参数是大写字符,则返回其小写,否则返回该参数
toupper() 如果参数是小写字母,则返回其大写,否则返回该参数
常用:
tolower()——toupper() 大写转为小写——小写转为大写
isupper()——islower() 判断是否为大写——判断是否为小写,若是返回true,否则返回该参数
isalnum()——isalpha() 判断是否为字母或数字,若是返回true,否则返回该参数——判断是否为字母,若是大写字母返回1,若是小写字母返回2,若不是字母返回0
https://blog.youkuaiyun.com/dingwood/article/details/7401146
编程题3:n个数组成的二元组差最小和最大的对数
题目描述:
小Q今天在上厕所时想到了这个问题:有n个数,两两组成二元组,相差最小的有多少对呢?相差最大呢?
解题思路:使用map存放以及对元素排序
首先把序列排序,然后对相邻元素两两相减,在n-1个差值中找到最小差值,然后输出差值中有多少最小差值就是差最小的对数。其次,输出序列中最小数的个数和最大数的个数,把它们相乘就是差最大的对数。
存在的问题若遇到 2 2 2 2 这个序列,按照上述方法会输出 1, 16, 显然不对, 考虑到组合,应该输出 6, 6。所以需要首先判断序列里数字是否都相同,如果相同,直接输出 n*(n-1)//2, 如果不相同,则需要判断序列中有无重复元素,如果有重复元素,数出所有重复元素的个数same_count,算出它们same_count*(same_count-1)//2的和。如果没有重复元素,则可以用上述求差最小的对数的方法求解,差最大的对数仍可以用上述的方法。
代码:
#include <iostream>
#include <map>
#include <utility>
using namespace std;
// 用一个map来存储输入的数,当存在相同的数时不插入新的数,而是将计数值+1
int main()
{
int num;
while(cin>>num)
{
map<int,int> myMap;
bool flag = false;
for(int i = 0; i < num ; i++)
{
int k ;
cin>>k;
map<int,int>::iterator ite;
ite = myMap.find(k);
if(ite != myMap.end())
{ (*ite).second++;flag = true;}
else
{
myMap.insert(make_pair(k,1));
}
} // end of for 读取输入的数据
map<int,int>::iterator ite = myMap.begin();
int min =0;
int minv = -1;
if(flag) //如果存在相同的数
{
for( ; ite!= myMap.end(); ite++)
{
if((*ite).second > 1)
min += ((*ite).second * ((*ite).second -1 ))/2;
} //最小差元组对数等于所有相等的数构成的元组对
}
else
{
for( map<int,int>::iterator ite2 = (++myMap.begin()); (ite2)!= myMap.end(); ite2++,ite++ )
{
int k = (*(ite2)).first - (*(ite)).first;
if( minv ==-1 || k < minv )
{ min = (*ite).second * (*ite2).second ;
minv = k; }
else if(minv == k)
{
min+= (*ite).second * (*ite2).second;
}
} // end of for 求不存在相等的值时的最小差的元组对数
}// 最小对的个数
int max = (*myMap.rbegin()).second * (*myMap.begin()).second;
//最大差的对数
cout<< min<<" "<<max<<endl;
}
}
//自己写的
#include <iostream>
#include <map>
#include <utility>
using namespace std;
int main(){
int n;
while(cin>>n){
map<int,int> Mymap;
bool flag=false;
for(int i=0;i<n;i++){
int k;
cin>>k;
map<int,int>::iterator iter;
iter=Mymap.find(k);
if(iter!=Mymap.end()){
(*iter).second++;
flag=true;
}
else{
Mymap.insert(make_pair(k,1));
}
}
map<int,int>::iterator iter1=Mymap.begin();
int min=0;
int minv=-1;
if(flag){
for(;iter1!=Mymap.end();iter1++){
if((*iter1).second>1){
min+=(((*iter1).second)*((*iter1).second-1))/2;
}
}
}
else{
map<int,int>::iterator iter2=++Mymap.begin();
for(;iter2!=Mymap.end();iter1++,iter2++){
int k=(*iter2).first-(*iter1).first;
if(minv==-1||k<minv){
min=((*iter2).second)*((*iter1).second);
minv=k;
}
else if(k==minv){
min+=((*iter2).second)*((*iter1).second);
}
}
}
int max=((*Mymap.begin()).second)*((*Mymap.rbegin()).second);
cout<<min<<" "<<max<<endl; //注意min和max输出之间一定要加一个空格,否则不能通过测试用例!!!
}
}
4.单选题
例1:下列属于无监督学习的是:(A)正确答案: A 你的答案: A (正确)
A.k-means B.SVM C.最大熵 D.CRF
解析:CRF是条件随机场,主要用在语音识别和文本识别,前提一个标记了的观察序列,计算需要验证的标签序列的联合概率。这里就有了标记集合和识别集合的概念,所以是监督学习。
例2:深度学习是当前很热门的机器学习算法。在深度学习中,涉及到大量矩阵相乘,现在需要计算三个稠密矩阵A,B,C的乘积ABC,假设三个矩阵的尺寸分别为m*n,n*p,p*q,且m<n<p<q,以下计算顺序效率最高的是:(B) 正确答案: B 你的答案: D (错误)
A.A(BC) B.(AB)C C.(AC)B D.所有效率都相同
解析:
首先,根据简单的矩阵知识,因为 A*B , A 的列数必须和 B 的行数相等。因此,可以排除C 选项,
然后,再看 A 、 B 选项。在 B 选项中, m*n 的矩阵 A 和 n*p 的矩阵 B 的乘积,得到 m*p 的矩阵 A*B ,而 A*B 的每个元素需要 n 次乘法和 n-1 次加法,忽略加法,共需要 m*n*p 次乘法运算。同样情况分析 A*B 之后再乘以 C 时的情况,共需要 m*p*q次乘法运算。因此, B选项的(AB)C 需要的乘法次数是 m*n*p+m*p*q 。同理分析, A选项的 A (BC)需要的乘法次数是 n*p*q+m*n*q 。
由于 m*n*p< m*n*q , m*p*q<n*p*q ,显然 A 运算次数更少,故选B。