0%

计算器

题目

- leetcode 224. 基本计算器

逆波兰表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
class Solution {
public:

//词素
struct morpheme
{
long digital;
char ops;
int type;
int priority = -1;

morpheme(int t, int d, char o, int p)
{
type = t;
digital = d;
ops = o;
priority = p;
}

morpheme()
{
}
};

string skipspace(string s)
{
string ans;

for (int i = 0; i < s.size(); i++) {
char c = s[i];
if (c != ' ') {
ans.push_back(c);
}
}
return ans;
}
void scanning(const string str, vector<morpheme>& v)
{
char morphemetype[] = { ')','+','-','*','/','(' };
int priority[] = { 0,1,1,3,3,5 };
int morphemetypelength = 6;

long digit = 0;
int len = str.length();
for (int i = 0; i < len; i++)
{
char c = str[i];
if (c == ' ')
{
continue;
}
bool flag = false;
for (int j = 0; j < morphemetypelength; j++)
{
if (c == morphemetype[j])
{
v.push_back(morpheme(j, 0, c, priority[j]));
flag = true;
}
}

if (flag == false)
{
digit = digit * 10 + (int)c - 0x30;

if (i == len - 1 || (str[i + 1] < '0' || str[i + 1] > '9'))
{
v.push_back(morpheme(-1, digit, '\0', -1));
digit = 0;
}
}
}
}

vector<morpheme> rpn(vector<morpheme>& v)
{
stack<morpheme> r;
stack<morpheme> opera;
for (int i = 0; i < v.size(); i++)
{
morpheme m = v[i];
if (m.type == -1)
{
r.push(m);
}
else if (m.priority == 5)
{
opera.push(m);
}
else
{
while (!opera.empty() && opera.top().priority != 5 && opera.top().priority >= m.priority)
{
r.push(opera.top());
opera.pop();
}

if (!opera.empty() && opera.top().priority == 5 && m.priority == 0)
{
opera.pop();
}
if(m.priority != 0)
{
opera.push(m);
}
}
}

while (!opera.empty())
{
r.push(opera.top());
opera.pop();
}

vector<morpheme> vv(r.size());
for (int i = r.size() - 1; i >= 0; i--)
{
vv[i] = r.top();
r.pop();
}

return vv;
}

long calcu(vector<morpheme>& rpn)
{
stack<morpheme> s;
for (int i = 0; i < rpn.size(); i++)
{
morpheme m = rpn[i];
if (m.type == -1)
{
s.push(m);
}
else
{
long left, right;
right = s.top().digital;
s.pop();
left = s.top().digital;
s.pop();
long value = 0;
if (m.type == 1)
{
value = left + right;
}
else if (m.type == 2)
{
value = left - right;
}
else if (m.type == 3)
{
value = left * right;
}
else if (m.type == 4)
{
value = left / right;
}
s.push(morpheme(-1,value,'\0',-1));
}
}

return s.top().digital;
}
int calculate(string s) {
string str = s;
str = skipspace(str);
vector<morpheme> in;
scanning(str, in);
vector<morpheme> out;
out = rpn(in);
long result = calcu(out);
return result;
}
};
阅读全文 »

分支限界法

描述

  1. 基本思想 :
    分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。
    在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。 此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。
  2. 常见的两种分支限界法:
    (1)队列式(FIFO)分支限界法
    按照队列先进先出(FIFO)原则选取下一个节点为扩展节点。
    (2)优先队列式分支限界法
    按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。
  3. 分支限界法与回溯法的不同:
    (1)求解目标:回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。
    (2)搜索方式的不同:回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。
  4. 解空间树的动态搜索
    (1)回溯求解0/1背包问题,虽剪枝减少了搜索空间,但整个搜索按深度优先机械进行,是盲目搜索(不可预测本结点以下的结点进行的如何)。
    (2)回溯求解TSP也是盲目的(虽有目标函数,也只有找到一个可行解后才有意义)
    (3)分支限界法首先确定一个合理的限界函数,并根据限界函数确定目标函数的界[down, up];然后按照广度优先策略遍历问题的解空间树,在某一分支上,依次搜索该结点的所有孩子结点,分别估算这些孩子结点的目标函数的可能取值(对最小化问题,估算结点的down,对最大化问题,估算结点的up)。如果某孩子结点的目标函数值超出目标函数的界,则将其丢弃(从此结点生成的解不会比目前已得的更好),否则入待处理表。
  5. 分支限界法的设计思路
    设求解最大化问题,解向量为X=(x1,…,xn),xi的取值范围为Si,|Si|=ri。在使用分支限界搜索问题的解空间树时,先根据限界函数估算目标函数的界[down, up],然后从根结点出发,扩展根结点的r1个孩子结点,从而构成分量x1的r1种可能的取值方式。
    对这r1个孩子结点分别估算可能的目标函数bound(x1),其含义:以该结点为根的子树所有可能的取值不大于bound(x1),即:
    bound(x1)≥bound(x1,x2)≥…≥ bound(x1,…,xn)
    若某孩子结点的目标函数值超出目标函数的下界,则将该孩子结点丢弃;否则,将该孩子结点保存在待处理结点表PT中。
    再取PT表中目标函数极大值结点作为扩展的根结点,重复上述。
    直到一个叶子结点时的可行解X=(x1,…,xn),及目标函数值bound(x1,…,xn)。

问题

1. 分支限界法之装载问题

2. 分支限界法之布线问题

3. 分支限界法之0/1背包问题

4. 分支限界法之旅行售货员问题

阅读全文 »

分治法

  • 描述

    1.将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
    2.对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
    3.1) 该问题的规模缩小到一定的程度就可以容易地解决
      2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
      3) 利用该问题分解出的子问题的解可以合并为该问题的解;
      4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
    
      第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
      第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;、
      第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。
      第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
    4.1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
      2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
      3 合并:将各个子问题的解合并为原问题的解。
    
  • 问题

    1)二分搜索
    2)大整数乘法
    3)Strassen矩阵乘法
    4)棋盘覆盖
    5)合并排序
    6)快速排序
    7)线性时间选择
    8)最接近点对问题
    9)循环赛日程表
    10)汉诺塔
    
阅读全文 »

动态规划(Dynamic Programming)

描述

  1. 每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划.
  2. 将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最
    一个子问题就是初始问题的解.
  3. 能采用动态规划求解的问题的一般要具有3个性质:
    (1) 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
    (2) 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
    (3) 有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
  4. 基本步骤
    (1) 分析最优解的性质,并刻画其结构特征。
    (2) 递归的定义最优解。
    (3) 以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值
    (4) 根据计算最优值时得到的信息,构造问题的最优解

公式

1.dp[i] = dp[i-1]+f(i)  
2.dp[i][j] = dp[i][k] + dp[k+1][j] + f(k)
3.

问题

1. 矩阵连乘

2. 走金字塔

3. 最长公共子序列(LCS)

4. 最长递增子序列(LIS)

阅读全文 »

正则表达式

题目

- 44. 通配符匹配

静态KMP算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
class Solution {
public:
vector<int> kmp(string p)
{
vector<int> next(p.length(),0);

for (int i = 0; i < p.length(); i++)
{
if (i == 0 || p[i - 1] == '*')
{
next[i] = i;
continue;
}
for (int j = i - 1; j >= 0; j--)
{
bool flag = true;
for (int k = j-1; k >= 0; k--)
{
if (p[k] == '*')
{
break;
}
else if (p[k] != p[i - (j - k)] && p[k] != '?' && p[i - (j - k)] != '?')
{
flag = false;
break;
}
}
if (flag == true)
{
next[i] = j;
break;
}
}
}
return next;
}
bool isMatch(string s, string p) {
if (p.empty())
{
if (s.empty())
{
return true;
}
else
{
return false;
}
}
vector<int> next = kmp(p);
int index = 0;
for (int i = 0; i < s.length();)
{
if (index == 0 && i > 0)
{
break;
}
else if (index != p.length() && p[index] == '?')
{
i++;
index++;
}
else if (index == p.length() && p[p.length() - 1] == '*')
{
break;
}
else if (index != p.length() && p[index] == '*')
{
index++;
}
else if (index != p.length() && s[i] == p[index])
{
i++;
index++;
}
else if (index != p.length() && index == next[index])
{
i++;
}
else
{
int ii;
int jj;
bool flag = false;
while (!flag)
{
ii = index;
if (index == p.length())
{
for (int j = ii - 1; j >= 0; j--)
{
bool flag = true;
for (int k = j - 1; k >= 0; k--)
{
if (p[k] == '*')
{
break;
}
else if (p[k] != p[ii - (j - k)] && p[k] != '?' && p[ii - (j - k)] != '?')
{
flag = false;
break;
}
}
if (flag == true)
{
index = j;
break;
}
}
}
else
{
index = next[index];
}
jj = index;
flag = true;
for (int k = jj - 1; k >= 0; k--)
{
if (p[k] == '*')
{
break;
}
if (p[k] != '?')
{
if (p[ii - jj + k] == '?')
{
if (p[k] != s[i - (jj - k)])
{
flag = false;
break;
}
}
}

if (k == 0 && i - (jj - k) > 0)
{
flag = false;
break;
}
}
}
}
}
while (p[index] == '*')
{
index++;
}
if (index == p.length())
{
return true;
}
else
{
return false;
}
}
};
阅读全文 »

回溯法

  • 描述

    1.概念
      回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
      回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
      许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
    2.思想策略
      在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。
      若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。
    3.特征:
      (1)针对所给问题,确定问题的解空间:首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
      (2)确定结点的扩展搜索规则
      (3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
    
  • 问题

    八皇后问题,
    图的着色问题,
    装载问题,
    批处理作业调度问题,
    再再论背包问题,
    最大团问题,
    连续邮资问题,
    符号三角形问题,
    
阅读全文 »