系列二:10~19题
【系列一】:传送门 【剑指offer系列】:所有系列传送门 【系列三】:传送门
目录
1、矩形覆盖【动态规划】
题目:矩形覆盖
题目描述
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
1.1、分析
1.2、代码
class Solution {
public:
int rectCover(int number) {
vector<int> f(number+1);
f[1]=1,f[2]=2; //当长度为2时,可以两个横着放,也可以两个竖着放,所以有两种方法
for(int i=3;i<=number;i++){
f[i]=f[i-1]+f[i-2];
}
return f[number];
}
};
2、二进制中1的个数【进制转化】【补码反码原码】
题目:原题链接
题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
2.1、分析
对于判断数值n的第i位是否为1方法是n>>i&1,所以我们只需要依次判断n的每一位是否为1,值得注意的是这里的n可能是负数,由于负数是用其补码表示的,我们1可以先对数据做一个转换就是把n转换成unsigned int类型,这个操作就是把原二进制表示进行了转换成表达式一样,但所表达的数值大小不一样了。
2.2、代码
class Solution {
public:
int NumberOf1(int n) {
int res=0;
unsigned int _n=n; //处理负数情况
while(_n){
if(_n&1) res++;
_n>>=1;
}
return res;
}
};
3、数值的整数次方【数学】
题目:原题链接
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
3.1、分析
我们只需要把base循环乘exponent次即可,值得注意的是当exponent为负数的时候,需要对结果求一个倒数即可。
3.2、代码
class Solution {
public:
double Power(double base, int exponent) {
int t=abs(exponent);
double res=1.0;
for(int i=0;i<t;i++){
res*=base;
}
return exponent<0?1.0/res:res;
}
};
4、调整数组顺序使奇数位于偶数前面【数组】
题目:原题链接
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
4.1、分析
由于题目需要保持奇数和奇数,偶数和偶数之间的相对位置不变,所以不能用两个指针分别从首尾向中间遍历,我们在这里用插入排序的思想,当当前数是奇数时,我们需要在这数之前找一个偶数与之交换。
4.2、代码
class Solution {
public:
void reOrderArray(vector<int> &array) {
for(int i=0;i<array.size();i++){
if(array[i]%2==1){
for(int j=i-1;j>=0;j--){
if(array[j]%2) break; //表示前面都是奇数
swap(array[j],array[j+1]); //相连交换,保证偶数顺序不变
}
}
}
}
};
5、链表中倒数第k个节点【链表】
题目:原题链接
题目描述
输入一个链表,输出该链表中倒数第k个结点。
5.1、分析
在这里我们可以用双指针,先用一个指针向后移动k步,再把另一个指针从头开始,两个指针同时向后走,因为两个指针距离就是k,所以当第一个指针1走到最后时,第二个指针所指的数就是倒数第k个数。
5.2、代码
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* head, unsigned int k) {
auto p=head;
while(p&&k){
p=p->next;
k--;
}
if(k) return NULL; //k可能大于链表长度
auto q=head;
while(p){
p=p->next;
q=q->next;
}
return q;
}
};
6、反转链表【链表】
题目:原题链接
题目描述
输入一个链表,反转链表后,输出新链表的表头。
6.1、分析
在这里用三个指针来遍历,a与b用于交换两两相连的节点。
6.2、代码
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(!pHead) return NULL;
auto a=pHead,b=pHead->next;
while(b){
auto c=b->next;
b->next=a;
a=b;
b=c;
}
pHead->next=NULL;
return a;
}
};
7、合并两个排序的链表【链表】
题目:原题链接
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
7.1、分析
我们分别遍历两个链表,把值更小的节点连接到最终的答案上,最后当有一个链表没遍历完时,直接把剩下的连接上即可。
7.2、代码
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
auto pre=new ListNode(-1);
auto res=pre;
while(pHead1&&pHead2){
if(pHead1->val<pHead2->val){
pre->next=pHead1;
pHead1=pHead1->next;
}
else{
pre->next=pHead2;
pHead2=pHead2->next;
}
pre=pre->next;
}
//当其中有一个链表没遍历完时
if(pHead1) pre->next=pHead1;
if(pHead2) pre->next=pHead2;
return res->next;
}
};
8、树的子结构【二叉树】
题目:原题链接
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
8.1、分析
我们先把一个二叉树序列化,假设序列化结果为:A:1,2,4,#,#,3,#,#,5,#,#,;B:2,4,#,#,3,#,#,我们再把B序列后面的#和,删掉,因为B树不一定是A树的一个子树,所以现在问题就成了在A序列中找一个长度等于B序列长度的一个相等连续子序列,若有则返回true,没有则返回false。
8.2、代码
/*
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(!pRoot2) return false;
string s1,s2;
dfs(pRoot1,s1);
dfs(pRoot2,s2);
while(s2.back()==','||s2.back()=='#') s2.pop_back();
for(int i=0;i<s1.size();i++){
string t=s1.substr(i,s2.size());
if(t==s2) return true;
}
return false;
}
//序列化二叉树
void dfs(TreeNode* root,string &s){
if(!root){
s+="#,";
return;
}
s+=to_string(root->val)+',';
dfs(root->left,s);
dfs(root->right,s);
}
};
9、二叉树的镜像
题目:原题链接
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
9.1、分析
我们可以递归来做这个题,当节点为空时,直接返回,否则就交换左右节点,再递归处理左子树和右子树。
9.2、代码
/*
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) return;
swap(pRoot->left,pRoot->right);
Mirror(pRoot->left);
Mirror(pRoot->right);
}
};
10、顺时针打印矩阵【数组】
题目:原题链接
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 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.
10.1、分析
我们在这里定义两个方向数组,即每次遍历的方向,然后按照方向依次遍历,当前面没有数字且没出界时,就继续按照当前方向继续遍历,当不满足条件时则需要换方向了。
10.2、代码
class Solution {
public:
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1}; //方向数组
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> res;
if(matrix.empty()||matrix[0].empty()) return res;
int n=matrix.size(),m=matrix[0].size();
vector<vector<bool>> st(n,vector<bool>(m));
int x=0,y=0,d=1;
for(int i=0;i<m*n;i++){
res.push_back(matrix[x][y]);
st[x][y]=true; //用于记录当前格子已经填了数字
int nexx=x+dx[d],nexy=y+dy[d];
if(nexx>=0&&nexx<n&&nexy>=0&&nexy<m&&!st[nexx][nexy]){}
else{ //不满足则换方向
d=(d+1)%4;
nexx=x+dx[d];
nexy=y+dy[d];
}
x=nexx,y=nexy;
}
return res;
}
};