Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 88da39b

Browse files
committed
新增堆栈部分练习
1 parent 7222ec6 commit 88da39b

File tree

14 files changed

+765
-52
lines changed

14 files changed

+765
-52
lines changed

‎README.md‎

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -84,27 +84,18 @@
8484

8585
## [哈希算法](./hash.md)
8686

87+
下面这四大基本算法,贪心,回溯,DP属于一类,这三个算法解决问题的模型都可以定义为"多阶段最优解问题"。
8788

88-
<!--# 解决多阶段决策最优解模型的算法
89-
解决问题的过程中,需要经过多个决策阶段,每个决策都会对应一个状态。我们寻找一组决策序列,经过这组决策序列,能够产生最终期望的最优值。我们把这种问题模型称为<font size=5 color=red>多阶段决策最优解模型</font>. DP,回溯,贪心都可以解决这类问题.
89+
回溯算法属于万金油,基本能用DP和贪心解决的题目都能用回溯暴力解决,不过回溯算法穷举的做法使得它的复杂度非常高,是指数级别的,所以只能适用于小规模数据的问题。
9090

91-
利用动态规划解决的问题,需要满足三个特征:
91+
DP比回溯更高效,但并不是所有问题都可以通过DP解决的。能用DP解决的问题,需要满足最优子结构,无后效性,重复子问题这三个特征。DP之所以如此高效,原因就在于它通过合并重复的状态,来达到剪枝的目的(比如矩阵从左上角移动到右下角,求最短路径这个题目,要么下一步向右走,要么下一步向下走,我们选择那条之前状态是最短路径的那条,这样我们就打到了剪枝的目的).
9292

93-
1. 最优子结构, 就是说后面的状态可以通过前面的状态推导出来
94-
2. 无后效性, 就是说一旦状态确定,就不会更改
95-
3. 重复子问题, 就是说不同的决策序列,到达某个相同的阶段时,可能会产生不同的状态。
9693

97-
贪心算法实际上是DP的一种特殊情况,它能解决的问题更加有限。需要满足三个条件:
94+
贪心算法能解决的问题更加局限。需要满足三个条件,最优子结构,无后效性和贪心选择性,即通过局部最优可以产生全局的最优选择。
9895

99-
1. 最优子结构
100-
2. 无后效性
101-
3. 贪心选择性, 意思就是局部最优选择,能产生全局最优选择。
10296

103-
所以贪心算法能否解决算法问题的关键在于: 局部最优能不能达到全局最优?
10497

10598

106-
回溯算法是"万金油"。基本上贪心和dp能解决的问题,回溯都能解决。回溯相当于穷举搜索,列举出所有的情况,然后对比得到最优解。不过回溯的复杂度一般都是指数级的,只能用来解决小规模数据的问题。-->
107-
10899

109100
## [贪心](./greed.md)
110101
## [回溯算法](./backtracking.md)

‎alg-cpp.xcodeproj/project.pbxproj‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@
185185
0925CDE722F67BB100DB3191 /* Permutation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Permutation.h; sourceTree = "<group>"; };
186186
0929C38122FE7804006905B4 /* deleteDuplicates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = deleteDuplicates.h; sourceTree = "<group>"; };
187187
0929C38422FE8DFE006905B4 /* getIntersectionNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = getIntersectionNode.h; sourceTree = "<group>"; };
188+
092A72A02358BA02005B0DC2 /* hanoiProblem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hanoiProblem.h; sourceTree = "<group>"; };
188189
093D6BFB22FBA0D000771DEC /* pascals_triangle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pascals_triangle.h; sourceTree = "<group>"; };
189190
093D6BFE22FBA5D200771DEC /* pascals_triangle_ii.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pascals_triangle_ii.h; sourceTree = "<group>"; };
190191
093D6C0122FC704700771DEC /* best_time_to_buy_and_sell_stock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = best_time_to_buy_and_sell_stock.h; sourceTree = "<group>"; };
@@ -254,6 +255,7 @@
254255
099EAEF822FFC4C5006437BD /* palindrome_number.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = palindrome_number.h; sourceTree = "<group>"; };
255256
09A208B02308D96E00094088 /* zigzag_conversion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = zigzag_conversion.h; sourceTree = "<group>"; };
256257
09A208B32309990C00094088 /* string_to_integer_atoi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = string_to_integer_atoi.h; sourceTree = "<group>"; };
258+
09A7AC1D234E34EA00DFECF0 /* matrix_move.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = matrix_move.h; sourceTree = "<group>"; };
257259
09ABED832300E22800113589 /* romanToInt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = romanToInt.h; sourceTree = "<group>"; };
258260
09ABED872300EE0900113589 /* longestCommonPrefix.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = longestCommonPrefix.h; sourceTree = "<group>"; };
259261
09ACF02B2316DB0B004A4A61 /* divideandconquer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = divideandconquer; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -286,6 +288,10 @@
286288
09E18E5D22F58301002D0227 /* GetNumberOfK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GetNumberOfK.h; sourceTree = "<group>"; };
287289
09E18E6022F588E8002D0227 /* FindNumsAppearOnce.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FindNumsAppearOnce.h; sourceTree = "<group>"; };
288290
09E18E6422F59065002D0227 /* replaceSpace.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = replaceSpace.h; sourceTree = "<group>"; };
291+
09EF0F4C235F43A4008E90DC /* getMaxWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = getMaxWindow.h; sourceTree = "<group>"; };
292+
09F082AF235768BD000F84C0 /* sortStackByStack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sortStackByStack.h; sourceTree = "<group>"; };
293+
09F2A5E52354CF320026F4A2 /* TwoStacksQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TwoStacksQueue.h; sourceTree = "<group>"; };
294+
09F32137235614E5005E29E2 /* reverseStack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = reverseStack.h; sourceTree = "<group>"; };
289295
09F38FCD22FCE719001B4618 /* best_time_to_buy_and_sell_stock_ii.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = best_time_to_buy_and_sell_stock_ii.h; sourceTree = "<group>"; };
290296
09F38FD022FCEC64001B4618 /* twoSum_ii.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = twoSum_ii.h; sourceTree = "<group>"; };
291297
09F38FD322FCEF88001B4618 /* majorityElement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = majorityElement.h; sourceTree = "<group>"; };
@@ -716,6 +722,7 @@
716722
3A04E7432341F1B7006770AF /* double11advance.h */,
717723
094524BA234A32D000CCBFB9 /* levenshtein_distance.h */,
718724
09665E9C234CD7ED003D0FAE /* pascals_triangle.h */,
725+
09A7AC1D234E34EA00DFECF0 /* matrix_move.h */,
719726
);
720727
path = dp;
721728
sourceTree = "<group>";
@@ -826,6 +833,18 @@
826833
path = "coding-interviews";
827834
sourceTree = "<group>";
828835
};
836+
09F2A5E42354CF0D0026F4A2 /* itinterviews */ = {
837+
isa = PBXGroup;
838+
children = (
839+
09F2A5E52354CF320026F4A2 /* TwoStacksQueue.h */,
840+
09F32137235614E5005E29E2 /* reverseStack.h */,
841+
09F082AF235768BD000F84C0 /* sortStackByStack.h */,
842+
092A72A02358BA02005B0DC2 /* hanoiProblem.h */,
843+
09EF0F4C235F43A4008E90DC /* getMaxWindow.h */,
844+
);
845+
path = itinterviews;
846+
sourceTree = "<group>";
847+
};
829848
09FC391C2304D98D00F0A2AE /* easy */ = {
830849
isa = PBXGroup;
831850
children = (
@@ -889,6 +908,7 @@
889908
3A5C8B9522E0109200354740 /* stack+queue */ = {
890909
isa = PBXGroup;
891910
children = (
911+
09F2A5E42354CF0D0026F4A2 /* itinterviews */,
892912
0946B02122F6B49E0043469D /* coding-interviews */,
893913
3A5C8BA422E05ABF00354740 /* leetcode */,
894914
3A5C8B9622E0109200354740 /* main.cpp */,
19.7 KB
Binary file not shown.

‎dp.md‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,24 @@
1111
## 什么样的问题适合使用动态规划来解决?
1212

1313

14-
动态规划适合用于<font size=5 color=red>多阶段决策最优解模型</font>, 就是说动态规划一般是用来解决最优解问题,而解决问题的过程需要经历多个阶段,每个阶段决策之后会对应一组状态,然后我们寻找一组决策序列,经过这组决策序列,最终求出问题的最优解。
14+
step1: 首先看它是否属于<font size=5 color=red>多阶段决策最优解模型</font>,就是说解决问题的过程需要经历多个阶段,每个阶段决策之后会对应一组状态,然后我们寻找一组决策序列,经过这组决策序列,最终求出问题的最优解。
1515

16+
step2: 是否满足最优子结构,即是否能够从前面的状态推导出后面的状态。
17+
18+
step3: 是否满足无后效性,即前面状态一旦确定,就不会受之后阶段决策的影响。并且我们通过推到后面的状态的时候,只关心前面状态的值,而并不用关心它是怎么一步步推导出来的。
19+
20+
step4: 是否满足重复子问题,即当到达同一个阶段时是否存在多个状态.
21+
22+
23+
## 动态规划的解题思路
24+
25+
26+
关键点在于,<font size=5 color=red>如何定义状态,如何通过之前的状态推导出后面阶段的状态。</font>
27+
28+
对照着下面整个具体的例子,可以更好的去理解上面这些概念.
29+
30+
31+
<font size=5 color=red>[matrix_move](./dp/matrix_move.h)</font>
1632

1733

1834

@@ -24,6 +40,7 @@
2440
| 双11打折优惠问题 | [double11advance](./dp/double11advance.h)||
2541
| 硬币找零问题 | [coinChange2](./dp/coinChange.h)||
2642
| 杨辉三角变种 | [pascals_triangle](./dp/pascals_triangle.h)||
43+
| 矩阵从左上角移动到右下角 | [matrix_move](./dp/matrix_move.h)||
2744
| 莱文斯坦最短编辑距离 | -- ||
2845
| 两个字符串的最长公共子序列 | -- ||
2946
| 数据序列的最长递增子序列 | -- ||

‎dp/main.cpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "coinChange.h"
1515
#include "levenshtein_distance.h"
1616
#include "pascals_triangle.h"
17+
#include "matrix_move.h"
1718
int main(int argc, const char * argv[]) {
1819
// insert code here...
1920
using namespace std;
@@ -22,5 +23,6 @@ int main(int argc, const char * argv[]) {
2223
test_coinChange();
2324
test_levenshtein_distance();
2425
test_pascals_triangle();
26+
test_matrix_move();
2527
return 0;
2628
}

‎dp/matrix_move.h‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// matrix_move.h
3+
// dp
4+
//
5+
// Created by junl on 2019年10月9日.
6+
// Copyright © 2019 junl. All rights reserved.
7+
//
8+
9+
#ifndef matrix_move_hpp
10+
#define matrix_move_hpp
11+
12+
#include <stdio.h>
13+
#include <vector>
14+
/*
15+
和前面的杨辉三角变种有点类似:
16+
问题是有一个n*n的矩阵,矩阵存储的都是正整数,旗子最开始位于左上角,终止位置在右下角。中间经过的数字是它们的路径和,求最短的路径和.
17+
18+
1,3,5,9
19+
2,1,3,4
20+
5,2,6,7
21+
6,8,4,3
22+
*/
23+
using namespace std;
24+
class matrix_move {
25+
public:
26+
int solve(vector<vector<int>> &matrix){
27+
if (matrix.empty() || matrix.size() != matrix[0].size()) {
28+
return 0;
29+
}
30+
solve(matrix, 0, 0, matrix[0][0]);
31+
return result;
32+
}
33+
private:
34+
int result = INT_MAX;
35+
36+
void solve(vector<vector<int>> &matrix, int row,int col ,int pathLen){
37+
size_t N = matrix.size()-1;
38+
if (row == N && col == N) {
39+
result = min(result, pathLen);
40+
return;
41+
}
42+
if (row < N) {
43+
solve(matrix, row+1, col, pathLen+matrix[row+1][col]);
44+
}
45+
if (col < N) {
46+
solve(matrix, row, col+1, pathLen+matrix[row][col+1]);
47+
}
48+
}
49+
};
50+
51+
52+
class matrix_move_dp {
53+
public:
54+
/*
55+
思路:
56+
假设矩阵大小为N*N,那么从左上角移动到右下角一共要移动2*(N-1)次.
57+
每一次要么向下移动,要么向右移动,每次移动之后,状态(路径和)都会发生变化。该问题符合动态规划的"一个模型三个条件"
58+
59+
怎么定义状态呢?
60+
-- int min_path[i][j] 里面保存的是从左上角到matrix[i][j]的最小路径和
61+
状态转移方程是什么?
62+
-- 走法只有两种,那么到达matrix[i][j]的位置,只有可能是min_path[i-1][j]或者min_path[i][j-1]中的一条.
63+
-- min_path[i][j] = min(min_path[i-1][j], min_path[i][j-1]) + matrix[i][j];
64+
*/
65+
int solve(vector<vector<int>> &matrix){
66+
if (matrix.empty()) {
67+
return 0;
68+
}
69+
size_t N = matrix.size();
70+
int min_path[N][N];
71+
memset(min_path, 0, sizeof(min_path));
72+
73+
//设置初始状态
74+
int sum=0;
75+
int i;
76+
for (i=0; i<N; i++) {//第一排的初始状态
77+
sum+=matrix[0][i];
78+
min_path[0][i]=sum;
79+
}
80+
81+
sum=0;
82+
for (i=0; i<N; i++) { //第一列的初始状态
83+
sum+=matrix[i][0];
84+
min_path[i][0]=sum;
85+
}
86+
87+
//通过初始状态动态向前推导,按照一行行的向下推导
88+
for (i=1; i<N; i++) {
89+
for (int j=1; j<N; j++) {
90+
min_path[i][j] = min(min_path[i-1][j], min_path[i][j-1]) + matrix[i][j];
91+
}
92+
}
93+
return min_path[N-1][N-1];
94+
};
95+
};
96+
97+
void test_matrix_move(){
98+
vector<vector<int>> matrix;
99+
matrix.push_back({1,3,5,9});
100+
matrix.push_back({2,1,3,4});
101+
matrix.push_back({5,2,6,7});
102+
matrix.push_back({6,8,4,3});
103+
class matrix_move so;
104+
cout << "----------矩阵移动-----------" << endl;
105+
cout << so.solve(matrix) << endl;
106+
107+
class matrix_move_dp so_dp;
108+
cout << so_dp.solve(matrix) << endl;
109+
110+
}
111+
112+
113+
#endif /* matrix_move_hpp */
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//
2+
// TwoStacksQueue.h
3+
// stack+queue
4+
//
5+
// Created by junl on 2019年10月14日.
6+
// Copyright © 2019 junl. All rights reserved.
7+
//
8+
9+
#ifndef TwoStacksQueue_hpp
10+
#define TwoStacksQueue_hpp
11+
12+
#include <stdio.h>
13+
#include <stack>
14+
/*
15+
思路:
16+
两个栈正好可以实现倒序.关键点把pushStack的数据倒序放到popStack中:
17+
1.如果popStack包含元素了的话,那么不能将数据从pushStack移动到popStack
18+
2.必须一次性的把pushStackd里面的元素移动到popStack中
19+
*/
20+
class TwoStacksQueue {
21+
public:
22+
typedef int Value_t;
23+
void add(Value_t x)//添加元素
24+
{
25+
pushStack.push(x);
26+
}
27+
void poll()//删除队列首元素
28+
{
29+
move();
30+
if (popStack.empty()) {
31+
throw stackEmpty();
32+
}
33+
popStack.pop();
34+
}
35+
Value_t peek()//获取首元素
36+
{
37+
if (popStack.empty() && pushStack.empty()) {
38+
throw stackEmpty();
39+
}
40+
move();
41+
return popStack.top();
42+
}
43+
private:
44+
std::stack<Value_t> pushStack;
45+
std::stack<Value_t> popStack;
46+
void move(){
47+
if (popStack.empty()) {
48+
//如果popStack为空,则把pushStack的所有元素倒序加入.
49+
while (!pushStack.empty()) {
50+
popStack.push(pushStack.top());
51+
pushStack.pop();
52+
}
53+
}
54+
}
55+
};
56+
57+
58+
void test_TwoStacksQueue(){
59+
std::cout << "-----------test_TwoStacksQueue-----------" << std::endl;
60+
TwoStacksQueue queue;
61+
queue.add(1);
62+
queue.add(2);
63+
queue.add(3);
64+
std::cout << "peek: "<< queue.peek() << std::endl;//1
65+
queue.poll();
66+
std::cout << "peek: "<< queue.peek() << std::endl;//2
67+
68+
}
69+
70+
#endif /* TwoStacksQueue_hpp */

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /