1.定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
思路:需要两个栈,主栈和辅助栈,每次插入主栈的值时确定,插入的值比辅助栈top大还是比辅助栈top小,只有在辅助栈的size为0或者比辅助栈小的情况下入辅助栈栈,pop操作也是同理,pop的值与辅助栈做比较,当相同时辅助栈做出栈操作。
class Solution {
public:
void push(int value) {
obj_stack.push(value);
if(min_stack.size() == 0)
{
min_stack.push(value);
return ;
}
if(min_stack.top() >= obj_stack.top() )
{
min_stack.push(value);
return ;
}
}
void pop() {
if(min_stack.top()==obj_stack.top())
{
min_stack.pop();
}
obj_stack.pop();
}
int top() {
return obj_stack.top();
}
int min() {
return min_stack.top();
}
private:
stack<int> min_stack,obj_stack;
};
2.输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
思路分析:需要一个vector<vector>和vector,用递归函数求出每条路径,当满足条件时,将该路径压入vector<vector>中,再pop_back,最后返回vector<vector>。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
if(root == nullptr)
return result;
_FindPath(root,expectNumber);
return result;
}
void _FindPath(TreeNode* root,int expectNumber)
{
temp.push_back(root->val);
if(root->left == nullptr && root->right == nullptr && expectNumber-root->val == 0)
{
result.push_back(temp);
}
if(root->left)
_FindPath(root->left,expectNumber- root->val);
if(root->right)
_FindPath(root->right,expectNumber- root->val);
temp.pop_back();
}
private:
vector<int> temp;
vector<vector<int>> result;
};
3。从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路分析:用一个vector或者queue保存每一层的节点,需要注意的是层序遍历,不能使用递归,而要使用迭代,因为vector的头删效率比较低所以下面的代码可以将in改成queue的类型,使用pop函数删除
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> out;
if (root == nullptr)
return out;
in.push_back(root);
while (!in.empty())
{
out.push_back(in.front()->val);
if (in.front()->left != nullptr)
in.push_back(in.front()->left);
if (in.front()->right != nullptr)
in.push_back(in.front()->right);
in.erase(in.begin());
}
return out;
}
private:
vector<TreeNode*> in;
};
4.输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路:所谓的二叉搜索树,左子树比根小,右子树比根大,后序遍历为左右根最后一个为根节点,可以用根从后往前比较大小,当一个数比根节点小下标为i-1,这个节点就算是左子树的最大序列的节点,再该序列将分为(begin,i-1)和(i,end-1)进行递归函数运算,输出结果。
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence)
{
if (sequence.size() == 0)
return false;
return judge(sequence, 0, sequence.size() - 1);
}
bool judge(vector<int> sequence, int begin, int end)
{
if (begin >= end)
return true;
int i = end;
while (i > begin && sequence[i-1] >= sequence[end])
{
i--;//最终的左子树的最大下标
}
for (int j = i-1; j >= begin; j--)
{
if (sequence[j] > sequence[end])
return false;
}
return (judge(sequence, begin, i-1)) && (judge(sequence, i, end-1));
}
};
5.输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路分析:设置一个辅助栈,将压入序列按顺序压入辅助栈中,每次判断是否和弹出序列的首个成员比较值是否相同,如果相同,则将该成员从辅助栈和弹出序列中删除,弹出序列的下一个元素成为新的首部序列,while循环直到将所有的压入顺序都判断一遍,再判断弹出序列是否为空,如果为空返回true否则返回false。
class Solution {
public:
bool IsPopOrder(vector<int> pushV, vector<int> popV) {
if (pushV.size() == 0)
return false;
int i = 0;
while (i != pushV.size())
{
in.push(pushV[i++]);
while (in.size() != 0 && in.top() == popV.front())
{
in.pop();
popV.erase(popV.begin());
}
}
if (popV.size() != 0)
return false;
return true;
}
private:
stack<int> in;
};
6.给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
思路分析:感觉这题挺沙雕的,判断一下,次方是否大于零,如果大于,就累乘,如果小於则base先被1除,再累乘,肯定有很多种解法,我下面的代码虽然通过测试,但是有溢出的隐患
class Solution {
public:
double Power(double base, int exponent) {
if(base == 0)
return 0;
if(exponent == 0)
return (double)1;
double _base =1;
if(exponent >0)
{
for(int i=0;i<exponent;i++)
_base *=base;
return _base;
}
if(exponent <0)
{
base = 1/base;
}
exponent = -exponent;
for(int i=0;i<exponent;i++)
_base *=base;
return _base;
}
};
7.输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
思路分析:可以设置两个辅助栈,一个用来装奇数,另一个用来装偶数,最终将两个栈合起来,再替换原来的栈。还有一种做法是类似冒泡算法,前奇后偶就交换。
class Solution {
public:
void reOrderArray(vector<int> &array) {
for(int i=0;i<array.size();i++)
{
if(array[i]%2 !=0)
{
Odd.push_back(array[i]);
}
else{
Eve.push_back(array[i]);
}
}
for(int i=0;i<Eve.size();i++)
{
Odd.push_back(Eve[i]);
}
for(int i=0;i<array.size();i++)
{
array[i] = Odd[i];
}
}
private:
vector<int> Eve;
vector<int> Odd;
};
8.输入一个链表,输出该链表中倒数第k个结点。
思路:设置两个指针的,先判断是输入参数否符合条件,当第一个指针走到第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) {
if(k == 0|| pListHead == nullptr)
return nullptr;
ListNode* l;
ListNode* m;
l = pListHead;
m = pListHead;
for(int i=1;i<k;i++)
{
if(l->next != nullptr)
l = l->next;
else
return nullptr;
}
while(l->next != nullptr)
{
l = l->next;
m = m->next;
}
return m;
}
};
9.输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路分析:先确定头节点是哪个,设置两个节点指针,一个用来存头解点,一个用来记录之后每个节点,需要注意的一点是,记录用的节点除了第一次头节点是用本身来记录节点,其他都是是用next来记录,不能用本身来记录节点,否则会出错。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == nullptr)
return pHead2;
if(pHead2 == nullptr)
return pHead1;
ListNode* pHd = nullptr;
ListNode* pHd2 = nullptr;
while(pHead1 != nullptr && pHead2 != nullptr)
{
if(pHead1->val >= pHead2->val)
{
if(pHd == nullptr)
{
pHd = pHead2;
pHd2 = pHd;
pHead2 = pHead2->next;
}
else
{
pHd->next = pHead2 ;
pHd = pHd ->next;
pHead2 = pHead2->next;
}
}
else
{
if(pHd == nullptr)
{
pHd = pHead1;
pHd2 = pHd;
pHead1 = pHead1->next;
}
else{
pHd->next = pHead1 ;
pHd = pHd ->next;
pHead1 = pHead1->next;
}
}
}
if(pHead1 == nullptr)
{
pHd->next = pHead2;
}
else{
pHd->next = pHead1;
}
return pHd2;
}
};
10.输入一个链表,反转链表后,输出新链表的表头。
思路:用三个指针分别储存当前指针,前面的一个指针,后面的指针,然后按次序反转,最后返回节点,下面用的是迭代的方法。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == nullptr||pHead->next == nullptr)
{
return pHead;
}
ListNode* head = pHead;
ListNode* nehead;
ListNode* prehead = nullptr;
while(head)
{
nehead = head->next;
head->next = prehead;
prehead = head;
head = nehead;
}
return prehead;
}
};
11.输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路分析:根据树的结构,应该用递归的形式用前序的方式判断每个根是否为B树的根,如果是调用判断函数,结果用应该bool值储存,如果有一个bool值为真,返回真,全部为假返回假,由子树的定义可知,当A的节点为空且B树并未空时,返回假,如果B节点为空返回真,否则进入下一层,得到结果。
/*
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* p1, TreeNode* p2)
{
if(p1 == nullptr )
return false;
if( p2 == nullptr)
return false;
bool result;
if(p1->val == p2->val)
{
result = _hass(p1,p2);
}
if(result == false)
{
result = HasSubtree(p1->left, p2);
}
if(result == false)
{
result = HasSubtree(p1->right, p2);
}
return result;
}
bool _hass(TreeNode* p1, TreeNode* p2)
{
if(p1 ==nullptr && p2 == nullptr)
return true;
if(p1 == nullptr)
return false;
if(p2 == nullptr)
return true;
if(p1->val != p2->val)
return false;
return _hass(p1->left,p2->left)&&_hass(p1->right,p2->right);
}
};
12.输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
思路分析:这道题的关键在于负数的补码里面的1的个数的计算,可以考虑用位移运算符来计算,要注意的是位移运算符的优先度最小,要加上括号,同时因为负数的算术左移问题,不能移动输入的数字,而是要移动用来判断的变量1,如果与的结果非0,count++。用count记录,最后输出
class Solution {
public:
int NumberOf1(int n) {
long long count = 0;
int flag = 1;
while(flag)
{
if((n & flag) != 0)
{
count++;
}
flag = (flag<<1);
}
return count;
}
};
13.输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路分析:这道题的关键是平衡二叉树的性质,可以用中序来对树的节点进行修改,设置一个节点指针变量pre = nullptr,用来储存上一个节点,用引用传递的方式传入参数,让当前的节点的左节点指向pre,判断pre是否为空指针,如果不是,pre的右节点指向当前的节点,最后一步,将当前的节点赋给pre,不断递归调用,停止条件为当前节点为空。完成后,用循环将最左边的节点找到,返回。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree == nullptr)
return nullptr;
if(pRootOfTree->left == nullptr && pRootOfTree->right == nullptr)
return pRootOfTree;
TreeNode* pre = nullptr;
_convert(pRootOfTree,pre);
while(pRootOfTree->left)
{
pRootOfTree = pRootOfTree->left;
}
return pRootOfTree;
}
void _convert(TreeNode* p,TreeNode*& pre)
{
if(p == nullptr)
return ;
_convert(p->left,pre);
p->left = pre;
if(pre)
pre->right = p;
pre = p;
_convert(p->right,pre);
}
};
14.输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路分析:解决这一题有多种方法,这里我采用了容易理解的三段法
1.在每个节点后加入一个复制节点。
2.遍历变长的链表,确定random的指向
3.断开链表
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
void Clone1(RandomListNode* p1)
{
//复制原始链表的任一节点N并创建新节点N',再把N'链接到N的后边
RandomListNode* p = p1;
while(p != nullptr)
{
RandomListNode* node = new RandomListNode(0);
node->label = p->label;
node ->next = p->next;
node ->random= nullptr;
p->next = node;
p = node->next;
}
}
void Clone2(RandomListNode* p1)
{
/*如果原始链表上的节点N的random指向S,则对应的复制节点N'的random指向S的下一个节点S'*/
if(p1 == nullptr)
return ;
RandomListNode* p = p1;
RandomListNode* clone = p->next;
while(p != nullptr)
{
if(p->random != nullptr)
{
clone->random= p->random->next ;
}
p = p->next->next;
clone = p->next;
}
}
RandomListNode* Clone3(RandomListNode* p1)
{
/*把得到的链表拆成两个链表,奇数位置上的结点组成原始链表,偶数位置上的结点组成复制出来的链表*/
if(p1 == nullptr)
return p1;
RandomListNode* phead,*pclone;
phead = pclone = p1->next;
p1->next = pclone->next;
p1 = pclone->next;
while(p1 != nullptr)
{
pclone->next = p1->next;
pclone = pclone->next;
p1->next = pclone->next;
p1 = p1->next;
}
return phead;
}
RandomListNode* Clone(RandomListNode* pHead)
{
Clone1(pHead);
Clone2(pHead);
return Clone3(pHead);
}
};
15.输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
思路分析:可以用递归法,列三个参数,字符串,vector,固定的字符的下标,通过循环将固定的字符和后面的每个字符交换,当交换到最后一位时,插入vector中,在插入前用find函数检查是否有重复的字符串,最后的结果再用vector的sort函数排序。
class Solution {
public:
vector<string> Permutation(string str) {
vector<string> beg;
if(str.empty())
{
return beg;
}
_Permutation(str,beg,0);
sort(beg.begin(),beg.end());
return beg;
}
void _Permutation(string str,vector<string>& beg,int num)
{
if(num == str.size()-1)
{
if(find(beg.begin(),beg.end(),str) == beg.end())
{
beg.push_back(str);
}
}
else
{
for(int i=num;i<str.size();i++)
{
my_swap(str[num],str[i]);
_Permutation(str,beg,num+1);
my_swap(str[num],str[i]);
}
}
}
void my_swap(char &a,char &b)
{
char c;
c =a;
a = b;
b = c;
}
};
16.给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
思路:当小于2时,无法分割,为2时,最大值为1,为3时最大值为2,大于3后 4 = 22
5= 23
6=222
可以看到所有的分割段都与2和3有关,只要计算能分割多少2和3,且尽量分割2的情况下,计算乘积合就是最大值
class Solution {
public:
int cutRope(int number)
{
if(number < 2)
return 0;
if(number == 2)
return 1;
if(number == 3)
return 2;
int num = number/3;
if(number - 3*num == 1)
num--;
int num2 = (number- 3*num)/2;
return pow(2,num2)*pow(3,num);
}
};
17.写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路:两个数相加,可以考虑为二进制下的运行,分为三步
- 在不计算进位的情况下,计算每个位相加的结果
- 计算两个数的进位结果,如果为0,就结束,否则重复1.
- 输出结果
以10进制为例 9 + 8 个位为7 十分位为1 ,在计算10+7结果十位为1 个位为7,进位为0结束运算。
class Solution {
public:
int Add(int num1, int num2)
{
int tem = num1;
while(num2 != 0)
{
tem = num1^num2;
num2 = (num1&num2)<<1;
num1 = tem;
}
return tem;
}
};
18.请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路:可以用递归解这道题,虽然题中为一个矩形,但是给出的是一个
指针,所以需要自己控制边界条件,再进行上下左右的判断,输出结果。
class Solution {
public:
bool hasPath(char* matrix, int rows, int cols, char* str)
{
if(str==NULL||rows<=0||cols<=0)
return false;
bool *isOk=new bool[rows*cols]();
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
if(isHsaPath(matrix,rows,cols,str,isOk,i,j))
return true;
}
return false;
}
bool isHsaPath(char *matrix,int rows,int cols,char *str,bool *isOk,int curx,int cury)
{
if(*str=='\0')
return true;
if(cury==cols)
{
curx++;
cury=0;
}
if(cury==-1)
{
curx--;
cury=cols-1;
}
if(curx<0||curx>=rows)
return false;
if(isOk[curx*cols+cury]||*str!=matrix[curx*cols+cury])
return false;
isOk[curx*cols+cury]=true;
bool sign=isHsaPath(matrix,rows,cols,str+1,isOk,curx-1,cury)
||isHsaPath(matrix,rows,cols,str+1,isOk,curx+1,cury)
||isHsaPath(matrix,rows,cols,str+1,isOk,curx,cury-1)
||isHsaPath(matrix,rows,cols,str+1,isOk,curx,cury+1);
isOk[curx*cols+cury]=false;
return sign;
}
};
- 地上有一个m行和n列的方格。一个机器人从座标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行座标和列座标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思路分析:开始想使用二维数组,发现禁止用变量来当下标,使用一维数组,确定边界条件,用数组的false和true确定是否走过当前格子,如果格子没走过合,返回 1+上下左右的返回值。
class Solution {
public:
int movingCount(int threshold, int rows, int cols)
{
if(rows == 0 || cols == 0 || threshold == 0)
{
return 0;
}
bool* m = new bool[rows * cols];
for(int i=0;i<rows*cols;i++)
{
m[i] = false;
}
return help(0,0,threshold,rows,cols,m);
}
int help(int i,int j,int threshold, int rows, int cols,bool* m)
{
if(i<0||j<0||i>=rows||j>=cols||m[i*cols+j] == true||numSum(i)+numSum(j) > threshold)
{
return 0;
}
m[i*cols+j] = true;
return (1+help(i+1,j,threshold,rows,cols,m)+help(i,j+1,threshold,rows,cols,m)+help(i-1,j,threshold,rows,cols,m)+help(i,j-1,threshold,rows,cols,m));
}
int numSum(int i)
{
int index = 0;
while(i >0)
{
index += i %10;
i /= 10;
}
return index;
}
};
- 请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"aba"均不匹配
思路:首先将传入的参数分情况讨论,
str为空 pattern不为空 不确定
,str为空 pattern为空 真
str为空 pattern不为空 假
然后判因为’表示它前面的字符可以出现任意次,所以再分情况讨论一个字符后面是否有*,当没有时比较简单,只要看当前字符是否和str的对应位置的字符相同或者为‘.’返回true否则返回false,当有*时,有两种情况,当当前字符和str的对应位置的字符值相同,分别对跳过当前字符和 ** 或者跳过str的字符,判断返回值,如果当前字符和str的对应位置的字符值不相同,只要判断跳过当前字符的值后的字符串是否和str匹配就行了,用递归实现。
class Solution {
public:
bool match(char* str, char* pattern)
{
if(*str=='\0' && *pattern == '\0')
return true;
if(*str !='\0' && *pattern == '\0')
return false;
char * str1 = str;
char * pat1 = pattern;
if(*(pat1+1) != '*')
{
if(*pat1 == *str1 || *pat1 == '.' && *str != '\0')
return match(str1+1,pat1+1);
else
return false;
}
else
{
if(*pat1 == *str1 || *pat1 == '.' && *str != '\0')
return match(str+1,pattern) || match(str,pattern+2);
else
return match(str,pattern+2);
}
}
};