面试题-写代码

算法类

1.合并数组

先写合并两个有序数组,接着写合并n个有序的数组,假设总共m个数

解法:N个数组进行两两合并,合并后的数组再继续执行合并过程,最后合成NM的有序数组。可以认为合并这个递归过程发生了logN次,每一次合并的过程都是NM个数合并,所以每一次合并的时间复杂度为NM,总的时间复杂度就是NM*logN


vector<int> num[maxn];//用vector<int> num[n]存储n个长度为m的数组,maxn>=n其实即可
vector<int> res[maxn];//整个原始数组两两合并合并的结果数组
vector<int> helper;//两个数组合并的时候辅助

//合并n个长度为m的数组,
vector<int> merge_all(vector<int> num)
    int cnt=num.size();//目前有几个数组待合并

    if(cnt==1) return num[0];//如果只剩一个数组直接返回

    res.clear();//清空之前的合并结果,准备再次合并

    for(int i=0;i<cnt;i+=2)
    {
        res[i/2]=mergetwoarray(ori[i],ori[i+1]);
    }
    return merge(res);//继续合并,直到合并成一个
}

//两个数组合并(归并排序的合并部分)
vector<int> mergetwoarray(vector<int> num1,vector<int> num2)
{
    int len1=num1.size();
    int len2=num2.size();
    int p=0,q=0;
    while(p<len1||q<len2)
    {
        if(q>=len2||p<len1&&num1[p]<=num2[q])helper.push_back(num1[p++]);
        else helper.push_back(num2[q++]);
    }
    return helper;
}
    

2.把字符串转化成整数

题目描述
请你写一个函数StrToInt,实现把字符串转换成整数这个功能。
当然,不能使用atoi或者其他类似的库函数。

越是简单的题目越是需要考虑 特殊情况的处理 比如,非数字字符,整数溢出,正负号等


INT_MAX=2^31-1;
INT_MIN=-2^31;

int str_to_int(string str)
{
    int flag,i=0;//flag记录正负,i记录当前字符下标
    
    long long int res=0;//res记录当前数值,因为有可能整数溢出,所以用long long
    
    int n=str.size();
    
    //先处理前面的空格
    while(str[i]==' '&&i<n)) i++;
    if(i>=n) return 0;//没有数字
    
    //只有第一个非空字符是正负号或者数字才有可能完成有效的转换
    if(str[i]='-'){flag=-1;i++;}
    else if(str[i]<'0'||str[i]>'9') return 0;
    else {flag=1;}
    
    while(str[i]>='0'&&str[i]<='9')
    {
        res=res*10+str[i]-'0';//每读入一位,就向前移动原来的数字(=*10)
        i++;
        if(res>INT_MAX) return INT_MAX;
        if(res<INT_MIN) return INT_MIN;
    }
    
    return res;   
    
}

3.查找数组中的重复元素

方法一:哈希表
map<int,int> hashmap;
for(int i=0;i<nums.size();i++)
{
    if(!hashmap.count(nums[i])) hashmap.insert(pair<int,int>(nums[i],nums[i]));
    else return nums[i];
}
return -1;

T ( n ) = O ( n ) T(n)=O(n) T(n)=O(n),遍历数组,每次查找这个元素是否在哈希表中需要 O ( 1 ) O(1) O(1)

S ( n ) = O ( n ) S(n)=O(n) S(n)=O(n),需要存储一个哈希表

方法二:原地交换
 for(int i=0;i<nums.size();i++)
    {
        if(i==nums[i]) continue;
        else if(nums[i]==nums[nums[i]]) return nums[i];
        else swap(nums[i],nums[nums[i]]);
    }
    return -1;
    }

T ( n ) = O ( n ) T(n)=O(n) T(n)=O(n),遍历数组,每次交换操作需要 O ( 1 ) O(1) O(1)
S ( n ) = O ( 1 ) S(n)=O(1) S(n)=O(1),在原数组上交换,不需要额外的空间

方法三:不改变原数组

数字都在1~n之间,如果没有重复的数字,那么数字总数应该是在n以内的,如果有重复的数字,总数就会大于n(当1到n全都出现的时候),那么可以用二分查找的思路,用2/n分成两部分,[1,2/n)和[2/n,n),统计[1,2/n)在原数组中出现的次数,如果出现的次数大于n/2,那么重复的数字就在[1,2/n)中,再继续二分直到找到某一个重复的数字

eg:[1,2,6,5,5,3,4,7,8]
先分别统计1,2,3,4,在数组中出现的次数
1:1次
2:1次
3:1次
4:1次
正好出现了4次,那么重复的数字就在[5,8]中,再把[5,8]分成[5,6]和[7,8]两部分,分别统计

T ( n ) = O ( n l o g n ) T(n)=O(nlogn) T(n)=O(nlogn),统计这个元素在指定区间内出现了几次的函数总共被调用 O ( l o g n ) O(logn) O(logn)次,
S ( n ) = O ( 1 ) S(n)=O(1) S(n)=O(1),在原数组上交换,不需要额外的空间

4.字符串替换

方法一:插入删除
  string replaceSpace(string s) {
        int p=0;
        while(s.find(" ",p)!=string::npos)
    {
        p=s.find(" ",p);
        s.insert(p,"%20");
        s.erase(p+3,1);
    }
    return s;
    }
while遍历字符串O(n),字符串查找函数find的时间复杂度O(n)因此,这个方法时间复杂度度为:$O(n^2)$ 
方法二:指针帮助复制

用两个指针p,q
首先可以遍历字符串,统计出空格个数k,这样就知道替换后的串长度,s.size()+2k,

(1)如果不要求空间复杂度,可以建立一个空串snew来存放结果字符串,
p指向原数组当前要复制到新数组的字符,q指向新数组当前可写入的下标,从前向后(或者从后向前遍历数组),
不是空格的时候,snew[q]=s[p];p++;q++
当遇到空格的时候,p++;snew[q]=‘2’;snew[++q]=‘0’;snew[++q]=’%’;q++;
直到遍历完整个数组

(2)如果只能在原字符串上操作,必须从后往前遍历字符串,因为这样不会让替换后的结果覆盖掉原来的串。

让p指向s的末尾,q指向替换后的字符串的末尾
p=s.size(); q=s.size()+2k;
从后往前遍历,
不是空格的时候,s[q]=s[p];p–;q–;
是空格的时候,是p–;s[q]=’%’;s[–q]=‘0’;s[–q]=‘2’;q–;
直到遍历整个字符串

这里用到s.resize()重置字符串长度

    string replaceSpace(string s) {
int i=0,blankcon=0;
while(i<s.size())
{
    if(s[i]==' ') blankcon++;
    i++;
}
int p=s.size()-1;
int q;
s.resize(s.size()+2*blankcon);
q=s.size()-1;

while(p>=0)
{
    if(s[p]!=' '){s[q]=s[p];q--;p--;}
    else{
        p--;
        s[q]='0';
        s[--q]='2';
        s[--q]='%';
        q--;
    }
}
return s;
    }

存在需要对数组整体进行多次移动的题目,考虑用一个辅助数组、两个指针,分别指向原数组和辅助数组进行操作
T ( n ) = O ( n ) T(n)=O(n) T(n)=O(n), S ( n ) = O ( 1 ) S(n)=O(1) S(n)=O(1)

数据结构类

1.优先队列什么实现的?

优先队列什么实现的?堆。自己写一个堆实现优先队列

小顶堆(最小的数由最高的优先级)
定义:priority_queue<int,vector<int>,greater<int> > pq;
大顶堆(最大的数先出队)
定义:priority_queue<int,vector<int>,less<int> > pq;

能够使用二叉树高效的解决上述问题,树的节点从上到下,从左到右的顺序紧凑排列的。最重要的是,儿子节点的值一定>=父亲节点的值

插入操作:自底向上提升,直到没有大小颠倒

出堆操作:自顶向下(堆顶元素出去,把最后一个元节点提到堆顶,删除最后一个节点,然后不断向下交换直到没有大小颠倒,交换的时候选择子节点中较小的一个交换)

时间复杂度:和树的高度有关,如果有n个节点,那么就是 O ( l o g n ) O(logn) O(logn)

堆的实现:
用数组存储堆的元素,而不用指针表示左右子树
父节点的编号是i
左孩子节点的编号是:2i+1
右孩子节点的编号是:2
i+2
求节点i的父节点编号用 (i-1)/2,因为C++除法的关系,不论i是左孩子还是右孩子,都会得到相同的父节点

int heap[MAX_N],sz=0;//初始堆的大小为0

//插入操作
void push(int x)
{
    int i=sz++;//i表示这个值x要插入的位置
    
    while(i>0)
    {
        int p=(i-1)/2;//父节点的编号
        
        if(heap[p]<=x) break;//没有大小颠倒,直接退出循环
        
        heap[i]=heap[p];//把父节点的值往下移,放到i的位置
        i=p;//这个值要插入的位置从最后移动到父节点的位置
    }
    heap[i]=x;   
}

//出堆操作
int pop()
{
    int ret=heap[0];//堆顶元素出堆
    
    int x=heap[--sz];//最后一个元素提到堆顶,因为sz是堆的大小,所以先减一
    
    int i=0;//i是原本的最后一个节点所在的位置(目前已经被提到堆顶)
    while(i*2+1<sz)//因为sz-1是原来最后一个节点的编号,现在背提到堆顶了,那么现在最后一个节点的编号应该是<=堆栈的size-2
    //同时因为让左孩子是两个孩子里比较小的,所以比较2i+1就可以了
    {
        int a=i*2+1,b=2*i+2;//当前节点的左右孩子
        if(b<sz&&heap[b]<=heap[a]) a=b;//让a是左右孩子中小的那个节点的编号
        
        if(heap[a]>=x) break;
        
        heap[i]=heap[a];//把小的孩子节点提上去
        
        i=a;//现在要比较x和小的孩子节点的左右孩子的大小
    }
    heap[i]=x;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值