文章目录
- 《剑指 offer》
- 剑指offer 001、二维数组中的查找
- 剑指offer 002、替换空格
- 剑指offer 003、从尾到头打印链表
- 剑指offer 004、重建二叉树
- 剑指offer 005、用两个栈实现队列
- 剑指offer 006、旋转数组的最小数字
- 剑指offer 007、斐波那契数列
- 剑指offer 008、跳台阶
- 剑指offer 009、跳台阶扩展问题
- 剑指offer 010、矩形覆盖
- 剑指offer 011、二进制中 1 的个数
- 剑指offer 012、数值的整数次方
- 剑指offer 013、调整数组顺序使奇数位于偶数前面
- 剑指offer 014、链表中倒数第k个结点
- 剑指offer 015、反转链表
- 剑指offer 016、合并两个排序的链表
- 剑指offer 017、树的子结构
- 剑指offer 018、二叉树的镜像
- 剑指offer 019、顺时针打印矩阵
- 剑指offer 020、包含min的栈
- 剑指offer 021、栈的压入、弹出序列
- 剑指offer 022、从上往下打印二叉树
- 剑指offer 023、二叉搜索树的后序遍历序列
- 剑指offer 024、二叉树中和为某一值的路径
- 剑指offer 025、复杂链表的复制
- 剑指offer 026、二叉搜索树与双向链表
- 剑指offer 027、字符串的排列
- 剑指offer 028、数组中出现次数超过一半的数字
- 剑指offer 029、最小的K个数
- 剑指offer 030、连续子数组的最大和
- 剑指offer 031、整数中1出现的次数(从1到n整数中1出现的次数)
- 剑指offer 032、把数组排成最小的数
- 剑指offer 033、丑数
- 剑指offer 034、第一个只出现一次的字符
- 剑指offer 035、数组中的逆序对
- 剑指offer 036、两个链表的第一个公共结点
- 剑指offer 037、数字在升序数组中出现的次数
- 剑指offer 038、二叉树的深度
- 剑指offer 039、平衡二叉树
- 剑指offer 040、数组中只出现一次的两个数字
- 剑指offer 041、和为S的连续正数序列
- 剑指offer 042、和为S的两个数字
- 剑指offer 043、左旋转字符串
- 剑指offer 044、翻转单词序列
- 剑指offer 045、扑克牌顺子
- 剑指offer 046、孩子们的游戏(圆圈中最后剩下的数)
- 剑指offer 047、求1+2+3+...+n
- 剑指offer 048、不能加减乘除做加法
- 剑指offer 049、把字符串转换成整数
- 剑指offer 050、数组中重复的数字
- 剑指offer 051、构建乘积数组
- 剑指offer 052、正则表达式匹配
- 剑指offer 053、表示数值的字符串
- 剑指offer 054、字符流中第一个不重复的字符
- 剑指offer 055、 链表中环的入口结点
- 剑指offer 056、删除链表中重复的结点
- 剑指offer 057、二叉树的下一结点
- 剑指offer 058、对称的二叉树
- 剑指offer 059、按之字形顺序打印二叉树
- 剑指offer 060、把二叉树打印成多行
- 剑指offer 061、序列化二叉树
- 剑指offer 062、二叉搜索树的第k个结点
- 剑指offer 063、数据流中的中位数
- 剑指offer 064、滑动窗口的最大值
- 剑指offer 065、矩阵中的路径
- 剑指offer 066、机器人的运动范围
- 剑指offer 067、剪绳子
《剑指 offer》
剑指offer 001、二维数组中的查找
题目
题解1
先观察,从右上角逐渐左下角逼近
- 当前位置元素比target小,则 row++
- 当前位置元素比target大,则col–
- 若相等,返回true
- 若越界了还未找到,说明该数组不存在与target相等元素,返回false
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
if (array.empty() || array[0].empty()) {
return false;
}
int row = array[0].size() - 1, col = array.size() - 1;
int x = 0, y = row;
while (x <= col && y >= 0) {
if (array[x][y] < target) {
x++;
}
else if (array[x][y] > target) {
y--;
}
else {
return true;
}
}
return false;
}
};
题解2
用二分查找
class Solution {
public:
bool binaryFind(vector<int> &arr, int target) {
int low = 0, high = arr.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] < target)
low = mid + 1;
else if (arr[mid] > target)
high = mid - 1;
else
return true;
}
return false;
}
bool Find(int target, vector<vector<int> > array) {
if (array.empty() || array[0].empty()) {
return false;
}
int row = array.size() - 1;
for (int x = 0; x <= row; x++) {
if (binaryFind(array[x], target)) {
return true;
}
}
return false;
}
};
剑指offer 002、替换空格
题目
题解1
class Solution {
public:
string replaceSpace(string s) {
int spaceCount = 0; // 空格的数量
int length = s.size() - 1;
for (int i = 0; i <= length; ++i) {
if (s[i] == ' ') spaceCount++;
}
int newLength = length + spaceCount * 2; // 新字符串的长度
s.resize(newLength + 1);
for (int i = length; i >= 0; i--) {
if (s[i] != ' ') {
s[newLength--] = s[i];
}
else {
s[newLength--] = '0';
s[newLength--] = '2';
s[newLength--] = '%';
}
}
return s;
}
};
剑指offer 003、从尾到头打印链表
题目
题解1
直接用C++的reverse()函数 或 反向迭代器
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
if (!head) return vector<int>();
vector<int> result;
while (head) {
result.push_back(head->val);
head = head->next;
}
/*
reverse(result.begin(), result.end());
return result;
*/
return vector<int>(result.rbegin(), result.rend());
}
};
剑指offer 004、重建二叉树
题目
题解1
首先要熟练掌握二叉树先序遍历和中序遍历的原理。
先找pre中的其实元素为根节点,在vin中找到根节点的索引mid;那么pre中[1, mid + 1]为左子树,[mid + 1, end] 为右子树;vin中[0, mid]为左子树,[mid + 1, end] 为右子树
/**
* 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> vin) {
if (pre.empty() || vin.empty()) {
return NULL;
}
TreeNode* newTree = new TreeNode(pre[0]);
int mid = distance(vin.begin(), find(vin.begin(), vin.end(), pre[0]));
vector<int> left_vin(vin.begin(), vin.begin() + mid);
vector<int> right_vin(vin.begin() + mid + 1, vin.end());
vector<int> left_pre(pre.begin() + 1, pre.begin() + mid + 1);
vector<int> right_pre(pre.begin() + mid + 1, pre.end());
newTree->left = reConstructBinaryTree(left_pre, left_vin);
newTree->right = reConstructBinaryTree(right_pre, right_vin);
return newTree;
}
};
剑指offer 005、用两个栈实现队列
题目
题解
入队:将元素进栈stack1
出队:判断栈stack2是否为空,如果为空,则将栈stack1中所有元素pop,并push进栈stack2,最后栈stack2出栈
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if (stack2.empty()) {
while (!stack1.empty()) {
int x = stack1.top();
stack1.pop();
stack2.push(x);
}
}
int top = stack2.top();
stack2.pop();
return top;
}
private:
stack<int> stack1;
stack<int> stack2;
};
剑指offer 006、旋转数组的最小数字
题目
题解
直接使用二分法
如有疑惑,可以看看这个解析:(牛友FINACK)
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if (rotateArray.empty()) return 0;
int low = 0, high = rotateArray.size() - 1;
while (low < high) {
int mid = low + (high - low) / 2;
if (rotateArray[mid] < rotateArray[high]) {
high = mid;
}
else if (rotateArray[mid] == rotateArray[high]) {
high--;
}
else {
low = mid + 1;
}
}
return rotateArray[low];
}
};
剑指offer 007、斐波那契数列
题目
题解
注意,本题是从第0项开始,即数列项依次为:0 1 1 2 3 5 8…
class Solution {
public:
int Fibonacci(int n) {
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
int num1 = 0, num2 = 1;
for (int i = 2; i <= n; i++) {
num2 += num1;
num1 = num2 - num1;
}
return num2;
}
};
剑指offer 008、跳台阶
题目
题解
用动态规划的来解决这道题,跳到第n个台阶只有两种可能
- 从第n - 1个台阶跳1个台阶
- 从第n - 2个台阶跳2个台阶
因此我们只需求出跳到第n - 1个台阶和第n - 2个台阶的可能跳法。
递推公式:f(n) = f(n - 1) + f(n - 2);
f(0) = 1, f(1) = 1;
class Solution {
public:
int jumpFloor(int number) {
if (number == 1) {
return 1;
}
int num0 = 1, num1 = 1;
for (int i = 2; i <= number; ++i) {
num1 += num0;
num0 = num1 - num0;
}
return num1;
}
};
剑指offer 009、跳台阶扩展问题
题目
题解
很简单,跳到第n个台阶时有f(n) = f(n - 1) + f(n - 2) + … + f(1) = 2 * f(n - 1) 种方法。
// 递归的方法
class Solution {
public:
int jumpFloorII(int number) {
if (number == 1) {
return 1;
}
return 2 * jumpFloorII(number - 1);
}
};
// 非递归
class Solution {
public:
int jumpFloorII(int number) {
if (number == 1) {
return 1;
}
int count = 1;
for (int i = 2; i <= number; ++i) {
count *= 2;
}
return count;
}
};
// 直接用pow函数
class Solution {
public:
int jumpFloorII(int number) {
if (number == 1) {
return 1;
}
return pow(2, number - 1);
}
};
剑指offer 010、矩形覆盖
题目
题解
emmm太笨了,刚开始没读懂题,找了好半天的题解才明白题目是什么意思。
这道题的意思是我们要用 n 个 2*1的小矩阵 横着 / 竖着 去覆盖更大的矩形
规定:
那么,
- n = 1 时
- 只能横着覆盖,一种
- n = 2 时
- 横着覆盖,一种
- 竖着覆盖,一种
- 总共有2种
- n = 3 时
- 第三级横着覆盖,用了两块,剩下n = 1,有一种覆盖方法
- 第三级竖着覆盖,用了一块,剩下n = 2,有两种覆盖方法
- 总共有3种
- n = 4 时
- 第四级横着覆盖,用了两块,剩下n = 2,有两种覆盖方法
- 第四级竖着覆盖,用了一块,剩下n = 3,有三种覆盖方法
- 总共有5种
- … …
- n = n时
- 第n级横着覆盖,用了两块,剩下n = n - 2,关注n = n - 2时有多少种覆盖方法
- 第n级竖着覆盖,用了一块,剩下n = n - 1,关注n = n - 1时有多少种覆盖方法
- 总共有n = n - 2 ,n = n - 1 两种情况之和
class Solution {
public:
int rectCover(int number) {
if (number <= 2) {
return number;
}
int pre1 = 2; // n = n - 1时的覆盖方法
int pre2 = 1; // n = n - 2时的覆盖方法
int cur = 0; // 当前数量的覆盖方法
for (int i = 3; i <= number; ++i) {
cur = pre1 + pre2;
pre2 = pre1;
pre1 = cur;
}
return cur;
}
};
上面是比较清晰的写法,为了少几个变量,也可以写成这样
class Solution {
public:
int rectCover(int number) {
if (number <= 2) {
return number;
}
int pre1 = 2, pre2 = 1;
for (int i = 3; i <= number; ++i) {
pre1 += pre2;
pre2 = pre1 - pre2;
}
return pre1;
}
};
还可以采用递归的方法去做
class Solution {
public:
int rectCover(int number) {
if (number <= 2) {
return number;
}
return rectCover(number - 1) + rectCover(number - 2);
}
};
剑指offer 011、二进制中 1 的个数
题目
题解
好吧,这道题俺不会做,这是自己写的错误解法:
// !!!!!!错误解法!!!!!!!
class Solution {
public:
int NumberOf1(int n) {
if (n == 0) {
return 0;
}
int binaryCount = 1;
if (n < 0) {
binaryCount = 2;
}
while (n / 2) {
if (n % 2) {
binaryCount++;
}
n /= 2;
}
return binaryCount;
}
};
正确解法
(来自牛客大佬 @菩提旭光)
如果一个整数不为0,那么这个整数至少有一位是1。
如果把这个整数减1,原来处在整数最右边的1就会变为0,原来在1后面的所有0变成1(如果最右边的1后面还有0的话)。其余的位不会受到影响
就这样,把一个整数减去1,再和原整数做 与运算,会把该整数最右边的一个1变成0。该整数的二进制有多少个1,就可以进行多少次这样的操作。
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
while (n != 0) {
count++;
n = n & (n - 1);
}
return count;
}
};
剑指offer 012、数值的整数次方
题目
题解
自己第一次做的,下面开始优化
class Solution {
public:
double Power(double base, int exponent) {
double result = 1;
if (exponent == 0) {
return 1;
}
else if (exponent > 0) {
for (int i = 1; i <= exponent; ++i) {
result *= base;
}
}
else {
exponent *= -1;
for (int i = 1; i <= exponent; ++i) {
result *= base;
}
result = 1 / result;
}
return result;
}
};
class Solution {
public:
double Power(double base, int exponent) {
if (exponent == 0) { return 1.0; }
if (base == 0) { return 0.0; }
bool flag = false;
if (exponent < 0) {
flag = true;
exponent *= -1; // 指数变正
}
double result = base;
for (int i = 2; i <= exponent; ++i) {
result *= base;
}
if (flag) {
return 1.0 / result;
}
else {
return result;
}
}
};
剑指offer 013、调整数组顺序使奇数位于偶数前面
题目
题解
暴力解法:开辟两个新数组
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param array int整型vector
* @return int整型vector
*/
vector<int> reOrderArray(vector<int>& array) {
// write code here
vector<int> odd, even;
for (auto x : array) {
if (x % 2) {
odd.push_back(x);
}
else {
even.push_back(x);
}
}
array.clear();
for (auto x : odd) {
array.push_back(x);
}
for (auto x : even) {
array.push_back(x);
}
return array;
}
};
暴力解法开辟一个新数组
class Solution {
public:
vector<int> reOrderArray(vector<int>& array) {
int length = array.size();
vector<int> temp(length, 0);
int low = 0;
// 特别注意 按位与& 的优先级低于 ==
for (int i = 0; i < length; ++i) {
if ((array[i] & 1) == 1) {
temp[low++] = array[i];
}
}
for (int i = 0; i < length; ++i) {
if ((array[i] & 1) == 0) {
temp[low++] = array[i];
}
}
array.assign(temp.begin(), temp.end());
return array;
}
};
时间和空间都是O(n)的做法
class Solution {
public:
vector<int> reOrderArray(vector<int>& array) {
vector<int> temp;
int oddIndex = 0;
for (auto x : array) {
if ((x & 1) == 1) {
array[oddIndex++] = x;
}
else {
temp.push_back(x);
}
}
for (int i = 0; i < temp.size(); ++i) {
array[oddIndex + i] = temp[i];
}
return array;
}
};
class Solution {
public:
vector<int> reOrderArray(vector<int>& array) {
vector<int> temp(array.size(), 0); // 这里
int oddIndex = 0, evenIndex = 0; // 这里
for (auto x : array) {
if ((x & 1) == 1) {
array[oddIndex++] = x;
}
else {
temp[evenIndex++] = x; // 这里
}
}
for (int i = 0; i < evenIndex; ++i) { // 这里
array[oddIndex + i] = temp[i];
}
return array;
}
};
剑指offer 014、链表中倒数第k个结点
题目
题解
自己的解法
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
ListNode* FindKthToTail(ListNode* pHead, int k) {
// write code here
int length = 0;
ListNode* newNode = pHead;
while (newNode) {
length++;
newNode = newNode->next;
}
if (length < k) {
return newNode;
}
for (int i = 1; i <= length - k; ++i) {
pHead = pHead->next;
}
return pHead;
}
};
快慢指针
画个图,模拟一下很清楚
class Solution {
public:
ListNode* FindKthToTail(ListNode* pHead, int k) {
ListNode* slowNode = pHead;
while (k--) {
if (pHead) { // 判断链表长度是否大于k
pHead = pHead->next;
}
else { // 链表长度小于k
return nullptr;
}
}
while(pHead) {
slowNode = slowNode->next;
pHead = pHead->next;
}
return slowNode;
}
};
剑指offer 015、反转链表
题目
题解
自己做的,太太太太low了,虽然思路是对的,也是头插法,但是写的太繁琐了。还是看下面的头插法吧
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if (pHead == nullptr) return nullptr;
if (pHead->next == nullptr) return pHead;
ListNode* node = pHead->next;
if (node->next == nullptr) {
node->next = pHead;
pHead->next = nullptr;
return node;
}
pHead->next = nullptr;
ListNode* preNode = pHead;
ListNode* nextNode = node->next;
while (nextNode) {
node->next = preNode;
preNode = node;
node = nextNode;
nextNode = nextNode->next;
}
node->next = preNode;
return node;
}
};
头插法
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* pre = nullptr;
ListNode* cur = pHead;
ListNode* nex = nullptr;
while (cur) {
nex = cur->next;
cur->next = pre;
pre = cur;
cur = nex;
}
return pre;
}
};
剑指offer 016、合并两个排序的链表
题目
题解
迭代去做
/*
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* newHead = (ListNode*)malloc(sizeof(struct ListNode));
ListNode* node = newHead;
while (pHead1 && pHead2) {
if (pHead1->val < pHead2->val) {
node->next = pHead1;
pHead1 = pHead1->next;
}
else {
node->next = pHead2;
pHead2 = pHead2->next;
}
node = node->next;
}
node->next = (pHead1 ? pHead1 : pHead2);
return newHead->next;
}
};
递归版本
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
if (pHead1 == nullptr) return pHead2;
if (pHead2 == nullptr) return pHead1;
if (pHead1->val < pHead2->val) {
pHead1->next = Merge(pHead1->next, pHead2);
return pHead1;
}
else {
pHead2->next = Merge(pHead1, pHead2->next);
return pHead2;
}
}
};
剑指offer 017、树的子结构
题目
题解
不会做。。参考大佬的:@Krahets 提供的思路写的
思路:
若树 B 是树 A 的子结构,则子结构的根节点可能为树 A 的任意一个节点。因此,判断树 B 是否是树 A 的子结构,需完成以下两步工作:
- 先序遍历树 A 中的每个节点 n_A(对应函数 isSubStructure(A, B))
- 判断树 A 中 以 n_A 为根节点的子树 是否包含树 B (对应函数 recur(A, B))
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool HasSubtreeCore(TreeNode* pRoot1, TreeNode* pRoot2) {
if (pRoot2 == nullptr) return true; // 说明树pRoot2已匹配完成(越过叶子节点),匹配成功
if (pRoot1 == nullptr) return false; // 说明已经越过树pRoot1叶子节点,匹配失败
if (pRoot1->val != pRoot2->val) {
return false; // 当两结点的值不同时,说明匹配失败
}
else { // 分别判断pRoot1和pRoot2的左右子结点是否相等
return HasSubtreeCore(pRoot1->left, pRoot2->left) &&
HasSubtreeCore(pRoot1->right, pRoot2->right);
}
}
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {
if (pRoot1 == nullptr || pRoot2 == nullptr) return false;
// 若pRoot2是pRoot1的子结构,则必须满足下面三种情况之一:以结点pRoot1为根的子树包括树pRoot2 / 树pRoot2是树pRoot1的左子树的子结构 / 树pRoot2是树pRoot1的右子树的子结构
return HasSubtreeCore(pRoot1, pRoot2) ||
HasSubtree(pRoot1->left, pRoot2) ||
HasSubtree(pRoot1->right, pRoot2);
}
};
剑指offer 018、二叉树的镜像
题目
题解
观察两幅图,镜像即为颠倒原二叉树的左右位置(左右对称),可用递归的方法来解决
class Solution {
public:
TreeNode* Mirror(TreeNode* pRoot) {
if (pRoot == nullptr) return nullptr;
swap(pRoot->left, pRoot->right);
Mirror(pRoot->left);
Mirror(pRoot->right);
return pRoot;
}
};
借助队列,迭代的方法来做
class Solution {
public:
TreeNode* Mirror(TreeNode* pRoot) {
if (pRoot == nullptr) return nullptr;
// 类似于二叉树的层次遍历
queue<TreeNode*> q;
TreeNode* node = nullptr;
q.push(pRoot);
while (!q.empty()) {
node = q.front();
q.pop();
if (node) {
q.push(node->left);
q.push(node->right);
swap(node->left, node->right);
}
}
return pRoot;
}
};
剑指offer 019、顺时针打印矩阵
题目
题解
自己做时思路都是对的,但是在边界的控制不到位,以至于旋转到矩阵最里层出现部分数字未打印。下面参考了一下大佬的处理方法才做出来
对于后面两个for循环,要加入条件判断,防止出现单行或单列的情况
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
int row = matrix.size();
int col = matrix[0].size();
vector<int> res;
// 输入的二维数组非法,返回空数组
if (!row || !col) return res;
// 四个变量用来限制上下左右打印范围
int left = 0, right = col - 1;
int top = 0, 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]);
}
// 还要加两个限制条件:top < bottom 和 left < right
// right to left
for (int i = right - 1; i >= left && top < bottom; --i) {
res.push_back(matrix[bottom][i]);
}
// bottom to top
for (int i = bottom - 1; i > top && left < right; --i) { // 注意这里是 i > top
res.push_back(matrix[i][left]);
}
left++, right--;
top++, bottom--;
}
return res;
}
};
剑指offer 020、包含min的栈
题目
题解
自己首刷
这里主栈和辅助栈一直同步
class Solution {
public:
stack<int> dataStack, minStack;
void push(int value) {
if (minStack.empty()) {
minStack.push(value);
}
else if (value > minStack.top()) {
minStack.push(minStack.top());
}
else {
minStack.push(value);
}
dataStack.push(value);
}
void pop() {
dataStack.pop();
minStack.pop();
}
int top() {
return dataStack.top();
}
int min() {
return minStack.top();
}
};
当然也可以这样:
压的时候,
- 如果主栈的压入元素 > 辅助栈栈顶,辅助栈不压
- 如果主栈的压入元素 ≤ 辅助栈栈顶,两栈同时压入
出栈,
- 如果两栈栈顶元素不等,主栈出,辅助栈不出
- 相等,一起出
class Solution {
public:
stack<int> dataStack, minStack;
void push(int value) {
if(minStack.empty()) {
minStack.push(value);
}
else if(value <= minStack.top()) {
minStack.push(value);
}
dataStack.push(value);
}
void pop() {
if(dataStack.top() == minStack.top())
minStack.pop();
dataStack.pop();
}
int top() {
return dataStack.top();
}
int min() {
return minStack.top();
}
};
剑指offer 021、栈的压入、弹出序列
题目
题解
emmmm,这道题刚拿出来就一脸懵逼,丝毫没有思路,在看了牛客大佬@Alisa提供的思路后才模仿写了出来
思路:
借用一个辅助栈,将压栈顺序的元素逐个放入辅助栈中,每放一个元素就进行判断:判断栈顶元素是否 等于 出栈序列的第一个元素(同时还要判断辅助栈是否为空)
- 如果 等于,辅助栈中该元素出栈,并且将出栈序列索引指向下一个
- 如果 不等,继续前行(将压栈顺序下一个元素放入辅助栈中)
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if (pushV.empty()) return false;
stack<int> tempStack;
int popIndex = 0; // 弹出序列数组popV的索引
for (int i = 0; i < pushV.size(); ++i) {
tempStack.push(pushV[i]);
// 如果辅助栈不为空且栈顶元素等于弹出序列
while (!tempStack.empty() && tempStack.top() == popV[popIndex]) {
// 出栈
tempStack.pop();
// 弹出序列后移一位
popIndex++;
}
}
return tempStack.empty();
}
};
剑指offer 022、从上往下打印二叉树
题目
题解
借助队列
/*
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) {
if (root == nullptr) return vector<int>();
vector<int> res;
TreeNode* node = nullptr;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
node = q.front();
q.pop();
res.push_back(node->val);
if (node->left) {
q.push(node->left);
}
if (node->right) {
q.push(node->right);
}
}
return res;
}
};
剑指offer 023、二叉搜索树的后序遍历序列
题目
题解
对于一个序列,最后一个元素是根root,去掉最后一个元素的序列可以分为两部分,前一段即左子树小于root,后一段即右子树大于root,并且这两段子树都是合法的后序序列,因此可以采用递归的方法
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
if (sequence.empty()) return false;
if (sequence.size() == 1) return true;
return verify(sequence, 0, sequence.size() - 1);
}
bool verify(vector<int>& sequence, int left, int right) {
if (left >= right) return true; // 一定是 >=
int i = right;
while (i > left && sequence[--i] > sequence[right]);
for (int j = i - 1; j >= left; --j) {
if (sequence[j] > sequence[right]) {
return false;
}
}
return verify(sequence, left, i) && verify(sequence, i + 1, right - 1);
}
};
还可以用非递归的方法去解决,感觉用非递归去做思路更清晰
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
if(sequence.empty()) return false;
int length = sequence.size();
int i = 0;
while (--length) {
while (sequence[i++] < sequence[length]);
while (sequence[i++] > sequence[length]);
if (i <= length) return false;
i = 0;
}
return true;
}
};
剑指offer 024、二叉树中和为某一值的路径
题目
题解
/*
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>> result;
vector<int> temp;
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
if (root == nullptr) return result;
temp.push_back(root->val);
expectNumber -= root->val;
if (expectNumber == 0 && !root->left && !root->right) {
result.push_back(temp);
}
FindPath(root->left, expectNumber);
FindPath(root->right, expectNumber);
// 移除最后一个元素,深度遍历完一条路径需要回退
temp.pop_back();
return result;
}
};
剑指offer 025、复杂链表的复制
题目
题解
参考牛客大佬@chancy
分为三个步骤:
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead) {
if (!pHead) return nullptr;
RandomListNode* curNode = pHead;
// 复制每个结点并插入到原结点后面
while (curNode) {
RandomListNode* nextNode = curNode->next;
RandomListNode* cloneNode = (RandomListNode*)malloc(sizeof(RandomListNode));
cloneNode->label = curNode->label;
cloneNode->next = nextNode;
curNode->next = cloneNode;
curNode = nextNode;
}
curNode = pHead;
// 重新遍历链表,复制原结点的随机指针给新结点
while (curNode) {
curNode->next->random = (curNode->random == nullptr ? nullptr : curNode->random->next);
curNode = curNode->next->next;
}
// 拆分链表
curNode = pHead;
RandomListNode* pCloneHead = pHead->next;
while (curNode) {
RandomListNode* cloneNode = curNode->next;
curNode->next = cloneNode->next;
cloneNode->next = (cloneNode->next == nullptr ? nullptr : cloneNode->next->next);
curNode = curNode->next;
}
return pCloneHead;
}
};
剑指offer 026、二叉搜索树与双向链表
题目
题解
解法1
第一种解法是违背题目要求(不得创建新的结点)的做法:
将二叉搜索进行中序遍历即可得到由小到大的顺序排列,通过中序遍历将二叉树元素存储下来,然后将数组中的结点逐个连接。
class Solution {
public:
vector<TreeNode*> treeList; // 用来存储结点的数组
void convertSort(TreeNode*& root) {
if (!root) return;
convertSort(root->left);
treeList.push_back(root); // 存储
convertSort(root->right);
}
TreeNode* Convert(TreeNode* pRootOfTree) {
if (!pRootOfTree) return pRootOfTree;
convertSort(pRootOfTree);
// 将数组中的结点逐个连接
for (int i = 1; i <= treeList.size() - 1; ++i) {
treeList[i - 1]->right = treeList[i];
treeList[i]->left = treeList[i - 1];
}
return treeList[0];
}
};
解法2
还是通过中序遍历,用preNode指向当前结点的前继,preNode->right == root
class Solution {
public:
TreeNode* preNode = nullptr; // 一定是全局变量
TreeNode* Convert(TreeNode* root) {
if (!root) return root;
TreeNode* Head = root;
// 找到双向链表的开头
while (Head->left) Head = Head->left;
converAdjust(root);
return Head;
}
void converAdjust(TreeNode* root) {
if (!root) return;
converAdjust(root->left);
root->left = preNode;
if (preNode) {
preNode->right = root;
}
preNode = root; // 更新preNode,指向当前结点
converAdjust(root->right);
}
};
剑指offer 027、字符串的排列
题目
题解
DFS + 回溯
还不会做,看的秀哥的做法。会了继续补充
class Solution {
public:
vector<string> result;
void permutationAdjust(string &s,int begin,int end) {
if(begin == end){
result.push_back(s);
return ;
}
unordered_map<int,int> visited;
for(int i = begin; i<= end; ++i){
if(visited[s[i]] == 1) continue;
swap(s[i],s[begin]);
permutationAdjust(s,begin+1,end);
swap(s[i],s[begin]);
visited[s[i]] =1;
}
}
vector<string> Permutation(string str) {
if (str.size() == 0) return vector<string>();
permutationAdjust(str,0,str.size()-1);
sort(result.begin(), result.end());
return result;
}
};
剑指offer 028、数组中出现次数超过一半的数字
题目
题解
常规做法,借助哈希表
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
unordered_map<int, int> count;
for (auto x : numbers) {
++count[x];
// 这里是 > 号,考虑[2, 2, 2, 3, 3, 3, 3]
if (count[x] > numbers.size() / 2) return x;
}
return 0;
}
};
剑指offer 029、最小的K个数
题目
题解
第一种方法不满足题意,时间复杂度为O(n),看第二种方法
解法1
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
if (k <= 0 || k > input.size()) return vector<int>();
vector<int> result;
sort(input.begin(), input.end());
for (int i = 0; i < k; ++i) {
result.push_back(input[i]);
}
return result;
}
};
解法2
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
if (k <= 0 || k > input.size()) return vector<int>();
vector<int> result;
// 借助优先队列(最小堆)
priority_queue<int, vector<int>, greater<int>> q;
for (auto x : input) {
q.push(x);
}
while (k--) {
result.push_back(q.top());
q.pop();
}
return result;
}
};
剑指offer 030、连续子数组的最大和
题目
题解
显然这是一道动态规划的题目
先看第一种解法
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
if (array.size() == 0) return 0;
int maxNum = array[0];
for (int i = 1; i < array.size(); ++i) {
array[i] = max(array[i], array[i - 1] + array[i]);
maxNum = max(maxNum, array[i]);
}
return maxNum;
}
};
由于有些题目要求不能修改原数组,而dp列表中dp[i] 只和 dp[i - 1]有关,所以创建两个变量循环利用即可。
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
if (array.size() == 0) return 0;
int max = array[0];
int pre = 0; // 记录dp[i - 1]的值
int cur = array[0]; // 记录dp[i]的值
for (auto x : array) {
cur = x;
if (pre > 0) cur += pre;
if (cur > max) max = cur;
pre = cur;
}
return max;
}
};
剑指offer 031、整数中1出现的次数(从1到n整数中1出现的次数)
题目
题解
太难了[/(ㄒoㄒ)/~~],真伤脑
copy @superkakayong 大佬的解法:
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n) {
long digit = 1; // 若n是INT_MAX digit会在最后一次循环越界
int high = n / 10, cur = n % 10, low = 0;
int res = 0;
while (high != 0 || cur != 0) {
if (cur == 0) {
res += high * digit;
}
else if (cur == 1) {
res += high * digit + low + 1;
}
else {
res += (high + 1) * digit;
}
low += cur * digit;
cur = high % 10;
high /= 10;
digit *= 10;
}
return res;
}
};
剑指offer 032、把数组排成最小的数
题目
题解
思路:
求拼接起来的最小数字,本质上是个排序问题。
设数组numbers中两数字的字符串为 x 和 y,则排序规则为:
- 若 x + y < y + x,则 排序完数组中 x 应在 y 左侧
- 若 x + y > y + x,则 排序完数组中 x 应在 y 右侧
class Solution {
public:
// 写一个仿函数
bool operator()(string x, string y) {
return x + y < y + x;
}
string PrintMinNumber(vector<int> numbers) {
vector<string> results;
string res;
for (auto x : numbers) {
string temp = to_string(x);
results.push_back(temp);
}
sort(results.begin(), results.end(), Solution());
for (auto x : results) {
res += x;
}
return res;
}
};
剑指offer 033、丑数
题目
题解
学习牛客大佬@事无巨细,悉究本末 的解法,
对于一个丑数,其因子只能为2,3,5,那么丑数 p = 2 ^ x * 3 ^ y * 5 ^ z,因此我们可以维护三个队列(乘以2的、乘以3的、乘以5的),选择队列头最小的数字加入丑数列中。
class Solution {
public:
int GetUglyNumber_Solution(int index) {
// 0- 6的丑数分别是0 - 6
if (index < 7) return index;
// i2, i3, i5分别为三个队列的指针,newNum为从队列头选出的最小值
int i2 = 0, i3 = 0, i5 = 0;
int newNum = 1;
vector<int> array;
array.push_back(newNum);
while (array.size() < index) {
// 求出三个队列头中最小的数
newNum = min(array[i2] * 2, min(array[i3] * 3, array[i5] * 5));
// 判断选出的newNum到底是哪个队列头,更新对应的队列头指针(可能存在多个的情况,所以用三条if语句)
if (array[i2] * 2 == newNum) ++i2;
if (array[i3] * 3 == newNum) ++i3;
if (array[i5] * 5 == newNum) ++i5;
array.push_back(newNum);
}
return newNum;
}
};
剑指offer 034、第一个只出现一次的字符
题目
题解
class Solution {
public:
int FirstNotRepeatingChar(string str) {
int res = 0; // 记录返回值
unordered_map<char, int> array; // 无序哈希表记录每个字符出现的次数
for (char x : str) ++array[x];
for (char x : str) {
if (array[x] == 1) return res;
++res;
}
return -1;
}
};
剑指offer 035、数组中的逆序对
题目
题解
学习牛客大佬@rs勿忘初心 的解法
class Solution {
public:
int InversePairs(vector<int> data) {
if (data.empty()) return 0;
vector<int> copy(data); // 辅助数组,每次递归后有序
long long count = InversePairsAdjust(data, copy, 0, data.size() - 1);
return count % 1000000007;
}
long long InversePairsAdjust(vector<int> &data,vector<int> ©,int begin,int end) {
if (begin == end) return 0;
int mid = begin + (end - begin) / 2;
long long left = InversePairsAdjust(copy, data, begin, mid);
long long right = InversePairsAdjust(copy, data, mid + 1, end);
int last_left = mid; // 左侧的尾端
int last_right = end; // 右侧的尾端
int index_copy = end; // 比较结果存入辅助数组尾部
long long res = 0;
// 归并排序, 相当于两个有序数组合成一个有序表(从尾端开始是为了计数)
while (last_left >= begin && last_right >= mid + 1) {
if (data[last_left] > data[last_right]) {
copy[index_copy--] = data[last_left--];
res += last_right - mid;
}
else {
copy[index_copy--] = data[last_right--];
}
}
while (last_left >= begin)
copy[index_copy--] = data[last_left--];
while (last_right >= mid + 1)
copy[index_copy--] = data[last_right--];
return left + right + res;
}
};
剑指offer 036、两个链表的第一个公共结点
题目
题解
分两种情况:
- 长度相同时:
- 若两链表有公共结点,第一次就遍历到了
- 若无公共结点,两链表同时走到尾部nullptr,返回nullptr
- 长度不同时:
- 若有公共结点,第一遍差值会出来,第二遍就可以同时走到公共结点
- 若无公共结点,第二遍时两链表同时走到尾部nullptr
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if (!pHead1 || !pHead2) return nullptr;
ListNode* p1 = pHead1;
ListNode* p2 = pHead2;
while (p1 != p2) {
p1 = (p1 == nullptr ? pHead2 : p1 = p1->next);
p2 = (p2 == nullptr ? pHead1 : p2 = p2->next);
}
return p1;
}
};
剑指offer 037、数字在升序数组中出现的次数
题目
题解
使用二分查找
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
if (data.empty()) return 0;
int low = 0, high = data.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (data[mid] < k) {
low = mid + 1;
}
else if (data[mid] > k) {
high = mid - 1;
}
else { // 说明已找到k
int count = 0;
count++;
int index = mid - 1;
while (index >= 0 && data[index] == k) {
count++;
index--;
}
index = mid + 1;
while (index <= data.size() - 1 && data[index] == k) {
count++;
index++;
}
return count;
}
}
return 0;
}
};
剑指offer 038、二叉树的深度
题目
题解
class Solution {
public:
int TreeDepth(TreeNode* pRoot) {
if (!pRoot) return 0;
int left = TreeDepth(pRoot->left);
int right = TreeDepth(pRoot->right);
return max(left, right) + 1;
}
};
剑指offer 039、平衡二叉树
题目
题解
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot) {
if (!pRoot) return true;
return abs(getDepth(pRoot->left) - getDepth(pRoot->right)) <= 1 && IsBalanced_Solution(pRoot->left) && IsBalanced_Solution(pRoot->right);
}
int getDepth(TreeNode* tree) {
if (!tree) return 0;
int left = getDepth(tree->left);
int right = getDepth(tree->right);
return max(left, right) + 1;
}
};
剑指offer 040、数组中只出现一次的两个数字
题目
题解
哇这道题根本没有想起来会用 异或 来解决的
思路
算法
- 先对所有数字进行一次异或,得到两个出现一次的数字的异或值。
- 在异或结果中找到任意为 11 的位。
- 根据这一位对所有的数字进行分组。
- 在每个组内进行异或操作,得到两个数字。
class Solution {
public:
vector<int> FindNumsAppearOnce(vector<int>& array) {
vector<int> result;
int res1 = 0, res2 = 0;
int orSum = 0;
for (auto x : array) {
orSum ^= x;
}
int t = 1; // 找出异或 结果中哪一位是1
while ((orSum & t) == 0) {
t = t << 1;
}
for (auto x : array) {
if (t & x) {
res1 ^= x;
}
else {
res2 ^= x;
}
}
result.push_back(min(res1, res2));
result.push_back(max(res1, res2));
return result;
}
};
剑指offer 041、和为S的连续正数序列
题目
题解
思路:
利用双指针技术,相当于有一个窗口,窗口的左右两边就是两个指针,我们根据窗口内值之和来确定窗口的位置和宽度。
注意,窗口始终是要往右移动的!不可以phigh--
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
if (sum == 1) return vector<vector<int> >();
vector<vector<int>> result; // 保存结果
int plow = 1, phigh = 2; // 双指针,根据窗口内的值之和确定窗口的大小和位置,plow要从1开始,不包含0
while (plow < phigh) {
// 连续数字之和公式:(a0+an)*n/2
int cur = (plow + phigh) * (phigh - plow + 1) / 2;
if (cur == sum) {
vector<int> temp;
for (int i = plow; i <= phigh; ++i) {
temp.push_back(i);
}
result.push_back(temp);
plow++; // 窗口要不断向右移动
}
else if (cur < sum) {
phigh++;
}
else {
// 当前窗口之和大于 sum,左边窗口右移
plow++;
}
}
return result;
}
};
剑指offer 042、和为S的两个数字
题目
题解
和41题很像,设置两个头尾指针
-
若ai + aj == sum,就是答案(相差越远乘积越小)
证明相差越远,乘积越小:
设x+y=C(C是常数),y-x=d>=0
即x=(C-d)/2,y=(C+d)/2
x*y=(C^2 - d^2)/4,画出图形:x*y是一个关于y轴对称的,开口向下的,变量为d的函数。d>=0,d越大,x*y越大。 -
若ai + aj > sum,aj肯定不是答案之一(前面已得出 i 前面的数已是不可能),j –
-
若ai + aj < sum,ai肯定不是答案之一(前面已得出 j 后面的数已是不可能),i ++
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> result;
int low = 0, high = array.size() - 1;
while (low < high) {
if (array[low] + array[high] == sum) {
result.push_back(array[low]);
result.push_back(array[high]);
break;
}
else if (array[low] + array[high] < sum) {
low++;
}
else {
high--;
}
}
return result;
}
};
剑指offer 043、左旋转字符串
题目
题解
自己第一次做的:
class Solution {
public:
string LeftRotateString(string str, int n) {
string temp;
for (int i = 0; i < n; ++i) {
temp.push_back(str[i]);
}
str.erase(0, n);
str += temp;
return str;
}
};
不错的解法
class Solution {
public:
string LeftRotateString(string str, int n) {
int length = str.size();
if(length==0) return str;
if (n >= length) n = n % length; // 若循环左移n位的长度 大于 字符串的长度
str += str;
// substr()函数:获得字符串s中从第n位开始的长度为length的字符串
return str.substr(n, length);
}
};
剑指offer 044、翻转单词序列
题目
题解
从前往后一直读取,遇到空格之后就把之前读取到的压到结果的前面并添加空格。
class Solution {
public:
string ReverseSentence(string str) {
string result = "", temp = ""; // temp是当前正在处理的单词
for (auto s : str) {
if (s == ' ') {
result = " " + temp + result;
temp = "";
}
else {
temp += s;
}
}
if (temp.size()) {
result = temp + result;
}
return result;
}
};
剑指offer 045、扑克牌顺子
题目
题解
排序 + 遍历
class Solution {
public:
bool IsContinuous( vector<int> numbers ) {
int zeroNum = 0;
sort(numbers.begin(), numbers.end()); // 给五张牌排序
for (int i = 0; i < 4; ++i) { // 总共有五张牌
if (numbers[i] == 0) { // 统计0的数量(大小王)
zeroNum++;
}
else if (numbers[i] == numbers[i + 1]) { // 如果相邻两个元素相等,返回false
return false;
}
}
return numbers[4] - numbers[zeroNum] < 5; // 最大牌 - 最小牌 < 5才能构成顺子
}
};
剑指offer 046、孩子们的游戏(圆圈中最后剩下的数)
题目
题解
来自力扣大佬@Sweetiee的小号 的解法
第二张图可能不容易理解,再看一个非常清晰的解析
class Solution {
public:
int LastRemaining_Solution(int n, int m) {
if(n <= 0 || m < 0) return -1;
int result = 0;
// 最后一轮剩下2人,从2开始反推
for (int i = 2; i <= n; ++i) {
result = (result + m) % i;
}
return result;
}
};
剑指offer 047、求1+2+3+…+n
题目
题解
class Solution {
public:
int Sum_Solution(int n) {
int sum = n;
// 递归调用,同时利用&&操作的短路特性实现递归终止
n > 0 && (sum += Sum_Solution(n - 1));
return sum;
}
};
剑指offer 048、不能加减乘除做加法
题目
题解
- 两个数异或:相当于每一位相加,而不考虑进位;
- 两个数相与,并左移一位:相当于求得进位;
- 将上述两步的结果相加
示例:5->101,7->111
第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。
第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。
第三步重复上述两步, 各位相加 1000 =010^1010,进位值为100=(010&1010)<<1。
继续重复上述两步:1100 ** =1000^100 ,进位值为0**,跳出循环,1100为最终结果。
class Solution {
public:
int Add(int num1, int num2) {
while (num2 != 0) {
int sum = num1 ^ num2;
int carray = (num1 & num2) << 1;
num1 = sum;
num2 = carray;
}
return num1;
}
};
剑指offer 049、把字符串转换成整数
题目
题解
class Solution {
public:
int StrToInt(string str) {
int length = str.size();
if (length == 0) return 0;
int flag = 1, symbol = 0, i = 0;
long long num = 0;
while (i < length && str[i] == ' ') {
i++; // 处理首部空格
}
if (i >= length) return 0; // 字符串一直为空格
if (str[i] == '-') { // 负号判断
flag = -1;
symbol++;
i++;
}
if (str[i] == '+') { // 正号判断
symbol++;
i++;
}
if (symbol > 1) return 0; // 如果出现两个符号则数字不合法
for (; i < length; ++i) {
if (str[i] < '0' || str[i] > '9') return 0; // 判断是否有不合法的数字如1a33
else {
num = num * 10 + str[i] - '0'; // 数字拼接
// 如果输入的是INT_MAX = 2147483647 无影响
if (num >= INT_MAX && flag == 1) return INT_MAX;
// 如果输入是INY_MIN = -2147483647-1 = -2147483648,因为num是正数,必须用num > INT_MAX才能正确判断
if (num > INT_MAX && flag == -1) return INT_MIN;
}
}
return num * flag;
}
};
剑指offer 050、数组中重复的数字
题目
题解
借助哈希表:
class Solution {
public:
int duplicate(vector<int>& numbers) {
unordered_map<int, int> array;
for (auto x : numbers) {
array[x]++;
if (array[x] == 2) return x;
}
return -1;
}
};
剑指offer 051、构建乘积数组
题目
题解
先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
int length = A.size();
if (length == 1) return vector<int>();
vector<int> B(length, 0);
B[0] = 1;
// 计算下三角
for (int i = 1; i < length; ++i) {
B[i] = B[i - 1] * A[i - 1];
}
// 计算上三角
int temp = 1;
for (int i = length - 2; i >= 0; --i) {
temp *= A[i + 1];
B[i] *= temp;
}
return B;
}
};
剑指offer 052、正则表达式匹配
题目
题解
还是不太懂
力扣大佬三叶姐的方法
class Solution {
public:
bool match(string s, string p) {
// 技巧,往原字符头部插入空格,这样得到的string数组是从1开始
int m = s.size(), n = p.size();
s.insert(s.begin(), ' ');
p.insert(p.begin(), ' ');
// f(i, j)代表s中的1~i字符和p中的1~j字符是否匹配
vector<vector<bool>> f(m + 1, vector<bool>(n + 1, false));
f[0][0] = true;
for (int i = 0; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
// 如果下一个字符是 '*',则代表当前字符不能被单独使用
if (j + 1 <= n && p[j + 1] == '*') continue;
// 对应了p[j] 为普通字符和 '.' 的两种情况
if (i - 1 >= 0 && p[j] != '*') {
f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
}
// 对应了p[j] 为 '*' 的情况
else if (p[j] == '*') {
f[i][j] = (j - 2 >= 0 && f[i][j - 2])
|| (i - 1 >= 0 && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));
}
}
}
return f[m][n];
}
};
剑指offer 053、表示数值的字符串
题目
题解
额,直接Ctrl c, Ctrl v。
官方解析:
class Solution {
public:
enum State {
STATE_INITIAL,
STATE_INT_SIGN,
STATE_INTEGER,
STATE_POINT,
STATE_POINT_WITHOUT_INT,
STATE_FRACTION,
STATE_EXP,
STATE_EXP_SIGN,
STATE_EXP_NUMBER,
STATE_END
};
enum CharType {
CHAR_NUMBER,
CHAR_EXP,
CHAR_POINT,
CHAR_SIGN,
CHAR_SPACE,
CHAR_ILLEGAL
};
CharType toCharType(char ch) {
if (ch >= '0' && ch <= '9') {
return CHAR_NUMBER;
} else if (ch == 'e' || ch == 'E') {
return CHAR_EXP;
} else if (ch == '.') {
return CHAR_POINT;
} else if (ch == '+' || ch == '-') {
return CHAR_SIGN;
} else if (ch == ' ') {
return CHAR_SPACE;
} else {
return CHAR_ILLEGAL;
}
}
bool isNumeric(string s) {
unordered_map<State, unordered_map<CharType, State>> transfer{
{
STATE_INITIAL, {
{CHAR_SPACE, STATE_INITIAL},
{CHAR_NUMBER, STATE_INTEGER},
{CHAR_POINT, STATE_POINT_WITHOUT_INT},
{CHAR_SIGN, STATE_INT_SIGN}
}
}, {
STATE_INT_SIGN, {
{CHAR_NUMBER, STATE_INTEGER},
{CHAR_POINT, STATE_POINT_WITHOUT_INT}
}
}, {
STATE_INTEGER, {
{CHAR_NUMBER, STATE_INTEGER},
{CHAR_EXP, STATE_EXP},
{CHAR_POINT, STATE_POINT},
{CHAR_SPACE, STATE_END}
}
}, {
STATE_POINT, {
{CHAR_NUMBER, STATE_FRACTION},
{CHAR_EXP, STATE_EXP},
{CHAR_SPACE, STATE_END}
}
}, {
STATE_POINT_WITHOUT_INT, {
{CHAR_NUMBER, STATE_FRACTION}
}
}, {
STATE_FRACTION,
{
{CHAR_NUMBER, STATE_FRACTION},
{CHAR_EXP, STATE_EXP},
{CHAR_SPACE, STATE_END}
}
}, {
STATE_EXP,
{
{CHAR_NUMBER, STATE_EXP_NUMBER},
{CHAR_SIGN, STATE_EXP_SIGN}
}
}, {
STATE_EXP_SIGN, {
{CHAR_NUMBER, STATE_EXP_NUMBER}
}
}, {
STATE_EXP_NUMBER, {
{CHAR_NUMBER, STATE_EXP_NUMBER},
{CHAR_SPACE, STATE_END}
}
}, {
STATE_END, {
{CHAR_SPACE, STATE_END}
}
}
};
int len = s.length();
State st = STATE_INITIAL;
for (int i = 0; i < len; i++) {
CharType typ = toCharType(s[i]);
if (transfer[st].find(typ) == transfer[st].end()) {
return false;
} else {
st = transfer[st][typ];
}
}
return st == STATE_INTEGER || st == STATE_POINT || st == STATE_FRACTION || st == STATE_EXP_NUMBER || st == STATE_END;
}
};
某位ACM选手的解法
class Solution {
public:
bool is_digit(char a) { //判断是否为数字
return a >= '0' && a <= '9';
}
bool is_e(char a) {
return a == 'e' || a == 'E';
}
bool check(string st) {
int count = 0;
for (char i : st) {
if (i == '.') count++;
if (count > 1) return false;
}
return true;
}
bool isNumeric(string s) {
if (check(s) == false) return false;//确保最多只有一个小数点
//前后去空格
int i = 0;
while (i < s.size() && s[i] == ' ') {
i++;
}
int end = s.size() - 1;
while (end >= 0 && s[end] == ' ') {
s.erase(s.begin() + end);
end--;
}
int len = s.size();
if (s[i] == '+' || s[i] == '-') {
i++;
if (i >= len) return false;
if (s[i] == '.') {
i++;
if (i >= len) return false;
if (is_digit(s[i])) {
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
if (is_e(s[i])) {
i++;
if (i >= len) return false;
if (s[i] == '-' || s[i] == '+') i++;
if (i >= len) return false;
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
else return false;
}
else {
return false;
}
}
else {
return false;
}
}
else if (is_digit(s[i])) {
while (i < len && is_digit(s[i])) { i++; }
if (i >= len)return true;
if (is_e(s[i])) {
i++;
if (i >= len) return false;
if (s[i] == '-' || s[i] == '+') i++;
if (i >= len) return false;
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
else return false;
}
else if (s[i] == '.' ) {
i++;
if (i >= len) return true;
if (is_digit(s[i])) {
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
if (is_e(s[i])) {
i++;
if (i >= len) return false;
if (s[i] == '-' || s[i] == '+') i++;
if (i >= len) return false;
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
else return false;
}
else {
return false;
}
}
else if (is_e(s[i])) {
i++;
if (i >= len) return false;
if (s[i] == '-' || s[i] == '+') i++;
if (i >= len) return false;
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
else return false;
}
else {
return false;
}
}
else {
return false;
}
}
else {
return false;
}
}
else if (s[i] == '.') {
i++;
if (i >= len) return false;
if (is_digit(s[i])) {
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
if (is_e(s[i])) {
i++;
if (i >= len) return false;
if (s[i] == '-' || s[i] == '+') i++;
if (i >= len) return false;
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
else return false;
}
else {
return false;
}
}
}
else if (is_digit(s[i])) {
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
if (s[i] == '.') {
i++;
if (i >= len) return true;
if (is_digit(s[i])) {
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
else if (is_e(s[i])) {
i++;
if (i >= len) return false;
if (s[i] == '-' || s[i] == '+') i++;
if (i >= len) return false;
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
else return false;
}
else {
return false;
}
}
else if (is_e(s[i])) {
i++;
if (i >= len) return false;
if (s[i] == '-' || s[i] == '+') i++;
if (i >= len) return false;
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
else return false;
}
else {
return false;
}
}
else if (is_e(s[i])) {
i++;
if (i >= len) return false;
if (s[i] == '-' || s[i] == '+') i++;
if (i >= len) return false;
while (i < len && is_digit(s[i])) { i++; }
if (i >= len) return true;
else return false;
}
else {
return false;
}
}
else {
return false;
}
return false;
}
};
剑指offer 054、字符流中第一个不重复的字符
题目
题解
借助哈希表,很简单
class Solution
{
public:
//Insert one char from stringstream
void Insert(char ch) {
v.push_back(ch);
result[ch]++;
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce() {
for (auto& ch : v) {
if (result[ch] == 1) return ch;
}
return '#';
}
private:
vector<char> v;
unordered_map<char, int> result;
};
剑指offer 055、 链表中环的入口结点
题目
题解
利用快慢指针,从头结点开始出发,fast指针每次移动两个节点,slow每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
相遇时: slow指针走过的节点数为: x + y
, fast指针走过的节点数:x + y + n (y + z)
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2: (x + y) * 2 = x + y + n (y + z)
,可得到x = n (y + z) - y
- 当 n为1的时候,公式就化解为
x = z
- 当n大于1时,就是fast指针在环形转n圈之后才遇到 slow指针。
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
ListNode* slow = pHead;
ListNode* fast = pHead;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
// 当快慢指针相遇后,一个指针从头结点开始,一个结点从相遇结点开始,直到再次相遇
if (slow == fast) {
ListNode* index1 = pHead;
ListNode* index2 = fast;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
}
return nullptr;
}
};
剑指offer 056、删除链表中重复的结点
题目
题解
- 首先添加一个头节点,以方便碰到第一个,第二个节点就相同的情况
- 设置 pre ,cur指针, pre指针指向当前确定不重复的那个节点,而cur指针相当于工作指针,一直往后面搜索。
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead) {
if (!pHead) return pHead;
ListNode* Head = new ListNode(0);
Head->next = pHead;
ListNode* pre = Head;
ListNode* cur = Head->next;
while (cur) {
if (cur->next != nullptr && cur->val == cur->next->val) {
// 寻找最后一个相同的结点
while (cur->next != nullptr && cur->val == cur->next->val) {
cur = cur->next;
}
pre->next = cur->next;
cur = cur->next;
}
else {
pre = pre->next;
cur = cur->next;
}
}
return Head->next;
}
};
剑指offer 057、二叉树的下一结点
题目
题解
思路:
- 二叉树为空,则返回空;
- 有右子树,下一结点是右子树中的最左结点
- 无右子树,且结点是该结点父结点的左子树,则下一结点是该结点的父结点
- 无右子树,且结点是该结点父结点的右子树,则我们一直沿着父结点追朔,直到找到某个结点是其父结点的左子树,如果存在这样的结点,那么这个结点的父结点就是我们要找的下一结点
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode) {
// 情况1
if (!pNode) return nullptr;
TreeLinkNode* node = nullptr;
// 情况2.如果当前节点有右子树,则右子树最左边的那个节点就是所要寻找的
if (pNode->right) {
node = pNode->right;
while (node->left) {
node = node->left;
}
return node;
}
while (pNode->next) { // 该结点的父结点存在时
TreeLinkNode* root = pNode->next;
if (root->left == pNode) return root; // 情况3
pNode = pNode->next;
}
return nullptr;
}
};
剑指offer 058、对称的二叉树
题目
题解
思路:
- 终止条件:
- 当左节点和右节点同时越过叶节点,则此树所有节点对称
- 当左右节点只有一个越过叶节点,返回false
- 当左右节点值不相等,此树不对称,返回false
- 递推过程:
- 判断两节点
lNode->left, rNode->right
是否对称,即recur(lNode->left, rNode->right)
- 判断两节点
lNode->right, rNode->left
是否对称,即recur(lNode->right, rNode->left);
- 判断两节点
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot) {
if (!pRoot) return true;
return (pRoot == nullptr ? true : recur(pRoot->left, pRoot->right));
}
bool recur (TreeNode* lNode, TreeNode* rNode) {
if (lNode == nullptr && rNode == nullptr) return true;
if (lNode == nullptr || rNode == nullptr || lNode->val != rNode->val) return false;
return recur(lNode->left, rNode->right) && recur(lNode->right, rNode->left);
}
};
剑指offer 059、按之字形顺序打印二叉树
题目
题解
非常不错的一道题
利用两个栈来解决(一个栈存奇数行,另一个存偶数行)
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
if (!pRoot) return vector<vector<int> >();
vector<vector<int> > result;
stack<TreeNode*> stack1, stack2;
stack1.push(pRoot);
while (!stack1.empty() || !stack2.empty()) {
vector<int> res1, res2;
TreeNode* cur = nullptr;
// 将偶数行放到栈2中
while (!stack1.empty()) {
cur = stack1.top();
if (cur->left)
stack2.push(cur->left);
if (cur->right)
stack2.push(cur->right);
res1.push_back(stack1.top()->val); // 保存奇数行的数据
stack1.pop();
}
if (!res1.empty())
result.push_back(res1);
// 将奇数行放到栈1中
while (!stack2.empty()) {
cur = stack2.top();
if (cur->right)
stack1.push(cur->right);
if (cur->left)
stack1.push(cur->left);
res2.push_back(stack2.top()->val); // 保存偶数行的数据
stack2.pop();
}
if (!res2.empty())
result.push_back(res2);
}
return result;
}
};
剑指offer 060、把二叉树打印成多行
题目
题解
借助队列,将该层元素从队列中移出,并将下一层元素移入
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
if (!pRoot) return vector<vector<int> >();
vector<vector<int> > result;
queue<TreeNode*> q; // 借助队列
q.push(pRoot);
while (!q.empty()) {
vector<int> temp;
// 将队列中原有元素移出,并将队列中元素的子节点添加进来
for (int i = q.size(); i > 0; --i) {
TreeNode* node = q.front();
temp.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
q.pop();
}
// 将该层的元素加入result中
result.push_back(temp);
}
return result;
}
};
剑指offer 061、序列化二叉树
题目
题解
咦~ 困难… 我可以的… 好难啊… 看看解析去… (⊙o⊙)?…
写了两三个小时了… 还是不太会…
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
char* Serialize(TreeNode *root) {
if (!root) return nullptr;
string s;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
if (q.front()) {
q.push(q.front()->left);
q.push(q.front()->right);
s += to_string(q.front()->val) + ' ';
}
else {
s += "# ";
}
q.pop();
}
// strdup:复制字符串,可以自动分配空间(如果这里使用strcpy,还需手动分配内存)
char* result = strdup(s.c_str());
return result;
}
TreeNode* Deserialize(char *str) {
if (!str) return nullptr;
int k = 0;
TreeNode* result = nextNode(str, k);
queue<TreeNode*> q;
q.push(result);
while (!q.empty()) {
q.front()->left = nextNode(str, k);
q.front()->right = nextNode(str, k);
if (q.front()->left)
q.push(q.front()->left);
if (q.front()->right)
q.push(q.front()->right);
q.pop();
}
return result;
}
private:
TreeNode* nextNode(char* str, int& k) {
string s;
while (str[k] != '\0' && str[k] != ' ') {
if (str[k] == '#') {
k += 2; // #后面还有一个空字符,所以要加2
return nullptr;
}
s += str[k];
++k;
}
if (str[k] == ' ')
k++;
if (!s.empty())
// stio:将字符串转换为整数
return new TreeNode(stoi(s));
return nullptr;
}
};
剑指offer 062、二叉搜索树的第k个结点
题目
题解
首先想到的是递归
class Solution {
public:
TreeNode* KthNode(TreeNode* pRoot, int k) {
if (!pRoot) return nullptr;
TreeNode* result = KthNode(pRoot->left, k);
if (count >= k) return result;
if (++count == k) return pRoot;
return KthNode(pRoot->right, k);
}
private:
int count = 0;
};
借助栈来寻找,这种解法还是不太理解
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
TreeNode* KthNode(TreeNode* pRoot, int k) {
if (!pRoot) return nullptr;
stack<TreeNode*> res;
TreeNode* p = pRoot;
while (!res.empty() || p) {
while (p) {
res.push(p);
p = p->left;
}
TreeNode* node = res.top();
res.pop();
if (--k == 0) // 计数器
return node;
p = node->right;
}
return nullptr;
}
};
剑指offer 063、数据流中的中位数
题目
题解
这道题在读懂题意后,利用优先队列做起来还挺方便
思路:
利用两个优先队列,一个是最大堆(存放最小的一半数字),一个是最小堆(存放最大的一半数字)
由于数据是流式的,所以在这个过程中,最大最小堆中的元素会不断调整:
- 当两堆的数据个数相同时,在最大堆中加入元素。
(先将该元素加入最小堆中调整,然后将最小堆中的根节点(最小)会被加入到最大堆中) - 当两堆的数据个数不同时(只会出现一种情况:最大堆中元素个数比最小堆多一个),在最小堆中加入元素。
(先将该元素放在最大堆中调整,然后将最大堆的根节点(最大)加入最小堆中)
class Solution {
public:
// 最大堆(堆顶最大,存放最小的一半数字)
priority_queue<int, vector<int>, less<int>> maxHeap;
// 最小堆(堆顶最小,存放较大的一半数字)
priority_queue<int, vector<int>, greater<int>> minHeap;
/*
* 当两堆的数据个数相同时,在最大堆中加入元素,注意这里不是直接加入最大堆中!
* 先将该元素加入最小堆中调整后,在最小堆中的根节点(最小)会被加入到最大堆中
* 这样就保证了最大堆中的元素始终比最小堆中元素小
*
* 否则,先将该元素放在最大堆中调整,然后将最大堆的根节点(最大)加入最小堆中
*/
void Insert(int num) {
if (maxHeap.size() == minHeap.size()) {
minHeap.push(num);
int top = minHeap.top();
minHeap.pop();
maxHeap.push(top);
}
else {
maxHeap.push(num);
int top = maxHeap.top();
maxHeap.pop();
minHeap.push(top);
}
}
double GetMedian() {
if (maxHeap.size() == minHeap.size()) {
return (maxHeap.top() + minHeap.top()) * 1.0 / 2;
}
else {
return maxHeap.top() * 1.0;
}
}
};
剑指offer 064、滑动窗口的最大值
题目
题解
非常不错的一道题!!!
利用双端队列:滑动窗口不为空时,只要准备进入的元素大于滑动窗口最后一个元素,就删除队列前一个元素,直到队列末尾元素比新数大或队列为空时停止。
看代码注释吧!
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
if (num.empty() || size == 0) return vector<int>();
vector<int> result;
deque<int> window; // 记录每个窗口的最大下标(队首为最大下标)
int leftPoint = 0;
for (unsigned int i = 0; i < num.size(); ++i) {
// 滑动窗口不为空时,只要准备进入的元素大于滑动窗口最后一个元素,直到队列末尾元素比新数大或队列为空时停止
while (!window.empty() && num[window.back()] < num[i]) {
window.pop_back();
}
window.push_back(i);
// 判断队首元素是否过期
if (window.front() == i - size) {
window.pop_front();
}
// 向result列表中加入元素,要判断一下是否为最开始的size元素,特殊处理
if (i >= size - 1) {
result.push_back(num[window.front()]);
}
}
return result;
}
};
剑指offer 065、矩阵中的路径
题目
题解
看看K神的解析:
class Solution {
public:
bool hasPath(vector<vector<char> >& matrix, string word) {
row = matrix.size();
col = matrix[0].size();
// 每次调用size()函数也是一笔开销
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
if (dfs(matrix, word, i, j, 0))
return true;
}
}
return false;
}
private:
int row, col;
// k表示string中第k个元素
bool dfs(vector<vector<char>>& matrix, string word, int i, int j, int k) {
// 当发生越界,当前矩阵元素与目标字符不匹配时,返回false
if (i >= row || i < 0 || j >= col || j < 0 || matrix[i][j] != word[k]) return false;
// 当k的值等于len(word) - 1;说明字符串全部匹配了
if (k == word.size() - 1) return true;
// 每次访问过后标记为'\0',避免重复访问
matrix[i][j] = '\0';
// 朝当前元素上下左右四个方向都递归
bool result = dfs(matrix, word, i + 1, j, k + 1) || dfs(matrix, word, i - 1, j, k + 1)
|| dfs(matrix, word, i, j + 1, k + 1) || dfs(matrix, word, i, j - 1, k + 1);
// 将标记的元素修改为初始值
matrix[i][j] = word[k];
return result;
}
};
剑指offer 066、机器人的运动范围
题目
题解
不错不错,我们借助一个矩阵,对矩阵每个元素,当满足条件时标记,不断递归调用即可解决。
class Solution {
public:
int movingCount(int threshold, int rows, int cols) {
vector<vector<bool>> matrix(rows, vector<bool>(cols, false));
return movingCountCore(threshold, rows, 0, cols, 0, matrix);
}
int movingCountCore(int threshold, int rows, int i, int cols, int j, vector<vector<bool>>& matrix) {
// 当边界值不符合 || 不能到达 || 已经走过 时,就返回0
if (i >= rows || j >= cols || !isArrival(threshold, i, j) || matrix[i][j] == true) return 0;
matrix[i][j] = true; // 已经走过并且符合要求,标记为true
// 计算向下 / 向右 递归调用之和
return movingCountCore(threshold, rows, i + 1, cols, j, matrix)
+ movingCountCore(threshold, rows, i, cols, j + 1, matrix) + 1;
}
bool isArrival(int threshold, int i, int j) {
// 计算是否满足threshold
int sum = 0;
while (i != 0) {
sum += i % 10;
i /= 10;
}
while (j != 0) {
sum += j % 10;
j /= 10;
}
return sum <= threshold;
}
};
剑指offer 067、剪绳子
题目
题解
先观察一组数据:
- 4 : 2*2
- 5 : 2*3
- 6 : 3*3
- 7 : 2*2*3
- 8 : 2*3*3
- 9 : 3*3*3
- 10:2*2*3*3
- 11:2*3*3*3
- 12:3*3*3*3
- 13:2*2*3*3*3
- 14:2*3*3*3*3
可以看出,对于所有的数字的最大乘积,都是由2和3的乘积组成,因此我们可以定义一个变量来保存3的乘积(每次乘以3,同时给number减一个3),直到number == 4为止。
看代码:
class Solution {
public:
int cutRope(int number) {
if (number < 2) return 0; // 题目中限定(2 <= n <= 60)
if (number < 4) return number - 1; // 题目中限定 n>1并且m>1
int ThreeMultip = 1; // 记录3的乘积
while (number > 4) {
ThreeMultip *= 3; // 每乘一个3,就给number减一个3
number -= 3;
}
return ThreeMultip * number;
}
};