目录
一、数字拼接为最小的数
1.1 string的比大小
1.2 直接实现
1.3 封装到类中
二、丑数
2.1 直观做法
2.2 错误代码
2.3正确代码
2.4 错误代码及其原因查找
三、最长上升子序列
3.1 题意及分析
3.2 代码及解析
四、旅行家问题
4.1 题目描述
4.2 解法一、贪心算法
4.3 全排列解法
https://www.nowcoder.com/practice/8fecd3f8ba334add803bf2a06af1b993?tpId=13&tqId=11185&tPage=2&rp=2&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
字符串可以直接比大小,例如:
#include
#include
#include
#include
#include
using namespace std;
bool concat_compare(string a, string b){
return (a + b < b + a);
}
int main(){
string a = "11136";
string b = "222";
cout << "a:" + a << endl;
cout << "b:" + b << endl;
if (a < b)cout << "a> end;
return 0;
}
输出:
a:11136
b:222
a
如果直接对结果进行书写,是完全可以实现的,并且不会报错,
#include
#include
#include
#include
#include
using namespace std;
bool concat_compare(string a, string b){
return (a + b < b + a);
}
int main(){
vector result = { "3", "32", "321" }; //321323
sort(result.begin(), result.end(), concat_compare);
for (auto item : result){
cout << item;
}
cout << endl;
int end; cin >> end;
return 0;
}
不懂下面代码为什么会报错:
class Solution {
public:
bool concat_compare(string a, string b){
return (a + b < b + a);
}
string int_to_string(int num){
string result;
while (num){
result += num % 10 + '0';
num /= 10;
}
for (int idx = 0; idx < result.size() / 2 - 1; idx++){
swap(result[idx], result[result.size() - 1 - idx]);
}
return result;
}
string PrintMinNumber(vector numbers) {
vector str_numbers;
for (auto item : numbers){
str_numbers.push_back(int_to_string(item));
}
std::sort(str_numbers.begin(), str_numbers.end(), Solution::concat_compare);
string result;
for (auto item : str_numbers){
result += item;
}
return result;
}
};
报错:
错误 3 error C2780: “void std::sort(_RanIt,_RanIt)”: 应输入 2 个参数,却提供了 3 个
e:\工作盘\c_program\run\run\run.cpp 30 1 run
错误 2 error C3867: “Solution::concat_compare”: 函数调用缺少参数列表;请使用
“&Solution::concat_compare”创建指向成员的指针
e:\工作盘\c_program\run\run\run.cpp 30 1 run
更改方法:
在函数定义的bool前加上static
下面他人方法简洁且高效
class Solution {
public:
string PrintMinNumber(vector 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;
}
};
https://www.nowcoder.com/practice/6aa9e04fc3794f68acf8778237ba065b?tpId=13&tqId=11186&tPage=2&rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
只能被2,3,4整除的数被称为丑数,丑数包括1,问第idx大的丑数是多少。
最直观的做法就是下面这种,直接遍历所有的数,判断是否为丑数,如果为丑数则进行自增;
#include
#include
#include
#include
#include
using namespace std;
class Solution {
public:
bool if_ugly_num(int num){
if (num < 1)return false;
while (num != 1){
if (num % 2 != 0 && num % 3 != 0 && num % 5 != 0)
return false;
if (num % 2 == 0)num = num / 2;
if (num % 3 == 0)num = num / 3;
if (num % 5 == 0)num = num / 5;
}
return true;
}
int GetUglyNumber_Solution(int index) {
if (index < 1)return 0;
int num_of_ugly = 0;
int current_ugly_num;
for (int idx = 1;; idx++){
if (if_ugly_num(idx)){
num_of_ugly++;
if (num_of_ugly == index)
return idx;
}
}
}
};
int main(){
int a = 3;
Solution s1;
for (int idx = 1; idx < 20; idx++){
cout << s1.GetUglyNumber_Solution(idx) << " ";
}
int end; cin >> end;
return 0;
}
从小到大丑数为:
1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32
采用相对优化后的算法,依然可以实现
思路为,前面的丑数乘以2,3,5中,最小的丑数,即下一个丑数。思考,为什么此法行不通?同时此法算法复杂度较高。
#include
#include
#include
#include
#include
using namespace std;
class Solution {
public:
int GetUglyNumber_Solution(int index) {
if (index < 1)return 0;
if (index == 1)return 1;
vector ugly_num;
ugly_num.push_back(1);
for (int loc = 1; loc < index; loc++){
int min_plus2, min_plus3, min_plus5;
int last_ugly_num = ugly_num[ugly_num.size() - 1];
int max_loc = loc - 1;
while (max_loc >= 0 && 2 * ugly_num[max_loc] >last_ugly_num){
min_plus2 = 2 * ugly_num[max_loc];
max_loc--;
}
max_loc = loc - 1;
while (max_loc >= 0 && 3 * ugly_num[max_loc]>last_ugly_num){
min_plus3 = 3 * ugly_num[max_loc];
max_loc--;
}
max_loc = loc - 1;
while (max_loc >= 0 && 5 * ugly_num[max_loc]>last_ugly_num){
min_plus5 = 5 * ugly_num[max_loc];
max_loc--;
}
int next_ugly_num = min(min_plus2, min_plus3);
next_ugly_num = min(next_ugly_num, min_plus5);
ugly_num.push_back(next_ugly_num);
}
return ugly_num[index - 1];
}
};
int main(){
int a = 3;
Solution s1;
for (int idx = 1; idx < 20; idx++){
cout << s1.GetUglyNumber_Solution(idx) << " ";
}
int end; cin >> end;
return 0;
}
只能通过86%左右。
用例:
1500
对应输出应该为:
859963392
你的输出为:
430467210
为什么这种方法会出现类似的错误后面我们会看出相应的问题出在哪。
此方法简洁且高效。相当与针对2,3,4最小的倍数,比大小,最小的存入ugly_num之中。
int GetUglyNumber_Solution(int index) {
if (index < 1)return 0;
vector ugly_num(index);
ugly_num[0] = 1;
int loc_plus2 = 0;
int loc_plus3 = 0;
int loc_plus5 = 0;
for (int loc = 1; loc < index; loc++){
ugly_num[loc] = min(ugly_num[loc_plus2] * 2,min( ugly_num[loc_plus3] * 3, ugly_num[loc_plus5] * 5));
if (ugly_num[loc] == ugly_num[loc_plus2] * 2)loc_plus2++;
if (ugly_num[loc] == ugly_num[loc_plus3] * 3)loc_plus3++;
if (ugly_num[loc] == ugly_num[loc_plus5] * 5)loc_plus5++;
}
return ugly_num[index - 1];
}
将两个代码进行对比,只要出现不一样的地方,则输出位置和对应的参数:
#include
#include
#include
#include
#include
using namespace std;
class Solution {
public:
int GetUglyNumber_Solution_error(int index) {
if (index < 1)return 0;
if (index == 1)return 1;
vector ugly_num;
ugly_num.push_back(1);
for (int loc = 1; loc < index; loc++){
int min_plus2, min_plus3, min_plus5;
int last_ugly_num = ugly_num[ugly_num.size() - 1];
int max_loc = loc - 1;
while (max_loc >= 0 && 2 * ugly_num[max_loc] >last_ugly_num){
min_plus2 = 2 * ugly_num[max_loc];
max_loc--;
}
max_loc = loc - 1;
while (max_loc >= 0 && 3 * ugly_num[max_loc]>last_ugly_num){
min_plus3 = 3 * ugly_num[max_loc];
max_loc--;
}
max_loc = loc - 1;
while (max_loc >= 0 && 5 * ugly_num[max_loc]>last_ugly_num){
min_plus5 = 5 * ugly_num[max_loc];
max_loc--;
}
int middle_min = min(min_plus2, min_plus3);
int next_ugly_num = min(middle_min, min_plus5);
ugly_num.push_back(next_ugly_num);
}
return ugly_num[index - 1];
}
int GetUglyNumber_Solution(int index) {
if (index < 1)return 0;
vector ugly_num(index);
ugly_num[0] = 1;
int loc_plus2 = 0;
int loc_plus3 = 0;
int loc_plus5 = 0;
for (int loc = 1; loc < index; loc++){
ugly_num[loc] = min(ugly_num[loc_plus2] * 2,min( ugly_num[loc_plus3] * 3, ugly_num[loc_plus5] * 5));
if (ugly_num[loc] == ugly_num[loc_plus2] * 2)loc_plus2++;
if (ugly_num[loc] == ugly_num[loc_plus3] * 3)loc_plus3++;
if (ugly_num[loc] == ugly_num[loc_plus5] * 5)loc_plus5++;
}
return ugly_num[index - 1];
}
};
int main(){
int a = 3;
Solution s1;
int index = 1356;
for (index = 1300; index < 1500; index++){
if (s1.GetUglyNumber_Solution(index) != s1.GetUglyNumber_Solution_error(index)){
cout << "location:" << index << ": ";
cout << s1.GetUglyNumber_Solution(index) << " ";
cout << s1.GetUglyNumber_Solution_error(index) << " ";
cout << endl;
}
}
int end; cin >> end;
return 0;
}
最终输出为:
location:1366: 432000000 430467210
location:1367: 437400000 430467210
location:1368: 439453125 430467210
location:1369: 442368000 430467210
location:1370: 442867500 430467210
location:1371: 447897600 430467210
location:1372: 450000000 430467210
location:1373: 452984832 430467210
location:1374: 453496320 430467210
location:1375: 455625000 430467210
我们看到,正确代码每次可以输出正确的丑数,但是错误代码无法输出正确的丑数,并且每次值都一样,430467210,可能与位数溢出有关。次数十进制位数已经达到9位数,非常容易溢出。数字自1346之后便一直保持在同一个值。
设置断点,看下1363之后的数是如何运算的结果发现,运算都不正确。搞不清楚到底为何?暂且放着往下进行。
OJ:https://leetcode-cn.com/problems/longest-increasing-subsequence/submissions/
longest increase subsequence(LIS)
即求解一个数列,最长的上升的子序列是多少。比如2,11,4,12,6,1的最长上升子序列是2,4,6或者2,11,12
动态规划相关问题。如果一个子序列是LIS,其右边的值A大于LIS中左右边的值,则这个值A可以加入其中。
例如 2,11,4,12,6,1中
相当于右边每加入一个元素,则可以append到左边比它小的元素后面。
class Solution
{
public:
int lengthOfLIS(vector &nums)
{
const int size = nums.size();
if (size < 1)
return 0;
int max_length = 1;
// lengthOfLISEndAtI[i]存储了:以nums[i]结尾的LIS的长度。
vector lengthOfLISEndAtI(size, 1);
for (int i = 1; i < size; i++)//最右边的元素i
{
for (int j = 0; j < i; j++)//i左边的元素j
{
// 找出那些在nums[i]左边且比nums[i]小的元素
if (nums[j] >= nums[i])
continue;
// 以nums[j]结尾的LIS与nums[i]组合,是否能产生更长的LIS(以nums[i]结尾)
if (lengthOfLISEndAtI[i] < lengthOfLISEndAtI[j] + 1)
{
lengthOfLISEndAtI[i] = lengthOfLISEndAtI[j] + 1;
}
}
// 以哪个元素结尾的LIS最长
if (max_length < lengthOfLISEndAtI[i])
{
max_length = lengthOfLISEndAtI[i];
}
}
return max_length;
}
};
对于此问题,完全可以采用更加简便的方法。动态规划加上二分搜索
#include
#include
#include
using namespace std;
class Solution {
public:
int lengthOfLIS(vector& nums) {
int length = nums.size();
if (length < 1) return 0;
vectorlis;
lis.push_back(nums[0]);
int lower = 0; int upper = 1;
for (int idx = 1; idx < length; idx++){
int current_num = nums[idx];
if (current_num>lis[lis.size() - 1]){
lis.push_back(current_num);
}
else{
lower = 0;
upper = lis.size()-1;
while (lower < upper){
if (current_num == 3)int b;
int mid = (upper + lower) / 2;
if (current_num>lis[mid])lower = mid+1;
else upper = mid;
if (current_num == 3)int a;
}
lis[lower] = current_num;
}
}
return lis.size();
}
};
int main(){
vectorgas = { 10, 9, 2, 5, 3, 7, 101, 18 }; //4
vector out_2 = { 10, 9, 2, 5, 3, 4 };//2
Solution s1;
cout << s1.lengthOfLIS(gas) << endl;
cout << s1.lengthOfLIS(out_2) << endl;
int end; cin >> end;
return 0;
}
旅行家问题,是典型的动态规划问题。
用户出行旅游通常都是一个闭环,即从某地出发游览各个景点,最终回到出发地。已知各个景点间的通行时间。请你设计一套算法,当用户给定出发地以及景点后,给出一条游览路线,使得用户能够不重复的游历每个景点并返回出发地的总通行时间最短,计算该最短通行时间。假设所有用户的出发地都是0
输入
第一行:出发地与景点的总个数 n
第二行:景点间的直达路线的个数m
其后m行:各个景点的通行时间a b t
表示a地与b地之间的通行时间是t。
输出
不重复游览完所有景点并返回出发地的最短游览时间T
若不存在这样的游览路线,返回-1
样例输入
4
6
0 1 4
0 2 3
0 3 1
1 2 1
1 3 2
2 3 5
样例输出
7
正确率只有63%
#include
#include
#include
#include
#include
#include
using namespace std;
int main(){
int n, lines; cin >> n >> lines;
vector each_row(n, -1);
vector> matrix(n, each_row);
for (int idx = 0; idx < lines; idx++){
int start, dest, cost; cin >> start >> dest >> cost;
matrix[start][dest] = cost;
matrix[dest][start] = cost;
}
int edge_count = 0;
int min;
int start_city = 0;
int next_city;
int final_length = 0;
vector reached(n, false);
reached[start_city] = true;
while (edge_count < n - 1){
min = 0x7fffffff;
for (int city_idx = 0; city_idx < n; city_idx++){
if (!reached[city_idx] && matrix[start_city][city_idx] != -1 && matrix[start_city][city_idx] < min){
next_city = city_idx;
min = matrix[start_city][city_idx];
}
}
if (next_city == start_city){
cout << -1 << endl;
return 0;
}
final_length += matrix[start_city][next_city];
edge_count++;
start_city = next_city;
reached[start_city] = true;
}
final_length += matrix[start_city][0];
cout << final_length << endl;
int ee; cin >> ee;
return 0;
}
生成全排列,然后统计出每个排列的cost。
#include
#include
#include
#include
#include
#include
using namespace std;
set> all_paths;
void all_perm(vector path, int idx){
int length = path.size();
all_paths.insert(path);
if (idx == length - 1){
return;
}
for (int change = idx; change < length; change++){
swap(path[change], path[idx]);
all_perm(path, idx + 1);
swap(path[change], path[idx]);
}
}
int main(){
int n, lines; cin >> n >> lines;
vector each_row(n, -1);
vector> matrix(n, each_row);
for (int idx = 0; idx < lines; idx++){
int start, dest, cost; cin >> start >> dest >> cost;
matrix[start][dest] = cost;
matrix[dest][start] = cost;
}
vector path;
for (int idx = 0; idx < n; idx++){
path.push_back(idx);
}
all_perm(path, 1);
bool exist = false;
int min_way = 0x7fffffff;
for (auto item : all_paths){
for (auto it : item){
cout << it << ',';
}
cout << endl;
int distance = 0;
bool done = true;
for (int idx = 0; idx < n - 1; idx++){
if (matrix[item[idx]][item[idx + 1]] == -1){
break;
done = false;
}
else{
distance += matrix[item[idx]][item[idx + 1]];
}
}
distance += matrix[item[n - 1]][0];
if (done){
exist = true;
min_way = min(min_way, distance);
}
}
if (exist)cout << min_way << endl;
else cout << -1 << endl;
int ee; cin >> ee;
return 0;
}
4.4 动态规划(待补充)
leetcode 64,OJ:
https://leetcode-cn.com/problems/minimum-path-sum/submissions/
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。每次只能向下或者向右移动一步。
思路:这种思路很简单,因为相应的路径只能从左边或者上边来,所以只要对比左边和上边最小的,加上本路径即可。
#include
#include
#include
using namespace std;
class Solution {
public:
int minPathSum(vector>& grid) {
int row = grid.size();
if (row < 1)return 0;
int col = grid[0].size();
if (col < 1)return 0;
for (int idx_r = 1; idx_r < row; idx_r++){
grid[idx_r][0] += grid[idx_r - 1][0];
}
for (int idx_c = 1; idx_c < col; idx_c++){
grid[0][idx_c] += grid[0][idx_c-1];
}
for (int idx_r = 1; idx_r < row; idx_r++){
for (int idx_c = 1; idx_c < col; idx_c++){
grid[idx_r][idx_c] += min(grid[idx_r-1][idx_c], grid[idx_r][idx_c-1]);
}
}
return grid[row-1][col-1];
}
};
int main(){
vector < vector < int >>grid = { { 1, 3, 1 },
{ 1, 5, 1 },
{4, 2, 1 }};
Solution s1;
cout << s1.minPathSum(grid) << endl;
int end; cin >> end;
return 0;
}