├── .devcontainer └── devcontainer.json ├── .gitignore ├── .vscode └── tasks.json ├── C++ ├── .DS_Store ├── README.md ├── abstract.tex ├── chapBFS.tex ├── chapBruteforce.tex ├── chapDFS.tex ├── chapDivideAndConquer.tex ├── chapDynamicProgramming.tex ├── chapGraph.tex ├── chapGreedy.tex ├── chapImplement.tex ├── chapLinearList.tex ├── chapSearching.tex ├── chapSorting.tex ├── chapStackAndQueue.tex ├── chapString.tex ├── chapTree.tex ├── chapTrick.tex ├── format.cls ├── images │ ├── .DS_Store │ ├── 8-queens.png │ ├── gray-code-construction.png │ ├── histogram-area.png │ ├── histogram.png │ ├── longest-substring-without-repeating-characters.png │ ├── next-permutation.png │ ├── phone-keyboard.png │ ├── robot-maze.png │ ├── rotate-image.png │ ├── sudoku-solution.png │ ├── sudoku.png │ └── trapping-rain-water.png ├── leetcode-cpp.pdf ├── leetcode-cpp.tex ├── tex-text-tt.map ├── tex-text-tt.tec ├── title.tex └── verbatim.cls ├── Java └── README.md ├── LICENSE ├── README.md └── 参考资料 ├── Longest Palindromic Substring Part II - LeetCode.com.pdf ├── Manacher's ALGORITHM_ O(n)时间求字符串的最长回文子串 - Blog of Felix021 - TechOnly.pdf ├── leetcode Single Number II - 位运算处理数组中的数 - 代金桥 - 博客园.pdf └── silicon-job.jpeg /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TeX Live", 3 | "image": "soulmachine/texlive:latest", 4 | "extensions": [ 5 | "James-Yu.latex-workshop" 6 | ] 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.toc 3 | *.aux 4 | *.idx 5 | *.out 6 | *.synctex.gz 7 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "lettcode-C++", 6 | "type": "shell", 7 | "command": "xelatex", 8 | "args": [ 9 | "-synctex=1", 10 | "-interaction=nonstopmode", 11 | "leetcode-cpp.tex" 12 | ], 13 | "options": { 14 | "cwd": "${workspaceFolder}/C++/" 15 | } 16 | }, 17 | { 18 | "label": "lettcode-Java", 19 | "type": "shell", 20 | "command": "xelatex", 21 | "args": [ 22 | "-synctex=1", 23 | "-interaction=nonstopmode", 24 | "leetcode-java.tex" 25 | ], 26 | "options": { 27 | "cwd": "${workspaceFolder}/Java/" 28 | } 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /C++/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/.DS_Store -------------------------------------------------------------------------------- /C++/README.md: -------------------------------------------------------------------------------- 1 | # C++版 2 | 3 | ## 编译 4 | 5 | ```bash 6 | docker run -it --rm -v $(pwd):/project -w /project soulmachine/texlive xelatex -interaction=nonstopmode leetcode-cpp.tex 7 | ```` 8 | -------------------------------------------------------------------------------- /C++/abstract.tex: -------------------------------------------------------------------------------- 1 | \subsubsection{内容简介} 2 | 本书的目标读者是准备去北美找工作的码农,也适用于在国内找工作的码农,以及刚接触ACM算法竞赛的新手。 3 | 4 | 本书包含了 LeetCode Online Judge(\myurl{http://leetcode.com/onlinejudge})所有题目的答案, 5 | 所有代码经过精心编写,编码规范良好,适合读者反复揣摩,模仿,甚至在纸上默写。 6 | 7 | 全书的代码,使用C++ 11的编写,并在 LeetCode Online Judge 上测试通过。本书中的代码规范,跟在公司中的工程规范略有不同,为了使代码短(方便迅速实现): 8 | 9 | \begindot 10 | \item 所有代码都是单一文件。这是因为一般OJ网站,提交代码的时候只有一个文本框,如果还是 11 | 按照标准做法,比如分为头文件.h和源代码.cpp,无法在网站上提交; 12 | 13 | \item Shorter is better。能递归则一定不用栈;能用STL则一定不自己实现。 14 | 15 | \item 不提倡防御式编程。不需要检查malloc()/new 返回的指针是否为nullptr;不需要检查内部函数入口参数的有效性。 16 | \myenddot 17 | 18 | 本手册假定读者已经学过《数据结构》\footnote{《数据结构》,严蔚敏等著,清华大学出版社, 19 | \myurl{http://book.douban.com/subject/2024655/}}, 20 | 《算法》\footnote{《Algorithms》,Robert Sedgewick, Addison-Wesley Professional, \myurl{http://book.douban.com/subject/4854123/}} 21 | 这两门课,熟练掌握C++或Java。 22 | 23 | \subsubsection{GitHub地址} 24 | 本书是开源的,GitHub地址:\myurl{https://github.com/soulmachine/leetcode} 25 | 26 | \subsubsection{北美求职微博群} 27 | 我和我的小伙伴们在这里:\myurl{http://q.weibo.com/1312378} 28 | -------------------------------------------------------------------------------- /C++/chapBruteforce.tex: -------------------------------------------------------------------------------- 1 | \chapter{暴力枚举法} 2 | 3 | 4 | \section{Subsets} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | \label{sec:subsets} 6 | 7 | 8 | \subsubsection{描述} 9 | Given a set of distinct integers, $S$, return all possible subsets. 10 | 11 | Note: 12 | \begindot 13 | \item Elements in a subset must be in non-descending order. 14 | \item The solution set must not contain duplicate subsets. 15 | \myenddot 16 | 17 | For example, If \code{S = [1,2,3]}, a solution is: 18 | \begin{Code} 19 | [ 20 | [3], 21 | [1], 22 | [2], 23 | [1,2,3], 24 | [1,3], 25 | [2,3], 26 | [1,2], 27 | [] 28 | ] 29 | \end{Code} 30 | 31 | 32 | \subsection{递归} 33 | 34 | 35 | \subsubsection{增量构造法} 36 | 每个元素,都有两种选择,选或者不选。 37 | 38 | \begin{Code} 39 | // LeetCode, Subsets 40 | // 增量构造法,深搜,时间复杂度O(2^n),空间复杂度O(n) 41 | class Solution { 42 | public: 43 | vector > subsets(vector &S) { 44 | sort(S.begin(), S.end()); // 输出要求有序 45 | vector > result; 46 | vector path; 47 | subsets(S, path, 0, result); 48 | return result; 49 | } 50 | 51 | private: 52 | static void subsets(const vector &S, vector &path, int step, 53 | vector > &result) { 54 | if (step == S.size()) { 55 | result.push_back(path); 56 | return; 57 | } 58 | // 不选S[step] 59 | subsets(S, path, step + 1, result); 60 | // 选S[step] 61 | path.push_back(S[step]); 62 | subsets(S, path, step + 1, result); 63 | path.pop_back(); 64 | } 65 | }; 66 | \end{Code} 67 | 68 | 69 | \subsubsection{位向量法} 70 | 开一个位向量\fn{bool selected[n]},每个元素可以选或者不选。 71 | 72 | \begin{Code} 73 | // LeetCode, Subsets 74 | // 位向量法,深搜,时间复杂度O(2^n),空间复杂度O(n) 75 | class Solution { 76 | public: 77 | vector > subsets(vector &S) { 78 | sort(S.begin(), S.end()); // 输出要求有序 79 | 80 | vector > result; 81 | vector selected(S.size(), false); 82 | subsets(S, selected, 0, result); 83 | return result; 84 | } 85 | 86 | private: 87 | static void subsets(const vector &S, vector &selected, int step, 88 | vector > &result) { 89 | if (step == S.size()) { 90 | vector subset; 91 | for (int i = 0; i < S.size(); i++) { 92 | if (selected[i]) subset.push_back(S[i]); 93 | } 94 | result.push_back(subset); 95 | return; 96 | } 97 | // 不选S[step] 98 | selected[step] = false; 99 | subsets(S, selected, step + 1, result); 100 | // 选S[step] 101 | selected[step] = true; 102 | subsets(S, selected, step + 1, result); 103 | } 104 | }; 105 | \end{Code} 106 | 107 | 108 | \subsection{迭代} 109 | 110 | 111 | \subsubsection{增量构造法} 112 | \begin{Code} 113 | // LeetCode, Subsets 114 | // 迭代版,时间复杂度O(2^n),空间复杂度O(1) 115 | class Solution { 116 | public: 117 | vector > subsets(vector &S) { 118 | sort(S.begin(), S.end()); // 输出要求有序 119 | vector > result(1); 120 | for (auto elem : S) { 121 | result.reserve(result.size() * 2); 122 | auto half = result.begin() + result.size(); 123 | copy(result.begin(), half, back_inserter(result)); 124 | for_each(half, result.end(), [&elem](decltype(result[0]) &e){ 125 | e.push_back(elem); 126 | }); 127 | } 128 | return result; 129 | } 130 | }; 131 | \end{Code} 132 | 133 | 134 | \subsubsection{二进制法} 135 | 本方法的前提是:集合的元素不超过int位数。用一个int整数表示位向量,第$i$位为1,则表示选择$S[i]$,为0则不选择。例如\fn{S=\{A,B,C,D\}},则\fn{0110=6}表示子集\fn{\{B,C\}}。 136 | 137 | 这种方法最巧妙。因为它不仅能生成子集,还能方便的表示集合的并、交、差等集合运算。设两个集合的位向量分别为$B_1$和$B_2$,则$B_1\cup B_2, B_1 \cap B_2, B_1 \triangle B_2$分别对应集合的并、交、对称差。 138 | 139 | 二进制法,也可以看做是位向量法,只不过更加优化。 140 | 141 | \begin{Code} 142 | // LeetCode, Subsets 143 | // 二进制法,时间复杂度O(2^n),空间复杂度O(1) 144 | class Solution { 145 | public: 146 | vector > subsets(vector &S) { 147 | sort(S.begin(), S.end()); // 输出要求有序 148 | vector > result; 149 | const size_t n = S.size(); 150 | vector v; 151 | 152 | for (size_t i = 0; i < 1 << n; i++) { 153 | for (size_t j = 0; j < n; j++) { 154 | if (i & 1 << j) v.push_back(S[j]); 155 | } 156 | result.push_back(v); 157 | v.clear(); 158 | } 159 | return result; 160 | } 161 | }; 162 | \end{Code} 163 | 164 | 165 | \subsubsection{相关题目} 166 | \begindot 167 | \item Subsets II,见 \S \ref{sec:subsets-ii} 168 | \myenddot 169 | 170 | 171 | \section{Subsets II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 172 | \label{sec:subsets-ii} 173 | 174 | 175 | \subsubsection{描述} 176 | Given a collection of integers that might contain duplicates, $S$, return all possible subsets. 177 | 178 | Note: 179 | 180 | Elements in a subset must be in non-descending order. 181 | The solution set must not contain duplicate subsets. 182 | For example, 183 | If \fn{S = [1,2,2]}, a solution is: 184 | \begin{Code} 185 | [ 186 | [2], 187 | [1], 188 | [1,2,2], 189 | [2,2], 190 | [1,2], 191 | [] 192 | ] 193 | \end{Code} 194 | 195 | 196 | \subsubsection{分析} 197 | 这题有重复元素,但本质上,跟上一题很类似,上一题中元素没有重复,相当于每个元素只能选0或1次,这里扩充到了每个元素可以选0到若干次而已。 198 | 199 | 200 | \subsection{递归} 201 | 202 | 203 | \subsubsection{增量构造法} 204 | \begin{Code} 205 | // LeetCode, Subsets II 206 | // 增量构造法,版本1,时间复杂度O(2^n),空间复杂度O(n) 207 | class Solution { 208 | public: 209 | vector > subsetsWithDup(vector &S) { 210 | sort(S.begin(), S.end()); // 必须排序 211 | 212 | vector > result; 213 | vector path; 214 | 215 | dfs(S, S.begin(), path, result); 216 | return result; 217 | } 218 | 219 | private: 220 | static void dfs(const vector &S, vector::iterator start, 221 | vector &path, vector > &result) { 222 | result.push_back(path); 223 | 224 | for (auto i = start; i < S.end(); i++) { 225 | if (i != start && *i == *(i-1)) continue; 226 | path.push_back(*i); 227 | dfs(S, i + 1, path, result); 228 | path.pop_back(); 229 | } 230 | } 231 | }; 232 | \end{Code} 233 | 234 | \begin{Code} 235 | // LeetCode, Subsets II 236 | // 增量构造法,版本2,时间复杂度O(2^n),空间复杂度O(n) 237 | class Solution { 238 | public: 239 | vector > subsetsWithDup(vector &S) { 240 | vector > result; 241 | sort(S.begin(), S.end()); // 必须排序 242 | 243 | unordered_map count_map; // 记录每个元素的出现次数 244 | for_each(S.begin(), S.end(), [&count_map](int e) { 245 | if (count_map.find(e) != count_map.end()) 246 | count_map[e]++; 247 | else 248 | count_map[e] = 1; 249 | }); 250 | 251 | // 将map里的pair拷贝到一个vector里 252 | vector > elems; 253 | for_each(count_map.begin(), count_map.end(), 254 | [&elems](const pair &e) { 255 | elems.push_back(e); 256 | }); 257 | sort(elems.begin(), elems.end()); 258 | vector path; // 中间结果 259 | 260 | subsets(elems, 0, path, result); 261 | return result; 262 | } 263 | 264 | private: 265 | static void subsets(const vector > &elems, 266 | size_t step, vector &path, vector > &result) { 267 | if (step == elems.size()) { 268 | result.push_back(path); 269 | return; 270 | } 271 | 272 | for (int i = 0; i <= elems[step].second; i++) { 273 | for (int j = 0; j < i; ++j) { 274 | path.push_back(elems[step].first); 275 | } 276 | subsets(elems, step + 1, path, result); 277 | for (int j = 0; j < i; ++j) { 278 | path.pop_back(); 279 | } 280 | } 281 | } 282 | }; 283 | \end{Code} 284 | 285 | 286 | \subsubsection{位向量法} 287 | \begin{Code} 288 | // LeetCode, Subsets II 289 | // 位向量法,时间复杂度O(2^n),空间复杂度O(n) 290 | class Solution { 291 | public: 292 | vector > subsetsWithDup(vector &S) { 293 | vector > result; // 必须排序 294 | sort(S.begin(), S.end()); 295 | vector count(S.back() - S.front() + 1, 0); 296 | // 计算所有元素的个数 297 | for (auto i : S) { 298 | count[i - S[0]]++; 299 | } 300 | 301 | // 每个元素选择了多少个 302 | vector selected(S.back() - S.front() + 1, -1); 303 | 304 | subsets(S, count, selected, 0, result); 305 | return result; 306 | } 307 | 308 | private: 309 | static void subsets(const vector &S, vector &count, 310 | vector &selected, size_t step, vector > &result) { 311 | if (step == count.size()) { 312 | vector subset; 313 | for(size_t i = 0; i < selected.size(); i++) { 314 | for (int j = 0; j < selected[i]; j++) { 315 | subset.push_back(i+S[0]); 316 | } 317 | } 318 | result.push_back(subset); 319 | return; 320 | } 321 | 322 | for (int i = 0; i <= count[step]; i++) { 323 | selected[step] = i; 324 | subsets(S, count, selected, step + 1, result); 325 | } 326 | } 327 | }; 328 | \end{Code} 329 | 330 | 331 | \subsection{迭代} 332 | 333 | 334 | \subsubsection{增量构造法} 335 | \begin{Code} 336 | // LeetCode, Subsets II 337 | // 增量构造法 338 | // 时间复杂度O(2^n),空间复杂度O(1) 339 | class Solution { 340 | public: 341 | vector > subsetsWithDup(vector &S) { 342 | sort(S.begin(), S.end()); // 必须排序 343 | vector > result(1); 344 | 345 | size_t previous_size = 0; 346 | for (size_t i = 0; i < S.size(); ++i) { 347 | const size_t size = result.size(); 348 | for (size_t j = 0; j < size; ++j) { 349 | if (i == 0 || S[i] != S[i-1] || j >= previous_size) { 350 | result.push_back(result[j]); 351 | result.back().push_back(S[i]); 352 | } 353 | } 354 | previous_size = size; 355 | } 356 | return result; 357 | } 358 | }; 359 | \end{Code} 360 | 361 | 362 | \subsubsection{二进制法} 363 | \begin{Code} 364 | // LeetCode, Subsets II 365 | // 二进制法,时间复杂度O(2^n),空间复杂度O(1) 366 | class Solution { 367 | public: 368 | vector > subsetsWithDup(vector &S) { 369 | sort(S.begin(), S.end()); // 必须排序 370 | // 用 set 去重,不能用 unordered_set,因为输出要求有序 371 | set > result; 372 | const size_t n = S.size(); 373 | vector v; 374 | 375 | for (size_t i = 0; i < 1U << n; ++i) { 376 | for (size_t j = 0; j < n; ++j) { 377 | if (i & 1 << j) 378 | v.push_back(S[j]); 379 | } 380 | result.insert(v); 381 | v.clear(); 382 | } 383 | vector > real_result; 384 | copy(result.begin(), result.end(), back_inserter(real_result)); 385 | return real_result; 386 | } 387 | }; 388 | \end{Code} 389 | 390 | 391 | \subsubsection{相关题目} 392 | \begindot 393 | \item Subsets,见 \S \ref{sec:subsets} 394 | \myenddot 395 | 396 | 397 | \section{Permutations} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 398 | \label{sec:permutations} 399 | 400 | 401 | \subsubsection{描述} 402 | Given a collection of numbers, return all possible permutations. 403 | 404 | For example, 405 | \fn{[1,2,3]} have the following permutations: 406 | \fn{[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2]}, and \fn{[3,2,1]}. 407 | 408 | 409 | \subsection{next_permutation()} 410 | 偷懒的做法,可以直接使用\fn{std::next_permutation()}。如果是在OJ网站上,可以用这个API偷个懒;如果是在面试中,面试官肯定会让你重新实现。 411 | 412 | \subsubsection{代码} 413 | \begin{Code} 414 | // LeetCode, Permutations 415 | // 时间复杂度O(n!),空间复杂度O(1) 416 | class Solution { 417 | public: 418 | vector > permute(vector &num) { 419 | vector > result; 420 | sort(num.begin(), num.end()); 421 | 422 | do { 423 | result.push_back(num); 424 | } while(next_permutation(num.begin(), num.end())); 425 | return result; 426 | } 427 | }; 428 | \end{Code} 429 | 430 | 431 | \subsection{重新实现next_permutation()} 432 | 见第 \S \ref{sec:next-permutation} 节。 433 | 434 | 435 | \subsubsection{代码} 436 | \begin{Code} 437 | // LeetCode, Permutations 438 | // 重新实现 next_permutation() 439 | // 时间复杂度O(n!),空间复杂度O(1) 440 | class Solution { 441 | public: 442 | vector > permute(vector &num) { 443 | vector > result; 444 | sort(num.begin(), num.end()); 445 | 446 | do { 447 | result.push_back(num); 448 | // 调用的是 2.1.12 节的 next_permutation() 449 | // 而不是 std::next_permutation() 450 | } while(next_permutation(num.begin(), num.end())); 451 | return result; 452 | } 453 | }; 454 | \end{Code} 455 | 456 | 457 | \subsection{递归} 458 | 本题是求路径本身,求所有解,函数参数需要标记当前走到了哪步,还需要中间结果的引用,最终结果的引用。 459 | 460 | 扩展节点,每次从左到右,选一个没有出现过的元素。 461 | 462 | 本题不需要判重,因为状态装换图是一颗有层次的树。收敛条件是当前走到了最后一个元素。 463 | 464 | \subsubsection{代码} 465 | \begin{Code} 466 | // LeetCode, Permutations 467 | // 深搜,增量构造法 468 | // 时间复杂度O(n!),空间复杂度O(n) 469 | class Solution { 470 | public: 471 | vector > permute(vector& num) { 472 | sort(num.begin(), num.end()); 473 | 474 | vector> result; 475 | vector path; // 中间结果 476 | 477 | dfs(num, path, result); 478 | return result; 479 | } 480 | private: 481 | void dfs(const vector& num, vector &path, 482 | vector > &result) { 483 | if (path.size() == num.size()) { // 收敛条件 484 | result.push_back(path); 485 | return; 486 | } 487 | 488 | // 扩展状态 489 | for (auto i : num) { 490 | // 查找 i 是否在path 中出现过 491 | auto pos = find(path.begin(), path.end(), i); 492 | 493 | if (pos == path.end()) { 494 | path.push_back(i); 495 | dfs(num, path, result); 496 | path.pop_back(); 497 | } 498 | } 499 | } 500 | }; 501 | \end{Code} 502 | 503 | 504 | \subsubsection{相关题目} 505 | \begindot 506 | \item Next Permutation, 见 \S \ref{sec:next-permutation} 507 | \item Permutation Sequence, 见 \S \ref{sec:permutation-sequence} 508 | \item Permutations II, 见 \S \ref{sec:permutations-ii} 509 | \item Combinations, 见 \S \ref{sec:combinations} 510 | \myenddot 511 | 512 | 513 | \section{Permutations II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 514 | \label{sec:permutations-ii} 515 | 516 | 517 | \subsubsection{描述} 518 | Given a collection of numbers that might contain duplicates, return all possible unique permutations. 519 | 520 | For example, 521 | \fn{[1,1,2]} have the following unique permutations: 522 | \fn{[1,1,2], [1,2,1]}, and \fn{[2,1,1]}. 523 | 524 | 525 | \subsection{next_permutation()} 526 | 直接使用\fn{std::next_permutation()},代码与上一题相同。 527 | 528 | 529 | \subsection{重新实现next_permutation()} 530 | 重新实现\fn{std::next_permutation()},代码与上一题相同。 531 | 532 | 533 | \subsection{递归} 534 | 递归函数\fn{permute()}的参数\fn{p},是中间结果,它的长度又能标记当前走到了哪一步,用于判断收敛条件。 535 | 536 | 扩展节点,每次从小到大,选一个没有被用光的元素,直到所有元素被用光。 537 | 538 | 本题不需要判重,因为状态装换图是一颗有层次的树。 539 | 540 | 541 | \subsubsection{代码} 542 | \begin{Code} 543 | // LeetCode, Permutations II 544 | // 深搜,时间复杂度O(n!),空间复杂度O(n) 545 | class Solution { 546 | public: 547 | vector > permuteUnique(vector& num) { 548 | sort(num.begin(), num.end()); 549 | 550 | unordered_map count_map; // 记录每个元素的出现次数 551 | for_each(num.begin(), num.end(), [&count_map](int e) { 552 | if (count_map.find(e) != count_map.end()) 553 | count_map[e]++; 554 | else 555 | count_map[e] = 1; 556 | }); 557 | 558 | // 将map里的pair拷贝到一个vector里 559 | vector > elems; 560 | for_each(count_map.begin(), count_map.end(), 561 | [&elems](const pair &e) { 562 | elems.push_back(e); 563 | }); 564 | 565 | vector> result; // 最终结果 566 | vector p; // 中间结果 567 | 568 | n = num.size(); 569 | permute(elems.begin(), elems.end(), p, result); 570 | return result; 571 | } 572 | 573 | private: 574 | size_t n; 575 | typedef vector >::const_iterator Iter; 576 | 577 | void permute(Iter first, Iter last, vector &p, 578 | vector > &result) { 579 | if (n == p.size()) { // 收敛条件 580 | result.push_back(p); 581 | } 582 | 583 | // 扩展状态 584 | for (auto i = first; i != last; i++) { 585 | int count = 0; // 统计 *i 在p中出现过多少次 586 | for (auto j = p.begin(); j != p.end(); j++) { 587 | if (i->first == *j) { 588 | count ++; 589 | } 590 | } 591 | if (count < i->second) { 592 | p.push_back(i->first); 593 | permute(first, last, p, result); 594 | p.pop_back(); // 撤销动作,返回上一层 595 | } 596 | } 597 | } 598 | }; 599 | \end{Code} 600 | 601 | 602 | \subsubsection{相关题目} 603 | \begindot 604 | \item Next Permutation, 见 \S \ref{sec:next-permutation} 605 | \item Permutation Sequence, 见 \S \ref{sec:permutation-sequence} 606 | \item Permutations, 见 \S \ref{sec:permutations} 607 | \item Combinations, 见 \S \ref{sec:combinations} 608 | \myenddot 609 | 610 | 611 | \section{Combinations} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 612 | \label{sec:combinations} 613 | 614 | 615 | \subsubsection{描述} 616 | Given two integers $n$ and $k$, return all possible combinations of $k$ numbers out of $1 ... n$. 617 | 618 | For example, 619 | If $n = 4$ and $k = 2$, a solution is: 620 | \begin{Code} 621 | [ 622 | [2,4], 623 | [3,4], 624 | [2,3], 625 | [1,2], 626 | [1,3], 627 | [1,4], 628 | ] 629 | \end{Code} 630 | 631 | 632 | \subsection{递归} 633 | \begin{Code} 634 | // LeetCode, Combinations 635 | // 深搜,递归 636 | // 时间复杂度O(n!),空间复杂度O(n) 637 | class Solution { 638 | public: 639 | vector > combine(int n, int k) { 640 | vector > result; 641 | vector path; 642 | dfs(n, k, 1, 0, path, result); 643 | return result; 644 | } 645 | private: 646 | // start,开始的数, cur,已经选择的数目 647 | static void dfs(int n, int k, int start, int cur, 648 | vector &path, vector > &result) { 649 | if (cur == k) { 650 | result.push_back(path); 651 | } 652 | for (int i = start; i <= n; ++i) { 653 | path.push_back(i); 654 | dfs(n, k, i + 1, cur + 1, path, result); 655 | path.pop_back(); 656 | } 657 | } 658 | }; 659 | \end{Code} 660 | 661 | 662 | \subsection{迭代} 663 | \begin{Code} 664 | // LeetCode, Combinations 665 | // use prev_permutation() 666 | // 时间复杂度O((n-k)!),空间复杂度O(n) 667 | class Solution { 668 | public: 669 | vector > combine(int n, int k) { 670 | vector values(n); 671 | iota(values.begin(), values.end(), 1); 672 | 673 | vector select(n, false); 674 | fill_n(select.begin(), k, true); 675 | 676 | vector > result; 677 | do{ 678 | vector one(k); 679 | for (int i = 0, index = 0; i < n; ++i) 680 | if (select[i]) 681 | one[index++] = values[i]; 682 | result.push_back(one); 683 | } while(prev_permutation(select.begin(), select.end())); 684 | return result; 685 | } 686 | }; 687 | \end{Code} 688 | 689 | 690 | \subsubsection{相关题目} 691 | \begindot 692 | \item Next Permutation, 见 \S \ref{sec:next-permutation} 693 | \item Permutation Sequence, 见 \S \ref{sec:permutation-sequence} 694 | \item Permutations, 见 \S \ref{sec:permutations} 695 | \item Permutations II, 见 \S \ref{sec:permutations-ii} 696 | \myenddot 697 | 698 | 699 | \section{Letter Combinations of a Phone Number } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 700 | \label{sec:letter-combinations-of-a-phone-number } 701 | 702 | 703 | \subsubsection{描述} 704 | Given a digit string, return all possible letter combinations that the number could represent. 705 | 706 | A mapping of digit to letters (just like on the telephone buttons) is given below. 707 | 708 | \begin{center} 709 | \includegraphics[width=150pt]{phone-keyboard.png}\\ 710 | \figcaption{Phone Keyboard}\label{fig:phone-keyboard} 711 | \end{center} 712 | 713 | \textbf{Input:}Digit string \code{"23"} 714 | 715 | \textbf{Output:} \code{["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]}. 716 | 717 | \textbf{Note:} 718 | Although the above answer is in lexicographical order, your answer could be in any order you want. 719 | 720 | 721 | \subsubsection{分析} 722 | 无 723 | 724 | 725 | \subsection{递归} 726 | \begin{Code} 727 | // LeetCode, Letter Combinations of a Phone Number 728 | // 时间复杂度O(3^n),空间复杂度O(n) 729 | class Solution { 730 | public: 731 | const vector keyboard { " ", "", "abc", "def", // '0','1','2',... 732 | "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; 733 | 734 | vector letterCombinations (const string &digits) { 735 | vector result; 736 | if (digits.empty()) return result; 737 | dfs(digits, 0, "", result); 738 | return result; 739 | } 740 | 741 | void dfs(const string &digits, size_t cur, string path, 742 | vector &result) { 743 | if (cur == digits.size()) { 744 | result.push_back(path); 745 | return; 746 | } 747 | for (auto c : keyboard[digits[cur] - '0']) { 748 | dfs(digits, cur + 1, path + c, result); 749 | } 750 | } 751 | }; 752 | \end{Code} 753 | 754 | 755 | \subsection{迭代} 756 | \begin{Code} 757 | // LeetCode, Letter Combinations of a Phone Number 758 | // 时间复杂度O(3^n),空间复杂度O(1) 759 | class Solution { 760 | public: 761 | const vector keyboard { " ", "", "abc", "def", // '0','1','2',... 762 | "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; 763 | 764 | vector letterCombinations (const string &digits) { 765 | if (digits.empty()) return vector(); 766 | vector result(1, ""); 767 | for (auto d : digits) { 768 | const size_t n = result.size(); 769 | const size_t m = keyboard[d - '0'].size(); 770 | 771 | result.resize(n * m); 772 | for (size_t i = 0; i < m; ++i) 773 | copy(result.begin(), result.begin() + n, result.begin() + n * i); 774 | 775 | for (size_t i = 0; i < m; ++i) { 776 | auto begin = result.begin(); 777 | for_each(begin + n * i, begin + n * (i+1), [&](string &s) { 778 | s += keyboard[d - '0'][i]; 779 | }); 780 | } 781 | } 782 | return result; 783 | } 784 | }; 785 | \end{Code} 786 | 787 | 788 | \subsubsection{相关题目} 789 | \begindot 790 | \item 无 791 | \myenddot 792 | -------------------------------------------------------------------------------- /C++/chapDivideAndConquer.tex: -------------------------------------------------------------------------------- 1 | \chapter{分治法} 2 | 3 | 4 | \section{Pow(x,n)} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | \label{sec:pow} 6 | 7 | 8 | \subsubsection{描述} 9 | Implement pow(x, n). 10 | 11 | 12 | \subsubsection{分析} 13 | 二分法,$x^n = x^{n/2} \times x^{n/2} \times x^{n\%2}$ 14 | 15 | 16 | \subsubsection{代码} 17 | \begin{Code} 18 | //LeetCode, Pow(x, n) 19 | // 二分法,$x^n = x^{n/2} * x^{n/2} * x^{n\%2}$ 20 | // 时间复杂度O(logn),空间复杂度O(1) 21 | class Solution { 22 | public: 23 | double myPow(double x, int n) { 24 | if (n < 0) return 1.0 / power(x, -n); 25 | else return power(x, n); 26 | } 27 | private: 28 | double power(double x, int n) { 29 | if (n == 0) return 1; 30 | double v = power(x, n / 2); 31 | if (n % 2 == 0) return v * v; 32 | else return v * v * x; 33 | } 34 | }; 35 | \end{Code} 36 | 37 | 38 | \subsubsection{相关题目} 39 | 40 | \begindot 41 | \item Sqrt(x),见 \S \ref{sec:sqrt} 42 | \myenddot 43 | 44 | 45 | \section{Sqrt(x)} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 46 | \label{sec:sqrt} 47 | 48 | \subsubsection{描述} 49 | Implement int \fn{sqrt(int x)}. 50 | 51 | Compute and return the square root of \fn{x}. 52 | 53 | 54 | \subsubsection{分析} 55 | 二分查找 56 | 57 | 58 | \subsubsection{代码} 59 | \begin{Code} 60 | // LeetCode, Sqrt(x) 61 | // 二分查找 62 | // 时间复杂度O(logn),空间复杂度O(1) 63 | class Solution { 64 | public: 65 | int mySqrt(int x) { 66 | int left = 1, right = x / 2; 67 | int last_mid; // 记录最近一次mid 68 | 69 | if (x < 2) return x; 70 | 71 | while(left <= right) { 72 | const int mid = left + (right - left) / 2; 73 | if(x / mid > mid) { // 不要用 x > mid * mid,会溢出 74 | left = mid + 1; 75 | last_mid = mid; 76 | } else if(x / mid < mid) { 77 | right = mid - 1; 78 | } else { 79 | return mid; 80 | } 81 | } 82 | return last_mid; 83 | } 84 | }; 85 | \end{Code} 86 | 87 | 88 | \subsubsection{相关题目} 89 | \begindot 90 | \item Pow(x),见 \S \ref{sec:pow} 91 | \myenddot 92 | -------------------------------------------------------------------------------- /C++/chapDynamicProgramming.tex: -------------------------------------------------------------------------------- 1 | \chapter{动态规划} 2 | 3 | 4 | \section{Triangle} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | \label{sec:triangle} 6 | 7 | 8 | \subsubsection{描述} 9 | Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below. 10 | 11 | For example, given the following triangle 12 | \begin{Code} 13 | [ 14 | [2], 15 | [3,4], 16 | [6,5,7], 17 | [4,1,8,3] 18 | ] 19 | \end{Code} 20 | The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11). 21 | 22 | Note: Bonus point if you are able to do this using only $O(n)$ extra space, where n is the total number of rows in the triangle. 23 | 24 | 25 | \subsubsection{分析} 26 | 设状态为$f(i, j)$,表示从从位置$(i,j)$出发,路径的最小和,则状态转移方程为 27 | $$ 28 | f(i,j)=\min\left\{f(i+1,j),f(i+1,j+1)\right\}+(i,j) 29 | $$ 30 | 31 | 32 | \subsubsection{代码} 33 | \begin{Code} 34 | // LeetCode, Triangle 35 | // 时间复杂度O(n^2),空间复杂度O(1) 36 | class Solution { 37 | public: 38 | int minimumTotal (vector>& triangle) { 39 | for (int i = triangle.size() - 2; i >= 0; --i) 40 | for (int j = 0; j < i + 1; ++j) 41 | triangle[i][j] += min(triangle[i + 1][j], 42 | triangle[i + 1][j + 1]); 43 | 44 | return triangle [0][0]; 45 | } 46 | }; 47 | \end{Code} 48 | 49 | 50 | \subsubsection{相关题目} 51 | \begindot 52 | \item 无 53 | \myenddot 54 | 55 | 56 | \section{Maximum Subarray} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 57 | \label{sec:maximum-subarray} 58 | 59 | 60 | \subsubsection{描述} 61 | Find the contiguous subarray within an array (containing at least one number) which has the largest sum. 62 | 63 | For example, given the array \code{[−2,1,−3,4,−1,2,1,−5,4]}, 64 | the contiguous subarray \code{[4,−1,2,1]} has the largest \code{sum = 6}. 65 | 66 | 67 | \subsubsection{分析} 68 | 最大连续子序列和,非常经典的题。 69 | 70 | 当我们从头到尾遍历这个数组的时候,对于数组里的一个整数,它有几种选择呢?它只有两种选择: 1、加入之前的SubArray;2. 自己另起一个SubArray。那什么时候会出现这两种情况呢? 71 | 72 | 如果之前SubArray的总体和大于0的话,我们认为其对后续结果是有贡献的。这种情况下我们选择加入之前的SubArray 73 | 74 | 如果之前SubArray的总体和为0或者小于0的话,我们认为其对后续结果是没有贡献,甚至是有害的(小于0时)。这种情况下我们选择以这个数字开始,另起一个SubArray。 75 | 76 | 设状态为\fn{f[j]},表示以\fn{S[j]}结尾的最大连续子序列和,则状态转移方程如下: 77 | \begin{eqnarray} 78 | f[j] &=& \max\left\{f[j-1]+S[j],S[j]\right\}, \text{ 其中 }1 \leq j \leq n \nonumber \\ 79 | target &=& \max\left\{f[j]\right\}, \text{ 其中 }1 \leq j \leq n \nonumber 80 | \end{eqnarray} 81 | 82 | 解释如下: 83 | \begindot 84 | \item 情况一,S[j]不独立,与前面的某些数组成一个连续子序列,则最大连续子序列和为$f[j-1]+S[j]$。 85 | \item 情况二,S[j]独立划分成为一段,即连续子序列仅包含一个数S[j],则最大连续子序列和为$S[j]$。 86 | \myenddot 87 | 88 | 其他思路: 89 | \begindot 90 | \item 思路2:直接在i到j之间暴力枚举,复杂度是$O(n^3)$ 91 | \item 思路3:处理后枚举,连续子序列的和等于两个前缀和之差,复杂度$O(n^2)$。 92 | \item 思路4:分治法,把序列分为两段,分别求最大连续子序列和,然后归并,复杂度$O(n\log n)$ 93 | \item 思路5:把思路2$O(n^2)$的代码稍作处理,得到$O(n)$的算法 94 | \item 思路6:当成M=1的最大M子段和 95 | \myenddot 96 | 97 | 98 | \subsubsection{动规} 99 | \begin{Code} 100 | // LeetCode, Maximum Subarray 101 | // 时间复杂度O(n),空间复杂度O(1) 102 | class Solution { 103 | public: 104 | int maxSubArray(vector& nums) { 105 | int result = INT_MIN, f = 0; 106 | for (int i = 0; i < nums.size(); ++i) { 107 | f = max(f + nums[i], nums[i]); 108 | result = max(result, f); 109 | } 110 | return result; 111 | } 112 | }; 113 | \end{Code} 114 | 115 | 116 | \subsubsection{思路5} 117 | \begin{Code} 118 | // LeetCode, Maximum Subarray 119 | // 时间复杂度O(n),空间复杂度O(n) 120 | class Solution { 121 | public: 122 | int maxSubArray(vector& A) { 123 | return mcss(A.begin(), A.end()); 124 | } 125 | private: 126 | // 思路5,求最大连续子序列和 127 | template 128 | static int mcss(Iter begin, Iter end) { 129 | int result, cur_min; 130 | const int n = distance(begin, end); 131 | int *sum = new int[n + 1]; // 前n项和 132 | 133 | sum[0] = 0; 134 | result = INT_MIN; 135 | cur_min = sum[0]; 136 | for (int i = 1; i <= n; i++) { 137 | sum[i] = sum[i - 1] + *(begin + i - 1); 138 | } 139 | for (int i = 1; i <= n; i++) { 140 | result = max(result, sum[i] - cur_min); 141 | cur_min = min(cur_min, sum[i]); 142 | } 143 | delete[] sum; 144 | return result; 145 | } 146 | }; 147 | \end{Code} 148 | 149 | 150 | \subsubsection{相关题目} 151 | \begindot 152 | \item Binary Tree Maximum Path Sum,见 \S \ref{sec:binary-tree-maximum-path-sum} 153 | \myenddot 154 | 155 | 156 | \section{Palindrome Partitioning II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 157 | \label{sec:palindrome-partitioning-ii} 158 | 159 | 160 | \subsubsection{描述} 161 | Given a string s, partition s such that every substring of the partition is a palindrome. 162 | 163 | Return the minimum cuts needed for a palindrome partitioning of s. 164 | 165 | For example, given \code{s = "aab"}, 166 | 167 | Return 1 since the palindrome partitioning \code{["aa","b"]} could be produced using 1 cut. 168 | 169 | 170 | \subsubsection{分析} 171 | 定义状态\fn{f(i,j)}表示区间\fn{[i,j]}之间最小的cut数,则状态转移方程为 172 | $$ 173 | f(i,j)=\min\left\{f(i,k)+f(k+1,j)\right\}, i \leq k \leq j, 0 \leq i \leq j= 0; i--) { 205 | for (int j = i; j < n; j++) { 206 | if (s[i] == s[j] && (j - i < 2 || p[i + 1][j - 1])) { 207 | p[i][j] = true; 208 | f[i] = min(f[i], f[j + 1] + 1); 209 | } 210 | } 211 | } 212 | return f[0]; 213 | } 214 | }; 215 | \end{Code} 216 | 217 | 218 | \subsubsection{相关题目} 219 | \begindot 220 | \item Palindrome Partitioning,见 \S \ref{sec:palindrome-partitioning} 221 | \myenddot 222 | 223 | 224 | \section{Maximal Rectangle} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 225 | \label{sec:maximal-rectangle} 226 | 227 | 228 | \subsubsection{描述} 229 | Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area. 230 | 231 | 232 | \subsubsection{分析} 233 | 无 234 | 235 | 236 | \subsubsection{代码} 237 | \begin{Code} 238 | // LeetCode, Maximal Rectangle 239 | // 时间复杂度O(n^2),空间复杂度O(n) 240 | class Solution { 241 | public: 242 | int maximalRectangle(vector > &matrix) { 243 | if (matrix.empty()) return 0; 244 | 245 | const int m = matrix.size(); 246 | const int n = matrix[0].size(); 247 | vector H(n, 0); 248 | vector L(n, 0); 249 | vector R(n, n); 250 | 251 | int ret = 0; 252 | for (int i = 0; i < m; ++i) { 253 | int left = 0, right = n; 254 | // calculate L(i, j) from left to right 255 | for (int j = 0; j < n; ++j) { 256 | if (matrix[i][j] == '1') { 257 | ++H[j]; 258 | L[j] = max(L[j], left); 259 | } else { 260 | left = j+1; 261 | H[j] = 0; L[j] = 0; R[j] = n; 262 | } 263 | } 264 | // calculate R(i, j) from right to left 265 | for (int j = n-1; j >= 0; --j) { 266 | if (matrix[i][j] == '1') { 267 | R[j] = min(R[j], right); 268 | ret = max(ret, H[j]*(R[j]-L[j])); 269 | } else { 270 | right = j; 271 | } 272 | } 273 | } 274 | return ret; 275 | } 276 | }; 277 | \end{Code} 278 | 279 | 280 | \subsubsection{相关题目} 281 | 282 | \begindot 283 | \item 无 284 | \myenddot 285 | 286 | 287 | \section{Best Time to Buy and Sell Stock III} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 288 | \label{sec:best-time-to-buy-and-sell-stock-iii} 289 | 290 | 291 | \subsubsection{描述} 292 | Say you have an array for which the i-th element is the price of a given stock on day i. 293 | 294 | Design an algorithm to find the maximum profit. You may complete at most two transactions. 295 | 296 | Note: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). 297 | 298 | 299 | \subsubsection{分析} 300 | 设状态$f(i)$,表示区间$[0,i](0 \leq i \leq n-1)$的最大利润,状态$g(i)$,表示区间$[i, n-1](0 \leq i \leq n-1)$的最大利润,则最终答案为$\max\left\{f(i)+g(i)\right\},0 \leq i \leq n-1$。 301 | 302 | 允许在一天内买进又卖出,相当于不交易,因为题目的规定是最多两次,而不是一定要两次。 303 | 304 | 将原数组变成差分数组,本题也可以看做是最大$m$子段和,$m=2$,参考代码:\myurl{https://gist.github.com/soulmachine/5906637} 305 | 306 | \subsubsection{代码} 307 | \begin{Code} 308 | // LeetCode, Best Time to Buy and Sell Stock III 309 | // 时间复杂度O(n),空间复杂度O(n) 310 | class Solution { 311 | public: 312 | int maxProfit(vector& prices) { 313 | if (prices.size() < 2) return 0; 314 | 315 | const int n = prices.size(); 316 | vector f(n, 0); 317 | vector g(n, 0); 318 | 319 | for (int i = 1, valley = prices[0]; i < n; ++i) { 320 | valley = min(valley, prices[i]); 321 | f[i] = max(f[i - 1], prices[i] - valley); 322 | } 323 | 324 | for (int i = n - 2, peak = prices[n - 1]; i >= 0; --i) { 325 | peak = max(peak, prices[i]); 326 | g[i] = max(g[i], peak - prices[i]); 327 | } 328 | 329 | int max_profit = 0; 330 | for (int i = 0; i < n; ++i) 331 | max_profit = max(max_profit, f[i] + g[i]); 332 | 333 | return max_profit; 334 | } 335 | }; 336 | \end{Code} 337 | 338 | 339 | \subsubsection{相关题目} 340 | \begindot 341 | \item Best Time to Buy and Sell Stock,见 \S \ref{sec:best-time-to-buy-and-sell-stock} 342 | \item Best Time to Buy and Sell Stock II,见 \S \ref{sec:best-time-to-buy-and-sell-stock-ii} 343 | \myenddot 344 | 345 | 346 | \section{Interleaving String} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 347 | \label{sec:interleaving-string} 348 | 349 | 350 | \subsubsection{描述} 351 | Given $s1, s2, s3$, find whether $s3$ is formed by the interleaving of $s1$ and $s2$. 352 | 353 | For example, Given: \code{s1 = "aabcc", s2 = "dbbca"}, 354 | 355 | When \code{s3 = "aadbbcbcac"}, return true. 356 | 357 | When \code{s3 = "aadbbbaccc"}, return false. 358 | 359 | 360 | \subsubsection{分析} 361 | 设状态\fn{f[i][j]},表示\fn{s1[0,i]}和\fn{s2[0,j]},匹配\fn{s3[0, i+j]}。如果s1的最后一个字符等于s3的最后一个字符,则\fn{f[i][j]=f[i-1][j]};如果s2的最后一个字符等于s3的最后一个字符,则\fn{f[i][j]=f[i][j-1]}。因此状态转移方程如下: 362 | \begin{Code} 363 | f[i][j] = (s1[i - 1] == s3 [i + j - 1] && f[i - 1][j]) 364 | || (s2[j - 1] == s3 [i + j - 1] && f[i][j - 1]); 365 | \end{Code} 366 | 367 | 368 | \subsubsection{递归} 369 | \begin{Code} 370 | // LeetCode, Interleaving String 371 | // 递归,会超时,仅用来帮助理解 372 | class Solution { 373 | public: 374 | bool isInterleave(const string& s1, const string& s2, const string& s3) { 375 | if (s3.length() != s1.length() + s2.length()) 376 | return false; 377 | 378 | return isInterleave(begin(s1), end(s1), begin(s2), end(s2), 379 | begin(s3), end(s3)); 380 | } 381 | 382 | template 383 | bool isInterleave(InIt first1, InIt last1, InIt first2, InIt last2, 384 | InIt first3, InIt last3) { 385 | if (first3 == last3) 386 | return first1 == last1 && first2 == last2; 387 | 388 | return (*first1 == *first3 389 | && isInterleave(next(first1), last1, first2, last2, 390 | next(first3), last3)) 391 | || (*first2 == *first3 392 | && isInterleave(first1, last1, next(first2), last2, 393 | next(first3), last3)); 394 | } 395 | }; 396 | \end{Code} 397 | 398 | 399 | \subsubsection{动规} 400 | \begin{Code} 401 | // LeetCode, Interleaving String 402 | // 二维动规,时间复杂度O(n^2),空间复杂度O(n^2) 403 | class Solution { 404 | public: 405 | bool isInterleave(const string& s1, const string& s2, const string& s3) { 406 | if (s3.length() != s1.length() + s2.length()) 407 | return false; 408 | 409 | vector> f(s1.length() + 1, 410 | vector(s2.length() + 1, true)); 411 | 412 | for (size_t i = 1; i <= s1.length(); ++i) 413 | f[i][0] = f[i - 1][0] && s1[i - 1] == s3[i - 1]; 414 | 415 | for (size_t i = 1; i <= s2.length(); ++i) 416 | f[0][i] = f[0][i - 1] && s2[i - 1] == s3[i - 1]; 417 | 418 | for (size_t i = 1; i <= s1.length(); ++i) 419 | for (size_t j = 1; j <= s2.length(); ++j) 420 | f[i][j] = (s1[i - 1] == s3[i + j - 1] && f[i - 1][j]) 421 | || (s2[j - 1] == s3[i + j - 1] && f[i][j - 1]); 422 | 423 | return f[s1.length()][s2.length()]; 424 | } 425 | }; 426 | \end{Code} 427 | 428 | 429 | \subsubsection{动规+滚动数组} 430 | \begin{Code} 431 | // LeetCode, Interleaving String 432 | // 二维动规+滚动数组,时间复杂度O(n^2),空间复杂度O(n) 433 | class Solution { 434 | public: 435 | bool isInterleave(const string& s1, const string& s2, const string& s3) { 436 | if (s1.length() + s2.length() != s3.length()) 437 | return false; 438 | 439 | if (s1.length() < s2.length()) 440 | return isInterleave(s2, s1, s3); 441 | 442 | vector f(s2.length() + 1, true); 443 | 444 | for (size_t i = 1; i <= s2.length(); ++i) 445 | f[i] = s2[i - 1] == s3[i - 1] && f[i - 1]; 446 | 447 | for (size_t i = 1; i <= s1.length(); ++i) { 448 | f[0] = s1[i - 1] == s3[i - 1] && f[0]; 449 | 450 | for (size_t j = 1; j <= s2.length(); ++j) 451 | f[j] = (s1[i - 1] == s3[i + j - 1] && f[j]) 452 | || (s2[j - 1] == s3[i + j - 1] && f[j - 1]); 453 | } 454 | 455 | return f[s2.length()]; 456 | } 457 | }; 458 | \end{Code} 459 | 460 | 461 | \subsubsection{相关题目} 462 | \begindot 463 | \item 无 464 | \myenddot 465 | 466 | 467 | \section{Scramble String} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 468 | \label{sec:scramble-string} 469 | 470 | 471 | \subsubsection{描述} 472 | Given a string $s1$, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively. 473 | 474 | Below is one possible representation of \code{s1 = "great"}: 475 | \begin{Code} 476 | great 477 | / \ 478 | gr eat 479 | / \ / \ 480 | g r e at 481 | / \ 482 | a t 483 | \end{Code} 484 | 485 | To scramble the string, we may choose any non-leaf node and swap its two children. 486 | 487 | For example, if we choose the node \code{"gr"} and swap its two children, it produces a scrambled string \code{"rgeat"}. 488 | \begin{Code} 489 | rgeat 490 | / \ 491 | rg eat 492 | / \ / \ 493 | r g e at 494 | / \ 495 | a t 496 | \end{Code} 497 | 498 | We say that \code{"rgeat"} is a scrambled string of \code{"great"}. 499 | 500 | Similarly, if we continue to swap the children of nodes \code{"eat"} and \code{"at"}, it produces a scrambled string \code{"rgtae"}. 501 | \begin{Code} 502 | rgtae 503 | / \ 504 | rg tae 505 | / \ / \ 506 | r g ta e 507 | / \ 508 | t a 509 | \end{Code} 510 | 511 | We say that \code{"rgtae"} is a scrambled string of \code{"great"}. 512 | 513 | Given two strings $s1$ and $s2$ of the same length, determine if $s2$ is a scrambled string of $s1$. 514 | 515 | 516 | \subsubsection{分析} 517 | 首先想到的是递归(即深搜),对两个string进行分割,然后比较四对字符串。代码虽然简单,但是复杂度比较高。有两种加速策略,一种是剪枝,提前返回;一种是加缓存,缓存中间结果,即memoization(翻译为记忆化搜索)。 518 | 519 | 剪枝可以五花八门,要充分观察,充分利用信息,找到能让节点提前返回的条件。例如,判断两个字符串是否互为scamble,至少要求每个字符在两个字符串中出现的次数要相等,如果不相等则返回false。 520 | 521 | 加缓存,可以用数组或HashMap。本题维数较高,用HashMap,\fn{map}和\fn{unordered_map}均可。 522 | 523 | 既然可以用记忆化搜索,这题也一定可以用动规。设状态为\fn{f[n][i][j]},表示长度为$n$,起点为\fn{s1[i]}和起点为\fn{s2[j]}两个字符串是否互为scramble,则状态转移方程为 524 | \begin{Code} 525 | f[n][i][j]} = (f[k][i][j] && f[n-k][i+k][j+k]) 526 | || (f[k][i][j+n-k] && f[n-k][i+k][j]) 527 | \end{Code} 528 | 529 | 530 | \subsubsection{递归} 531 | 532 | \begin{Code} 533 | // LeetCode, Scramble String 534 | // 递归,会超时,仅用来帮助理解 535 | // 时间复杂度O(n^6),空间复杂度O(1) 536 | class Solution { 537 | public: 538 | bool isScramble(const string& s1, const string& s2) { 539 | return isScramble(s1.begin(), s1.end(), s2.begin()); 540 | } 541 | private: 542 | typedef string::iterator Iterator; 543 | bool isScramble(Iterator first1, Iterator last1, Iterator first2) { 544 | auto length = distance(first1, last1); 545 | auto last2 = next(first2, length); 546 | 547 | if (length == 1) return *first1 == *first2; 548 | 549 | for (int i = 1; i < length; ++i) 550 | if ((isScramble(first1, first1 + i, first2) 551 | && isScramble(first1 + i, last1, first2 + i)) 552 | || (isScramble(first1, first1 + i, last2 - i) 553 | && isScramble(first1 + i, last1, first2))) 554 | return true; 555 | 556 | return false; 557 | } 558 | }; 559 | \end{Code} 560 | 561 | 562 | \subsubsection{动规} 563 | \begin{Code} 564 | // LeetCode, Scramble String 565 | // 动规,时间复杂度O(n^3),空间复杂度O(n^3) 566 | class Solution { 567 | public: 568 | bool isScramble(const string& s1, const string& s2) { 569 | const int N = s1.size(); 570 | if (N != s2.size()) return false; 571 | 572 | // f[n][i][j],表示长度为n,起点为s1[i]和 573 | // 起点为s2[j]两个字符串是否互为scramble 574 | bool f[N + 1][N][N]; 575 | fill_n(&f[0][0][0], (N + 1) * N * N, false); 576 | 577 | for (int i = 0; i < N; i++) 578 | for (int j = 0; j < N; j++) 579 | f[1][i][j] = s1[i] == s2[j]; 580 | 581 | for (int n = 1; n <= N; ++n) { 582 | for (int i = 0; i + n <= N; ++i) { 583 | for (int j = 0; j + n <= N; ++j) { 584 | for (int k = 1; k < n; ++k) { 585 | if ((f[k][i][j] && f[n - k][i + k][j + k]) || 586 | (f[k][i][j + n - k] && f[n - k][i + k][j])) { 587 | f[n][i][j] = true; 588 | break; 589 | } 590 | } 591 | } 592 | } 593 | } 594 | return f[N][0][0]; 595 | } 596 | }; 597 | \end{Code} 598 | 599 | 600 | \subsubsection{递归+剪枝} 601 | \begin{Code} 602 | // LeetCode, Scramble String 603 | // 递归+剪枝 604 | // 时间复杂度O(n^6),空间复杂度O(1) 605 | class Solution { 606 | public: 607 | bool isScramble(const string& s1, const string& s2) { 608 | return isScramble(s1.begin(), s1.end(), s2.begin()); 609 | } 610 | private: 611 | typedef string::const_iterator Iterator; 612 | bool isScramble(Iterator first1, Iterator last1, Iterator first2) { 613 | auto length = distance(first1, last1); 614 | auto last2 = next(first2, length); 615 | if (length == 1) return *first1 == *first2; 616 | 617 | // 剪枝,提前返回 618 | int A[26]; // 每个字符的计数器 619 | fill(A, A + 26, 0); 620 | for(int i = 0; i < length; i++) A[*(first1+i)-'a']++; 621 | for(int i = 0; i < length; i++) A[*(first2+i)-'a']--; 622 | for(int i = 0; i < 26; i++) if (A[i] != 0) return false; 623 | 624 | for (int i = 1; i < length; ++i) 625 | if ((isScramble(first1, first1 + i, first2) 626 | && isScramble(first1 + i, last1, first2 + i)) 627 | || (isScramble(first1, first1 + i, last2 - i) 628 | && isScramble(first1 + i, last1, first2))) 629 | return true; 630 | 631 | return false; 632 | } 633 | }; 634 | \end{Code} 635 | 636 | 637 | \subsubsection{备忘录法} 638 | \begin{Code} 639 | // LeetCode, Scramble String 640 | // 递归+map做cache 641 | // 时间复杂度O(n^3),空间复杂度O(n^3), TLE 642 | class Solution { 643 | public: 644 | bool isScramble(const string& s1, const string& s2) { 645 | cache.clear(); 646 | return isScramble(s1.begin(), s1.end(), s2.begin()); 647 | } 648 | private: 649 | typedef string::const_iterator Iterator; 650 | map, bool> cache; 651 | 652 | bool isScramble(Iterator first1, Iterator last1, Iterator first2) { 653 | auto length = distance(first1, last1); 654 | auto last2 = next(first2, length); 655 | 656 | if (length == 1) return *first1 == *first2; 657 | 658 | for (int i = 1; i < length; ++i) 659 | if ((getOrUpdate(first1, first1 + i, first2) 660 | && getOrUpdate(first1 + i, last1, first2 + i)) 661 | || (getOrUpdate(first1, first1 + i, last2 - i) 662 | && getOrUpdate(first1 + i, last1, first2))) 663 | return true; 664 | 665 | return false; 666 | } 667 | 668 | bool getOrUpdate(Iterator first1, Iterator last1, Iterator first2) { 669 | auto key = make_tuple(first1, last1, first2); 670 | auto pos = cache.find(key); 671 | 672 | return (pos != cache.end()) ? 673 | pos->second : (cache[key] = isScramble(first1, last1, first2)); 674 | } 675 | }; 676 | \end{Code} 677 | 678 | 679 | \subsubsection{备忘录法} 680 | \begin{Code} 681 | typedef string::const_iterator Iterator; 682 | typedef tuple Key; 683 | // 定制一个哈希函数 684 | namespace std { 685 | template<> struct hash { 686 | size_t operator()(const Key & x) const { 687 | Iterator first1, last1, first2; 688 | tie(first1, last1, first2) = x; 689 | 690 | int result = *first1; 691 | result = result * 31 + *last1; 692 | result = result * 31 + *first2; 693 | result = result * 31 + *(next(first2, distance(first1, last1)-1)); 694 | return result; 695 | } 696 | }; 697 | } 698 | 699 | // LeetCode, Scramble String 700 | // 递归+unordered_map做cache,比map快 701 | // 时间复杂度O(n^3),空间复杂度O(n^3) 702 | class Solution { 703 | public: 704 | unordered_map cache; 705 | 706 | bool isScramble(const string& s1, const string& s2) { 707 | cache.clear(); 708 | return isScramble(s1.begin(), s1.end(), s2.begin()); 709 | } 710 | 711 | bool isScramble(Iterator first1, Iterator last1, Iterator first2) { 712 | auto length = distance(first1, last1); 713 | auto last2 = next(first2, length); 714 | 715 | if (length == 1) 716 | return *first1 == *first2; 717 | 718 | for (int i = 1; i < length; ++i) 719 | if ((getOrUpdate(first1, first1 + i, first2) 720 | && getOrUpdate(first1 + i, last1, first2 + i)) 721 | || (getOrUpdate(first1, first1 + i, last2 - i) 722 | && getOrUpdate(first1 + i, last1, first2))) 723 | return true; 724 | 725 | return false; 726 | } 727 | 728 | bool getOrUpdate(Iterator first1, Iterator last1, Iterator first2) { 729 | auto key = make_tuple(first1, last1, first2); 730 | auto pos = cache.find(key); 731 | 732 | return (pos != cache.end()) ? 733 | pos->second : (cache[key] = isScramble(first1, last1, first2)); 734 | } 735 | }; 736 | \end{Code} 737 | 738 | 739 | \subsubsection{相关题目} 740 | \begindot 741 | \item 无 742 | \myenddot 743 | 744 | 745 | \section{Minimum Path Sum} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 746 | \label{sec:minimum-path-sum} 747 | 748 | 749 | \subsubsection{描述} 750 | Given a $m \times n$ grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path. 751 | 752 | Note: You can only move either down or right at any point in time 753 | 754 | 755 | \subsubsection{分析} 756 | 跟 Unique Paths (见 \S \ref{sec:unique-paths}) 很类似。 757 | 758 | 设状态为\fn{f[i][j]},表示从起点$(0,0)$到达$(i,j)$的最小路径和,则状态转移方程为: 759 | \begin{Code} 760 | f[i][j]=min(f[i-1][j], f[i][j-1])+grid[i][j] 761 | \end{Code} 762 | 763 | 764 | \subsubsection{备忘录法} 765 | \begin{Code} 766 | // LeetCode, Minimum Path Sum 767 | // 备忘录法 768 | class Solution { 769 | public: 770 | int minPathSum(vector > &grid) { 771 | const int m = grid.size(); 772 | const int n = grid[0].size(); 773 | this->f = vector >(m, vector(n, -1)); 774 | return dfs(grid, m-1, n-1); 775 | } 776 | private: 777 | vector > f; // 缓存 778 | 779 | int dfs(const vector > &grid, int x, int y) { 780 | if (x < 0 || y < 0) return INT_MAX; // 越界,终止条件,注意,不是0 781 | 782 | if (x == 0 && y == 0) return grid[0][0]; // 回到起点,收敛条件 783 | 784 | return min(getOrUpdate(grid, x - 1, y), 785 | getOrUpdate(grid, x, y - 1)) + grid[x][y]; 786 | } 787 | 788 | int getOrUpdate(const vector > &grid, int x, int y) { 789 | if (x < 0 || y < 0) return INT_MAX; // 越界,注意,不是0 790 | if (f[x][y] >= 0) return f[x][y]; 791 | else return f[x][y] = dfs(grid, x, y); 792 | } 793 | }; 794 | \end{Code} 795 | 796 | 797 | \subsubsection{动规} 798 | \begin{Code} 799 | // LeetCode, Minimum Path Sum 800 | // 二维动规 801 | class Solution { 802 | public: 803 | int minPathSum(vector > &grid) { 804 | if (grid.size() == 0) return 0; 805 | const int m = grid.size(); 806 | const int n = grid[0].size(); 807 | 808 | int f[m][n]; 809 | f[0][0] = grid[0][0]; 810 | for (int i = 1; i < m; i++) { 811 | f[i][0] = f[i - 1][0] + grid[i][0]; 812 | } 813 | for (int i = 1; i < n; i++) { 814 | f[0][i] = f[0][i - 1] + grid[0][i]; 815 | } 816 | 817 | for (int i = 1; i < m; i++) { 818 | for (int j = 1; j < n; j++) { 819 | f[i][j] = min(f[i - 1][j], f[i][j - 1]) + grid[i][j]; 820 | } 821 | } 822 | return f[m - 1][n - 1]; 823 | } 824 | }; 825 | \end{Code} 826 | 827 | 828 | \subsubsection{动规+滚动数组} 829 | \begin{Code} 830 | // LeetCode, Minimum Path Sum 831 | // 二维动规+滚动数组 832 | class Solution { 833 | public: 834 | int minPathSum(vector > &grid) { 835 | const int m = grid.size(); 836 | const int n = grid[0].size(); 837 | 838 | int f[n]; 839 | fill(f, f+n, INT_MAX); // 初始值是 INT_MAX,因为后面用了min函数。 840 | f[0] = 0; 841 | 842 | for (int i = 0; i < m; i++) { 843 | f[0] += grid[i][0]; 844 | for (int j = 1; j < n; j++) { 845 | // 左边的f[j],表示更新后的f[j],与公式中的f[i[[j]对应 846 | // 右边的f[j],表示老的f[j],与公式中的f[i-1][j]对应 847 | f[j] = min(f[j - 1], f[j]) + grid[i][j]; 848 | } 849 | } 850 | return f[n - 1]; 851 | } 852 | }; 853 | \end{Code} 854 | 855 | \subsubsection{相关题目} 856 | \begindot 857 | \item Unique Paths, 见 \S \ref{sec:unique-paths} 858 | \item Unique Paths II, 见 \S \ref{sec:unique-paths-ii} 859 | \myenddot 860 | 861 | 862 | \section{Edit Distance} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 863 | \label{sec:edit-distance} 864 | 865 | 866 | \subsubsection{描述} 867 | Given two words \fn{word1} and \fn{word2}, find the minimum number of steps required to convert \fn{word1} to \fn{word2}. (each operation is counted as 1 step.) 868 | 869 | You have the following 3 operations permitted on a word: 870 | \begindot 871 | \item Insert a character 872 | \item Delete a character 873 | \item Replace a character 874 | \myenddot 875 | 876 | 877 | \subsubsection{分析} 878 | 设状态为\fn{f[i][j]},表示\fn{A[0,i]}和\fn{B[0,j]}之间的最小编辑距离。设\fn{A[0,i]}的形式是\fn{str1c},\fn{B[0,j]}的形式是\fn{str2d}, 879 | \begin{enumerate} 880 | \item 如果\fn{c==d},则\fn{f[i][j]=f[i-1][j-1]}; 881 | \item 如果\fn{c!=d}, 882 | \begin{enumerate} 883 | \item 如果将c替换成d,则\fn{f[i][j]=f[i-1][j-1]+1}; 884 | \item 如果在c后面添加一个d,则\fn{f[i][j]=f[i][j-1]+1}; 885 | \item 如果将c删除,则\fn{f[i][j]=f[i-1][j]+1}; 886 | \end{enumerate} 887 | \end{enumerate} 888 | 889 | 890 | \subsubsection{动规} 891 | \begin{Code} 892 | // LeetCode, Edit Distance 893 | // 二维动规,时间复杂度O(n*m),空间复杂度O(n*m) 894 | class Solution { 895 | public: 896 | int minDistance(const string &word1, const string &word2) { 897 | const size_t n = word1.size(); 898 | const size_t m = word2.size(); 899 | // 长度为n的字符串,有n+1个隔板 900 | int f[n + 1][m + 1]; 901 | for (size_t i = 0; i <= n; i++) 902 | f[i][0] = i; 903 | for (size_t j = 0; j <= m; j++) 904 | f[0][j] = j; 905 | 906 | for (size_t i = 1; i <= n; i++) { 907 | for (size_t j = 1; j <= m; j++) { 908 | if (word1[i - 1] == word2[j - 1]) 909 | f[i][j] = f[i - 1][j - 1]; 910 | else { 911 | int mn = min(f[i - 1][j], f[i][j - 1]); 912 | f[i][j] = 1 + min(f[i - 1][j - 1], mn); 913 | } 914 | } 915 | } 916 | return f[n][m]; 917 | } 918 | }; 919 | \end{Code} 920 | 921 | 922 | \subsubsection{动规+滚动数组} 923 | \begin{Code} 924 | // LeetCode, Edit Distance 925 | // 二维动规+滚动数组 926 | // 时间复杂度O(n*m),空间复杂度O(n) 927 | class Solution { 928 | public: 929 | int minDistance(const string &word1, const string &word2) { 930 | if (word1.length() < word2.length()) 931 | return minDistance(word2, word1); 932 | 933 | int f[word2.length() + 1]; 934 | int upper_left = 0; // 额外用一个变量记录f[i-1][j-1] 935 | 936 | for (size_t i = 0; i <= word2.size(); ++i) 937 | f[i] = i; 938 | 939 | for (size_t i = 1; i <= word1.size(); ++i) { 940 | upper_left = f[0]; 941 | f[0] = i; 942 | 943 | for (size_t j = 1; j <= word2.size(); ++j) { 944 | int upper = f[j]; 945 | 946 | if (word1[i - 1] == word2[j - 1]) 947 | f[j] = upper_left; 948 | else 949 | f[j] = 1 + min(upper_left, min(f[j], f[j - 1])); 950 | 951 | upper_left = upper; 952 | } 953 | } 954 | 955 | return f[word2.length()]; 956 | } 957 | }; 958 | \end{Code} 959 | 960 | 961 | \subsubsection{相关题目} 962 | \begindot 963 | \item 无 964 | \myenddot 965 | 966 | 967 | \section{Decode Ways} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 968 | \label{sec:decode-ways} 969 | 970 | 971 | \subsubsection{描述} 972 | A message containing letters from \fn{A-Z} is being encoded to numbers using the following mapping: 973 | \begin{Code} 974 | 'A' -> 1 975 | 'B' -> 2 976 | ... 977 | 'Z' -> 26 978 | \end{Code} 979 | 980 | Given an encoded message containing digits, determine the total number of ways to decode it. 981 | 982 | For example, 983 | Given encoded message \fn{"12"}, it could be decoded as \fn{"AB"} (1 2) or \fn{"L"} (12). 984 | 985 | The number of ways decoding \fn{"12"} is 2. 986 | 987 | 988 | \subsubsection{分析} 989 | 跟 Climbing Stairs (见 \S \ref{sec:climbing-stairs})很类似,不过多加一些判断逻辑。 990 | 991 | 992 | \subsubsection{代码} 993 | \begin{Code} 994 | // LeetCode, Decode Ways 995 | // 动规,时间复杂度O(n),空间复杂度O(1) 996 | class Solution { 997 | public: 998 | int numDecodings(const string &s) { 999 | if (s.empty() || s[0] == '0') return 0; 1000 | 1001 | int prev = 0; 1002 | int cur = 1; 1003 | // 长度为n的字符串,有 n+1个阶梯 1004 | for (size_t i = 1; i <= s.size(); ++i) { 1005 | if (s[i-1] == '0') cur = 0; 1006 | 1007 | if (i < 2 || !(s[i - 2] == '1' || 1008 | (s[i - 2] == '2' && s[i - 1] <= '6'))) 1009 | prev = 0; 1010 | 1011 | int tmp = cur; 1012 | cur = prev + cur; 1013 | prev = tmp; 1014 | } 1015 | return cur; 1016 | } 1017 | }; 1018 | \end{Code} 1019 | 1020 | 1021 | \subsubsection{相关题目} 1022 | \begindot 1023 | \item Climbing Stairs, 见 \S \ref{sec:climbing-stairs} 1024 | \myenddot 1025 | 1026 | 1027 | \section{Distinct Subsequences} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1028 | \label{sec:distinct-subsequences} 1029 | 1030 | 1031 | \subsubsection{描述} 1032 | Given a string $S$ and a string $T$, count the number of distinct subsequences of $T$ in $S$. 1033 | 1034 | A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, \fn{"ACE"} is a subsequence of \fn{"ABCDE"} while \fn{"AEC"} is not). 1035 | 1036 | Here is an example: 1037 | $S$ = \fn{"rabbbit"}, $T$ = \fn{"rabbit"} 1038 | 1039 | Return 3. 1040 | 1041 | 1042 | \subsubsection{分析} 1043 | 设状态为$f(i,j)$,表示\fn{T[0,j]}在\fn{S[0,i]}里出现的次数。首先,无论\fn{S[i]}和\fn{T[j]}是否相等,若不使用\fn{S[i]},则$f(i,j)=f(i-1,j)$;若\fn{S[i]==T[j]},则可以使用\fn{S[i]},此时$f(i,j)=f(i-1,j)+f(i-1, j-1)$。 1044 | 1045 | 1046 | \subsubsection{代码} 1047 | \begin{Code} 1048 | // LeetCode, Distinct Subsequences 1049 | // 二维动规+滚动数组 1050 | // 时间复杂度O(m*n),空间复杂度O(n) 1051 | class Solution { 1052 | public: 1053 | int numDistinct(const string &S, const string &T) { 1054 | vector f(T.size() + 1); 1055 | f[0] = 1; 1056 | for (int i = 0; i < S.size(); ++i) { 1057 | for (int j = T.size() - 1; j >= 0; --j) { 1058 | f[j + 1] += S[i] == T[j] ? f[j] : 0; 1059 | } 1060 | } 1061 | 1062 | return f[T.size()]; 1063 | } 1064 | }; 1065 | \end{Code} 1066 | 1067 | 1068 | \subsubsection{相关题目} 1069 | \begindot 1070 | \item 无 1071 | \myenddot 1072 | 1073 | 1074 | \section{Word Break} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1075 | \label{sec:word-break} 1076 | 1077 | 1078 | \subsubsection{描述} 1079 | Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words. 1080 | 1081 | For example, given \\ 1082 | s = \fn{"leetcode"},\\ 1083 | dict = \fn{["leet", "code"]}. 1084 | 1085 | Return true because \fn{"leetcode"} can be segmented as \fn{"leet code"}. 1086 | 1087 | 1088 | \subsubsection{分析} 1089 | 设状态为$f(i)$,表示\fn{s[0,i]}是否可以分词,则状态转移方程为 1090 | $$ 1091 | f(i) = any\_of(f(j) \&\& s[j+1,i] \in dict), 0 \leq j < i 1092 | $$ 1093 | 1094 | 1095 | \subsubsection{深搜} 1096 | \begin{Code} 1097 | // LeetCode, Word Break 1098 | // 深搜,超时 1099 | // 时间复杂度O(2^n),空间复杂度O(n) 1100 | class Solution { 1101 | public: 1102 | bool wordBreak(string s, unordered_set &dict) { 1103 | return dfs(s, dict, 0, 0); 1104 | } 1105 | private: 1106 | static bool dfs(const string &s, unordered_set &dict, 1107 | size_t start, size_t cur) { 1108 | if (cur == s.size()) { 1109 | return dict.find(s.substr(start, cur-start+1)) != dict.end(); 1110 | } 1111 | if (dfs(s, dict, start, cur+1)) return true; 1112 | if (dict.find(s.substr(start, cur-start+1)) != dict.end()) 1113 | if (dfs(s, dict, cur+1, cur+1)) return true; 1114 | return false; 1115 | } 1116 | }; 1117 | \end{Code} 1118 | 1119 | 1120 | \subsubsection{动规} 1121 | \begin{Code} 1122 | // LeetCode, Word Break 1123 | // 动规,时间复杂度O(n^2),空间复杂度O(n) 1124 | class Solution { 1125 | public: 1126 | bool wordBreak(string s, unordered_set &dict) { 1127 | // 长度为n的字符串有n+1个隔板 1128 | vector f(s.size() + 1, false); 1129 | f[0] = true; // 空字符串 1130 | for (int i = 1; i <= s.size(); ++i) { 1131 | for (int j = i - 1; j >= 0; --j) { 1132 | if (f[j] && dict.find(s.substr(j, i - j)) != dict.end()) { 1133 | f[i] = true; 1134 | break; 1135 | } 1136 | } 1137 | } 1138 | return f[s.size()]; 1139 | } 1140 | }; 1141 | \end{Code} 1142 | 1143 | 1144 | \subsubsection{相关题目} 1145 | \begindot 1146 | \item Word Break II, 见 \S \ref{sec:word-break-ii} 1147 | \myenddot 1148 | 1149 | 1150 | \section{Word Break II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1151 | \label{sec:word-break-ii} 1152 | 1153 | 1154 | \subsubsection{描述} 1155 | Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word. 1156 | 1157 | Return all such possible sentences. 1158 | 1159 | For example, given \\ 1160 | s = \fn{"catsanddog"}, \\ 1161 | dict = \fn{["cat", "cats", "and", "sand", "dog"]}. 1162 | 1163 | A solution is \fn{["cats and dog", "cat sand dog"]}. 1164 | 1165 | 1166 | \subsubsection{分析} 1167 | 在上一题的基础上,要返回解本身。 1168 | 1169 | 1170 | \subsubsection{代码} 1171 | \begin{Code} 1172 | // LeetCode, Word Break II 1173 | // 动规,时间复杂度O(n^2),空间复杂度O(n^2) 1174 | class Solution { 1175 | public: 1176 | vector wordBreak(string s, unordered_set &dict) { 1177 | // 长度为n的字符串有n+1个隔板 1178 | vector f(s.length() + 1, false); 1179 | // prev[i][j]为true,表示s[j, i)是一个合法单词,可以从j处切开 1180 | // 第一行未用 1181 | vector > prev(s.length() + 1, vector(s.length())); 1182 | f[0] = true; 1183 | for (size_t i = 1; i <= s.length(); ++i) { 1184 | for (int j = i - 1; j >= 0; --j) { 1185 | if (f[j] && dict.find(s.substr(j, i - j)) != dict.end()) { 1186 | f[i] = true; 1187 | prev[i][j] = true; 1188 | } 1189 | } 1190 | } 1191 | vector result; 1192 | vector path; 1193 | gen_path(s, prev, s.length(), path, result); 1194 | return result; 1195 | 1196 | } 1197 | private: 1198 | // DFS遍历树,生成路径 1199 | void gen_path(const string &s, const vector > &prev, 1200 | int cur, vector &path, vector &result) { 1201 | if (cur == 0) { 1202 | string tmp; 1203 | for (auto iter = path.crbegin(); iter != path.crend(); ++iter) 1204 | tmp += *iter + " "; 1205 | tmp.erase(tmp.end() - 1); 1206 | result.push_back(tmp); 1207 | } 1208 | for (size_t i = 0; i < s.size(); ++i) { 1209 | if (prev[cur][i]) { 1210 | path.push_back(s.substr(i, cur - i)); 1211 | gen_path(s, prev, i, path, result); 1212 | path.pop_back(); 1213 | } 1214 | } 1215 | } 1216 | }; 1217 | \end{Code} 1218 | 1219 | 1220 | \subsubsection{相关题目} 1221 | \begindot 1222 | \item Word Break, 见 \S \ref{sec:word-break} 1223 | \myenddot 1224 | -------------------------------------------------------------------------------- /C++/chapGraph.tex: -------------------------------------------------------------------------------- 1 | \chapter{图} 2 | 3 | 无向图的节点定义如下: 4 | \begin{Code} 5 | // 无向图的节点 6 | struct UndirectedGraphNode { 7 | int label; 8 | vector neighbors; 9 | UndirectedGraphNode(int x) : label(x) {}; 10 | }; 11 | \end{Code} 12 | 13 | 14 | \section{Clone Graph} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | \label{sec:clone-graph} 16 | 17 | 18 | \subsubsection{描述} 19 | Clone an undirected graph. Each node in the graph contains a \code{label} and a list of its \code{neighbours}. 20 | 21 | 22 | OJ's undirected graph serialization: 23 | Nodes are labeled uniquely. 24 | 25 | We use \code{\#} as a separator for each node, and \code{,} as a separator for node label and each neighbour of the node. 26 | As an example, consider the serialized graph \code{\{0,1,2\#1,2\#2,2\}}. 27 | 28 | The graph has a total of three nodes, and therefore contains three parts as separated by \code{\#}. 29 | \begin{enumerate} 30 | \item First node is labeled as 0. Connect node 0 to both nodes 1 and 2. 31 | \item Second node is labeled as 1. Connect node 1 to node 2. 32 | \item Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle. 33 | \end{enumerate} 34 | 35 | Visually, the graph looks like the following: 36 | \begin{Code} 37 | 1 38 | / \ 39 | / \ 40 | 0 --- 2 41 | / \ 42 | \_/ 43 | \end{Code} 44 | 45 | 46 | \subsubsection{分析} 47 | 广度优先遍历或深度优先遍历都可以。 48 | 49 | 50 | \subsubsection{DFS} 51 | \begin{Code} 52 | // LeetCode, Clone Graph 53 | // DFS,时间复杂度O(n),空间复杂度O(n) 54 | class Solution { 55 | public: 56 | UndirectedGraphNode *cloneGraph(const UndirectedGraphNode *node) { 57 | if(node == nullptr) return nullptr; 58 | // key is original node,value is copied node 59 | unordered_map copied; 61 | clone(node, copied); 62 | return copied[node]; 63 | } 64 | private: 65 | // DFS 66 | static UndirectedGraphNode* clone(const UndirectedGraphNode *node, 67 | unordered_map &copied) { 69 | // a copy already exists 70 | if (copied.find(node) != copied.end()) return copied[node]; 71 | 72 | UndirectedGraphNode *new_node = new UndirectedGraphNode(node->label); 73 | copied[node] = new_node; 74 | for (auto nbr : node->neighbors) 75 | new_node->neighbors.push_back(clone(nbr, copied)); 76 | return new_node; 77 | } 78 | }; 79 | \end{Code} 80 | 81 | 82 | \subsubsection{BFS} 83 | \begin{Code} 84 | // LeetCode, Clone Graph 85 | // BFS,时间复杂度O(n),空间复杂度O(n) 86 | class Solution { 87 | public: 88 | UndirectedGraphNode *cloneGraph(const UndirectedGraphNode *node) { 89 | if (node == nullptr) return nullptr; 90 | // key is original node,value is copied node 91 | unordered_map copied; 93 | // each node in queue is already copied itself 94 | // but neighbors are not copied yet 95 | queue q; 96 | q.push(node); 97 | copied[node] = new UndirectedGraphNode(node->label); 98 | while (!q.empty()) { 99 | const UndirectedGraphNode *cur = q.front(); 100 | q.pop(); 101 | for (auto nbr : cur->neighbors) { 102 | // a copy already exists 103 | if (copied.find(nbr) != copied.end()) { 104 | copied[cur]->neighbors.push_back(copied[nbr]); 105 | } else { 106 | UndirectedGraphNode *new_node = 107 | new UndirectedGraphNode(nbr->label); 108 | copied[nbr] = new_node; 109 | copied[cur]->neighbors.push_back(new_node); 110 | q.push(nbr); 111 | } 112 | } 113 | } 114 | return copied[node]; 115 | } 116 | }; 117 | \end{Code} 118 | 119 | 120 | \subsubsection{相关题目} 121 | \begindot 122 | \item 无 123 | \myenddot 124 | -------------------------------------------------------------------------------- /C++/chapGreedy.tex: -------------------------------------------------------------------------------- 1 | \chapter{贪心法} 2 | 3 | 4 | \section{Jump Game} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | \label{sec:jump-game} 6 | 7 | 8 | \subsubsection{描述} 9 | Given an array of non-negative integers, you are initially positioned at the first index of the array. 10 | 11 | Each element in the array represents your maximum jump length at that position. 12 | 13 | Determine if you are able to reach the last index. 14 | 15 | For example: 16 | 17 | \code{A = [2,3,1,1,4]}, return true. 18 | 19 | \code{A = [3,2,1,0,4]}, return false. 20 | 21 | 22 | \subsubsection{分析} 23 | 由于每层最多可以跳\fn{A[i]}步,也可以跳0或1步,因此如果能到达最高层,则说明每一层都可以到达。有了这个条件,说明可以用贪心法。 24 | 25 | 思路一:正向,从0出发,一层一层网上跳,看最后能不能超过最高层,能超过,说明能到达,否则不能到达。 26 | 27 | 思路二:逆向,从最高层下楼梯,一层一层下降,看最后能不能下降到第0层。 28 | 29 | 思路三:如果不敢用贪心,可以用动规,设状态为\fn{f[i]},表示从第0层出发,走到\fn{A[i]}时剩余的最大步数,则状态转移方程为: 30 | $$ 31 | f[i] = max(f[i-1], A[i-1])-1, i > 0 32 | $$ 33 | 34 | 35 | \subsubsection{代码1} 36 | \begin{Code} 37 | // LeetCode, Jump Game 38 | // 思路1,时间复杂度O(n),空间复杂度O(1) 39 | class Solution { 40 | public: 41 | bool canJump(const vector& nums) { 42 | int reach = 1; // 最右能跳到哪里 43 | for (int i = 0; i < reach && reach < nums.size(); ++i) 44 | reach = max(reach, i + 1 + nums[i]); 45 | return reach >= nums.size(); 46 | } 47 | }; 48 | \end{Code} 49 | 50 | 51 | \subsubsection{代码2} 52 | \begin{Code} 53 | // LeetCode, Jump Game 54 | // 思路2,时间复杂度O(n),空间复杂度O(1) 55 | class Solution { 56 | public: 57 | bool canJump (const vector& nums) { 58 | if (nums.empty()) return true; 59 | // 逆向下楼梯,最左能下降到第几层 60 | int left_most = nums.size() - 1; 61 | 62 | for (int i = nums.size() - 2; i >= 0; --i) 63 | if (i + nums[i] >= left_most) 64 | left_most = i; 65 | 66 | return left_most == 0; 67 | } 68 | }; 69 | \end{Code} 70 | 71 | 72 | \subsubsection{代码3} 73 | \begin{Code} 74 | // LeetCode, Jump Game 75 | // 思路三,动规,时间复杂度O(n),空间复杂度O(n) 76 | class Solution { 77 | public: 78 | bool canJump(const vector& nums) { 79 | vector f(nums.size(), 0); 80 | f[0] = 0; 81 | for (int i = 1; i < nums.size(); i++) { 82 | f[i] = max(f[i - 1], nums[i - 1]) - 1; 83 | if (f[i] < 0) return false;; 84 | } 85 | return f[nums.size() - 1] >= 0; 86 | } 87 | }; 88 | \end{Code} 89 | 90 | 91 | \subsubsection{相关题目} 92 | \begindot 93 | \item Jump Game II ,见 \S \ref{sec:jump-game-ii} 94 | \myenddot 95 | 96 | 97 | \section{Jump Game II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 98 | \label{sec:jump-game-ii} 99 | 100 | 101 | \subsubsection{描述} 102 | Given an array of non-negative integers, you are initially positioned at the first index of the array. 103 | 104 | Each element in the array represents your maximum jump length at that position. 105 | 106 | Your goal is to reach the last index in the minimum number of jumps. 107 | 108 | For example: 109 | Given array \code{A = [2,3,1,1,4]} 110 | 111 | The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.) 112 | 113 | 114 | \subsubsection{分析} 115 | 贪心法。 116 | 117 | 118 | \subsubsection{代码1} 119 | \begin{Code} 120 | // LeetCode, Jump Game II 121 | // 时间复杂度O(n),空间复杂度O(1) 122 | class Solution { 123 | public: 124 | int jump(const vector& nums) { 125 | int step = 0; // 最小步数 126 | int left = 0; 127 | int right = 0; // [left, right]是当前能覆盖的区间 128 | if (nums.size() == 1) return 0; 129 | 130 | while (left <= right) { // 尝试从每一层跳最远 131 | ++step; 132 | const int old_right = right; 133 | for (int i = left; i <= old_right; ++i) { 134 | int new_right = i + nums[i]; 135 | if (new_right >= nums.size() - 1) return step; 136 | 137 | if (new_right > right) right = new_right; 138 | } 139 | left = old_right + 1; 140 | } 141 | return 0; 142 | } 143 | }; 144 | \end{Code} 145 | 146 | 147 | \subsubsection{代码2} 148 | \begin{Code} 149 | // LeetCode, Jump Game II 150 | // 时间复杂度O(n),空间复杂度O(1) 151 | class Solution { 152 | public: 153 | int jump(const vector& nums) { 154 | int result = 0; 155 | // the maximum distance that has been reached 156 | int last = 0; 157 | // the maximum distance that can be reached by using "ret+1" steps 158 | int cur = 0; 159 | for (int i = 0; i < nums.size(); ++i) { 160 | if (i > last) { 161 | last = cur; 162 | ++result; 163 | } 164 | cur = max(cur, i + nums[i]); 165 | } 166 | 167 | return result; 168 | } 169 | }; 170 | \end{Code} 171 | 172 | 173 | \subsubsection{相关题目} 174 | \begindot 175 | \item Jump Game ,见 \S \ref{sec:jump-game} 176 | \myenddot 177 | 178 | 179 | \section{Best Time to Buy and Sell Stock} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 180 | \label{sec:best-time-to-buy-and-sell-stock} 181 | 182 | 183 | \subsubsection{描述} 184 | Say you have an array for which the i-th element is the price of a given stock on day i. 185 | 186 | If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit. 187 | 188 | 189 | \subsubsection{分析} 190 | 贪心法,分别找到价格最低和最高的一天,低进高出,注意最低的一天要在最高的一天之前。 191 | 192 | 把原始价格序列变成差分序列,本题也可以做是最大$m$子段和,$m=1$。 193 | 194 | \subsubsection{代码} 195 | \begin{Code} 196 | // LeetCode, Best Time to Buy and Sell Stock 197 | // 时间复杂度O(n),空间复杂度O(1) 198 | class Solution { 199 | public: 200 | int maxProfit(vector &prices) { 201 | if (prices.size() < 2) return 0; 202 | int profit = 0; // 差价,也就是利润 203 | int cur_min = prices[0]; // 当前最小 204 | 205 | for (int i = 1; i < prices.size(); i++) { 206 | profit = max(profit, prices[i] - cur_min); 207 | cur_min = min(cur_min, prices[i]); 208 | } 209 | return profit; 210 | } 211 | }; 212 | \end{Code} 213 | 214 | 215 | \subsubsection{相关题目} 216 | \begindot 217 | \item Best Time to Buy and Sell Stock II,见 \S \ref{sec:best-time-to-buy-and-sell-stock-ii} 218 | \item Best Time to Buy and Sell Stock III,见 \S \ref{sec:best-time-to-buy-and-sell-stock-iii} 219 | \myenddot 220 | 221 | 222 | \section{Best Time to Buy and Sell Stock II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 223 | \label{sec:best-time-to-buy-and-sell-stock-ii} 224 | 225 | 226 | \subsubsection{描述} 227 | Say you have an array for which the i-th element is the price of a given stock on day i. 228 | 229 | Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). 230 | 231 | 232 | \subsubsection{分析} 233 | 贪心法,低进高出,把所有正的价格差价相加起来。 234 | 235 | 把原始价格序列变成差分序列,本题也可以做是最大$m$子段和,$m=$数组长度。 236 | 237 | \subsubsection{代码} 238 | \begin{Code} 239 | // LeetCode, Best Time to Buy and Sell Stock II 240 | // 时间复杂度O(n),空间复杂度O(1) 241 | class Solution { 242 | public: 243 | int maxProfit(vector &prices) { 244 | int sum = 0; 245 | for (int i = 1; i < prices.size(); i++) { 246 | int diff = prices[i] - prices[i - 1]; 247 | if (diff > 0) sum += diff; 248 | } 249 | return sum; 250 | } 251 | }; 252 | \end{Code} 253 | 254 | 255 | \subsubsection{相关题目} 256 | \begindot 257 | \item Best Time to Buy and Sell Stock,见 \S \ref{sec:best-time-to-buy-and-sell-stock} 258 | \item Best Time to Buy and Sell Stock III,见 \S \ref{sec:best-time-to-buy-and-sell-stock-iii} 259 | \myenddot 260 | 261 | 262 | \section{Longest Substring Without Repeating Characters} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 263 | \label{sec:longest-substring-without-repeating-characters} 264 | 265 | 266 | \subsubsection{描述} 267 | Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for \code{"abcabcbb"} is \code{"abc"}, which the length is 3. For \code{"bbbbb"} the longest substring is \code{"b"}, with the length of 1. 268 | 269 | 270 | \subsubsection{分析} 271 | 假设子串里含有重复字符,则父串一定含有重复字符,单个子问题就可以决定父问题,因此可以用贪心法。跟动规不同,动规里,单个子问题只能影响父问题,不足以决定父问题。 272 | 273 | 从左往右扫描,当遇到重复字母时,以上一个重复字母的\fn{index+1},作为新的搜索起始位置,直到最后一个字母,复杂度是$O(n)$。如图~\ref{fig:longest-substring-without-repeating-characters}所示。 274 | 275 | \begin{center} 276 | \includegraphics[width=300pt]{longest-substring-without-repeating-characters.png}\\ 277 | \figcaption{不含重复字符的最长子串}\label{fig:longest-substring-without-repeating-characters} 278 | \end{center} 279 | 280 | 281 | \subsubsection{代码} 282 | \begin{Code} 283 | // LeetCode, Longest Substring Without Repeating Characters 284 | // 时间复杂度O(n),空间复杂度O(1) 285 | // 考虑非字母的情况 286 | class Solution { 287 | public: 288 | int lengthOfLongestSubstring(string s) { 289 | const int ASCII_MAX = 255; 290 | int last[ASCII_MAX]; // 记录字符上次出现过的位置 291 | int start = 0; // 记录当前子串的起始位置 292 | 293 | fill(last, last + ASCII_MAX, -1); // 0也是有效位置,因此初始化为-1 294 | int max_len = 0; 295 | for (int i = 0; i < s.size(); i++) { 296 | if (last[s[i]] >= start) { 297 | max_len = max(i - start, max_len); 298 | start = last[s[i]] + 1; 299 | } 300 | last[s[i]] = i; 301 | } 302 | return max((int)s.size() - start, max_len); // 别忘了最后一次,例如"abcd" 303 | } 304 | }; 305 | \end{Code} 306 | 307 | 308 | \subsubsection{相关题目} 309 | \begindot 310 | \item 无 311 | \myenddot 312 | 313 | 314 | \section{Container With Most Water} 315 | \label{sec:container-with-most-water} 316 | 317 | 318 | \subsubsection{描述} 319 | Given $n$ non-negative integers $a_1, a_2, ..., a_n$, where each represents a point at coordinate $(i, a_i)$. n vertical lines are drawn such that the two endpoints of line $i$ is at $(i, a_i)$ and $(i, 0)$. Find two lines, which together with x-axis forms a container, such that the container contains the most water. 320 | 321 | Note: You may not slant the container. 322 | 323 | 324 | \subsubsection{分析} 325 | 每个容器的面积,取决于最短的木板。 326 | 327 | 328 | \subsubsection{代码} 329 | \begin{Code} 330 | // LeetCode, Container With Most Water 331 | // 时间复杂度O(n),空间复杂度O(1) 332 | class Solution { 333 | public: 334 | int maxArea(vector &height) { 335 | int start = 0; 336 | int end = height.size() - 1; 337 | int result = INT_MIN; 338 | while (start < end) { 339 | int area = min(height[end], height[start]) * (end - start); 340 | result = max(result, area); 341 | if (height[start] <= height[end]) { 342 | start++; 343 | } else { 344 | end--; 345 | } 346 | } 347 | return result; 348 | } 349 | }; 350 | \end{Code} 351 | 352 | 353 | \subsubsection{相关题目} 354 | \begindot 355 | \item Trapping Rain Water, 见 \S \ref{sec:trapping-rain-water} 356 | \item Largest Rectangle in Histogram, 见 \S \ref{sec:largest-rectangle-in-histogram} 357 | \myenddot 358 | -------------------------------------------------------------------------------- /C++/chapImplement.tex: -------------------------------------------------------------------------------- 1 | \chapter{细节实现题} 2 | 这类题目不考特定的算法,纯粹考察写代码的熟练度。 3 | 4 | 5 | \section{Reverse Integer} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 6 | \label{sec:reverse-integer} 7 | 8 | 9 | \subsubsection{描述} 10 | Reverse digits of an integer. 11 | 12 | Example1: x = 123, return 321 13 | 14 | Example2: x = -123, return -321 15 | 16 | 17 | \textbf{Have you thought about this?} 18 | 19 | Here are some good questions to ask before coding. Bonus points for you if you have already thought through this! 20 | 21 | If the integer's last digit is 0, what should the output be? ie, cases such as 10, 100. 22 | 23 | Did you notice that the reversed integer might overflow? Assume the input is a 32-bit integer, then the reverse of 1000000003 overflows. How should you handle such cases? 24 | 25 | Throw an exception? Good, but what if throwing an exception is not an option? You would then have to re-design the function (ie, add an extra parameter). 26 | 27 | 28 | \subsubsection{分析} 29 | 短小精悍的题,代码也可以写的很短小。 30 | 31 | 32 | \subsubsection{代码} 33 | \begin{Code} 34 | //LeetCode, Reverse Integer 35 | // 时间复杂度O(logn),空间复杂度O(1) 36 | // 考虑 1.负数的情况 2. 溢出的情况(正溢出&&负溢出,比如 x = -2147483648(即-2^31) ) 37 | class Solution { 38 | public: 39 | int reverse (int x) { 40 | long long r = 0; 41 | long long t = x; 42 | t = t > 0 ? t : -t; 43 | for (; t; t /= 10) 44 | r = r * 10 + t % 10; 45 | 46 | bool sign = x > 0 ? false: true; 47 | if (r > 2147483647 || (sign && r > 2147483648)) { 48 | return 0; 49 | } else { 50 | if (sign) { 51 | return -r; 52 | } else { 53 | return r; 54 | } 55 | } 56 | } 57 | }; 58 | \end{Code} 59 | 60 | 61 | \subsubsection{相关题目} 62 | \begindot 63 | \item Palindrome Number, 见 \S \ref{sec:palindrome-number} 64 | \myenddot 65 | 66 | 67 | \section{Palindrome Number} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 68 | \label{sec:palindrome-number} 69 | 70 | 71 | \subsubsection{描述} 72 | Determine whether an integer is a palindrome. Do this without extra space. 73 | 74 | \textbf{Some hints:} 75 | 76 | Could negative integers be palindromes? (ie, -1) 77 | 78 | If you are thinking of converting the integer to string, note the restriction of using extra space. 79 | 80 | You could also try reversing an integer. However, if you have solved the problem "Reverse Integer", you know that the reversed integer might overflow. How would you handle such case? 81 | 82 | There is a more generic way of solving this problem. 83 | 84 | 85 | \subsubsection{分析} 86 | 首先想到,可以利用上一题,将整数反转,然后与原来的整数比较,是否相等,相等则为 Palindrome 的。可是 reverse()会溢出。 87 | 88 | 正确的解法是,不断地取第一位和最后一位(10进制下)进行比较,相等则取第二位和倒数第二位,直到完成比较或者中途找到了不一致的位。 89 | 90 | 91 | \subsubsection{代码} 92 | \begin{Code} 93 | //LeetCode, Palindrome Number 94 | // 时间复杂度O(1),空间复杂度O(1) 95 | class Solution { 96 | public: 97 | bool isPalindrome(int x) { 98 | if (x < 0) return false; 99 | int d = 1; // divisor 100 | while (x / d >= 10) d *= 10; 101 | 102 | while (x > 0) { 103 | int q = x / d; // quotient 104 | int r = x % 10; // remainder 105 | if (q != r) return false; 106 | x = x % d / 10; 107 | d /= 100; 108 | } 109 | return true; 110 | } 111 | }; 112 | \end{Code} 113 | 114 | 115 | \subsubsection{相关题目} 116 | \begindot 117 | \item Reverse Integer, 见 \S \ref{sec:reverse-integer} 118 | \item Valid Palindrome, 见 \S \ref{sec:valid-palindrome} 119 | \myenddot 120 | 121 | 122 | \section{Insert Interval} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 123 | \label{sec:insert-interval} 124 | 125 | 126 | \subsubsection{描述} 127 | Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). 128 | 129 | You may assume that the intervals were initially sorted according to their start times. 130 | 131 | Example 1: 132 | Given intervals \code{[1,3],[6,9]}, insert and merge \code{[2,5]} in as \code{[1,5],[6,9]}. 133 | 134 | Example 2: 135 | Given \code{[1,2],[3,5],[6,7],[8,10],[12,16]}, insert and merge \code{[4,9]} in as \code{[1,2],[3,10],[12,16]}. 136 | 137 | This is because the new interval \code{[4,9]} overlaps with \code{[3,5],[6,7],[8,10]}. 138 | 139 | 140 | \subsubsection{分析} 141 | 无 142 | 143 | 144 | \subsubsection{代码} 145 | \begin{Code} 146 | struct Interval { 147 | int start; 148 | int end; 149 | Interval() : start(0), end(0) { } 150 | Interval(int s, int e) : start(s), end(e) { } 151 | }; 152 | 153 | //LeetCode, Insert Interval 154 | // 时间复杂度O(n),空间复杂度O(1) 155 | class Solution { 156 | public: 157 | vector insert(vector &intervals, Interval newInterval) { 158 | vector::iterator it = intervals.begin(); 159 | while (it != intervals.end()) { 160 | if (newInterval.end < it->start) { 161 | intervals.insert(it, newInterval); 162 | return intervals; 163 | } else if (newInterval.start > it->end) { 164 | it++; 165 | continue; 166 | } else { 167 | newInterval.start = min(newInterval.start, it->start); 168 | newInterval.end = max(newInterval.end, it->end); 169 | it = intervals.erase(it); 170 | } 171 | } 172 | intervals.insert(intervals.end(), newInterval); 173 | return intervals; 174 | } 175 | }; 176 | \end{Code} 177 | 178 | 179 | \subsubsection{相关题目} 180 | 181 | \begindot 182 | \item Merge Intervals,见 \S \ref{sec:merge-intervals} 183 | \myenddot 184 | 185 | 186 | \section{Merge Intervals} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 187 | \label{sec:merge-intervals} 188 | 189 | 190 | \subsubsection{描述} 191 | Given a collection of intervals, merge all overlapping intervals. 192 | 193 | For example, 194 | Given \code{[1,3],[2,6],[8,10],[15,18]}, 195 | return \code{[1,6],[8,10],[15,18]} 196 | 197 | 198 | \subsubsection{分析} 199 | 复用一下Insert Intervals的解法即可,创建一个新的interval集合,然后每次从旧的里面取一个interval出来,然后插入到新的集合中。 200 | 201 | 202 | \subsubsection{代码} 203 | \begin{Code} 204 | struct Interval { 205 | int start; 206 | int end; 207 | Interval() : start(0), end(0) { } 208 | Interval(int s, int e) : start(s), end(e) { } 209 | }; 210 | 211 | //LeetCode, Merge Interval 212 | //复用一下Insert Intervals的解法即可 213 | // 时间复杂度O(n1+n2+...),空间复杂度O(1) 214 | class Solution { 215 | public: 216 | vector merge(vector &intervals) { 217 | vector result; 218 | for (int i = 0; i < intervals.size(); i++) { 219 | insert(result, intervals[i]); 220 | } 221 | return result; 222 | } 223 | private: 224 | vector insert(vector &intervals, Interval newInterval) { 225 | vector::iterator it = intervals.begin(); 226 | while (it != intervals.end()) { 227 | if (newInterval.end < it->start) { 228 | intervals.insert(it, newInterval); 229 | return intervals; 230 | } else if (newInterval.start > it->end) { 231 | it++; 232 | continue; 233 | } else { 234 | newInterval.start = min(newInterval.start, it->start); 235 | newInterval.end = max(newInterval.end, it->end); 236 | it = intervals.erase(it); 237 | } 238 | } 239 | intervals.insert(intervals.end(), newInterval); 240 | return intervals; 241 | } 242 | }; 243 | \end{Code} 244 | 245 | 246 | \subsubsection{相关题目} 247 | 248 | \begindot 249 | \item Insert Interval,见 \S \ref{sec:insert-interval} 250 | \myenddot 251 | 252 | 253 | \section{Minimum Window Substring} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 254 | \label{sec:minimum-window-substring} 255 | 256 | 257 | \subsubsection{描述} 258 | Given a string $S$ and a string $T$, find the minimum window in $S$ which will contain all the characters in $T$ in complexity $O(n)$. 259 | 260 | For example, \code{S = "ADOBECODEBANC", T = "ABC"} 261 | 262 | Minimum window is \code{"BANC"}. 263 | 264 | Note: 265 | \begindot 266 | \item If there is no such window in $S$ that covers all characters in $T$, return the emtpy string \code{""}. 267 | \item If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in $S$. 268 | \myenddot 269 | 270 | 271 | \subsubsection{分析} 272 | 双指针,动态维护一个区间。尾指针不断往后扫,当扫到有一个窗口包含了所有$T$的字符后,然后再收缩头指针,直到不能再收缩为止。最后记录所有可能的情况中窗口最小的 273 | 274 | 275 | \subsubsection{代码} 276 | \begin{Code} 277 | // LeetCode, Minimum Window Substring 278 | // 时间复杂度O(n),空间复杂度O(1) 279 | class Solution { 280 | public: 281 | string minWindow(string S, string T) { 282 | if (S.empty()) return ""; 283 | if (S.size() < T.size()) return ""; 284 | 285 | const int ASCII_MAX = 256; 286 | int appeared_count[ASCII_MAX]; 287 | int expected_count[ASCII_MAX]; 288 | fill(appeared_count, appeared_count + ASCII_MAX, 0); 289 | fill(expected_count, expected_count + ASCII_MAX, 0); 290 | 291 | for (size_t i = 0; i < T.size(); i++) expected_count[T[i]]++; 292 | 293 | int minWidth = INT_MAX, min_start = 0; // 窗口大小,起点 294 | int wnd_start = 0; 295 | int appeared = 0; // 完整包含了一个T 296 | //尾指针不断往后扫 297 | for (size_t wnd_end = 0; wnd_end < S.size(); wnd_end++) { 298 | if (expected_count[S[wnd_end]] > 0) { // this char is a part of T 299 | appeared_count[S[wnd_end]]++; 300 | if (appeared_count[S[wnd_end]] <= expected_count[S[wnd_end]]) 301 | appeared++; 302 | } 303 | if (appeared == T.size()) { // 完整包含了一个T 304 | // 收缩头指针 305 | while (appeared_count[S[wnd_start]] > expected_count[S[wnd_start]] 306 | || expected_count[S[wnd_start]] == 0) { 307 | appeared_count[S[wnd_start]]--; 308 | wnd_start++; 309 | } 310 | if (minWidth > (wnd_end - wnd_start + 1)) { 311 | minWidth = wnd_end - wnd_start + 1; 312 | min_start = wnd_start; 313 | } 314 | } 315 | } 316 | 317 | if (minWidth == INT_MAX) return ""; 318 | else return S.substr(min_start, minWidth); 319 | } 320 | }; 321 | \end{Code} 322 | 323 | 324 | \subsubsection{相关题目} 325 | 326 | \begindot 327 | \item 无 328 | \myenddot 329 | 330 | 331 | \section{Multiply Strings} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 332 | \label{sec:multiply-strings} 333 | 334 | 335 | \subsubsection{描述} 336 | Given two numbers represented as strings, return multiplication of the numbers as a string. 337 | 338 | Note: The numbers can be arbitrarily large and are non-negative. 339 | 340 | 341 | \subsubsection{分析} 342 | 高精度乘法。 343 | 344 | 常见的做法是将字符转化为一个int,一一对应,形成一个int数组。但是这样很浪费空间,一个int32的最大值是$2^{31}-1=2147483647$,可以与9个字符对应,由于有乘法,减半,则至少可以与4个字符一一对应。一个int64可以与9个字符对应。 345 | 346 | 347 | \subsubsection{代码1} 348 | \begin{Code} 349 | // LeetCode, Multiply Strings 350 | // @author 连城 (http://weibo.com/lianchengzju) 351 | // 一个字符对应一个int 352 | // 时间复杂度O(n*m),空间复杂度O(n+m) 353 | typedef vector bigint; 354 | 355 | bigint make_bigint(string const& repr) { 356 | bigint n; 357 | transform(repr.rbegin(), repr.rend(), back_inserter(n), 358 | [](char c) { return c - '0'; }); 359 | return n; 360 | } 361 | 362 | string to_string(bigint const& n) { 363 | string str; 364 | transform(find_if(n.rbegin(), prev(n.rend()), 365 | [](char c) { return c > '\0'; }), n.rend(), back_inserter(str), 366 | [](char c) { return c + '0'; }); 367 | return str; 368 | } 369 | 370 | bigint operator*(bigint const& x, bigint const& y) { 371 | bigint z(x.size() + y.size()); 372 | 373 | for (size_t i = 0; i < x.size(); ++i) 374 | for (size_t j = 0; j < y.size(); ++j) { 375 | z[i + j] += x[i] * y[j]; 376 | z[i + j + 1] += z[i + j] / 10; 377 | z[i + j] %= 10; 378 | } 379 | 380 | return z; 381 | } 382 | 383 | class Solution { 384 | public: 385 | string multiply(string num1, string num2) { 386 | return to_string(make_bigint(num1) * make_bigint(num2)); 387 | } 388 | }; 389 | \end{Code} 390 | 391 | 392 | \subsubsection{代码2} 393 | \begin{Code} 394 | // LeetCode, Multiply Strings 395 | // 9个字符对应一个int64_t 396 | // 时间复杂度O(n*m/81),空间复杂度O((n+m)/9) 397 | /** 大整数类. */ 398 | class BigInt { 399 | public: 400 | /** 401 | * @brief 构造函数,将字符串转化为大整数. 402 | * @param[in] s 输入的字符串 403 | * @return 无 404 | */ 405 | BigInt(string s) { 406 | vector result; 407 | result.reserve(s.size() / RADIX_LEN + 1); 408 | 409 | for (int i = s.size(); i > 0; i -= RADIX_LEN) { // [i-RADIX_LEN, i) 410 | int temp = 0; 411 | const int low = max(i - RADIX_LEN, 0); 412 | for (int j = low; j < i; j++) { 413 | temp = temp * 10 + s[j] - '0'; 414 | } 415 | result.push_back(temp); 416 | } 417 | elems = result; 418 | } 419 | /** 420 | * @brief 将整数转化为字符串. 421 | * @return 字符串 422 | */ 423 | string toString() { 424 | stringstream result; 425 | bool started = false; // 用于跳过前导0 426 | for (auto i = elems.rbegin(); i != elems.rend(); i++) { 427 | if (started) { // 如果多余的0已经都跳过,则输出 428 | result << setw(RADIX_LEN) << setfill('0') << *i; 429 | } else { 430 | result << *i; 431 | started = true; // 碰到第一个非0的值,就说明多余的0已经都跳过 432 | } 433 | } 434 | 435 | if (!started) return "0"; // 当x全为0时 436 | else return result.str(); 437 | } 438 | 439 | /** 440 | * @brief 大整数乘法. 441 | * @param[in] x x 442 | * @param[in] y y 443 | * @return 大整数 444 | */ 445 | static BigInt multiply(const BigInt &x, const BigInt &y) { 446 | vector z(x.elems.size() + y.elems.size(), 0); 447 | 448 | for (size_t i = 0; i < y.elems.size(); i++) { 449 | for (size_t j = 0; j < x.elems.size(); j++) { // 用y[i]去乘以x的各位 450 | // 两数第i, j位相乘,累加到结果的第i+j位 451 | z[i + j] += y.elems[i] * x.elems[j]; 452 | 453 | if (z[i + j] >= BIGINT_RADIX) { // 看是否要进位 454 | z[i + j + 1] += z[i + j] / BIGINT_RADIX; // 进位 455 | z[i + j] %= BIGINT_RADIX; 456 | } 457 | } 458 | } 459 | while (z.back() == 0) z.pop_back(); // 没有进位,去掉最高位的0 460 | return BigInt(z); 461 | } 462 | 463 | private: 464 | typedef long long int64_t; 465 | /** 一个数组元素对应9个十进制位,即数组是亿进制的 466 | * 因为 1000000000 * 1000000000 没有超过 2^63-1 467 | */ 468 | const static int BIGINT_RADIX = 1000000000; 469 | const static int RADIX_LEN = 9; 470 | /** 万进制整数. */ 471 | vector elems; 472 | BigInt(const vector num) : elems(num) {} 473 | }; 474 | 475 | 476 | class Solution { 477 | public: 478 | string multiply(string num1, string num2) { 479 | BigInt x(num1); 480 | BigInt y(num2); 481 | return BigInt::multiply(x, y).toString(); 482 | } 483 | }; 484 | \end{Code} 485 | 486 | 487 | \subsubsection{相关题目} 488 | 489 | \begindot 490 | \item 无 491 | \myenddot 492 | 493 | 494 | \section{Substring with Concatenation of All Words} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 495 | \label{sec:substring-with-concatenation-of-all-words} 496 | 497 | 498 | \subsubsection{描述} 499 | You are given a string, $S$, and a list of words, $L$, that are all of the same length. Find all starting indices of substring(s) in $S$ that is a concatenation of each word in $L$ exactly once and without any intervening characters. 500 | 501 | For example, given: 502 | \begin{Code} 503 | S: "barfoothefoobarman" 504 | L: ["foo", "bar"] 505 | \end{Code} 506 | 507 | You should return the indices: \code{[0,9]}.(order does not matter). 508 | 509 | 510 | \subsubsection{分析} 511 | 无 512 | 513 | 514 | \subsubsection{代码} 515 | \begin{Code} 516 | // LeetCode, Substring with Concatenation of All Words 517 | // 时间复杂度O(n*m),空间复杂度O(m) 518 | class Solution { 519 | public: 520 | vector findSubstring(string s, vector& dict) { 521 | size_t wordLength = dict.front().length(); 522 | size_t catLength = wordLength * dict.size(); 523 | vector result; 524 | 525 | if (s.length() < catLength) return result; 526 | 527 | unordered_map wordCount; 528 | 529 | for (auto const& word : dict) ++wordCount[word]; 530 | 531 | for (auto i = begin(s); i <= prev(end(s), catLength); ++i) { 532 | unordered_map unused(wordCount); 533 | 534 | for (auto j = i; j != next(i, catLength); j += wordLength) { 535 | auto pos = unused.find(string(j, next(j, wordLength))); 536 | 537 | if (pos == unused.end() || pos->second == 0) break; 538 | 539 | if (--pos->second == 0) unused.erase(pos); 540 | } 541 | 542 | if (unused.size() == 0) result.push_back(distance(begin(s), i)); 543 | } 544 | 545 | return result; 546 | } 547 | }; 548 | \end{Code} 549 | 550 | 551 | \subsubsection{相关题目} 552 | 553 | \begindot 554 | \item 无 555 | \myenddot 556 | 557 | 558 | \section{Pascal's Triangle} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 559 | \label{sec:pascal-s-triangle} 560 | 561 | 562 | \subsubsection{描述} 563 | Given $numRows$, generate the first $numRows$ of Pascal's triangle. 564 | 565 | For example, given $numRows = 5$, 566 | 567 | Return 568 | \begin{Code} 569 | [ 570 | [1], 571 | [1,1], 572 | [1,2,1], 573 | [1,3,3,1], 574 | [1,4,6,4,1] 575 | ] 576 | \end{Code} 577 | 578 | 579 | \subsubsection{分析} 580 | 本题可以用队列,计算下一行时,给上一行左右各加一个0,然后下一行的每个元素,就等于左上角和右上角之和。 581 | 582 | 另一种思路,下一行第一个元素和最后一个元素赋值为1,中间的每个元素,等于上一行的左上角和右上角元素之和。 583 | 584 | 585 | \subsubsection{从左到右} 586 | \begin{Code} 587 | // LeetCode, Pascal's Triangle 588 | // 时间复杂度O(n^2),空间复杂度O(n) 589 | class Solution { 590 | public: 591 | vector > generate(int numRows) { 592 | vector > result; 593 | if(numRows == 0) return result; 594 | 595 | result.push_back(vector(1,1)); //first row 596 | 597 | for(int i = 2; i <= numRows; ++i) { 598 | vector current(i,1); // 本行 599 | const vector &prev = result[i-2]; // 上一行 600 | 601 | for(int j = 1; j < i - 1; ++j) { 602 | current[j] = prev[j-1] + prev[j]; // 左上角和右上角之和 603 | } 604 | result.push_back(current); 605 | } 606 | return result; 607 | } 608 | }; 609 | \end{Code} 610 | 611 | 612 | \subsubsection{从右到左} 613 | \begin{Code} 614 | // LeetCode, Pascal's Triangle 615 | // 时间复杂度O(n^2),空间复杂度O(n) 616 | class Solution { 617 | public: 618 | vector > generate(int numRows) { 619 | vector > result; 620 | vector array; 621 | for (int i = 1; i <= numRows; i++) { 622 | for (int j = i - 2; j > 0; j--) { 623 | array[j] = array[j - 1] + array[j]; 624 | } 625 | array.push_back(1); 626 | result.push_back(array); 627 | } 628 | return result; 629 | } 630 | }; 631 | \end{Code} 632 | 633 | 634 | \subsubsection{相关题目} 635 | \begindot 636 | \item Pascal's Triangle II,见 \S \ref{sec:pascals-triangle-ii} 637 | \myenddot 638 | 639 | 640 | \section{Pascal's Triangle II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 641 | \label{sec:pascal-s-triangle-ii} 642 | 643 | 644 | \subsubsection{描述} 645 | Given an index $k$, return the $k^{th}$ row of the Pascal's triangle. 646 | 647 | For example, given $k = 3$, 648 | 649 | Return \code{[1,3,3,1]}. 650 | 651 | Note: Could you optimize your algorithm to use only $O(k)$ extra space? 652 | 653 | 654 | \subsubsection{分析} 655 | 滚动数组。 656 | 657 | 658 | \subsubsection{代码} 659 | 660 | \begin{Code} 661 | // LeetCode, Pascal's Triangle II 662 | // 滚动数组,时间复杂度O(n^2),空间复杂度O(n) 663 | class Solution { 664 | public: 665 | vector getRow(int rowIndex) { 666 | vector array; 667 | for (int i = 0; i <= rowIndex; i++) { 668 | for (int j = i - 1; j > 0; j--){ 669 | array[j] = array[j - 1] + array[j]; 670 | } 671 | array.push_back(1); 672 | } 673 | return array; 674 | } 675 | }; 676 | \end{Code} 677 | 678 | 679 | \subsubsection{相关题目} 680 | \begindot 681 | \item Pascal's Triangle,见 \S \ref{sec:pascals-triangle} 682 | \myenddot 683 | 684 | 685 | \section{Spiral Matrix} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 686 | \label{sec:spiral-matrix} 687 | 688 | 689 | \subsubsection{描述} 690 | Given a matrix of $m \times n$ elements ($m$ rows, $n$ columns), return all elements of the matrix in spiral order. 691 | 692 | For example, 693 | Given the following matrix: 694 | \begin{Code} 695 | [ 696 | [ 1, 2, 3 ], 697 | [ 4, 5, 6 ], 698 | [ 7, 8, 9 ] 699 | ] 700 | \end{Code} 701 | You should return \fn{[1,2,3,6,9,8,7,4,5]}. 702 | 703 | 704 | \subsubsection{分析} 705 | 模拟。 706 | 707 | \subsubsection{代码} 708 | \begin{Code} 709 | // LeetCode, Spiral Matrix 710 | // @author 龚陆安 (http://weibo.com/luangong) 711 | // 时间复杂度O(n^2),空间复杂度O(1) 712 | class Solution { 713 | public: 714 | vector spiralOrder(vector >& matrix) { 715 | vector result; 716 | if (matrix.empty()) return result; 717 | int beginX = 0, endX = matrix[0].size() - 1; 718 | int beginY = 0, endY = matrix.size() - 1; 719 | while (true) { 720 | // From left to right 721 | for (int j = beginX; j <= endX; ++j) result.push_back(matrix[beginY][j]); 722 | if (++beginY > endY) break; 723 | // From top to bottom 724 | for (int i = beginY; i <= endY; ++i) result.push_back(matrix[i][endX]); 725 | if (beginX > --endX) break; 726 | // From right to left 727 | for (int j = endX; j >= beginX; --j) result.push_back(matrix[endY][j]); 728 | if (beginY > --endY) break; 729 | // From bottom to top 730 | for (int i = endY; i >= beginY; --i) result.push_back(matrix[i][beginX]); 731 | if (++beginX > endX) break; 732 | } 733 | return result; 734 | } 735 | }; 736 | \end{Code} 737 | 738 | 739 | \subsubsection{相关题目} 740 | \begindot 741 | \item Spiral Matrix II ,见 \S \ref{sec:spiral-matrix-ii} 742 | \myenddot 743 | 744 | 745 | \section{Spiral Matrix II} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 746 | \label{sec:spiral-matrix-ii} 747 | 748 | 749 | \subsubsection{描述} 750 | Given an integer $n$, generate a square matrix filled with elements from 1 to $n^2$ in spiral order. 751 | 752 | For example, 753 | Given $n = 3$, 754 | 755 | You should return the following matrix: 756 | \begin{Code} 757 | [ 758 | [ 1, 2, 3 ], 759 | [ 8, 9, 4 ], 760 | [ 7, 6, 5 ] 761 | ] 762 | \end{Code} 763 | 764 | 765 | \subsubsection{分析} 766 | 这题比上一题要简单。 767 | 768 | 769 | \subsubsection{代码1} 770 | \begin{Code} 771 | // LeetCode, Spiral Matrix II 772 | // 时间复杂度O(n^2),空间复杂度O(n^2) 773 | class Solution { 774 | public: 775 | vector > generateMatrix(int n) { 776 | vector > matrix(n, vector(n)); 777 | int begin = 0, end = n - 1; 778 | int num = 1; 779 | 780 | while (begin < end) { 781 | for (int j = begin; j < end; ++j) matrix[begin][j] = num++; 782 | for (int i = begin; i < end; ++i) matrix[i][end] = num++; 783 | for (int j = end; j > begin; --j) matrix[end][j] = num++; 784 | for (int i = end; i > begin; --i) matrix[i][begin] = num++; 785 | ++begin; 786 | --end; 787 | } 788 | 789 | if (begin == end) matrix[begin][begin] = num; 790 | 791 | return matrix; 792 | } 793 | }; 794 | \end{Code} 795 | 796 | 797 | \subsubsection{代码2} 798 | \begin{Code} 799 | // LeetCode, Spiral Matrix II 800 | // @author 龚陆安 (http://weibo.com/luangong) 801 | // 时间复杂度O(n^2),空间复杂度O(n^2) 802 | class Solution { 803 | public: 804 | vector > generateMatrix(int n) { 805 | vector< vector > matrix(n, vector(n)); 806 | if (n == 0) return matrix; 807 | int beginX = 0, endX = n - 1; 808 | int beginY = 0, endY = n - 1; 809 | int num = 1; 810 | while (true) { 811 | for (int j = beginX; j <= endX; ++j) matrix[beginY][j] = num++; 812 | if (++beginY > endY) break; 813 | 814 | for (int i = beginY; i <= endY; ++i) matrix[i][endX] = num++; 815 | if (beginX > --endX) break; 816 | 817 | for (int j = endX; j >= beginX; --j) matrix[endY][j] = num++; 818 | if (beginY > --endY) break; 819 | 820 | for (int i = endY; i >= beginY; --i) matrix[i][beginX] = num++; 821 | if (++beginX > endX) break; 822 | } 823 | return matrix; 824 | } 825 | }; 826 | \end{Code} 827 | 828 | 829 | \subsubsection{相关题目} 830 | \begindot 831 | \item Spiral Matrix, 见 \S \ref{sec:spiral-matrix} 832 | \myenddot 833 | 834 | 835 | \section{ZigZag Conversion} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 836 | \label{sec:zigzag-conversion} 837 | 838 | 839 | \subsubsection{描述} 840 | The string \code{"PAYPALISHIRING"} is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility) 841 | 842 | \begin{Code} 843 | P A H N 844 | A P L S I I G 845 | Y I R 846 | \end{Code} 847 | 848 | And then read line by line: \code{"PAHNAPLSIIGYIR"} 849 | 850 | Write the code that will take a string and make this conversion given a number of rows: 851 | \begin{Code} 852 | string convert(string text, int nRows); 853 | \end{Code} 854 | \code{convert("PAYPALISHIRING", 3)} should return \code{"PAHNAPLSIIGYIR"}. 855 | 856 | 857 | \subsubsection{分析} 858 | 要找到数学规律。真正面试中,不大可能出这种问题。 859 | 860 | n=4: 861 | \begin{Code} 862 | P I N 863 | A L S I G 864 | Y A H R 865 | P I 866 | \end{Code} 867 | 868 | n=5: 869 | \begin{Code} 870 | P H 871 | A S I 872 | Y I R 873 | P L I G 874 | A N 875 | \end{Code} 876 | 877 | 所以,对于每一层垂直元素的坐标 $(i,j)= (j+1 )*n +i$;对于每两层垂直元素之间的插入元素(斜对角元素),$(i,j)= (j+1)*n -i$ 878 | 879 | 880 | \subsubsection{代码} 881 | \begin{Code} 882 | // LeetCode, ZigZag Conversion 883 | // 时间复杂度O(n),空间复杂度O(1) 884 | class Solution { 885 | public: 886 | string convert(string s, int nRows) { 887 | if (nRows <= 1 || s.size() <= 1) return s; 888 | string result; 889 | for (int i = 0; i < nRows; i++) { 890 | for (int j = 0, index = i; index < s.size(); 891 | j++, index = (2 * nRows - 2) * j + i) { 892 | result.append(1, s[index]); // 垂直元素 893 | if (i == 0 || i == nRows - 1) continue; // 斜对角元素 894 | if (index + (nRows - i - 1) * 2 < s.size()) 895 | result.append(1, s[index + (nRows - i - 1) * 2]); 896 | } 897 | } 898 | return result; 899 | } 900 | }; 901 | \end{Code} 902 | 903 | 904 | \subsubsection{相关题目} 905 | \begindot 906 | \item 无 907 | \myenddot 908 | 909 | 910 | \section{Divide Two Integers} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 911 | \label{sec:divide-two-integers} 912 | 913 | 914 | \subsubsection{描述} 915 | Divide two integers without using multiplication, division and mod operator. 916 | 917 | 918 | \subsubsection{分析} 919 | 不能用乘、除和取模,那剩下的,还有加、减和位运算。 920 | 921 | 最简单的方法,是不断减去被除数。在这个基础上,可以做一点优化,每次把被除数翻倍,从而加速。 922 | 923 | 924 | \subsubsection{代码1} 925 | \begin{Code} 926 | // LeetCode, Divide Two Integers 927 | // 时间复杂度O(logn),空间复杂度O(1) 928 | class Solution { 929 | public: 930 | int divide(int dividend, int divisor) { 931 | // 当 dividend = INT_MIN时,-dividend会溢出,所以用 long long 932 | long long a = dividend >= 0 ? dividend : -(long long)dividend; 933 | long long b = divisor >= 0 ? divisor : -(long long)divisor; 934 | 935 | // 当 dividend = INT_MIN时,divisor = -1时,结果会溢出,所以用 long long 936 | long long result = 0; 937 | while (a >= b) { 938 | long long c = b; 939 | for (int i = 0; a >= c; ++i, c <<= 1) { 940 | a -= c; 941 | result += 1 << i; 942 | } 943 | } 944 | 945 | return ((dividend^divisor) >> 31) ? (-result) : (result); 946 | } 947 | }; 948 | \end{Code} 949 | 950 | 951 | \subsubsection{代码2} 952 | \begin{Code} 953 | // LeetCode, Divide Two Integers 954 | // 时间复杂度O(logn),空间复杂度O(1) 955 | class Solution { 956 | public: 957 | int divide(int dividend, int divisor) { 958 | int result = 0; // 当 dividend = INT_MIN时,divisor = -1时,结果会溢出 959 | const bool sign = (dividend > 0 && divisor < 0) || 960 | (dividend < 0 && divisor > 0); // 异号 961 | 962 | // 当 dividend = INT_MIN时,-dividend会溢出,所以用 unsigned int 963 | unsigned int a = dividend >= 0 ? dividend : -dividend; 964 | unsigned int b = divisor >= 0 ? divisor : -divisor; 965 | 966 | while (a >= b) { 967 | int multi = 1; 968 | unsigned int bb = b; 969 | while (a >= bb) { 970 | a -= bb; 971 | result += multi; 972 | 973 | if (bb < INT_MAX >> 1) { // 防止溢出 974 | bb += bb; 975 | multi += multi; 976 | } 977 | } 978 | } 979 | if (sign) return -result; 980 | else return result; 981 | } 982 | }; 983 | \end{Code} 984 | 985 | 986 | \subsubsection{相关题目} 987 | \begindot 988 | \item 无 989 | \myenddot 990 | 991 | 992 | \section{Text Justification} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 993 | \label{sec:text-justification} 994 | 995 | 996 | \subsubsection{描述} 997 | Given an array of words and a length $L$, format the text such that each line has exactly $L$ characters and is fully (left and right) justified. 998 | 999 | You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces \fn{' '} when necessary so that each line has exactly $L$ characters. 1000 | 1001 | Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line do not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right. 1002 | 1003 | For the last line of text, it should be left justified and no extra space is inserted between words. 1004 | 1005 | For example, \\ 1006 | words: \code{["This", "is", "an", "example", "of", "text", "justification."]} \\ 1007 | L: 16. 1008 | 1009 | Return the formatted lines as: 1010 | \begin{Code} 1011 | [ 1012 | "This is an", 1013 | "example of text", 1014 | "justification. " 1015 | ] 1016 | \end{Code} 1017 | 1018 | Note: Each word is guaranteed not to exceed $L$ in length. 1019 | 1020 | Corner Cases: 1021 | \begindot 1022 | \item A line other than the last line might contain only one word. What should you do in this case? 1023 | \item In this case, that line should be left 1024 | \myenddot 1025 | 1026 | 1027 | \subsubsection{分析} 1028 | 无 1029 | 1030 | 1031 | \subsubsection{代码} 1032 | \begin{Code} 1033 | // LeetCode, Text Justification 1034 | // 时间复杂度O(n),空间复杂度O(1) 1035 | class Solution { 1036 | public: 1037 | vector fullJustify(vector &words, int L) { 1038 | vector result; 1039 | const int n = words.size(); 1040 | int begin = 0, len = 0; // 当前行的起点,当前长度 1041 | for (int i = 0; i < n; ++i) { 1042 | if (len + words[i].size() + (i - begin) > L) { 1043 | result.push_back(connect(words, begin, i - 1, len, L, false)); 1044 | begin = i; 1045 | len = 0; 1046 | } 1047 | len += words[i].size(); 1048 | } 1049 | // 最后一行不足L 1050 | result.push_back(connect(words, begin, n - 1, len, L, true)); 1051 | return result; 1052 | } 1053 | /** 1054 | * @brief 将 words[begin, end] 连成一行 1055 | * @param[in] words 单词列表 1056 | * @param[in] begin 开始 1057 | * @param[in] end 结束 1058 | * @param[in] len words[begin, end]所有单词加起来的长度 1059 | * @param[in] L 题目规定的一行长度 1060 | * @param[in] is_last 是否是最后一行 1061 | * @return 对齐后的当前行 1062 | */ 1063 | string connect(vector &words, int begin, int end, 1064 | int len, int L, bool is_last) { 1065 | string s; 1066 | int n = end - begin + 1; 1067 | for (int i = 0; i < n; ++i) { 1068 | s += words[begin + i]; 1069 | addSpaces(s, i, n - 1, L - len, is_last); 1070 | } 1071 | 1072 | if (s.size() < L) s.append(L - s.size(), ' '); 1073 | return s; 1074 | } 1075 | 1076 | /** 1077 | * @brief 添加空格. 1078 | * @param[inout]s 一行 1079 | * @param[in] i 当前空隙的序号 1080 | * @param[in] n 空隙总数 1081 | * @param[in] L 总共需要添加的空额数 1082 | * @param[in] is_last 是否是最后一行 1083 | * @return 无 1084 | */ 1085 | void addSpaces(string &s, int i, int n, int L, bool is_last) { 1086 | if (n < 1 || i > n - 1) return; 1087 | int spaces = is_last ? 1 : (L / n + (i < (L % n) ? 1 : 0)); 1088 | s.append(spaces, ' '); 1089 | } 1090 | }; 1091 | \end{Code} 1092 | 1093 | 1094 | \subsubsection{相关题目} 1095 | \begindot 1096 | \item 无 1097 | \myenddot 1098 | 1099 | 1100 | \section{Max Points on a Line} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1101 | \label{sec:max-points-on-a-line} 1102 | 1103 | 1104 | \subsubsection{描述} 1105 | Given $n$ points on a 2D plane, find the maximum number of points that lie on the same straight line. 1106 | 1107 | 1108 | \subsubsection{分析} 1109 | 暴力枚举法。两点决定一条直线,$n$个点两两组合,可以得到$\dfrac{1}{2}n(n+1)$条直线,对每一条直线,判断$n$个点是否在该直线上,从而可以得到这条直线上的点的个数,选择最大的那条直线返回。复杂度$O(n^3)$。 1110 | 1111 | 上面的暴力枚举法以“边”为中心,再看另一种暴力枚举法,以每个“点”为中心,然后遍历剩余点,找到所有的斜率,如果斜率相同,那么一定共线对每个点,用一个哈希表,key为斜率,value为该直线上的点数,计算出哈希表后,取最大值,并更新全局最大值,最后就是结果。时间复杂度$O(n^2)$,空间复杂度$O(n)$。 1112 | 1113 | 1114 | \subsubsection{以边为中心} 1115 | \begin{Code} 1116 | // LeetCode, Max Points on a Line 1117 | // 暴力枚举法,以边为中心,时间复杂度O(n^3),空间复杂度O(1) 1118 | class Solution { 1119 | public: 1120 | int maxPoints(vector &points) { 1121 | if (points.size() < 3) return points.size(); 1122 | int result = 0; 1123 | 1124 | for (int i = 0; i < points.size() - 1; i++) { 1125 | for (int j = i + 1; j < points.size(); j++) { 1126 | int sign = 0; 1127 | int a, b, c; 1128 | if (points[i].x == points[j].x) sign = 1; 1129 | else { 1130 | a = points[j].x - points[i].x; 1131 | b = points[j].y - points[i].y; 1132 | c = a * points[i].y - b * points[i].x; 1133 | } 1134 | int count = 0; 1135 | for (int k = 0; k < points.size(); k++) { 1136 | if ((0 == sign && a * points[k].y == c + b * points[k].x) || 1137 | (1 == sign&&points[k].x == points[j].x)) 1138 | count++; 1139 | } 1140 | if (count > result) result = count; 1141 | } 1142 | } 1143 | return result; 1144 | } 1145 | }; 1146 | \end{Code} 1147 | 1148 | 1149 | \subsubsection{以点为中心} 1150 | \begin{Code} 1151 | // LeetCode, Max Points on a Line 1152 | // 暴力枚举,以点为中心,时间复杂度O(n^2),空间复杂度O(n) 1153 | class Solution { 1154 | public: 1155 | int maxPoints(vector &points) { 1156 | if (points.size() < 3) return points.size(); 1157 | int result = 0; 1158 | 1159 | unordered_map slope_count; 1160 | for (int i = 0; i < points.size()-1; i++) { 1161 | slope_count.clear(); 1162 | int samePointNum = 0; // 与i重合的点 1163 | int point_max = 1; // 和i共线的最大点数 1164 | 1165 | for (int j = i + 1; j < points.size(); j++) { 1166 | double slope; // 斜率 1167 | if (points[i].x == points[j].x) { 1168 | slope = std::numeric_limits::infinity(); 1169 | if (points[i].y == points[j].y) { 1170 | ++ samePointNum; 1171 | continue; 1172 | } 1173 | } else { 1174 | slope = 1.0 * (points[i].y - points[j].y) / 1175 | (points[i].x - points[j].x); 1176 | } 1177 | 1178 | int count = 0; 1179 | if (slope_count.find(slope) != slope_count.end()) 1180 | count = ++slope_count[slope]; 1181 | else { 1182 | count = 2; 1183 | slope_count[slope] = 2; 1184 | } 1185 | 1186 | if (point_max < count) point_max = count; 1187 | } 1188 | result = max(result, point_max + samePointNum); 1189 | } 1190 | return result; 1191 | } 1192 | }; 1193 | \end{Code} 1194 | 1195 | 1196 | \subsubsection{相关题目} 1197 | \begindot 1198 | \item 无 1199 | \myenddot 1200 | -------------------------------------------------------------------------------- /C++/chapSearching.tex: -------------------------------------------------------------------------------- 1 | \chapter{查找} 2 | 3 | 4 | \section{Search for a Range} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | \label{sec:search-for-a-range} 6 | 7 | 8 | \subsubsection{描述} 9 | Given a sorted array of integers, find the starting and ending position of a given target value. 10 | 11 | Your algorithm's runtime complexity must be in the order of $O(\log n)$. 12 | 13 | If the target is not found in the array, return \code{[-1, -1]}. 14 | 15 | For example, 16 | Given \code{[5, 7, 7, 8, 8, 10]} and target value 8, 17 | return \code{[3, 4]}. 18 | 19 | 20 | \subsubsection{分析} 21 | 已经排好了序,用二分查找。 22 | 23 | 24 | \subsubsection{使用STL} 25 | \begin{Code} 26 | // LeetCode, Search for a Range 27 | // 偷懒的做法,使用STL 28 | // 时间复杂度O(logn),空间复杂度O(1) 29 | class Solution { 30 | public: 31 | vector searchRange(vector& nums, int target) { 32 | const int l = distance(nums.begin(), lower_bound(nums.begin(), nums.end(), target)); 33 | const int u = distance(nums.begin(), prev(upper_bound(nums.begin(), nums.end(), target))); 34 | if (nums[l] != target) // not found 35 | return vector { -1, -1 }; 36 | else 37 | return vector { l, u }; 38 | } 39 | }; 40 | \end{Code} 41 | 42 | 43 | \subsubsection{重新实现 lower_bound 和 upper_bound} 44 | \begin{Code} 45 | // LeetCode, Search for a Range 46 | // 重新实现 lower_bound 和 upper_bound 47 | // 时间复杂度O(logn),空间复杂度O(1) 48 | class Solution { 49 | public: 50 | vector searchRange (vector& nums, int target) { 51 | auto lower = lower_bound(nums.begin(), nums.end(), target); 52 | auto uppper = upper_bound(lower, nums.end(), target); 53 | 54 | if (lower == nums.end() || *lower != target) 55 | return vector { -1, -1 }; 56 | else 57 | return vector {distance(nums.begin(), lower), distance(nums.begin(), prev(uppper))}; 58 | } 59 | 60 | template 61 | ForwardIterator lower_bound (ForwardIterator first, 62 | ForwardIterator last, T value) { 63 | while (first != last) { 64 | auto mid = next(first, distance(first, last) / 2); 65 | 66 | if (value > *mid) first = ++mid; 67 | else last = mid; 68 | } 69 | 70 | return first; 71 | } 72 | 73 | template 74 | ForwardIterator upper_bound (ForwardIterator first, 75 | ForwardIterator last, T value) { 76 | while (first != last) { 77 | auto mid = next(first, distance (first, last) / 2); 78 | 79 | if (value >= *mid) first = ++mid; // 与 lower_bound 仅此不同 80 | else last = mid; 81 | } 82 | 83 | return first; 84 | } 85 | }; 86 | \end{Code} 87 | 88 | \subsubsection{相关题目} 89 | \begindot 90 | \item Search Insert Position, 见 \S \ref{sec:search-insert-position} 91 | \myenddot 92 | 93 | 94 | \section{Search Insert Position} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 95 | \label{sec:search-insert-position} 96 | 97 | 98 | \subsubsection{描述} 99 | Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. 100 | 101 | You may assume no duplicates in the array. 102 | 103 | Here are few examples. 104 | \begin{Code} 105 | [1,3,5,6], 5 → 2 106 | [1,3,5,6], 2 → 1 107 | [1,3,5,6], 7 → 4 108 | [1,3,5,6], 0 → 0 109 | \end{Code} 110 | 111 | 112 | \subsubsection{分析} 113 | 即\fn{std::lower_bound()}。 114 | 115 | 116 | \subsubsection{代码} 117 | \begin{Code} 118 | // LeetCode, Search Insert Position 119 | // 重新实现 lower_bound 120 | // 时间复杂度O(logn),空间复杂度O(1) 121 | class Solution { 122 | public: 123 | int searchInsert(vector& nums, int target) { 124 | return distance(nums.begin(), lower_bound(nums.begin(), nums.end(), target)); 125 | } 126 | 127 | template 128 | ForwardIterator lower_bound (ForwardIterator first, 129 | ForwardIterator last, T value) { 130 | while (first != last) { 131 | auto mid = next(first, distance(first, last) / 2); 132 | 133 | if (value > *mid) first = ++mid; 134 | else last = mid; 135 | } 136 | 137 | return first; 138 | } 139 | }; 140 | \end{Code} 141 | 142 | 143 | \subsubsection{相关题目} 144 | \begindot 145 | \item Search for a Range, 见 \S \ref{sec:search-for-a-range} 146 | \myenddot 147 | 148 | 149 | \section{Search a 2D Matrix} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 150 | \label{sec:search-a-2d-matrix} 151 | 152 | 153 | \subsubsection{描述} 154 | Write an efficient algorithm that searches for a value in an $m \times n$ matrix. This matrix has the following properties: 155 | \begindot 156 | \item Integers in each row are sorted from left to right. 157 | \item The first integer of each row is greater than the last integer of the previous row. 158 | \myenddot 159 | 160 | For example, Consider the following matrix: 161 | \begin{Code} 162 | [ 163 | [1, 3, 5, 7], 164 | [10, 11, 16, 20], 165 | [23, 30, 34, 50] 166 | ] 167 | \end{Code} 168 | Given \fn{target = 3}, return true. 169 | 170 | 171 | \subsubsection{分析} 172 | 二分查找。 173 | 174 | 175 | \subsubsection{代码} 176 | \begin{Code} 177 | // LeetCode, Search a 2D Matrix 178 | // 时间复杂度O(logn),空间复杂度O(1) 179 | class Solution { 180 | public: 181 | bool searchMatrix(const vector>& matrix, int target) { 182 | if (matrix.empty()) return false; 183 | const size_t m = matrix.size(); 184 | const size_t n = matrix.front().size(); 185 | 186 | int first = 0; 187 | int last = m * n; 188 | 189 | while (first < last) { 190 | int mid = first + (last - first) / 2; 191 | int value = matrix[mid / n][mid % n]; 192 | 193 | if (value == target) 194 | return true; 195 | else if (value < target) 196 | first = mid + 1; 197 | else 198 | last = mid; 199 | } 200 | 201 | return false; 202 | } 203 | }; 204 | \end{Code} 205 | 206 | 207 | \subsubsection{相关题目} 208 | \begindot 209 | \item 无 210 | \myenddot 211 | -------------------------------------------------------------------------------- /C++/chapSorting.tex: -------------------------------------------------------------------------------- 1 | \chapter{排序} 2 | 3 | \section{Merge Two Sorted Arrays} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | \label{sec:merge-two-sorted-arrays} 5 | 6 | 7 | \subsubsection{描述} 8 | Given two sorted integer arrays A and B, merge B into A as one sorted array. 9 | 10 | Note: 11 | You may assume that A has enough space to hold additional elements from B. The number of elements initialized in A and B are m and n respectively. 12 | 13 | 14 | \subsubsection{分析} 15 | 无 16 | 17 | 18 | \subsubsection{代码} 19 | \begin{Code} 20 | //LeetCode, Merge Sorted Array 21 | // 时间复杂度O(m+n),空间复杂度O(1) 22 | class Solution { 23 | public: 24 | void merge(vector& A, int m, vector& B, int n) { 25 | int ia = m - 1, ib = n - 1, icur = m + n - 1; 26 | while(ia >= 0 && ib >= 0) { 27 | A[icur--] = A[ia] >= B[ib] ? A[ia--] : B[ib--]; 28 | } 29 | while(ib >= 0) { 30 | A[icur--] = B[ib--]; 31 | } 32 | } 33 | }; 34 | \end{Code} 35 | 36 | 37 | \subsubsection{相关题目} 38 | \begindot 39 | \item Merge Two Sorted Lists,见 \S \ref{sec:merge-two-sorted-lists} 40 | \item Merge k Sorted Lists,见 \S \ref{sec:merge-k-sorted-lists} 41 | \myenddot 42 | 43 | 44 | \section{Merge Two Sorted Lists} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 45 | \label{sec:merge-two-sorted-lists} 46 | 47 | 48 | \subsubsection{描述} 49 | Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. 50 | 51 | 52 | \subsubsection{分析} 53 | 无 54 | 55 | 56 | \subsubsection{代码} 57 | \begin{Code} 58 | //LeetCode, Merge Two Sorted Lists 59 | // 时间复杂度O(min(m,n)),空间复杂度O(1) 60 | class Solution { 61 | public: 62 | ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { 63 | if (l1 == nullptr) return l2; 64 | if (l2 == nullptr) return l1; 65 | ListNode dummy(-1); 66 | ListNode *p = &dummy; 67 | for (; l1 != nullptr && l2 != nullptr; p = p->next) { 68 | if (l1->val > l2->val) { p->next = l2; l2 = l2->next; } 69 | else { p->next = l1; l1 = l1->next; } 70 | } 71 | p->next = l1 != nullptr ? l1 : l2; 72 | return dummy.next; 73 | } 74 | }; 75 | \end{Code} 76 | 77 | 78 | \subsubsection{相关题目} 79 | \begindot 80 | \item Merge Sorted Array \S \ref{sec:merge-sorted-array} 81 | \item Merge k Sorted Lists,见 \S \ref{sec:merge-k-sorted-lists} 82 | \myenddot 83 | 84 | 85 | \section{Merge k Sorted Lists} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 86 | \label{sec:merge-k-sorted-lists} 87 | 88 | 89 | \subsubsection{描述} 90 | Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 91 | 92 | 93 | \subsubsection{分析} 94 | 可以复用Merge Two Sorted Lists(见 \S \ref{sec:merge-two-sorted-lists})的函数 95 | 96 | 97 | \subsubsection{代码} 98 | \begin{Code} 99 | //LeetCode, Merge k Sorted Lists 100 | // 时间复杂度O(n1+n2+...),空间复杂度O(1) 101 | class Solution { 102 | public: 103 | 104 | ListNode * mergeTwo(ListNode * l1, ListNode * l2){ 105 | if(!l1) return l2; 106 | if(!l2) return l1; 107 | ListNode dummy(-1); 108 | ListNode * p = &dummy; 109 | for(; l1 && l2; p = p->next){ 110 | if(l1->val > l2->val){ 111 | p->next = l2; l2 = l2->next; 112 | } 113 | else{ 114 | p->next = l1; l1 = l1->next; 115 | } 116 | } 117 | p->next = l1 ? l1 : l2; 118 | return dummy.next; 119 | } 120 | 121 | ListNode* mergeKLists(vector& lists) { 122 | if(lists.size() == 0) return nullptr; 123 | 124 | // multi pass 125 | deque dq(lists.begin(), lists.end()); 126 | while(dq.size() > 1){ 127 | ListNode * first = dq.front(); dq.pop_front(); 128 | ListNode * second = dq.front(); dq.pop_front(); 129 | dq.push_back(mergeTwo(first,second)); 130 | } 131 | 132 | return dq.front(); 133 | } 134 | }; 135 | \end{Code} 136 | 137 | 138 | \subsubsection{相关题目} 139 | \begindot 140 | \item Merge Sorted Array \S \ref{sec:merge-sorted-array} 141 | \item Merge Two Sorted Lists,见 \S \ref{sec:merge-two-sorted-lists} 142 | \myenddot 143 | 144 | 145 | \section{Insertion Sort List} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 146 | \label{sec:insertion-sort-list} 147 | 148 | 149 | \subsubsection{描述} 150 | Sort a linked list using insertion sort. 151 | 152 | 153 | \subsubsection{分析} 154 | 无 155 | 156 | 157 | \subsubsection{代码} 158 | \begin{Code} 159 | // LeetCode, Insertion Sort List 160 | // 时间复杂度O(n^2),空间复杂度O(1) 161 | class Solution { 162 | public: 163 | ListNode *insertionSortList(ListNode *head) { 164 | ListNode dummy(INT_MIN); 165 | //dummy.next = head; 166 | 167 | for (ListNode *cur = head; cur != nullptr;) { 168 | auto pos = findInsertPos(&dummy, cur->val); 169 | ListNode *tmp = cur->next; 170 | cur->next = pos->next; 171 | pos->next = cur; 172 | cur = tmp; 173 | } 174 | return dummy.next; 175 | } 176 | 177 | ListNode* findInsertPos(ListNode *head, int x) { 178 | ListNode *pre = nullptr; 179 | for (ListNode *cur = head; cur != nullptr && cur->val <= x; 180 | pre = cur, cur = cur->next) 181 | ; 182 | return pre; 183 | } 184 | }; 185 | \end{Code} 186 | 187 | 188 | \subsubsection{相关题目} 189 | \begindot 190 | \item Sort List, 见 \S \ref{sec:Sort-List} 191 | \myenddot 192 | 193 | 194 | \section{Sort List} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 195 | \label{sec:sort-list} 196 | 197 | 198 | \subsubsection{描述} 199 | Sort a linked list in $O(n log n)$ time using constant space complexity. 200 | 201 | 202 | \subsubsection{分析} 203 | 常数空间且$O(nlogn)$,单链表适合用归并排序,双向链表适合用快速排序。本题可以复用 "Merge Two Sorted Lists" 的代码。 204 | 205 | 206 | \subsubsection{代码} 207 | \begin{Code} 208 | // LeetCode, Sort List 209 | // 归并排序,时间复杂度O(nlogn),空间复杂度O(1) 210 | class Solution { 211 | public: 212 | ListNode *sortList(ListNode *head) { 213 | if (head == NULL || head->next == NULL)return head; 214 | 215 | // 快慢指针找到中间节点 216 | ListNode *fast = head, *slow = head; 217 | while (fast->next != NULL && fast->next->next != NULL) { 218 | fast = fast->next->next; 219 | slow = slow->next; 220 | } 221 | // 断开 222 | fast = slow; 223 | slow = slow->next; 224 | fast->next = NULL; 225 | 226 | ListNode *l1 = sortList(head); // 前半段排序 227 | ListNode *l2 = sortList(slow); // 后半段排序 228 | return mergeTwoLists(l1, l2); 229 | } 230 | 231 | // Merge Two Sorted Lists 232 | ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { 233 | ListNode dummy(-1); 234 | for (ListNode* p = &dummy; l1 != nullptr || l2 != nullptr; p = p->next) { 235 | int val1 = l1 == nullptr ? INT_MAX : l1->val; 236 | int val2 = l2 == nullptr ? INT_MAX : l2->val; 237 | if (val1 <= val2) { 238 | p->next = l1; 239 | l1 = l1->next; 240 | } else { 241 | p->next = l2; 242 | l2 = l2->next; 243 | } 244 | } 245 | return dummy.next; 246 | } 247 | }; 248 | \end{Code} 249 | 250 | 251 | \subsubsection{相关题目} 252 | \begindot 253 | \item Insertion Sort List,见 \S \ref{sec:Insertion-Sort-List} 254 | \myenddot 255 | 256 | 257 | \section{First Missing Positive} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 258 | \label{sec:first-missing-positive} 259 | 260 | 261 | \subsubsection{描述} 262 | Given an unsorted integer array, find the first missing positive integer. 263 | 264 | For example, 265 | Given \fn{[1,2,0]} return \fn{3}, 266 | and \fn{[3,4,-1,1]} return \fn{2}. 267 | 268 | Your algorithm should run in $O(n)$ time and uses constant space. 269 | 270 | 271 | \subsubsection{分析} 272 | 本质上是桶排序(bucket sort),每当\fn{A[i]!= i+1}的时候,将A[i]与A[A[i]-1]交换,直到无法交换为止,终止条件是 \fn{A[i]== A[A[i]-1]}。 273 | 274 | 275 | \subsubsection{代码} 276 | \begin{Code} 277 | // LeetCode, First Missing Positive 278 | // 时间复杂度O(n),空间复杂度O(1) 279 | class Solution { 280 | public: 281 | int firstMissingPositive(vector& nums) { 282 | bucket_sort(nums); 283 | 284 | for (int i = 0; i < nums.size(); ++i) 285 | if (nums[i] != (i + 1)) 286 | return i + 1; 287 | return nums.size() + 1; 288 | } 289 | private: 290 | static void bucket_sort(vector& A) { 291 | const int n = A.size(); 292 | for (int i = 0; i < n; i++) { 293 | while (A[i] != i + 1) { 294 | if (A[i] <= 0 || A[i] > n || A[i] == A[A[i] - 1]) 295 | break; 296 | swap(A[i], A[A[i] - 1]); 297 | } 298 | } 299 | } 300 | }; 301 | \end{Code} 302 | 303 | 304 | \subsubsection{相关题目} 305 | \begindot 306 | \item Sort Colors, 见 \S \ref{sec:sort-colors} 307 | \myenddot 308 | 309 | 310 | \section{Sort Colors} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 311 | \label{sec:sort-colors} 312 | 313 | 314 | \subsubsection{描述} 315 | Given an array with $n$ objects colored red, white or blue, sort them so that objects of the same color are adjacent, with the colors in the order red, white and blue. 316 | 317 | Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively. 318 | 319 | Note: 320 | You are not suppose to use the library's sort function for this problem. 321 | 322 | \textbf{Follow up:} 323 | 324 | A rather straight forward solution is a two-pass algorithm using counting sort. 325 | 326 | First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's. 327 | 328 | Could you come up with an one-pass algorithm using only constant space? 329 | 330 | 331 | \subsubsection{分析} 332 | 由于0, 1, 2 非常紧凑,首先想到计数排序(counting sort),但需要扫描两遍,不符合题目要求。 333 | 334 | 由于只有三种颜色,可以设置两个index,一个是red的index,一个是blue的index,两边往中间走。时间复杂度$O(n)$,空间复杂度$O(1)$。 335 | 336 | 第3种思路,利用快速排序里 partition 的思想,第一次将数组按0分割,第二次按1分割,排序完毕,可以推广到$n$种颜色,每种颜色有重复元素的情况。 337 | 338 | 339 | \subsubsection{代码1} 340 | \begin{Code} 341 | // LeetCode, Sort Colors 342 | // Counting Sort 343 | // 时间复杂度O(n),空间复杂度O(1) 344 | class Solution { 345 | public: 346 | void sortColors(vector& A) { 347 | int counts[3] = { 0 }; // 记录每个颜色出现的次数 348 | 349 | for (int i = 0; i < A.size(); i++) 350 | counts[A[i]]++; 351 | 352 | for (int i = 0, index = 0; i < 3; i++) 353 | for (int j = 0; j < counts[i]; j++) 354 | A[index++] = i; 355 | 356 | } 357 | }; 358 | \end{Code} 359 | 360 | 361 | \subsubsection{代码2} 362 | \begin{Code} 363 | // LeetCode, Sort Colors 364 | // 双指针,时间复杂度O(n),空间复杂度O(1) 365 | class Solution { 366 | public: 367 | void sortColors(vector& A) { 368 | // 一个是red的index,一个是blue的index,两边往中间走 369 | int red = 0, blue = A.size() - 1; 370 | 371 | for (int i = 0; i < blue + 1;) { 372 | if (A[i] == 0) 373 | swap(A[i++], A[red++]); 374 | else if (A[i] == 2) 375 | swap(A[i], A[blue--]); 376 | else 377 | i++; 378 | } 379 | } 380 | }; 381 | \end{Code} 382 | 383 | 384 | \subsubsection{代码3} 385 | \begin{Code} 386 | // LeetCode, Sort Colors 387 | // use partition() 388 | // 时间复杂度O(n),空间复杂度O(1) 389 | class Solution { 390 | public: 391 | void sortColors(vector& nums) { 392 | partition(partition(nums.begin(), nums.end(), bind1st(equal_to(), 0)), 393 | nums.end(), bind1st(equal_to(), 1)); 394 | } 395 | }; 396 | \end{Code} 397 | 398 | 399 | \subsubsection{代码4} 400 | \begin{Code} 401 | // LeetCode, Sort Colors 402 | // 重新实现 partition() 403 | // 时间复杂度O(n),空间复杂度O(1) 404 | class Solution { 405 | public: 406 | void sortColors(vector& nums) { 407 | partition(partition(nums.begin(), nums.end(), bind1st(equal_to(), 0)), 408 | nums.end(), bind1st(equal_to(), 1)); 409 | } 410 | private: 411 | template 412 | ForwardIterator partition(ForwardIterator first, ForwardIterator last, 413 | UnaryPredicate pred) { 414 | auto pos = first; 415 | 416 | for (; first != last; ++first) 417 | if (pred(*first)) 418 | swap(*first, *pos++); 419 | 420 | return pos; 421 | } 422 | }; 423 | \end{Code} 424 | 425 | 426 | \subsubsection{相关题目} 427 | \begindot 428 | \item First Missing Positive, 见 \S \ref{sec:first-missing-positive} 429 | \myenddot 430 | -------------------------------------------------------------------------------- /C++/chapStackAndQueue.tex: -------------------------------------------------------------------------------- 1 | \chapter{栈和队列} 2 | 3 | 4 | \section{栈} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | 6 | 7 | \subsection{Valid Parentheses} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | \label{sec:valid-parentheses} 9 | 10 | 11 | \subsubsection{描述} 12 | Given a string containing just the characters \code{'(', ')', '\{', '\}', '['} and \code{']'}, determine if the input string is valid. 13 | 14 | The brackets must close in the correct order, \code{"()"} and \code{"()[]{}"} are all valid but \code{"(]"} and \code{"([)]"} are not. 15 | 16 | 17 | \subsubsection{分析} 18 | 无 19 | 20 | 21 | \subsubsection{代码} 22 | \begin{Code} 23 | // LeetCode, Valid Parentheses 24 | // 时间复杂度O(n),空间复杂度O(n) 25 | class Solution { 26 | public: 27 | bool isValid (string const& s) { 28 | string left = "([{"; 29 | string right = ")]}"; 30 | stack stk; 31 | 32 | for (auto c : s) { 33 | if (left.find(c) != string::npos) { 34 | stk.push (c); 35 | } else { 36 | if (stk.empty () || stk.top () != left[right.find (c)]) 37 | return false; 38 | else 39 | stk.pop (); 40 | } 41 | } 42 | return stk.empty(); 43 | } 44 | }; 45 | \end{Code} 46 | 47 | 48 | \subsubsection{相关题目} 49 | \begindot 50 | \item Generate Parentheses, 见 \S \ref{sec:generate-parentheses} 51 | \item Longest Valid Parentheses, 见 \S \ref{sec:longest-valid-parentheses} 52 | \myenddot 53 | 54 | 55 | \subsection{Longest Valid Parentheses} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 56 | \label{sec:longest-valid-parentheses} 57 | 58 | 59 | \subsubsection{描述} 60 | Given a string containing just the characters \code{'('} and \code{')'}, find the length of the longest valid (well-formed) parentheses substring. 61 | 62 | For \code{"(()"}, the longest valid parentheses substring is \code{"()"}, which has length = 2. 63 | 64 | Another example is \code{")()())"}, where the longest valid parentheses substring is \code{"()()"}, which has length = 4. 65 | 66 | 67 | \subsubsection{分析} 68 | 无 69 | 70 | 71 | \subsubsection{使用栈} 72 | \begin{Code} 73 | // LeetCode, Longest Valid Parenthese 74 | // 使用栈,时间复杂度O(n),空间复杂度O(n) 75 | class Solution { 76 | public: 77 | int longestValidParentheses(const string& s) { 78 | int max_len = 0, last = -1; // the position of the last ')' 79 | stack lefts; // keep track of the positions of non-matching '('s 80 | 81 | for (int i = 0; i < s.size(); ++i) { 82 | if (s[i] =='(') { 83 | lefts.push(i); 84 | } else { 85 | if (lefts.empty()) { 86 | // no matching left 87 | last = i; 88 | } else { 89 | // find a matching pair 90 | lefts.pop(); 91 | if (lefts.empty()) { 92 | max_len = max(max_len, i-last); 93 | } else { 94 | max_len = max(max_len, i-lefts.top()); 95 | } 96 | } 97 | } 98 | } 99 | return max_len; 100 | } 101 | }; 102 | \end{Code} 103 | 104 | \subsubsection{Dynamic Programming, One Pass} 105 | \begin{Code} 106 | // LeetCode, Longest Valid Parenthese 107 | // 时间复杂度O(n),空间复杂度O(n) 108 | // @author 一只杰森(http://weibo.com/wjson) 109 | class Solution { 110 | public: 111 | int longestValidParentheses(const string& s) { 112 | vector f(s.size(), 0); 113 | int ret = 0; 114 | for (int i = s.size() - 2; i >= 0; --i) { 115 | int match = i + f[i + 1] + 1; 116 | // case: "((...))" 117 | if (s[i] == '(' && match < s.size() && s[match] == ')') { 118 | f[i] = f[i + 1] + 2; 119 | // if a valid sequence exist afterwards "((...))()" 120 | if (match + 1 < s.size()) f[i] += f[match + 1]; 121 | } 122 | ret = max(ret, f[i]); 123 | } 124 | return ret; 125 | } 126 | }; 127 | \end{Code} 128 | 129 | 130 | \subsubsection{两遍扫描} 131 | \begin{Code} 132 | // LeetCode, Longest Valid Parenthese 133 | // 两遍扫描,时间复杂度O(n),空间复杂度O(1) 134 | // @author 曹鹏(http://weibo.com/cpcs) 135 | class Solution { 136 | public: 137 | int longestValidParentheses(const string& s) { 138 | int answer = 0, depth = 0, start = -1; 139 | for (int i = 0; i < s.size(); ++i) { 140 | if (s[i] == '(') { 141 | ++depth; 142 | } else { 143 | --depth; 144 | if (depth < 0) { 145 | start = i; 146 | depth = 0; 147 | } else if (depth == 0) { 148 | answer = max(answer, i - start); 149 | } 150 | } 151 | } 152 | 153 | depth = 0; 154 | start = s.size(); 155 | for (int i = s.size() - 1; i >= 0; --i) { 156 | if (s[i] == ')') { 157 | ++depth; 158 | } else { 159 | --depth; 160 | if (depth < 0) { 161 | start = i; 162 | depth = 0; 163 | } else if (depth == 0) { 164 | answer = max(answer, start - i); 165 | } 166 | } 167 | } 168 | return answer; 169 | } 170 | }; 171 | \end{Code} 172 | 173 | 174 | \subsubsection{相关题目} 175 | \begindot 176 | \item Valid Parentheses, 见 \S \ref{sec:valid-parentheses} 177 | \item Generate Parentheses, 见 \S \ref{sec:generate-parentheses} 178 | \myenddot 179 | 180 | 181 | \subsection{Largest Rectangle in Histogram} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 182 | \label{sec:largest-rectangle-in-histogram} 183 | 184 | 185 | \subsubsection{描述} 186 | Given $n$ non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram. 187 | 188 | \begin{center} 189 | \includegraphics[width=120pt]{histogram.png}\\ 190 | \figcaption{Above is a histogram where width of each bar is 1, given height = \fn{[2,1,5,6,2,3]}.}\label{fig:histogram} 191 | \end{center} 192 | 193 | \begin{center} 194 | \includegraphics[width=120pt]{histogram-area.png}\\ 195 | \figcaption{The largest rectangle is shown in the shaded area, which has area = 10 unit.}\label{fig:histogram-area} 196 | \end{center} 197 | 198 | For example, 199 | Given height = \fn{[2,1,5,6,2,3]}, 200 | return 10. 201 | 202 | 203 | \subsubsection{分析} 204 | 简单的,类似于 Container With Most Water(\S \ref{sec:container-with-most-water}),对每个柱子,左右扩展,直到碰到比自己矮的,计算这个矩形的面积,用一个变量记录最大的面积,复杂度$O(n^2)$,会超时。 205 | 206 | 如图\S \ref{fig:histogram-area}所示,从左到右处理直方,当$i=4$时,小于当前栈顶(即直方3),对于直方3,无论后面还是前面的直方,都不可能得到比目前栈顶元素更高的高度了,处理掉直方3(计算从直方3到直方4之间的矩形的面积,然后从栈里弹出);对于直方2也是如此;直到碰到比直方4更矮的直方1。 207 | 208 | 这就意味着,可以维护一个递增的栈,每次比较栈顶与当前元素。如果当前元素大于栈顶元素,则入栈,否则合并现有栈,直至栈顶元素小于当前元素。结尾时入栈元素0,重复合并一次。 209 | 210 | 211 | \subsubsection{代码} 212 | \begin{Code} 213 | // LeetCode, Largest Rectangle in Histogram 214 | // 时间复杂度O(n),空间复杂度O(n) 215 | class Solution { 216 | public: 217 | int largestRectangleArea(vector &height) { 218 | stack s; 219 | height.push_back(0); 220 | int result = 0; 221 | for (int i = 0; i < height.size(); ) { 222 | if (s.empty() || height[i] > height[s.top()]) 223 | s.push(i++); 224 | else { 225 | int tmp = s.top(); 226 | s.pop(); 227 | result = max(result, 228 | height[tmp] * (s.empty() ? i : i - s.top() - 1)); 229 | } 230 | } 231 | return result; 232 | } 233 | }; 234 | \end{Code} 235 | 236 | 237 | \subsubsection{相关题目} 238 | \begindot 239 | \item Trapping Rain Water, 见 \S \ref{sec:trapping-rain-water} 240 | \item Container With Most Water, 见 \S \ref{sec:container-with-most-water} 241 | \myenddot 242 | 243 | 244 | \subsection{Evaluate Reverse Polish Notation} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 245 | \label{sec:evaluate-reverse-polish-notation} 246 | 247 | 248 | \subsubsection{描述} 249 | Evaluate the value of an arithmetic expression in Reverse Polish Notation. 250 | 251 | Valid operators are \fn{+, -, *, /}. Each operand may be an integer or another expression. 252 | 253 | Some examples: 254 | \begin{Code} 255 | ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 256 | ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6 257 | \end{Code} 258 | 259 | 260 | \subsubsection{分析} 261 | 无 262 | 263 | 264 | \subsubsection{递归版} 265 | \begin{Code} 266 | // LeetCode, Evaluate Reverse Polish Notation 267 | // 递归,时间复杂度O(n),空间复杂度O(logn) 268 | class Solution { 269 | public: 270 | int evalRPN(vector &tokens) { 271 | int x, y; 272 | auto token = tokens.back(); tokens.pop_back(); 273 | if (is_operator(token)) { 274 | y = evalRPN(tokens); 275 | x = evalRPN(tokens); 276 | if (token[0] == '+') x += y; 277 | else if (token[0] == '-') x -= y; 278 | else if (token[0] == '*') x *= y; 279 | else x /= y; 280 | } else { 281 | size_t i; 282 | x = stoi(token, &i); 283 | } 284 | return x; 285 | } 286 | private: 287 | bool is_operator(const string &op) { 288 | return op.size() == 1 && string("+-*/").find(op) != string::npos; 289 | } 290 | }; 291 | \end{Code} 292 | 293 | 294 | \subsubsection{迭代版} 295 | \begin{Code} 296 | // LeetCode, Max Points on a Line 297 | // 迭代,时间复杂度O(n),空间复杂度O(logn) 298 | class Solution { 299 | public: 300 | int evalRPN(vector &tokens) { 301 | stack s; 302 | for (auto token : tokens) { 303 | if (!is_operator(token)) { 304 | s.push(token); 305 | } else { 306 | int y = stoi(s.top()); 307 | s.pop(); 308 | int x = stoi(s.top()); 309 | s.pop(); 310 | if (token[0] == '+') x += y; 311 | else if (token[0] == '-') x -= y; 312 | else if (token[0] == '*') x *= y; 313 | else x /= y; 314 | s.push(to_string(x)); 315 | } 316 | } 317 | return stoi(s.top()); 318 | } 319 | private: 320 | bool is_operator(const string &op) { 321 | return op.size() == 1 && string("+-*/").find(op) != string::npos; 322 | } 323 | }; 324 | \end{Code} 325 | 326 | 327 | \subsubsection{相关题目} 328 | \begindot 329 | \item 无 330 | \myenddot 331 | 332 | 333 | \section{队列} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 334 | 335 | -------------------------------------------------------------------------------- /C++/chapString.tex: -------------------------------------------------------------------------------- 1 | \chapter{字符串} 2 | 3 | 4 | \section{Valid Palindrome} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | \label{sec:valid-palindrome} 6 | 7 | 8 | \subsubsection{描述} 9 | Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. 10 | 11 | For example,\\ 12 | \code{"A man, a plan, a canal: Panama"} is a palindrome.\\ 13 | \code{"race a car"} is not a palindrome. 14 | 15 | Note: 16 | Have you consider that the string might be empty? This is a good question to ask during an interview. 17 | 18 | For the purpose of this problem, we define empty string as valid palindrome. 19 | 20 | 21 | \subsubsection{分析} 22 | 无 23 | 24 | 25 | \subsubsection{代码} 26 | \begin{Code} 27 | // Leet Code, Valid Palindrome 28 | // 时间复杂度O(n),空间复杂度O(1) 29 | class Solution { 30 | public: 31 | bool isPalindrome(string s) { 32 | transform(s.begin(), s.end(), s.begin(), ::tolower); 33 | auto left = s.begin(), right = prev(s.end()); 34 | while (left < right) { 35 | if (!::isalnum(*left)) ++left; 36 | else if (!::isalnum(*right)) --right; 37 | else if (*left != *right) return false; 38 | else { left++, right--; } 39 | } 40 | return true; 41 | } 42 | }; 43 | \end{Code} 44 | 45 | 46 | \subsubsection{相关题目} 47 | \begindot 48 | \item Palindrome Number, 见 \S \ref{sec:palindrome-number} 49 | \myenddot 50 | 51 | 52 | \section{Implement strStr()} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 53 | \label{sec:strstr} 54 | 55 | 56 | \subsubsection{描述} 57 | Implement strStr(). 58 | 59 | Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack. 60 | 61 | 62 | \subsubsection{分析} 63 | 暴力算法的复杂度是 $O(m*n)$,代码如下。更高效的的算法有KMP算法、Boyer-Mooer算法和Rabin-Karp算法。面试中暴力算法足够了,一定要写得没有BUG。 64 | 65 | 66 | \subsubsection{暴力匹配} 67 | \begin{Code} 68 | // LeetCode, Implement strStr() 69 | // 暴力解法,时间复杂度O(N*M),空间复杂度O(1) 70 | class Solution { 71 | public: 72 | int strStr(const string& haystack, const string& needle) { 73 | if (needle.empty()) return 0; 74 | 75 | const int N = haystack.size() - needle.size() + 1; 76 | for (int i = 0; i < N; i++) { 77 | int j = i; 78 | int k = 0; 79 | while (j < haystack.size() && k < needle.size() && haystack[j] == needle[k]) { 80 | j++; 81 | k++; 82 | } 83 | if (k == needle.size()) return i; 84 | } 85 | return -1; 86 | } 87 | }; 88 | \end{Code} 89 | 90 | 91 | \subsubsection{KMP} 92 | \begin{Code} 93 | // LeetCode, Implement strStr() 94 | // KMP,时间复杂度O(N+M),空间复杂度O(M) 95 | class Solution { 96 | public: 97 | int strStr(const string& haystack, const string& needle) { 98 | return kmp(haystack.c_str(), needle.c_str()); 99 | } 100 | private: 101 | /* 102 | * @brief 计算部分匹配表,即next数组. 103 | * 104 | * @param[in] pattern 模式串 105 | * @param[out] next next数组 106 | * @return 无 107 | */ 108 | static void compute_prefix(const char *pattern, int next[]) { 109 | int i; 110 | int j = -1; 111 | const int m = strlen(pattern); 112 | 113 | next[0] = j; 114 | for (i = 1; i < m; i++) { 115 | while (j > -1 && pattern[j + 1] != pattern[i]) j = next[j]; 116 | 117 | if (pattern[i] == pattern[j + 1]) j++; 118 | next[i] = j; 119 | } 120 | } 121 | 122 | /* 123 | * @brief KMP算法. 124 | * 125 | * @param[in] text 文本 126 | * @param[in] pattern 模式串 127 | * @return 成功则返回第一次匹配的位置,失败则返回-1 128 | */ 129 | static int kmp(const char *text, const char *pattern) { 130 | int i; 131 | int j = -1; 132 | const int n = strlen(text); 133 | const int m = strlen(pattern); 134 | if (n == 0 && m == 0) return 0; /* "","" */ 135 | if (m == 0) return 0; /* "a","" */ 136 | int *next = (int*)malloc(sizeof(int) * m); 137 | 138 | compute_prefix(pattern, next); 139 | 140 | for (i = 0; i < n; i++) { 141 | while (j > -1 && pattern[j + 1] != text[i]) j = next[j]; 142 | 143 | if (text[i] == pattern[j + 1]) j++; 144 | if (j == m - 1) { 145 | free(next); 146 | return i-j; 147 | } 148 | } 149 | 150 | free(next); 151 | return -1; 152 | } 153 | }; 154 | \end{Code} 155 | 156 | 157 | \subsubsection{相关题目} 158 | \begindot 159 | \item String to Integer (atoi) ,见 \S \ref{sec:string-to-integer} 160 | \myenddot 161 | 162 | 163 | \section{String to Integer (atoi)} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 164 | \label{sec:string-to-integer} 165 | 166 | 167 | \subsubsection{描述} 168 | Implement \fn{atoi} to convert a string to an integer. 169 | 170 | \textbf{Hint}: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases. 171 | 172 | \textbf{Notes}: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front. 173 | 174 | \textbf{Requirements for atoi}: 175 | 176 | The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value. 177 | 178 | The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function. 179 | 180 | If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed. 181 | 182 | If no valid conversion could be performed, a zero value is returned. If the correct value is out of the range of representable values, \code{INT_MAX (2147483647)} or \code{INT_MIN (-2147483648)} is returned. 183 | 184 | \subsubsection{分析} 185 | 细节题。注意几个测试用例: 186 | \begin{enumerate} 187 | \item 不规则输入,但是有效,"-3924x8fc", " + 413", 188 | \item 无效格式," ++c", " ++1" 189 | \item 溢出数据,"2147483648" 190 | \end{enumerate} 191 | 192 | \subsubsection{代码} 193 | \begin{Code} 194 | // LeetCode, String to Integer (atoi) 195 | // 时间复杂度O(n),空间复杂度O(1) 196 | class Solution { 197 | public: 198 | int myAtoi(const string &str) { 199 | int num = 0; 200 | int sign = 1; 201 | const int n = str.length(); 202 | int i = 0; 203 | 204 | while (str[i] == ' ' && i < n) i++; 205 | 206 | if (str[i] == '+') { 207 | i++; 208 | } else if (str[i] == '-') { 209 | sign = -1; 210 | i++; 211 | } 212 | 213 | for (; i < n; i++) { 214 | if (str[i] < '0' || str[i] > '9') 215 | break; 216 | if (num > INT_MAX / 10 || 217 | (num == INT_MAX / 10 && 218 | (str[i] - '0') > INT_MAX % 10)) { 219 | return sign == -1 ? INT_MIN : INT_MAX; 220 | } 221 | num = num * 10 + str[i] - '0'; 222 | } 223 | return num * sign; 224 | } 225 | }; 226 | \end{Code} 227 | 228 | 229 | \subsubsection{相关题目} 230 | \begindot 231 | \item Implement strStr() ,见 \S \ref{sec:strstr} 232 | \myenddot 233 | 234 | 235 | \section{Add Binary} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 236 | \label{sec:add-binary} 237 | 238 | 239 | \subsubsection{描述} 240 | Given two binary strings, return their sum (also a binary string). 241 | 242 | For example, 243 | \begin{Code} 244 | a = "11" 245 | b = "1" 246 | \end{Code} 247 | Return {\small \fontspec{Latin Modern Mono} "100"}. 248 | 249 | 250 | \subsubsection{分析} 251 | 无 252 | 253 | 254 | \subsubsection{代码} 255 | \begin{Code} 256 | //LeetCode, Add Binary 257 | // 时间复杂度O(n),空间复杂度O(1) 258 | class Solution { 259 | public: 260 | string addBinary(string a, string b) { 261 | string result; 262 | const size_t n = a.size() > b.size() ? a.size() : b.size(); 263 | reverse(a.begin(), a.end()); 264 | reverse(b.begin(), b.end()); 265 | int carry = 0; 266 | for (size_t i = 0; i < n; i++) { 267 | const int ai = i < a.size() ? a[i] - '0' : 0; 268 | const int bi = i < b.size() ? b[i] - '0' : 0; 269 | const int val = (ai + bi + carry) % 2; 270 | carry = (ai + bi + carry) / 2; 271 | result.insert(result.begin(), val + '0'); 272 | } 273 | if (carry == 1) { 274 | result.insert(result.begin(), '1'); 275 | } 276 | return result; 277 | } 278 | }; 279 | \end{Code} 280 | 281 | 282 | \subsubsection{相关题目} 283 | \begindot 284 | \item Add Two Numbers, 见 \S \ref{sec:add-two-numbers} 285 | \myenddot 286 | 287 | 288 | \section{Longest Palindromic Substring} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 289 | \label{sec:longest-palindromic-substring} 290 | 291 | 292 | \subsubsection{描述} 293 | Given a string $S$, find the longest palindromic substring in $S$. You may assume that the maximum length of $S$ is 1000, and there exists one unique longest palindromic substring. 294 | 295 | 296 | \subsubsection{分析} 297 | 最长回文子串,非常经典的题。 298 | 299 | 思路一:暴力枚举,以每个元素为中间元素,同时从左右出发,复杂度$O(n^2)$。 300 | 301 | 思路二:记忆化搜索,复杂度$O(n^2)$。设\fn{f[i][j]} 表示[i,j]之间的最长回文子串,递推方程如下: 302 | \begin{Code} 303 | f[i][j] = if (i == j) S[i] 304 | if (S[i] == S[j] && f[i+1][j-1] == S[i+1][j-1]) S[i][j] 305 | else max(f[i+1][j-1], f[i][j-1], f[i+1][j]) 306 | \end{Code} 307 | 308 | 思路三:动规,复杂度$O(n^2)$。设状态为\fn{f(i,j)},表示区间[i,j]是否为回文串,则状态转移方程为 309 | $$ 310 | f(i,j)=\begin{cases} 311 | true & ,i=j\\ 312 | S[i]=S[j] & , j = i + 1 \\ 313 | S[i]=S[j] \text{ and } f(i+1, j-1) & , j > i + 1 314 | \end{cases} 315 | $$ 316 | 317 | 思路四:Manacher’s Algorithm, 复杂度$O(n)$。详细解释见 \myurl{http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html} 。 318 | 319 | 320 | \subsubsection{备忘录法} 321 | \begin{Code} 322 | // LeetCode, Longest Palindromic Substring 323 | // 备忘录法,会超时 324 | // 时间复杂度O(n^2),空间复杂度O(n^2) 325 | typedef string::const_iterator Iterator; 326 | 327 | namespace std { 328 | template<> 329 | struct hash> { 330 | size_t operator()(pair const& p) const { 331 | return ((size_t) &(*p.first)) ^ ((size_t) &(*p.second)); 332 | } 333 | }; 334 | } 335 | 336 | class Solution { 337 | public: 338 | string longestPalindrome(string const& s) { 339 | cache.clear(); 340 | return cachedLongestPalindrome(s.begin(), s.end()); 341 | } 342 | 343 | private: 344 | unordered_map, string> cache; 345 | 346 | string longestPalindrome(Iterator first, Iterator last) { 347 | size_t length = distance(first, last); 348 | 349 | if (length < 2) return string(first, last); 350 | 351 | auto s = cachedLongestPalindrome(next(first), prev(last)); 352 | 353 | if (s.length() == length - 2 && *first == *prev(last)) 354 | return string(first, last); 355 | 356 | auto s1 = cachedLongestPalindrome(next(first), last); 357 | auto s2 = cachedLongestPalindrome(first, prev(last)); 358 | 359 | // return max(s, s1, s2) 360 | if (s.size() > s1.size()) return s.size() > s2.size() ? s : s2; 361 | else return s1.size() > s2.size() ? s1 : s2; 362 | } 363 | 364 | string cachedLongestPalindrome(Iterator first, Iterator last) { 365 | auto key = make_pair(first, last); 366 | auto pos = cache.find(key); 367 | 368 | if (pos != cache.end()) return pos->second; 369 | else return cache[key] = longestPalindrome(first, last); 370 | } 371 | }; 372 | \end{Code} 373 | 374 | 375 | \subsubsection{动规} 376 | \begin{Code} 377 | // LeetCode, Longest Palindromic Substring 378 | // 动规,时间复杂度O(n^2),空间复杂度O(n^2) 379 | class Solution { 380 | public: 381 | string longestPalindrome(const string& s) { 382 | const int n = s.size(); 383 | bool f[n][n]; 384 | fill_n(&f[0][0], n * n, false); 385 | // 用 vector 会超时 386 | //vector > f(n, vector(n, false)); 387 | size_t max_len = 1, start = 0; // 最长回文子串的长度,起点 388 | 389 | for (size_t i = 0; i < s.size(); i++) { 390 | f[i][i] = true; 391 | for (size_t j = 0; j < i; j++) { // [j, i] 392 | f[j][i] = (s[j] == s[i] && (i - j < 2 || f[j + 1][i - 1])); 393 | if (f[j][i] && max_len < (i - j + 1)) { 394 | max_len = i - j + 1; 395 | start = j; 396 | } 397 | } 398 | } 399 | return s.substr(start, max_len); 400 | } 401 | }; 402 | \end{Code} 403 | 404 | 405 | \subsubsection{Manacher’s Algorithm} 406 | \begin{Code} 407 | // LeetCode, Longest Palindromic Substring 408 | // Manacher’s Algorithm 409 | // 时间复杂度O(n),空间复杂度O(n) 410 | class Solution { 411 | public: 412 | // Transform S into T. 413 | // For example, S = "abba", T = "^#a#b#b#a#$". 414 | // ^ and $ signs are sentinels appended to each end to avoid bounds checking 415 | string preProcess(const string& s) { 416 | int n = s.length(); 417 | if (n == 0) return "^$"; 418 | 419 | string ret = "^"; 420 | for (int i = 0; i < n; i++) ret += "#" + s.substr(i, 1); 421 | 422 | ret += "#$"; 423 | return ret; 424 | } 425 | 426 | string longestPalindrome(string s) { 427 | string T = preProcess(s); 428 | const int n = T.length(); 429 | // 以T[i]为中心,向左/右扩张的长度,不包含T[i]自己, 430 | // 因此 P[i]是源字符串中回文串的长度 431 | int P[n]; 432 | int C = 0, R = 0; 433 | 434 | for (int i = 1; i < n - 1; i++) { 435 | int i_mirror = 2 * C - i; // equals to i' = C - (i-C) 436 | 437 | P[i] = (R > i) ? min(R - i, P[i_mirror]) : 0; 438 | 439 | // Attempt to expand palindrome centered at i 440 | while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) 441 | P[i]++; 442 | 443 | // If palindrome centered at i expand past R, 444 | // adjust center based on expanded palindrome. 445 | if (i + P[i] > R) { 446 | C = i; 447 | R = i + P[i]; 448 | } 449 | } 450 | 451 | // Find the maximum element in P. 452 | int max_len = 0; 453 | int center_index = 0; 454 | for (int i = 1; i < n - 1; i++) { 455 | if (P[i] > max_len) { 456 | max_len = P[i]; 457 | center_index = i; 458 | } 459 | } 460 | 461 | return s.substr((center_index - 1 - max_len) / 2, max_len); 462 | } 463 | }; 464 | \end{Code} 465 | 466 | 467 | \subsubsection{相关题目} 468 | \begindot 469 | \item 无 470 | \myenddot 471 | 472 | 473 | \section{Regular Expression Matching} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 474 | \label{sec:regular-expression-matching} 475 | 476 | 477 | \subsubsection{描述} 478 | Implement regular expression matching with support for \fn{'.'} and \fn{'*'}. 479 | 480 | \fn{'.'} Matches any single character. 481 | \fn{'*'} Matches zero or more of the preceding element. 482 | 483 | The matching should cover the entire input string (not partial). 484 | 485 | The function prototype should be: 486 | \begin{Code} 487 | bool isMatch(const char *s, const char *p) 488 | \end{Code} 489 | 490 | Some examples: 491 | \begin{Code} 492 | isMatch("aa","a") → false 493 | isMatch("aa","aa") → true 494 | isMatch("aaa","aa") → false 495 | isMatch("aa", "a*") → true 496 | isMatch("aa", ".*") → true 497 | isMatch("ab", ".*") → true 498 | isMatch("aab", "c*a*b") → true 499 | \end{Code} 500 | 501 | 502 | \subsubsection{分析} 503 | 这是一道很有挑战的题。 504 | 505 | 506 | \subsubsection{递归版} 507 | \begin{Code} 508 | // LeetCode, Regular Expression Matching 509 | // 递归版,时间复杂度O(n),空间复杂度O(1) 510 | class Solution { 511 | public: 512 | bool isMatch(const string& s, const string& p) { 513 | return isMatch(s.c_str(), p.c_str()); 514 | } 515 | private: 516 | bool isMatch(const char *s, const char *p) { 517 | if (*p == '\0') return *s == '\0'; 518 | 519 | // next char is not '*', then must match current character 520 | if (*(p + 1) != '*') { 521 | if (*p == *s || (*p == '.' && *s != '\0')) 522 | return isMatch(s + 1, p + 1); 523 | else 524 | return false; 525 | } else { // next char is '*' 526 | while (*p == *s || (*p == '.' && *s != '\0')) { 527 | if (isMatch(s, p + 2)) 528 | return true; 529 | s++; 530 | } 531 | return isMatch(s, p + 2); 532 | } 533 | } 534 | }; 535 | \end{Code} 536 | 537 | 538 | \subsubsection{迭代版} 539 | \begin{Code} 540 | 541 | \end{Code} 542 | 543 | 544 | \subsubsection{相关题目} 545 | \begindot 546 | \item Wildcard Matching, 见 \S \ref{sec:wildcard-matching} 547 | \myenddot 548 | 549 | 550 | \section{Wildcard Matching} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 551 | \label{sec:wildcard-matching} 552 | 553 | 554 | \subsubsection{描述} 555 | Implement wildcard pattern matching with support for \fn{'?'} and \fn{'*'}. 556 | 557 | \fn{'?'} Matches any single character. 558 | \fn{'*'} Matches any sequence of characters (including the empty sequence). 559 | 560 | The matching should cover the entire input string (not partial). 561 | 562 | The function prototype should be: 563 | \begin{Code} 564 | bool isMatch(const char *s, const char *p) 565 | \end{Code} 566 | 567 | Some examples: 568 | \begin{Code} 569 | isMatch("aa","a") → false 570 | isMatch("aa","aa") → true 571 | isMatch("aaa","aa") → false 572 | isMatch("aa", "*") → true 573 | isMatch("aa", "a*") → true 574 | isMatch("ab", "?*") → true 575 | isMatch("aab", "c*a*b") → false 576 | \end{Code} 577 | 578 | 579 | \subsubsection{分析} 580 | 跟上一题很类似。 581 | 582 | 主要是\fn{'*'}的匹配问题。\fn{p}每遇到一个\fn{'*'},就保留住当前\fn{'*'}的坐标和\fn{s}的坐标,然后\fn{s}从前往后扫描,如果不成功,则\fn{s++},重新扫描。 583 | 584 | 585 | \subsubsection{递归版} 586 | \begin{Code} 587 | // LeetCode, Wildcard Matching 588 | // 递归版,会超时,用于帮助理解题意 589 | // 时间复杂度O(n!*m!),空间复杂度O(n) 590 | class Solution { 591 | public: 592 | bool isMatch(const string& s, const string& p) { 593 | return isMatch(s.c_str(), p.c_str()); 594 | } 595 | private: 596 | bool isMatch(const char *s, const char *p) { 597 | if (*p == '*') { 598 | while (*p == '*') ++p; //skip continuous '*' 599 | if (*p == '\0') return true; 600 | while (*s != '\0' && !isMatch(s, p)) ++s; 601 | 602 | return *s != '\0'; 603 | } 604 | else if (*p == '\0' || *s == '\0') return *p == *s; 605 | else if (*p == *s || *p == '?') return isMatch(++s, ++p); 606 | else return false; 607 | } 608 | }; 609 | \end{Code} 610 | 611 | 612 | \subsubsection{迭代版} 613 | \begin{Code} 614 | // LeetCode, Wildcard Matching 615 | // 迭代版,时间复杂度O(n*m),空间复杂度O(1) 616 | class Solution { 617 | public: 618 | bool isMatch(const string& s, const string& p) { 619 | return isMatch(s.c_str(), p.c_str()); 620 | } 621 | private: 622 | bool isMatch(const char *s, const char *p) { 623 | bool star = false; 624 | const char *str, *ptr; 625 | for (str = s, ptr = p; *str != '\0'; str++, ptr++) { 626 | switch (*ptr) { 627 | case '?': 628 | break; 629 | case '*': 630 | star = true; 631 | s = str, p = ptr; 632 | while (*p == '*') p++; //skip continuous '*' 633 | if (*p == '\0') return true; 634 | str = s - 1; 635 | ptr = p - 1; 636 | break; 637 | default: 638 | if (*str != *ptr) { 639 | // 如果前面没有'*',则匹配不成功 640 | if (!star) return false; 641 | s++; 642 | str = s - 1; 643 | ptr = p - 1; 644 | } 645 | } 646 | } 647 | while (*ptr == '*') ptr++; 648 | return (*ptr == '\0'); 649 | } 650 | }; 651 | \end{Code} 652 | 653 | 654 | \subsubsection{相关题目} 655 | \begindot 656 | \item Regular Expression Matching, 见 \S \ref{sec:regular-expression-matching} 657 | \myenddot 658 | 659 | 660 | \section{Longest Common Prefix} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 661 | \label{sec:longest-common-prefix} 662 | 663 | 664 | \subsubsection{描述} 665 | Write a function to find the longest common prefix string amongst an array of strings. 666 | 667 | 668 | \subsubsection{分析} 669 | 从位置0开始,对每一个位置比较所有字符串,直到遇到一个不匹配。 670 | 671 | 672 | \subsubsection{纵向扫描} 673 | \begin{Code} 674 | // LeetCode, Longest Common Prefix 675 | // 纵向扫描,从位置0开始,对每一个位置比较所有字符串,直到遇到一个不匹配 676 | // 时间复杂度O(n1+n2+...) 677 | // @author 周倩 (http://weibo.com/zhouditty) 678 | class Solution { 679 | public: 680 | string longestCommonPrefix(vector &strs) { 681 | if (strs.empty()) return ""; 682 | 683 | for (int idx = 0; idx < strs[0].size(); ++idx) { // 纵向扫描 684 | for (int i = 1; i < strs.size(); ++i) { 685 | if (strs[i][idx] != strs[0][idx]) return strs[0].substr(0,idx); 686 | } 687 | } 688 | return strs[0]; 689 | } 690 | }; 691 | \end{Code} 692 | 693 | 694 | \subsubsection{横向扫描} 695 | \begin{Code} 696 | // LeetCode, Longest Common Prefix 697 | // 横向扫描,每个字符串与第0个字符串,从左到右比较,直到遇到一个不匹配, 698 | // 然后继续下一个字符串 699 | // 时间复杂度O(n1+n2+...) 700 | class Solution { 701 | public: 702 | string longestCommonPrefix(vector &strs) { 703 | if (strs.empty()) return ""; 704 | 705 | int right_most = strs[0].size() - 1; 706 | for (size_t i = 1; i < strs.size(); i++) 707 | for (int j = 0; j <= right_most; j++) 708 | if (strs[i][j] != strs[0][j]) // 不会越界,请参考string::[]的文档 709 | right_most = j - 1; 710 | 711 | return strs[0].substr(0, right_most + 1); 712 | } 713 | }; 714 | \end{Code} 715 | 716 | 717 | \subsubsection{相关题目} 718 | \begindot 719 | \item 无 720 | \myenddot 721 | 722 | 723 | \section{Valid Number} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 724 | \label{sec:valid-number} 725 | 726 | 727 | \subsubsection{描述} 728 | Validate if a given string is numeric. 729 | 730 | Some examples: 731 | \begin{Code} 732 | "0" => true 733 | " 0.1 " => true 734 | "abc" => false 735 | "1 a" => false 736 | "2e10" => true 737 | \end{Code} 738 | 739 | Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one. 740 | 741 | 742 | \subsubsection{分析} 743 | 细节实现题。 744 | 745 | 本题的功能与标准库中的\fn{strtod()}功能类似。 746 | 747 | 748 | \subsubsection{有限自动机} 749 | \begin{Code} 750 | // LeetCode, Valid Number 751 | // @author 龚陆安 (http://weibo.com/luangong) 752 | // finite automata,时间复杂度O(n),空间复杂度O(n) 753 | class Solution { 754 | public: 755 | bool isNumber(const string& s) { 756 | enum InputType { 757 | INVALID, // 0 758 | SPACE, // 1 759 | SIGN, // 2 760 | DIGIT, // 3 761 | DOT, // 4 762 | EXPONENT, // 5 763 | NUM_INPUTS // 6 764 | }; 765 | const int transitionTable[][NUM_INPUTS] = { 766 | -1, 0, 3, 1, 2, -1, // next states for state 0 767 | -1, 8, -1, 1, 4, 5, // next states for state 1 768 | -1, -1, -1, 4, -1, -1, // next states for state 2 769 | -1, -1, -1, 1, 2, -1, // next states for state 3 770 | -1, 8, -1, 4, -1, 5, // next states for state 4 771 | -1, -1, 6, 7, -1, -1, // next states for state 5 772 | -1, -1, -1, 7, -1, -1, // next states for state 6 773 | -1, 8, -1, 7, -1, -1, // next states for state 7 774 | -1, 8, -1, -1, -1, -1, // next states for state 8 775 | }; 776 | 777 | int state = 0; 778 | for (auto ch : s) { 779 | InputType inputType = INVALID; 780 | if (isspace(ch)) 781 | inputType = SPACE; 782 | else if (ch == '+' || ch == '-') 783 | inputType = SIGN; 784 | else if (isdigit(ch)) 785 | inputType = DIGIT; 786 | else if (ch == '.') 787 | inputType = DOT; 788 | else if (ch == 'e' || ch == 'E') 789 | inputType = EXPONENT; 790 | 791 | // Get next state from current state and input symbol 792 | state = transitionTable[state][inputType]; 793 | 794 | // Invalid input 795 | if (state == -1) return false; 796 | } 797 | // If the current state belongs to one of the accepting (final) states, 798 | // then the number is valid 799 | return state == 1 || state == 4 || state == 7 || state == 8; 800 | 801 | } 802 | }; 803 | \end{Code} 804 | 805 | 806 | \subsubsection{使用strtod()} 807 | \begin{Code} 808 | // LeetCode, Valid Number 809 | // @author 连城 (http://weibo.com/lianchengzju) 810 | // 偷懒,直接用 strtod(),时间复杂度O(n) 811 | class Solution { 812 | public: 813 | bool isNumber (const string& s) { 814 | return isNumber(s.c_str()); 815 | } 816 | private: 817 | bool isNumber (char const* s) { 818 | char* endptr; 819 | strtod (s, &endptr); 820 | 821 | if (endptr == s) return false; 822 | 823 | for (; *endptr; ++endptr) 824 | if (!isspace (*endptr)) return false; 825 | 826 | return true; 827 | } 828 | }; 829 | \end{Code} 830 | 831 | 832 | \subsubsection{相关题目} 833 | \begindot 834 | \item 无 835 | \myenddot 836 | 837 | 838 | \section{Integer to Roman} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 839 | \label{sec:integer-to-roman} 840 | 841 | 842 | \subsubsection{描述} 843 | Given an integer, convert it to a roman numeral. 844 | 845 | Input is guaranteed to be within the range from 1 to 3999. 846 | 847 | 848 | \subsubsection{分析} 849 | 无 850 | 851 | 852 | \subsubsection{代码} 853 | \begin{Code} 854 | // LeetCode, Integer to Roman 855 | // 时间复杂度O(num),空间复杂度O(1) 856 | class Solution { 857 | public: 858 | string intToRoman(int num) { 859 | const int radix[] = {1000, 900, 500, 400, 100, 90, 860 | 50, 40, 10, 9, 5, 4, 1}; 861 | const string symbol[] = {"M", "CM", "D", "CD", "C", "XC", 862 | "L", "XL", "X", "IX", "V", "IV", "I"}; 863 | 864 | string roman; 865 | for (size_t i = 0; num > 0; ++i) { 866 | int count = num / radix[i]; 867 | num %= radix[i]; 868 | for (; count > 0; --count) roman += symbol[i]; 869 | } 870 | return roman; 871 | } 872 | }; 873 | \end{Code} 874 | 875 | 876 | \subsubsection{相关题目} 877 | \begindot 878 | \item Roman to Integer, 见 \S \ref{sec:roman-to-integer} 879 | \myenddot 880 | 881 | 882 | \section{Roman to Integer} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 883 | \label{sec:roman-to-integer} 884 | 885 | 886 | \subsubsection{描述} 887 | Given a roman numeral, convert it to an integer. 888 | 889 | Input is guaranteed to be within the range from 1 to 3999. 890 | 891 | 892 | \subsubsection{分析} 893 | 从前往后扫描,用一个临时变量记录分段数字。 894 | 895 | 如果当前比前一个大,说明这一段的值应该是当前这个值减去上一个值。比如\fn{IV = 5 – 1};否则,将当前值加入到结果中,然后开始下一段记录。比如\fn{VI = 5 + 1, II=1+1} 896 | 897 | 898 | \subsubsection{代码} 899 | \begin{Code} 900 | // LeetCode, Roman to Integer 901 | // 时间复杂度O(n),空间复杂度O(1) 902 | class Solution { 903 | public: 904 | inline int map(const char c) { 905 | switch (c) { 906 | case 'I': return 1; 907 | case 'V': return 5; 908 | case 'X': return 10; 909 | case 'L': return 50; 910 | case 'C': return 100; 911 | case 'D': return 500; 912 | case 'M': return 1000; 913 | default: return 0; 914 | } 915 | } 916 | 917 | int romanToInt(const string& s) { 918 | int result = 0; 919 | for (size_t i = 0; i < s.size(); i++) { 920 | if (i > 0 && map(s[i]) > map(s[i - 1])) { 921 | result += (map(s[i]) - 2 * map(s[i - 1])); 922 | } else { 923 | result += map(s[i]); 924 | } 925 | } 926 | return result; 927 | } 928 | }; 929 | \end{Code} 930 | 931 | 932 | \subsubsection{相关题目} 933 | \begindot 934 | \item Integer to Roman, 见 \S \ref{sec:integer-to-roman} 935 | \myenddot 936 | 937 | 938 | \section{Count and Say} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 939 | \label{sec:count-and-say} 940 | 941 | 942 | \subsubsection{描述} 943 | The count-and-say sequence is the sequence of integers beginning as follows: 944 | \begin{Code} 945 | 1, 11, 21, 1211, 111221, ... 946 | \end{Code} 947 | 948 | \fn{1} is read off as \fn{"one 1"} or \fn{11}. 949 | 950 | \fn{11} is read off as \fn{"two 1s"} or \fn{21}. 951 | 952 | \fn{21} is read off as \fn{"one 2"}, then \fn{"one 1"} or \fn{1211}. 953 | 954 | Given an integer $n$, generate the nth sequence. 955 | 956 | Note: The sequence of integers will be represented as a string. 957 | 958 | 959 | \subsubsection{分析} 960 | 模拟。 961 | 962 | 963 | \subsubsection{代码} 964 | \begin{Code} 965 | // LeetCode, Count and Say 966 | // @author 连城 (http://weibo.com/lianchengzju) 967 | // 时间复杂度O(n^2),空间复杂度O(n) 968 | class Solution { 969 | public: 970 | string countAndSay(int n) { 971 | string s("1"); 972 | 973 | while (--n) 974 | s = getNext(s); 975 | 976 | return s; 977 | } 978 | 979 | string getNext(const string &s) { 980 | stringstream ss; 981 | 982 | for (auto i = s.begin(); i != s.end(); ) { 983 | auto j = find_if(i, s.end(), bind1st(not_equal_to(), *i)); 984 | ss << distance(i, j) << *i; 985 | i = j; 986 | } 987 | 988 | return ss.str(); 989 | } 990 | }; 991 | \end{Code} 992 | 993 | 994 | \subsubsection{相关题目} 995 | \begindot 996 | \item 无 997 | \myenddot 998 | 999 | 1000 | \section{Anagrams} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1001 | \label{sec:anagrams} 1002 | 1003 | 1004 | \subsubsection{描述} 1005 | Given an array of strings, return all groups of strings that are anagrams. 1006 | 1007 | Note: All inputs will be in lower-case. 1008 | 1009 | 1010 | \subsubsection{分析} 1011 | Anagram(回文构词法)是指打乱字母顺序从而得到新的单词,比如 \fn{"dormitory"} 打乱字母顺序会变成 \fn{"dirty room"} ,\fn{"tea"} 会变成\fn{"eat"}。 1012 | 1013 | 回文构词法有一个特点:单词里的字母的种类和数目没有改变,只是改变了字母的排列顺序。因此,将几个单词按照字母顺序排序后,若它们相等,则它们属于同一组 anagrams 。 1014 | 1015 | 1016 | \subsubsection{代码} 1017 | \begin{Code} 1018 | // LeetCode, Anagrams 1019 | // 时间复杂度O(n),空间复杂度O(n) 1020 | class Solution { 1021 | public: 1022 | vector anagrams(vector &strs) { 1023 | unordered_map > group; 1024 | for (const auto &s : strs) { 1025 | string key = s; 1026 | sort(key.begin(), key.end()); 1027 | group[key].push_back(s); 1028 | } 1029 | 1030 | vector result; 1031 | for (auto it = group.cbegin(); it != group.cend(); it++) { 1032 | if (it->second.size() > 1) 1033 | result.insert(result.end(), it->second.begin(), it->second.end()); 1034 | } 1035 | return result; 1036 | } 1037 | }; 1038 | \end{Code} 1039 | 1040 | 1041 | \subsubsection{相关题目} 1042 | \begindot 1043 | \item 无 1044 | \myenddot 1045 | 1046 | 1047 | \section{Simplify Path} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1048 | \label{sec:simplify-path} 1049 | 1050 | 1051 | \subsubsection{描述} 1052 | Given an absolute path for a file (Unix-style), simplify it. 1053 | 1054 | For example, \\ 1055 | path = \fn{"/home/"}, => \fn{"/home"} \\ 1056 | path = \fn{"/a/./b/../../c/"}, => \fn{"/c"} \\ 1057 | 1058 | Corner Cases: 1059 | \begindot 1060 | \item Did you consider the case where path = \fn{"/../"}? 1061 | In this case, you should return \fn{"/"}. 1062 | \item 1063 | Another corner case is the path might contain multiple slashes \fn{'/'} together, such as \fn{"/home//foo/"}. 1064 | In this case, you should ignore redundant slashes and return \fn{"/home/foo"}. 1065 | \myenddot 1066 | 1067 | 1068 | \subsubsection{分析} 1069 | 很有实际价值的题目。 1070 | 1071 | 1072 | \subsubsection{代码} 1073 | \begin{Code} 1074 | // LeetCode, Simplify Path 1075 | // 时间复杂度O(n),空间复杂度O(n) 1076 | class Solution { 1077 | public: 1078 | string simplifyPath(const string& path) { 1079 | vector dirs; // 当做栈 1080 | 1081 | for (auto i = path.begin(); i != path.end();) { 1082 | ++i; 1083 | 1084 | auto j = find(i, path.end(), '/'); 1085 | auto dir = string(i, j); 1086 | 1087 | if (!dir.empty() && dir != ".") {// 当有连续 '///'时,dir 为空 1088 | if (dir == "..") { 1089 | if (!dirs.empty()) 1090 | dirs.pop_back(); 1091 | } else 1092 | dirs.push_back(dir); 1093 | } 1094 | 1095 | i = j; 1096 | } 1097 | 1098 | stringstream out; 1099 | if (dirs.empty()) { 1100 | out << "/"; 1101 | } else { 1102 | for (auto dir : dirs) 1103 | out << '/' << dir; 1104 | } 1105 | 1106 | return out.str(); 1107 | } 1108 | }; 1109 | \end{Code} 1110 | 1111 | 1112 | \subsubsection{相关题目} 1113 | \begindot 1114 | \item 无 1115 | \myenddot 1116 | 1117 | 1118 | \section{Length of Last Word} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1119 | \label{sec:length-of-last-word} 1120 | 1121 | 1122 | \subsubsection{描述} 1123 | Given a string s consists of upper/lower-case alphabets and empty space characters \fn{' '}, return the length of last word in the string. 1124 | 1125 | If the last word does not exist, return 0. 1126 | 1127 | Note: A word is defined as a character sequence consists of non-space characters only. 1128 | 1129 | For example, 1130 | Given \fn{s = "Hello World"}, 1131 | return 5. 1132 | 1133 | 1134 | \subsubsection{分析} 1135 | 细节实现题。 1136 | 1137 | 1138 | \subsubsection{用 STL} 1139 | \begin{Code} 1140 | // LeetCode, Length of Last Word 1141 | // 偷懒,用 STL 1142 | // 时间复杂度O(n),空间复杂度O(1) 1143 | class Solution { 1144 | public: 1145 | int lengthOfLastWord(const string& s) { 1146 | auto first = find_if(s.rbegin(), s.rend(), ::isalpha); 1147 | auto last = find_if_not(first, s.rend(), ::isalpha); 1148 | return distance(first, last); 1149 | } 1150 | }; 1151 | \end{Code} 1152 | 1153 | 1154 | \subsubsection{顺序扫描} 1155 | \begin{Code} 1156 | // LeetCode, Length of Last Word 1157 | // 顺序扫描,记录每个 word 的长度 1158 | // 时间复杂度O(n),空间复杂度O(1) 1159 | class Solution { 1160 | public: 1161 | int lengthOfLastWord(const string& s) { 1162 | return lengthOfLastWord(s.c_str()); 1163 | } 1164 | private: 1165 | int lengthOfLastWord(const char *s) { 1166 | int len = 0; 1167 | while (*s) { 1168 | if (*s++ != ' ') 1169 | ++len; 1170 | else if (*s && *s != ' ') 1171 | len = 0; 1172 | } 1173 | return len; 1174 | } 1175 | }; 1176 | \end{Code} 1177 | 1178 | 1179 | \subsubsection{相关题目} 1180 | \begindot 1181 | \item 无 1182 | \myenddot 1183 | -------------------------------------------------------------------------------- /C++/chapTrick.tex: -------------------------------------------------------------------------------- 1 | \chapter{编程技巧} 2 | 3 | 在判断两个浮点数a和b是否相等时,不要用\fn{a==b},应该判断二者之差的绝对值\fn{fabs(a-b)}是否小于某个阈值,例如\fn{1e-9}。 4 | 5 | 判断一个整数是否是为奇数,用\fn{x \% 2 != 0},不要用\fn{x \% 2 == 1},因为x可能是负数。 6 | 7 | 用\fn{char}的值作为数组下标(例如,统计字符串中每个字符出现的次数),要考虑到\fn{char}可能是负数。有的人考虑到了,先强制转型为\fn{unsigned int}再用作下标,这仍然是错的。正确的做法是,先强制转型为\fn{unsigned char},再用作下标。这涉及C++整型提升的规则,就不详述了。 8 | 9 | 以下是关于STL使用技巧的,很多条款来自《Effective STL》这本书。 10 | 11 | \subsubsection{vector和string优先于动态分配的数组} 12 | 13 | 首先,在性能上,由于\fn{vector}能够保证连续内存,因此一旦分配了后,它的性能跟原始数组相当; 14 | 15 | 其次,如果用new,意味着你要确保后面进行了delete,一旦忘记了,就会出现BUG,且这样需要都写一行delete,代码不够短; 16 | 17 | 再次,声明多维数组的话,只能一个一个new,例如: 18 | \begin{Code} 19 | int** ary = new int*[row_num]; 20 | for(int i = 0; i < row_num; ++i) 21 | ary[i] = new int[col_num]; 22 | \end{Code} 23 | 用vector的话一行代码搞定, 24 | \begin{Code} 25 | vector > ary(row_num, vector(col_num, 0)); 26 | \end{Code} 27 | 28 | \subsubsection{使用reserve来避免不必要的重新分配} 29 | -------------------------------------------------------------------------------- /C++/format.cls: -------------------------------------------------------------------------------- 1 | \usepackage[centering,paperwidth=180mm,paperheight=230mm,% 2 | body={390pt,530pt},marginparsep=10pt,marginpar=50pt]{geometry} 3 | \usepackage{color} 4 | \usepackage{enumitem} 5 | \usepackage{fancyvrb} 6 | \usepackage[bottom,perpage,symbol*]{footmisc} 7 | \usepackage{graphicx} 8 | \usepackage[hidelinks]{hyperref} 9 | \usepackage{makeidx} 10 | \usepackage[toc]{multitoc} 11 | \usepackage{pifont} 12 | \usepackage{underscore} 13 | \usepackage{amsmath} 14 | 15 | \DefineFNsymbols*{chinese}{{\ding{172}}{\ding{173}}{\ding{174}}{\ding{175}}% 16 | {\ding{176}}{\ding{177}}{\ding{178}}{\ding{179}}{\ding{180}}{\ding{181}}} 17 | \setfnsymbol{chinese} 18 | 19 | \hypersetup{bookmarksnumbered=true,bookmarksdepth=2} 20 | 21 | \CTEXsetup[number={\thechapter}]{chapter} 22 | \CTEXsetup[format+={\raggedleft}]{chapter} 23 | \CTEXsetup[beforeskip={10pt}]{chapter} 24 | \CTEXsetup[afterskip={30pt}]{chapter} 25 | \def\CTEX@chapter@aftername{\par} % \CTEXsetup[aftername={\par}]{chapter} 26 | \CTEXsetup[format+={\raggedright}]{section} 27 | \CTEXsetup[beforeskip={-3.0ex plus -1ex minus -.2ex}]{section} 28 | \CTEXsetup[afterskip={2.3ex plus .2ex minus 0.2ex}]{section} 29 | 30 | \renewcommand \thefigure{\thechapter-\arabic{figure}} 31 | \renewcommand \thetable{\thechapter-\arabic{table}} 32 | 33 | \newcommand\figcaption[1]{\def\@captype{figure}\caption{#1}} 34 | \newcommand\tabcaption[1]{\def\@captype{table}\caption{#1}} 35 | 36 | \long\def\@caption#1[#2]#3{% 37 | \addcontentsline{\csname ext@#1\endcsname}{#1}% 38 | {\protect\numberline{\csname fnum@#1\endcsname}{ \ignorespaces #2}}% change "the" to "fnum@" 39 | \normalsize 40 | \@makecaption{\csname fnum@#1\endcsname}{\ignorespaces #3}} 41 | 42 | \long\def\@makecaption#1#2{% 43 | \vskip\abovecaptionskip 44 | \sbox\@tempboxa{#1\quad#2}% 45 | \ifdim \wd\@tempboxa >\hsize 46 | #1\quad#2\par 47 | \else 48 | \global \@minipagefalse 49 | \hb@xt@\hsize{\hfil\box\@tempboxa\hfil}% 50 | \fi 51 | \vskip\belowcaptionskip} 52 | 53 | \setlength\abovecaptionskip{0pt} 54 | 55 | \setmainfont{Times New Roman} 56 | %\setmainfont{Linux Libertine} 57 | %\setmainfont{TeX Gyre Pagella} 58 | \newfontfamily\urlfont{PT Sans Narrow} 59 | %\setmonofont[AutoFakeBold=1.6,AutoFakeSlant=0.17,Mapping=tex-text-tt]{Inconsolata} 60 | \setCJKfamilyfont{zhyou}{YouYuan} 61 | 62 | \newcommand{\fn}[1]{\texttt{#1}} 63 | \newcommand{\sfn}[1]{\texttt{\small #1}} 64 | \newcommand{\kw}[1]{\textsf{#1}} 65 | \newcommand{\myurl}[1]{{\urlfont #1}} 66 | \newcommand{\mpar}[1]{\marginpar[\hfill\kaishu #1]{\kaishu #1}} 67 | \newcommand{\mn}[1]{\texttt{\bs #1}} 68 | \renewcommand{\today}{\the\year-\the\month-\the\day} 69 | \newcommand\bs{\textbackslash} 70 | \newcommand{\code}[1]{\small{\fontspec{Latin Modern Mono} #1}} 71 | 72 | \newcommand\begindot{\begin{itemize} 73 | [itemsep=2pt plus 2pt minus 2pt,% 74 | topsep=3pt plus 2pt minus 2pt,% 75 | parsep=0pt plus 2pt minus 2pt]} 76 | \newcommand\myenddot{\end{itemize}} 77 | 78 | \newcommand\beginnum{\begin{enumerate} 79 | [itemsep=2pt plus 2pt minus 2pt,% 80 | topsep=3pt plus 2pt minus 2pt,% 81 | parsep=0pt plus 2pt minus 2pt]} 82 | \newcommand\myendnum{\end{enumerate}} 83 | 84 | \DefineVerbatimEnvironment% 85 | {Code}{Verbatim} 86 | {fontsize=\small,baselinestretch=0.9,xleftmargin=3mm} 87 | 88 | \raggedbottom 89 | %\setlength{\parskip}{1ex plus .5ex minus .5ex} 90 | 91 | \input{verbatim.cls} 92 | \DefineVerbatimEnvironment% 93 | {Codex}{Verbatim} 94 | {fontsize=\small,baselinestretch=0.9,xleftmargin=3mm,% 95 | frame=lines,labelposition=all,framesep=5pt} 96 | 97 | \DefineVerbatimEnvironment% 98 | {Code}{Verbatim} 99 | {fontsize=\small,baselinestretch=0.9,xleftmargin=3mm} 100 | 101 | \makeindex 102 | -------------------------------------------------------------------------------- /C++/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/.DS_Store -------------------------------------------------------------------------------- /C++/images/8-queens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/8-queens.png -------------------------------------------------------------------------------- /C++/images/gray-code-construction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/gray-code-construction.png -------------------------------------------------------------------------------- /C++/images/histogram-area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/histogram-area.png -------------------------------------------------------------------------------- /C++/images/histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/histogram.png -------------------------------------------------------------------------------- /C++/images/longest-substring-without-repeating-characters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/longest-substring-without-repeating-characters.png -------------------------------------------------------------------------------- /C++/images/next-permutation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/next-permutation.png -------------------------------------------------------------------------------- /C++/images/phone-keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/phone-keyboard.png -------------------------------------------------------------------------------- /C++/images/robot-maze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/robot-maze.png -------------------------------------------------------------------------------- /C++/images/rotate-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/rotate-image.png -------------------------------------------------------------------------------- /C++/images/sudoku-solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/sudoku-solution.png -------------------------------------------------------------------------------- /C++/images/sudoku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/sudoku.png -------------------------------------------------------------------------------- /C++/images/trapping-rain-water.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/images/trapping-rain-water.png -------------------------------------------------------------------------------- /C++/leetcode-cpp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/leetcode-cpp.pdf -------------------------------------------------------------------------------- /C++/leetcode-cpp.tex: -------------------------------------------------------------------------------- 1 | \documentclass[10pt,adobefonts,fancyhdr,hyperref,UTF8]{ctexbook} 2 | 3 | \usepackage{multirow} 4 | % for \soul 删除线 5 | \usepackage{ulem} 6 | % 表头斜线 7 | \usepackage{diagbox} 8 | 9 | \makeatletter 10 | \input{format.cls} 11 | \makeatother 12 | 13 | \begin{document} 14 | \sloppy 15 | \newcommand\BookTitle{LeetCode题解} 16 | \pagestyle{fancy} 17 | \fancyhf{} 18 | \fancyhead[RE]{\normalfont\small\rmfamily\nouppercase{\leftmark}} 19 | \fancyhead[LO]{\normalfont\small\rmfamily\nouppercase{\rightmark}} 20 | \fancyhead[LE,RO]{\thepage} 21 | %\fancyfoot[LE,LO]{\small\normalfont\youyuan\BookTitle} 22 | %\fancyfoot[RE,RO]{\textsf{\small \color{blue} https://github.com/soulmachine/leetcode}} 23 | 24 | \makeatletter 25 | \@openrightfalse 26 | \makeatother 27 | 28 | \frontmatter % 开始前言目录,页码用罗马数字 29 | 30 | \include{title} 31 | 32 | \tableofcontents 33 | 34 | \mainmatter % 开始正文,页码用阿拉伯数字 35 | 36 | \graphicspath{{images/}} 37 | 38 | \include{chapTrick} 39 | \include{chapLinearList} 40 | \include{chapString} 41 | \include{chapStackAndQueue} 42 | \include{chapTree} 43 | \include{chapSorting} 44 | \include{chapSearching} 45 | \include{chapBruteforce} 46 | \include{chapBFS} 47 | \include{chapDFS} 48 | \include{chapDivideAndConquer} 49 | \include{chapGreedy} 50 | \include{chapDynamicProgramming} 51 | \include{chapGraph} 52 | \include{chapImplement} 53 | 54 | \appendix % 开始附录,章用字母编号 55 | \printindex 56 | 57 | \end{document} 58 | -------------------------------------------------------------------------------- /C++/tex-text-tt.map: -------------------------------------------------------------------------------- 1 | ; TECkit mapping for TeX input conventions <-> Unicode characters 2 | 3 | LHSName "TeX-text" 4 | RHSName "UNICODE" 5 | 6 | pass(Unicode) 7 | 8 | ; ligatures from Knuth's original CMR fonts 9 | ;U+002D U+002D <> U+2013 ; -- -> en dash 10 | ;U+002D U+002D U+002D <> U+2014 ; --- -> em dash 11 | 12 | ;U+0027 <> U+2019 ; ' -> right single quote 13 | ;U+0027 U+0027 <> U+201D ; '' -> right double quote 14 | ;U+0022 > U+201D ; " -> right double quote 15 | 16 | ;U+0060 <> U+2018 ; ` -> left single quote 17 | ;U+0060 U+0060 <> U+201C ; `` -> left double quote 18 | 19 | ;U+0021 U+0060 <> U+00A1 ; !` -> inverted exclam 20 | ;U+003F U+0060 <> U+00BF ; ?` -> inverted question 21 | 22 | ; additions supported in T1 encoding 23 | ;U+002C U+002C <> U+201E ; ,, -> DOUBLE LOW-9 QUOTATION MARK 24 | ;U+003C U+003C <> U+00AB ; << -> LEFT POINTING GUILLEMET 25 | ;U+003E U+003E <> U+00BB ; >> -> RIGHT POINTING GUILLEMET 26 | 27 | ;U+0027 <> U+2019 ; ' -> right single quote 28 | ;U+0022 > U+201D ; " -> right double quote 29 | -------------------------------------------------------------------------------- /C++/tex-text-tt.tec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/C++/tex-text-tt.tec -------------------------------------------------------------------------------- /C++/title.tex: -------------------------------------------------------------------------------- 1 | \thispagestyle{plain} 2 | \begin{center} 3 | {\LARGE\textbf{\BookTitle}} 4 | 5 | \vspace{1em} 6 | {\large 灵魂机器 (soulmachine@gmail.com)} 7 | 8 | \vspace{1ex} 9 | \myurl{https://github.com/soulmachine/leetcode} 10 | 11 | \vspace{1ex} 12 | 最后更新 \today 13 | 14 | \vspace{1em} 15 | \textbf{\large 版权声明} 16 | \end{center} 17 | \noindent 本作品采用“Creative Commons 署名-非商业性使用-相同方式共享 3.0 Unported许可协议 18 | (cc by-nc-sa)”进行许可。 19 | \texttt{\small http://creativecommons.org/licenses/by-nc-sa/3.0/} 20 | 21 | \vspace{1em} 22 | \input{abstract} -------------------------------------------------------------------------------- /C++/verbatim.cls: -------------------------------------------------------------------------------- 1 | \def\FV@SetLineWidth{% 2 | \if@FV@ResetMargins\else 3 | \advance\leftmargin\@totalleftmargin 4 | \fi 5 | \advance\leftmargin\FV@XLeftMargin\relax 6 | \advance\rightmargin\FV@XRightMargin\relax 7 | \linewidth\hsize 8 | %\advance\linewidth-\leftmargin 9 | %\advance\linewidth-\rightmargin 10 | \hfuzz\FancyVerbHFuzz\relax} 11 | 12 | 13 | \def\FV@SingleFrameLine#1{% 14 | %% DG/SR modification end 15 | \hbox to\z@{% 16 | %\kern\leftmargin 17 | %% DG/SR modification begin - Jun. 22, 1998 18 | \ifnum#1=\z@ 19 | \let\FV@Label\FV@LabelBegin 20 | \else 21 | \let\FV@Label\FV@LabelEnd 22 | \fi 23 | \ifx\FV@Label\relax 24 | %% DG/SR modification end 25 | \FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}% 26 | %% DG/SR modification begin - Jun. 22, 1998 27 | \else 28 | \ifnum#1=\z@ 29 | \setbox\z@\hbox{\strut\enspace\urlfont\FV@LabelBegin\strut}% 30 | \else 31 | \setbox\z@\hbox{\strut\enspace\urlfont\FV@LabelEnd\strut}% 32 | \fi 33 | \@tempdimb=\dp\z@ 34 | \advance\@tempdimb -.5\ht\z@ 35 | \@tempdimc=\linewidth 36 | \advance\@tempdimc -\wd\z@ 37 | %\divide\@tempdimc\tw@ 38 | \ifnum#1=\z@ % Top line 39 | \ifx\FV@LabelPositionTopLine\relax 40 | \FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}% 41 | \else 42 | \FV@FrameLineWithLabel 43 | \fi 44 | \else % Bottom line 45 | \ifx\FV@LabelPositionBottomLine\relax 46 | \FancyVerbRuleColor{\vrule \@width\linewidth \@height\FV@FrameRule}% 47 | \else 48 | \FV@FrameLineWithLabel 49 | \fi 50 | \fi 51 | \fi 52 | %% DG/SR modification end 53 | \hss}} 54 | 55 | 56 | %% DG/SR modification begin - May. 19, 1998 57 | \def\FV@FrameLineWithLabel{% 58 | \ht\z@\@tempdimb\dp\z@\@tempdimb% 59 | \FancyVerbRuleColor{% 60 | \raise 0.5ex\hbox{\vrule \@width\@tempdimc \@height\FV@FrameRule}% 61 | \raise\@tempdimb\box\z@}} 62 | %% DG/SR modification end 63 | 64 | 65 | \def\FV@EndListFrame@Lines{% 66 | \begingroup 67 | %\vskip 0.5ex 68 | \baselineskip\z@skip 69 | \kern\FV@FrameSep\relax 70 | %% DG/SR modification begin - May. 19, 1998 71 | %% \FV@SingleFrameLine 72 | \FV@SingleFrameLine{\@ne}% 73 | %% DG/SR modification end 74 | \endgroup} 75 | 76 | \newskip\mytopsep 77 | \setlength{\mytopsep}{4pt plus 2pt minus 3pt} 78 | 79 | \def\FV@ListVSpace{% 80 | \@topsepadd\mytopsep 81 | \if@noparlist\advance\@topsepadd\partopsep\fi 82 | \if@inlabel 83 | \vskip\parskip 84 | \else 85 | \if@nobreak 86 | \vskip\parskip 87 | \clubpenalty\@M 88 | \else 89 | \addpenalty\@beginparpenalty 90 | \@topsep\@topsepadd 91 | \advance\@topsep\parskip 92 | \addvspace\@topsep 93 | \fi 94 | \fi 95 | %\showthe \@topsepadd 96 | %\showthe \topsep 97 | %\showthe \partopsep 98 | %\showthe \parskip 99 | \global\@nobreakfalse 100 | \global\@inlabelfalse 101 | \global\@minipagefalse 102 | \global\@newlistfalse} 103 | 104 | \def\FV@EndList{% 105 | \FV@ListProcessLastLine 106 | \FV@EndListFrame 107 | %\showthe \@topsepadd 108 | \@endparenv 109 | \endgroup 110 | \@endpetrue} 111 | 112 | \def\theFancyVerbLine{\sffamily\scriptsize\arabic{FancyVerbLine}} 113 | -------------------------------------------------------------------------------- /Java/README.md: -------------------------------------------------------------------------------- 1 | #Java版 2 | ----------------- 3 | 4 | ## 编译 5 | 6 | docker pull soulmachine/texlive 7 | docker run -it --rm -v $(pwd):/data -w /data soulmachine/texlive-full xelatex -synctex=1 -interaction=nonstopmode leetcode-java.tex 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, soulmachine and The Contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of the soulmachine nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LeetCode题解 2 | 3 | ## 在线阅读 4 | 5 | 6 | 7 | ## PDF下载 8 | 9 | LeetCode题解(C++版).pdf 10 | 11 | C++ 文件夹下是C++版,内容一模一样,代码是用C++写的。 12 | 13 | Java 文件夹下是Java版,目前正在编写中,由于拖延症,不知道猴年马月能完成。 14 | 15 | ## 如何编译PDF 16 | 17 | ### 命令行编译 18 | 19 | ```bash 20 | docker run -it --rm -v $(pwd)/C++:/project -w /project soulmachine/texlive xelatex -interaction=nonstopmode leetcode-cpp.tex 21 | ``` 22 | 23 | ### vscode下编译 24 | 25 | 本项目已经配置好了vscode devcontainer, 可以在 Windows, Linux 和 macOS 三大平台上编译。 26 | 27 | 用 vscode 打开本项目,选择右下角弹出的 `"Reopen in Container"`,就会在容器中打开本项目,该容器安装了 Tex Live 2022 以及所需要的10个字体。 28 | 29 | 点击vscode左下角的齿轮图标,选择 `Command Palette`,输入`tasks`, 选择 `Run Task`, 选择 `leetcode-C++`,即可启动编译。 30 | 31 | ## LaTeX模板 32 | 33 | 本书使用的是陈硕开源的[模板](https://github.com/chenshuo/typeset)。这个模板制作精良,感谢陈硕 :) 34 | 35 | 这个LaTex模板总共使用了10个字体,下载地址 。有的字体Windows自带了,有的字体Ubuntu自带了,但都不全,还是一次性安装完所有字体比较方便。 36 | 37 | 也可以参考 [Dockerfile](https://github.com/soulmachine/docker-images/blob/master/texlive/Dockerfile) 去学习如何安装所有字体。 38 | 39 | ## 贡献代码 40 | 41 | 欢迎给本书添加内容或纠正错误,在自己本地编译成PDF,预览没问题后,就可以发pull request过来了。 42 | -------------------------------------------------------------------------------- /参考资料/Longest Palindromic Substring Part II - LeetCode.com.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/参考资料/Longest Palindromic Substring Part II - LeetCode.com.pdf -------------------------------------------------------------------------------- /参考资料/Manacher's ALGORITHM_ O(n)时间求字符串的最长回文子串 - Blog of Felix021 - TechOnly.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/参考资料/Manacher's ALGORITHM_ O(n)时间求字符串的最长回文子串 - Blog of Felix021 - TechOnly.pdf -------------------------------------------------------------------------------- /参考资料/leetcode Single Number II - 位运算处理数组中的数 - 代金桥 - 博客园.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/参考资料/leetcode Single Number II - 位运算处理数组中的数 - 代金桥 - 博客园.pdf -------------------------------------------------------------------------------- /参考资料/silicon-job.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulmachine/leetcode/a12593e0d6da5bce3ca69d6ad1bf71acb95650d5/参考资料/silicon-job.jpeg --------------------------------------------------------------------------------