最近在准备面试找工作,于是将这套题做了一下,将其记录一下,争取用最常见的方法来实现,还有就是尽量减小算法的复杂度。参考代码来源于网上,有的是参考网上的代码后修改而成,每个都能正确运行。
编程语言为C++
1-1题目: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
代码:
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
int rows = array.size();
int cols = array[0].size();
int i,j;
for(i = rows-1,j=0;i>=0&&j<cols;)
{
if(target == array[i][j])
{
return true;
}
else if(target<array[i][j])
{
i--;
continue;
}
else if(target>array[i][j])
{
j++;
continue;
}
}
return false;
}
};
1-2 请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy
代码:
class Solution {
public:
void replaceSpace(char *str,int length)
{
int blankNumber = 0; //空格的数量
int oldstringLen; //记录原字符串的长度
//首先遍历原字符串,找出字符串的长度以及其中的空格数量
for (oldstringLen = 0; str[oldstringLen] != '\0'; oldstringLen++)
{
if (str[oldstringLen] == ' ')
blankNumber++;
}
//根据原字符串的长度和空格的数量我们可以求出最后新字符串的长度
int newstringLen = oldstringLen + blankNumber * 2; //新字符串的长度
if (newstringLen>length)
return;
str[newstringLen] = '\0'; //此行很重要,因为原字符串最后一个字符为'\0'
//设置两个指针point1和point2分别指向原字符串和新字符串的末尾位置
int point1 = oldstringLen - 1, point2 = newstringLen - 1; //因为'\0'已经手工加到最后新串的最后一个字符,所以减1咯
while (point1 >= 0 && point2>point1)
{ //两指针相同时,跳出循环
if (str[point1] == ' ')
{ //如果point1指向为空格,那么从point2开始赋值“02%”
str[point2--] = '0';
str[point2--] = '2';
str[point2--] = '%';
}
else //如果point1指向内容不为空格,那么将内容赋值给point2指向的位置
str[point2--] = str[point1];
point1--; //不管是if还是else都要把point1前移,为了下一次的执行
}
}
};
1-3: 输入一个链表,从尾到头打印链表每个节点的值。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
//方法一 使用容器
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head)
{
vector<int>result;
stack<struct ListNode*>nodes;
struct ListNode* pNode = head;
while(pNode!=NULL)
{
nodes.push(pNode);
pNode = pNode->next;
}
while(!nodes.empty())
{
pNode = nodes.top();
result.push_back(pNode->val);
nodes.pop();
}
return result;
}
};
//方法二:使用递归
void printListFromTailToHead(ListNode* pListHead)
{
if(pListHead!=NULL)
{
if(pListHead->next!=NULL)
{
printListFromTailToHead(pListHead->next);
}
print("%d",pListHead->val);
}
}
//方法三
vector<int> printListFromTailToHead(struct ListNode* head)
{
vector<int> result;
struct ListNode* pNode=head;
while(pNode!=NULL)
{
result.push_back(pNode->val);
pNode=pNode->next;
}
reverse(result.begin(),result.end());//applying reverse()
return result;
}
1-4: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
代码:
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> in)
{
int in_size = in.size();
if(in_size == 0)
{
return NULL;
}
vector<int>pre_left,pre_right,in_left,in_right;
int val = pre[0];
TreeNode* node = new TreeNode(val);
int p = 0;
for(p;p<in_size;++p)
{
if(in[p] == val)
{
break;
}
}
for(int i = 0;i<in_size;++i)
{
if(i<p)
{
in_left.push_back(in[i]);
pre_left.push_back(pre[i+1]);
}
else if(i>p)
{
in_right.push_back(in[i]);
pre_right.push_back(pre[i]);
}
}
node->left = reConstructBinaryTree(pre_left,in_left);
node->right = reConstructBinaryTree(pre_right,in_right);
return node;
}
};
1-5: 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
代码:
class Solution
{
public:
void push(int node)
{
stack1.push(node);
}
int pop()
{
int result;
if(stack2.empty())
{
while(!stack1.empty())
{
stack2.push(stack1.top());
stack1.pop();
}
}
result = stack2.top();
stack2.pop();
return result;
}
private:
stack<int> stack1;
stack<int> stack2;
};
1-6: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0
代码:
//两外一个非正规思路,将原来的序列排序,那么第一个数就是要找的数;
//还有一个非正规思路,模拟冒泡排序,遍历一遍就找到最小的数,放再第一位,输出;
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.empty())
{
return 0;
}
int low = 0;
int high = rotateArray.size()-1;
int middle = 0;
if(rotateArray.at(low)<rotateArray.at(high))
{
return rotateArray.at(low);
}
//二分法查找
while(low<high)
{
if(high-low == 1)
{
middle = high;
break;
}
int middle = (low+high)/2;
if(rotateArray.at(middle)>=rotateArray.at(low))
{
low = middle;
}
else if(rotateArray.at(middle)<=rotateArray.at(low))
{
high = middle;
}
}
return rotateArray.at(middle);
}
};
1-7: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。n<=39
代码:
//方法1 以一种比较原始的方法;
class Solution {
public:
int Fibonacci(int n)
{
int a=1,b=1,c;
if(n<=0)
return 0;
else if(n == 1||n == 2)
return 1;
else
{
for(int i =3;i<=n;i++)
{
c= a+b;
b = a;
a = c;
}
}
return c;
}
};
// 对于这种调用层级太深的题目,最好不要用递归,否则运行可能会超时,例如下面的方法
class Solution {
public:
int Fibonacci(int n)
{
if(n<=2)
return n;
else
return Fibonacci(n-1)+Fibonacci(n-2);
}
};
1-8: 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
代码:
//最终就是一个斐波拉契数列问题;
class Solution {
public:
int jumpFloor(int number)
{
int a = 1,b=1,c=0;
if(number<=1)
return number;
for(int i = 2;i<=number;i++)//
{
c = a+b;
a = b;
b = c;
}
return c;
}
};
1-9: 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
代码:
//方法1 运用数学归纳法进行计算
class Solution {
public:
int jumpFloorII(int number)
{
if(number<=0)
return 0;
else
return pow(2,number-1);
}
};
//方法2 递归的方法
public long jumpFloor(int n)
{
if (n <= 0)
return -1;
if (n == 1)
return 1;
return 2 * jumpFloor(n - 1);
}
1-10 : 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
代码:
和1-9一样,写一个斐波拉契数列就好
1-11: 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
代码:
//如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作
//
class Solution {
public:
int NumberOf1(int n)
{
int count = 0;
if(n==0)
return count;
while(n!=0)
{
++count;
n = (n-1)&n;
}
return count;
}
};
1-12 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
代码:
//代码很简单,如下所示
class Solution {
public:
double Power(double base, int exponent)
{
double result = 1;
if(exponent == 0)
return 1;
int abs_exponent = abs(exponent);
for(int i = 0;i<abs_exponent;i++)
result *=base;
if(exponent<0)
return 1/result;
else
return result;
}
};
1-13 : 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
代码:
// 使用vector,烧苗两遍数组,第一遍将数组中的奇数存入vector中,第二遍将偶数存入vector中,直接返回vector,就好了。
class Solution {
public:
void reOrderArray(vector<int> &array)
{
vector<int>res;
for(int i = 0;i<array.size();i++)
{
if(array[i]%2==1)
res.push_back(array[i]);
}
for(int i = 0;i<array.size();i++)
{
if(array[i]%2==0)
{
res.push_back(array[i]);
}
}
array = res;
}
};
1-14; 输入一个链表,输出该链表中倒数第k个结点。
代码:
//思路:用两个指针,相差k个位置,然后同时向后移动,当后面那个指针移动到尾部,那么另外那个元素所指向的就是要求的节点
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
{
ListNode *p1,*p2;
p1 = pListHead;
p2 = pListHead;
if(pListHead == NULL||k <= 0)
return NULL;
for(int i = 0;i<k-1;i++)
{
if(p2->next!=NULL)
p2 = p2->next;
else
return NULL;
}
while(p2->next!=NULL)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
};
1-15: 输入一个链表,反转链表后,输出链表的所有元素。
代码:
//思路:依次遍历所有节点,将所有节点的next指向前一个节点
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* head)
{
ListNode* pre = NULL;
ListNode* next = NULL;
while(head!=NULL)
{
next = head->next;
head->next = pre;
pre = head;
head = next;
}
return pre;
}
};
1-16 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
代码:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
ListNode *head;
if(pHead1 == NULL&&pHead2!=NULL)
return pHead2;
else if(pHead2 == NULL&&pHead1!=NULL)
return pHead1;
else if(pHead1==NULL&&pHead2==NULL)
return NULL;
if(pHead1->val < pHead2->val)
{
head = pHead1;
head->next = Merge(pHead1->next,pHead2);
}
else
{
head = pHead2;
head->next = Merge(pHead1,pHead2->next);
}
return head;
}
};
1-17 : 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
代码:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(pRoot1 == NULL||pRoot2 == NULL)
{
return false;
}
return AhaseB(pRoot1,pRoot2) || HasSubtree(pRoot1->left,pRoot2) || HasSubtree(pRoot1->right,pRoot2);
}
bool AhaseB(TreeNode* pRoot1,TreeNode* pRoot2)
{
if(pRoot2 == NULL)
return true;
if(pRoot1 == NULL)
return false;
if(pRoot1->val == pRoot2->val)
return AhaseB(pRoot1->left,pRoot2->left) && AhaseB(pRoot1->right,pRoot2->right);
return false;
}
};
1-18: 操作给定的二叉树,将其变换为源二叉树的镜像。输入描述:
二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
代码:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
void Mirror(TreeNode *pRoot)
{
if((pRoot == NULL)||(pRoot->left==NULL&&pRoot->right == NULL))
return;
//交换左右子节点
TreeNode *pTemp = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right=pTemp;
//当左右子节点不为空,递归调用自身,整个过程就是一个递归,注意递归停止的条件
if(pRoot->left)
Mirror(pRoot->left);
if(pRoot->right)
Mirror(pRoot->right);
}
};
1-19: 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
代码:
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
int row = matrix.size();
int col = matrix[0].size();
vector<int> res;
// 输入的二维数组非法,返回空的数组
if (row == 0 || col == 0) return res;
// 定义四个关键变量,表示左上和右下的打印范围
int left = 0, top = 0, right = col - 1, bottom = row - 1;
while (left <= right && top <= bottom)
{
// left to right
for (int i = left; i <= right; ++i) res.push_back(matrix[top][i]);
// top to bottom
for (int i = top + 1; i <= bottom; ++i) res.push_back(matrix[i][right]);
// right to left
if (top != bottom)
for (int i = right - 1; i >= left; --i) res.push_back(matrix[bottom][i]);
// bottom to top
if (left != right)
for (int i = bottom - 1; i > top; --i) res.push_back(matrix[i][left]);
left++,top++,right--,bottom--;
}
return res;
}
};
1-20: 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
代码:
class Solution {
public:
void push(int value)
{
st.push(value);
if(smin.empty())
smin.push(value);
if(smin.top()>value)
smin.push(value);
}
void pop()
{
if(smin.top() == st.top())
smin.pop();
st.pop();
}
int top()
{
return st.top();
}
int min()
{
return smin.top();
}
private:
stack<int>st;
stack<int>smin;
};
1-21: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
代码:
//
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV)
{
int length = pushV.size();
stack<int>stackData;
if(pushV.size()>0&&popV.size()>0&&pushV.size()==popV.size())
{
int i=0,j=0;
while(j<length)
{
while(stackData.empty()||stackData.top()!=popV[j])
{
if(i>length-1)
break;
stackData.push(pushV[i]);
++i;
}
if(stackData.top()!=popV[j])
break;
stackData.pop();
++j;
}
if(stackData.empty()&&j == length)
return true;
}
return false;
}
};
未完待续。。。