# 剑指offer

【声明：文章大部分来自牛客网的题解（小部分是自己的解法）】

## 斐波那契数列

1. 使用递归
class Solution {
public:
int Fibonacci(int n) {
if(0 == n || 1 == n)
return n;
return Fibonacci(n-1)+Fibonacci(n-2);
}
};


1. 使用记忆法
class Solution {
public:
int Fib(int n, vector<int>& dp) {
if (n==0 || n==1) return n;
if (dp[n] != -1) return dp[n];
return dp[n] = Fib(n-1,dp) + Fib(n-2,dp);
}
int Fibonacci(int n) {
vector<int> dp(45, -1); // 因为答案都是>=0 的， 所以初始为-1，表示没计算过
return Fib(n, dp);
}
};


1. 动态规划

int Fibonacci(int n) {
vector<int> dp(n+1, 0);
dp[1] = 1;
for (int i=2; i<=n; ++i) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}


###继续优化

int Fibonacci(int n) {
if (n == 0 || n == 1) return n;
int a = 0, b = 1, c;
for (int i=2; i<=n; ++i) {
c = a + b;
a = b;
b = c;
}
return c;
}


## 跳台阶

public class Solution {
public int JumpFloor(int n) {
if (n == 1) return 1;
if (n == 2) return 2;
return JumpFloor(n - 1) + JumpFloor(n - 2);
}
}


public class Solution {
public int JumpFloor(int target) {
int a = 1, b = 1;
for (int i = 1; i < target; i++) {
a = a + b;
b = a - b;
}
return a;
}
}


## 变态跳台阶

f(0) = 1 : 表示从地上(0级台阶)直接跳到n级台阶
1级台阶 —> f(1) = f(0) = 1
2级台阶 —> f(2) = f(1)+f(0) = 2
3级台阶 —> f(3) = f(2)+f(1)+f(0) = 4
4级台阶 —> f(4) = f(3) + f(2)+f(1)+f(0) = 8

n-1级台阶 —> f(n-1) = f(n-2)+…+f(2)+f(1)+f(0)
n级台阶 —> f(n) = f(n-1)+f(n-2)+…+f(2)+f(1)+f(0)

f ( n ) = { 1 n = 0 1 n = 1 2 n = 2 f ( n − 1 ) + f ( n − 2 ) + . . . + f ( 2 ) + f ( 1 ) + f ( 0 ) o t h e r f(n)=\begin{cases} 1 & n=0 \\ 1 & n=1 \\ 2 & n=2 \\ f(n-1)+f(n-2)+...+f(2)+f(1)+f(0) & other \end{cases}

//使用记忆法
class Solution {
public:
int Fib(vector<int>& dp, int n){
if(0 == n) return 1;
if(1 == n) return 1;
if(dp[n] != -1) return dp[n];
// 求和
int sum = 0;
for(int i=0; i<n; ++i){
sum += Fib(dp, i);
}
dp[n] = sum;

return dp[n];
}
int jumpFloorII(int number) {
vector<int> res(number+1,-1);// 分配 0 ~ n 个位置
return Fib(res, number);
}
};


class Solution {
vector<int> m_dp;//记忆 f(0),f(1),f(2),...
public:
int Fib(int n){
if(0 == n) return 1;
if(1 == n) return 1;
if(m_dp[n] != -1) return m_dp[n];

int sum = 0;
for(int i=0; i<n; ++i){
sum += Fib(i);
}
m_dp[n] = sum;

return m_dp[n];
}
int jumpFloorII(int number) {
m_dp.assign(number+1,-1);// 分配 0 ~ n 个位置
return Fib(number);
}
};


–》推荐的

    int jumpFloorII(int n) {
vector<int> dp(n+1, 0);
dp[0] = 1;
dp[1] = 1;
for (int i=2; i<=n; ++i) {
int sum=0;
for(int j=0;j<i;++j){
sum += dp[j];
}
dp[i] = sum;
}
return dp[n];
}


class Solution {
public:
int jumpFloorII(int n) {
if(0==n || 1==n)
return 1;
int a = 1;
int tmp;
for(int i=0; i<n-1; ++i){
tmp = a << 1;//  口诀：左移乘2，右移除2
a = tmp;
}
// 即 2^(n-1)    等价 pow(2, n-1)
return tmp;
}
};

///
int jumpFloorII(int n) {
if(0==n || 1==n)
return 1;
return static_cast<int>(pow(2, n-1));
}


## 矩形覆盖

题目描述



涂掉最后一级矩形的时候，是用什么方式完成的？

n = 1 的时候
只能横着覆盖，一种
n = 2 的时候
可以横着和竖着覆盖，两种
n = 3 的时候
第三级横着覆盖，用了一级，剩下 n = 2，有两种覆盖方法
第三季竖着覆盖，用了两级，剩下 n = 1，有一种覆盖方法
总共有 3 种
n = 4 的时候
第 4 级横着覆盖，用了一级，剩下 n = 3，有三种覆盖方法
第 4 级竖着覆盖，用了两级，剩下 n = 2，有两种覆盖方法
总共有 5 种方法
n = n 的时候
第 n 级横着覆盖，用了一级，剩下 n = n - 1，所以关注第 n - 1 种有几种覆盖方法
第 n 级竖着覆盖，用了两级，剩下 n = n - 2，所以关注第 n - 2 种有几种覆盖方法
总和为两种情况的总和


    int rectCover(int number) {
if(0==number) return 0;
if(1==number) return 1;
if(2==number) return 2;
int a=1,b=2;
for(int i=3; i<=number; ++i){
b = a+b;
a = b-a;
}
return b;
}


## 二进制中1的个数

int  NumberOf1(int n) {
int count=0;
int key = 0x01;
while(key != 0 ){
if(n & key){
count++;
}
key <<= 1;
}
return count;
}


     int  NumberOf1(int n) {
int ret=0;
while(n){
++ret;
n = n&(n-1);
}
return ret;
}


## 数值的整次方

class Solution {
public:
double Power(double b, int n) {
if (n < 0) {
b = 1 / b;
n = -n;
}
double ret = 1.0;
for (int i=0; i<n; ++i) ret *= b;
return ret;
}
};


class Solution {
public:
double q_power(double b, int n) {
if (n == 0) return 1.0;
double ret = q_power(b, n/2);
if (n&1) { // 奇数
return ret * ret * b;
}
else {
return ret * ret;
}
}
double Power(double b, int n) {
if (n < 0) {
b = 1 / b;
n = -n;
}
return q_power(b, n);
}
};


class Solution {
public:
double Power(double b, int n) {
if (n < 0) {
b = 1 / b;
n = -n;
}
double x = b; // 记录x^0, x^1, x^2 ...
double ret = 1.0;
while (n) {
if (n&1) {
ret *= x; // 二进制位数是1的，乘进答案。
}
x *= x;
n >>= 1;
}
return ret;
}
};


## 调整数组顺序使奇数位于偶数前面

class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int> v1,v2;
for(size_t i=0;i<array.size();++i){
if(1 == array[i] % 2)//奇数
{
v1.push_back(array[i]);
}
else{
v2.push_back(array[i]);
}
}
v1.insert(v1.end(), v2.begin(),v2.end());
array = v1;
}
};


class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int> arr;
for (const int v : array) {
if (v&1) arr.push_back(v); // 奇数
}
for (const int v : array) {
if (!(v&1)) arr.push_back(v); // 偶数
}
copy(arr.begin(), arr.end(), array.begin());
}
};


j 表示数组的下标，初始值为0， 表示从下标0开始遍历。

1. 如果遇到偶数，j++
2. 如果遇到奇数,假设位置为j，就将此奇数插入到i所指的位置，然后i往后移动一个位置，在插入之前，显然会涉及到数据的移动，也就是将[i,j-1]整体往后移动。
3. 直到整个数组遍历完毕，结束
class Solution {
public:
void reOrderArray(vector<int> &array) {
int i = 0;
for (int j=0; j<array.size(); ++j) {
if (array[j]&1) {
int tmp = array[j];
for (int k=j-1; k>=i; --k) {
array[k+1] = array[k];
}
array[i++] = tmp;
}
}
}
};



template< class BidirIt, class UnaryPredicate > BidirIt stable_partition( BidirIt first, BidirIt last, UnaryPredicate p );


class Solution {
public:
void reOrderArray(vector<int> &array) {
stable_partition(array.begin(), array.end(), [](int x) {return x&1;} );
}
};


## 链表中倒数第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(NULL == pListHead || k <= 0) return nullptr;
while(k--){
if(q)
q = q->next;
else
return nullptr;
}
while(q){
p = p->next;
q = q->next;
}
return p;
}
};


## 翻转链表

/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* cur = pre->next;
ListNode* nex;
while(cur){
nex = cur->next;
cur->next = pre;
pre = cur;
cur = nex;
}
}
};


ListNode* ReverseList(ListNode* pHead) {
vector<ListNode*> v;
}
reverse(v.begin(), v.end()); // 反转vector，也可以逆向遍历
for (int i=1; i<v.size(); ++i) { // 构造链表
cur->next = v[i]; // 当前节点的下一个指针指向下一个节点
cur = cur->next; // 当前节点后移
}
cur->next = nullptr; // 切记最后一个节点的下一个指针指向nullptr
}


1）pre指针指向已经反转好的链表的最后一个节点，最开始没有反转，所以指向nullptr
3）nex指针指向待反转链表的第二个节点，目的是保存链表，因为cur改变指向后，后面的链表则失效了，所以需要保存

1）nex = cur->next, 保存作用
2）cur->next = pre 未反转链表的第一个节点的下个指针指向已反转链表的最后一个节点
3）pre = cur， cur = nex; 指针后移，操作下一个未反转链表的第一个节点

ListNode* ReverseList(ListNode* pHead) {
ListNode *pre = nullptr;
ListNode *nex = nullptr; // 这里可以指向nullptr，循环里面要重新指向
while (cur) {
nex = cur->next;
cur->next = pre;
pre = cur;
cur = nex;
}
return pre;
}


## 合并两个有序的链表

/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
{
ListNode *h1,*h2,*h3,*ret;
ret = h3 = NULL;
if(NULL == h1  &&  NULL == h2)
return NULL;
if(NULL == h1)
return h2;
if(NULL == h2)
return h1;
if(h1->val <= h2->val){
ret = h3 = h1;
h1 = h1->next;
}
else{
ret = h3 = h2;
h2 = h2->next;
}
while(h1 && h2){
if(h1->val <= h2->val){
h3->next = h1;
h3 = h3->next;
h1 = h1->next;
}
else{
h3->next = h2;
h3 = h3->next;
h2 = h2->next;
}
}
while(h1){
h3->next = h1;
h3 = h3->next;
h1 = h1->next;
}
while(h2){
h3->next = h2;
h3 = h3->next;
h2 = h2->next;
}
h3 = NULL;
return ret;
}
};
//或者
{
ListNode *h1,*h2,*h3,*ret;
ret = h3 = NULL;
if(NULL == h1  &&  NULL == h2)
return NULL;
if(NULL == h1)
return h2;
if(NULL == h2)
return h1;
if(h1->val <= h2->val){
ret = h3 = h1;
h1 = h1->next;
}
else{
ret = h3 = h2;
h2 = h2->next;
}
while(h1 && h2){
if(h1->val <= h2->val){
h3->next = h1;
h3 = h3->next;
h1 = h1->next;
}
else{
h3->next = h2;
h3 = h3->next;
h2 = h2->next;
}
}
if(h1){
h3->next = h1;
}
if(h2){
h3->next = h2;
}
return ret;
}


public ListNode Merge(ListNode list1, ListNode list2) {

while (list1 != null && list2 != null) {
if (list1.val > list2.val) {
movnode.next = list2;
list2 = list2.next;
} else {
movnode.next = list1;
list1 = list1.next;
}
movnode = movnode.next;
}
if (list1 != null) {
movnode.next = list1;
}
if (list2 != null) {
movnode.next = list2;
}
}


## 二维数组中的查找

1）设初始值为右上角元素，val = arr[0][5] ，目标tar = arr[3][1]
2）接下来进行二分操作：
3）如果val == target,直接返回
4）如果 tar > val, 说明target在更大的位置，val左边的元素显然都是 < val，间接 < tar，说明第 0 行都是无效的，所以val下移到arr[1][5]
5）如果 tar < val, 说明target在更小的位置，val下边的元素显然都是 > val，间接 > tar，说明第 5 列都是无效的，所以val左移到arr[0][4]
6）继续步骤2）

    bool Find(int target, vector<vector<int> > array) {
const int rows = array.size();
const int cols = array[0].size();
if(0==rows || 0==cols) return false;
int r,c;
r=0; c=cols-1;
while(r<rows && c>=0){
if(array[r][c] == target)
return true;
else if(array[r][c] < target)
++r;
else
--c;
}
return false;
}


## 树的子结构

/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
bool dfs(TreeNode *r1, TreeNode *r2) {
if (!r2) return true;
if (!r1) return false;
return r1->val==r2->val && dfs(r1->left, r2->left) && dfs(r1->right, r2->right);
}

public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(NULL == pRoot1 || NULL == pRoot2)
return false;
return dfs(pRoot1,pRoot2) || HasSubtree(pRoot1->left, pRoot2) ||
HasSubtree(pRoot1->right, pRoot2);
}
};


## 二叉树的镜像

输入描述:

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(NULL == pRoot) return;
TreeNode *tmp = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = tmp;
Mirror(pRoot->left);
Mirror(pRoot->right);
}
};


• 后序遍历的模板：
void postOrder(TreeNode *root) {
if (!root) return;
postOrder(root->left); // left child
postOrder(root->right); // right child
// process root
}

class Solution {
public:
TreeNode* swapLR(TreeNode* r){
if(nullptr == r) return nullptr;
TreeNode* pLeftNode = swapLR(r->left);
TreeNode* pRightNode = swapLR(r->right);
r->left = pRightNode;
r->right = pLeftNode;
return r;
}
void Mirror(TreeNode *pRoot) {
if(NULL == pRoot) return;
swapLR(pRoot);
}
};


void bfs(TreeNode *root) {
queue<TreeNode*> pq;
pq.push(root);
while (!pq.empty()) {
int sz = pq.size();
while (sz--) {
TreeNode *node = pq.front(); pq.pop();
// push value to queue
if (node->left) pq.push(node->left);
if (node->right) pq.push(node->right);

} // end inner while
} // end outer while
}


class Solution {
public:
void Mirror(TreeNode *pRoot)
{
//传入头结点为空直接返回
if(!pRoot) return ;
queue<TreeNode *> que;
//在while操作之前把头结点加入队列,否则队列为空直接结束
que.push(pRoot);

while(!que.empty())
{
//获取每一层的结点数,即会对这一层的queSize个结点都执行交换操作
int queSize = que.size();
//处理完一个结点后,计数-1
while(queSize--)
{
//取得当前队列第一个元素(并非出队)
TreeNode *temp = que.front();
//出队
que.pop();

//先交换,再把其左右结点入队
//定义临时的TreeNode类型指针,用于交换左右结点
TreeNode *swap;
swap = temp->left;
temp->left = temp->right;
temp->right = swap;

//如果左(或右)结点不为空,就加入队列
if(temp->left) {que.push(temp->left);}
if(temp->right) {que.push(temp->right);}
}//继续对这一层的下一个结点进行操作
}//队列不空时,获取下一层结点的个数,并会执行相应次数的交换与入队操作
}
};


## 顺时针打印矩阵

[[1,2],[3,4]]

[1,2,4,3]

vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> ret;
if (matrix.empty()) return ret;
int row = matrix.size();
int col = matrix[0].size();

int up = 0, down = row - 1, left = 0, right = col - 1;
while (true){
for (int j = left; j <= right; ++j)// 最上面一行
ret.push_back(matrix[up][j]);
++up;
if (up>down)
break;
for (int i = up; i <= down; ++i)
ret.push_back(matrix[i][right]);// 最右边一列
--right;
if (left>right)
break;
for (int j = right; j >= left; --j)// 最下面一行
ret.push_back(matrix[down][j]);
--down;
if (up>down)
break;
for (int i = down; i >= up; --i)// 最左边一列
ret.push_back(matrix[i][left]);
++left;
if (left>right)
break;
}
return ret;
}



## 包括min函数的栈

class Solution {
public:
stack<int> normal, minval;
void push(int value) {
normal.push(value);
if (minval.empty()) {
minval.push(value);
}
else {
if (value <= minval.top()) {
minval.push(value);
}
else {
minval.push(minval.top());
}
}
}
void pop() {
normal.pop();
minval.pop();
}
int top() {
return normal.top();
}
int min() {
return minval.top();
}
};


## 栈的压人、弹出序列

bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.size()==0 || popV.size()==0 || pushV.size()!=popV.size()){
return false;
}
stack<int> st;
int i=0, j=0;
while(i<pushV.size()){
if(pushV[i] != popV[j])
st.push(pushV[i++]);
else{
++i,++j;
while(!st.empty() && st.top()==popV[j]){
st.pop();
++j;
}
}
}
return st.empty();
}


## 层次遍历二叉树

/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> vRet;
int deep = 0; //深度
if(nullptr == root) return vRet;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int sz = q.size();
while(sz--){
TreeNode* node = q.front(); q.pop();
vRet.push_back(node->val);
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
++deep;
}
return vRet;
}
};


06-01

09-17 8万+
02-03 4万+
05-28
08-06
06-02 2973
03-24
09-11
10-23
08-16 5万+
11-02 5万+
03-08
01-18