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

2.3 找出映射规律

剑指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出现的次数

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
#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(需要区分大小写)

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

5.2 哈希表

实现一个字符到数字的映射,直接用哈希表即可。特别的,针对字符的问题,只有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;
	}
};
  • 直接对字符进行[ ]取地址运算,相当于直接对字符的ASICII码进行操作。

c++实际应用汇总(一)ASICII码|存储|malloc与new|虚函数|类|静态变量|强制类型转换

c++策略类O(n)编程问题汇总(扑克的顺子|约瑟夫环|整数1出现的次数|股票最大利润)_第1张图片

针对字符的ASIC码的值,参见上面这篇。ASIC 码从小到大的排列顺序为,数字,大写,小写;符号穿插中间。一共128个。

例如,下面操作就能输出相应的值:

	int a = 'P';//P ASIC is 80
	cout << a << endl;
	char c = 0x50;// 0x50==80
	cout << c << endl;
/* 输出
80
P                     */

 

 

 

 

 

 

你可能感兴趣的:(c/c++,编程与算法)