可參考內容:
c++動態規劃類算法編程彙總(二)全排列| O(n)排序 | manacher法
c++策略類O(n)編程問題彙總(撲克的順子|約瑟夫環|整數1出現的次數|股票最大利潤)
目錄
一、撲克牌的順子
1.1 題幹
一個數組,5個數字作爲排號,可能出現從1-13,0值可以當作任意牌。如果出現順子,比如12345,或者01345(0可以當作2補全)則返回true(能構成順子)
1.2 解法
較爲簡單,統計出來0的值,再統計出來gap,如果排中有重複的數字,則返回false
如果gap>0的個數,返回false,其他返回true,較簡單
class Solution {
public:
bool IsContinuous(vector<int> 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;
}
};
二、約瑟夫環
環狀鏈表的最後一個數字,經典的約瑟夫環的問題。
2.1 題幹
數字0到n構成一個環,每次從第0個數到第m-1個,把m-1個刪掉,再從第m個開始數。最終剩下一個數字,這個數字是多少?
2.2 解法
常規解法是O(mn)的複雜度。
用雙向鏈表list,可以方便的進行插入和刪除。需要注意的點:
- list.end()的時候,相當於NULL,即比list中最後一個元素還往後一位
- list對iterator進行erase的時候,erase之後不能進行++或者--了,因爲這個元素已經不存在了,所以需要用next來存iterator++
- 當前數到m個,相當於往後移動m-1位
- list中與vector不同,不能用iterator+1,只能++或者--表示前移動或者後移動
#include<list>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if (n < 1 || m<1)return -1;
list<int> 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;
}
2.3 找出映射規律
劍指offer P322
我們採用與之類似的思路,
三、股票的最大利潤
股票價格按先後順序,存於數組之中,如果賣出價格減去前面買入價格即爲利潤,問利潤最大多少?
如果遍歷,則複雜度O(n*n),複雜度較高。
可以將之前最低值存入min_value, 則目前減去前面最低值,則爲當前賣出的最大利潤。
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
class Solution {
public:
int most_benefit(vector<int> 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<int> value1 = { 7, 1, 5, 3, 6, 4 };//output 5
vector<int> 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出現的次數
4.1 題幹
1,2,3 ...到n,幾個數字,這些數字中1出現了幾次?
輸入n,輸出1,2,3...n中1出現的次數。
4.2 常規解法
統計出每個數字中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;
}
4.3 按規律優化爲O(n)的複雜度
分而治之,依次統計出個位,十位,百位...的1出現的次數。相比劍指offer給出的遞歸的方法,更加節省內存。
我們拿 21345來舉例,每位上出現1的次數:
個位出現了 (2134+1)*1 次
十位出現了 (213 +1)*10 次
百位出現了 (21+1)*100 次
千位出現了 (2+0)*1000+345+1 次
萬位出現了 (0+1)*10000 次
歸納一下規律,就是:
#include<iostream>
#include<vector>
#include<string>
#include<cmath>
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;
}