文章目录
11、二进制中1的个数
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
一般拿到这种问题,我们首先想到的算法就是判断最低位是不是1,也就是将这个数与1做与运算,看是否是1,如果是1,个数+1,然后这个数右移一位,进行循环直到这个数为0,但是这种方法在遇到负数的情况下,很容易造成死循环的问题,因此我们要考虑另一种算法,也就是我们不将这个数右移,而是每一次将1左移一位,直到1变成了0这样就可以判断这个数的最低位、次低位、次次低位等是不是1,但是这种算法比较常规,我们还可以再想想第三种算法,即:
我们拿到这个数,给它-1,如果它的最低位是1,那么此时变为0,此时这个数和-1之后的进行与操作,count++,然后继续此过程直到这个数为0,就可以得到它的二进制有多少个1;
如果这个数的第n位是1,右边的位都是0,那么-1之后第n位变为0,第n位右边的位全部变为1,左边的不变,这样我们把这个整数和它-1之后的数进行与运算,-1之后的数右边的就可以全部变为0,循环直到这个数为0,就可以得到这个数二进制1的个数。
实现代码(第二种算法):
class Solution {
public:
int NumberOf1(int n) {
int count=0;
unsigned int flag=1;
while(flag)
{
if(n&flag)
count++;
flag = flag<<1;
}
return count;
}
};
第三种算法:
class Solution {
public:
int NumberOf1(int n) {
int count=0;
while(n)
{
count++;
n=n&(n-1);
}
return count;
}
};
12、数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0.
此时我们如果题目已经设置了exponent是无符号整数,此时我们可以采用递归的方式,即例如exponent是32时,只需要知道它的16次方,再平方一次就可以了;而要知道16次方,只需要知道8次方,再平方一次就可以了;而要知道8次方,只需要知道4次方,再平方一次就可以了;要知道4次方,只需要知道2次方,再平方一次就可以了;要知道2次方,只需要知道1次方,再平方一次就可以了,如果exponent是奇数,最后只需要再乘一遍base即可;那么此时它的代码就是:
double Power(double base, unsigned int exponent)
{
if(exponent==0)
return 1;
if(exponent==1)
return base;
double sum=Power(base,exponent>>1);
sum*=sum;
if(exponent & 0x1 ==1)//是奇数
sum*=base;
return sum;
}
但是我们要注意此题给的是exponet是int类型的整数,也就是有符号整数,那么就要考虑负数的情况,就不能使用递归的方式了,此时我们需要判断exponent如果是负数,最终的sum=1/sum;
实现代码:
class Solution {
public:
double Power(double base, int exponent) {
if(base == 0)
return 0;
if(exponent == 0)
return 1;
if(exponent == 1)
return base;
int e=exponent;
if(exponent < 0)
{
e = -exponent;
}
//e为exponent的绝对值
double sum=1;
while(e!=0)
{
sum*=base;
e--;
}
if(exponent < 0)
sum=1/sum;
return sum;
}
};
13、调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
这个题拿到的第一想法就是进行循环遍历,如果遇到当前元素是偶数,下一个元素是奇数,就交换,一共遍历arr.size()次,并且这种方法也保证了相对位置不变,实现代码:
class Solution {
public:
void reOrderArray(vector<int> &array) {
int len=array.size();
for(int i=0;i<len;++i)
{
for(int j=0;j<len-1;++j)
{
if(array[j]%2==0&&array[j+1]%2!=0)
{
int tmp=array[j];
array[j]=array[j+1];
array[j+1]=tmp;
}
}
}
}
};
如果题目没有要求相对位置不变的话,我们可以采用begin指针从前往后遍历数组找到第一个偶数,同样采用end指针从后往前遍历找到第一个奇数,如果begin<end,交换,然后继续循环直到begin>=end即可,实现代码:
void reOrderArray(vector<int> &array)
{
int begin=0;
int end=array.size()-1;
while(begin<end)
{
while(begin<end && (array[begin] & 0x1!=0))//找到第一个不是奇数的
begin++;
while(begin<end && (array[end] & 0x1 == 0))//找到第一个不是偶数的
end--;
if(begin<end)
{
int t=array[begin];
array[begin]=array[end];
array[end]=t;
}
}
}
我们这里要扩展的是,如果我们遇到此类问题,比如所有负数都在非负数的前面,只需要第二个和第三个while中判断条件,因此我们可以将其写成一个函数,利用函数指针的方式即可。
14、链表中倒数第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(pListHead==nullptr)
return nullptr;
ListNode *front=pListHead;
ListNode *back=pListHead;
for(int i=0;i<k;++i)
{
if(front==nullptr)
return nullptr;
front=front->next;
}
while(front!=nullptr)
{
front=front->next;
back=back->next;
}
return back;
}
};
15、反转链表
输入一个链表,反转链表后,输出新链表的表头。
我们的思想就是先保存pHead后所有的结点,然后将pHead->next置为nullptr,再将保存的结点逐个头插到pHead之前即可。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == nullptr)
return nullptr;
ListNode *p= pHead->next;
pHead->next=nullptr;
while(p)
{
ListNode *next=p->next;
p->next=pHead;
pHead=p;
p=next;
}
return pHead;
}
};
16、合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
有两种方法,第一种就是定义一个结果链表,分别遍历两个链表,每次将两者较小的尾插到结果链表中(记得要考虑两个链表不一样长的情况,最后要将长的链表剩下的结点链接到结果链表后),实现代码:
/*
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* result=nullptr;
ListNode* c1=pHead1;
ListNode* c2=pHead2;
ListNode* last=nullptr;
while(c1!=nullptr && c2!=nullptr)
{
if(c1->val<=c2->val)
{
ListNode* next=c1->next;
c1->next=nullptr;//将c1拆下来
if(result == nullptr)
result=c1;
else
last->next=c1;
last=c1;
c1=next;
}
else
{
ListNode* next=c2->next;
c2->next=nullptr;//将c2拆下来
if(result==nullptr)
result=c2;
else
last->next=c2;
last=c2;
c2=next;
}
}
if(c1!=nullptr)
last->next=c1;
if(c2!=nullptr)
last->next=c2;
return result;
}
};
当然第二种方法就是使用递归的方式,如果pHead1小于pHead2,那么此时result=pHead1,result的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* result=nullptr;
if(pHead1->val<=pHead2->val)
{
result=pHead1;
result->next=Merge(pHead1->next,pHead2);
}
else
{
result=pHead2;
result->next=Merge(pHead1,pHead2->next);
}
return result;
}
};
17、树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
判断B是否是A的子结构,首先判断两者的根结点的值是否相同,如果相同,则继续判断两者是否具有相同的结构,如果相同,返回true,如果两者根结点的值不相同,则要在A的左子树中递归,找是否有值和B的根结点值相同的,看是否结构相同,否则去A的右子树中递归
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
private:
bool isSame(TreeNode *p,TreeNode *q)//判断以p为根结点与以q为根结点的二叉树是否相同
{
if(q==nullptr)
return true;
if(p==nullptr)
return false;
if(p->val!=q->val)
return false;
return isSame(p->left,q->left) && isSame(p->right,q->right);
}
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(pRoot1==nullptr || pRoot2==nullptr)
return false;
if(pRoot1->val==pRoot2->val)//如果两个根结点的值相同,则判断这两个代表的二叉树是否相同,相同返回true
{
if(isSame(pRoot1,pRoot2))
return true;
}
//如果两个根结点不相同,继续去pRoot1的左子树中找是否相同,找不到去右子树中找
return HasSubtree(pRoot1->left,pRoot2) || HasSubtree(pRoot1->right,pRoot2);
}
};
18、二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
要得到二叉树的镜像,交换当前根结点的左右孩子,然后左右子树继续递归即可
实现代码:
/*
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==nullptr)
return;
TreeNode *t = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = t;
Mirror(pRoot->left);
Mirror(pRoot->right);
}
};
19、顺时针打印矩阵
即例如:
我们假设start表示左上角的坐标,result为最终结果数组,row为矩阵行数,col为矩阵列数
首先我们要知道如果想要从左到右走(第一步),不需要条件就可以,即如图这种:
此时只需要实现代码:
for(int i=start;i<=col-1-start;++i)
{
result.push_back(matrix[start][i]);
}
如果我们要从上往下走(第二步),就要保证有的走,即最少要再有一行,即一共两行,也就是条件是终止行数>起始行数,此时第start行的第col-1-start列的数已经走过,向下走就不用再走一遍,循环从start+1开始,如图:
实现代码:
if(start<row-1-start)
{
for(int i=start+1;i<=row-1-start;++i)
{
result.push_back(matrix[i][col-1-start]);
}
}
如果我们要从右往左走(第三步),也要保证有的走,即最少要再有一列,即一共两列,条件就是在第二步的终止行数大于终止列数前提下,终止列数大于起始列数,此时第col-1-start列的第row-1-start这一行的数第二步已经走过,这里就不用走了,也就是循环从col-1-1-start开始,如图:
实现代码:
if(start<row-1-start && start<col-1-start)
{
for(int i=col-1-1-start;i>=start;--i)
{
result.push_back(matrix[row-1-start][i]);
}
}
我们要从下往上走(第四步),也要保证有的走,即要再有一行可以走,也就是要在第三步的前提下,再有一行,即三行两列,也就是终止行数要比起始行数至少大于2,终止列数大于起始列数,第三步已经将第row-1-start行的第start列的那个数走过来和第一步已经将第start行的第start列那个数走过了,因此循环从row-1-1-start开始,终止于start+1,如图为要满足四步所得,即三行两列:
第四步的代码为:
if(start<row-1-1-start && start<col-1-start)
{
for(int i=row-1-1-start;i>=start+1;--i)
{
result.push_back(matrix[i][start]);
}
}
最后就是我们的循环条件,我们可以得出,走最后一圈的左上角的坐标为(2,2),也就是循环条件为row>start2和col>start2,表示走了start圈,总体实现代码:
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> result;
int row=matrix.size();
int col=matrix[0].size();
if(matrix.size() == 0 || row==0 || col==0)
return result;
int start=0;
while(row > start*2 && col > start*2)
{
//第一步
for(int i=start;i<=col-1-start;++i)
{
result.push_back(matrix[start][i]);
}
//第二步
if(start<row-1-start)
{
for(int i=start+1;i<=row-1-start;++i)
{
result.push_back(matrix[i][col-1-start]);
}
}
//第三步
if(start<row-1-start && start<col-1-start)
{
for(int i=col-1-1-start;i>=start;--i)
{
result.push_back(matrix[row-1-start][i]);
}
}
//第四步
if(start<row-1-1-start && start<col-1-start)
{
for(int i=row-1-1-start;i>=start+1;--i)
{
result.push_back(matrix[i][start]);
}
}
start++;
}
return result;
}
};
20、包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法.
这里我们使用两个栈,in栈保存每一次压入的数,out的栈顶一定是最小的,也就是out如果是空的或者要压入的数小于out的栈顶元素,就压入out栈,否则out压入自己当前的栈顶元素。
实现代码:
class Solution {
private:
std::stack<int> in;
std::stack<int> out;
public:
void push(int value) {
in.push(value);
if(out.empty() || value<=out.top())
out.push(value);
else
out.push(out.top());
}
void pop() {
in.pop();
out.pop();
}
int top() {
return in.top();
}
int min() {
return out.top();
}
};