编程之旅-Day22

52 篇文章 0 订阅
48 篇文章 0 订阅
这篇博客回顾了Day22的学习内容,涉及剑指Offer的面试题,LeetCode的编程例子,2017年腾讯暑期实习编程题的复习,以及相关解题思路和代码实现。主要涵盖数据结构与算法的应用,如栈的min函数,寻找和为s的数字序列,最长回文子串等。
摘要由CSDN通过智能技术生成

目录

Day22-学习内容:

1.剑指Offer

面试题30:包含min函数的栈

面试题57:和为s的数字

面试题57:和为s的连续整数序列

 2.Leetcode

例1:插入间隔

例2:最长回文子串

 3.2017年腾讯暑期实习编程题-复习

编程题1:构造回文

编程题2:算法基础-字符移位

编程题3:n个数组成的二元组差最小和最大的对数

4.单选题


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.csdn.net/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.csdn.net/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。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值