文章目录
-
- 1、二维数组中的查找
- 2、替换空格
- 3、从尾到头打印链表
- 4、用两个栈实现队列
- 5、旋转数组的最小数字
- 6、递归循环(斐波那契数列)
- 7、递归循环(跳台阶)
- 8、递归循环(变态跳台阶)
- 9、递归循环(矩阵覆盖)
- 10、位操作(二进制中1的个数)
- 11、数值的整数次方
- 12、调整数组顺序使奇数位于偶数前面
- 13、链表中倒数第k个节点
- 14、反转链表
- 15、合并两个排序的链表
- 16、字符串最后一个单词的长度
- 17、顺时针输出矩阵
- 18、包含min函数的栈
- 19、栈的压入、弹出序列
- 20、复杂链表的复制
- 21、字符串的排序
- 22、数组中出现次数超过一半的数字
- 23、最小的K个数
- 24、连续子数组的最大和
- 25、整数中1出现的次数(从1到n整数中1出现的次数)
- 26、把数组排成最小的数
- 27、丑数
- 28、第一个只出现一次的字符
- 29、数组中的逆序对
- 30、两个链表的第一个公共结点
- 31、数字在排序数组中出现的次数
- 32、数组中只出现一次的数字
- 33、和为S的连续正数序列
- 34、和为S的两个数字
- 35、左旋转字符串
- 36、翻转单词顺序列
- 37、扑克牌顺子
- 38、孩子们的游戏(圆圈中最后剩下的数)(标记一下)
- 39、求1+2+3+...+n
- 40、不用加减乘除做加法
- 41、把字符串转换为整数
- 42、数组中重复的数字
- 43、构建乘积数组
- 44、正则表达式匹配
- 45、表示数值的字符串
- 46、字符流中第一个不重复的字符
- 47、链表中环的入口结点
- 48、删除链表中重复的节点
- 49、数据流中的中位数
- 50、滑动窗口的最大值
- 51、矩阵中的路径
- 52、机器人的运动范围
- 53、剪绳子
1、二维数组中的查找
题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
解决代码:
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
int rows = array.size();
int cols = array[0].size();
int i=rows-1,j=0;//左下角元素坐标
while(i>=0 && j<cols){
//使其不超出数组范围
if(target<array[i][j])
i--;//查找的元素较少,往上找
else if(target>array[i][j])
j++;//查找元素较大,往右找
else
return true;//找到
}
return false;
}
};
2、替换空格
题目:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
解决代码:
class Solution {
public:
void replaceSpace(char *str,int length) {
int SpaceCount = 0;
//计算空格数量
for(int i = 0; i < length; i++){
if(str[i] == ' '){
SpaceCount ++;
}
}
for(int i = length - 1; i >= 0; i--){
if(str[i] != ' '){
str[i+SpaceCount*2] = str[i];
}
else{
SpaceCount --;
str[i+SpaceCount*2] = '%';
str[i+SpaceCount*2+1] = '2';
str[i+SpaceCount*2+2] = '0';
}
}
}
};
3、从尾到头打印链表
题目:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
解决代码:
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
int count = 0;
vector<int> v;
stack<int> stk;
while(head != NULL){
stk.push((*head).val);
head = head->next;
}
int stk_length = stk.size();
for(int i = 0; i < stk_length; i++){
v.push_back(stk.top());
stk.pop();
}
return v;
}
};
4、用两个栈实现队列
题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
解决代码:
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
int res,val;
if(stack2.size() > 0){
val = stack2.top();
stack2.pop();
}
else if(stack1.size() > 0){
while(stack1.size() > 0){
res = stack1.top();
stack2.push(res);
stack1.pop();
}
val = stack2.top();
stack2.pop();
}
return val;
}
private:
stack<int> stack1;
stack<int> stack2;
};
5、旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。。
解决代码:
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int minNumber;
for(int i = 0; i < rotateArray.size(); i++){
for(int j = 1; j < rotateArray.size(); j++){
if(rotateArray[j] < rotateArray[i]){
int temp;
temp = rotateArray[i];
rotateArray[i] = rotateArray[j];
rotateArray[j] = temp;
}
}
}
minNumber = rotateArray[0];
return minNumber;
}
};
6、递归循环(斐波那契数列)
题目:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
n<=39
解决思路:
通项公式为:
f(n) = f(n - 1) + f(n - 2) (n > 2)
f(0) = 0; f(1) = 1;
解决代码:
class Solution {
public:
int Fibonacci(int n) {
int n1 = 0,n2 = 1,n3 = 0;
if(n <= 0)
return 0;
if(n == 1)
return 1;
else{
for(int i = 2; i <= n; i++){
n3 = n1 + n2;
n1 = n2;
n2 = n3;
}
return n3;
}
}
};
7、递归循环(跳台阶)
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
解决思路:
当n=1时,1种方法
当n=2时,2种方法
当n=3时,3种方法
当n=4时,5种方法
···· ·····
当n=n时,只有f(n-1)+f(n-2)种跳法,符合斐波那契数列条件
解决代码:
class Solution {
public:
int jumpFloor(int number) {
int step;
int a = 1,b = 2;
if(number == 1)
return 1;
if(number == 2)
return 2;
step = a + b;
for(int i = 3; i <= number; i++){
step = a + b;
a = b;
b = step;
}
return step;
}
};
8、递归循环(变态跳台阶)
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
解决思路(来源于别人):
链接:源地址
1)这里的f(n) 代表的是n个台阶有一次1,2,…n阶的 跳法数。
2)n = 1时,只有1种跳法,f(1) = 1
-
n = 2时,会有两个跳得方式,一次1阶或者2阶,这回归到了问题(1) ,f(2) = f(2-1) + f(2-2)
-
n = 3时,会有三种跳得方式,1阶、2阶、3阶,
那么就是第一次跳出1阶后面剩下:f(3-1);第一次跳出2阶,剩下f(3-2);第一次3阶,那么剩下f(3-3)
因此结论是f(3) = f(3-1)+f(3-2)+f(3-3)
-
n = n时,会有n中跳的方式,1阶、2阶…n阶,得出结论:
f(n) = f(n-1)+f(n-2)+…+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + … + f(n-1)
-
由以上已经是一种结论,但是为了简单,我们可以继续简化:
f(n-1) = f(0) + f(1)+f(2)+f(3) + … + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + … + f(n-2)
f(n) = f(0) + f(1) + f(2) + f(3) + … + f(n-2) + f(n-1) = f(n-1) + f(n-1)
可以得出:
f(n) = 2*f(n-1)
-
得出最终结论,在n阶台阶,一次有1、2、…n阶的跳的方式时,总得跳法为:
| 1 ,(n=0 ) f(n) = | 1 ,(n=1 ) | 2*f(n-1),(n>=2)
解决代码:
class Solution {
public:
int jumpFloorII(int number) {
int step;
if(number <= 0)
return 0;
else if(number == 1)
return 1;
else{
step = 2 * jumpFloorII(number - 1);
return step;
}
}
};
9、递归循环(矩阵覆盖)
题目:我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
比如n=3时,2*3的矩形块有3种覆盖方法:
解决思路:
当n=1时,1种方法
当n=2时,2种方法
当n=3时,3种方法
当n=4时,5种方法
当n=5时,8种方法
当n=6时,13种方法
···· ·····
当n=n时,只有f(n-1)+f(n-2)种跳法,符合斐波那契数列条件
解决代码:
class Solution {
public:
int rectCover(int number) {
int a = 1,b = 2, c = 0;
if(number <= 0){
return 0;
}
else if(number > 0 && number < 3){
return number;
}
for(int i = 3; i <= number; i++){
c = a + b;
a = b;
b = c;
}
return c;
}
};
10、位操作(二进制中1的个数)
题目:
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
解决思路:
当 n 为正数时,好理解,反复对2取余,不为0,1的个数就加1
但是当 n 为负数时,需要对 n 进行转化,将 n 最高位的符号位1变成0,也就是n & 0x7FFFFFFF,这样就把负数转化成正数了,唯一差别就是最高位由1变成0,因为少了一个1,所以 1 的个数加1。然后按照正数进行处理就行了。
解决代码:
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
if(n < 0){
n &= 0x7FFFFFFF;
count ++;
}
while(n != 0){
if( n % 2 != 0){
count ++;
}
n /= 2;
}
return count;
}
};
11、数值的整数次方
题目: 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0。
解决代码:
class Solution {
public:
double Power(double base, int exponent) {
double ret = 1;
if(base == 0)
return 0;
else if(exponent == 0)
return 1;
else if(exponent > 0){
for(int i = 0; i < exponent; i++){
ret = (double)ret * base;
}
}
else{
for(int i = 0; i < -exponent; i++){
ret = (double)ret * (1/base);
}
}
return ret;
}
};
12、调整数组顺序使奇数位于偶数前面
题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
解决思路:
1.从前往后遍历,第一个元素如果是偶数,记住这个 偶数的角标位。如果不是偶数,啥也不干。
2.当遇到偶数的时候,判断这是不是第一个偶数,如果是 记住角标位。
3.当遇到奇数的时候,判断其前面有没有偶数,如果有,则将前面的连续的多个偶数,顺序往后移动一位,然后将奇数插入第一个偶数位置。
解决代码:
class Solution {
public:
void reOrderArray(vector<int> &array) {
int lengthA = array.size();
int i = 0,index = -1;
while(i < lengthA){
if((array[i] % 2) != 0){
if(index >= 0){
//和index位置的值交换
int temp = array[i];
for(int j = i; j > index; j--){
array[j] = array[j-1];
}
array[index] = temp;
index++;
}
i++;
continue;
}
else{
//从左边开始的第一个偶数的位置,如果第一个元素就是偶数,这个临界值一定要考虑到。
//如果第一个元素不是偶数,那当遍历到第一个偶数的时候,开始记住其角标。
if(i == 0)
index = 0;
if(index < 0)
index = i;
i++;
continue;
}
}
}
};
13、链表中倒数第k个节点
题目:输入一个链表,输出该链表中倒数第k个结点。
解决思路:
(1)求得链表的总长度
(2)正序找到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) {
int len_list = 1;
int count = 1;
int pos = 0;
ListNode* plist = pListHead;
while(plist){
plist = plist->next;
len_list ++;
}
if(k >= len_list){
return plist;
}
pos = len_list - k + 1;
plist = pListHead;
while(plist && count < pos-1){
plist = plist->next;
count++;
}
return plist;
}
};
14、反转链表
题目: 输入一个链表,反转链表后,输出新链表的表头。
解决代码:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* pNext = NULL;
ListNode* pPre = NULL;
if(pHead == NULL){
return NULL;
}
while(pHead != NULL){
//先用pXext保存pHead的下一个节点的信息,保证单链表不会因为失去pHead节点的原pNext节点而就此断裂
pNext = pHead->next;
//让pHead从指向pNext变成指向pPre
pHead->next = pPre;
//pHead指向pPre后,就继续依次反转下一个节点
//让pPre,pHead,pNext依次向后移动一个节点,继续下一次的指针反转
pPre = pHead;
pHead = pNext;
}
//如果pHead为NULL的时候,pPre就为最后一个节点了,但是链表已经反转完毕,pPre就是反转后链表的第一个节点
return pPre;
}
};
15、合并两个排序的链表
题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
解决代码:
(1)递归版本:
/*
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 == NULL)
return pHead2;
else if(pHead2 == NULL)
return pHead1;
ListNode* pHeadMerge = NULL;
if(pHead1->val < pHead2->val){
pHeadMerge = pHead1;
pHeadMerge->next = Merge(pHead1->next,pHead2);
}
else{
pHeadMerge = pHead2;
pHeadMerge->next = Merge(pHead1,pHead2->next);
}
return pHeadMerge;
}
};
(2)非递归版本
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
ListNode *head = new ListNode(0);
ListNode *p = head;
while (pHead1 && pHead2)
{
if (pHead1->val < pHead2->val)
{
p->next = pHead1;
pHead1 = pHead1->next;
}
else
{
p->next = pHead2;
pHead2 = pHead2->next;
}
p = p->next;
}
while (pHead1)
{
p->next = pHead1;
pHead1 = pHead1->next;
p = p->next;
}
while (pHead2)
{
p->next = pHead2;
pHead2 = pHead2->next;
p = p->next;
}
p = head->next;
delete head;
return p;
}
};
16、字符串最后一个单词的长度
题目:计算字符串最后一个单词的长度,单词以空格隔开。
解决代码:
#include <iostream>
#include <string>
using namespace std;
void lenLastWord(string s)
{
while(getline(cin, s)) {
cout << (s.size() - s.rfind(' ') - 1)<< endl;
}
}
int main()
{
string s;
lenLastWord(s);
return 0;
}
17、顺时针输出矩阵
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.
解决思路:就是一个从左到右,从上到下,从右向左,从下到上的循环思路
比较难的部分是怎么确定边界条件,用作判断
解决代码:
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> result;
if(matrix.empty())
return result;
//矩阵行数
int row = matrix.size();
//矩阵列数
int col = matrix[0].size();
//矩阵四角
int left = 0;
int right = col - 1;
int top = 0;
int botm = row - 1;
//循环
while((left <= right)&&(top <= botm)){
//第一步
for(int i = left; i <= right; i++){
result.push_back(matrix[top][i]);
}
//第二步
if(top < botm){
//第二步边界条件
for(int i = top + 1; i <= botm; i++){
result.push_back(matrix[i][right]);
}
}
//第三步
if((top < botm) && (left < right)){
//第三步边界条件
for(int i = right - 1; i >= left; i--){
result.push_back(matrix[botm][i]);
}
}
//第四步
if(top+1 < botm && left < right){
//第四步边界条件
for(int i = botm - 1; i >= top + 1; i--){
result.push_back(matrix[i][left]);
}
}
left++;
right--;
top++;
botm--;
}
return result;
}
};
18、包含min函数的栈
题目:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。
解决代码:一开始没有太理解题意,参考别人的代码
class Solution {
public:
stack<int> data; //数据栈
stack<int> mMin; //最小栈
//
void push(int value) {
data.push(value); //无论怎样,数据栈首先入栈
//当最小栈为空,或者要入的元素<最小栈栈顶,则给最小栈入栈
if(mMin.empty() || value<=mMin.top()){
mMin.push(value);
}
else{
//大于等于就入最小栈原来栈顶元素
mMin.push(mMin.top());
}
}
void pop() {
//保证两个栈都不为空
if(!data.empty() && !mMin.empty()){
data.pop();
mMin.pop();
}
}
//最小元素(直接就是最小栈的栈顶)
int top() {
return data.top();
}
int min() {
return mMin.top();
}
};
19、栈的压入、弹出序列
题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
解决思路:借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。
解决代码:
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.empty() || popV.empty()){
return false;
}
stack<int> s;
int j = 0;
for(int i = 0; i < pushV.size(); i++){
s.push(pushV[i]);
while(!s.empty() && s.top()==popV[j]){
s.pop();
j++;
}
}
if(s.empty()){
return true;
}
else{
return false;
}
}
};
20、复杂链表的复制
题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
这个自己没有什么思路,随机这个有点困难,记录一下大神的解题思路,自己也是要搞懂,借鉴的。
解题思路:(分为三步)
解决代码:
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
/*
1、复制每个节点,如:复制节点A得到A1,将A1插入节点A后面
2、遍历链表,A1->random = A->random->next;
3、将链表拆分成原链表和复制后的链表
*/
if(!pHead) return NULL;
RandomListNode *currNode = pHead;
while(currNode){
RandomListNode *node = new RandomListNode(currNode->label);
node->next = currNode->next;
currNode->next = node;
currNode = node->next;
}
currNode = pHead;
while(currNode){
RandomListNode *node = currNode->next;
if(currNode->random){
node->random = currNode->random->next;
}
currNode = node->next;
}
//拆分
RandomListNode *pCloneHead = pHead->next;
RandomListNode *tmp;
currNode = pHead;
while(currNode->next){
tmp = currNode->next;
currNode->next =tmp->next;
currNode = tmp;
}
return pCloneHead;
}
};
21、字符串的排序
题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母
解决思路:
链接:https://www.nowcoder.com/questionTerminal/fe6b651b66ae47d7acce78ffdd9a96c7
递归算法
* 对于无重复值的情况
* 固定第一个字符,递归取得首位后面的各种字符串组合;
* 再把第一个字符与后面每一个字符交换,并同样递归获得首位后面的字符串组合; *递归的出口,就是只剩一个字符的时候,递归的循环过程,就是从每个子串的第二个字符开始依次与第一个字符交换,然后继续处理子串。
*
* 假如有重复值呢?
* *由于全排列就是从第一个数字起,每个数分别与它后面的数字交换,我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。
* 例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。
* 但是对bab,第二个数和第三个数不 同,则需要交换,得到bba。
* 由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。
*
* 换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,
* 所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!
解决代码:
class Solution {
public:
vector<string> result;
vector<string> Permutation(string str) {
if(str.length()==0){
return result;
}
Permutation1(str,0);
sort(result.begin(),result.end());
return result;
}
void Permutation1(string str,int begin){
if(begin == str.length()){
result.push_back(str);
return ;
}
for(int i = begin; str[i]!='\0';i++){
if(i!=begin&&str[begin]==str[i]){
continue;
}
swap(str[begin],str[i]);
Permutation1(str,begin+1);
swap(str[begin],str[i]);
}
}
};
22、数组中出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解决思路:
先对数组中元素进行排序,如果存在数组中某一个数字大于数组大小一半的话,那么这个数字一定是数组的中位数,反之为0.但是算法时间复杂度偏高,为O(nlogn)。
解决代码:
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.empty())
return 0;
int length = numbers.size();
sort(numbers.begin(),numbers.end());
int middle = numbers[length/2];
int count = 0;
for(int i = 0; i < length; i++){
if(middle == numbers[i])
count++;
}
return count>length/2 ? middle : 0;
}
};
另一种算法复杂度为O(n)的解法
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
int n = numbers.size();
if (n == 0) return 0;
int num = numbers[0], count = 1;
for (int i = 1; i < n; i++) {
if (numbers[i] == num) count++;
else count--;
if (count == 0) {
num = numbers[i];
count = 1;
}
}
// Verifying
count = 0;
for (int i = 0; i < n; i++) {
if (numbers[i] == num) count++;
}
if (count * 2 > n) return num;
return 0;
}
};
23、最小的K个数
题目:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
解决思路:
(1)投机的简单方法,调用已经C++中vector容器中排序的方法,排序完直接输出前K个数
(2)冒泡排序,不过不用全排序,只需要排出前K个就行
解决代码:
(1)投机的简单方法
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int len = input.size();
vector<int> result;
if(len < k)
return 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) {
vector<int> result;
if (k > input.size()) {
return result;
}
for (int i = 0; i < k; i++) {
for (int j = 0; j < input.size() - i - 1; j++) {
if (input[j] < input[j + 1]) {
int temp = input[j];
input[j] = input[j + 1];
input[j + 1] = temp;
}
}
result.push_back(input[input.size() - i - 1]);
}
return result;
}
};
24、连续子数组的最大和
题目:HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
解决代码:
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
if(array.size() == 0){
return 0;
}
int maxNumArray = INT_MIN;
int currentNumArray = 0;
for(int i = 0; i < array.size(); i++){
if(currentNumArray < 0){
currentNumArray = array[i];
}
else{
currentNumArray += array[i];
}
maxNumArray = max(maxNumArray,currentNumArray);
}
return maxNumArray;
}
};
25、整数中1出现的次数(从1到n整数中1出现的次数)
题目:求出1到13的整数中1出现的次数,并算出100到1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
解决思路: n的个位、十位、百位、千位…都要进行判断。
解决代码:
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n)
{
int count = 0;
if(n < 1)
return 0;
for(int i = 1; i <= n; i++){
int temp = i;
while(temp){
if(temp%10 == 1)
count++;
temp /= 10;
}
}
return count;
}
};
26、把数组排成最小的数
题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
解决代码:
class Solution {
public:
string PrintMinNumber(vector<int> numbers) {
int len = numbers.size();
if(len == 0) return "";
sort(numbers.begin(), numbers.end(), cmp);
string res;
for(int i = 0; i < len; i++){
res += to_string(numbers[i]);
}
return res;
}
static bool cmp(int a, int b){
string A = to_string(a) + to_string(b);
string B = to_string(b) + to_string(a);
return A < B;
}
};
27、丑数
题目:把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
解决思路(来自牛客网,自己的代码运行超出内存限制):
链接:https://www.nowcoder.com/questionTerminal/6aa9e04fc3794f68acf8778237ba065b
来源:牛客网
首先从丑数的定义我们知道,一个丑数的因子只有2,3,5,那么丑数p = 2 ^ x * 3 ^ y * 5 ^ z,换句话说一个丑数一定由另一个丑数乘以2或者乘以3或者乘以5得到,那么我们从1开始乘以2,3,5,就得到2,3,5三个丑数,在从这三个丑数出发乘以2,3,5就得到4,6,10,6,9,15,10,15,25九个丑数,我们发现这种方***得到重复的丑数,而且我们题目要求第N个丑数,这样的方法得到的丑数也是无序的。那么我们可以维护三个队列:
(1)丑数数组: 1
乘以2的队列:2
乘以3的队列:3
乘以5的队列:5
选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(2)丑数数组:1,2
乘以2的队列:4
乘以3的队列:3,6
乘以5的队列:5,10
选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(3)丑数数组:1,2,3
乘以2的队列:4,6
乘以3的队列:6,9
乘以5的队列:5,10,15
选择三个队列头里最小的数4加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(4)丑数数组:1,2,3,4
乘以2的队列:6,8
乘以3的队列:6,9,12
乘以5的队列:5,10,15,20
选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(5)丑数数组:1,2,3,4,5
乘以2的队列:6,8,10,
乘以3的队列:6,9,12,15
乘以5的队列:10,15,20,25
选择三个队列头里最小的数6加入丑数数组,但我们发现,有两个队列头都为6,所以我们弹出两个队列头,同时将12,18,30放入三个队列;
……………………
疑问:
1.为什么分三个队列?
丑数数组里的数一定是有序的,因为我们是从丑数数组里的数乘以2,3,5选出的最小数,一定比以前未乘以2,3,5大,同时对于三个队列内部,按先后顺序乘以2,3,5分别放入,所以同一个队列内部也是有序的;
2.为什么比较三个队列头部最小的数放入丑数数组?
因为三个队列是有序的,所以取出三个头中最小的,等同于找到了三个队列所有数中最小的。
解决代码:
class Solution {
public://别人的代码就是精简,惭愧啊,继续学习。
int GetUglyNumber_Solution(int index) {
if (index < 7)return index;
vector<int> res(index);
res[0] = 1;
int t2 = 0, t3 = 0, t5 = 0, i;
for (i = 1; i < index; ++i)
{
res[i] = min(res[t2] * 2, min(res[t3] * 3, res[t5] * 5));
if (res[i] == res[t2] * 2)t2++;
if (res[i] == res[t3] * 3)t3++;
if (res[i] == res[t5] * 5)t5++;
}
return res[index - 1];
}
};
28、第一个只出现一次的字符
题目:在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
解决思路:
步骤一:都是字母,一定在ASCII码范围内,所以就直接定义一直字符数组,遍历整个字符串,当字符串中出现一个字母时候,就将该字符的ASCII位置的 0 加一;
步骤二:再次遍历字符串,直到字符串ASCII码对应的位置为1,则返回坐标,反之,返回0.
解决代码:
class Solution {
public:
int FirstNotRepeatingChar(string str) {
int len = str.length();
if(len == 0)
return -1;
char czeros[256] = {
0};
for(int i = 0; i < len; i++){
czeros[str[i]]++;
}
for(int i = 0; i < len; i++){
if(czeros[str[i]] == 1)
return i;
}
return 0;
}
};
29、数组中的逆序对
题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
示例1:
输入:1,2,3,4,5,6,7,0
输出:7
解决思路:
方法一:暴力循环法,但是这种算法复杂度太大,不推荐,而且牛客网上运行时间过不去
方法一代码:
class Solution {
public:
int InversePairs(vector<int> data) {
int P = 0;
for(int i = 0; i < data.size(); i++){
for(int j = i + 1; j < data.size(); j++){
if(data[i] > data[j]){
P++;
}
}
}
return P%1000000007;
}
};
方法二: (此方法来自与大神的分享,原链接为归并排序)
分治思想,采用归并排序的思路来处理,如下图,先分后治:
先把数组分解成两个长度为2的子数组,再把这两个子数组分解成两个长度为1的子数组。接下来一边合并相邻的子数组,一边统计逆序对的数目。在第一对长度为1的子数组{7}、{5}中7>5,因此(7,5)组成一个逆序对。同样在第二对长度为1的子数组{6},{4}中也有逆序对(6,4),由于已经统计了这两对子数组内部的逆序对,因此需要把这两对子数组进行排序,避免在之后的统计过程中重复统计。
逆序对的总数 = 左边数组中的逆序对的数量 + 右边数组中逆序对的数量 + 左右结合成新的顺序数组时中出现的逆序对的数量
- 这是一个归并排序的合并过程,主要是考虑合并两个有序序列时,计算逆序对数。
- 对于两个升序序列,设置两个下标:两个有序序列的末尾。每次比较两个末尾值,如果前末尾大于后末尾值,则有”后序列当前长度“个逆序对;否则不构成逆序对。然后把较大值拷贝到辅助数组的末尾,即最终要将两个有序序列合并到辅助数组并有序。
- 这样,每次在合并前,先递归地处理左半段、右半段,则左、右半段有序,且左右半段的逆序对数可得到,再计算左右半段合并时逆序对的个数。
方法二代码为:
class Solution {
public:
int InversePairs(vector<int> data) {
if(data.size() == 0){
return 0;
}
// 排序的辅助数组
vector<int> copy;
for(int i = 0; i < data.size(); ++i){
copy.push_back(data[i]);
}
return InversePairsCore(data, copy, 0, data.size() - 1) % 1000000007;
}
long InversePairsCore(vector<int> &data, vector<int> ©, int begin, int end){
// 如果指向相同位置,则没有逆序对。
if(begin == end){
copy[begin] = data[end];
return 0;
}
// 求中点
int mid = (end + begin) >> 1;
// 使data左半段有序,并返回左半段逆序对的数目
long leftCount = InversePairsCore(copy, data, begin, mid);
// 使data右半段有序,并返回右半段逆序对的数目
long rightCount = InversePairsCore(copy, data, mid + 1, end);
int i = mid; // i初始化为前半段最后一个数字的下标
int j = end; // j初始化为后半段最后一个数字的下标
int indexcopy = end; // 辅助数组复制的数组的最后一个数字的下标
long count = 0; // 计数,逆序对的个数,注意类型
while(i >= begin && j >= mid + 1){
if(data[i] > data[j]){
copy[indexcopy--] = data[i--];
count += j - mid;
}
else{
copy[indexcopy--] = data[j--];
}
}
for(;i >= begin; --i){
copy[indexcopy--] = data[i];
}
for(;j >= mid + 1; --j){
copy[indexcopy--] = data[j];
}
return leftCount + rightCount + count;
}
};
30、两个链表的第一个公共结点
题目:输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
解决思路:
因为是单链表,如果出现公共节点,则两个链表的尾部一定是一样的。
- 假定 List1长度: a+n List2 长度:b+n, 且 a<b
- 那么 p1 会先到链表尾部, 这时p2 走到 a+n位置,将p1换成List2头部
- 接着p2 再走b+n-(n+a) =b-a 步到链表尾部,这时p1也走到List2的b-a位置,还差a步就到可能的第一个公共节点。
- 将p2 换成 List1头部,p2走a步也到可能的第一个公共节点。如果恰好p1==p2,那么p1就是第一个公共节点。 或者p1和p2一起走n步到达列表尾部,二者没有公共节点,退出循环。 同理a>=b.
- 时间复杂度O(n+a+b)
解决代码:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
ListNode* p1 = pHead1;
ListNode* p2 = pHead2;
int len1 = 0;
int len2 = 0;
int diff = 0;
while(p1 != NULL){
p1 = p1->next;
len1 ++;
}
while(p2 != NULL){
p2 = p2->next;
len2 ++;
}
if(len1 > len2){
diff = len1 - len2;
p1 = pHead1;
p2 = pHead2;
}
else{
diff = len2 - len1;
p1 = pHead2;
p2 = pHead1;
}
for(int i = 0; i < diff; i++){
p1 = p1->next;
}
while(p1 != NULL && p2 != NULL){
if(p1 == p2){
break;
}
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
};
31、数字在排序数组中出现的次数
题目:统计一个数字在排序数组中出现的次数。
解题思路:
方法一:暴力遍历,不利用排序数组这个条件直接求,当数字为 k 时,计数加一即可,时间复杂度为O(n)
方法一解决代码:
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
int len = data.size();
int count = 0;
for(int i = 0; i < len; i++){
if(k == data[i]){
count ++;
}
}
return count;
}
};
方法二:
- 观察数组本身的特性可以发现,排序数组这样做没有充分利用数组的特性,可以使用二分查找,找出数据,然后进行左右进行统计
- 具体算法设计: 二分查找找到k的所在位置
在原数组里面分别左右对k的出现次数进行统计
方法二:解决代码
class Solution {
public:
int BinarySearch(vector<int> data, int low, int high, int k)
{
while (low<=high)
{
int m = (high + low) / 2;
if (data[m] == k)return m;
else if (data[m] < k) low = m+ 1;
else high = m - 1;
}
return -1;
}
int GetNumberOfK(vector<int> data ,int k) {
if(data.size()==0)return 0;
int len=data.size();
int KeyIndex=0;
KeyIndex=BinarySearch(data,0,len-1,k);
if(KeyIndex==-1) return 0;
int sumber=1;
int m=KeyIndex-1;
int n=KeyIndex+1;
while(m>=0&&data[m]==k){
m--;sumber++;
}
while(n<len&&data[n]==k){
n++; sumber++;
}
return sumber;
}
};
32、数组中只出现一次的数字
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
解决思路:
- 首先:位运算中异或的性质:两个相同数字异或=0,一个数和0异或还是它本身。
- 当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。
- 依照这个思路,我们来看两个数(我们假设是AB)出现一次的数组。我们首先还是先异或,剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。
解决代码:
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int* num2) {
if(data.size() < 2)
return;
int resultExclusiveOR = 0;
for(int i = 0; i < data.size(); i++){
resultExclusiveOR ^= data[i];
}
unsigned int indexOf1 = FindFirstBitIs1(resultExclusiveOR);
*num1 = *num2 = 0;
for(int j = 0; j < data.size(); j++){
if(IsBit1(data[j], indexOf1))
*num1 ^= data[j];
else
*num2 ^= data[j];
}
}
unsigned int FindFirstBitIs1(int num){
int indexBit = 0;
while(((num & 1) == 0) && (indexBit < 8*sizeof(int))){
num = num >> 1;
indexBit++;
}
return indexBit;
}
bool IsBit1(int num, unsigned int indexBit){
num = num >> indexBit;
return (num&1);
}
};
33、和为S的连续正数序列
题目:小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
解决思路:
- 题目求的是连续正数序列,而且至少含有两个数,那么我们可以从1,2这两个数开始,
- 以求和为9的所有连续序列为例,假设两个指针pSmall和pBig,分别指向正数序列的首尾,pSum表示序列之和,一开始pSmall=1,pBig=2,,pSum=3<9,序列需要包含更多的数,于是pBig+1,此时pSum=6,依旧小于9,于是pBig+1,此时pSum=10,大于9,序列需要删除一些数,于是pSmall-1,pSum=9,找到第一个满足条件的序列;接着pBig+1,按照前面的方法继续查找满足条件的序列,直到pSmall等于(s+1)/2.
解决代码:
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > res;
vector<int> sequence;
int pSmall = 1,pBig = 2;
int currentSum = pSmall + pBig;
int halfSum = (sum+1) / 2;
while(pSmall < halfSum){
if(currentSum == sum){
getSequence(sequence,pSmall,pBig);
res.push_back(sequence);
}
while(currentSum > sum && pSmall < pBig-1){
currentSum -= pSmall;
pSmall++;
if(currentSum == sum){
getSequence(sequence,pSmall,pBig);
res.push_back(sequence);
break;
}
}
pBig++;
currentSum += pBig;
}
return res;
}
void getSequence(vector<int> &sequence,int small,int big)
{
sequence.clear();
for(int i = small; i <= big; i++){
sequence.push_back(i);
}
}
};
34、和为S的两个数字
题目:输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。
解决思路:一开始被 如果有多对数字的和等于S,输出两个数的乘积最小的 给误导了,乘积最小,则外层乘积最小,证明如下:
假设:若b>a,且存在,
a + b = s;
(a - m ) + (b + m) = s
则:(a - m )(b + m)=ab - (b-a)m - m*m < ab;说明外层的乘积更小
也就是说依然是左右夹逼法!!!只需要2个指针
所以,只需要两个指针,一个指向头,一个指向尾部,和小的时候头++,大的时候尾部++即可。
解决代码:
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> res;
int length = array.size();
if (length <= 1)
return res;
int i = 0;
int j = length - 1;
int currentSum;
while (i < j)
{
currentSum = array[i] + array[j];
if (currentSum > sum)
j--;
else if (currentSum < sum)
i++;
else
{
res.push_back(array[i]);
res.push_back(array[j]);
break;
}
}
return res;
}
};
35、左旋转字符串
题目:汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
解决思路:
这题用最简单的思维是,发现新数组和n的规律,然后for循环输入新数组就行,算法复杂度为O(n)
当然还有其他的思路,请发动脑袋想一下。
解决代码:
class Solution {
public:
string LeftRotateString(string str, int n) {
string res = "";
int len = str.length();
for(int i = n; i < len; i++){
res.push_back(str[i]);
}
for(int i = 0; i < n; i++){
res.push_back(str[i]);
}
return res;
}
};
36、翻转单词顺序列
题目:牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
解决思路:先翻转单词,再翻转句子,调用的C++中的翻转程序,也可以自己重新写
解决代码:
class Solution {
public:
string ReverseSentence(string str) {
int len = str.length();
int start = 0;
for(int i = 0; i < len; i++){
if(str[i] == ' '){
reverse(str.begin()+start,str.begin()+i);
start = i + 1;
}
if(i == len-1){
reverse(str.begin()+start, str.end());
}
}
reverse(str.begin(),str.end());
return str;
}
};
37、扑克牌顺子
题目:LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
解决思路:
如果是顺子的话,那么 5 张牌最大数和最小数的差应该小于5,同时,每个数字只能出现一次,满足这种条件,则为顺子。
解决代码:
class Solution {
public:
bool IsContinuous( vector<int> numbers ) {
int count[14] = {
0};
int len = numbers.size();
int max = -1;
int min = 14;
if(len == 0){
return false;
}
for(int i = 0; i < len; i++){
count[numbers[i]]++;
if(numbers[i] == 0)
continue;
if(count[numbers[i]] > 1)
return false;
if(numbers[i] > max)
max = numbers[i];
if(numbers[i] < min)
min = numbers[i];
}
if(max - min < 5)
return true;
return false;
}
};
38、孩子们的游戏(圆圈中最后剩下的数)(标记一下)
题目:每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1
解决思路:
- 利用数学分析得到的解法
- f(n,m)=[f(n-1,m)+m]%n,其中f(n,m)为长度为n的删除第m个节点,最后剩下的数字
解决代码:
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if(n<=0 || m<=0)
return -1;
int last = 0;
for(int i = 2; i <= n; i++){
last = (last+m)%i;
}
return last;
}
};
39、求1+2+3+…+n
题目:求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
解决代码:
利用递归以及&&的短路原理
class Solution {
public:
int Sum_Solution(int n) {
int sum = n;
sum && (sum += Sum_Solution(n - 1));
return sum;
}
};
40、不用加减乘除做加法
题目:写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
解决思路:
- 1、按位与是查看两个数哪些二进制位都为1,这些都是进位位,结果需左移一位,表示进位后的结果
- 2、异或是查看两个数哪些二进制位只有一个为1,这些是非进位位,可以直接加、减,结果表示非进位位进行加操作后的结果
- 3、n1&n2是查看有没有进位位了,如果有,需要重复step1、step2;如果没有,保留n1、n2上二进制为1的部分,用或将之合为一个数,即为最后结果
解决代码:
class Solution {
public:
int Add(int num1, int num2)
{
int n1 = (num1 & num2) << 1;
int n2 = num1 ^ num2;
while(n1 & n2){
num1 = n1;
num2 = n2;
n1 = (num1 & num2) << 1;
n2 = num1 ^ num2;
}
return n1 | n2;
}
};
41、把字符串转换为整数
题目:将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述
输入一个字符串,包括数字字母符号,可以为空
输出描述
如果是合法的数值表达则返回该数字,否则返回0
示例1:
输入:
+2147483647
1a33
输出:
2147483647
0
解决代码(超出 int 范围的数无法表示):
class Solution {
public:
int StrToInt(string str) {
if(str.length() == 0)
return 0;
int i = 0;
int flag = 1;
int num = 0;
if(str[i] == ' '){
i++;
}
if(str[i] == '+'){
i++;
flag = 1;
}else if(str[i] == '-'){
i++;
flag = -1;
}
while(str[i] != '\0'){
if(str[i] >= '0' && str[i] <= '9'){
num = num * 10 + flag * (str[i] - '0');
}
else{
num = 0;
break;
}
i++;
}
return num;
}
};
42、数组中重复的数字
题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
解决思路:暴力遍历,算法复杂度较高,可以尝试其他方法,比如排序完再找
解决代码:
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication) {
if(length == 0)
return false;
for(int i = 0; i < length; i++){
for(int j = i+1; j < length; j++){
if(numbers[i] == numbers[j]){
*duplication = numbers[i];
return true;
}
}
}
return false;
}
};
43、构建乘积数组
题目:给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)
解决思路:
B[i] 的值可以看作下图每行元素相乘的结果;
解决代码:
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
vector<int> B;
int sz = A.size();
if(sz == 0)
return B;
B.push_back(1);
for(int i = 0; i < sz-1; i++)
B.push_back(B.back() * A[i]);
int tmp = 1;
for(int i = sz - 1; i >= 0; i--)
{
B[i] = B[i] * tmp;
tmp = tmp * A[i];
}
return B;
}
};
44、正则表达式匹配
题目:请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
解决思路和代码(参考牛客的第一个评论):
链接:https://www.nowcoder.com/questionTerminal/45327ae22b7b413ea21df13ee7d6429c
来源:牛客网
/*
解这题需要把题意仔细研究清楚,反正我试了好多次才明白的。
首先,考虑特殊情况:
1>两个字符串都为空,返回true
2>当第一个字符串不空,而第二个字符串空了,返回false(因为这样,就无法
匹配成功了,而如果第一个字符串空了,第二个字符串非空,还是可能匹配成
功的,比如第二个字符串是“a*a*a*a*”,由于‘*’之前的元素可以出现0次,
所以有可能匹配成功)
之后就开始匹配第一个字符,这里有两种可能:匹配成功或匹配失败。但考虑到pattern
下一个字符可能是‘*’, 这里我们分两种情况讨论:pattern下一个字符为‘*’或
不为‘*’:
1>pattern下一个字符不为‘*’:这种情况比较简单,直接匹配当前字符。如果
匹配成功,继续匹配下一个;如果匹配失败,直接返回false。注意这里的
“匹配成功”,除了两个字符相同的情况外,还有一种情况,就是pattern的
当前字符为‘.’,同时str的当前字符不为‘\0’。
2>pattern下一个字符为‘*’时,稍微复杂一些,因为‘*’可以代表0个或多个。
这里把这些情况都考虑到:
a>当‘*’匹配0个字符时,str当前字符不变,pattern当前字符后移两位,
跳过这个‘*’符号;
b>当‘*’匹配1个或多个时,str当前字符移向下一个,pattern当前字符
不变。(这里匹配1个或多个可以看成一种情况,因为:当匹配一个时,
由于str移到了下一个字符,而pattern字符不变,就回到了上边的情况a;
当匹配多于一个字符时,相当于从str的下一个字符继续开始匹配)
之后再写代码就很简单了。
*/
class Solution {
public:
bool match(char* str, char* pattern)
{
//(这题代码确实有点难)
if (*str == '\0' && *pattern == '\0')
return true;
if (*str != '\0' && *pattern == '\0')
return false;
//if the next character in pattern is not '*'
if (*(pattern+1) != '*')
{
if (*str == *pattern || (*str != '\0' && *pattern == '.'))
return match(str+1, pattern+1);
else
return false;
}
//if the next character is '*'
else
{
if (*str == *pattern || (*str != '\0' && *pattern == '.'))
return match(str, pattern+2) || match(str+1, pattern);
else
return match(str, pattern+2);
}
}
};
45、表示数值的字符串
题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
解决思路及代码:
class Solution {
public:
bool isNumeric(char* string)
{
//标记符号、小数点、e是否出现过
bool sign = false,decimal = false,hasE = false;
for(int i = 0; i < strlen(string); i++){
if(string[i] == 'E' || string[i] == 'e'){
//e后面一定要接数字
if(i == strlen(string)-1)
return false;
if(hasE)
return false;
hasE = true;
}
else if(string[i] == '+' || string[i] == '-'){
//第二次出现+-符号,则必须紧接在e之后
if(sign && string[i-1] != 'e' && string[i-1] != 'E')
return false;
// 第一次出现+-符号,且不是在字符串开头,则也必须紧接在e之后
if(!sign && i > 0 && string[i-1] != 'e' && string[i-1] != 'E')
return false;
sign = true;
}
else if(string[i] == '.'){
//e后面不能接小数点,小数点不能出现两次
if(hasE || decimal)
return false;
decimal = true;
}
else if(string[i] < '0' || string[i] > '9'){
return false;
}
}
return true;
}
};
46、字符流中第一个不重复的字符
题目:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。
解决代码:
class Solution
{
public:
//Insert one char from stringstream
vector<char> v;
void Insert(char ch)
{
v.push_back(ch);
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce()
{
int count[255] = {
0};
for(int i = 0; i < v.size(); i++){
if(!count[v[i]]){
count[v[i]] = 1;
}
else{
count[v[i]] = 2;
}
}
for(int i = 0; i < v.size(); i++){
if(count[v[i]] == 1)
return v[i];
}
return '#';
}
};
47、链表中环的入口结点
题目:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
解决思路:
假设有两个指针,一个fast指针,一个slow指针,每次走两步,一个slow指针,每次走一步,当fast指针与slow指针相遇时,假设fast指针走了2x,那么slow指针走了x,由于有环,那么为了便于理解,分为两种情况
- 第一种情况
(1)当fast指针仅仅只比slow指针多走一个环,如图所示
(2)第一次相遇的时候,如图
(3)这个时候将fast 重新赋值为开头,如图
(4)再走两次,则找到了环的入口结点
所以思路为:
a、第一步,找环中相汇点。分别用fast,slow指向链表头部,slow每次走一步,fast每次走二步,直到fast= =slow找到在环中的相汇点。
b、第二步,找环的入口。接上步,当fast==slow时,fast所经过节点数为2x,slow所经过节点数为x,设环中有n个节点,fast比slow多走一圈有2x=n+x; n=x;
可以看出slow实际走了一个环的步数,再让fast指向链表头部,slow位置不变。
假设链表开头到环接口的距离是y,如下图所示,那么x-y表示slow指针走过的除链表开头y在环中走过的距离,那么slow再走y步,此时fast结点与slow结点相遇,fast == slow ,x-y+y=x = n,即此时slow指向环的入口。
- 第二种情况:当fast比slow 多走n个环
所以,思路为:
a、第一步,找环中相汇点。分别用fast,slow指向链表头部,slow每次走一步,fast每次走二步,直到fast= =slow找到在环中的相汇点。
b、第二步,找环的入口。接上步,当fast==slow时,fast所经过节点数为2x,slow所经过节点数为x,设环中有n个节点,fast比slow多走r圈有2x=rn+x; x=rn;(r为圈数,n为一圈的结点数)
可以看出slow实际走了多个环的步数,再让fast指向链表头部,slow位置不变。
假设链表开头到环接口的距离是y,那么x-y表示slow指针走过的除链表开头y在环中走过的距离,那么slow再走y步,此时fast结点与slow结点相遇,fast == slow ,x-y+y=x = rn,即此时slow指向环的入口。(原文连接)
解决代码:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
while(pFast!=NULL && pFast->next!=NULL){
pFast = pFast->next->next;
pSlow = pSlow->next;
//当快指针和慢指针相遇时
if(pFast == pSlow){
pFast = pHead;
//再次相遇
while(pFast != pSlow){
pFast = pFast->next;
pSlow = pSlow->next;
}
return pFast;
}
}
return NULL;
}
};
48、删除链表中重复的节点
题目:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
解决思路及代码:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
ListNode* pre = NULL;
ListNode* p = pHead;
ListNode* q = NULL;
while(p != NULL){
//当前结点p,(其实是p指向当前结点),与它下一个结点p->next的val相同,说明要删掉有这个val的所有结点
if(p->next != NULL && p->next->val == p->val){
q = p->next;
//找到q,它指向最后一个与p val相同的结点,那p 到 q (包含) 都是要删除的
while(q != NULL && q->next != NULL && q->next->val == p->val){
q = q->next;
}
//如果p指向链表中第一个元素,p -> ... -> q ->... , 要删除p到q, 将指向链表第一个元素的指针pHead指向q->next。
if(p == pHead){
pHead = q->next;
}
//如果p不指向链表中第一个元素,pre -> p ->...->q ->... ,要删除p到q,即pre->next = q->next
else{
pre->next = q->next;
}
//当前处理的p要向链表尾部移动
p = q->next;
}
else{
pre = p;
p = p->next;
}
}
return pHead;
}
};
49、数据流中的中位数
题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
解决代码一:
直接利用算法进行排序,然后直接输出
class Solution {
public:
vector<int> v;
void Insert(int num)
{
v.push_back(num);
}
double GetMedian()
{
sort(v.begin(),v.end());
int len = v.size();
double midNum = 0.0;
int mid = len / 2;
if(len % 2 == 0){
midNum = double(double((v[mid] + v[mid-1])) / 2);
}
else{
midNum = double(v[mid]);
}
return midNum;
}
};
解决代码二:(这个要细看)
class Solution {
private:
vector<int> min;
vector<int> max;
public:
void Insert(int num)
{
int size=min.size()+max.size();
if((size&1)==0)
{
if(max.size()>0 && num<max[0])
{
max.push_back(num);
push_heap(max.begin(),max.end(),less<int>());
num=max[0];
pop_heap(max.begin(),max.end(),less<int>());
max.pop_back();
}
min.push_back(num);
push_heap(min.begin(),min.end(),greater<int>());
}
else
{
if(min.size()>0 && num>min[0])
{
min.push_back(num);
push_heap(min.begin(),min.end(),greater<int>());
num=min[0];
pop_heap(min.begin(),min.end(),greater<int>());
min.pop_back();
}
max.push_back(num);
push_heap(max.begin(),max.end(),less<int>());
}
}
double GetMedian()
{
int size=min.size()+max.size();
if(size<=0)
return 0;
if((size&1)==0)
return (max[0]+min[0])/2.0;
else
return min[0];
}
};
50、滑动窗口的最大值
题目:给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
解决代码:
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> max;
if(num.empty() || size > num.size() || size < 1)
return max;
int m;
for(int i = 0; i < num.size() - size + 1; i++)
{
m = num[i];
for(int j = i + 1; j < i + size; j++)
{
if(num[j] > m)
{
m = num[j];
}
}
max.push_back(m);
}
return max;
}
};
51、矩阵中的路径
题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
这题标记一下,有点困难,慢慢再看
解决思路:
分析:回溯算法
这是一个可以用回朔法解决的典型题。首先,在矩阵中任选一个格子作为路径的起点。如果路径上的第i个字符不是ch,那么这个格子不可能处在路径上的
第i个位置。如果路径上的第i个字符正好是ch,那么往相邻的格子寻找路径上的第i+1个字符。除在矩阵边界上的格子之外,其他格子都有4个相邻的格子。
重复这个过程直到路径上的所有字符都在矩阵中找到相应的位置。
由于回朔法的递归特性,路径可以被开成一个栈。当在矩阵中定位了路径中前n个字符的位置之后,在与第n个字符对应的格子的周围都没有找到第n+1个
字符,这个时候只要在路径上回到第n-1个字符,重新定位第n个字符。
由于路径不能重复进入矩阵的格子,还需要定义和字符矩阵大小一样的布尔值矩阵,用来标识路径是否已经进入每个格子。 当矩阵中坐标为(row,col)的
格子和路径字符串中相应的字符一样时,从4个相邻的格子(row,col-1),(row-1,col),(row,col+1)以及(row+1,col)中去定位路径字符串中下一个字符
如果4个相邻的格子都没有匹配字符串中下一个的字符,表明当前路径字符串中字符在矩阵中的定位不正确,我们需要回到前一个,然后重新定位。
一直重复这个过程,直到路径字符串上所有字符都在矩阵中找到合适的位置
解决代码:
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;
}
};
52、机器人的运动范围
题目:地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
解决思路:
- 回溯法
核心思路:
1.从(0,0)开始走,每成功走一步标记当前位置为true,然后从当前位置往四个方向探索,
返回1 + 4 个方向的探索值之和。
2.探索时,判断当前节点是否可达的标准为:
1)当前节点在矩阵内;
2)当前节点未被访问过;
3)当前节点满足limit限制。
解决代码:
class Solution {
public:
int movingCount(int threshold, int rows, int cols)
{
bool *flag = new bool[rows * cols];
for(int i = 0; i < rows * cols; i++)
flag[i] = false;
int count = moving(threshold, rows, cols, 0, 0, flag);
return count;
}
int moving(int threshold, int rows, int cols, int i, int j, bool* flag)
{
int count = 0;
if(i >= 0 && i < rows && j >= 0 && j < cols && getsum(i) + getsum(j) <= threshold && flag[i * cols + j] == false)
{
flag[i * cols + j] = true;
count =1 + moving(threshold, rows, cols, i + 1, j, flag)
+ moving(threshold, rows, cols, i - 1, j, flag)
+ moving(threshold, rows, cols, i , j - 1, flag)
+ moving(threshold, rows, cols, i, j + 1, flag);
}
return count;
}
int getsum(int num)
{
int sum = 0;
while(num)
{
sum += num % 10;
num /= 10;
}
return sum;
}
};
53、剪绳子
题目:给你一根长度为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。
输入描述:
输入一个数n,意义见题面。(2 <= n <= 60)
输出描述:
输出答案。
示例:
输入:
8
输出:
18
解决思路:(来源牛客官网题解)
方法:(1)暴力递归
链接:https://www.nowcoder.com/questionTerminal/57d85990ba5b440ab888fc72b0751bf8?answerType=1&f=discussion
来源:牛客网
暴力递归就要想到递归三部曲:
递归函数的设计和功能:back_track(n); 含义是:求长度为n的数,最后分段后的最大乘积,这里我们不需要关心分成多少段
递归函数的终止条件: 如果n <= 4, 显然back_track(n) = n,初始条件也就是我们不用计算就能得到的。
下一步递归:对于长度n,我们需要减少递归参数n,如果第一段为1, 显然下一步递归为back_track(n-1),如果第一段为2, 则下一步递归为
back_track(n-2)...因为要至少分2段,所以,最后一次可能的情况为最后一段为n-1, 下一步递归为back_track(1),因此,每一步可能的结果为
1 * back_track(n-1), 2 * back_track(n-2), ..., (n-1) * back_track(1),在n-1种情况中取一个最大值即可。 这里我们不用关系back_track(n-1)等的值为多少,因为最终会递归到我们的终止条件,因此绝对是可以求出来。
时间复杂度:O(n!)
空间复杂度:O(n), 最多分n段,每段长度为1, 所以递归深度为n
方法一:解决代码:
class Solution {
public:
int back_track(int n) {
// n <= 4, 表明不分,长度是最大的
if (n <= 4) {
return n;
}
int ret = 0;
for (int i = 1; i < n; ++i) {
ret = max(ret, i * back_track(n - i));
}
return ret;
}
int cutRope(int number) {
// number = 2 和 3 时,分 2 段和分 1 段的结果是不一样的,所以需要特判一下
if (number == 2) {
return 1;
}
else if (number == 3) {
return 2;
}
return back_track(number);
}
};
方法(2):记忆化递归
根据方法一,假设求back_track(7),如下:
用f() 替代 back_track(),可知,红色的部分重复了。
因此,我们可以开一个数组,把计算过的结果存起来。
步骤如下:
初始化一个大小为 n+1 的数组,初始值为 -1 , 也可以-2, 反正是不可能得到的值
时间复杂度:O(n^2)
空间复杂度:O(n)
在方法一的代码上,记录一下,详细代码如下
方法二代码:
class Solution {
public:
int back_track(int n, vector<int> &mark) {
if (n <= 4) {
return n;
}
// 在方法一的基础上添加
if (mark[n] != -1) {
return mark[n];
}
int ret = 0;
for (int i = 1; i < n; ++i) {
ret = max(ret, i * back_track(n - i));
}
// 添加部分
return mark[n] = ret;
}
int cutRope(int number) {
if (number == 2) {
return 1;
}
else if (number == 3) {
return 2;
}
// 添加部分
vector<int> mark(number, -1);
return back_track(numberm, mark);
}
};
方法(3):动态规划
将方法二修改为迭代版本的动态规划
时间复杂度:O(n^2)
空间复杂度:O(n)
方法三代码:
class Solution {
public:
int cutRope(int number) {
if (number == 2) {
return 1;
}
else if (number == 3) {
return 2;
}
vector<int> f(number + 1, -1);
for (int i = 1; i <= 4; ++i) {
f[i] = i;
}
for (int i = 5; i <= number; ++i) {
for (int j = 1; j < i; ++j) {
f[i] = max(f[i], j * f[i - j]);
}
}
return f[number];
}
};
剑指offer,第一遍笔记完成,但是没有二叉树的,二叉树的专题题目见下一篇博客!!!