找工作--笔试面试--准备1

本文精选了若干经典的编程面试题目,涵盖了字符串操作、数组处理、链表排序等多个方面,并提供了详细的算法实现及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

后续所有方案中,二维数组动态的,可以如此申请

vector<vector<int>> vec(10,vector<int>(0));

1、首先第一个,strstr()函数,这个是阿里曾经面试的一道题目。

其实来源于,leetcode的一道题目,Implement strStr()

其实就是寻找一个字符串是否是另外一个字符串的子字符串,然后返回其位置就可以了。。。

第一个想法,暴力,不过如果你对算法比较熟悉的话,第一个应该想起的是KMP,不过KMP不好一下写出来吧,至今对他的编写还是有点发怵,不过july大神讲过这个了,好好思考一下,应该可以的。

先来暴力的算法版本,这个也是c语言的源码

char *strStr(char *haystack, char *needle) {
       int len2;
        if (!(len2 = strlen(needle)) )
        	return (char *)haystack;
        for (; *haystack; ++haystack )
        {
        	if ( *haystack == *needle && strncmp( haystack, needle, len2 )==0 )
        	return (char *)haystack;
        }
        return NULL;
    }
先查needle的长度,然后一个循环,利用strncmp,这里不能用strcmp,这个函数看来我是记错功能了,他的主要用途是判别两个字符串是否相等,而不是某几个。与这个类似的还有

这里需要注意的是,返回值是char*类型的,有必要进行强制转换一下char*,比较稳妥。

然后是KMP的算法,KMP包含两个部分,一个是计算next数组,另外一个则是计算相关返回值。

void GetNext(char* p,int next[])  
{  
    int pLen = strlen(p);  
    next[0] = -1;  
    int k = -1;  
    int j = 0;  
    while (j < pLen - 1)  
    {  
        //p[k]表示前缀,p[j]表示后缀  
        if (k == -1 || p[j] == p[k])   
        {  
            ++k;  
            ++j;  
            next[j] = k;  
        }  
        else   
        {  
            k = next[k];  
        }  
    }  
}  
int KmpSearch(char* s, char* p)  
{  
    int i = 0;  
    int j = 0;  
    int sLen = strlen(s);  
    int pLen = strlen(p);  
    while (i < sLen && j < pLen)  
    {  
        //①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++      
        if (j == -1 || s[i] == p[j])  
        {  
            i++;  
            j++;  
        }  
        else  
        {  
            //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]      
            //next[j]即为j所对应的next值        
            j = next[j];  
        }  
    }  
    if (j == pLen)  
        return i - j;  
    else  
        return -1;  
}  
getnext函数也是和获取的next数组反复利用而得到的。

2、strcpy函数 这个面试的频率也好高啊

首先考虑的是,src的char * 指针是const char *的,对于const char * 和char * const的区别,这里有个记忆的巧法,将const char 连在一起的,是指向静态字符串的,而char * const 则指这个是指向静态指针的。

另外一个考虑的是 内存重叠问题,差不多的意思,如果是从头开始进行复制,那么src的字符串的尾部就会被改变,这是需要考虑的。

已知strcpy函数的原型是:
char * strcpy(char * strDest,const char * strSrc);
1.不调用库函数,实现strcpy函数。
2.解释为什么要返回char *。

这个题目没有考虑内存重叠的问题。

char * strcpy(char * strDest,const char * strSrc)

        {

                if ((strDest==NULL)||(strSrc==NULL)) //[1]

                        throw "Invalid argument(s)"; //[2]

                char * strDestCopy=strDest;  //[3]

                while ((*strDest++=*strSrc++)!='\0'); //[4]

                return strDestCopy;

        }
考虑了内存重叠的问题以后会出现的问题

char * strcpy(char * strDest,const char * strSrc)

        {

                if ((strDest==NULL)||(strSrc==NULL)) //[1]

                        throw "Invalid argument(s)"; //[2]

                char * strDestCopy=strDest;  //[3]
                int count = 1;
                char * strSrcCopy = strSrc;
                
                while(*strSrcCopy++!='\0'){
                    count++;
                }
                if(strDestCopy<strSrc||strDestCopy>strSrc+count){
                    while ((*strDest++=*strSrc++)!='\0'); //[4]
                }
                else{
                    strDestCopy = strDest+count;
                    strSrcCopy = strSrc + count;
                    while((*strDestCopy--=*strSrcCopy--)!='\0');
                }

                return strDestCopy;

        }
3、strcmp函数

C/C++函数,比较两个字符串。设这两个字符串为str1,str2,若str1==str2,则返回零;若str1>str2,则返回正数;若str1<str2,则返回负数。

int strcmp(const char * src1,const char * src2){
if(src1==NULL||src2==NULL){
     throw exception;
}
while(*str1&&*str2&&*str1==*str2){
     str1++;
     str2++;
}
}
return (*str1-*str2);


4、在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b。 

定义一个hashtable,256大小,字符串所有字符遍历一遍,然后查找为1的字符。


5、求连续子数组连续和

int maxsequence3(int a[], int len)
{
    int maxsum, maxhere;
    maxsum = maxhere = a[0];   //初始化最大和为a【0】
    for (int i=1; i<len; i++) {
        if (maxhere <= 0)
            maxhere = a[i];  //如果前面位置最大连续子序列和小于等于0,则以当前位置i结尾的最大连续子序列和为a[i]
        else
            maxhere += a[i]; //如果前面位置最大连续子序列和大于0,则以当前位置i结尾的最大连续子序列和为它们两者之和
        if (maxhere > maxsum) {
            maxsum = maxhere;  //更新最大连续子序列和
        }
    }
    return maxsum;
}
这个是连续子树组的连续和,复杂度为n,其实主要是判断前面是否为小于0的,如果是则可以忽略了。动态规划也可以做这个,但是时间和空间复杂度都太高了。

下面这个是leetcode最新的一个题目,但是我觉得其实就是这个变体,

Maximum Product Subarray,这个是求连续子数列的最大连乘积。

int maxProduct(int A[], int n) {
        
        int imax = A[0];
        int imin = A[0];
        int iresult  = A[0];
        for(int i = 1;i<n;i++){
            int tmax = max(A[i],max(A[i]*imax,A[i]*imin));
            int tmin = min(A[i],min(A[i]*imax,A[i]*imin));
            imax = tmax;
            imin = tmin;
            iresult = max(iresult,imax);
        }
        return iresult;
其实原理差不多,保留最大,最小值,然后作为下一次的基底。


6、reverse words in a string

Given an input string, reverse the string word by word.

For example,
Given s = "the sky is blue",
return "blue is sky the".
这个其实就是反转字符串,但是需要注意的是,这里面还是有些需要和面试官沟通的,比如如果多个空格怎么办?当作一个,开头和结尾的地方有空格怎么办?忽略。一个word的定义是什么,中间没有空格。

这里的思路是,一个一个的读入字符,遇到一个空格,则认为空格是分隔符,然后将刚才读入的单词进行反转,然后放入栈中,以后再取出来。

不过下面给出的代码的原理是,从后往前读入,然后放入string中,连重新再读入都省掉了。


void reverseWords(string &s) {
        string rs;  
        for (int i = s.length()-1; i >= 0; )  
        {  
            while (i >= 0 && s[i] == ' ') i--;  
            if (i < 0) break;  
            if (!rs.empty()) rs.push_back(' ');  
            string t;  
            while (i >= 0 && s[i] != ' ') t.push_back(s[i--]);  
            reverse(t.begin(), t.end());  
            rs.append(t);  
        }  
        s = rs;  
        
    }
7、Evaluate Reverse Polish Notation
Evaluate the value of an arithmetic expression in Reverse Polish Notation.

Valid operators are +, -, *, /. Each operand may be an integer or another expression.

Some examples:
["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9
["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6

其实这个应该在数据结构的那本书,严蔚敏的那本,上面字符串讲过了,就是入栈出栈的操作。

int evalRPN(vector<string> &tokens) {
        stack<int> stack;
	for(int i = 0;i<tokens.size();i++){
	<span style="white-space:pre">	</span>const char * p  = tokens[i].c_str();
		if(p[1] == '\0' && ( p[0] == '+' || p[0] == '-' || p[0] == '*' || p[0] == '/' )) {
		switch(*p){
		    case 43:  
                    {   
                        int operand2 = stack.top();  
                        stack.pop();  
                        int operand1 = stack.top();  
                        stack.pop();  
                        stack.push(operand1 + operand2);  
                        break;  
                    }  
                    case 45:  
                    {    
                        int operand2 = stack.top();  
                        stack.pop();  
                        int operand1 = stack.top();  
                        stack.pop();  
                        stack.push(operand1 - operand2);  
                        break;  
                    }  
                    case 42:  
                    {  
                        int operand2 = stack.top();  
                        stack.pop();  
                        int operand1 = stack.top();  
                        stack.pop();  
                        stack.push(operand1 * operand2);   
                        break;  
                    }  
                    case 47:  
                    {   
                        int operand2 = stack.top();  
                        stack.pop();  
                        int operand1 = stack.top();  
                        stack.pop();  
                        stack.push(operand1 / operand2);  
                        break;  
                    }  
                    default:  
                    {  
                        break;  
                    }  	
		}
		}
		else{
		     stack.push(atoi(p));
		}
	}
	return stack.top();  
    }
我觉的最重要的是有一个思路,知道整体程序结构是如何,然后才开始写,是最好的。

8、Max Points on a Line
Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.

其实这个,我一开始是没有思路的,当然暴力的思路遍历就不算了。这个题目首先需要注意的是如何定义一条直线?学过数学知道,y = kx+b,最重要的是k和b,这当然是废话,不过这个却可以用来做这个题目。

遍历两个点的可能组合,然后计算这两个点的直线,然后将剩下的所有的点,看是否在这个线上。处理技巧包括,这条线的斜率为无穷。

三种情况,两个点是同一个位置,两个点是垂直线,两个点正常情况。。。。然后挨个处理就可以了。。。这个复杂度是n^3,竟然过了leetcode。参考的网上的代码。

/**
 * Definition for a point.
 * struct Point {
 *     int x;
 *     int y;
 *     Point() : x(0), y(0) {}
 *     Point(int a, int b) : x(a), y(b) {}
 * };
 */
class Solution {
public:
    int maxPoints(vector<Point> &points) {
		int max_points(0);  
        double a(0), b(0); // denote a straight line  
        LINE_TYPE line_type(LINE);   
          
        if(points.size() == 0) return 0;  
        else if(points.size() == 1) return 1;  
          
        for(int i=0; i<points.size(); ++i)  
          for(int j=i+1; j<points.size(); ++j)  
          {  
              if(points[i].x == points[j].x){  
                  if(points[i].y == points[j].y)  
                    line_type = POINT;  
                  else  
                    line_type = VLINE;  
              }  
              else{  
                line_type = LINE;  
                a = (points[j].y - points[i].y) / static_cast<double>(points[j].x - points[i].x); //tangent  
                b = points[i].y - a * points[i].x;  
              }  
                
              // compute how many points on the line    
              int point_count(2);  
              for(int k=0; k<points.size(); ++k){  
                if(k == i || k == j) continue;  
                  
                // test whether points[k] on the line  
                if(line_type == LINE){  
                    if( fabs(points[k].y - a*points[k].x - b) < 1e-10)  
                      ++ point_count;  
                }  
                else if(line_type == POINT){  
                    if(points[k].x == points[i].x && points[k].y == points[i].y)  
                        ++ point_count;  
                }  
                else{  
                    if(points[k].x == points[i].x)  
                        ++ point_count;  
                }  
              }  
              max_points = max(max_points, point_count);  
          }  
          
        return max_points;  
    }
private:
	enum LINE_TYPE{POINT,LINE,VLINE};
};
9、Sort List

Sort a linked list in O(n log n) time using constant space complexity.

constant space,复杂度,o(nlogn)

快排的思路,因为这里说了nlogn,而且这个是链表,就不用考虑会创建其他的结点了,但是有一点不明白,如果用递归,递归的堆栈的深度谁来算?如果有看到好的解法,再来。。。

思路,两个指针,一个指针走一步,一个指针走两步,然后找到链表重点,然后进行拆分成两个链表,然后进行递归调用快排,然后将两个merge。

先找中点,然后拆分

然后sort,sort

然后,merge,返回merge的首节点。。。。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *sortList(ListNode *head) {
		if(head == NULL||head->next == NULL){
			return head;
		}
		else{
			//从中间进行分开sort
			ListNode * fast=head,*low = head;
			while(fast->next!=NULL&&fast->next->next!=NULL){
				fast=fast->next->next;
				low=low->next;
			}
			fast=low;
			low =low->next;//结尾
			fast->next = NULL;
			fast = sortList(head);
			low = sortList(low);
			return merge(fast,low);
		}
    }
	ListNode * merge(ListNode * head1,ListNode * head2){
		if(head1 == NULL){
			return head2;
		}
		if(head2 == NULL){
			return head1;
		}
		ListNode *p,*res;
		if(head1->val<head2->val){
			res = head1;
			head1 = head1->next;
		}
		else{
			res = head2;
			head2 = head2->next;
		}
		p=res;
		while(head1!=NULL&&head2!=NULL){
			if(head1->val<head2->val){
				p->next = head1;
				head1 = head1->next;
			}
			else{
				p->next = head2;
				head2 = head2->next;
			}
			p = p->next;
		
		}
		if(head1 == NULL){
			p->next = head2;
		}
		else if(head2 == NULL){
			p->next = head1;
		}
		return res;
	}
};


10、Insertion Sort List

Sort a linked list using insertion sort.

插入式的sort方法,直接插入法

先查看将要插入的点和head相比较,然后确定头结点

如果比head大,那就一直查到合适的位置进行插入

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *insertionSortList(ListNode *head) {
		if(head == NULL||head->next == NULL){
			return head;
		}
		ListNode* p=head->next,*res = head;//循环,头指针,遍历的前一个指针
		res->next = NULL;
		while(p!=NULL){
			ListNode * ptr = res,*phead = ptr;
			if(ptr->val>p->val){
				phead = p;
				p=p->next;
				phead->next = ptr;
				res = phead;
				continue;
			}//查看是否比头结点大
			ptr=ptr->next;
			while(ptr!=NULL&&ptr->val<p->val){
				phead = ptr;
				ptr = ptr->next;
			}//查到合适的位置
			if(ptr == NULL){
				ptr = p;
				p = p->next;
				phead->next = ptr;
				ptr->next = NULL;
			}//尾结点
			else{
				ptr = p;
				p=p->next;
				ptr->next = phead->next;
				phead->next = ptr;
			}//非尾结点~

		}
		return res;
	}
};





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值