可参考内容:
c++动态规划类算法编程汇总(一)背包问题|回溯法
c++动态规划类算法编程汇总(二)全排列| O(n)排序 | manacher法
c++策略类O(n)编程问题汇总(扑克的顺子|约瑟夫环|整数1出现的次数|股票最大利润)
目录
一、扑克牌的顺子
1.1 题干
1.2 解法
二、约瑟夫环
2.1 题干
2.2 解法
2.3 找出映射规律
三、股票的最大利润
四、整数中1出现的次数
4.1 题干
4.2 常规解法
4.3 按规律优化为O(n)的复杂度
oj:https://www.nowcoder.com/practice/762836f4d43d43ca9deb273b3de8e1f4?tpId=13&tqId=11198&tPage=3&rp=3&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking
一个数组,5个数字作为排号,可能出现从1-13,0值可以当作任意牌。如果出现顺子,比如12345,或者01345(0可以当作2补全)则返回true(能构成顺子)
较为简单,统计出来0的值,再统计出来gap,如果排中有重复的数字,则返回false
如果gap>0的个数,返回false,其他返回true,较简单
class Solution {
public:
bool IsContinuous(vector numbers) {
int size = numbers.size();
if (size != 5)return false;
sort(numbers.begin(),numbers.end());
int number_of_0 = 0;
int seq_num = 0;
int botton, top;
for (int idx = 0; idx < 5; idx++){
if (numbers[idx] == 0)number_of_0++;
else break;
}
int gap = 0;
for (int idx = number_of_0; idx < 4; idx++){
if (numbers[idx + 1] == numbers[idx])return false;
gap += numbers[idx + 1] - numbers[idx] - 1;
}
if (gap>number_of_0)return false;
else return true;
}
};
环状链表的最后一个数字,经典的约瑟夫环的问题。
oj:https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6?tpId=13&tqId=11199&tPage=3&rp=3&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking
数字0到n构成一个环,每次从第0个数到第m-1个,把m-1个删掉,再从第m个开始数。最终剩下一个数字,这个数字是多少?
常规解法是O(mn)的复杂度。
用双向链表list,可以方便的进行插入和删除。需要注意的点:
#include
#include
#include
#include
using namespace std;
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if (n < 1 || m<1)return -1;
list students;
for (int idx = 0; idx < n; idx++){
students.push_back(idx);
}
auto current = students.begin();
while (students.size() != 1){
int next_idx = (m-1)%students.size();
for (int idx = 0; idx < next_idx;idx++){
current++;
if (current == students.end())current = students.begin();
}
auto next = ++current ;
if (next == students.end())next = students.begin();
current--;
students.erase(current);
current = next;
}
return *(students.begin());
}
};
int main(){
Solution s1;
cout << s1.LastRemaining_Solution(5, 3) << endl;
int end; cin >> end;
return 0;
}
剑指offer P322
我们采用与之类似的思路,
股票价格按先后顺序,存于数组之中,如果卖出价格减去前面买入价格即为利润,问利润最大多少?
如果遍历,则复杂度O(n*n),复杂度较高。
可以将之前最低值存入min_value, 则目前减去前面最低值,则为当前卖出的最大利润。
#include
#include
#include
#include
using namespace std;
class Solution {
public:
int most_benefit(vector value){
int v_size = value.size();
if (v_size < 2)return 0;
int current_min = value[0];
int max_benefit = 0x80000000;
for (auto item : value){
int benefit = item - current_min;
if (benefit>max_benefit)max_benefit = benefit;
if (item < current_min)current_min = item;
}
return max_benefit;
}
};
int main(){
Solution s1;
vector value1 = { 7, 1, 5, 3, 6, 4 };//output 5
vector value2 = { 7, 6, 4, 3, 1 };//output 0
cout << s1.most_benefit(value1) << endl;
cout << s1.most_benefit(value2) << endl;
int end; cin >> end;
return 0;
}
1,2,3 ...到n,几个数字,这些数字中1出现了几次?
输入n,输出1,2,3...n中1出现的次数。
统计出每个数字中1出现的次数,算法复杂度 n*logn显然不是最优:
//代码简单但是算法复杂度较高,需要n*logn的算法复杂度
int NumberOf1Between1AndN_Solution(int n)
{
int times_one = 0;
for (int current_num = 1; current_num <= n; current_num++){
int calcu_num = current_num;
while (calcu_num>0){
if (calcu_num % 10 == 1)times_one++;
calcu_num = calcu_num / 10;
}
}
return times_one;
}
分而治之,依次统计出个位,十位,百位...的1出现的次数。相比剑指offer给出的递归的方法,更加节省内存。
我们拿 21345来举例,每位上出现1的次数:
个位出现了 (2134+1)*1 次
十位出现了 (213 +1)*10 次
百位出现了 (21+1)*100 次
千位出现了 (2+0)*1000+345+1 次
万位出现了 (0+1)*10000 次
归纳一下规律,就是:
#include
#include
#include
#include
using namespace std;
class Solution {
public:
//代码简单但是算法复杂度较高,需要n*logn的算法复杂度
int NumberOf1Between1AndN_Solution(int n)
{
int times_one = 0;
for (int current_num = 1; current_num <= n; current_num++){
int calcu_num = current_num;
while (calcu_num>0){
if (calcu_num % 10 == 1)times_one++;
calcu_num = calcu_num / 10;
}
}
return times_one;
}
// 算法复杂度较低,只有logn的算法复杂度
int easy_logn_NumberOf1Between1AndN(int n){
int pow_of_10 = 0;
int times_one = 0;
int current_decimal_pow = 1; // 1,10,100
while (n / current_decimal_pow){
int upper_pow = current_decimal_pow * 10; //10
int current_decimal = (n / current_decimal_pow) % 10;
if (current_decimal == 0){
times_one += n / upper_pow * current_decimal_pow;
}
else if (current_decimal == 1){
times_one += n / upper_pow*current_decimal_pow;
times_one += n % current_decimal_pow + 1;
}
else{
times_one += (n / upper_pow + 1)*current_decimal_pow;
}
current_decimal_pow = upper_pow;
}
return times_one;
}
};
int main(){
Solution s1;
bool error = false;
for (int idx = 198; idx < 200; idx++){
if (s1.NumberOf1Between1AndN_Solution(idx) != s1.easy_logn_NumberOf1Between1AndN(idx))
error = true;
}
if (error)cout << "Error!" << endl;
int end; cin >> end;
return 0;
}
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)
直观的想到,拿一个字符与其他字符进行对比,如果重复,则直接break,不重复则输出。想法简单,但是算法复杂度较高。因此需要用其他更简单的方法。
class Solution {
public:
int FirstNotRepeatingChar(string str) {
int loc_idx=0;
while(str[loc_idx]!='\0'){
for(int search_loc=0;str[search_loc]!='\0';search_loc++){
if(str[loc_idx]==str[search_loc]&&loc_idx!=search_loc){
break;
}
if(str[search_loc+1]=='\0')
return loc_idx;
}
loc_idx++;
}
return -1;
}
};
实现一个字符到数字的映射,直接用哈希表即可。特别的,针对字符的问题,只有char型的2^8=256的256种ASIC||码的值。因此更加简单。
class Solution {
public:
int FirstNotRepeatingChar(string str) {
int length = str.size();
vector hash_map(256,0);
for (int idx = 0; idx < length; idx++){
hash_map[str[idx]]++;
}
for (int idx = 0; idx < length; idx++){
if (hash_map[str[idx]] == 1){
return idx;
}
}
return -1;
}
};
c++实际应用汇总(一)ASICII码|存储|malloc与new|虚函数|类|静态变量|强制类型转换
针对字符的ASIC码的值,参见上面这篇。ASIC 码从小到大的排列顺序为,数字,大写,小写;符号穿插中间。一共128个。
例如,下面操作就能输出相应的值:
int a = 'P';//P ASIC is 80
cout << a << endl;
char c = 0x50;// 0x50==80
cout << c << endl;
/* 输出
80
P */