c++策略類O(n)編程問題彙總(撲克的順子|約瑟夫環|整數1出現的次數|股票最大利潤)

可參考內容:

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

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;
	}
};

 

二、約瑟夫環

環狀鏈表的最後一個數字,經典的約瑟夫環的問題。

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

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;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章