后续所有方案中,二维数组动态的,可以如此申请
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 NotationEvaluate 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;
}
};