
[{"content":"$\\KaTeX$ is used for rendering LaTeX math expressions. It can be enabled per page by setting math to true in the page front matter.\n--- title: \u0026#34;My Page with LaTeX\u0026#34; math: true --- When enabled, the scripts, stylesheets and fonts from KaTeX will be included automatically in your site. You can start using LaTeX math expressions in your Markdown content.\nExample # Both inline and separate paragraph LaTeX math expressions are supported in the Markdown content.\nInline # This $\\sigma(z) = \\frac{1}{1 + e^{-z}}$ is inline. This $\\sigma(z) = \\frac{1}{1 + e^{-z}}$ is inline.\nSeparate Paragraph # $$F(\\omega) = \\int_{-\\infty}^{\\infty} f(t) e^{-j\\omega t} \\, dt$$ will be rendered as:\n$$F(\\omega) = \\int_{-\\infty}^{\\infty} f(t) e^{-j\\omega t} , dt$$\nSupported Functions # For a list of supported functions, see KaTeX supported functions.\nChemistry # Chemistry expressions are supported via mhchem extension.\nInline: $\\ce{H2O}$ is water.\nSeparate paragraph:\n$$\\ce{Hg^2+ -\u0026gt;[I-] HgI2 -\u0026gt;[I-] [Hg^{II}I4]^2-}$$ $$\\ce{Hg^2+ -\u0026gt;[I-] HgI2 -\u0026gt;[I-] [Hg^{II}I4]^2-}$$\n","date":"23 October 2023","externalUrl":null,"permalink":"/notes/latex/","section":"Notes","summary":"$\\KaTeX$ is used for rendering LaTeX math expressions. It can be enabled per page by setting math to true in the page front matter.\n","title":"Latex","type":"notes"},{"content":" .claude \u0026gt; TODO.md # # project-name TODO ## In Progress (from git status) - [ ] Commit widget implementation changes - [ ] folder/file - [ ] folder/new-file (new) - [ ] folder.new-file2 (new) - [ ] Commit Live Activityand entitlements updates ## Build \u0026amp; Validation Commands **IMPORTANT: After every code change, validate the build succeeds.** ```bash # Build for xxx # Quick error check # Install on xxx # Open in IDE Architecture # Second Brain concept: Code base will grow to have these small domain like md files or some form of like Vectorized Memory that AI can use very quickly. So it doesn\u0026#39;t have to parse the entire codebase every single time. It\u0026#39;s essentially just a small compaction of context that is really available at all times for Claude code to use. Custom Workflows concept: Once we have this like second brain and this real context that we have. A skill of Claude code is basically a saved workflow, a set of steps, and all of it is actually written by Claude itself. It\u0026#39;s actually just a giant prompt in the MD file, but it\u0026#39;s written in a way that Claude knows how to execute those steps every single time consistently. e.x. Every time I ship something, I need to write the same kind of post to let the people know that I had shipped something, what changed, how to test it, what to watch out for, how to get feedback. It\u0026#39;s basically the same structure every single time. And to build this skill so that you don\u0026#39;t have to ever manually write this again. You just actually do these steps manually with Claude just once, step by step. And then all you say is \u0026#34;Turn this into a skill I can reuse\u0026#34; . And now whenever we need to post something, as long as we have been working on the second brain for after every like work session. We can just trigger this skill and instantly. Claude will write into style we wanna to without reexplaining, reinfecting doing anything about like dragging and dropping. We just prompt it and we just have the content that we need to post. - Context is king. Context is best served fresh and condensed. - Let Claude code to manage context on our behalf. - SEV investigation is a big part of our job ## Prompt Update the project notes with what we worked on Give me the latest status report Split this PR into logical chunks Turn this into a skill I can reuse Tell me a good way to plan a live activity for when I background the app This is a cool picture of the Clock Home mascot. Do something with it. Add this to my rules. Hey, can you update that rule for me so we never do it again? Use my Xcode MCP to build the app. Audit my codebase and make sure that we\u0026#39;re removing all of the audio playback and references to the audio of part of the xxx app. Save this to my local CLAUD.md in my project directory. Save the work that we just did. Load my context from my local projects. (Once we have second brain concept, we can say a command like this) After every code change, validate the build succeeds. Add debug logs, and run the app, and then control the emulator. (Then do the action we are trying to do). Read the logs, and debug that way. (Or we can use perfetto, hook into perfecto mcps, do a run, then have it read the traces and then see if it can find junk just like the timings. ) (For web, we could do like puppeteer and have claude navigate it using a /chrome command and then just actually do the navigation or have it write just test or have like integration end to end test.) (To build a skill) Go and fetch hacker news for latest iOS news. And then save a summary to my local claude directory Save what we just did into a new skill called \u0026#34;Fetch Hacker News.\u0026#34; Why don\u0026#39;t you extend this fetch Hacker News to fetch Twitter for Apple News?\u2028Find me a good Sigma MCP. Why don\u0026#39;t you just install this for me?\u2028Spawn an iOS architecture agent, sub-agent, and do some investigation on my codebase and see if it\u0026#39;s actually good or not. Use what we just did to create an iOS architecture sub-agent. Start working on my live activities feature. All right, let\u0026#39;s test out this iOS architecture and then just spawn a sub-agent. Change the notification to ring a little sound when you finish execution. ```md # TODO.md ## Current Tasks - Commit widget implementation changes (new files) - Commit Live Activity and entitlements updates - Commit music files deletions (~70 mp3s removed) - Commit Models.swift and TimerViews.swift changes - Decide on untracked config files (.claude/, .mcp.json, CLAUDE.md, downloads/) - Validate build after all changes ## Settings - Project: ark-tree - Scheme: ark-tree - Device: rocket ## Available Commands - /build - Build for rocket - /install - Install on rocket - / audit - Run codebase audit - /implement-feature - PRD-driven implementation ## Skills/Agents - Live Activity expert - Widget expert - iOS architect - SwiftUI specialist - Swift reviewer Tips # /init, 300 lines in CLAUD.md /memory Shift + tab (plan mode with new feature ) Escape + up/ give me something else Double click Escape, then restore that context point /help /clear, to clear the context. Create new tab will bring up a new context as well. It\u0026rsquo;s good to start with a new feature and have completely done with the old task. /context /compact, to save some version of context into local second brain. /model /resume , recover the context /mcp Use templates for git /rewind, to restore the code and the conversion(git is better than this) CLAUD.md # Priority from top to bottom Manually update rules: \u0026ldquo;Never do X\u0026rdquo; or \u0026ldquo;Always do Y.\u0026rdquo; Document mistakes here. Add to it whenever Claude makes a mistake you don\u0026rsquo;t want repeated. Ask Claude to update rules: Say \u0026ldquo;add this to my rules\u0026rdquo; and Claude updates the file. Our CLAUDE.md becomes how we actually work. Use workflow triggers: When user says deploy, run deploy script. This turns Claude into a workflow engine. Compound Engineering: Commit CLAUDE.md to git: Teams gets instant access. They contribute back. Shared knowledge. Need to get rid of anything like generic for our code like file path and things like that. And we also want to be mindful how large this file becomes. claude --dangerously-skip-permissions Dangerously-skip for throwaway eves: Docker container. Unpunished branch. If we can blow it away, let Claude run free. /permissions Daily workflow # Start features in Plan Mode: Shift + Tab twice. Claude can read but can\u0026rsquo;t execute. Explores without risk. Iterating with claude code. Arguing with it. Having a conversation with it. Really treating it like another just good engineer that I\u0026rsquo;m like working with and I never just like purely accept the first answers that it gives to me. I always kind of challenging it and I spent a lot of time and put a lot of effort at this stage of the development. Because I feel like once cloud code builds up that context and have a good execution specs, the generation of the code is actually the easy part. I love being in the zone and writing code. At least I used to. The thing is it\u0026rsquo;s really hard to get back into that mode. It\u0026rsquo;s like very different levels of abstractions of the way we\u0026rsquo;re working. (I gave the command and I go to the next one. I am building the context. I give it a command and I go to the other one. And then I give a command. So I end up like still can get into the zone, but it\u0026rsquo;s like a different style.) Fresh context beats bloated. Less is more. When the context is full of dead ends, start a new session. Persist before ending session. Sessions are temporary. CLAUD.md is permanent. This is long-term memory. \u0026ldquo;Save this to my local CLAUD.md in my project directory. Save the work that we just did.\u0026rdquo; Lazy load context. Index at root pointing to subdirectory docs. Claude loads detail only when needed. \u0026ldquo;Load my context from my local projects. (Once we have second brain concept, we can say a command like this)\u0026rdquo; Give verification commands. Test, lint, type check commands in CLAUDE.md. This 2-3x\u0026rsquo;s output quality. Read thinking blocks. \u0026ldquo;I assume\u0026hellip;\u0026rdquo; Or \u0026ldquo;I\u0026rsquo;m not sure\u0026hellip;\u0026rdquo; = course-correct before Claude commits to wrong path. Power User # Composability Framework\nFour composability primitives. Skills, Commands, MCPs, Subagents. How they work together separates casuals from power users. Skills = recurring workflows. Templates that load context when triggered. Lazy-loaded. Don\u0026rsquo;t eat tokens until needed. Never create commands manually. \u0026ldquo;Create a command that does X.\u0026rdquo; Let Claude manage the file structure. MCPs = external service docs. Not just API connections. Structured documentation for database, browsers, systems. \u0026ldquo;Find me a good Sigma MCP\u0026rdquo; Subagents = isolated context. Task() spawns clones for parallel work, but also it\u0026rsquo;s to protect our context window. Each gets fresh context window. \u0026ldquo;Spawn an iOS architecture agent, sub-agent, and do some investigation on my codebase and see if it\u0026rsquo;s actually good or not.\u0026rdquo;, \u0026ldquo;Use what we just did to create an iOS architecture sub-agent.\u0026rdquo; Avoid instruction overload. Context is best served fresh and condensed. Quality over quantity.\u2028Advanced # Run multiple instances. Enable notifications. Custom sound when Claude finishes. Context-switch while Claude works. \u0026ldquo;Change the notification to ring a little sound when you finish execution.\u0026rdquo; Git corktree for isolation. Multiple Claude instances, same repo, isolated files. Each corktree = Separate checkout. /chrome connects browser. See and interact with web pages. Authenticated session. No API keys. Powerful for debugging. Navigate, click, fill forms, read console. \u0026ldquo;Fix the error in console.\u0026rdquo; One flow. Hooks \u0026amp; Automation # /hooks. Hooks intercept actions. PreToolUse = before. PostToolUse = after. Auto-format with PostToolUse. Format after edits. Catches CI edge cases. Never commit ugly code. Block dangerous commands. PreToolUse blocks before execution. Guardrails for destructive ops. Explore the plugin ecosystem. Pre-built skills, commands, hooks. Install and use immediately. GitHub: Claude-plugins-official ","date":"28 March 2026","externalUrl":null,"permalink":"/notes/claude-tips/","section":"Notes","summary":".claude \u003e TODO.md # # project-name TODO ## In Progress (from git status) - [ ] Commit widget implementation changes - [ ] folder/file - [ ] folder/new-file (new) - [ ] folder.new-file2 (new) - [ ] Commit Live Activityand entitlements updates ## Build \u0026 Validation Commands **IMPORTANT: After every code change, validate the build succeeds.** ```bash # Build for xxx # Quick error check # Install on xxx # Open in IDE Architecture # Second Brain concept: Code base will grow to have these small domain like md files or some form of like Vectorized Memory that AI can use very quickly. So it doesn't have to parse the entire codebase every single time. It's essentially just a small compaction of context that is really available at all times for Claude code to use. Custom Workflows concept: Once we have this like second brain and this real context that we have. A skill of Claude code is basically a saved workflow, a set of steps, and all of it is actually written by Claude itself. It's actually just a giant prompt in the MD file, but it's written in a way that Claude knows how to execute those steps every single time consistently. e.x. Every time I ship something, I need to write the same kind of post to let the people know that I had shipped something, what changed, how to test it, what to watch out for, how to get feedback. It's basically the same structure every single time. And to build this skill so that you don't have to ever manually write this again. You just actually do these steps manually with Claude just once, step by step. And then all you say is \"Turn this into a skill I can reuse\" . And now whenever we need to post something, as long as we have been working on the second brain for after every like work session. We can just trigger this skill and instantly. Claude will write into style we wanna to without reexplaining, reinfecting doing anything about like dragging and dropping. We just prompt it and we just have the content that we need to post. - Context is king. Context is best served fresh and condensed. - Let Claude code to manage context on our behalf. - SEV investigation is a big part of our job ## Prompt Update the project notes with what we worked on Give me the latest status report Split this PR into logical chunks Turn this into a skill I can reuse Tell me a good way to plan a live activity for when I background the app This is a cool picture of the Clock Home mascot. Do something with it. Add this to my rules. Hey, can you update that rule for me so we never do it again? Use my Xcode MCP to build the app. Audit my codebase and make sure that we're removing all of the audio playback and references to the audio of part of the xxx app. Save this to my local CLAUD.md in my project directory. Save the work that we just did. Load my context from my local projects. (Once we have second brain concept, we can say a command like this) After every code change, validate the build succeeds. Add debug logs, and run the app, and then control the emulator. (Then do the action we are trying to do). Read the logs, and debug that way. (Or we can use perfetto, hook into perfecto mcps, do a run, then have it read the traces and then see if it can find junk just like the timings. ) (For web, we could do like puppeteer and have claude navigate it using a /chrome command and then just actually do the navigation or have it write just test or have like integration end to end test.) (To build a skill) Go and fetch hacker news for latest iOS news. And then save a summary to my local claude directory Save what we just did into a new skill called \"Fetch Hacker News.\" Why don't you extend this fetch Hacker News to fetch Twitter for Apple News?\u2028Find me a good Sigma MCP. Why don't you just install this for me?\u2028Spawn an iOS architecture agent, sub-agent, and do some investigation on my codebase and see if it's actually good or not. Use what we just did to create an iOS architecture sub-agent. Start working on my live activities feature. All right, let's test out this iOS architecture and then just spawn a sub-agent. Change the notification to ring a little sound when you finish execution. ```md # TODO.md ## Current Tasks - Commit widget implementation changes (new files) - Commit Live Activity and entitlements updates - Commit music files deletions (~70 mp3s removed) - Commit Models.swift and TimerViews.swift changes - Decide on untracked config files (.claude/, .mcp.json, CLAUDE.md, downloads/) - Validate build after all changes ## Settings - Project: ark-tree - Scheme: ark-tree - Device: rocket ## Available Commands - /build - Build for rocket - /install - Install on rocket - / audit - Run codebase audit - /implement-feature - PRD-driven implementation ## Skills/Agents - Live Activity expert - Widget expert - iOS architect - SwiftUI specialist - Swift reviewer Tips # /init, 300 lines in CLAUD.md /memory Shift + tab (plan mode with new feature ) Escape + up/ give me something else Double click Escape, then restore that context point /help /clear, to clear the context. Create new tab will bring up a new context as well. It’s good to start with a new feature and have completely done with the old task. /context /compact, to save some version of context into local second brain. /model /resume , recover the context /mcp Use templates for git /rewind, to restore the code and the conversion(git is better than this) CLAUD.md # Priority from top to bottom Manually update rules: “Never do X” or “Always do Y.” Document mistakes here. Add to it whenever Claude makes a mistake you don’t want repeated. Ask Claude to update rules: Say “add this to my rules” and Claude updates the file. Our CLAUDE.md becomes how we actually work. Use workflow triggers: When user says deploy, run deploy script. This turns Claude into a workflow engine. Compound Engineering: Commit CLAUDE.md to git: Teams gets instant access. They contribute back. Shared knowledge. Need to get rid of anything like generic for our code like file path and things like that. And we also want to be mindful how large this file becomes. claude --dangerously-skip-permissions Dangerously-skip for throwaway eves: Docker container. Unpunished branch. If we can blow it away, let Claude run free. /permissions Daily workflow # Start features in Plan Mode: Shift + Tab twice. Claude can read but can’t execute. Explores without risk. Iterating with claude code. Arguing with it. Having a conversation with it. Really treating it like another just good engineer that I’m like working with and I never just like purely accept the first answers that it gives to me. I always kind of challenging it and I spent a lot of time and put a lot of effort at this stage of the development. Because I feel like once cloud code builds up that context and have a good execution specs, the generation of the code is actually the easy part. I love being in the zone and writing code. At least I used to. The thing is it’s really hard to get back into that mode. It’s like very different levels of abstractions of the way we’re working. (I gave the command and I go to the next one. I am building the context. I give it a command and I go to the other one. And then I give a command. So I end up like still can get into the zone, but it’s like a different style.) Fresh context beats bloated. Less is more. When the context is full of dead ends, start a new session. Persist before ending session. Sessions are temporary. CLAUD.md is permanent. This is long-term memory. “Save this to my local CLAUD.md in my project directory. Save the work that we just did.” Lazy load context. Index at root pointing to subdirectory docs. Claude loads detail only when needed. “Load my context from my local projects. (Once we have second brain concept, we can say a command like this)” Give verification commands. Test, lint, type check commands in CLAUDE.md. This 2-3x’s output quality. Read thinking blocks. “I assume…” Or “I’m not sure…” = course-correct before Claude commits to wrong path. Power User # Composability Framework\n","title":"Claude Tips","type":"notes"},{"content":" Time # Jan 2022 - May 2022, Data structure \u0026amp; algorithms by Calvin Lin Jan 2025 - May 2025, Advances in Deep Learning May 2025 - Aug 2025, Advances in Deep Learning Aug 2025 - Dec 2025, Natural Language Processing Jan 2026 - Present, Advances in Generative Modeling ","date":"27 March 2026","externalUrl":null,"permalink":"/notes/ta/","section":"Notes","summary":"Time # Jan 2022 - May 2022, Data structure \u0026 algorithms by Calvin Lin Jan 2025 - May 2025, Advances in Deep Learning May 2025 - Aug 2025, Advances in Deep Learning Aug 2025 - Dec 2025, Natural Language Processing Jan 2026 - Present, Advances in Generative Modeling ","title":"TA","type":"notes"},{"content":" Alias for chekcing logs # alias grepless=\u0026#39;grep --color=always \u0026#34;$@\u0026#34; | less -R\u0026#39; # grepless \u0026#34;error\u0026#34; *.log ","date":"8 November 2025","externalUrl":null,"permalink":"/notes/grep/","section":"Notes","summary":"Alias for chekcing logs # alias grepless='grep --color=always \"$@\" | less -R' # grepless \"error\" *.log","title":"Grep","type":"notes"},{"content":" pre-requisite # Extension Pack for Java (Microsoft) Debugger for Java (usually included via the pack) Also make sure VS Code is using your JDK (Java 17/21 etc): Ctrl+Shift+P → Java: Configure Java Runtime Ensure JAVA_HOME is set (System env) and matches what you want. .vscode/tasks.json # { \u0026#34;version\u0026#34;: \u0026#34;2.0.0\u0026#34;, \u0026#34;tasks\u0026#34;: [ { \u0026#34;label\u0026#34;: \u0026#34;boot:run (debug 5005)\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;shell\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;mvn -q -DskipTests spring-boot:run -Dspring-boot.run.jvmArguments=\\\u0026#34;-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=127.0.0.1:5005\\\u0026#34;\u0026#34;, \u0026#34;options\u0026#34;: { \u0026#34;cwd\u0026#34;: \u0026#34;${workspaceFolder}\u0026#34; }, \u0026#34;problemMatcher\u0026#34;: [] }, { \u0026#34;label\u0026#34;: \u0026#34;boot:run (normal)\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;shell\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;mvn -q -DskipTests spring-boot:run\u0026#34;, \u0026#34;options\u0026#34;: { \u0026#34;cwd\u0026#34;: \u0026#34;${workspaceFolder}\u0026#34; }, \u0026#34;problemMatcher\u0026#34;: [] } ] } .vscode/launch.json # { \u0026#34;version\u0026#34;: \u0026#34;0.2.0\u0026#34;, \u0026#34;configurations\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;Attach to Spring Boot (5005)\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;java\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;attach\u0026#34;, \u0026#34;hostName\u0026#34;: \u0026#34;127.0.0.1\u0026#34;, \u0026#34;port\u0026#34;: 5005 } ] } profile # mvn spring-boot:run ^ -Dspring-boot.run.profiles=local ^ -Dspring-boot.run.jvmArguments=\u0026#34;-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=127.0.0.1:5005\u0026#34; x # With 16GB RAM + big Java/Maven repo, your goal is: reduce file watching/indexing, cap Java language server, and avoid scanning build outputs. Do these in this order.\n1) Exclude build outputs everywhere (highest impact) # Open Settings (JSON) and add/merge:\n{ \u0026#34;files.watcherExclude\u0026#34;: { \u0026#34;**/.git/**\u0026#34;: true, \u0026#34;**/target/**\u0026#34;: true, \u0026#34;**/build/**\u0026#34;: true, \u0026#34;**/out/**\u0026#34;: true, \u0026#34;**/.gradle/**\u0026#34;: true, \u0026#34;**/.mvn/**\u0026#34;: false }, \u0026#34;search.exclude\u0026#34;: { \u0026#34;**/target/**\u0026#34;: true, \u0026#34;**/build/**\u0026#34;: true, \u0026#34;**/out/**\u0026#34;: true, \u0026#34;**/node_modules/**\u0026#34;: true }, \u0026#34;files.exclude\u0026#34;: { \u0026#34;**/target/**\u0026#34;: true, \u0026#34;**/build/**\u0026#34;: true } } Why: on big repos, target/ churn is the #1 VS Code killer.\n2) Cap Java Language Server memory (don’t let it eat your machine) # Add:\n{ \u0026#34;java.jdt.ls.vmargs\u0026#34;: \u0026#34;-Xms256m -Xmx2048m -XX:+UseG1GC -XX:+UseStringDeduplication -Dsun.zip.disableMemoryMapping=true\u0026#34; } -Xmx2048m is a good ceiling on 16GB (you still need RAM for Chrome, Maven builds, etc.). If you notice OOM in Java features, bump to 3072m, but don’t go higher unless you must. When things get sluggish:\nCtrl+Shift+P → Java: Clean Java Language Server Workspace (it often fixes “mysterious lag”). 3) Kill expensive UI features (small but steady wins) # { \u0026#34;editor.minimap.enabled\u0026#34;: false, \u0026#34;editor.codeLens\u0026#34;: false, \u0026#34;breadcrumbs.enabled\u0026#34;: false, \u0026#34;git.autofetch\u0026#34;: false } If you like CodeLens for tests, keep it on—but it costs CPU on big projects.\n4) Stop VS Code from doing “smart” stuff in huge files # { \u0026#34;editor.largeFileOptimizations\u0026#34;: true, \u0026#34;files.maxMemoryForLargeFilesMB\u0026#34;: 4096 } 5) Extensions: go on a diet # In a big repo, too many extensions = death by a thousand cuts.\nDisable anything not essential (Docker/K8s, Python, extra linters, spell-checkers, multiple AI tools, etc.) Prefer Disable (Workspace) so other projects aren’t affected. 6) Windows Defender exclusions (often a huge win) # Exclude:\nYour repo directory Maven cache: C:\\Users\\\u0026lt;you\u0026gt;\\.m2\\repository This can speed up both indexing and mvn runs a lot.\n7) Don’t import the whole world into Java # If you opened a parent folder containing many repos, VS Code Java will try to be “helpful” and index everything. Open the exact repo root you’re working on.\nA good “senior” workflow for big repos in VS Code # Run Spring Boot via terminal/tasks (Maven controls build) Use VS Code primarily for navigation + attach debugging Keep Java language server memory capped and avoid indexing target/ If you paste:\nyour current .vscode/settings.json (or global settings JSON), and roughly how many Maven modules, I can suggest a tighter exclude pattern and whether -Xmx2G is enough or you should go 3G. ","date":"25 October 2025","externalUrl":null,"permalink":"/notes/memo/","section":"Notes","summary":"pre-requisite # Extension Pack for Java (Microsoft) Debugger for Java (usually included via the pack) Also make sure VS Code is using your JDK (Java 17/21 etc): Ctrl+Shift+P → Java: Configure Java Runtime Ensure JAVA_HOME is set (System env) and matches what you want. .vscode/tasks.json # { \"version\": \"2.0.0\", \"tasks\": [ { \"label\": \"boot:run (debug 5005)\", \"type\": \"shell\", \"command\": \"mvn -q -DskipTests spring-boot:run -Dspring-boot.run.jvmArguments=\\\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=127.0.0.1:5005\\\"\", \"options\": { \"cwd\": \"${workspaceFolder}\" }, \"problemMatcher\": [] }, { \"label\": \"boot:run (normal)\", \"type\": \"shell\", \"command\": \"mvn -q -DskipTests spring-boot:run\", \"options\": { \"cwd\": \"${workspaceFolder}\" }, \"problemMatcher\": [] } ] } .vscode/launch.json # { \"version\": \"0.2.0\", \"configurations\": [ { \"name\": \"Attach to Spring Boot (5005)\", \"type\": \"java\", \"request\": \"attach\", \"hostName\": \"127.0.0.1\", \"port\": 5005 } ] } profile # mvn spring-boot:run ^ -Dspring-boot.run.profiles=local ^ -Dspring-boot.run.jvmArguments=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=127.0.0.1:5005\" x # With 16GB RAM + big Java/Maven repo, your goal is: reduce file watching/indexing, cap Java language server, and avoid scanning build outputs. Do these in this order.\n","title":"MEMO","type":"notes"},{"content":" 1) What uv is (why use it) # Ultra-fast project/env manager—replaces python -m venv, much of pip, pip-tools, pipx, and parts of Poetry/pyenv workflows. It does envs, dependency locking, and tool running. (Astral Docs) 2) Install uv # macOS/Linux (user local): curl -LsSf https://astral.sh/uv/install.sh | sh Windows (PowerShell): irm https://astral.sh/uv/install.ps1 | iex (These are from the official docs “Getting started”.) (Astral Docs) 3) Create \u0026amp; manage virtual environments # New env in .venv: uv venv Named path: uv venv .venv-dev Pick Python version: uv venv --python 3.11 (uv can download/resolve versions for you). (Astral Docs) Project conventions: .venv holds the env; optional .python-version pins default Python for the project. (Astral Docs) Note: You usually don’t activate the env. Instead, run commands inside it with uv run (next section). (Astral Docs)\n4) Running things in the env (no “activate” needed) # Run Python/script within the project env: uv run python -V uv run pytest -q uv run -- python script.py (use -- to separate uv options from your command). uv run ensures the env is up-to-date before running. (Astral Docs) 5) Declaring dependencies (pyproject.toml) # Put deps in pyproject.toml (standard):\n[project] dependencies = [\u0026#34;httpx\u0026#34;, \u0026#34;ruff\u0026gt;=0.3.0\u0026#34;] [project.optional-dependencies] dev = [\u0026#34;pytest\u0026#34;] uv understands this and manages install/lock accordingly. (Astral Docs)\n6) Adding/removing packages (edit pyproject + install + lock) # Add: uv add fastapi\nWith versions/extras: uv add \u0026quot;httpx\u0026gt;0.27\u0026quot;, uv add \u0026quot;uvicorn[standard]\u0026quot; Dev deps: uv add --dev pytest Remove: uv remove httpx These commands update pyproject.toml, install into .venv, and keep the lock in sync. (Astral Docs)\n7) Locking \u0026amp; syncing (reproducibility) # Lock: uv lock → writes uv.lock (pin versions). Sync env to lock: uv sync (installs exactly what’s in uv.lock). Use --frozen to prevent lock updates during runs: uv run --frozen pytest. (Astral Docs) You can also export/compile to requirements.txt if needed: uv pip compile pyproject.toml -o requirements.txt. (Astral Docs) Treat uv.lock like Poetry’s lock: commit it; don’t edit by hand. (SaaS Pegasus) 8) Installing/inspecting like pip (drop-in) # Install into current env: uv pip install . or uv pip install \u0026quot;pydantic\u0026lt;2\u0026quot; List/freeze/tree/check: uv pip list | freeze | tree | check This mirrors common pip commands, but faster/stricter. (Astral Docs) 9) Tools \u0026amp; one-off binaries (like pipx) # Run tools without installing into your project: uvx ruff (alias for uv tool run ruff) Pin versions: uvx ruff@latest, or uvx --from \u0026quot;ruff==0.5.0\u0026quot; ruff check You can also install tools globally with uv’s tool interface if desired. (Astral Docs) 10) Typical project workflow (cheat sheet) # # new or existing repo uv venv --python 3.12 # create .venv with Python 3.12 uv add --dev pytest # add test dep uv add httpx ruff # add runtime deps uv lock # produce uv.lock uv run pytest # run tests in the env uv sync # re-sync others to uv.lock (CI/teammates) uv run ruff check . # run tool from project env # one-off tool outside project uvx black@latest --version 11) Tips \u0026amp; gotchas # No activation needed: prefer uv run …. If you do activate: source .venv/bin/activate (POSIX) still works, but uv is designed to avoid it. (Astral Docs) Pin Python: put 3.12 (etc.) in .python-version so uv venv uses it. (Astral Docs) Lock discipline: in CI or releases, use uv run --frozen … so uv.lock won’t change unexpectedly. (GitHub) Strictness vs pip: uv enforces some specs more strictly than pip; if something fails that pip would install, check the package index/links. (Astral Docs) Exporting for non-uv users: if teammates still want requirements.txt, use uv pip compile (above). (Astral Docs) 12) Cleaning up # Remove caches and uv-managed Pythons/tools if you ever uninstall: uv cache clean then remove uv dirs/binaries per docs. (Stack Overflow) If you tell me your repo layout (do you already have a pyproject.toml? target Python?), I’ll give you a drop-in Makefile or CI snippet using uv run --frozen and uv sync.\n","date":"22 October 2025","externalUrl":null,"permalink":"/notes/uv/","section":"Notes","summary":"1) What uv is (why use it) # Ultra-fast project/env manager—replaces python -m venv, much of pip, pip-tools, pipx, and parts of Poetry/pyenv workflows. It does envs, dependency locking, and tool running. (Astral Docs) 2) Install uv # macOS/Linux (user local): curl -LsSf https://astral.sh/uv/install.sh | sh Windows (PowerShell): irm https://astral.sh/uv/install.ps1 | iex (These are from the official docs “Getting started”.) (Astral Docs) 3) Create \u0026 manage virtual environments # New env in .venv: uv venv Named path: uv venv .venv-dev Pick Python version: uv venv --python 3.11 (uv can download/resolve versions for you). (Astral Docs) Project conventions: .venv holds the env; optional .python-version pins default Python for the project. (Astral Docs) Note: You usually don’t activate the env. Instead, run commands inside it with uv run (next section). (Astral Docs)\n","title":"UV","type":"notes"},{"content":" The tar command — a practical, modern guide # Below is a tight-but-complete playbook you can actually use. Copy/paste the recipes; tweak paths as needed.\n0) What tar is # tar = “tape archive”: it groups files/dirs into a single stream (“archive”). It can also compress by delegating to gzip/bzip2/xz/zstd/etc. Common archive suffixes: .tar (no compression), .tar.gz/.tgz, .tar.bz2, .tar.xz, .tar.zst, .tar.lz4. 1) The 5 core verbs # c = create x = extract t = list (table of contents) r = append (to end; works only for uncompressed .tar) u = update (append newer versions; also for plain .tar) Always pair with f to name the archive file.\nPatterns\n# Create tar -cf archive.tar DIR ... tar -czf archive.tar.gz DIR ... # gzip tar -cJf archive.tar.xz DIR ... # xz tar -cjf archive.tar.bz2 DIR ... # bzip2 tar --zstd -cf archive.tar.zst DIR ...# zstd (GNU tar) tar --lz4 -cf archive.tar.lz4 DIR ...# lz4 (GNU tar) # List tar -tf archive.tar[.gz|.xz|...] # see contents # Extract tar -xf archive.tar[.gz|.xz|...] tar -xf archive.tar -C /target/dir # extract somewhere else # One file / subset tar -xf archive.tar path/inside/file tar -xf archive.tar --strip-components=1 path/top/dir/ Tip: Use -v (verbose) to see file names. Use -C DIR to change into a dir before acting.\n2) Compression options (when creating) # -z = gzip (fast, common) -j = bzip2 (slower, better than gzip) -J = xz (slowest, best ratio) --zstd = Zstandard (great ratio and speed; needs GNU tar) --lz4 = LZ4 (very fast, lower ratio; GNU tar) -a = auto: pick compressor from extension (GNU tar) Examples:\n# Auto-compress by extension (GNU tar) tar -caf backup.tar.zst /data # Fast, good default (zstd) tar --zstd -cf site.tar.zst /var/www 3) Must-know safety habits # Peek before extracting: tar -tf archive.tar.gz | less\nDon’t overwrite existing files: tar -xkf archive.tar.gz (keep old files) or --skip-old-files\nAvoid absolute paths in archives (they can write to /). If you must extract them, you’d need -P/--absolute-names—avoid unless you trust the archive.\nChoose the target dir with -C and/or strip leading folders:\ntar -xf thing.tar.gz -C /opt/app --strip-components=1 4) Excluding things # tar -czf src.tar.gz src/ --exclude-vcs \\ --exclude=\u0026#39;node_modules\u0026#39; --exclude=\u0026#39;*.log\u0026#39; # From a file tar -czf home.tar.gz /home/user --exclude-from=exclude.txt # exclude.txt lines: # .cache # **/*.tmp # Downloads/large/* On GNU tar, globs in --exclude are handled by tar; if you pass globs as positional paths, quote them so the shell doesn’t expand first.\n5) Preserve ownership, permissions, metadata # When creating: tar stores mode/uid/gid/mtime by default.\nWhen extracting:\nAs root, use --same-owner to preserve owners; as non-root, owners map to your uid.\n-p/--preserve-permissions to keep file modes.\nFor extended metadata:\n--xattrs (Linux extended attributes) --acls (POSIX ACLs) --selinux (SELinux contexts) Example:\nsudo tar -xpf backup.tar --xattrs --acls --selinux -C / 6) Verify and compare # # Compare archive vs filesystem tar -df archive.tar[.gz|...] # reports differences # Or verify integrity of the file you created tar -tf archive.tar.gz \u0026gt; /dev/null # lists everything; nonzero exit on error # For compressed archives, add a checksum file after creation: sha256sum archive.tar.zst \u0026gt; archive.tar.zst.sha256 sha256sum -c archive.tar.zst.sha256 7) Incremental and differential backups (GNU tar) # Keep a snapshot file that records state between runs. # Full tar --listed-incremental=snap.snar -cpf full.tar /home # Next runs (incremental) tar --listed-incremental=snap.snar -cpf inc-2025-10-21.tar /home Restoring: extract the full first, then incrementals in order. To create a fresh full backup next time, remove/rotate the .snar. If you just want a “changed since last full” without .snar, consider using rsync or --newer-mtime='2025-10-01' for date-based selection.\n8) Streaming \u0026amp; remote copies (no temp files) # # Push to remote over SSH tar -C /data -czf - . | ssh host \u0026#39;tar -xzf - -C /srv/backup\u0026#39; # Pull from remote ssh host \u0026#39;tar -C /srv/logs -czf - .\u0026#39; | tar -xzf - -C ./logs # Stream to/from cloud tools that read stdin/stdout similarly 9) Split large archives \u0026amp; reassemble # # Create and split into 2 GiB parts tar -czf - bigdir/ | split -b 2000m - bigdir.tar.gz.part- # Reassemble and extract cat bigdir.tar.gz.part-* | tar -xzf - 10) Sparse files, devices, FIFOs # Sparse files (disk images, databases): --sparse on create \u0026amp; extract to save space. Special files (device nodes, FIFOs, sockets): tar can store them; to restore properly you usually need root, and often --same-owner -p. tar -c --sparse -f images.tar /var/lib/libvirt/images 11) Path rewrites and layout control # # Pack contents of ./build into archive root (no leading \u0026#39;build/\u0026#39;) tar -C build -czf app.tar.gz . # Strip first N leading components when extracting tar -xzf app.tar.gz --strip-components=1 -C /opt/app # Rename paths on the fly (GNU tar) tar -czf src-renamed.tar.gz src/ --transform=\u0026#39;s,^src/,project-src/,\u0026#39; 12) Selecting by time or size # # Only files newer than a date tar -czf recent.tar.gz /data --newer-mtime=\u0026#39;2025-10-01\u0026#39; # Only files under a size (use find + tar) find /logs -type f -size -50M -print0 | tar --null -T - -czf small-logs.tar.gz 13) Show progress # # Rough progress (file count) tar -czf backup.tar.gz /home \\ --checkpoint=.500 --totals # With pv (pipe viewer) for byte-level progress tar -cf - /home | pv | gzip \u0026gt; home.tar.gz 14) Extract to stdout (for one-off inspection) # # Print a file from inside the archive tar -xOf archive.tar.gz path/inside/file.txt | less # Save a single file tar -xzf archive.tar.gz path/inside/file.txt -O \u0026gt; file.txt 15) Appending \u0026amp; updating (plain .tar only) # tar -rf archive.tar newfile tar -uf archive.tar maybe/updated/dir Not supported for compressed archives; recreate instead (or use --append --file=- tricks with compressors, but it’s messy—prefer a new archive).\n16) Reproducible (deterministic) archives # Make byte-identical archives across machines/runs:\ntar -cf app.tar src \\ --sort=name \\ --mtime=\u0026#39;UTC 2020-01-01\u0026#39; \\ --owner=0 --group=0 --numeric-owner gzip -n app.tar # gzip without timestamps 17) Platform notes # Linux (GNU tar): supports --zstd, --lz4, --transform, -a, incremental --listed-incremental, rich metadata flags. macOS default tar = bsdtar (libarchive). Most flags are similar; some long options differ. For full GNU features (--zstd, certain transforms), install gnu-tar via Homebrew (brew install gnu-tar, command is gtar). Windows: in Git Bash or WSL, use GNU tar. On vanilla PowerShell, prefer WSL or 3rd-party ports. 18) Quick cheat sheet (copy me) # # Create (auto-compress by extension, GNU tar) tar -caf backup.tar.zst DIR/ # Safe extract: inspect first, then extract to a clean dir tar -tf backup.tar.zst | less mkdir restore \u0026amp;\u0026amp; tar -xaf backup.tar.zst -C restore # Exclude stuff tar -caf src.tar.xz src/ --exclude-vcs --exclude=\u0026#39;node_modules\u0026#39; --exclude=\u0026#39;*.log\u0026#39; # Extract subset + strip leading folder tar -xaf pkg.tar.gz --strip-components=1 -C /opt/pkg path/top/dir/ # Remote copy over SSH tar -C /data -caf - . | ssh host \u0026#39;tar -xaf - -C /srv/backup\u0026#39; # Incremental (GNU) tar --listed-incremental=state.snar -cpf full.tar /home tar --listed-incremental=state.snar -cpf inc1.tar /home # Progress tar -caf backup.tar.zst /home --checkpoint=.1000 --totals # Verify tar -df backup.tar.zst If you tell me your OS (Linux/macOS) and which compressor you prefer (gzip/zstd/xz), I can tailor a few one-liners and a tiny backup.sh for your exact setup.\n","date":"21 October 2025","externalUrl":null,"permalink":"/notes/tar/","section":"Notes","summary":"The tar command — a practical, modern guide # Below is a tight-but-complete playbook you can actually use. Copy/paste the recipes; tweak paths as needed.\n0) What tar is # tar = “tape archive”: it groups files/dirs into a single stream (“archive”). It can also compress by delegating to gzip/bzip2/xz/zstd/etc. Common archive suffixes: .tar (no compression), .tar.gz/.tgz, .tar.bz2, .tar.xz, .tar.zst, .tar.lz4. 1) The 5 core verbs # c = create x = extract t = list (table of contents) r = append (to end; works only for uncompressed .tar) u = update (append newer versions; also for plain .tar) Always pair with f to name the archive file.\n","title":"Tar","type":"notes"},{"content":"Great question. Here’s a pragmatic, Spring-friendly playbook for handling runtime exceptions cleanly and predictably.\n1) Let them bubble to the right boundary # Don’t catch RuntimeException deep in your code unless you’re adding context or translating it.\nLet it bubble to a well-defined boundary (controller, scheduled job, async worker) where you can convert it to:\na meaningful HTTP error (for web APIs), or a log + metric + retry decision (for background work). This keeps code simple, avoids double-logging, and preserves stack traces.\n2) Use a single global handler for web APIs # Prefer a global handler over per-controller handlers so behavior is uniform.\n@RestControllerAdvice public class GlobalExceptionHandler { // Map *expected* domain errors (still unchecked) to 4xx @ExceptionHandler(DomainException.class) public ProblemDetail handleDomain(DomainException ex) { ProblemDetail p = ProblemDetail.forStatus(HttpStatus.UNPROCESSABLE_ENTITY); p.setTitle(\u0026#34;Domain rule violated\u0026#34;); p.setDetail(ex.getMessage()); p.setProperty(\u0026#34;code\u0026#34;, ex.getCode()); // stable machine code return p; } // Fallback for unexpected runtime failures → 500 @ExceptionHandler(RuntimeException.class) public ProblemDetail handleRuntime(RuntimeException ex) { ProblemDetail p = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); p.setTitle(\u0026#34;Internal error\u0026#34;); p.setDetail(\u0026#34;An unexpected error occurred.\u0026#34;); return p; // log the exception separately (see §5) } } Why ProblemDetail? (Spring 6+) It implements RFC 9457 “Problem Details” — a clean, extensible error schema.\n3) Classify exceptions up front # Domain/Business exceptions (e.g., “insufficient funds”, “invalid state transition”) → custom unchecked types, mapped to 4xx. Infrastructure/Transient (timeouts, DB deadlocks) → consider retry/circuit breaker. Programming bugs (NPE, IllegalStateException) → 500; fix at source. 4) Prefer translation, not blanket catching # At integration boundaries (DB, HTTP clients), translate low-level exceptions into your domain/integration exceptions with context. Avoid catch (Exception e) { /* swallow */ }. If you must catch, rethrow with added context (message fields like ids, state) and keep the original cause. 5) Log once, at the edge, with context # One log per failure (usually in the global handler or job runner). Multiple logs for the same exception = noisy.\nUse structured logging (+ MDC) to attach requestId, userId, entityId, etc.\nLevels:\nWARN for expected client mistakes (4xx you anticipate). ERROR for real server faults (5xx). Never printStackTrace; use SLF4J and log the exception object.\n@ExceptionHandler(RuntimeException.class) public ProblemDetail handleRuntime(RuntimeException ex, HttpServletRequest req) { log.error(\u0026#34;Unhandled runtime exception path={} reqId={}\u0026#34;, req.getRequestURI(), MDC.get(\u0026#34;requestId\u0026#34;), ex); return ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); } 6) Don’t expose internals to clients # Return stable error codes and human-safe messages; keep stack traces out of responses. Put technical details in logs/metrics, not the API payload. 7) Validation first, not exceptions later # Use method parameter validation (@Validated, @NotNull, etc.). Handle MethodArgumentNotValidException in your advice to return a clean 400 with field errors. @Override protected ResponseEntity\u0026lt;Object\u0026gt; handleMethodArgumentNotValid( MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { var p = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST); p.setTitle(\u0026#34;Validation failed\u0026#34;); p.setProperty(\u0026#34;errors\u0026#34;, ex.getBindingResult().getFieldErrors().stream() .map(err -\u0026gt; Map.of(\u0026#34;field\u0026#34;, err.getField(), \u0026#34;msg\u0026#34;, err.getDefaultMessage())) .toList()); return ResponseEntity.badRequest().body(p); } (# You can get this by extending ResponseEntityExceptionHandler or write your own.)\n8) Transactions \u0026amp; retries # Transactions: by default, Spring rolls back on RuntimeException. Keep it that way; avoid catching and “continuing”. Retries: wrap transient failures with Resilience4j (@Retry, circuit breakers, timeouts), and make write operations idempotent. 9) Async \u0026amp; scheduled jobs # For @Async, register an AsyncUncaughtExceptionHandler. For scheduled tasks, wrap the job body with a top-level try/catch that logs once and decides on retry/alert. @Configuration @EnableAsync class AsyncCfg implements AsyncConfigurer { @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex, method, params) -\u0026gt; log.error(\u0026#34;Uncaught async exception in {} with params={}\u0026#34;, method, Arrays.toString(params), ex); } } 10) Testing \u0026amp; contracts # Unit test handlers and slice tests (@WebMvcTest) for representative failures. Publish an error contract (Problem Details schema + codes) so clients can rely on stable behavior. TL;DR checklist # Let runtime exceptions bubble; handle at boundaries. Use @RestControllerAdvice + ProblemDetail for uniform API errors. Classify: domain (4xx), transient (retry), programming bug (5xx). Log once with MDC; sanitize responses. Validate early; handle validation errors as 400. Respect transactional rollback; add resilience for transient faults. Cover async/scheduled pathways. Test handlers and document error codes. If you share a snippet of your current handler or a specific failure mode you’re seeing (e.g., DB timeout vs. NPE), I can tailor the mappings and logging fields precisely to your stack.\n","date":"18 October 2025","externalUrl":null,"permalink":"/notes/springboot-exceptions/","section":"Notes","summary":"Great question. Here’s a pragmatic, Spring-friendly playbook for handling runtime exceptions cleanly and predictably.\n1) Let them bubble to the right boundary # Don’t catch RuntimeException deep in your code unless you’re adding context or translating it.\nLet it bubble to a well-defined boundary (controller, scheduled job, async worker) where you can convert it to:\na meaningful HTTP error (for web APIs), or a log + metric + retry decision (for background work). This keeps code simple, avoids double-logging, and preserves stack traces.\n","title":"Springboot Exceptions","type":"notes"},{"content":" Github Repository # Rest Client File Upload # POST http://localhost:3000/upload Content-Type: multipart/form-data; boundary=boundary --boundary Content-Disposition: form-data; name=\u0026#34;file\u0026#34;; filename=\u0026#34;example.txt\u0026#34; Content-Type: text/plain \u0026lt; ./example.txt --boundary ","date":"9 October 2025","externalUrl":null,"permalink":"/notes/rest-client/","section":"Notes","summary":"Github Repository # Rest Client File Upload # POST http://localhost:3000/upload Content-Type: multipart/form-data; boundary=boundary --boundary Content-Disposition: form-data; name=\"file\"; filename=\"example.txt\" Content-Type: text/plain \u003c ./example.txt --boundary","title":"Rest Client","type":"notes"},{"content":" USER # in java 21, springboot 3, what\u0026rsquo;s the best practice to handle exception in projects\nThoughts # In Spring Boot 3, I’ll suggest using the ProblemDetail class (RFC7807) for structured error reports. Utilize @ControllerAdvice, @ExceptionHandler, and @RestControllerAdvice with logging (slf4j). Map exceptions to details like type, title, and status. While @ResponseStatus is still valid, consider customizing with WebMvcConfigurer for MVC and WebFlux.\nASSISTANT # Great question. In Java 21 + Spring Boot 3 (Spring Framework 6), the “modern” best practice is to use RFC 7807 Problem Details everywhere, with a small, intentional exception hierarchy and a single global handler. Here’s a compact blueprint you can drop into any project.\n1) Define a small, meaningful exception set # Prefer unchecked domain exceptions (extend RuntimeException), e.g. ResourceNotFoundException, BusinessRuleViolationException, ConflictException. Keep messages developer-friendly; expose safe, client-facing text in the response body (not stack traces). Use Spring’s built-ins where appropriate (e.g. MethodArgumentNotValidException, ConstraintViolationException, HttpMessageNotReadableException, DataIntegrityViolationException). public class ResourceNotFoundException extends RuntimeException { public ResourceNotFoundException(String msg) { super(msg); } } 2) Return RFC 7807 responses via ProblemDetail # Spring 6 introduces ProblemDetail and ErrorResponse/ErrorResponseException. Prefer these for consistent JSON errors.\n@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) ProblemDetail handleNotFound(ResourceNotFoundException ex, HttpServletRequest req) { ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.NOT_FOUND); pd.setTitle(\u0026#34;Resource Not Found\u0026#34;); pd.setDetail(ex.getMessage()); pd.setInstance(URI.create(req.getRequestURI())); pd.setType(URI.create(\u0026#34;https://example.com/problems/resource-not-found\u0026#34;)); pd.setProperty(\u0026#34;errorCode\u0026#34;, \u0026#34;RES_404_001\u0026#34;); return pd; } @ExceptionHandler(MethodArgumentNotValidException.class) ProblemDetail handleValidation(MethodArgumentNotValidException ex, HttpServletRequest req) { ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST); pd.setTitle(\u0026#34;Validation Failed\u0026#34;); pd.setDetail(\u0026#34;One or more fields are invalid.\u0026#34;); pd.setInstance(URI.create(req.getRequestURI())); var fieldErrors = ex.getBindingResult().getFieldErrors().stream() .map(fe -\u0026gt; Map.of(\u0026#34;field\u0026#34;, fe.getField(), \u0026#34;message\u0026#34;, fe.getDefaultMessage())) .toList(); pd.setProperty(\u0026#34;violations\u0026#34;, fieldErrors); return pd; } @ExceptionHandler(ConstraintViolationException.class) ProblemDetail handleConstraint(ConstraintViolationException ex, HttpServletRequest req) { ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST); pd.setTitle(\u0026#34;Constraint Violation\u0026#34;); pd.setDetail(\u0026#34;One or more constraints were violated.\u0026#34;); pd.setInstance(URI.create(req.getRequestURI())); var violations = ex.getConstraintViolations().stream() .map(v -\u0026gt; Map.of(\u0026#34;path\u0026#34;, v.getPropertyPath().toString(), \u0026#34;message\u0026#34;, v.getMessage())) .toList(); pd.setProperty(\u0026#34;violations\u0026#34;, violations); return pd; } // Fallback @ExceptionHandler(Exception.class) ProblemDetail handleUnknown(Exception ex, HttpServletRequest req) { // log full details server-side only ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); pd.setTitle(\u0026#34;Internal Server Error\u0026#34;); pd.setDetail(\u0026#34;An unexpected error occurred. If the problem persists, contact support.\u0026#34;); pd.setInstance(URI.create(req.getRequestURI())); pd.setType(URI.create(\u0026#34;https://example.com/problems/internal\u0026#34;)); return pd; } } Typical JSON (client-facing):\n{ \u0026#34;type\u0026#34;: \u0026#34;https://example.com/problems/validation-failed\u0026#34;, \u0026#34;title\u0026#34;: \u0026#34;Validation Failed\u0026#34;, \u0026#34;status\u0026#34;: 400, \u0026#34;detail\u0026#34;: \u0026#34;One or more fields are invalid.\u0026#34;, \u0026#34;instance\u0026#34;: \u0026#34;/api/orders\u0026#34;, \u0026#34;violations\u0026#34;: [ { \u0026#34;field\u0026#34;: \u0026#34;email\u0026#34;, \u0026#34;message\u0026#34;: \u0026#34;must be a well-formed email address\u0026#34; } ], \u0026#34;errorCode\u0026#34;: \u0026#34;VAL_400_002\u0026#34; } 3) Use ErrorResponseException inside controllers/services when needed # If you must throw with an HTTP status from deep layers:\nthrow new ErrorResponseException(HttpStatus.FORBIDDEN, ProblemDetail.forStatusAndDetail(HttpStatus.FORBIDDEN, \u0026#34;Not allowed for this account\u0026#34;), null); 4) Validation: prefer annotations + groups # DTOs: jakarta.validation annotations + @Valid. For query/path validations: @Validated on controller + ConstraintViolationException handling (shown above). public record CreateUserRequest( @NotBlank String username, @Email String email ) {} @PostMapping(\u0026#34;/users\u0026#34;) public UserDto create(@Valid @RequestBody CreateUserRequest req) { ... } 5) Logging \u0026amp; observability # Never log stack traces at INFO/WARN. Use: ERROR for server bugs (5xx), WARN for client issues worth attention (e.g., repeated 4xx), INFO for expected flows (optional), DEBUG/TRACE during development. Add a correlation/request ID (e.g., from X-Request-Id) via a filter and put it in MDC so logs tie to a request. Return it back in responses. @Component public class CorrelationIdFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String cid = Optional.ofNullable(((HttpServletRequest) request).getHeader(\u0026#34;X-Request-Id\u0026#34;)) .orElse(UUID.randomUUID().toString()); try (MDC.MDCCloseable ignored = MDC.putCloseable(\u0026#34;cid\u0026#34;, cid)) { ((HttpServletResponse) response).setHeader(\u0026#34;X-Request-Id\u0026#34;, cid); chain.doFilter(request, response); } } } 6) Transactions \u0026amp; rollback rules # By default, runtime exceptions cause rollback; checked exceptions do not. If you throw checked exceptions and still want rollback: @Transactional(rollbackFor = Exception.class). Keep business exceptions unchecked to align with default rollback semantics. 7) Don’t leak internals # Avoid exposing DB keys, stack traces, or class names in detail. Map low-level exceptions to stable, documented error codes. Keep a central ErrorCatalog (enum or constants) and document them in your API spec. 8) Integrate with API docs # If you use springdoc-openapi, define common error schemas (ProblemDetail with violations, errorCode) and reference them in endpoint responses for 4xx/5xx. 9) Configuration and defaults # Keep Boot’s default /error path; but turn on helpful bits in non-prod only: server.error.include-message=never (prod), always (local) server.error.include-binding-errors=never (prod), always (local) server.error.include-exception=never Centralize handler in one package and test it with @WebMvcTest or @SpringBootTest. 10) For reactive (WebFlux) # Same approach: @RestControllerAdvice + ProblemDetail. Use reactive equivalents (ServerWebExchange if you need the path for instance).\nTL;DR template (copy/paste) # Create a few custom RuntimeExceptions for domain errors. One @RestControllerAdvice that returns ProblemDetail for: domain errors (404/409/422, etc.), validation (MethodArgumentNotValidException, ConstraintViolationException), parsing/binding (HttpMessageNotReadableException), persistence (DataIntegrityViolationException), a final catch-all Exception → 500. Add correlation-ID filter + structured logging (MDC). Keep messages safe, publish stable errorCodes, document them in OpenAPI. Lean on default transaction rollback (unchecked exceptions). If you want, I can turn this into a small starter module (with tests) you can drop into your multi-module repo.\nAwesome — here’s a pragmatic, hybrid strategy that uses ProblemDetail, ErrorResponse, and ErrorResponseException together without overengineering.\n0) Goals # One consistent JSON shape (RFC 7807). Minimal boilerplate for common cases. Clear separation: domain errors vs framework/validation/infra. Good logging + traceability. 1) Project layout (suggested) # com.example.shared.error ├─ ErrorCode.java // stable catalog of error codes ├─ BaseAppException.java // implements ErrorResponse ├─ NotFoundException.java // extends BaseAppException ├─ ConflictException.java // extends BaseAppException ├─ ValidationAdvice.java // @RestControllerAdvice: validation \u0026amp; parsing └─ FallbackAdvice.java // @RestControllerAdvice: 3rd-party, unknown 2) Prefer custom exceptions for domain errors (implement ErrorResponse) # Make reusable domain exceptions self-describing by implementing ErrorResponse, so you don’t need a handler for each.\n// ErrorCode is your stable, documented catalog public enum ErrorCode { USER_NOT_FOUND(\u0026#34;USR_404_001\u0026#34;, \u0026#34;The user does not exist.\u0026#34;), EMAIL_TAKEN(\u0026#34;USR_409_002\u0026#34;, \u0026#34;Email already in use.\u0026#34;); // ... public final String code; public final String defaultMessage; ErrorCode(String code, String defaultMessage) { this.code = code; this.defaultMessage = defaultMessage; } } public abstract class BaseAppException extends RuntimeException implements ErrorResponse { private final ProblemDetail body; private final HttpStatus status; protected BaseAppException(HttpStatus status, ErrorCode error, String detail, URI type, URI instance) { super(detail); this.status = status; this.body = ProblemDetail.forStatus(status); body.setTitle(error.defaultMessage); // safe, client-facing summary body.setDetail(detail); // domain-specific detail (safe) body.setType(type != null ? type : URI.create(\u0026#34;https://example.com/problems/\u0026#34; + error.code)); if (instance != null) body.setInstance(instance); body.setProperty(\u0026#34;errorCode\u0026#34;, error.code); // stable code for clients } @Override public ProblemDetail getBody() { return body; } @Override public HttpStatusCode getStatusCode(){ return status; } } public class NotFoundException extends BaseAppException { public NotFoundException(ErrorCode error, String detail, URI instance) { super(HttpStatus.NOT_FOUND, error, detail, null, instance); } } public class ConflictException extends BaseAppException { public ConflictException(ErrorCode error, String detail, URI instance) { super(HttpStatus.CONFLICT, error, detail, null, instance); } } Usage (service or controller):\n// Service layer userRepo.findById(id).orElseThrow(() -\u0026gt; new NotFoundException(ErrorCode.USER_NOT_FOUND, \u0026#34;User %s not found\u0026#34;.formatted(id), URI.create(\u0026#34;/users/\u0026#34; + id))); ➡️ Why: The exception already knows how to serialize to a ProblemDetail. No per-type handler needed.\n3) Use ProblemDetail in advice for framework/validation/infra # Keep two small advices to normalize validation, parsing, DB, security, etc.\n@RestControllerAdvice class ValidationAdvice { @ExceptionHandler(MethodArgumentNotValidException.class) ProblemDetail handleInvalid(MethodArgumentNotValidException ex, HttpServletRequest req) { var pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST); pd.setTitle(\u0026#34;Validation Failed\u0026#34;); pd.setDetail(\u0026#34;One or more fields are invalid.\u0026#34;); pd.setInstance(URI.create(req.getRequestURI())); pd.setType(URI.create(\u0026#34;https://example.com/problems/validation\u0026#34;)); pd.setProperty(\u0026#34;violations\u0026#34;, ex.getBindingResult().getFieldErrors().stream() .map(fe -\u0026gt; Map.of(\u0026#34;field\u0026#34;, fe.getField(), \u0026#34;message\u0026#34;, fe.getDefaultMessage())) .toList()); return pd; } @ExceptionHandler(ConstraintViolationException.class) ProblemDetail handleConstraint(ConstraintViolationException ex, HttpServletRequest req) { var pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST); pd.setTitle(\u0026#34;Constraint Violation\u0026#34;); pd.setDetail(\u0026#34;One or more constraints were violated.\u0026#34;); pd.setInstance(URI.create(req.getRequestURI())); pd.setType(URI.create(\u0026#34;https://example.com/problems/validation\u0026#34;)); pd.setProperty(\u0026#34;violations\u0026#34;, ex.getConstraintViolations().stream() .map(v -\u0026gt; Map.of(\u0026#34;path\u0026#34;, v.getPropertyPath().toString(), \u0026#34;message\u0026#34;, v.getMessage())) .toList()); return pd; } } @RestControllerAdvice class FallbackAdvice { @ExceptionHandler(DataIntegrityViolationException.class) ProblemDetail handleDataIntegrity(DataIntegrityViolationException ex, HttpServletRequest req) { var pd = ProblemDetail.forStatus(HttpStatus.CONFLICT); pd.setTitle(\u0026#34;Data Integrity Violation\u0026#34;); pd.setDetail(\u0026#34;A database constraint was violated.\u0026#34;); pd.setInstance(URI.create(req.getRequestURI())); pd.setType(URI.create(\u0026#34;https://example.com/problems/data-integrity\u0026#34;)); return pd; } @ExceptionHandler(Exception.class) ProblemDetail handleUnknown(Exception ex, HttpServletRequest req) { // log with correlation id in MDC (see §6) var pd = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); pd.setTitle(\u0026#34;Internal Server Error\u0026#34;); pd.setDetail(\u0026#34;An unexpected error occurred. If the problem persists, contact support.\u0026#34;); pd.setInstance(URI.create(req.getRequestURI())); pd.setType(URI.create(\u0026#34;https://example.com/problems/internal\u0026#34;); return pd; } } ➡️ Why: Centralize all “generic” exceptions via ProblemDetail without custom types.\n4) Use ErrorResponseException for quick one-offs # If you don’t need a custom class and want to bubble up an HTTP error from deep code:\nthrow new ErrorResponseException( HttpStatus.FORBIDDEN, ProblemDetail.forStatusAndDetail(HttpStatus.FORBIDDEN, \u0026#34;Not allowed for this account\u0026#34;), null ); Good for rare cases or adapter layers where you don’t want new exception types.\n5) Simple decision checklist # Is it a recurring domain error? ➜ Create/throw a custom exception that extends BaseAppException (implements ErrorResponse). Is it a framework/validation/infra exception? ➜ Let a @RestControllerAdvice return ProblemDetail. Is it a one-off HTTP error from deep code? ➜ Throw ErrorResponseException with a ProblemDetail. 6) Cross-cutting: logging, request ID, and safety # Put a correlation/request ID (e.g., X-Request-Id) in MDC via a servlet filter and echo it in responses. Log domain exceptions at WARN (client-caused) or INFO (expected), and unknown/5xx at ERROR. Keep detail safe for clients; never expose stack traces, SQL, class names. server.error.include-message=never server.error.include-binding-errors=never server.error.include-exception=never 7) OpenAPI \u0026amp; clients # With springdoc-openapi, define a Problem schema once (ProblemDetail + errorCode + violations) and reference it for 4xx/5xx. This guarantees a predictable contract for frontend/other services.\n8) Testing pattern # @WebMvcTest for each advice class to snapshot JSON structure for typical exceptions. Service-layer tests that assert thrown exception type (NotFoundException, ConflictException, etc.). One integration test ensuring MDC/request-id is present on error responses. TL;DR # Domain errors: custom exceptions that implement ErrorResponse (via an abstract BaseAppException). Framework/validation/infra: handle in global @RestControllerAdvice and return ProblemDetail. Edge cases/one-offs: throw ErrorResponseException with a prepared ProblemDetail. If you want, tell me your package names and I’ll drop in ready-to-compile classes (with JUnit tests and a minimal sample controller) tailored to your repo.\n","date":"9 September 2025","externalUrl":null,"permalink":"/notes/spring-boot-exception/","section":"Notes","summary":"USER # in java 21, springboot 3, what’s the best practice to handle exception in projects\nThoughts # In Spring Boot 3, I’ll suggest using the ProblemDetail class (RFC7807) for structured error reports. Utilize @ControllerAdvice, @ExceptionHandler, and @RestControllerAdvice with logging (slf4j). Map exceptions to details like type, title, and status. While @ResponseStatus is still valid, consider customizing with WebMvcConfigurer for MVC and WebFlux.\n","title":"Spring Boot Exception","type":"notes"},{"content":" Milestones # 2013: Word2vec and N-grams 2014: RNN/LSTM 2015: Attention mechanism 2017: Large Pre-Trained Language Models (Transformers) 2018: BERT 2019: T5 2020: GPT-3 ","date":"30 August 2025","externalUrl":null,"permalink":"/notes/nlp/","section":"Notes","summary":"Milestones # 2013: Word2vec and N-grams 2014: RNN/LSTM 2015: Attention mechanism 2017: Large Pre-Trained Language Models (Transformers) 2018: BERT 2019: T5 2020: GPT-3 ","title":"NLP","type":"notes"},{"content":" Magic: The Gathering Terminology # Creature - A permanent type representing a living being. Sorcery - A spell type that is cast and then put into the graveyard. Summoning sickness - only affects creatures Tapped out - A state where a player has no untapped lands available to use for mana.\nFlying - A keyword ability that allows a creature to avoid being blocked except by creatures with flying or reach. Touch death - A state where a creature with 0 toughness is put into the graveyard.\nInstants and activated abilities(:) can be used at any time. Sorceries, creatures, artifacts, enchantments, and lands can only be played on my own main phases and not in response to other spells or abilities.\nblock does not require to be tapped\nPermanents Spell # Creatures - Represent living beings and can attack or block. Artifacts - Colorless permanents that can represent magical items or constructs. Enchantments - Represent ongoing magical effects and can enchant creatures, lands, or other permanents. Lands - Provide mana and are the foundation for casting spells. Planeswalkers - Powerful allies that can use one of their abilities each turn. Battles - A new type of permanent introduced in the latest set, representing a conflict between two sides. Non-Permanent Spells # Instants - Spells that can be cast at any time, even during an opponent\u0026rsquo;s turn. Sorceries - Spells that can only be cast during your main phase when the stack is empty. Parts of a turn # Untap step Draw step: except on the first player first turn 1st Main: cast spell, play one land Combat: player declares attackers, defending player declares blockers, and then all damage is dealt at once 2nd Main: cast spells, play one land if not played at 1st Main. Normally use land and put new creatures End Step Discard down to hand size(7 is the maximum hand size) \u0026ldquo;UNTIL END OF TURN\u0026rdquo; effects stop + Damage wears off Beginning Phase\nUntap step Upkeep step Draw step Main Phase\nPlay lands Cast spells Combat Phase\nDeclare attackers Declare blockers Combat damage Ending Phase\nEnd step Cleanup step I am a defender # Blocking - When an opponent declares an attacker, I can choose to block with my creatures. Instant - I can cast instants at any time, even during my opponent\u0026rsquo;s turn. Activated Abilities - I can use activated abilities of my creatures and other permanents at any time. There are two time:\nbefore attacker attacks, we can use instants and activated abilities. once opponent attacks, we can use instants and activated abilities. Notes # \u0026ldquo;I cast on your \u0026rdquo; \u0026ldquo;Before it resolves, I cast on my \u0026rdquo; Deck #1: Blue Farm # Topdeck Moxfield Wincon # Thassa\u0026rsquo;s Oracle + Demonic Consultation Thassa\u0026rsquo;s Oracle + Tainted Pact Underworld Breach + Lion\u0026rsquo;s Eye Diamond Underworld Breach + Brain Freeze Intuition search for Underworld Breach, Lion\u0026rsquo;s Eye Diamond, and Sevinne\u0026rsquo;s Reclamation. Mnemonic Betrayal Ad Nauseam\nMystic Remora + Esper Sentinel Rhystic Study + Esper Sentinel\nAdvantage Engine # Esper Sentinel Ragavan, Nimble Pilferer mainly in the early game.\n++ # Dockside Extortionist Drannith Magistrate Swords to Plowshares Dispel Path to Exile\n1 Kraum, Ludevic\u0026#39;s Opus (C16) 34 1 Tymna the Weaver (C16) 48 1 Ad Nauseam (ALA) 63 1 An Offer You Can\u0026#39;t Refuse (SNC) 464 1 Ancient Tomb (TMP) 315 1 Arcane Signet (ELD) 331 1 Arid Mesa (ZEN) 211 1 Badlands (ME4) 241 1 Birgi, God of Storytelling // Harnfel, Horn of Bounty (KHM) 123 1 Bloodstained Mire (ONS) 313 1 Borne Upon a Wind (PLTR) 44s 1 Brain Freeze (SCG) 29 1 Cabal Ritual (TOR) 51 1 Cavern of Souls (AVR) 226 1 Chain of Vapor (ONS) 73 1 Chrome Mox (MRD) 152 1 City of Brass (ME4) 243 1 Command Tower (CMD) 269 1 Culling the Weak (EXO) 55 1 Dark Ritual (CEI) 99 1 Deflecting Swat (C20) 50 1 Demonic Consultation (ICE) 121 1 Demonic Tutor (30A) 398 1 Diabolic Intent (PLS) 42 1 Dragon\u0026#39;s Rage Channeler (MH2) 121 1 Enlightened Tutor (MIR) 14 1 Esper Sentinel (MH2) 328 1 Exotic Orchard (CON) 142 1 Faerie Mastermind (MOM) 58 1 Fellwar Stone (DRK) 102 1 Fierce Guardianship (C20) 35 1 Final Fortune (MIR) 173 1 Flare of Duplication (MH3) 416 1 Flooded Strand (ONS) 316 1 Flusterstorm (PLST) CMD-46 1 Force of Negation (MH1) 52 1 Force of Will (ALL) 28 1 Gamble (USG) 188 1 Gemstone Caverns (TSP) 274 1 Grand Abolisher (M12) 19 1 Imperial Seal (PTK) 78 1 Into the Flood Maw (BLB) 52 1 Intuition (TMP) 70 1 Jeska\u0026#39;s Will (CMR) 187 1 Lion\u0026#39;s Eye Diamond (MIR) 307 1 Lotho, Corrupt Shirriff (LTR) 370 1 Lotus Petal (TMP) 294 1 Mana Confluence (JOU) 163 1 Mana Vault (LEB) 260 1 Marsh Flats (ZEN) 219 1 Mental Misstep (NPH) 38 1 Mindbreak Trap (ZEN) 57 1 Mistrise Village (TDM) 261 1 Misty Rainforest (ZEN) 220 1 Mnemonic Betrayal (GRN) 189 1 Mockingbird (BLB) 61 1 Mox Diamond (STH) 138 1 Mox Opal (SOM) 179 1 Mystic Remora (ICE) 87 1 Mystical Tutor (MIR) 80 1 Necropotence (ICE) 154 1 Orcish Bowmasters (PLTR) 103s 1 Otawara, Soaring City (NEO) 271 1 Pact of Negation (FUT) 42 1 Plateau (ME4) 249 1 Polluted Delta (ONS) 321 1 Professional Face-Breaker (SNC) 426 1 Ragavan, Nimble Pilferer (MH2) 138 1 Rain of Filth (USG) 151 1 Ranger-Captain of Eos (MH1) 21 1 Red Elemental Blast (30A) 462 1 Rhystic Study (PCY) 45 1 Rite of Flame (CSP) 96 1 Scalding Tarn (ZEN) 223 1 Scrubland (ME4) 251 1 Sevinne\u0026#39;s Reclamation (C19) 5 1 Silence (M10) 31 1 Simian Spirit Guide (PLC) 122 1 Smothering Tithe (RNA) 22 1 Snapback (TSP) 78 1 Sol Ring (WHO) 836 1 Spire of Industry (AER) 184 1 Swan Song (THS) 65 1 Tainted Pact (ODY) 164 1 Tarnished Citadel (ODY) 329 1 Tataru Taru (FIC) 138 1 Thassa\u0026#39;s Oracle (THB) 308 1 Tundra (ME4) 255 1 Underground Sea (ME4) 256 1 Underworld Breach (THB) 161 1 Valley Floodcaller (BLB) 79 1 Vampiric Tutor (VIS) 72 1 Verdant Catacombs (ZEN) 229 1 Voice of Victory (TDM) 33 1 Volcanic Island (ME4) 260 1 Watery Grave (RAV) 286 1 Wheel of Fortune (VMA) 192 1 Windfall (USG) 111 1 Windswept Heath (ONS) 328 1 Wishclaw Talisman (ELD) 110 considering(38) # 1 Teferi, Time Raveler 1 Delney, Streetwise Lookout 1 Flesh Duplicate 1 Grim Hireling 1 Knuckles the Echidna 1 Ledger Shredder 1 Mercurial Spelldancer 1 Opposition Agent 1 Phantasmal Image 1 Pollywog Prodigy 1 Rev, Tithe Extractor 1 Talion, the Kindly Lord 1 Vivi Ornitier 1 Beseech the Mirror 1 Demonic Counsel 1 Last Chance 1 Peer into the Abyss 1 Praetor\u0026#39;s Grasp 1 Toxic Deluge 1 Yawgmoth\u0026#39;s Will 1 Fire Covenant 1 Burnt Offering 1 Cyclonic Rift 1 Gifts Ungiven 1 March of Swirling Mist 1 Miscast 1 Pyroblast 1 Grim Monolith 1 Mox Amber 1 Talisman of Dominance 1 Talisman of Progress 1 The One Ring 1 Steal Enchantment 1 Hallowed Fountain 1 Morphic Pool 1 Steam Vents 1 Undercity Sewers 1 Wooded Foothills Deck #2: Godspeed # Topdeck Moxfield ","date":"20 August 2025","externalUrl":null,"permalink":"/notes/magic/","section":"Notes","summary":" Magic: The Gathering Terminology # Creature - A permanent type representing a living being. Sorcery - A spell type that is cast and then put into the graveyard. Summoning sickness - only affects creatures Tapped out - A state where a player has no untapped lands available to use for mana.\nFlying - A keyword ability that allows a creature to avoid being blocked except by creatures with flying or reach. Touch death - A state where a creature with 0 toughness is put into the graveyard.\n","title":"Magic","type":"notes"},{"content":" CLI # Rendercv CLI rendercv new \u0026#34;Name\u0026#34; --theme \u0026#34;engineeringclassic\u0026#34; rendercv render \u0026#34;xxx.yaml\u0026#34; ","date":"19 August 2025","externalUrl":null,"permalink":"/notes/rendercv/","section":"Notes","summary":"CLI # Rendercv CLI rendercv new \"Name\" --theme \"engineeringclassic\" rendercv render \"xxx.yaml\"","title":"Rendercv","type":"notes"},{"content":" References # LeetCode Bit Manipulation Notes Prefix Sum 2D Notes Difference Array Notes Modular Arithmetic Notes: modular arithmetic identities/Fermat\u0026rsquo;s Little Theorem/binomial coefficient(n choose k) Problem List # Data Structures: basic/prefix sum/stack/queue/heap/trie/union-find/fenwick tree (BIT (binary indexed tree))/segment tree Sliding Window and Two Pointers: fixed-length sliding window/variable-length sliding window/single-sequence case (on one array/string)/two-sequence case (on two arrays/strings)/three pointers/grouped iteration DP: knapsack (0/1)/partition/state-machine/interval/bitmask/digit/tree/DP optimization Bit Manipulation: basic/XOR/AND/OR/LogTrick Binary Search: binary search on the answer (space)/minimize the maximum (value)/maximize the minimum (value)/k-th smallest (element) Monotonic Stack: rectangle(largest rectangle in a histogram; maximal rectangle in a binary matrix)/contribution technique/lexicographically smallest Grid: DFS/BFS Graph Algorithm: DFS/BFS/topological sort/unicyclic graph/shortest path/minimum spanning tree (MST)/network flow Mathematical Algorithm: number theory/combinatorics/probability and expectation/game theory/computational geometry/randomized algorithms Greedy Algorithm: basic/rollback/interval/lexicographic/math-based/greedy thinking/constructive Linked List, Binary Tree, Backtracking: front-back pointers/fast-slow pointers/DFS/BFS/diameter/LCA/general tree String: KMP (Knuth-Morris-Pratt; prefix function/pi array)/Manacher (longest palindromic substring)/string hashing/AC automation(Aho-Corasick automation; trie with failure links)/suffix array (SA; LCP array (longest common prefix)) Data Structures # Basic # 1. Enumerate the right, maintain the left. # Core Concept: For two-variable problems like the two-sum where a_i + a_j = t:\nEnumerate the right-hand variable a_j Transform into a single-variable problem: find a_i = t - a_j Use a hash table to maintain previously seen left elements Implementation Pattern:\ndef two_sum_pattern(arr, target): seen = {} # Hash table for left elements for j, a_j in enumerate(arr): complement = target - a_j if complement in seen: return [seen[complement], j] seen[a_j] = j return None 2. Enumerate the middle. # Core Concept: When dealing with multiple variables with ordering constraints, enumerate the middle variable to automatically separate the problem space.\nStrategy Best For Time Space Key Tool Right Enumeration 2-variable problems O(n) O(n) Hash table Middle Enumeration 3+ variable problems O(n²) O(1) Two pointers Implementation Pattern:\ndef three_sum_pattern(arr): arr.sort() # Enable two-pointer technique result = [] for j in range(1, len(arr) - 1): # Enumerate middle left, right = 0, len(arr) - 1 while left \u0026lt; j and right \u0026gt; j: current_sum = arr[left] + arr[j] + arr[right] if current_sum == target: result.append([arr[left], arr[j], arr[right]]) left += 1 right -= 1 elif current_sum \u0026lt; target: left += 1 else: right -= 1 return result Prefix Sum # 1. Left-Closed Right-Open Formula # Core Concept: For the sum of elements in the left-closed right-open interval [left, right):\nRange Sum = sum[right] - sum[left] Where:\nsum[i] represents the sum of the first i elements (i.e., arr[0] + arr[1] + ... + arr[i-1]) Interval [left, right) includes left but excludes right Implementation Template:\ndef build_prefix_sum(arr): \u0026#34;\u0026#34;\u0026#34; Build prefix sum array sum[i] represents the sum of first i elements \u0026#34;\u0026#34;\u0026#34; n = len(arr) prefix_sum = [0] * (n + 1) # Length n+1 for easier boundary handling for i in range(n): prefix_sum[i + 1] = prefix_sum[i] + arr[i] return prefix_sum def range_sum(prefix_sum, left, right): \u0026#34;\u0026#34;\u0026#34; Query sum of interval [left, right) Time Complexity: O(1) \u0026#34;\u0026#34;\u0026#34; return prefix_sum[right] - prefix_sum[left] class PrefixSum: def __init__(self, arr): \u0026#34;\u0026#34;\u0026#34;Initialize prefix sum array\u0026#34;\u0026#34;\u0026#34; self.n = len(arr) self.prefix = [0] * (self.n + 1) # Build prefix sum for i in range(self.n): self.prefix[i + 1] = self.prefix[i] + arr[i] def query(self, left, right): \u0026#34;\u0026#34;\u0026#34; Query sum of interval [left, right) Args: left: Left boundary (inclusive) right: Right boundary (exclusive) Returns: Range sum \u0026#34;\u0026#34;\u0026#34; return self.prefix[right] - self.prefix[left] # Usage Example arr = [1, 2, 3, 4, 5] ps = PrefixSum(arr) # Query different intervals print(ps.query(0, 3)) # [0, 3) -\u0026gt; 1+2+3 = 6 print(ps.query(1, 4)) # [1, 4) -\u0026gt; 2+3+4 = 9 print(ps.query(2, 5)) # [2, 5) -\u0026gt; 3+4+5 = 12 ","date":"16 August 2025","externalUrl":null,"permalink":"/notes/lc-2025/","section":"Notes","summary":"References # LeetCode Bit Manipulation Notes Prefix Sum 2D Notes Difference Array Notes Modular Arithmetic Notes: modular arithmetic identities/Fermat’s Little Theorem/binomial coefficient(n choose k) Problem List # Data Structures: basic/prefix sum/stack/queue/heap/trie/union-find/fenwick tree (BIT (binary indexed tree))/segment tree Sliding Window and Two Pointers: fixed-length sliding window/variable-length sliding window/single-sequence case (on one array/string)/two-sequence case (on two arrays/strings)/three pointers/grouped iteration DP: knapsack (0/1)/partition/state-machine/interval/bitmask/digit/tree/DP optimization Bit Manipulation: basic/XOR/AND/OR/LogTrick Binary Search: binary search on the answer (space)/minimize the maximum (value)/maximize the minimum (value)/k-th smallest (element) Monotonic Stack: rectangle(largest rectangle in a histogram; maximal rectangle in a binary matrix)/contribution technique/lexicographically smallest Grid: DFS/BFS Graph Algorithm: DFS/BFS/topological sort/unicyclic graph/shortest path/minimum spanning tree (MST)/network flow Mathematical Algorithm: number theory/combinatorics/probability and expectation/game theory/computational geometry/randomized algorithms Greedy Algorithm: basic/rollback/interval/lexicographic/math-based/greedy thinking/constructive Linked List, Binary Tree, Backtracking: front-back pointers/fast-slow pointers/DFS/BFS/diameter/LCA/general tree String: KMP (Knuth-Morris-Pratt; prefix function/pi array)/Manacher (longest palindromic substring)/string hashing/AC automation(Aho-Corasick automation; trie with failure links)/suffix array (SA; LCP array (longest common prefix)) Data Structures # Basic # 1. Enumerate the right, maintain the left. # Core Concept: For two-variable problems like the two-sum where a_i + a_j = t:\n","title":"LC 2025","type":"notes"},{"content":" Syntax # 📘 RULE STRUCTURE ──────────────────────────── rule \u0026#34;Rule Name\u0026#34; when // CONDITIONS (LHS) $fact : Class(field == value) not(Class(field != value)) exists(Class(field \u0026gt; 100)) eval(customMethod($fact)) accumulate(...) then // ACTIONS (RHS) System.out.println($fact); modify($fact) { setField(\u0026#34;new value\u0026#34;) } insert(new Fact(...)) retract($fact) end General Syntax of the when block # when // pattern $var: SomeFactType(field1 == \u0026#34;value\u0026#34;, field2 \u0026gt; 10) // condition only (no binding) SomeOtherType(field3 matches \u0026#34;regex.*\u0026#34;) // logical combinations eval(someJavaMethod($var)) // negation, existence not(SomeType(...)) exists(SomeType(...)) // temporal logic (for CEP) EventA() EventB(this after[0s,10s] EventA()) Fact Pattern Matching $person: Person(age \u0026gt; 18, name == \u0026#34;Alice\u0026#34;) Person: class name (fact type) $person: variable binding (optional). You can refer to this in then. age \u0026gt; 18, name == \u0026quot;Alice\u0026quot;: field constraints (support most Java expressions) Binding Variables $amount: Order(amount \u0026gt; 1000) Binds the Order fact to $amount so you can use it in the then block. Order($value: amount) This binds amount field to $value directly. Logical Operators in Constraints inside a pattern\n==, !=, \u0026lt;, \u0026gt;, \u0026gt;=, \u0026lt;=, \u0026amp;\u0026amp; in (1, 2, 3) not in (1, 2, 3) matches contains not, exist, forall These add logical negation or existential conditions: not(Person(age \u0026lt; 18)) // No person under 18 exists exists(Order(amount \u0026gt; 1000)) // At least one order \u0026gt; 1000 exists forall(Person(age \u0026gt; 18)) // All Persons must satisfy age \u0026gt; 18 eval() - Arbitrary Java Expressions eval(someCustomCheck($person)) Use this if you need complex logic that\u0026rsquo;s not easily expressed in patterns.\n⚠️ Try to avoid eval() unless necessary — it breaks indexing and slows down performance.\nAccumulate(Aggregate Matching) accumulate( $o: Order(amount \u0026gt; 100), $sum: sum($o.amount) ) Other functions: count(), average(), collectList(), etc.\nExample:\nrule \u0026#34;VIP Alert\u0026#34; when $c: Customer(type == \u0026#34;VIP\u0026#34;, $spent: totalSpent \u0026gt; 1000) not(BanList(name == $c.name)) accumulate( Order(customer == $c) over window:time(10m), $count: count() ) eval($count \u0026gt;= 3) then System.out.println(\u0026#34;Alert: VIP customer \u0026#34; + $c.getName() + \u0026#34; made 3+ orders in 10 min!\u0026#34;); end ","date":"13 July 2025","externalUrl":null,"permalink":"/notes/bre-drools/","section":"Notes","summary":"Syntax # 📘 RULE STRUCTURE ──────────────────────────── rule \"Rule Name\" when // CONDITIONS (LHS) $fact : Class(field == value) not(Class(field != value)) exists(Class(field \u003e 100)) eval(customMethod($fact)) accumulate(...) then // ACTIONS (RHS) System.out.println($fact); modify($fact) { setField(\"new value\") } insert(new Fact(...)) retract($fact) end General Syntax of the when block # when // pattern $var: SomeFactType(field1 == \"value\", field2 \u003e 10) // condition only (no binding) SomeOtherType(field3 matches \"regex.*\") // logical combinations eval(someJavaMethod($var)) // negation, existence not(SomeType(...)) exists(SomeType(...)) // temporal logic (for CEP) EventA() EventB(this after[0s,10s] EventA()) Fact Pattern Matching $person: Person(age \u003e 18, name == \"Alice\") Person: class name (fact type) $person: variable binding (optional). You can refer to this in then. age \u003e 18, name == \"Alice\": field constraints (support most Java expressions) Binding Variables $amount: Order(amount \u003e 1000) Binds the Order fact to $amount so you can use it in the then block. Order($value: amount) This binds amount field to $value directly. Logical Operators in Constraints inside a pattern\n","title":"BRE Drools","type":"notes"},{"content":" Platform threads # Platform threads are the traditional threads provided by the operating system. They are managed by the OS and can be used to perform concurrent tasks. However, they can be resource-intensive and may lead to issues like thread contention and deadlocks.\nVirtual threads # Virtual threads are a lightweight alternative to platform threads, introduced in Java 19 as a preview feature and made stable in Java 21. They are designed to be more efficient and easier to use for concurrent programming. Virtual threads are managed by the Java Virtual Machine (JVM) rather than the operating system, allowing for a large number of concurrent tasks without the overhead associated with platform threads. Virtual threads are created using the Thread.ofVirtual() method, and they can be used in the same way as platform threads. However, they are more efficient because they do not require a separate OS thread for each task. Instead, they use a small amount of memory and can be scheduled by the JVM to run on available platform threads.\nPlatform thread vs Virtual thread # // platform thread Thread.ofPlatform().start(() -\u0026gt; { System.out.println(Thread.curentThread()); // Thread[#21,Thread-0,5,main] }); // virtual thread Thread vt = Thread.ofVirtual().start(() -\u0026gt; { System.out.println(Thread.currentThread()); // VirtualThread[#22]/runnable@ForkJoinPool-1-worker-1 }); // wait for the virtual thread to finish vt.join(); Virtual thread Example # Set\u0026lt;String\u0026gt; threadNames = ConcurrentHashMap.newKeySet(); // Create virtual threads with executors ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); IntStream.range(0, 1_000_000).forEach(i -\u0026gt; { executor.submit(() -\u0026gt; { Thread.sleep(Duration.ofSeconds(1)); String threadInfo = Thread.currentThread().toString(); // VirtualThread[#22]/runnable@ForkJoinPool-1-worker-1 String workerName = threadInfo.split(\u0026#34;@\u0026#34;)[1] threadNames.add(workerName); return i; }); }); System.out.println(\u0026#34;Platform Threads: \u0026#34; + threadNames.size()); Best Practices # Avoid Thread Pools Use Executors.newVirtualThreadPerTaskExecutor() instead of traditional pools. try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(() -\u0026gt; processTask(task)); } Never Pool Virtual Threads They’re designed to be ephemeral. Creating millions is safe.\nReplace Async Callbacks Favor synchronous code over CompletableFuture or reactive patterns:\n// INSTEAD OF: CompletableFuture.supplyAsync(() -\u0026gt; fetchData(), pool); // USE: Thread.startVirtualThread(() -\u0026gt; fetchData()); Minimize synchronized Blocks Synchronized blocks pin virtual threads to carrier threads, reducing throughput. Prefer ReentrantLock: private final ReentrantLock lock = new ReentrantLock(); void safeMethod() { lock.lock(); // Allows thread unmounting try { /* ... */ } finally { lock.unlock(); } } Limit ThreadLocal Usage Millions of virtual threads = millions of ThreadLocal copies. Use ScopedValue (Java 21) for shared immutable data: final ScopedValue\u0026lt;String\u0026gt; USER = ScopedValue.newInstance(); ScopedValue.where(USER, \u0026#34;alice\u0026#34;, () -\u0026gt; processRequest()); Avoid Object.finalize() Finalizers hold thread references, delaying garbage collection.\nUse NIO/Asynchronous Libraries Ensure I/O libraries (JDBC, HTTP, etc.) support non-blocking operations. Wrap blocking I/O in virtual threads.\nStructured Concurrency (Preview) Group related tasks with StructuredTaskScope (Java 21):\ntry (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Future\u0026lt;String\u0026gt; user = scope.fork(() -\u0026gt; fetchUser()); Future\u0026lt;Integer\u0026gt; order = scope.fork(() -\u0026gt; fetchOrders()); scope.join(); return new Response(user.resultNow(), order.resultNow()); } Monitor with JDK Flight Recorder Use JFR events (jdk.VirtualThreadStart, jdk.VirtualThreadEnd) for profiling.\nHandle Blocking Operations Carefully Offload CPU-bound tasks to a separate pool of platform threads:\nExecutorService cpuBoundExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); When to Use Virtual Threads # I/O-Bound Workloads: HTTP servers, database calls, message queues. High-Concurrency Apps: Each request can run in its own thread. Simplified Code: Replace complex async code with synchronous logic. When to Avoid # CPU-Intensive Tasks: Use platform threads instead. Native Code/JNI: Operations blocking in native code. Conclusion # StructuredTaskScope:\t✅✅✅ 最结构化、最安全 Executors.newVirtualThreadPerTaskExecutor():\t✅✅ 适合大多数任务提交场景 Thread.ofVirtual().start():\t✅ 用于简单任务，不建议用于批量任务 自定义虚拟线程工厂 + Executors:\t✅ 灵活定制，适合需要控制线程属性的场景 ✅ 最佳实践建议：尽量使用 结构化并发（如 StructuredTaskScope）或通过 Executors.newVirtualThreadPerTaskExecutor() 管理虚拟线程，避免零散使用裸线程。\nStructuredTaskScope # 场景 1：并行调用多个服务，只要最快返回的结果 # 比如我们有多个外部服务，结果一致，我们只需要最快响应的那一个。 使用 StructuredTaskScope.ShutdownOnSuccess：\nimport java.util.concurrent.StructuredTaskScope; String result; try (var scope = new StructuredTaskScope.ShutdownOnSuccess\u0026lt;String\u0026gt;()) { scope.fork(() -\u0026gt; callServiceA()); scope.fork(() -\u0026gt; callServiceB()); scope.fork(() -\u0026gt; callServiceC()); scope.join(); // 等待其中一个成功 result = scope.result(); // 获取第一个成功的结果 } System.out.println(\u0026#34;First successful result: \u0026#34; + result); ✅ 特点： # 只保留第一个成功的任务。 其他线程自动中断（通过虚拟线程的挂起机制，几乎无成本）。 场景 2：多个任务并发执行，但任何一个失败就全部取消 # 比如我们在并行执行数据库写入、日志记录、缓存刷新，只要任何一个失败就放弃整个操作。 使用 StructuredTaskScope.ShutdownOnFailure：\nimport java.util.concurrent.StructuredTaskScope; try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { scope.fork(() -\u0026gt; writeToDatabase()); scope.fork(() -\u0026gt; updateCache()); scope.fork(() -\u0026gt; writeAuditLog()); scope.join(); // 等所有完成或失败 scope.throwIfFailed(); // 任何一个失败都会抛出异常 } ✅ 特点： # 所有任务并发执行。 一旦有任务抛出异常，其他任务会被取消。 统一处理异常和取消逻辑，不再需要 try-catch 每个任务。 场景 3：父任务等待所有子任务完成，然后合并结果 # 比如我们调用 3 个服务，要组合它们的结果生成最终响应。 使用 StructuredTaskScope + ShutdownOnFailure + Future 变量：\nString userInfo, orderInfo, paymentInfo; try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { var userFuture = scope.fork(() -\u0026gt; getUser()); var orderFuture = scope.fork(() -\u0026gt; getOrder()); var paymentFuture = scope.fork(() -\u0026gt; getPayment()); scope.join(); scope.throwIfFailed(); userInfo = userFuture.resultNow(); orderInfo = orderFuture.resultNow(); paymentInfo = paymentFuture.resultNow(); String response = combine(userInfo, orderInfo, paymentInfo); System.out.println(\u0026#34;Final response: \u0026#34; + response); } 使用情境 适合的 Scope 子类 特点 等最快返回结果 ShutdownOnSuccess\u0026lt;T\u0026gt; 类似 \u0026ldquo;anyOf\u0026rdquo; 等全部成功 ShutdownOnFailure 所有成功才继续 聚合多个任务结果 ShutdownOnFailure + 多个 Future 效果类似 Promise.all() 有条件并发执行 任意 StructuredTaskScope 动态控制任务加入和取消 带超时的结构化并发 任意 StructuredTaskScope + joinUntil() 带 deadline 控制 如果你在 Java 中有多个并发任务需要一起协调、失败处理、结果聚合，StructuredTaskScope 是最安全、最现代化的解决方案，特别适合配合虚拟线程使用。\nExecutors.newVirtualThreadPerTaskExecutor() # 场景 1：高并发 Web 爬虫 / 批处理任务 # 假设你需要爬取上千个网页，或者处理成千上万个任务，传统线程池（如 FixedThreadPool）会由于线程资源限制而阻塞。但使用虚拟线程可以立即创建大量轻量级线程并执行任务。\nimport java.net.URI; import java.net.http.*; import java.util.concurrent.*; var urls = List.of(\u0026#34;https://a.com\u0026#34;, \u0026#34;https://b.com\u0026#34;, \u0026#34;https://c.com\u0026#34;); // 假设有上千个 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { List\u0026lt;Future\u0026lt;String\u0026gt;\u0026gt; futures = urls.stream() .map(url -\u0026gt; executor.submit(() -\u0026gt; fetchPage(url))) .toList(); for (Future\u0026lt;String\u0026gt; f : futures) { System.out.println(f.get()); // 输出内容或处理 } } String fetchPage(String url) throws Exception { var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder(new URI(url)).build(); return client.send(request, HttpResponse.BodyHandlers.ofString()).body(); } ✅ 特点： # 自动创建虚拟线程，每个任务不受线程池限制 使用 try-with-resources 自动关闭执行器，避免资源泄露 任务之间完全隔离，不会互相影响 场景 2：服务端请求并发处理 # 每个 HTTP 请求用一个虚拟线程处理，避免了传统线程池的瓶颈：\nExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); server.onRequest((req) -\u0026gt; { executor.submit(() -\u0026gt; handleRequest(req)); }); 适用于微服务、API 网关、数据库连接池等服务端并发处理场景。\n场景 3：异步任务编排，不关心返回值 # 比如日志记录、统计打点，不需要返回值，可以直接提交：\ntry (var executor = Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(() -\u0026gt; logUserAction(\u0026#34;login\u0026#34;, \u0026#34;user123\u0026#34;)); executor.submit(() -\u0026gt; sendAnalytics(\u0026#34;open_homepage\u0026#34;)); } 场景 4：并行测试执行器 # 在测试框架中，可以用虚拟线程并行跑多个测试用例，提升测试吞吐量：\ntry (var executor = Executors.newVirtualThreadPerTaskExecutor()) { List\u0026lt;Future\u0026lt;Boolean\u0026gt;\u0026gt; results = testCases.stream() .map(test -\u0026gt; executor.submit(() -\u0026gt; runTestCase(test))) .toList(); for (Future\u0026lt;Boolean\u0026gt; r : results) { assert r.get(); // 所有测试应通过 } } ⚠️ 注意事项（高级用法细节） # 每个任务创建一个新虚拟线程，适合 IO 密集型、不共享状态的任务。 不适用于高频、极短任务（如纳秒级计算），否则调度开销可能超过任务成本。 使用时应配合 try-with-resources 保证资源关闭。 不自动限制并发数，如有资源限制应手动控制（如信号量或限流器）。 ","date":"5 June 2025","externalUrl":null,"permalink":"/notes/virtual-thread/","section":"Notes","summary":"Platform threads # Platform threads are the traditional threads provided by the operating system. They are managed by the OS and can be used to perform concurrent tasks. However, they can be resource-intensive and may lead to issues like thread contention and deadlocks.\nVirtual threads # Virtual threads are a lightweight alternative to platform threads, introduced in Java 19 as a preview feature and made stable in Java 21. They are designed to be more efficient and easier to use for concurrent programming. Virtual threads are managed by the Java Virtual Machine (JVM) rather than the operating system, allowing for a large number of concurrent tasks without the overhead associated with platform threads. Virtual threads are created using the Thread.ofVirtual() method, and they can be used in the same way as platform threads. However, they are more efficient because they do not require a separate OS thread for each task. Instead, they use a small amount of memory and can be scheduled by the JVM to run on available platform threads.\n","title":"Virtual Thread","type":"notes"},{"content":" Syntax # -X, \u0026ndash;request, HTTP method to be used. -i, \u0026ndash;include, Include the response header. -d, \u0026ndash;data, Data to be sent in the API. -H, \u0026ndash;header, Any additional headers to be sent. Api testing with Scripts # pip install pyyaml - name: Get User method: GET url: https://api.example.com/users/{userId} path_params: userId: 123 query_params: verbose: true headers: Authorization: Bearer \u0026lt;TOKEN\u0026gt; - name: Create User method: POST url: https://api.example.com/users headers: Authorization: Bearer \u0026lt;TOKEN\u0026gt; Content-Type: application/json payload: name: John Doe email: john@example.com import yaml import json import urllib.parse def load_api_definitions(file_path): with open(file_path, \u0026#39;r\u0026#39;) as f: return yaml.safe_load(f) def build_curl_command(api): method = api.get(\u0026#34;method\u0026#34;, \u0026#34;GET\u0026#34;).upper() url = api[\u0026#34;url\u0026#34;] # Replace path parameters if \u0026#34;path_params\u0026#34; in api: for key, value in api[\u0026#34;path_params\u0026#34;].items(): url = url.replace(f\u0026#34;{{{key}}}\u0026#34;, str(value)) # Add query parameters if \u0026#34;query_params\u0026#34; in api: query_string = urllib.parse.urlencode(api[\u0026#34;query_params\u0026#34;]) url += f\u0026#34;?{query_string}\u0026#34; curl_cmd = f\u0026#34;curl -X {method} \u0026#39;{url}\u0026#39;\u0026#34; # Headers headers = api.get(\u0026#34;headers\u0026#34;, {}) for key, value in headers.items(): curl_cmd += f\u0026#34; -H \u0026#39;{key}: {value}\u0026#39;\u0026#34; # Payload if \u0026#34;payload\u0026#34; in api and method in [\u0026#34;POST\u0026#34;, \u0026#34;PUT\u0026#34;, \u0026#34;PATCH\u0026#34;]: payload_str = json.dumps(api[\u0026#34;payload\u0026#34;]) curl_cmd += f\u0026#34; -d \u0026#39;{payload_str}\u0026#39;\u0026#34; return curl_cmd def generate_curl_scripts(api_file_path, output_script_path): apis = load_api_definitions(api_file_path) with open(output_script_path, \u0026#39;w\u0026#39;) as f: f.write(\u0026#34;#!/bin/bash\\n\\n\u0026#34;) for api in apis: f.write(f\u0026#34;# {api.get(\u0026#39;name\u0026#39;, \u0026#39;Unnamed API\u0026#39;)}\\n\u0026#34;) curl_cmd = build_curl_command(api) f.write(curl_cmd + \u0026#34;\\n\\n\u0026#34;) print(f\u0026#34;✅ Generated cURL script: {output_script_path}\u0026#34;) # Run the generator if __name__ == \u0026#34;__main__\u0026#34;: generate_curl_scripts(\u0026#34;apis.yaml\u0026#34;, \u0026#34;run_api_tests.sh\u0026#34;) #!/bin/bash # Get User curl -X GET \u0026#39;https://api.example.com/users/123?verbose=true\u0026#39; -H \u0026#39;Authorization: Bearer \u0026lt;TOKEN\u0026gt;\u0026#39; # Create User curl -X POST \u0026#39;https://api.example.com/users\u0026#39; -H \u0026#39;Authorization: Bearer \u0026lt;TOKEN\u0026gt;\u0026#39; -H \u0026#39;Content-Type: application/json\u0026#39; -d \u0026#39;{\u0026#34;name\u0026#34;: \u0026#34;John Doe\u0026#34;, \u0026#34;email\u0026#34;: \u0026#34;john@example.com\u0026#34;}\u0026#39; ","date":"26 May 2025","externalUrl":null,"permalink":"/notes/curl/","section":"Notes","summary":"Syntax # -X, –request, HTTP method to be used. -i, –include, Include the response header. -d, –data, Data to be sent in the API. -H, –header, Any additional headers to be sent. Api testing with Scripts # pip install pyyaml - name: Get User method: GET url: https://api.example.com/users/{userId} path_params: userId: 123 query_params: verbose: true headers: Authorization: Bearer \u003cTOKEN\u003e - name: Create User method: POST url: https://api.example.com/users headers: Authorization: Bearer \u003cTOKEN\u003e Content-Type: application/json payload: name: John Doe email: john@example.com import yaml import json import urllib.parse def load_api_definitions(file_path): with open(file_path, 'r') as f: return yaml.safe_load(f) def build_curl_command(api): method = api.get(\"method\", \"GET\").upper() url = api[\"url\"] # Replace path parameters if \"path_params\" in api: for key, value in api[\"path_params\"].items(): url = url.replace(f\"{{{key}}}\", str(value)) # Add query parameters if \"query_params\" in api: query_string = urllib.parse.urlencode(api[\"query_params\"]) url += f\"?{query_string}\" curl_cmd = f\"curl -X {method} '{url}'\" # Headers headers = api.get(\"headers\", {}) for key, value in headers.items(): curl_cmd += f\" -H '{key}: {value}'\" # Payload if \"payload\" in api and method in [\"POST\", \"PUT\", \"PATCH\"]: payload_str = json.dumps(api[\"payload\"]) curl_cmd += f\" -d '{payload_str}'\" return curl_cmd def generate_curl_scripts(api_file_path, output_script_path): apis = load_api_definitions(api_file_path) with open(output_script_path, 'w') as f: f.write(\"#!/bin/bash\\n\\n\") for api in apis: f.write(f\"# {api.get('name', 'Unnamed API')}\\n\") curl_cmd = build_curl_command(api) f.write(curl_cmd + \"\\n\\n\") print(f\"✅ Generated cURL script: {output_script_path}\") # Run the generator if __name__ == \"__main__\": generate_curl_scripts(\"apis.yaml\", \"run_api_tests.sh\") #!/bin/bash # Get User curl -X GET 'https://api.example.com/users/123?verbose=true' -H 'Authorization: Bearer \u003cTOKEN\u003e' # Create User curl -X POST 'https://api.example.com/users' -H 'Authorization: Bearer \u003cTOKEN\u003e' -H 'Content-Type: application/json' -d '{\"name\": \"John Doe\", \"email\": \"john@example.com\"}'","title":"Curl","type":"notes"},{"content":" 198 - House Robber # class Solution: def rob(self, nums: List[int]) -\u0026gt; int: n = len(nums) f0 = f1 = 0 for i, x in enumerate(nums): f0, f1 = f1, max(f0 + x, f1) return f1 # cahce = [-1] * n # def dfs(i): # if i \u0026lt; 0: # return 0 # if cache[i] != -1: # return cache[i] # cache[i] = max(dfs(i - 1), dfs(i - 2) + nums[i]) # return cache[i] class Solution { public: int rob(vector\u0026lt;int\u0026gt;\u0026amp; nums) { // int n = nums.size(); // int f0 = 0, f1 = 0; // for (int i = 0; i \u0026lt; n; ++i) { // int x = nums[i]; // int new_f1 = max(f0 + x, f1); // f0 = f1; // f1 = new_f1; // } // return f1; // Below is the optional recursive + memoized (DFS + cache) version int n = nums.size(); vector\u0026lt;int\u0026gt; cache(n, -1); return dfs(n - 1, nums, cache); } int dfs(int i, vector\u0026lt;int\u0026gt;\u0026amp; nums, vector\u0026lt;int\u0026gt;\u0026amp; cache) { if (i \u0026lt; 0) return 0; if (cache[i] != -1) return cache[i]; cache[i] = max(dfs(i - 1, nums, cache), (i \u0026gt;= 1 ? dfs(i - 2, nums, cache) : 0) + nums[i]); return cache[i]; } // // corner cases // if (nums.size() == 0) return 0; // if (nums.size() == 1) return nums[0]; // // dp // // f[i] represents: max money by robbing previous i houses // vector\u0026lt;int\u0026gt; f(3, 0); // f[0] = nums[0]; // f[1] = max(nums[0], nums[1]); // for (int i = 2; i \u0026lt; nums.size(); ++i) { // f[i % 3] = max(nums[i] + f[(i - 2) % 3], f[(i - 1) % 3]); // } // return f[(nums.size() - 1) % 3]; }; ","date":"1 March 2025","externalUrl":null,"permalink":"/blog/dp-memo-to-iteration/","section":"Blog","summary":"198 - House Robber # class Solution: def rob(self, nums: List[int]) -\u003e int: n = len(nums) f0 = f1 = 0 for i, x in enumerate(nums): f0, f1 = f1, max(f0 + x, f1) return f1 # cahce = [-1] * n # def dfs(i): # if i \u003c 0: # return 0 # if cache[i] != -1: # return cache[i] # cache[i] = max(dfs(i - 1), dfs(i - 2) + nums[i]) # return cache[i] class Solution { public: int rob(vector\u003cint\u003e\u0026 nums) { // int n = nums.size(); // int f0 = 0, f1 = 0; // for (int i = 0; i \u003c n; ++i) { // int x = nums[i]; // int new_f1 = max(f0 + x, f1); // f0 = f1; // f1 = new_f1; // } // return f1; // Below is the optional recursive + memoized (DFS + cache) version int n = nums.size(); vector\u003cint\u003e cache(n, -1); return dfs(n - 1, nums, cache); } int dfs(int i, vector\u003cint\u003e\u0026 nums, vector\u003cint\u003e\u0026 cache) { if (i \u003c 0) return 0; if (cache[i] != -1) return cache[i]; cache[i] = max(dfs(i - 1, nums, cache), (i \u003e= 1 ? dfs(i - 2, nums, cache) : 0) + nums[i]); return cache[i]; } // // corner cases // if (nums.size() == 0) return 0; // if (nums.size() == 1) return nums[0]; // // dp // // f[i] represents: max money by robbing previous i houses // vector\u003cint\u003e f(3, 0); // f[0] = nums[0]; // f[1] = max(nums[0], nums[1]); // for (int i = 2; i \u003c nums.size(); ++i) { // f[i % 3] = max(nums[i] + f[(i - 2) % 3], f[(i - 1) % 3]); // } // return f[(nums.size() - 1) % 3]; };","title":"DP Memo to Iteration","type":"blog"},{"content":" Attach Java Debugger # { \u0026#34;version\u0026#34;: \u0026#34;0.2.0\u0026#34;, \u0026#34;configurations\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;java\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;Attach to Spring Boot\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;attach\u0026#34;, \u0026#34;hostName\u0026#34;: \u0026#34;localhost\u0026#34;, \u0026#34;port\u0026#34;: 5005 } ] } /* mvn -Plocal spring-boot:run -Dspring-boot.run.jvmArguments=\u0026#34;-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005\u0026#34; */ Vim setup # Press Ctrl + Shift + P (Cmd + Shift + P on Mac) Type \u0026ldquo;Preferences: Open User Settings (JSON)\u0026rdquo; \u0026#34;vim.normalModeKeyBindingsNonRecursive\u0026#34;: [ { \u0026#34;before\u0026#34;: [\u0026#34;g\u0026#34;, \u0026#34;r\u0026#34;], \u0026#34;commands\u0026#34;: [\u0026#34;references-view.findReferences\u0026#34;] }, { \u0026#34;before\u0026#34;: [\u0026#34;g\u0026#34;, \u0026#34;R\u0026#34;], \u0026#34;commands\u0026#34;: [\u0026#34;editor.action.referenceSearch.trigger\u0026#34;] }, { \u0026#34;before\u0026#34;: [\u0026#34;g\u0026#34;, \u0026#34;d\u0026#34;], \u0026#34;commands\u0026#34;: [\u0026#34;editor.action.revealDefinition\u0026#34;] }, { \u0026#34;before\u0026#34;: [\u0026#34;g\u0026#34;, \u0026#34;D\u0026#34;], \u0026#34;commands\u0026#34;: [\u0026#34;editor.action.revealDeclaration\u0026#34;] }, { \u0026#34;before\u0026#34;: [\u0026#34;g\u0026#34;, \u0026#34;i\u0026#34;], \u0026#34;commands\u0026#34;: [\u0026#34;editor.action.goToImplementation\u0026#34;] } ] Restart VSCode or reload the window (Ctrl + Shift + P → \u0026ldquo;Developer: Reload Window\u0026rdquo;) ","date":"5 February 2025","externalUrl":null,"permalink":"/notes/vscode/","section":"Notes","summary":"Attach Java Debugger # { \"version\": \"0.2.0\", \"configurations\": [ { \"type\": \"java\", \"name\": \"Attach to Spring Boot\", \"request\": \"attach\", \"hostName\": \"localhost\", \"port\": 5005 } ] } /* mvn -Plocal spring-boot:run -Dspring-boot.run.jvmArguments=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005\" */ Vim setup # Press Ctrl + Shift + P (Cmd + Shift + P on Mac) Type “Preferences: Open User Settings (JSON)” \"vim.normalModeKeyBindingsNonRecursive\": [ { \"before\": [\"g\", \"r\"], \"commands\": [\"references-view.findReferences\"] }, { \"before\": [\"g\", \"R\"], \"commands\": [\"editor.action.referenceSearch.trigger\"] }, { \"before\": [\"g\", \"d\"], \"commands\": [\"editor.action.revealDefinition\"] }, { \"before\": [\"g\", \"D\"], \"commands\": [\"editor.action.revealDeclaration\"] }, { \"before\": [\"g\", \"i\"], \"commands\": [\"editor.action.goToImplementation\"] } ] Restart VSCode or reload the window (Ctrl + Shift + P → “Developer: Reload Window”) ","title":"Vscode","type":"notes"},{"content":" Example # Result # common.Result\nimport lombok.Data; @Data public class Result { private Integer code; private String message; private Object data; protected Result(Integer code, String message, Object data) { this.code = code; this.message = message; this.data = data; } public static Result OK() { return new Result(200, null, null); } public static Result success(Object data) { return new Result(200, null, data); } public static Result success(String message, Object data) { return new Result(200, message, data); } } BackgroundController # controller.BackgroundController\nimport org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class BackgroundController { @GetMapping(\u0026#34;background\u0026#34;) public Result background() { return Result.success(\u0026#34;backend data\u0026#34;); } } run the application and visit http://localhost:8080/background to see the result.\nLoginIntercept # intercept.LoginIntercept\nimport org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; @Component public class LoginIntercept implements HandlerInterceptor { // return true, means continue, can acess following API // return false, means stop, immediately return result to client @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. get HttpSession object HttpSession session = request.getSession(false); if (session != null \u0026amp;\u0026amp; session.getAttribute(\u0026#34;userinfo\u0026#34;) != null) { // means user has logged in return true; } // execute to here, means user has not logged in response.sendRedirect(\u0026#34;/api/user/login\u0026#34;); return false; } } CustomMvcConfig # config.CustomMvcConfig\nimport org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.method.HandlerTypePredicate; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import jakarta.annotation.Resource; @Configuration public class CustomMvcConfig implements WebMvcConfigurer { @Resource private LoginIntercept loginIntercept; @Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer.addPathPrefix(\u0026#34;api\u0026#34;, HandlerTypePredicate.forAnnotation(RestController.class)); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginIntercept) .addPathPatterns(\u0026#34;/**\u0026#34;) .excludePathPatterns(\u0026#34;/api/user/login\u0026#34;); } } UserController # controller.UserController\nimport org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; @RestController @RequestMapping(\u0026#34;user\u0026#34;) public class UserController { @GetMapping(\u0026#34;login\u0026#34;) public Result login(HttpServletRequest request, String username, String password) { if (StringUtils.hasLength(username) \u0026amp;\u0026amp; StringUtils.hasLength(password)) { if (username.equals(\u0026#34;admin\u0026#34;) \u0026amp;\u0026amp; password.equals(\u0026#34;admin\u0026#34;) { HttpSession session = request.getSession(); session.setAttribute(\u0026#34;userinfo\u0026#34;, \u0026#34;userinfo\u0026#34;); return Result.success(\u0026#34;login success\u0026#34;, true); } } return Result.success(\u0026#34;login failed, please login first\u0026#34;, false); } @GetMapping(\u0026#34;logout\u0026#34;) public Result logout(HttpServletRequest request) { HttpSession session = request.getSession(); session.removeAttribute(\u0026#34;userinfo\u0026#34;); return Result.OK(); } } run the application and visit http://localhost:8080/background to see if it reminds you login failed, please login first .\nvisit http://localhost:8080/api/user/login?username=admin\u0026amp;password=admin to see the result, it should be login success.\nvisit http://localhost:8080/api/background to see the result, it should be backend data.\nvisit http://localhost:8080/api/user/logout to see the result, it should be backend data.\n","date":"5 February 2025","externalUrl":null,"permalink":"/notes/spring-boot-session/","section":"Notes","summary":"Example # Result # common.Result\nimport lombok.Data; @Data public class Result { private Integer code; private String message; private Object data; protected Result(Integer code, String message, Object data) { this.code = code; this.message = message; this.data = data; } public static Result OK() { return new Result(200, null, null); } public static Result success(Object data) { return new Result(200, null, data); } public static Result success(String message, Object data) { return new Result(200, message, data); } } BackgroundController # controller.BackgroundController\n","title":"Spring Boot Session","type":"notes"},{"content":"// install node with version nvm install 22.13.1 // set default node version nvm alias default 16.13.0 // verify node -v ","date":"2 February 2025","externalUrl":null,"permalink":"/notes/nvm/","section":"Notes","summary":"// install node with version nvm install 22.13.1 // set default node version nvm alias default 16.13.0 // verify node -v","title":"nvm","type":"notes"},{"content":" SETNX # The SETNX command in Redis stands for SET if Not eXists. It is used to set a key to a value only if the key does not already exist. This operation is atomic, which means it ensures that no race conditions occur when multiple clients attempt to execute the command simultaneously. syntax: SETNX key value Parameters: # key: The name of the key to set. value: The value to associate with the key if it does not already exist.\nBehavior: # If the key does not exist, the command sets the key with the given value and returns 1. If the key already exists, the command does nothing and returns 0. This is useful for implementing distributed locks or ensuring that certain keys are only set once. Summary # SETNX is commonly used to implement a distributed locking mechanism. For example: Use SETNX lock_key \u0026ldquo;lock_value\u0026rdquo; to acquire a lock. If the command returns 1, the lock is successfully acquired. If the command returns 0, another process holds the lock. To ensure the lock is eventually released, this is often paired with a TTL (using EXPIRE or SET with options) to avoid deadlocks. Let me know if you’d like more examples or related use cases!\nTools like Redis (Redlock) are used to handle locks in distributed systems. # Queue-Based Processing # Implement message queues (e.g., Kafka, RabbitMQ) at the Java level for sequential processing of requests. Optimistic Locking # If your database schema includes a version column, you can handle it in Java: @Version private int version; JPA will automatically check and manage version conflicts during updates. Transaction annotation # Annotate methods with @Transactional to manage database operations atomically. # Prompt # how bank system solve race conditions to avoid duplicate transactions how to use 2. Optimistic Locking and 3. Pessimistic Locking in bank system. should they be used in database or java system? explain Libraries like Axon Framework can help manage event sourcing and CQRS in Java applications. ","date":"22 January 2025","externalUrl":null,"permalink":"/notes/redis/","section":"Notes","summary":"SETNX # The SETNX command in Redis stands for SET if Not eXists. It is used to set a key to a value only if the key does not already exist. This operation is atomic, which means it ensures that no race conditions occur when multiple clients attempt to execute the command simultaneously. syntax: SETNX key value Parameters: # key: The name of the key to set. value: The value to associate with the key if it does not already exist.\n","title":"Redis","type":"notes"},{"content":" File Structure # .github/workflows/**.yml: Github Actions workflow files Example # name: workflow Name # workflow name on: push # trigger event permissions: # to avoid bugs when add fake-deploy step contents: write jobs: # define jobs, jobs are running in parallel npm-build: # random job name name: npm build job # job name runs-on: ubuntu-latest # runner steps: # define steps, steps are running in sequence - name: get code from github # step name uses: actions/checkout@v4 # action - name: group steps together with vertical bar # step name run: | npm install npm run build - name: fake-deploy # step name uses: JamesIves/github-pages-deploy-action@4 # action with: # action input branch: gh-pages # branch name folder: build # artifact folder Docker Example # auto generate Docker Image and push to Docker Hub create a docker hub repository with namereact-app New Access Token in Docker Hub FROM node:18-alpine WORKDIR /react-app COPY public/ /react-app/public/ COPY src/ /react-app/src/ COPY package.json /react-app/package.json RUN npm install CMD [\u0026#34;npm\u0026#34;, \u0026#34;start\u0026#34;] name: Generate Docker Image and Push to Docker Hub on: push jobs: npm-build: name: npm build job name runs-on: ubuntu-latest steps: - name: get code from github uses: actions/checkout@v4 - name: login to docker hub uses: docker/login-actiion@v3 with: username: ${{ secrets.DOCKER_USERNAME }} # github -\u0026gt; Security -\u0026gt; Secrets and variables -\u0026gt; Action password: ${{ secrets.DOCKER_PASSWORD }} - name: build docker image and push to docker hub uses: docker/build-push-action@v5 with: push: true tags: ${{ secrets.DOCKER_USERNAME }}/react-app:latest # tag here must be the same as the repository name in Docker Hub ","date":"11 January 2025","externalUrl":null,"permalink":"/notes/github-actions/","section":"Notes","summary":"File Structure # .github/workflows/**.yml: Github Actions workflow files Example # name: workflow Name # workflow name on: push # trigger event permissions: # to avoid bugs when add fake-deploy step contents: write jobs: # define jobs, jobs are running in parallel npm-build: # random job name name: npm build job # job name runs-on: ubuntu-latest # runner steps: # define steps, steps are running in sequence - name: get code from github # step name uses: actions/checkout@v4 # action - name: group steps together with vertical bar # step name run: | npm install npm run build - name: fake-deploy # step name uses: JamesIves/github-pages-deploy-action@4 # action with: # action input branch: gh-pages # branch name folder: build # artifact folder Docker Example # auto generate Docker Image and push to Docker Hub create a docker hub repository with namereact-app New Access Token in Docker Hub FROM node:18-alpine WORKDIR /react-app COPY public/ /react-app/public/ COPY src/ /react-app/src/ COPY package.json /react-app/package.json RUN npm install CMD [\"npm\", \"start\"] name: Generate Docker Image and Push to Docker Hub on: push jobs: npm-build: name: npm build job name runs-on: ubuntu-latest steps: - name: get code from github uses: actions/checkout@v4 - name: login to docker hub uses: docker/login-actiion@v3 with: username: ${{ secrets.DOCKER_USERNAME }} # github -\u003e Security -\u003e Secrets and variables -\u003e Action password: ${{ secrets.DOCKER_PASSWORD }} - name: build docker image and push to docker hub uses: docker/build-push-action@v5 with: push: true tags: ${{ secrets.DOCKER_USERNAME }}/react-app:latest # tag here must be the same as the repository name in Docker Hub","title":"Github Actions","type":"notes"},{"content":" Intellij SpringBoot DevTools Setup # Add spring-boot-devtools in pom.xml Settings - Build,Execution,Deployment - Compiler, enable Build project automatically Settings - Advanced Settings, enable Allow auto-make to start even if developed application is currently running Disable cache on browser \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-devtools\u0026lt;/artifactId\u0026gt; \u0026lt;scope\u0026gt;runtime\u0026lt;/scope\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt; spring.devtools.restart.enabled=true spring.devtools.restart.additional-paths=src/main/java spring.devtools.restart.exclude=WEB-INF/** Debug # Line Breakpoint (red circle) # Condition: must put a expression returns a boolean Stream: Trace Current Stream Chain Force Return: Debug Window -\u0026gt; Debugger -\u0026gt; Function Name(right click) -\u0026gt; Force Return Throw Exception: same as above Multi Thread: Right click break point -\u0026gt; Enable Suspend with Thread Method Breakpoint (red diamond) # Most of time use in Java interface Field Watchpoint (red eye) # Exception Breakpoint (red thunder) # Manually add Java Exception Breakpoints ideaVim # diw di\u0026#34; ciw ci\u0026#34; cit // delete within tag dt\u0026gt; // delete to \u0026gt; ct\u0026gt; // delete to \u0026gt; ","date":"11 January 2025","externalUrl":null,"permalink":"/notes/idea/","section":"Notes","summary":"Intellij SpringBoot DevTools Setup # Add spring-boot-devtools in pom.xml Settings - Build,Execution,Deployment - Compiler, enable Build project automatically Settings - Advanced Settings, enable Allow auto-make to start even if developed application is currently running Disable cache on browser \u003cdependency\u003e \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e \u003cartifactId\u003espring-boot-devtools\u003c/artifactId\u003e \u003cscope\u003eruntime\u003c/scope\u003e \u003coptional\u003etrue\u003c/optional\u003e \u003c/dependency\u003e spring.devtools.restart.enabled=true spring.devtools.restart.additional-paths=src/main/java spring.devtools.restart.exclude=WEB-INF/** Debug # Line Breakpoint (red circle) # Condition: must put a expression returns a boolean Stream: Trace Current Stream Chain Force Return: Debug Window -\u003e Debugger -\u003e Function Name(right click) -\u003e Force Return Throw Exception: same as above Multi Thread: Right click break point -\u003e Enable Suspend with Thread Method Breakpoint (red diamond) # Most of time use in Java interface Field Watchpoint (red eye) # Exception Breakpoint (red thunder) # Manually add Java Exception Breakpoints ideaVim # diw di\" ciw ci\" cit // delete within tag dt\u003e // delete to \u003e ct\u003e // delete to \u003e","title":"IDEA","type":"notes"},{"content":" doc helper # npx nx list @nx/angular useful properties and tips # --dry-run --skip-tests @nx/angular:xxx is optional, we can directly use xxx, and then choose. generate monorepo project # npx create-nx-workspace@latest monorepo-name --preset=angular-monorepo generate app # npx nx g app apps/app-name generate component/directive/pipe/service # npx nx g c|d|p|s full-path --skip-tests generate library # npx nx g lib libs/lib-name NX Angular MFS # npx create-nx-workspace@latest ng-mf --preset=apps cd ng-mf npx nx add @nx/angular npx nx g @nx/angular:host apps/dashboard --prefix=ng-mf npx nx g @nx/angular:remote apps/login --prefix=ng-mf --host=dashboard ","date":"1 December 2024","externalUrl":null,"permalink":"/notes/nx/","section":"Notes","summary":" doc helper # npx nx list @nx/angular useful properties and tips # --dry-run --skip-tests @nx/angular:xxx is optional, we can directly use xxx, and then choose. generate monorepo project # npx create-nx-workspace@latest monorepo-name --preset=angular-monorepo generate app # npx nx g app apps/app-name generate component/directive/pipe/service # npx nx g c|d|p|s full-path --skip-tests generate library # npx nx g lib libs/lib-name NX Angular MFS # npx create-nx-workspace@latest ng-mf --preset=apps cd ng-mf npx nx add @nx/angular npx nx g @nx/angular:host apps/dashboard --prefix=ng-mf npx nx g @nx/angular:remote apps/login --prefix=ng-mf --host=dashboard","title":"Nx","type":"notes"},{"content":" # ","date":"15 November 2024","externalUrl":null,"permalink":"/notes/css/","section":"Notes","summary":" # ","title":"CSS","type":"notes"},{"content":"const express = require(\u0026#34;express\u0026#34;); const cors = require(\u0026#34;cors\u0026#34;); const app = express(); const PORT = 3000; // Enable CORS for cross-origin requests app.use(cors()); // Middleware to parse JSON requests app.use(express.json()); // Mock data const mockData = { page: \u0026#34;1\u0026#34;, total: \u0026#34;18\u0026#34;, records: \u0026#34;342\u0026#34;, summaries: [ { ptsTransferRqstID: \u0026#34;506327\u0026#34;, id: \u0026#34;1\u0026#34;, fromIssuer: { issuerId: \u0026#34;3663\u0026#34;, issuerName: \u0026#34;M\u0026amp;T BANK\u0026#34;, }, toIssuer: { issuerId: \u0026#34;1555\u0026#34;, issuerName: \u0026#34;GUILD MORTGAGE COMPANY\u0026#34;, }, gmpUserId: \u0026#34;I_twu23663\u0026#34;, ptsTransferRqstTyp: \u0026#34;Standard-Partial\u0026#34;, ptsTrnsfrRqstDt: \u0026#34;12/01/2024\u0026#34;, ptsSaleDt: \u0026#34;11/21/2024\u0026#34;, ptsTransferRqstStatus: \u0026#34;Validation with Errors\u0026#34;, ptsTransferRqstCode: \u0026#34;PVF\u0026#34;, ptsBuyerSellerFlag: \u0026#34;S\u0026#34;, rowCount: 342, rowNum: 1, assAgreementDocId: null, appLetterDocId: null, hecmIssrFlag: \u0026#34;N\u0026#34;, }, ], }; // Define API endpoint app.get(\u0026#34;/pts/services/v1/transfers\u0026#34;, (req, res) =\u0026gt; { // Extract query parameters (optional) const { issuerId, page = \u0026#34;1\u0026#34;, sellingIssuer, buyingIssuer, transferType, transferDate, transferStatus, transferNumber, } = req.query; console.log(\u0026#34;Query Parameters:\u0026#34;, req.query); // Return the mock data res.json(mockData); }); // Start the server app.listen(PORT, () =\u0026gt; { console.log(`Mock server is running at http://localhost:${PORT}`); }); ","date":"11 November 2024","externalUrl":null,"permalink":"/notes/temp/","section":"Notes","summary":"const express = require(\"express\"); const cors = require(\"cors\"); const app = express(); const PORT = 3000; // Enable CORS for cross-origin requests app.use(cors()); // Middleware to parse JSON requests app.use(express.json()); // Mock data const mockData = { page: \"1\", total: \"18\", records: \"342\", summaries: [ { ptsTransferRqstID: \"506327\", id: \"1\", fromIssuer: { issuerId: \"3663\", issuerName: \"M\u0026T BANK\", }, toIssuer: { issuerId: \"1555\", issuerName: \"GUILD MORTGAGE COMPANY\", }, gmpUserId: \"I_twu23663\", ptsTransferRqstTyp: \"Standard-Partial\", ptsTrnsfrRqstDt: \"12/01/2024\", ptsSaleDt: \"11/21/2024\", ptsTransferRqstStatus: \"Validation with Errors\", ptsTransferRqstCode: \"PVF\", ptsBuyerSellerFlag: \"S\", rowCount: 342, rowNum: 1, assAgreementDocId: null, appLetterDocId: null, hecmIssrFlag: \"N\", }, ], }; // Define API endpoint app.get(\"/pts/services/v1/transfers\", (req, res) =\u003e { // Extract query parameters (optional) const { issuerId, page = \"1\", sellingIssuer, buyingIssuer, transferType, transferDate, transferStatus, transferNumber, } = req.query; console.log(\"Query Parameters:\", req.query); // Return the mock data res.json(mockData); }); // Start the server app.listen(PORT, () =\u003e { console.log(`Mock server is running at http://localhost:${PORT}`); });","title":"Temp","type":"notes"},{"content":" Find and Kill process # netstat -ano | findstr :4200 taskkill /PID \u0026lt;PID\u0026gt; /F ","date":"4 November 2024","externalUrl":null,"permalink":"/notes/windows/","section":"Notes","summary":"Find and Kill process # netstat -ano | findstr :4200 taskkill /PID \u003cPID\u003e /F","title":"Windows","type":"notes"},{"content":" What # Mission # Ginnie Mae’s guaranty links the United States housing market to the global financial markets, ensuring sustainability, affordability, and liquidity. Goal # Ensuring affordable mortgage especially for low-income and first-time homebuyers. How # providing consistent access to capital for mortgage lenders, which allows them to offer lower interest rates to borrowers.\nGuarantee: The key role of Ginnie Mae is to provide a government-backed guarantee to ensure investors receive timely payments of principal and interest, even if the underlying borrowers default on their loans. This guarantee makes Ginnie Mae MBS attractive to investors, thereby increasing the capital available to mortgage lenders.\nLenders: Lenders originate the mortgages, pool them into MBS, and sell those securities to investors, such as pension funds, mutual funds, and other financial institutions.\nWhy # Supports Affordable Housing: Ginnie Mae plays a significant role in providing financing for first-time homebuyers, low-income households, and veterans. Its support for FHA, VA, and USDA loans ensures these borrowers have access to affordable home loans.\nImpact on Interest Rates: By guaranteeing MBS, Ginnie Mae reduces the risk for investors, which helps to keep mortgage interest rates lower than they otherwise would be.\nLiquidity Provider: Ginnie Mae enables mortgage lenders to sell their loans into the secondary market, freeing up capital to issue more loans. This process helps lenders manage liquidity and continue offering new mortgages, especially during times of economic stress.\nSummary # Ensuring affordable mortgage especially for low-income and first-time homebuyers.\nGinnie Mae enables mortgage lenders to sell their loans into the secondary market, freeing up capital to issue more loans. Ginnie Mae is to provide a government-backed guarantee to ensure investors receive timely payments of principal and interest, even if the underlying borrowers default on their loans.\nwhich helps to keep mortgage interest rates lower than they otherwise would be.\nQuestions # Who will I be working with most closely? What\u0026rsquo;s the next step of the hiring process? Is there any difference between pittsburgh and lake mary office? ","date":"5 September 2024","externalUrl":null,"permalink":"/notes/ginnie_mae/","section":"Notes","summary":"What # Mission # Ginnie Mae’s guaranty links the United States housing market to the global financial markets, ensuring sustainability, affordability, and liquidity. Goal # Ensuring affordable mortgage especially for low-income and first-time homebuyers. How # providing consistent access to capital for mortgage lenders, which allows them to offer lower interest rates to borrowers.\n","title":"Ginnie Mae","type":"notes"},{"content":" Youtube ","date":"4 September 2024","externalUrl":null,"permalink":"/notes/tailwind/","section":"Notes","summary":" Youtube ","title":"Tailwind","type":"notes"},{"content":" Tips\nYoutube\nPros # The component shouldn\u0026rsquo;t have to worry about how to manage the state when something happens. It doesn\u0026rsquo;t need to know what services to inject or methods to call to get the job done. Work flow # A component dispatches an action to indicate that something happened. The part of our application actually responsible for determining how actions should modify state are the reducers. Reducers detect all of the actions being dispatched in the app and determine how the state should be modified as a result. Usually we would have a reducer for each feature or entity in the app. Reducers detect the action, take the current state and store the new state in the store. So the store which is often global is where all of the state for our application lives. Store is really just one big object full of data. When a component wants to use some of that state from the store. Component can use a selector to pull in the state that it needs from the store. Effects, an important concept is that effects reduces the functions that take in an action and create a new state are pure functions. That means two thing: Given the same input, they will always produce the same output. Pure functions should also not create any side effects. So when an action is dispatched, it needs to be immediately handled by the reducer. And the state will be changed based on just the data immediately available to that action. That means for us is that when we dispatch an action, we need to give it all of the data that the reducer needs to make the state modification immediately. We cannot be making asynchronous calls to go load in data from a server for example.\nIf we want to load some data into the application and add it to the store. We might need to do this with some asynchronous operation that\u0026rsquo;s going to call a service that\u0026rsquo;s going to load in some data from some API somewhere, and that\u0026rsquo;s going to take sometime to complete. This is where effects come into play. Like a reducer, an effect can also listen to all of the actions being dispatched in the app. But unlike a reducer, that has to be a pure function intended just to update the state. An effect can run whatever side effects it likes. In the case of loading data, we would first dispatch an action like load todos, this will be immediately handled by the reducer, but we don\u0026rsquo;t have the data we need yet. Because we need to make a call to the service to load the data. So all the reducer will do in response to that load todo action, is do something like set a flag in the store, changing the status of the todo state to loading or something like that. However, our effect will also be listening for that load todo action. And when it detects this, it will go off and fetch the todo from the service. And once that data has finished loading, it will dispatch a new action, either load todo success or load todo failure. Now the reducer can handle this load todo success action that was just dispatched from our effect. And this time the action already has all of the data available to it. And so now the reducer can set that status flag to success or something similar and it can update the todo\u0026rsquo;s property in the store with the data we just loaded.\nConcepts # Store, this is where our data is stored \u0026amp; managed. Component, this is where the data is needed \u0026amp; updates should be reflected. Selector(Optional), Component can read data from the store(\u0026ldquo;listen to changes). Action, Standardized messages(\u0026ldquo;events\u0026rdquo;) to which reducers can listen. Describe the changes that should be performed + any extra data that might be needed. Reducer, Contains state changing logic. e.g. increment counter by 1. it have the actual logic that gets triggered based on those actions Effects, Side-effects that should be triggered for certain actions. e.g. send HTTP request. Install NgRx # ng add @ngrx/store\nModule # import { StoreModule } from \u0026#39;@ngrx/store\u0026#39;; // ... imports: [..., StoreModule.forRoot({}, {})]; Standalone # import { provideStore } from \u0026#39;@ngrx/store\u0026#39;; bootstrapApplication(AppComponent, { providers: [provideStore()] // added providedStore() }); syntax # createAction + createReducer combineLatest # import { Component, OnDestroy, OnInit } from \u0026#39;@angular/core\u0026#39;; import { combineLatest, Subject } from \u0026#39;rxjs\u0026#39;; import { StoreA } from \u0026#39;../store-a/store-a.service\u0026#39;; import { StoreB } from \u0026#39;../store-b/store-b.service\u0026#39;; import { takeUntil } from \u0026#39;rxjs/operators\u0026#39;; @Component({ selector: \u0026#39;app-example\u0026#39;, templateUrl: \u0026#39;./example.component.html\u0026#39;, styleUrls: [\u0026#39;./example.component.scss\u0026#39;] }) export class ExampleComponent implements OnInit, OnDestroy { private destroy$ = new Subject\u0026lt;void\u0026gt;(); dataA$ = this.storeA.data$; // Observable from StoreA dataB$ = this.storeB.data$; // Observable from StoreB combinedData: any; // Combined data from both stores constructor(private storeA: StoreA, private storeB: StoreB) {} ngOnInit(): void { // Example of combining streams for component logic combineLatest([this.dataA$, this.dataB$]) .pipe(takeUntil(this.destroy$)) .subscribe(([dataA, dataB]) =\u0026gt; { this.combinedData = { dataA, dataB }; console.log(\u0026#39;Combined Data:\u0026#39;, this.combinedData); }); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } } \u0026lt;div *ngIf=\u0026#34;dataA$ | async as dataA\u0026#34;\u0026gt; \u0026lt;p\u0026gt;Data from Store A: {{ dataA | json }}\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div *ngIf=\u0026#34;dataB$ | async as dataB\u0026#34;\u0026gt; \u0026lt;p\u0026gt;Data from Store B: {{ dataB | json }}\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; More Example # import { Component, OnInit } from \u0026#39;@angular/core\u0026#39;; import { Store } from \u0026#39;@ngrx/store\u0026#39;; import { Observable, combineLatest } from \u0026#39;rxjs\u0026#39;; import { map } from \u0026#39;rxjs/operators\u0026#39;; import { loadUsers } from \u0026#39;./store/user.actions\u0026#39;; import { selectUsers, selectUsersLoading, selectUsersError } from \u0026#39;./store/user.selectors\u0026#39;; @Component({ selector: \u0026#39;app-users\u0026#39;, template: ` \u0026lt;div *ngIf=\u0026#34;loading\u0026#34;\u0026gt;Loading...\u0026lt;/div\u0026gt; \u0026lt;div *ngIf=\u0026#34;error\u0026#34;\u0026gt;{{ error }}\u0026lt;/div\u0026gt; \u0026lt;ul *ngIf=\u0026#34;processedUsers.length \u0026gt; 0\u0026#34;\u0026gt; \u0026lt;li *ngFor=\u0026#34;let user of processedUsers\u0026#34;\u0026gt;{{ user.name }}\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;div *ngIf=\u0026#34;!loading \u0026amp;\u0026amp; processedUsers.length === 0\u0026#34;\u0026gt;No users found.\u0026lt;/div\u0026gt; `, }) export class UsersComponent implements OnInit { loading = false; error: string | null = null; processedUsers: { id: string; name: string }[] = []; constructor(private store: Store) {} ngOnInit(): void { this.store.dispatch(loadUsers()); // Combine streams for business logic combineLatest([ this.store.select(selectUsers), this.store.select(selectUsersLoading), this.store.select(selectUsersError), ]) .pipe( map(([users, loading, error]) =\u0026gt; { this.loading = loading; this.error = error; // Apply business logic here if (users) { return users.filter(user =\u0026gt; user.isActive); // Example business logic: Filter active users } return []; }) ) .subscribe((processedUsers) =\u0026gt; { this.processedUsers = processedUsers; }); } } if-else in subscribe block # If the logic is straightforward and doesn\u0026rsquo;t involve too many nested conditions, it\u0026rsquo;s acceptable. subscribe(([dataA, dataB]) =\u0026gt; { if (dataA \u0026amp;\u0026amp; dataB) { this.combinedData = { dataA, dataB }; } else { console.error(\u0026#39;Missing data\u0026#39;); } }); Move the conditional logic to operators like filter, map, or switchMap before the subscribe block. combineLatest([this.dataA$, this.dataB$]) .pipe( takeUntil(this.destroy$), filter(([dataA, dataB]) =\u0026gt; !!dataA \u0026amp;\u0026amp; !!dataB), // Ensure both exist map(([dataA, dataB]) =\u0026gt; ({ dataA, dataB })) // Transform data ) .subscribe(combinedData =\u0026gt; { this.combinedData = combinedData; console.log(\u0026#39;Combined Data:\u0026#39;, combinedData); }); Extract complex logic into a separate method. subscribe(([dataA, dataB]) =\u0026gt; { this.processData(dataA, dataB); }); processData(dataA: any, dataB: any): void { if (dataA \u0026amp;\u0026amp; dataB) { this.combinedData = { dataA, dataB }; } else { console.error(\u0026#39;Missing data\u0026#39;); } } combineLatest # combineLatest([observable1, observable2, ...]): Observable\u0026lt;[T1, T2, ...]\u0026gt; It waits until each of the source observables has emitted at least one value. Behavior Subject # import { combineLatest, BehaviorSubject } from \u0026#39;rxjs\u0026#39;; const filter$ = new BehaviorSubject\u0026lt;string\u0026gt;(\u0026#39;active\u0026#39;); // Emits \u0026#34;active\u0026#34; const sort$ = new BehaviorSubject\u0026lt;string\u0026gt;(\u0026#39;name\u0026#39;); // Emits \u0026#34;name\u0026#34; // Combine the latest values combineLatest([filter$, sort$]).subscribe(([filter, sort]) =\u0026gt; { console.log(`Filter: ${filter}, Sort: ${sort}`); }); // Output: // Filter: active, Sort: name // Updating streams filter$.next(\u0026#39;completed\u0026#39;); // Output: Filter: completed, Sort: name sort$.next(\u0026#39;date\u0026#39;); // Output: Filter: completed, Sort: date EntityState for CRUD operations # import { EntityState, EntityAdapter, createEntityAdapter } from \u0026#39;@ngrx/entity\u0026#39;; export interface Todo { id: string; title: string; completed: boolean; } export interface TodosState extends EntityState\u0026lt;Todo\u0026gt; {} export const adapter: EntityAdapter\u0026lt;Todo\u0026gt; = createEntityAdapter\u0026lt;Todo\u0026gt;(); export const initialTodosState: TodosState = adapter.getInitialState(); Partial for states that are populated after an API call # Use Partial or Lazy Initialization When Necessary For very large or complex state, you can use Partial to initialize only top-level properties and lazy-load the deeper ones as needed. This can be useful for states that are populated after an API call. export interface DeepComplexState { topLevel: string; deeplyNested: { level1: { level2: { value: string; }; }; }; } export const initialDeepComplexState: Partial\u0026lt;DeepComplexState\u0026gt; = { topLevel: \u0026#39;\u0026#39;, deeplyNested: undefined, // Initialize later when necessary }; Avoid any # Avoid any in favor of type-safe solutions like Partial, Record, or EntityState. logger for signal store # The following example shows how to create a custom feature that logs SignalStore state changes to the console. content_copy import { effect } from \u0026#39;@angular/core\u0026#39;; import { getState, signalStoreFeature, withHooks } from \u0026#39;@ngrx/signals\u0026#39;; export function withLogger(name: string) { return signalStoreFeature( withHooks({ onInit(store) { effect(() =\u0026gt; { const state = getState(store); console.log(`${name} state changed`, state); }); }, }) ); } The withLogger feature can be used in the BooksStore as follows: import { signalStore } from \u0026#39;@ngrx/signals\u0026#39;; import { withEntities } from \u0026#39;@ngrx/signals/entities\u0026#39;; import { withRequestStatus } from \u0026#39;./request-status.feature\u0026#39;; import { withLogger } from \u0026#39;./logger.feature\u0026#39;; import { Book } from \u0026#39;./book.model\u0026#39;; export const BooksStore = signalStore( withEntities\u0026lt;Book\u0026gt;(), withRequestStatus(), withLogger(\u0026#39;books\u0026#39;) ); State changes will be logged to the console whenever the BooksStore state is updated. Entity Management # The entityConfig function reduces repetitive code when defining a custom entity configuration and ensures strong typing. It accepts a config object where the entity type is required, and the collection name and custom ID selector are optional. import { patchState, signalStore, type, withMethods, } from \u0026#39;@ngrx/signals\u0026#39;; import { addEntity, entityConfig, removeEntity, withEntities, } from \u0026#39;@ngrx/signals/entities\u0026#39;; type Todo = { key: number; text: string; completed: boolean; }; const todoConfig = entityConfig({ entity: type\u0026lt;Todo\u0026gt;(), collection: \u0026#39;todo\u0026#39;, selectId: (todo) =\u0026gt; todo.key, }); export const TodosStore = signalStore( withEntities(todoConfig), withMethods((store) =\u0026gt; ({ addTodo(todo: Todo): void { patchState(store, addEntity(todo, todoConfig)); }, removeTodo(todo: Todo): void { patchState(store, removeEntity(todo, todoConfig)); }, })) ); Manual Cleanup signalMethod created in an ancestor injection context # @Injectable({ providedIn: \u0026#39;root\u0026#39; }) export class NumbersService { readonly logDoubledNumber = signalMethod\u0026lt;number\u0026gt;((num) =\u0026gt; { const double = num * 2; console.log(double); }); } @Component({ /* ... */ }) export class NumbersComponent implements OnInit { readonly numbersService = inject(NumbersService); readonly injector = inject(Injector); ngOnInit(): void { const value = signal(1); // 👇 Providing the `NumbersComponent` injector // to ensure cleanup on component destroy. this.numbersService.logDoubledNumber(value, { injector: this.injector, }); // 👇 No need to provide an injector for static values. this.numbersService.logDoubledNumber(2); } } ","date":"4 September 2024","externalUrl":null,"permalink":"/notes/angular_ngrx/","section":"Notes","summary":" Tips\nYoutube\nPros # The component shouldn’t have to worry about how to manage the state when something happens. It doesn’t need to know what services to inject or methods to call to get the job done. Work flow # A component dispatches an action to indicate that something happened. The part of our application actually responsible for determining how actions should modify state are the reducers. Reducers detect all of the actions being dispatched in the app and determine how the state should be modified as a result. Usually we would have a reducer for each feature or entity in the app. Reducers detect the action, take the current state and store the new state in the store. So the store which is often global is where all of the state for our application lives. Store is really just one big object full of data. When a component wants to use some of that state from the store. Component can use a selector to pull in the state that it needs from the store. Effects, an important concept is that effects reduces the functions that take in an action and create a new state are pure functions. That means two thing: Given the same input, they will always produce the same output. Pure functions should also not create any side effects. So when an action is dispatched, it needs to be immediately handled by the reducer. And the state will be changed based on just the data immediately available to that action. That means for us is that when we dispatch an action, we need to give it all of the data that the reducer needs to make the state modification immediately. We cannot be making asynchronous calls to go load in data from a server for example.\n","title":"Angular NgRx","type":"notes"},{"content":" Common Usage # npm install -g aws-cdk cdk --version cdk init app --language typescript npm install @aws-cdk/aws-dynamodb cdk synth cdk deploy ","date":"17 August 2024","externalUrl":null,"permalink":"/notes/aws_cdk/","section":"Notes","summary":"Common Usage # npm install -g aws-cdk cdk --version cdk init app --language typescript npm install @aws-cdk/aws-dynamodb cdk synth cdk deploy","title":"AWS CDK","type":"notes"},{"content":" Common Use Case # sam build sam deploy --guided sam local start-api ","date":"16 August 2024","externalUrl":null,"permalink":"/notes/aws_sam/","section":"Notes","summary":"Common Use Case # sam build sam deploy --guided sam local start-api","title":"Aws Sam","type":"notes"},{"content":" Resume bullet points # how to convert my the following description into one sentence that I can use in resume: \u0026#34;\u0026gt; Managed Kubernetes Cluster, meaning AWS will manage the **Master Nodes** for us. It will create the master nodes. Install all the necessary applications on them, like **Container Runtime**, Kubernetes **Master Processes**. It will take care of scaling it when needed, doing backup on that, etc. If you have small team of people, usually it\u0026#39;s a good idea to let the platform do this maintenance for you. So we can focus on deploying your applications in K8s without worrying about whether the master nodes are properly backed up etc. This means we only have to care about the worker nodes. \u0026gt; **we will have a control plane once AWS creates all master nodes**(by choosing cluster name, k8s version, choose region and VPC, set Security Group for the cluster). \u0026gt; **Then, we have to create worker nodes and connect to the cluster**(by creating Node Group, and choose the cluster it attaches to, define Security Group, select instance type of EC2 instances, basically which resources your EC2 instances should have, etc.). On AWS these worker nodes will be some EC2 instances with certain CPU RAM and Storage Resources. With Node Group, we should have auto-scaling. So based on the cluster needs, depend on how much load the cluster has, the new worker nodes will automatically be added or removed in the cluster. So for that we should define max and min number of nodes, and we have some other configurations as well. \u0026gt; **Finally connect to the cluster from local machine,** to deploy our applications from laptop using kubectl, which is k8s command line tool. It basically uses configured kubectl to talk to remote cluster.\u0026#34; please give me 2 descriptions that I can use in resume for ELK experience. ","date":"15 August 2024","externalUrl":null,"permalink":"/notes/prompts/","section":"Notes","summary":"Resume bullet points # how to convert my the following description into one sentence that I can use in resume: \"\u003e Managed Kubernetes Cluster, meaning AWS will manage the **Master Nodes** for us. It will create the master nodes. Install all the necessary applications on them, like **Container Runtime**, Kubernetes **Master Processes**. It will take care of scaling it when needed, doing backup on that, etc. If you have small team of people, usually it's a good idea to let the platform do this maintenance for you. So we can focus on deploying your applications in K8s without worrying about whether the master nodes are properly backed up etc. This means we only have to care about the worker nodes. \u003e **we will have a control plane once AWS creates all master nodes**(by choosing cluster name, k8s version, choose region and VPC, set Security Group for the cluster). \u003e **Then, we have to create worker nodes and connect to the cluster**(by creating Node Group, and choose the cluster it attaches to, define Security Group, select instance type of EC2 instances, basically which resources your EC2 instances should have, etc.). On AWS these worker nodes will be some EC2 instances with certain CPU RAM and Storage Resources. With Node Group, we should have auto-scaling. So based on the cluster needs, depend on how much load the cluster has, the new worker nodes will automatically be added or removed in the cluster. So for that we should define max and min number of nodes, and we have some other configurations as well. \u003e **Finally connect to the cluster from local machine,** to deploy our applications from laptop using kubectl, which is k8s command line tool. It basically uses configured kubectl to talk to remote cluster.\" please give me 2 descriptions that I can use in resume for ELK experience. ","title":"Prompts","type":"notes"},{"content":" no trailing forward slash hierarchical relationships use hyphens no actions use plurals avoid complexity ","date":"15 August 2024","externalUrl":null,"permalink":"/notes/api_design/","section":"Notes","summary":" no trailing forward slash hierarchical relationships use hyphens no actions use plurals avoid complexity ","title":"Api Design","type":"notes"},{"content":" Input Validation and Sanitization Monitoring and Logging Data Encryption Regular Database Audits Penetration Testing Authentication and Authorization JWT # Secure Client Storage To avoid XSS, CSRF Token Expiration Validate ","date":"15 August 2024","externalUrl":null,"permalink":"/notes/api_security/","section":"Notes","summary":" Input Validation and Sanitization Monitoring and Logging Data Encryption Regular Database Audits Penetration Testing Authentication and Authorization JWT # Secure Client Storage To avoid XSS, CSRF Token Expiration Validate ","title":"Api Security","type":"notes"},{"content":" Engineer Manager My team manages the core infrastructure for CarRentals.com for the transaction (booking, payment, finance) and pricing areas. We manage 12 microservices (java, php) deployed in AWS, using AWS technologies like SQS, dynamoDB, elasticCache, SNS, ELB, EC2, serverless framework. We are tackling big challenges even if we are a small team. But the philosophy of the team : quality first scalability knowing perfectly the business to deliver better features always search for the best technology always even if it unknown for you and we will have to learn, together innovate, modernize don\u0026rsquo;t be afraid if we have to work on huge changes or if you want to propose huge changes the team is here to support you hiring the right persons : skills but not only, team fitting, communication, humbleness, humour this philosophy allows us to succeed, to deliver in time and with a high quality. Maybe not always the best quality we wanted because of deadlines and resources we have but the quality to maintain our business and reduce our support.\nLead Engineeer I am responsible to design and implement solutions and microservices for the booking stack for carrentals.com : Booking creation Payment Accounting This work took place in the CarRentals One Stack project which was about create from scratch the new CarRentals search, book, cancel stack. This new stack is built with 15 microservices using different technologies : java, node.js, php. Fully hosted in aws cloud, this stack uses a lot of aws services : SQS, DynamoDb, Elasticache, eb deployer \u0026hellip;\nDeveloper http://www.t0t1.com/ Notre coeur de métier : les sites et les applications web à haute valeur ajoutée et à challenge technique. Nos méthodes de travail et le développement de notre framework PHP maison ( https://github.com/LaurentGrimaud/MySweetFrameWork) nous permettent de mettre en place rapidement et efficacement pour nos clients leurs MVP ou POC Nos dernières réalisations : www.subleem.com, la plateforme d\u0026rsquo;échanges de routines beauté mais pas que \u0026hellip; Propulsé par mysfw Mise en place du blog du camping pour www.toocamp.com le comparateur de camping Mise en place du système de paiement (modules Ogone, Paybox, Syspay) pour une plate-forme d\u0026rsquo;abonnements (Symfony2) Plateforme d\u0026rsquo;affiliation pour cette même plateforme d\u0026rsquo;abonnements (propulsée par mysfw)\n","date":"8 August 2024","externalUrl":null,"permalink":"/notes/linkedin_del/","section":"Notes","summary":" Engineer Manager My team manages the core infrastructure for CarRentals.com for the transaction (booking, payment, finance) and pricing areas. We manage 12 microservices (java, php) deployed in AWS, using AWS technologies like SQS, dynamoDB, elasticCache, SNS, ELB, EC2, serverless framework. We are tackling big challenges even if we are a small team. But the philosophy of the team : quality first scalability knowing perfectly the business to deliver better features always search for the best technology always even if it unknown for you and we will have to learn, together innovate, modernize don’t be afraid if we have to work on huge changes or if you want to propose huge changes the team is here to support you hiring the right persons : skills but not only, team fitting, communication, humbleness, humour this philosophy allows us to succeed, to deliver in time and with a high quality. Maybe not always the best quality we wanted because of deadlines and resources we have but the quality to maintain our business and reduce our support.\n","title":"Linkedin Del","type":"notes"},{"content":" My advantages # I have a proven track record of problem-solving.\nI can analyze data to develop real-life engineering solution.\nI possess excellent communication skills.\nMy attitude to work.\nMy attention to details skills.\nMy commitment to the organization.\nMy advantage, I am passionate about engineering and I take responsibility for my own continuous learning and development.\nGreetings # Good evening! Thank you for taking the time to meet with me today. I appreciate the opportunity to speak with you and discuss how I can contribute to your team. I\u0026rsquo;m excited for our conversation. General Questions # Why are you interested in this role? # The project is focusing on Java Backend, and Microservice Architecture, and AWS That aligns with my experience and interests. What do you know about our company? # I know that Expedia is a leader in [online travel industry], providing [hotel bookings, flight reservations, car rentals, and so on]. Why are you leaving your current job? # I have enjoyed my time at my current job and learned a lot, but I am looking for new challenges and opportunities to grow. And additionally, my current project is about to end. So I need to switch to a new project. Where do you see yourself in five years? # In five years, I see myself as a senior backend developer, having contributed to several successful projects and having taken on more responsibilities. I am committed to continuous learning and hope to keep expanding my skills in new technologies and methodologies. Role-Specific Questions # What interests you about this particular role? # This role excites me because it involves working on scalable and high-performance systems using technologies I am passionate about, such as Spring Boot and microservices architecture. I am eager to apply my expertise in developing robust backend solutions to help the team achieve its goals. How do your skills and experiences align with the requirements of this job? # My experience with Java, Spring Boot, and microservices architecture aligns well with the key requirements of this role. I have a proven track record of designing and implementing scalable backend systems, and I am confident that my problem-solving skills and ability to work in a collaborative environment will be valuable assets to your team. Can you describe a typical day in your current/previous role? # A typical day involves reviewing and writing code, participating in stand-up meetings, collaborating with front-end developers and other team members, and troubleshooting any issues that arise. I also spend time optimizing code for performance, conducting code reviews, and ensuring the systems are running smoothly. What key skills do you think are necessary for this position? # Key skills include a deep understanding of core Java, experience with Spring Boot and microservices, strong problem-solving abilities, and familiarity with database technologies like SQL and NoSQL. Additionally, effective communication and teamwork are crucial for collaborating with other developers and stakeholders.\u0026quot; Behavioral Questions # Can you describe a challenging situation you faced at work and how you dealt with it? # In a previous project, we faced a challenge with a critical service that was experiencing frequent downtime. I took the initiative to investigate the issue, identified that it was due to a memory leak, and implemented a fix by optimizing the resource management in the code. I also set up monitoring and alerting to catch similar issues in the future. This proactive approach helped stabilize the service and improved its reliability. Tell me about a time you had to work closely with a difficult colleague. How did you handle it? # I once worked with a colleague who had a different approach to problem-solving, which led to some friction. I decided to have a candid conversation to understand their perspective and find common ground. We agreed on a collaborative approach where we would openly discuss our ideas and find the best solution together. This improved our working relationship and resulted in more effective teamwork. Describe a situation where you had to meet a tight deadline. How did you manage your time? # In a recent project, we had a tight deadline to deliver a new feature requested by a key client. I prioritized my tasks, broke down the work into manageable chunks, and set clear milestones. I also collaborated closely with my team to ensure we were all aligned and on track. By staying focused and maintaining open communication, we were able to meet the deadline successfully. Can you give an example of a project where you took the lead? # I led a project to migrate our legacy system to a microservices architecture. I was responsible for planning the migration, designing the new system, and coordinating the work across different teams. I conducted regular meetings to ensure everyone was on the same page and addressed any issues promptly. The project was completed on time and resulted in improved system performance and maintainability. How do you prioritize tasks when you have multiple deadlines? # I prioritize tasks based on their urgency and impact on the project. I use tools like Jira to keep track of tasks and deadlines. I also communicate with stakeholders to understand their priorities and ensure that the most critical tasks are addressed first. By staying organized and focusing on high-priority items, I manage to meet multiple deadlines effectively. Tell me about a time you made a mistake at work. How did you handle it? # I once deployed a code change that caused a critical service to fail. I immediately took responsibility, rolled back the change, and informed my team and stakeholders. I then investigated the root cause, fixed the issue, and implemented additional testing to prevent similar mistakes in the future. This experience taught me the importance of thorough testing and effective communication during incidents. How do you handle feedback and criticism? # I view feedback and criticism as opportunities for growth. I listen carefully, ask clarifying questions if needed, and reflect on how I can improve. I also seek feedback proactively to understand how I can better meet expectations. By maintaining a positive attitude and being open to learning, I continuously improve my skills and performance. Team and Culture Fit Questions # How do you handle working in a team environment? # I thrive in a team environment where collaboration and open communication are valued. I believe in sharing knowledge, supporting my teammates, and contributing to a positive work culture. I enjoy working with diverse teams and leveraging each team member\u0026rsquo;s strengths to achieve our common goals. Describe your ideal work environment. # My ideal work environment is one that promotes collaboration, innovation, and continuous learning. I appreciate a culture where feedback is encouraged, and team members are empowered to take initiative. I also value a healthy work-life balance and a supportive atmosphere where everyone feels respected and valued. How do you handle conflicts within a team? # I believe in addressing conflicts openly and constructively. I listen to all perspectives, seek to understand the underlying issues, and work towards finding a mutually agreeable solution. By fostering open communication and focusing on the common goals, I help ensure that conflicts are resolved amicably and do not impact the team\u0026rsquo;s productivity. What do you think is most important for fostering a positive team environment? # Open communication, mutual respect, and a shared commitment to the team\u0026rsquo;s goals are crucial for fostering a positive team environment. Encouraging collaboration, celebrating successes, and supporting each other through challenges help build a strong and cohesive team. How do you approach collaboration with remote team members? # I ensure effective collaboration with remote team members by leveraging communication tools like Slack and Zoom for regular check-ins and updates. I also use project management tools like Jira to keep everyone aligned and informed. Building trust and maintaining clear communication are key to successful remote collaboration. Problem-Solving and Analytical Questions # Can you describe a complex problem you solved and how you approached it? # In a previous project, we faced a complex issue with data synchronization between two services. I analyzed the problem, identified the root cause, and designed a solution using event-driven architecture to ensure reliable data synchronization. I implemented the solution, tested it thoroughly, and monitored its performance to ensure it worked as expected. The solution improved data consistency and system reliability. How do you approach troubleshooting and debugging technical issues? # I approach troubleshooting systematically by first understanding the problem and gathering relevant information. I review logs and error messages to identify potential causes. I then use debugging tools to isolate and investigate the issue. Once identified, I implement a fix and conduct thorough testing to ensure the problem is resolved. I also document the issue and solution for future reference. What steps do you take to ensure your code is of high quality? # I follow best practices such as writing clean and maintainable code, conducting code reviews, and implementing unit tests. I also use static code analysis tools to identify potential issues early. Additionally, I adhere to design principles like SOLID and ensure my code is well-documented. Continuous learning and staying updated with the latest best practices help me maintain high code quality. Describe a situation where you had to quickly learn a new tool or technology. How did you do it? # In a recent project, I had to quickly learn Docker for containerizing our applications. I started by reading the official documentation and tutorials to understand the basics. I then set up a local development environment and experimented with creating and managing containers. I also sought advice from colleagues with Docker experience and participated in online forums. Within a few weeks, I was able to effectively use Docker for our project. Leadership and Initiative Questions # Have you ever taken the initiative to improve a process at work? # Yes, I noticed that our deployment process was manual and error-prone, leading to frequent downtime. I took the initiative to implement a CI/CD pipeline using Jenkins, which automated the build, test, and deployment processes. This significantly reduced deployment time and errors, leading to more stable and reliable releases. Can you give an example of a time you mentored or coached a colleague? # I mentored a junior developer who was new to our team. I provided guidance on coding standards, best practices, and our development workflow. I also conducted regular code reviews and offered constructive feedback. Over time, the junior developer became more confident and productive, contributing effectively to our projects. How do you motivate yourself and others on your team? # I stay motivated by setting clear goals and continuously challenging myself to learn and improve. To motivate my team, I ensure open communication, recognize and celebrate achievements, and provide support during challenges. Creating a positive and collaborative environment where everyone feels valued helps keep the team motivated. Describe a situation where you had to persuade others to follow your idea. # In a project, I proposed migrating our monolithic application to a microservices architecture to improve scalability and maintainability. Some team members were hesitant due to the perceived complexity and risks. I presented a detailed plan, highlighting the benefits and addressing their concerns. I also suggested a phased approach to mitigate risks. My proposal was eventually accepted, and the migration led to significant improvements in our system\u0026rsquo;s performance and flexibility. Questions for the Hiring Manager # Can you describe the team I would be working with? # What are the biggest challenges the team is currently facing? # How do you measure success for this role? # What does a typical career path look like for someone in this position? # Can you tell me about the company culture? # What are the next steps in the interview process? # ","date":"5 August 2024","externalUrl":null,"permalink":"/notes/hiring_manager_interview/","section":"Notes","summary":"My advantages # I have a proven track record of problem-solving.\nI can analyze data to develop real-life engineering solution.\nI possess excellent communication skills.\nMy attitude to work.\nMy attention to details skills.\nMy commitment to the organization.\nMy advantage, I am passionate about engineering and I take responsibility for my own continuous learning and development.\nGreetings # Good evening! Thank you for taking the time to meet with me today. I appreciate the opportunity to speak with you and discuss how I can contribute to your team. I’m excited for our conversation. General Questions # Why are you interested in this role? # The project is focusing on Java Backend, and Microservice Architecture, and AWS That aligns with my experience and interests. What do you know about our company? # I know that Expedia is a leader in [online travel industry], providing [hotel bookings, flight reservations, car rentals, and so on]. Why are you leaving your current job? # I have enjoyed my time at my current job and learned a lot, but I am looking for new challenges and opportunities to grow. And additionally, my current project is about to end. So I need to switch to a new project. Where do you see yourself in five years? # In five years, I see myself as a senior backend developer, having contributed to several successful projects and having taken on more responsibilities. I am committed to continuous learning and hope to keep expanding my skills in new technologies and methodologies. Role-Specific Questions # What interests you about this particular role? # This role excites me because it involves working on scalable and high-performance systems using technologies I am passionate about, such as Spring Boot and microservices architecture. I am eager to apply my expertise in developing robust backend solutions to help the team achieve its goals. How do your skills and experiences align with the requirements of this job? # My experience with Java, Spring Boot, and microservices architecture aligns well with the key requirements of this role. I have a proven track record of designing and implementing scalable backend systems, and I am confident that my problem-solving skills and ability to work in a collaborative environment will be valuable assets to your team. Can you describe a typical day in your current/previous role? # A typical day involves reviewing and writing code, participating in stand-up meetings, collaborating with front-end developers and other team members, and troubleshooting any issues that arise. I also spend time optimizing code for performance, conducting code reviews, and ensuring the systems are running smoothly. What key skills do you think are necessary for this position? # Key skills include a deep understanding of core Java, experience with Spring Boot and microservices, strong problem-solving abilities, and familiarity with database technologies like SQL and NoSQL. Additionally, effective communication and teamwork are crucial for collaborating with other developers and stakeholders.\" Behavioral Questions # Can you describe a challenging situation you faced at work and how you dealt with it? # In a previous project, we faced a challenge with a critical service that was experiencing frequent downtime. I took the initiative to investigate the issue, identified that it was due to a memory leak, and implemented a fix by optimizing the resource management in the code. I also set up monitoring and alerting to catch similar issues in the future. This proactive approach helped stabilize the service and improved its reliability. Tell me about a time you had to work closely with a difficult colleague. How did you handle it? # I once worked with a colleague who had a different approach to problem-solving, which led to some friction. I decided to have a candid conversation to understand their perspective and find common ground. We agreed on a collaborative approach where we would openly discuss our ideas and find the best solution together. This improved our working relationship and resulted in more effective teamwork. Describe a situation where you had to meet a tight deadline. How did you manage your time? # In a recent project, we had a tight deadline to deliver a new feature requested by a key client. I prioritized my tasks, broke down the work into manageable chunks, and set clear milestones. I also collaborated closely with my team to ensure we were all aligned and on track. By staying focused and maintaining open communication, we were able to meet the deadline successfully. Can you give an example of a project where you took the lead? # I led a project to migrate our legacy system to a microservices architecture. I was responsible for planning the migration, designing the new system, and coordinating the work across different teams. I conducted regular meetings to ensure everyone was on the same page and addressed any issues promptly. The project was completed on time and resulted in improved system performance and maintainability. How do you prioritize tasks when you have multiple deadlines? # I prioritize tasks based on their urgency and impact on the project. I use tools like Jira to keep track of tasks and deadlines. I also communicate with stakeholders to understand their priorities and ensure that the most critical tasks are addressed first. By staying organized and focusing on high-priority items, I manage to meet multiple deadlines effectively. Tell me about a time you made a mistake at work. How did you handle it? # I once deployed a code change that caused a critical service to fail. I immediately took responsibility, rolled back the change, and informed my team and stakeholders. I then investigated the root cause, fixed the issue, and implemented additional testing to prevent similar mistakes in the future. This experience taught me the importance of thorough testing and effective communication during incidents. How do you handle feedback and criticism? # I view feedback and criticism as opportunities for growth. I listen carefully, ask clarifying questions if needed, and reflect on how I can improve. I also seek feedback proactively to understand how I can better meet expectations. By maintaining a positive attitude and being open to learning, I continuously improve my skills and performance. Team and Culture Fit Questions # How do you handle working in a team environment? # I thrive in a team environment where collaboration and open communication are valued. I believe in sharing knowledge, supporting my teammates, and contributing to a positive work culture. I enjoy working with diverse teams and leveraging each team member’s strengths to achieve our common goals. Describe your ideal work environment. # My ideal work environment is one that promotes collaboration, innovation, and continuous learning. I appreciate a culture where feedback is encouraged, and team members are empowered to take initiative. I also value a healthy work-life balance and a supportive atmosphere where everyone feels respected and valued. How do you handle conflicts within a team? # I believe in addressing conflicts openly and constructively. I listen to all perspectives, seek to understand the underlying issues, and work towards finding a mutually agreeable solution. By fostering open communication and focusing on the common goals, I help ensure that conflicts are resolved amicably and do not impact the team’s productivity. What do you think is most important for fostering a positive team environment? # Open communication, mutual respect, and a shared commitment to the team’s goals are crucial for fostering a positive team environment. Encouraging collaboration, celebrating successes, and supporting each other through challenges help build a strong and cohesive team. How do you approach collaboration with remote team members? # I ensure effective collaboration with remote team members by leveraging communication tools like Slack and Zoom for regular check-ins and updates. I also use project management tools like Jira to keep everyone aligned and informed. Building trust and maintaining clear communication are key to successful remote collaboration. Problem-Solving and Analytical Questions # Can you describe a complex problem you solved and how you approached it? # In a previous project, we faced a complex issue with data synchronization between two services. I analyzed the problem, identified the root cause, and designed a solution using event-driven architecture to ensure reliable data synchronization. I implemented the solution, tested it thoroughly, and monitored its performance to ensure it worked as expected. The solution improved data consistency and system reliability. How do you approach troubleshooting and debugging technical issues? # I approach troubleshooting systematically by first understanding the problem and gathering relevant information. I review logs and error messages to identify potential causes. I then use debugging tools to isolate and investigate the issue. Once identified, I implement a fix and conduct thorough testing to ensure the problem is resolved. I also document the issue and solution for future reference. What steps do you take to ensure your code is of high quality? # I follow best practices such as writing clean and maintainable code, conducting code reviews, and implementing unit tests. I also use static code analysis tools to identify potential issues early. Additionally, I adhere to design principles like SOLID and ensure my code is well-documented. Continuous learning and staying updated with the latest best practices help me maintain high code quality. Describe a situation where you had to quickly learn a new tool or technology. How did you do it? # In a recent project, I had to quickly learn Docker for containerizing our applications. I started by reading the official documentation and tutorials to understand the basics. I then set up a local development environment and experimented with creating and managing containers. I also sought advice from colleagues with Docker experience and participated in online forums. Within a few weeks, I was able to effectively use Docker for our project. Leadership and Initiative Questions # Have you ever taken the initiative to improve a process at work? # Yes, I noticed that our deployment process was manual and error-prone, leading to frequent downtime. I took the initiative to implement a CI/CD pipeline using Jenkins, which automated the build, test, and deployment processes. This significantly reduced deployment time and errors, leading to more stable and reliable releases. Can you give an example of a time you mentored or coached a colleague? # I mentored a junior developer who was new to our team. I provided guidance on coding standards, best practices, and our development workflow. I also conducted regular code reviews and offered constructive feedback. Over time, the junior developer became more confident and productive, contributing effectively to our projects. How do you motivate yourself and others on your team? # I stay motivated by setting clear goals and continuously challenging myself to learn and improve. To motivate my team, I ensure open communication, recognize and celebrate achievements, and provide support during challenges. Creating a positive and collaborative environment where everyone feels valued helps keep the team motivated. Describe a situation where you had to persuade others to follow your idea. # In a project, I proposed migrating our monolithic application to a microservices architecture to improve scalability and maintainability. Some team members were hesitant due to the perceived complexity and risks. I presented a detailed plan, highlighting the benefits and addressing their concerns. I also suggested a phased approach to mitigate risks. My proposal was eventually accepted, and the migration led to significant improvements in our system’s performance and flexibility. Questions for the Hiring Manager # Can you describe the team I would be working with? # What are the biggest challenges the team is currently facing? # How do you measure success for this role? # What does a typical career path look like for someone in this position? # Can you tell me about the company culture? # What are the next steps in the interview process? # ","title":"Hiring Manager Interview","type":"notes"},{"content":" I # worked\nfocus\nbuild\ndesign\nimplement\noptimize\nutilize\ndeveloped\nhandle\nmanage\nuse\nensure\nhave (deep understanding of XXX concept)\nhave (experience with XXX tools and frameworks)\nam proficient in (using)\nXXX Experience # equipped (me with skills to deliver XXX solutions) Tools # provide allows helps Approach # starts with understanding set also use ","date":"2 August 2024","externalUrl":null,"permalink":"/notes/keyword_structure/","section":"Notes","summary":"I # worked\nfocus\nbuild\ndesign\nimplement\noptimize\nutilize\ndeveloped\nhandle\nmanage\nuse\nensure\nhave (deep understanding of XXX concept)\nhave (experience with XXX tools and frameworks)\nam proficient in (using)\nXXX Experience # equipped (me with skills to deliver XXX solutions) Tools # provide allows helps Approach # starts with understanding set also use ","title":"Keyword Structure","type":"notes"},{"content":" Java # Yes, I have extensive experience with Java, having worked as a Java developer for over six years. During this time, I have primarily focused on backend development and building robust microservice architectures.\nIn my current role, I have been involved in various projects where I designed and implemented scalable and efficient solutions using core Java concepts and popular frameworks. For instance, in a recent project, I developed a data analytics dashboard where I utilized Java in conjunction with Spring Boot to create RESTful APIs, handle business logic, and manage database interactions using Hibernate and JPA.\nI have a deep understanding of object-oriented programming principles, including inheritance, polymorphism, encapsulation, and abstraction. I am proficient in using Java collections, multithreading, concurrency utilities, and Java Streams API for processing large datasets efficiently.\nAdditionally, I have experience with various Java frameworks and tools such as:\nSpring Framework: Including Spring Boot, Spring MVC, Spring Security, and Spring Data JPA. Hibernate: For ORM and database interactions. JUnit and Mockito: For unit testing and test-driven development. Maven and Gradle: For build automation and dependency management. I am also well-versed in modern development practices such as continuous integration/continuous deployment (CI/CD) using tools like Jenkins, Docker, and Kubernetes.\nOverall, my experience with Java has equipped me with the skills to deliver high-quality, maintainable, and scalable software solutions, and I am always eager to leverage this expertise in new and challenging projects.\nDebugging # Yes, I have extensive experience with debugging programs. Debugging is a crucial part of my development process, and I\u0026rsquo;ve used various tools and techniques to identify and fix issues efficiently.\nTools: I frequently use IDEs like IntelliJ IDEA, which provide powerful debugging tools. These tools allow me to set breakpoints, step through code, inspect variables, and evaluate expressions. I also use logging frameworks such as Log4j and SLF4J to log important information, which helps in understanding the flow of the application and identifying issues.\nTechniques: My approach to debugging usually starts with replicating the issue and understanding the context in which it occurs. I set breakpoints at critical points in the code to inspect the state of the application. I also use watch expressions to monitor specific variables and conditions.\nExamples: In one of my recent projects, we encountered a performance issue where a particular API endpoint was responding slowly. I used profiling tools like VisualVM and JProfiler to analyze the application\u0026rsquo;s performance. By examining the CPU and memory usage, I was able to pinpoint a specific method that was causing a bottleneck. Further inspection revealed an inefficient database query, which I optimized to improve the response time significantly.\nCollaboration: I also believe in collaborating with my team when debugging complex issues. Sometimes, a fresh pair of eyes or a different perspective can help identify the root cause of a problem more quickly.\nOverall, my debugging experience has equipped me with the skills to identify, analyze, and resolve issues efficiently, ensuring that the applications I work on are reliable and perform well.\nSpringBoot # Yes, I have extensive experience with Spring Boot. In my current role, I have used Spring Boot to develop several microservices as part of a larger microservice architecture. One notable project was a data analytics dashboard, where I was responsible for designing and implementing backend services using Spring Boot.\nI used Spring Boot\u0026rsquo;s various modules like Spring Data JPA for database interactions, Spring Security for securing APIs, and Spring Cloud for service discovery and configuration management. I also leveraged Spring Boot\u0026rsquo;s support for RESTful APIs to create endpoints for data retrieval and manipulation.\nOne of the key benefits I found with Spring Boot is its auto-configuration feature, which significantly speeds up development by reducing boilerplate code. I also appreciated the ease of integrating other Spring projects and third-party libraries, which helped in building robust and scalable applications.\nOverall, Spring Boot is a valuable tool in my development toolkit.\nDatabase # Yes, I have extensive experience with databases. I have worked with both relational databases like IBM DB2 and Microsoft SQL Server, as well as NoSQL databases like Cassandra.\nFor relational databases, I have designed and optimized complex queries, created stored procedures, and managed database schema changes. In one of my projects, I was responsible for migrating a legacy system to IBM DB2, which involved data modeling, writing SQL scripts, and ensuring data integrity and performance optimization.\nWith NoSQL databases, specifically Cassandra, I have worked on a high-traffic web application where we needed a scalable and distributed database solution. I was involved in setting up and configuring Cassandra clusters, designing the data model to handle large volumes of data efficiently, and writing CQL (Cassandra Query Language) queries. I also ensured that the data replication and consistency settings were optimized for our application’s needs.\nIn addition, I have experience with ORMs like Hibernate and Spring Data JPA, which abstract the database interactions and help in writing cleaner and more maintainable code.\nOverall, my experience with both relational and NoSQL databases has equipped me with a strong understanding of how to design, implement, and optimize database solutions to support various types of applications.\nUnit Test and Integration Test # Yes, I have extensive experience with both unit and integration testing.\nFor unit testing, I primarily use JUnit and Mockito. In my projects, I write unit tests to ensure that individual components or methods work as expected. I use Mockito to create mock objects and isolate the component being tested from its dependencies. For instance, in one of my Spring Boot applications, I wrote comprehensive unit tests for the service layer to verify business logic independently from the data access layer.\nFor integration testing, I use Spring Boot’s testing framework, which includes tools like Spring TestContext Framework. Integration tests verify that different parts of the application work together correctly. I often write tests that start the entire Spring context and use actual components. For example, I have written integration tests to test RESTful endpoints using MockMvc:\nIn my projects, I ensure that unit tests cover the business logic thoroughly, while integration tests validate the interaction between different layers and components of the application. This approach helps in identifying issues early in the development cycle and ensures the reliability of the application.\nOverall, my experience with unit and integration testing has helped me build robust and maintainable software by catching issues early and ensuring the correctness of the application components and their interactions.\nAngular # Yes, I have considerable experience with Angular. In my previous role, I was responsible for developing the front-end of a complex web application using Angular.\nOne significant project I worked on was an e-commerce platform where I built several key components such as the product catalog, shopping cart, and user authentication system. I used Angular\u0026rsquo;s powerful data binding and dependency injection features to create dynamic, responsive user interfaces.\nI also utilized Angular\u0026rsquo;s routing module to manage navigation within the application, and Angular Services to handle business logic and communicate with RESTful APIs for data retrieval and updates. Additionally, I implemented state management using NgRx to maintain a consistent state across the application.\nDuring the project, I adhered to best practices for component-based architecture, which helped in maintaining clean, modular, and reusable code. I also performed unit testing using Jasmine and Karma to ensure the reliability of the components.\nMy experience with Angular has enabled me to build high-performance, scalable applications that provide a seamless user experience.\n","date":"2 August 2024","externalUrl":null,"permalink":"/notes/do_you_have_experience/","section":"Notes","summary":"Java # Yes, I have extensive experience with Java, having worked as a Java developer for over six years. During this time, I have primarily focused on backend development and building robust microservice architectures.\nIn my current role, I have been involved in various projects where I designed and implemented scalable and efficient solutions using core Java concepts and popular frameworks. For instance, in a recent project, I developed a data analytics dashboard where I utilized Java in conjunction with Spring Boot to create RESTful APIs, handle business logic, and manage database interactions using Hibernate and JPA.\n","title":"Do You Have Experience","type":"notes"},{"content":"3-5 years of strong programming skills in Java with proficiency in object-oriented design principles Experience with Java frameworks such as DropWizard, Spring, and Hibernate Familiarity with web development frameworks (Angular or React) Familiarity with distributed storage systems like DB2, Oracle, Cassandra, MongoDB Familiarity with continuous integration and continuous deployment (CI/CD) pipelines, especially using Git Working knowledge of Unix/Linux environments\nOOD principles # Encapsulation Example: \u0026ldquo;Encapsulation involves bundling the data (attributes) and methods (functions) that operate on the data into a single unit, or class, and restricting access to some of the object\u0026rsquo;s components. In my projects, I\u0026rsquo;ve used encapsulation to ensure that an object\u0026rsquo;s internal state cannot be altered directly from outside the class, which helps maintain data integrity.\u0026rdquo;\nAbstraction Example: \u0026ldquo;Abstraction is about hiding the complex implementation details and showing only the necessary features of an object. I utilize abstraction to create simple interfaces for complex systems. For instance, in a payment processing system, I defined abstract classes and interfaces for different payment methods, allowing the system to handle various payment types in a uniform manner.\u0026rdquo;\nInheritance Example: \u0026ldquo;Inheritance allows a class to inherit properties and behavior from another class. This helps in reusing code and establishing a natural hierarchy between classes. I used inheritance in a project where different types of employees inherited from a base Employee class, allowing common properties like name and ID to be shared while extending specific behaviors for full-time and part-time employees.\u0026rdquo;\nPolymorphism Example: \u0026ldquo;Polymorphism enables objects to be treated as instances of their parent class rather than their actual class. This is particularly useful for implementing dynamic method dispatch. I\u0026rsquo;ve implemented polymorphism in scenarios like defining a common interface for different shape classes (Circle, Square, Triangle) and using it to perform operations like area calculation without knowing the specific type of shape at runtime.\u0026rdquo;\nThe first answer is focused on explaining object-oriented design principles (OOP) such as encapsulation, abstraction, inheritance, and polymorphism.\n(encapsulation, abstraction, inheritance, and polymorphism) The second answer focuses specifically on SOLID principles, which are a subset of object-oriented design principles aimed at improving software design.\n(Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion) DropWizard # Yes, I have experience with DropWizard. In my current project, we use both DropWizard and SpringBoot as backend framework. And I use DropWizard only for performance purpose. For instance some our services requires fast response and they doesn\u0026rsquo;t have any complex business logic, like those realtime update services in our dashboard system. For those microservices we use DropWizard, as it designed for simplicity and performance. And the only difference between DropWizard and SpringBoot, is that it DropWizard doesn\u0026rsquo;t support dependency injection by default. That\u0026rsquo;s also the main reason it has better performance for the simple services.\nThat\u0026rsquo;s how we use DropWizard and SpringBoot in our current project.\nYeah, that\u0026rsquo;s my experience with DropWizard.\nIt uses Jetty, a high-performance HTTP server, and integrates with libraries like Jackson and Jersey for efficient JSON processing and RESTful services.\nMy current project uses DropWizard for the microservices that require fast response like the those realtime update widget in the system.\nAngular # Yes, I have extensive experience with Angular. Over the past three years, I have worked on several projects using from Angular 8 and Angular 15. In my current role at Shopify, I developed a complex data analytics dashboard using Angular, integrating it with RESTful APIs built with Spring Boot and DropWizard.\nI have a solid understanding of core Angular concepts such as components, directives, services, dependency injection, routing, and reactive forms.\nFor example, in one of my projects, I implemented lazy loading to optimize the performance of a large application, which resulted in a significant reduction in load time.\nAdditionally, I am proficient with Angular CLI for project setup and development, and I have used RxJS extensively for handling asynchronous data streams. One particular challenge I faced was managing state in a large application, which I successfully addressed using NgRx for state management.\nCassandra # Yes, I have experience with Cassandra. I have been working with Cassandra for the past 2 years. In my previous project at Lewis, I was responsible for setting up and managing Cassandra clusters. This involved configuring nodes, setting up replication, and ensuring high availability. I worked on optimizing read and write performance, managing data modeling, and implementing Cassandra query language (CQL) for database operations. Additionally, I handled backup and restore processes and monitored cluster health using tools like nodetool and OpsCenter.\nMongoDB # Yes, I have experience with MongoDB. In my current role at Shopify, I worked extensively with MongoDB to develop a high-performance backend for a data analytics dashboard. I was responsible for designing the database schema, optimizing queries, and implementing data aggregation pipelines. One notable project involved migrating data from a relational database to MongoDB, which resulted in a 30% improvement in query performance. Additionally, I used MongoDB’s indexing and sharding capabilities to ensure scalability and efficiency as the dataset grew.\nGit # Yes, I have extensive experience with Git. I have used Git for version control in all my projects over the past 10 years. I am comfortable with all the basic and advanced features of Git. Besides those basic operations like git clone, git rebase, git checkout. I also have integrated Git with CI/CD tools like Jenkins and GitLab CI for automated builds and deployments. I have also used GitHub and GitLab for repository hosting and project management.\nLinux # Yes, I have extensive experience with Linux. In my previous roles, I have used Linux for various tasks such as server management, deployment of applications, and shell scripting. For instance, I have managed web servers using Apache and Nginx, automated deployment processes with shell scripts, and monitored system performance using tools like top, htop, and vmstat. I also set up and maintained a Kubernetes cluster on Linux servers to support a microservices architecture. This involved configuring Docker, managing Kubernetes nodes, and ensuring high availability and security of the services.\nI have also worked with Linux-based systems in my current project, a data analytics dashboard. We use Linux servers for hosting our backend services, and I am responsible for maintaining these servers, handling system updates, and troubleshooting any issues that arise.\n","date":"29 July 2024","externalUrl":null,"permalink":"/notes/goldman/","section":"Notes","summary":"3-5 years of strong programming skills in Java with proficiency in object-oriented design principles Experience with Java frameworks such as DropWizard, Spring, and Hibernate Familiarity with web development frameworks (Angular or React) Familiarity with distributed storage systems like DB2, Oracle, Cassandra, MongoDB Familiarity with continuous integration and continuous deployment (CI/CD) pipelines, especially using Git Working knowledge of Unix/Linux environments\nOOD principles # Encapsulation Example: “Encapsulation involves bundling the data (attributes) and methods (functions) that operate on the data into a single unit, or class, and restricting access to some of the object’s components. In my projects, I’ve used encapsulation to ensure that an object’s internal state cannot be altered directly from outside the class, which helps maintain data integrity.”\n","title":"Goldman","type":"notes"},{"content":" DS # Data Scientist Role at Disney: Passionate Data Scientist with Strong Technical Background Hi Chrissy, Hope you\u0026#39;re doing great! I noticed that you currently work at The Walt Disney Company, and I\u0026#39;m excited to learn about the Data Scientist II role you\u0026#39;re recruiting for. With a Master of Science degree from UT Austin and a strong background in computer science, I have 6 years of experience building practical machine learning solutions using Python, statistical modeling, and data analysis. My highly relevant skills include Data Analytics, Hive, and Computer Science. Disney\u0026#39;s mission to create unforgettable content experiences resonates with me, as someone who is passionate about innovation and driven by data-driven solutions. Is this role still available? Thank you! Best regards, DS # Interested in the Senior Marketing Data Scientist, CRM \u0026amp; Governance role at Peloton Hi Jordan, Hope you\u0026#39;re having a great day! I noticed you work at Peloton and wanted to learn more about the Senior Marketing Data Scientist, CRM \u0026amp; Governance role. My background in 5 year data science and visualization aligns well with the analytical experience required for this position. Peloton\u0026#39;s connected fitness ecosystem is impressive, and I\u0026#39;m excited to use my skills to contribute towards your mission. Is this opportunity still available? Thank you, DS # Analytics Engineer (L5) - Messaging - Inquery and Interest Hi Mallory, I am Yixian, a data scientist with 5 years of experience. I was impressed by your recruting experience at Netflix. I am highly interested in the Analytics Engineer (L5) - Messaging role. I have expertise in bridging the gap between technical and business teams, ensuring actionable and interpretable data. My skills include SQL, Python, Scala, AWS, Airflow, dbt, Hadoop, Hive, Spark, and Tableu. I am also an AWS Certified Solution Architect - Associate (SAA). Could we schedule a call this week to discuss this opportunity further? Thanks, # ","date":"29 July 2024","externalUrl":null,"permalink":"/notes/reach_out_letter/","section":"Notes","summary":"DS # Data Scientist Role at Disney: Passionate Data Scientist with Strong Technical Background Hi Chrissy, Hope you're doing great! I noticed that you currently work at The Walt Disney Company, and I'm excited to learn about the Data Scientist II role you're recruiting for. With a Master of Science degree from UT Austin and a strong background in computer science, I have 6 years of experience building practical machine learning solutions using Python, statistical modeling, and data analysis. My highly relevant skills include Data Analytics, Hive, and Computer Science. Disney's mission to create unforgettable content experiences resonates with me, as someone who is passionate about innovation and driven by data-driven solutions. Is this role still available? Thank you! Best regards, DS # Interested in the Senior Marketing Data Scientist, CRM \u0026 Governance role at Peloton Hi Jordan, Hope you're having a great day! I noticed you work at Peloton and wanted to learn more about the Senior Marketing Data Scientist, CRM \u0026 Governance role. My background in 5 year data science and visualization aligns well with the analytical experience required for this position. Peloton's connected fitness ecosystem is impressive, and I'm excited to use my skills to contribute towards your mission. Is this opportunity still available? Thank you, DS # Analytics Engineer (L5) - Messaging - Inquery and Interest Hi Mallory, I am Yixian, a data scientist with 5 years of experience. I was impressed by your recruting experience at Netflix. I am highly interested in the Analytics Engineer (L5) - Messaging role. I have expertise in bridging the gap between technical and business teams, ensuring actionable and interpretable data. My skills include SQL, Python, Scala, AWS, Airflow, dbt, Hadoop, Hive, Spark, and Tableu. I am also an AWS Certified Solution Architect - Associate (SAA). Could we schedule a call this week to discuss this opportunity further? Thanks, # ","title":"Reach Out Letter","type":"notes"},{"content":" String Segmentation # class Outcome { public static String solve(String s, List\u0026lt;String\u0026gt; wordDict) { Set\u0026lt;String\u0026gt; hs = new HashSet\u0026lt;\u0026gt;(wordDict); boolean[] f = new boolean[s.length() + 1]; f[0] = true; for (int i = 1; i \u0026lt;= s.length(); i++) { for (int j = 0; j \u0026lt; i; j++) { if (f[j] \u0026amp;\u0026amp; hs.contains(s.substring(j, i))) { f[i] = true; break; } } } return f[s.length()] ? \u0026#34;true\u0026#34; : \u0026#34;false\u0026#34;; } } Little Brother\u0026rsquo;s Factorial Challenge # class Outcome { public static List\u0026lt;Integer\u0026gt; solve(int m, int n) { if (n \u0026lt; m) return new ArrayList\u0026lt;Integer\u0026gt;(); List\u0026lt;Integer\u0026gt; result = new ArrayList\u0026lt;Integer\u0026gt;(); BigInteger[] factorials = new BigInteger[n + 1]; factorials[0] = BigInteger.ZERO; factorials[1] = BigInteger.ONE; for (int i = 2; i \u0026lt;= n; i++) { factorials[i] = BigInteger.valueOf(i).multiply(factorials[i - 1]); } for (int i = m; i \u0026lt;= n; i++) { if (isEven(factorials[i])) result.add(i); } return result.isEmpty() ? Arrays.asList(0) : result; } private static boolean isEven(BigInteger i) { return (i.toString().charAt(0) - \u0026#39;0\u0026#39;) % 2 == 0; } } Brother\u0026rsquo;s Game # class Outcome { public static int solve(List\u0026lt;Integer\u0026gt; nums) { int n = nums.size(); int[] f1 = new int[n]; int[] f2 = new int[n]; int[] f3 = new int[n]; f1[0] = nums.get(0) ^ 1; f2[0] = nums.get(0); f3[0] = nums.get(0); int result = 0; for (int i = 1; i \u0026lt; n; i++) { f1[i] = Math.max(f1[i - 1] + (nums.get(i) ^ 1), f3[i - 1] + (nums.get(i) ^ 1)); f2[i] = Math.max(f1[i - 1] + nums.get(i), f2[i - 1] + nums.get(i)); f3[i] = f3[i - 1] + nums.get(i); result = Math.max(result, Math.max(f1[i], f2[i])); } return result; } } Binary Addition # class Outcome { public static String solve(String a, String b) { StringBuilder sb = new StringBuilder(); int i = a.length() - 1, j = b.length() - 1; int carry = 0; while (i \u0026gt;= 0 || j \u0026gt;= 0 || carry == 1) { if (i \u0026gt;= 0) { carry += a.charAt(i--) - \u0026#39;0\u0026#39;; } if (j \u0026gt;= 0) { carry += b.charAt(j--) - \u0026#39;0\u0026#39;; } sb.append(carry % 2); carry = carry / 2; } return sb.reverse().toString(); } } Biggest Rectangle # class Outcome { public static int maxArea(List\u0026lt;Integer\u0026gt; b) { Deque\u0026lt;Integer\u0026gt; q = new ArrayDeque\u0026lt;Integer\u0026gt;(); int result = 0; for (int i = 0; i \u0026lt;= b.size(); i++) { int curr = i == b.size() ? 0 : b.get(i); while (!q.isEmpty() \u0026amp;\u0026amp; b.get(q.peekLast()) \u0026gt; curr) { int h = b.get(q.pollLast()); int l = q.isEmpty() ? 0 : q.peekLast() + 1; result = Math.max(result, (i - l) * h); } q.offerLast(i); } return result; } } LRU # class Outcome { private int capacity; LinkedHashMap\u0026lt;Integer, Integer\u0026gt; LRU = new LinkedHashMap\u0026lt;\u0026gt;(); public Outcome(int capacity) { this.capacity = capacity; } public int get(int key) { if (!LRU.containsKey(key)) { return -1; } updateRecent(key); return LRU.get(key); } public void put(int key, int val) { if (LRU.containsKey(key)) { LRU.put(key, val); updateRecent(key); return; } if (LRU.size() \u0026gt;= this.capacity) { int oldestKey = LRU.keySet().iterator().next(); LRU.remove(oldestKey); } LRU.put(key, val); } private void updateRecent(int key) { int val = LRU.get(key); LRU.remove(key); LRU.put(key, val); } public static List\u0026lt;Integer\u0026gt; solve(int capacity, List\u0026lt;String\u0026gt; ar) { Outcome LRU = new Outcome(capacity); List\u0026lt;Integer\u0026gt; result = new ArrayList\u0026lt;\u0026gt;(); for (String Operation : ar) { String[] parts = Operation.split(\u0026#34;,\u0026#34;); if (parts[0].equals(\u0026#34;PUT\u0026#34;)) { int key = Integer.parseInt(parts[1]); int value = Integer.parseInt(parts[2]); LRU.put(key, value); } else if (parts[0].equals(\u0026#34;GET\u0026#34;)) { int key = Integer.parseInt(parts[1]); result.add(LRU.get(key)); } } return result; } } ","date":"22 July 2024","externalUrl":null,"permalink":"/notes/glider/","section":"Notes","summary":"String Segmentation # class Outcome { public static String solve(String s, List\u003cString\u003e wordDict) { Set\u003cString\u003e hs = new HashSet\u003c\u003e(wordDict); boolean[] f = new boolean[s.length() + 1]; f[0] = true; for (int i = 1; i \u003c= s.length(); i++) { for (int j = 0; j \u003c i; j++) { if (f[j] \u0026\u0026 hs.contains(s.substring(j, i))) { f[i] = true; break; } } } return f[s.length()] ? \"true\" : \"false\"; } } Little Brother’s Factorial Challenge # class Outcome { public static List\u003cInteger\u003e solve(int m, int n) { if (n \u003c m) return new ArrayList\u003cInteger\u003e(); List\u003cInteger\u003e result = new ArrayList\u003cInteger\u003e(); BigInteger[] factorials = new BigInteger[n + 1]; factorials[0] = BigInteger.ZERO; factorials[1] = BigInteger.ONE; for (int i = 2; i \u003c= n; i++) { factorials[i] = BigInteger.valueOf(i).multiply(factorials[i - 1]); } for (int i = m; i \u003c= n; i++) { if (isEven(factorials[i])) result.add(i); } return result.isEmpty() ? Arrays.asList(0) : result; } private static boolean isEven(BigInteger i) { return (i.toString().charAt(0) - '0') % 2 == 0; } } Brother’s Game # class Outcome { public static int solve(List\u003cInteger\u003e nums) { int n = nums.size(); int[] f1 = new int[n]; int[] f2 = new int[n]; int[] f3 = new int[n]; f1[0] = nums.get(0) ^ 1; f2[0] = nums.get(0); f3[0] = nums.get(0); int result = 0; for (int i = 1; i \u003c n; i++) { f1[i] = Math.max(f1[i - 1] + (nums.get(i) ^ 1), f3[i - 1] + (nums.get(i) ^ 1)); f2[i] = Math.max(f1[i - 1] + nums.get(i), f2[i - 1] + nums.get(i)); f3[i] = f3[i - 1] + nums.get(i); result = Math.max(result, Math.max(f1[i], f2[i])); } return result; } } Binary Addition # class Outcome { public static String solve(String a, String b) { StringBuilder sb = new StringBuilder(); int i = a.length() - 1, j = b.length() - 1; int carry = 0; while (i \u003e= 0 || j \u003e= 0 || carry == 1) { if (i \u003e= 0) { carry += a.charAt(i--) - '0'; } if (j \u003e= 0) { carry += b.charAt(j--) - '0'; } sb.append(carry % 2); carry = carry / 2; } return sb.reverse().toString(); } } Biggest Rectangle # class Outcome { public static int maxArea(List\u003cInteger\u003e b) { Deque\u003cInteger\u003e q = new ArrayDeque\u003cInteger\u003e(); int result = 0; for (int i = 0; i \u003c= b.size(); i++) { int curr = i == b.size() ? 0 : b.get(i); while (!q.isEmpty() \u0026\u0026 b.get(q.peekLast()) \u003e curr) { int h = b.get(q.pollLast()); int l = q.isEmpty() ? 0 : q.peekLast() + 1; result = Math.max(result, (i - l) * h); } q.offerLast(i); } return result; } } LRU # class Outcome { private int capacity; LinkedHashMap\u003cInteger, Integer\u003e LRU = new LinkedHashMap\u003c\u003e(); public Outcome(int capacity) { this.capacity = capacity; } public int get(int key) { if (!LRU.containsKey(key)) { return -1; } updateRecent(key); return LRU.get(key); } public void put(int key, int val) { if (LRU.containsKey(key)) { LRU.put(key, val); updateRecent(key); return; } if (LRU.size() \u003e= this.capacity) { int oldestKey = LRU.keySet().iterator().next(); LRU.remove(oldestKey); } LRU.put(key, val); } private void updateRecent(int key) { int val = LRU.get(key); LRU.remove(key); LRU.put(key, val); } public static List\u003cInteger\u003e solve(int capacity, List\u003cString\u003e ar) { Outcome LRU = new Outcome(capacity); List\u003cInteger\u003e result = new ArrayList\u003c\u003e(); for (String Operation : ar) { String[] parts = Operation.split(\",\"); if (parts[0].equals(\"PUT\")) { int key = Integer.parseInt(parts[1]); int value = Integer.parseInt(parts[2]); LRU.put(key, value); } else if (parts[0].equals(\"GET\")) { int key = Integer.parseInt(parts[1]); result.add(LRU.get(key)); } } return result; } }","title":"Glider","type":"notes"},{"content":" Array / String # Two Pointer # Sliding Window # Prefix Sum # HashMap / Set # Stack # Stack # Queue # Linked List # Binary Tree - DFS # Binary Tree - BFS # Binary Search Tree # Graph DFS # Graph BFS # Heap / Priority Queue # Binary Search # Backtracking # DP - 1D # DP - Multidimensional # Bit Manipulation # Trie # Intervals # Monotonic Stack # ","date":"20 July 2024","externalUrl":null,"permalink":"/notes/leetcode75/","section":"Notes","summary":"Array / String # Two Pointer # Sliding Window # Prefix Sum # HashMap / Set # Stack # Stack # Queue # Linked List # Binary Tree - DFS # Binary Tree - BFS # Binary Search Tree # Graph DFS # Graph BFS # Heap / Priority Queue # Binary Search # Backtracking # DP - 1D # DP - Multidimensional # Bit Manipulation # Trie # Intervals # Monotonic Stack # ","title":"LeetCode 75","type":"notes"},{"content":" Prompt # how to convert my the following description into one sentence that I can use in resume: \u0026#34;Managed Kubernetes Cluster, meaning AWS will manage the Master Nodes for us. It will create the master nodes. Install all the necessary applications on them, like Container Runtime, Kubernetes Master Processes. It will take care of scaling it when needed, doing backup on that, etc. If you have small team of people, usually it\u0026#39;s a good idea to let the platform do this maintenance for you. So we can focus on deploying your applications in K8s without worrying about whether the master nodes are properly backed up etc. This means we only have to care about the worker nodes. we will have a control plane once AWS creates all master nodes**(by choosing cluster name, k8s version, choose region and VPC, set Security Group for the cluster). Then, we have to create worker nodes and connect to the cluster(by creating Node Group, and choose the cluster it attaches to, define Security Group, select instance type of EC2 instances, basically which resources your EC2 instances should have, etc.). On AWS these worker nodes will be some EC2 instances with certain CPU RAM and Storage Resources. With Node Group, we should have auto-scaling. So based on the cluster needs, depend on how much load the cluster has, the new worker nodes will automatically be added or removed in the cluster. So for that we should define max and min number of nodes, and we have some other configurations as well. Finally connect to the cluster from local machine, to deploy our applications from laptop using kubectl, which is k8s command line tool. It basically uses configured kubectl to talk to remote cluster.\u0026#34; please give me similar description that I can use in resume for XXX experience Java # DropWizard # Developed and maintained RESTful web services using DropWizard, ensuring high performance and scalability by implementing efficient resource management and optimization techniques. Led the integration of DropWizard with various backend systems, enhancing system reliability and simplifying deployment processes through robust configuration management and monitoring. Spring Boot # Spring MVC # Spring Security # Spring Data JPA # Hibernate # Kafka # Junit # TestNG # Selenium # Cucumber # Jmeter # Log4J # Splunk, Logstash, Kibana # SonarQube # Maven # Tomcat # SOAP # JWT # Oauth2 # J2EE (Java Servlets, JDBC) # Restful Web Services # Microservice # DataDog # Dynatrace # Postman # AWS # Compute # EC2 (Elastic Compute Cloud) # Understand EC2 instance types, pricing models, Auto Scaling, and best practices.\nProvisioned and managed AWS EC2 instances to deliver scalable compute resources, configured instance types, security groups, and storage options, ensuring optimal performance and availability for applications. Lambada # Familiarity with serverless computing, event-driven architecture, and common use cases.\nDeveloped and managed AWS Lambda functions by creating and configuring functions, setting up event triggers, integrating with other AWS services, optimizing performance, and ensuring scalability and reliability. Elastic Beanstalk # Managed service for deploying and scaling web applications and services.\nManaged AWS Elastic Beanstalk environments by provisioning and configuring application resources, handling auto-scaling and load balancing, deploying applications, and ensuring continuous integration and delivery using CI/CD pipelines. Storage # S3 (Simple Storage Service) # Knowledge of storage classes, lifecycle policies, access control, and data protection mechanisms.\nConfigured and managed AWS S3 for scalable storage solutions, ensuring secure data access and efficient storage management with versioning, lifecycle policies, and access control configurations. EBS (Elastic Block Store) # Understanding of volumes, snapshots, and performance optimization.\nEFS (Elastic File System) # Network file system concepts and use cases.\nDatabases # RDS (Relational Database Service) # Managed relational databases, including Aurora, MySQL, PostgreSQL, and more.\nManaged AWS RDS instances by creating and configuring databases, optimizing performance, automating backups, ensuring high availability and security, and integrating with other AWS services for scalable and reliable database solutions. Managed AWS RDS instances by configuring databases, performing automated backups, monitoring performance, and ensuring high availability and scalability for production environments. DynamoDB # NoSQL database, key-value and document store, scaling, and performance optimization.\nRedshift # Data warehousing and analytics.\nElastiCache # Managed caching for Redis and Memcached.\nNetWorking # VPC (Virtual Private Cloud) # Network segmentation, subnets, route tables, NAT gateways, and security groups. basically our Virtual Private Space in AWS, basically a space where you do your own stuff, it doesn\u0026rsquo;t interfere with other AWS users in the cloud. That\u0026rsquo;s basically what VPC is.\nDesigned and managed AWS VPCs to create isolated network environments, configure subnets, route tables, and gateways, ensuring secure and efficient network communication for applications. Route 53 # DNS service, routing policies, and domain management.\nAPI Gateway # Creating, deploying, and managing APIs.\nSecurity and Identity # IAM (Identity and Access Management) # Users, roles, policies, and best practices.\nIAM role # AWS user\nDesigned and managed AWS IAM roles to control access and permissions, ensuring secure and efficient resource management across various AWS services. KMS (Key Management Service) # Managing encryption keys and securing data.\nImplemented and managed AWS KMS by creating and configuring encryption keys, ensuring secure key management, integrating with other AWS services for data protection, and enforcing compliance with security policies and best practices. Cognito # User authentication and authorization\nMonitoring and Management # CloudWatch # Monitoring, logging, and alerting.\nImplemented and managed AWS CloudWatch by configuring monitoring and alerting for resources, setting up custom metrics and dashboards, analyzing logs, and ensuring system performance and reliability through automated responses and insights. CloudTrail # Logging and auditing AWS API calls.\nConfig # Resource inventory, configuration history, and change notifications.\nDevOps and CI/CD # CodePipeline # Continuous integration and continuous delivery.\nCodeBuild # Build and test code.\nCodeDeploy # Automated deployment of applications.\nAnalytics and Big Data # EMR (Elastic MapReduce) # Bit data processing using Hadoop, Spark, etc.\nAthena # Querying data in S3 using SQL.\nKinesis # Real-time data processing and streaming.\nMachine Learning # SageMaker # Building, training, and deploying machine learning models.\nImplemented and managed machine learning workflows on AWS by setting up and maintaining SageMaker environments, configuring EC2 instances for scalable training and inference, and deploying models using automated pipelines and endpoint management. Rekognition # Image and video analysis.\nComprehend # Natural Language Processing.\nApplication Integration # SQS (Simple Queue Service) # Messaging queue service.\nDesigned and implemented AWS SQS solutions by creating and configuring queues, integrating with other AWS services, optimizing message handling, and ensuring reliable and scalable message processing. SNS (Simple Notification Service) # Messaging and notifications.\nDesigned and managed AWS SNS by creating and configuring topics, setting up subscriptions, integrating with other AWS services, optimizing notification delivery, and ensuring reliable and scalable message broadcasting. Step Function # Orchestration of serverless workflows.\nArchitecture Best Practices # Well-Architected Framework # Understanding the pillars of operational excellence, security, reliability, performance efficiency, and cost optimization.\nMicroservices # Designing and deploying microservices on AWS\nGeneral Concepts # Infrastructure as Code (IaC) # Using AWS CloudFormation or Terraform\nCost Management # Best practices for cost optimization and monitoring\nDisaster Recovery # Strategies for backups, failover, and resilience.\nOthers # Security Group # a list of permissions\nConfigured and managed AWS Security Groups to control inbound and outbound traffic, ensuring secure network communication for applications and compliance with security policies. ECS # Managed and deployed containerized applications using AWS ECS, configuring clusters, task definitions, and service scaling to ensure high availability and efficient resource utilization. EKS # Managed Kubernetes Cluster, meaning AWS will manage the Master Nodes for us. It will create the master nodes. Install all the necessary applications on them, like Container Runtime, Kubernetes Master Processes. It will take care of scaling it when needed, doing backup on that, etc. If you have small team of people, usually it\u0026rsquo;s a good idea to let the platform do this maintenance for you. So we can focus on deploying your applications in K8s without worrying about whether the master nodes are properly backed up etc. This means we only have to care about the worker nodes.\nwe will have a control plane once AWS creates all master nodes(by choosing cluster name, k8s version, choose region and VPC, set Security Group for the cluster). Then, we have to create worker nodes and connect to the cluster(by creating Node Group, and choose the cluster it attaches to, define Security Group, select instance type of EC2 instances, basically which resources your EC2 instances should have, etc.). On AWS these worker nodes will be some EC2 instances with certain CPU RAM and Storage Resources. With Node Group, we should have auto-scaling. So based on the cluster needs, depend on how much load the cluster has, the new worker nodes will automatically be added or removed in the cluster. So for that we should define max and min number of nodes, and we have some other configurations as well. Finally connect to the cluster from local machine, to deploy our applications from laptop using kubectl, which is k8s command line tool. It basically uses configured kubectl to talk to remote cluster.\nCons: complex comparing to Linux K8s Engine and Digital Ocean K8s. Pros: powerful, popular.\neksctl one single eks single command, it basically does all above steps in background. Configuration will use default values. We can override configurations via parameters. But we still have one command.\nManaged AWS EKS cluster by overseeing the creation and maintenance of master nodes, configuring worker nodes with auto-scaling EC2 instances, and deploying applications using kubectl and eksctl. CloudFormation # Developed and maintained AWS CloudFormation templates by defining infrastructure as code, automating resource provisioning, ensuring consistent environments, and managing stack updates and rollbacks for scalable and reliable deployments. Python # Python Script # Developed and maintained Python scripts for automating data processing tasks, and improving workflow efficiency, while ensuring code quality through testing and version control. Developed and maintained Python scripts for automation, data analysis, and process optimization, and improved efficiency and accuracy of repetitive tasks. Machine Learning # Developed and deployed machine learning models by managing data preprocessing, feature engineering, model training, and evaluation, utilizing cloud platforms for scalable computing resources, and integrating models into production environments to ensure continuous performance monitoring and optimization. Pytorch # Developed and deployed deep learning models using PyTorch, performed data preprocessing and augmentation, optimized model performance, and collaborated with research and engineering teams to implement scalable solutions for various machine learning tasks. Developed and optimized deep learning models using PyTorch, implemented data preprocessing pipelines, and trained models on large datasets to achieve high performance and accuracy. Sklearn # Implemented and optimized machine learning models using Scikit-learn, conducted data preprocessing and feature engineering, and performed model evaluation and tuning to achieve high predictive accuracy. pySpark # Developed and optimized large-scale data processing pipelines using PySpark, performed data cleansing and transformation, implemented complex algorithms for data analysis, and collaborated with data engineering teams to ensure efficient data flow and integration within a distributed computing environment. Developed and optimized data processing workflows using PySpark, implemented ETL processes on large-scale datasets, and performed distributed computing tasks to enhance data analysis efficiency and scalability. PyTest # Designed and implemented comprehensive test suites using PyTest to ensure the reliability and performance of applications, developed automated test scripts for functional and regression testing. Unittest # Designed and implemented unit tests using the Unittest framework in Python to ensure code quality and reliability, created comprehensive test cases for various functionalities, automated testing processes. Numpy # Utilized NumPy to perform high-performance numerical computations, developed efficient algorithms for data manipulation and analysis, optimized data processing workflows, and collaborated with data science teams to implement and test statistical models and simulations. Utilized NumPy for efficient numerical computations, performed data manipulation and analysis, and optimized performance of scientific computing tasks through array operations and vectorization techniques. Pandas # Utilized Pandas to perform data manipulation, analysis, and visualization, developed efficient data processing workflows, cleaned and transformed large datasets, and collaborated with data science teams to derive actionable insights and support data-driven decision-making. Leveraged Pandas for data manipulation and analysis, performed data cleaning, transformation, and aggregation, and optimized workflows for handling large datasets to enhance data-driven decision-making. Scikit-learn # Developed and implemented machine learning models using Scikit-learn, leveraging algorithms such as linear regression, decision trees, and k-nearest neighbors to analyze and predict data trends, resulting in a 15% increase in prediction accuracy. Utilized Scikit-learn for data preprocessing, feature selection, and model evaluation, enabling efficient and effective development of predictive analytics solutions that improved decision-making processes and operational efficiency. Dash # Developed interactive data visualization dashboards using Dash and Plotly, enabling real-time data analysis and decision-making for business stakeholders. Integrated data from various sources, including SQL databases and APIs, to provide comprehensive insights and actionable metrics. Implemented and optimized Dash applications to display complex data sets through intuitive visualizations and user-friendly interfaces. Focused on enhancing performance, usability, and responsiveness to ensure seamless user experiences across different devices and platforms. Dask # Optimized data processing workflows by implementing Dask for distributed computing, significantly improving the performance and scalability of large-scale data analytics tasks. Developed and deployed efficient data pipelines using Dask, enabling parallel processing of complex datasets and reducing overall computation time in data-intensive projects. Flask # Developed and maintained robust web applications using Flask, implementing RESTful APIs, integrating with various databases, and ensuring efficient request handling and response processing to optimize performance and user experience. Designed and deployed scalable Flask-based microservices, focusing on application security, session management, and seamless integration with front-end frameworks, resulting in improved application stability and responsiveness. Boto3 # Automated AWS infrastructure management by developing Python scripts with Boto3 to provision, configure, and maintain services such as EC2, S3, and RDS, resulting in improved operational efficiency and reduced manual intervention. Enhanced cloud operations through the integration of Boto3, enabling seamless interaction with AWS services, automating resource scaling, and implementing robust monitoring and alerting solutions for optimized performance and cost management. SQL # PostgresSQL # Designed, developed, and optimized PostgreSQL databases, implemented complex SQL queries and stored procedures, and ensured database performance, security, and scalability for application development projects. Oracle # Developed and maintained Oracle databases, implemented complex PL/SQL scripts and stored procedures, optimized database performance, and ensured data integrity and security in support of business operations. MySQL # Designed, developed, and maintained MySQL databases, optimized query performance, implemented data models, and ensured data integrity and security in high-traffic applications. NoSQL # AWS DynamoDB # Table Item Attribute Primary Key Partition Key Sort Key Global Secondary Index AWS ElasticCache # Redis # MongoDB # Cassandra # GraphQL # ElasticSearch # HTML \u0026amp; CSS # Typescript # Angular # Collaborated with cross-functional teams to design and integrate RESTful APIs in Angular applications, leveraging Angular services, routing, and dependency injection to ensure efficient data flow and seamless user interactions. Bootstrap # Material UI # Javascript # React # jQuery # C++ # CUDA # Go (Golang) # Devops # Git # GitLab # GitHub # Bitbucket # Implemented and managed Bitbucket repositories for version control, ensuring efficient code collaboration and integration across development teams by utilizing pull requests, branch management, and code reviews. Jenkins # Bamboo # Implemented and maintained continuous integration and continuous deployment (CI/CD) pipelines using Bamboo, streamlining the build, test, and release process, resulting in a 30% reduction in deployment time and improved software quality. Developed custom build plans and automated workflows in Bamboo, ensuring seamless integration with version control systems and facilitating efficient collaboration among development teams. Artifactory # Implemented and managed Artifactory for artifact storage and distribution, ensuring seamless integration with CI/CD pipelines, leading to improved build efficiency and artifact version control. Configured and maintained Artifactory repositories for multiple projects, enabling secure and efficient artifact management, versioning, and retrieval across development teams. Optimized build and deployment processes by leveraging Artifactory\u0026rsquo;s capabilities for storing and managing Docker images, Maven, and npm packages, resulting in enhanced workflow automation and reduced downtime.### Concourse Ansible # Developed and maintained Ansible playbooks to automate the deployment, configuration, and management of cloud infrastructure across multiple environments, ensuring consistency and reducing manual intervention. Implemented continuous integration and continuous deployment (CI/CD) pipelines using Ansible to automate application deployment, configuration updates, and system patching, significantly improving deployment speed and reliability. Led the migration of legacy infrastructure to an automated environment by creating Ansible roles and playbooks, which improved scalability, reduced downtime, and enhanced system performance through efficient resource management. Airflow # Jira # Confluence # Docker # Kubernetes # Design Pattern # Singleton # Proxy # Factory # Builder # MVC # DAO # Linux # Big Data # Hadoop # Spark # HDFS # Hive # Kafka # Skills - Long # Languages: Java, C++, SQL, TypeScript, JavaScript, Python, Go. Databases: PostgreSQL, Oracle, MySQL, Hive, Redis, MongoDB, Cassandra, AWS RDS, AWS DynamoDB. Frameworks: Spring Boot, Spring MVC, Spring Security, Spring Data JPA, Hibernate, Kafka, Junit, TestNG, Selenium. Cloud Service: AWS (EC2, S3, ECS, EKS, RDS, DynamoDB, SNS, SQS, Lambda, Fargate, KMS, CloudWatch). Frontend Technologies: Angular 10+, HTML, CSS, Bootstrap, Material UI, jQuery. Web Technologies: J2EE (Java Servlets, JDBC), RESTful Web Services, SOAP, JWT, Oauth2. Design Patterns: Singleton, Factory, Builder, Proxy, MVC, DAO. Dev/Ops: Git, Docker, Kubernetes, Jenkins, GitHub, GitLab, Linux, Bash. IDE \u0026amp; Tools: IntelliJ IDEA, WebStorm, VS Code, Maven, Jira, Splunk, Logstash, Kibana. AI / ML / HPC: Pytorch, Sklearn, Pandas, Numpy, CUDA.\nSkills - Short # Technical Skills: Java, SQL, TypeScript, JavaScript, Python, Go, C++, HTML, CSS, Spring Boot, Spring MVC, Spring Security, Spring Data JPA, Hibernate, Kafka, Junit, TestNG, Selenium, PostgreSQL, Oracle, MySQL, Hive, Redis, MongoDB, Cassandra, Angular, Bootstrap, Angular Material, jQuery, AWS (EC2, S3, ECS, EKS, RDS, DynamoDB, SNS, SQS, Lambda, Fargate, KMS, CloudWatch). Tools \u0026amp; Knowledge: J2EE (Java Servlets, JDBC), RESTful Web Services, JWT, Oauth2, Maven, Jira, Splunk, Logstash, Kibana, Git, Docker, Kubernetes, Jenkins, GitHub, GitLab, Linux, Bash, Pytorch, Sklearn, Pandas, Numpy, CUDA.\nQuestion: Do you have Cassandra experience? # Scenario 1: You Have Experience with Cassandra # Start with a Confirmation:\n\u0026ldquo;Yes, I have experience with Cassandra.\u0026rdquo; Mention the Duration:\n\u0026ldquo;I have been working with Cassandra for the past [X] years/months.\u0026rdquo; Describe Your Role and Responsibilities:\n\u0026ldquo;In my previous project, I was responsible for setting up and managing Cassandra clusters. This involved configuring nodes, setting up replication, and ensuring high availability.\u0026rdquo; Highlight Specific Tasks:\n\u0026ldquo;I worked on optimizing read and write performance, managing data modeling, and implementing Cassandra query language (CQL) for database operations. Additionally, I handled backup and restore processes and monitored cluster health using tools like nodetool and OpsCenter.\u0026rdquo; Discuss Relevant Projects:\n\u0026ldquo;One notable project was [briefly describe the project], where we used Cassandra to handle large-scale data for real-time analytics. My role included designing the schema, ensuring data consistency, and optimizing query performance.\u0026rdquo; Mention Challenges and Solutions:\n\u0026ldquo;One challenge we faced was [describe a challenge], and to overcome it, we [describe the solution]. This improved our system\u0026rsquo;s reliability and performance significantly.\u0026rdquo; Conclude with Your Enthusiasm:\n\u0026ldquo;Overall, I found Cassandra to be a robust and scalable solution for our needs, and I\u0026rsquo;m keen to leverage my experience in Cassandra to contribute to your team.\u0026rdquo; Scenario 2: You Have Limited or Indirect Experience with Cassandra # Be Honest:\n\u0026ldquo;I have limited experience with Cassandra.\u0026rdquo; Explain Your Level of Exposure:\n\u0026ldquo;I have worked on a project where Cassandra was used as the primary database, and while I wasn\u0026rsquo;t directly responsible for managing it, I collaborated closely with the database team.\u0026rdquo; Highlight Related Skills:\n\u0026ldquo;I am familiar with NoSQL databases and concepts, having worked extensively with [mention any other NoSQL databases you have experience with, e.g., MongoDB, DynamoDB]. This includes data modeling, query optimization, and handling large datasets.\u0026rdquo; Mention Learning Efforts:\n\u0026ldquo;To expand my knowledge, I have completed an online course on Cassandra and have experimented with it in my personal projects. I set up a small cluster and performed basic operations using CQL.\u0026rdquo; Express Willingness to Learn:\n\u0026ldquo;I am enthusiastic about deepening my expertise in Cassandra and am confident that my background in NoSQL databases will help me quickly get up to speed.\u0026rdquo; Scenario 3: You Have No Experience with Cassandra # Be Honest:\n\u0026ldquo;I haven\u0026rsquo;t had the opportunity to work with Cassandra directly.\u0026rdquo; Highlight Related Experience:\n\u0026ldquo;However, I have extensive experience with NoSQL databases like MongoDB and DynamoDB. I understand the principles of distributed databases, data modeling, and managing large-scale data.\u0026rdquo; Show Willingness to Learn:\n\u0026ldquo;I am very interested in learning Cassandra and have already started going through online resources and documentation to familiarize myself with its architecture and operations.\u0026rdquo; Connect with Relevant Skills:\n\u0026ldquo;Given my strong background in [mention relevant technologies or skills], I am confident that I can quickly adapt to using Cassandra in a professional setting.\u0026rdquo; Express Enthusiasm:\n\u0026ldquo;I am eager to expand my skill set to include Cassandra and believe that my existing knowledge of NoSQL databases will be a strong foundation for learning it effectively.\u0026rdquo; General Tips # Be Honest: Never exaggerate your experience. Interviewers appreciate honesty and a willingness to learn. Be Specific: Provide concrete examples and details about your experience and knowledge. Show Enthusiasm: Demonstrate your interest in Cassandra and your commitment to expanding your skills. Relate to the Role: Connect your experience with Cassandra (or lack thereof) to the responsibilities of the position you are applying for. ","date":"10 July 2024","externalUrl":null,"permalink":"/notes/resume_parts/","section":"Notes","summary":"Prompt # how to convert my the following description into one sentence that I can use in resume: \"Managed Kubernetes Cluster, meaning AWS will manage the Master Nodes for us. It will create the master nodes. Install all the necessary applications on them, like Container Runtime, Kubernetes Master Processes. It will take care of scaling it when needed, doing backup on that, etc. If you have small team of people, usually it's a good idea to let the platform do this maintenance for you. So we can focus on deploying your applications in K8s without worrying about whether the master nodes are properly backed up etc. This means we only have to care about the worker nodes. we will have a control plane once AWS creates all master nodes**(by choosing cluster name, k8s version, choose region and VPC, set Security Group for the cluster). Then, we have to create worker nodes and connect to the cluster(by creating Node Group, and choose the cluster it attaches to, define Security Group, select instance type of EC2 instances, basically which resources your EC2 instances should have, etc.). On AWS these worker nodes will be some EC2 instances with certain CPU RAM and Storage Resources. With Node Group, we should have auto-scaling. So based on the cluster needs, depend on how much load the cluster has, the new worker nodes will automatically be added or removed in the cluster. So for that we should define max and min number of nodes, and we have some other configurations as well. Finally connect to the cluster from local machine, to deploy our applications from laptop using kubectl, which is k8s command line tool. It basically uses configured kubectl to talk to remote cluster.\" please give me similar description that I can use in resume for XXX experience Java # DropWizard # Developed and maintained RESTful web services using DropWizard, ensuring high performance and scalability by implementing efficient resource management and optimization techniques. Led the integration of DropWizard with various backend systems, enhancing system reliability and simplifying deployment processes through robust configuration management and monitoring. Spring Boot # Spring MVC # Spring Security # Spring Data JPA # Hibernate # Kafka # Junit # TestNG # Selenium # Cucumber # Jmeter # Log4J # Splunk, Logstash, Kibana # SonarQube # Maven # Tomcat # SOAP # JWT # Oauth2 # J2EE (Java Servlets, JDBC) # Restful Web Services # Microservice # DataDog # Dynatrace # Postman # AWS # Compute # EC2 (Elastic Compute Cloud) # Understand EC2 instance types, pricing models, Auto Scaling, and best practices.\n","title":"Resume Parts","type":"notes"},{"content":" Study Plan 2024 # Language # Java: Backend, Spring Typescript: Angular Go: Backend Python: Kaggle C++: CUDA Long # Leetcode\nhttps://leetcode.com/problemset/ OOD, System Design\nhttps://www.designgurus.io/course-play/grokking-the-object-oriented-design-interview/doc/637d2288eeb90d2166ac95be Focus # Java SpringBoot Angular Note # merge sort quick select union find segment tree binary index tree strongly connected component shortest path monotonic queue ","date":"8 July 2024","externalUrl":null,"permalink":"/notes/note/","section":"Notes","summary":"Study Plan 2024 # Language # Java: Backend, Spring Typescript: Angular Go: Backend Python: Kaggle C++: CUDA Long # Leetcode\nhttps://leetcode.com/problemset/ OOD, System Design\nhttps://www.designgurus.io/course-play/grokking-the-object-oriented-design-interview/doc/637d2288eeb90d2166ac95be Focus # Java SpringBoot Angular Note # merge sort quick select union find segment tree binary index tree strongly connected component shortest path monotonic queue ","title":"NOTE","type":"notes"},{"content":"","date":"4 July 2024","externalUrl":null,"permalink":"/notes/kafka/","section":"Notes","summary":"","title":"Kafka","type":"notes"},{"content":"","date":"1 July 2024","externalUrl":null,"permalink":"/notes/bugs/","section":"Notes","summary":"","title":"Bugs","type":"notes"},{"content":" Various container runtimes # Kubernetes is responsible for scheduling pods. Container runtimes manage the containers which make up these pods. The Container Runtime Interface (CRI) allows Kubernetes to use any CRI-compliant runtime. ","date":"30 June 2024","externalUrl":null,"permalink":"/notes/k8s/","section":"Notes","summary":"Various container runtimes # Kubernetes is responsible for scheduling pods. Container runtimes manage the containers which make up these pods. The Container Runtime Interface (CRI) allows Kubernetes to use any CRI-compliant runtime. ","title":"K8s","type":"notes"},{"content":" 1. Self Introduction # My name is Austin. I have been working as a Java developer for over 6 years, and primarily focusing on Backend Development, and the MicroService architecture.\nMy expertise basically like the core java concept, and (I am) familiar with those popular frameworks such as Spring Boot, Spring MVC, Spring Security, Spring Data JPA, Hibernate, Kafka, JUnit, and so on. And for front-end, I have experience using React and also Angular. I also have hands-on experience on building robust MicroService architecture with AWS cloud.\njust add a brief idea like what industry have I participated in. like what project I have worked on.\n2. My most recent project # In my most recent project I contribute(d) to a system called ADS, which is stand for Analytics Dashboard System. And it is designed to satisfy the company’s data analysis needs. And Initially the ADS was designed in a monolithic architecture. And my project is aim to basic decompose the whole architecture into MicroService and deploy the system on AWS. And to enhance the scalability and flexibility of the whole system.\nI am in charge of the order functionality for this project, including order trending service, real-time order analysis service, and promotion analysis service.\nThe order functionalities basically is handle like the order data on different dimensions for analysis purpose.\nthe order trending MS, it handles the order trending data over time in (daily, weekly, monthly, and so on). and order real-time MS, like during some sales event, it handles real-time ordering data. and promotion MS, it handles the data like the revenue and customer retention rate improvement and so on. It also contains report service that will generate comprehensive report.\nThis whole architecture shape basically enhance the whole systems’ scalability, flexibility, and maintainability. And also enhance the data management process.\nIn terms of my tech stack, I specialize in backend Restful API using Java and Spring Boot and alongside with the Spring Data JPA, and Hibernate for data management, and TestNG and Selenium for Testing. For the MicroService I have intensely use Kafka to build like the event driven and support the real-time update. And I primarily work with the PostgreSQL database. But I also have experience with non-sql type of database. For front-end, I use Angular to create frontend user interface for different modules, so for current project I basically worked on order related modules, and services.\n3. Talk about the biggest challenge in my project. # So the biggest challenge in the ADS project, is basically I worked on the enhance performance for one of our API endpoints. It’s called like customer retention rate analysis service in the Promotion MicroService. And as our company is going bigger this year. And we have experience like database query delay to this specific endpoint. And it often cause like a performance issue. And sometimes it overloads our database, and even occasionally crash our database.\nI discussed this problem with my team lead. And he proposed like may be we could try to kind of to use like Elastic Search. And Elastic Search is kind of new technology for like for the team and also for me. And we do have like the other team which working on the e-commerce websites to use ElasticSearch. Since our e-commerce website is basically for like unstructured data and will have a lot of like filter stuff. But for like this specific customer retention rate analysis endpoint, is basically we need it fast response time. So first of all I have a team meeting with all team members and my manager. To aligns up may be we need to try this kind of approach. I just want to make sure that everyone is on the same page. And I also go the person like who is working on the e-commerce website, to kind of like talk about my implementation and to see like if this approach is on track. And he gave me like a remind me a tool like called OpenDistro which can help me to like convert like SQL statements to ElasticSearch queries. And we did try that approach, however like, it’s still like causing sometime random chaos in our system. So I did the further research and we just determined that we may be we should just try to use the native ElasticSearch queries. And I work closely with my team lead. And we basically migrate that old MySQL database for the Customer to ElasticSearch. Using ElasticSearch as the backend. And we have successfully launched this enhanced feature like in two months. I feel like this is going be the most challenge part I found in my project.\nAngular Phone Interview # Okay, I use Angular in both my current project and previous project.\nSo for the current project, it\u0026rsquo;s a dashboard system. I use Angular to build frontend for different modules, I basically worked on order related modules, and services. This involves integrating different APIs to fetch data, and create UI components. And I also implemented data visualizations using D3 and Chart external libraries in Angular.\nAnd in my previous projects, I also used Angular for the frontend, with NgRx for state management, and Angular Material for the UI part.\nQuestions for Interviewer # What\u0026rsquo;s the tech stack that the team currently use? That sounds great. And I am strong fit in this role. Do you recommend other tech stack I need to know in this role? Can you tell me about the team I’ll be working with? Who will I be work with most closely? What’s the company and team culture like? Is it more of a team or independent work environment? What\u0026rsquo;s the next step of the interview? ","date":"26 June 2024","externalUrl":null,"permalink":"/notes/interview_self_introduction/","section":"Notes","summary":"1. Self Introduction # My name is Austin. I have been working as a Java developer for over 6 years, and primarily focusing on Backend Development, and the MicroService architecture.\nMy expertise basically like the core java concept, and (I am) familiar with those popular frameworks such as Spring Boot, Spring MVC, Spring Security, Spring Data JPA, Hibernate, Kafka, JUnit, and so on. And for front-end, I have experience using React and also Angular. I also have hands-on experience on building robust MicroService architecture with AWS cloud.\n","title":"Self Introduction","type":"notes"},{"content":" Guide # please write down design document for your most recent project, including: features / functionalities / system purpose + overview database schema (tables) high level design (microservice architecture) and provide module pictures rest api design (design 2 - 4 rest apis) Data flow, prepare 2 - 3 data flow diagram (example: when user client some buttons to upload some files, what happens next, how does request go through your service Design Doc of eBay project - Data Analytics Dashboard # 1. Features / Functionalities / System Purpose + Overview # Dashboard # Requirements # there is an analytical dashboard it has a ton of widgets Out of Scope # write operations there may have been some kind of \u0026ldquo;generate report\u0026rdquo; button Numbers # 1000 TPS reads Approaches # Rendering approaches: # client-side rendering\nPros: less complex Cons: higher latency server-side rendering\nPros: faster page loads (lower latency) Cons: duplicated rendering logic could be bandwidth bound JSON blob side-load rendering, of all the data approach\nPros: still fairly faster page loads (lower latency) no duplication of rendering logic Cons: might be a little more complex Data fetching approach: # Non-aggregated Pros: stronger consistency Cons: tail end latency amplifications eventual consistency Pre-aggregated Pros: probably faster page load times (you don\u0026rsquo;t depend on the response times of upstream services) Cons: eventual consistency Sometimes we can do both Widget decoupling strategy # AGENDA # client side rendering\nget index.html shell file retrieve assets from CDN make a bunch of back-end calls client side rendering\nget index.html retrieve assets from CDN make a call to aggregated endpoint server-side rendering\ncall comes into the back-end server either pre-cached data OR a bunch of API calls return the pre-rendered HTML JSON blob side-load rendering approach\ncall comes into the back-end server either pre-cached data OR a bunch of API calls return a shell index.html file with an attached JSON blob pull assets from CDN Widget decoupling strategy # Live discussion questions # Hey, do you follow the book \u0026ldquo;System Design Interview\u0026rdquo; by Lewis Len?\nYes, I do have that book! :) I like Stanley Chiang\u0026rsquo;s too is the pre cached data and cdn for historical data? i thought dashboards usually refresh at a frequent rate to get new data from the backend?\ndepends on how stale that data can get would depends on what kind of charts are we looking. Say for stock ticker, it would send data through push/socket and asks application/client to update data in chart itself\nFor static charts(updates once a day or so), cdn would help just like bundled js\nSEO capability — Since the page load happens in server side, it is easier for the SEO crawlers to search, explore content and index the pages.\nThe equivalent of this in the industry is - \u0026ldquo;Design Grafana\u0026rdquo; ?\nI think CDN would be needed in case there\u0026rsquo;s some video/giphy? Otherwise response would be kind of big\nIf its hosted within an iFrame, then a CDN is still required to perform filters, slicing on SSR payload?\nCDN will host the js bundles which has the slicing functions\nwhat is the difference between the last one and pre-aggregated, server side rendering?\npre-aggregated, server side rendering uses a cache\nthe last one (the side-loaded JSON blob) does not use a cache (for stronger consistency)\nDid we discuss the tech for caching store and data layout, etc.? Thanks! :)\nIn interview were you given specific info whether you want to design real time/static dashboard, non predictable dashboard? You should clarify that with the interviewer for sure \u0026ndash; whether stale data is tolerated\nI personally used MongoDB as aggregated cache using _id. Worked with way less $$ than redis \u0026ldquo;caching\u0026rdquo;\nHow to handle streaming data say for a time chart? that might require a socket \u0026ndash; for continuous updating without resfreshing the page if page refreshes are fine, you can just have more fresh data from not not passing that data throught the caching data store\nIf alerting is in scope - for breaching thresholds, at what layer should the alert rules be stored? alerts do not come from dashboard\nwhat\u0026rsquo;s in schemaless_JSON_blob? states of widget? anything you want, possibly in ion format\nwhere the aggregated data aggregated stored ? DynamoDB / MongoDB\nFor the Hybrid approach, what is the purple color rectangle in your diagram?\nor it is that front end will call aggregated data service which would call the service a , b apis\n@Jitendra Vyas hmm why don\u0026rsquo;t we use MQ here? Can service push msg to MQ and then the \u0026ldquo;some task runner\u0026rdquo; service push data to cache?\nOr other approach is \u0026ldquo;some task runner\u0026rdquo; is like a cronjob, and it will runs for every 5 mins?\nyou\u0026rsquo;re right actually. That purple box is blackbox here to state purpose of there\u0026rsquo;s some event that says there\u0026rsquo;s data update in system\nwhat messages are store inside the purple msg broker kafka?\nny relevant info, new aggregated data available in system\nhow can we integrate real time data capturing elements like web sockets or sse?\nhmm so can service A, B, C push to mq and then \u0026ldquo;some task runners\u0026rdquo; will consume it?\ni mean push data to UI instead of pull\nFor the hybrid approach, I see you combine both the pre-aggregate and non-aggregate. Suppose that we have a client request, how can we decide which way should we do? it does both\nWhat does the js bundle for the CDN widget for each service have?\n{ user1234_configA: { lineChartWidget: \u0026#34;{ lineData: [5,6,9,2] }\u0026#34;, pieChartWidget: \u0026#34;{ sessionMap: { email: 982 social: 968 display: 910 } }\u0026#34;, tableWidget: \u0026#34;{ // }\u0026#34; } } Notes # We had a ton of Upstream dependencies which kind of is analogous to having a bunch of different widgets. Each widget required like different API call.\nRendering the page, it might take a dozen of API calls. Then we are susceptible to the problem that DDIA was called tail end latency amplification?.\nTail latency amplification is a phenomenon that increases the impact of long delays by making all requests that arrive during a period of time more likely to experience latency.\n","date":"24 June 2024","externalUrl":null,"permalink":"/notes/project_design_doc/","section":"Notes","summary":"Guide # please write down design document for your most recent project, including: features / functionalities / system purpose + overview database schema (tables) high level design (microservice architecture) and provide module pictures rest api design (design 2 - 4 rest apis) Data flow, prepare 2 - 3 data flow diagram (example: when user client some buttons to upload some files, what happens next, how does request go through your service Design Doc of eBay project - Data Analytics Dashboard # 1. Features / Functionalities / System Purpose + Overview # Dashboard # Requirements # there is an analytical dashboard it has a ton of widgets Out of Scope # write operations there may have been some kind of “generate report” button Numbers # 1000 TPS reads Approaches # Rendering approaches: # client-side rendering\n","title":"Project Design Doc","type":"notes"},{"content":" Guide # Project Preparation Guide # Company Info. Brief company info. Project Name. Why the project exists? What\u0026rsquo;s the main goal? Users. The project is used by whom? How many users approximately? What\u0026rsquo;s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) What\u0026rsquo;s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? High-level System Structure TechStack Environments Cloud/Platform Architecture One Specific Functionality. High-Level System Structure Guide # TechStack Java version, SpringBoot, REST, What kind of DB, AWS(Cloud), React/Angular, Junit, Mockito Environments Dev, Test, Staging, Production Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. Project 1: eBay - Data Analytics Dashboard # Company Info. eBay is an e-commerce company, that connects buyers and sellers around the world. Project Name. Why the project exists? What\u0026rsquo;s the main goal? The project is used to provide comprehensive insights and analytics to various departments within eBay, such as sales, marketing, product development, and customer support. The dashboard will aggregate data from different sources, process it in real-time, and present it through interactive and customizable visualizations. Users. The project is used by whom? How many users approximately? This project is only used by internal users. There is about 3,000 users are allowed to access the dashboard with different access level. What\u0026rsquo;s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) 1 Product Manager + Scrum Master 1 BA 1 Project Manger + BE TL BE 1 TL + 7 Dev FE 1 TL + 5 Dev QA 1 TL + 4 QA DBA 2 DBA DevOps 2 DevOps What\u0026rsquo;s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective The PM/SM is responsible for creating and maintaining the Product Backlog, a prioritized list of features, enhancements, and bug fixes required for the product. One single Scrum Spring consists: Product Backlog ↓ Sprint Planning ↓ Sprint Backlog ↓ Sprint Execution (2-4 weeks) ↓ ↓ ↓ Daily Scrum Development Task Updates ↓ ↓ ↓ Sprint Review (end of Sprint) ↓ Product Increment ↓ Sprint Retrospective ↓ Continuous Improvement \u0026amp; Next Sprint Planning Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? Work on tasks Design the best solution of the task Fix program issues Conduct unit tests Collaborate within the team Work with external team For example, I am responsible for designing and implementing the real-time data processing pipeline and integrating it with the frontend dashboard. High-level System Structure TechStack Java Version: Java 11 Spring Boot: Version 2.5 Databases: PostgreSQL for transactional data, MongoDB for NoSQL needs AWS (Cloud): EC2, RDS, S3, MSK (Managed Streaming for Apache Kafka), ElastiCache (Redis) Frontend Framework: Angular 11 Testing Frameworks: JUnit 5, Mockito Environments Development (Dev): Where initial development and testing occur. Testing (Test): Used by QA for functional and integration testing. Staging: Pre-production environment where final testing is conducted to mimic the production setup. Production: Live environment where the application is used by internal users. Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Deployment: The project is deployed on AWS. CI/CD Pipelines: Jenkins is used for continuous integration and continuous deployment, automating the build, test, and deployment processes across all environments. Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database +-------------------+ +-------------------+ +-------------------+ | | | | | | | API Gateway | \u0026lt;---\u0026gt; | Authentication | \u0026lt;---\u0026gt; | User Service | | | | Service (Spring | | (Spring Boot) | | | | Boot + Spring | | | +-------------------+ | Security) | +-------------------+ | +-------------------+ | | / \\ | / \\ | / \\ +-------------------+ +-------------------+ +-------------------+ | | | | | | | Activity | \u0026lt;---\u0026gt; | Messaging | \u0026lt;---\u0026gt; | Data Processing | | Service | | Service | | Service | | (Spring Boot) | | (Kafka) | | (Spring Boot) | | | | | | | +-------------------+ +-------------------+ +-------------------+ | | | | v v +-------------------+ +-------------------+ +-------------------+ | | | | | | | PostgreSQL | | Redis (Elasti- | | MongoDB | | (RDS) | | Cache) | | | | | | | | | +-------------------+ +-------------------+ +-------------------+ | | v +-------------------+ | | | S3 (Data Storage)| | | +-------------------+ One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. One Specific Functionality. Functionality: Real-Time User Activity Logging and Analysis Detailed Breakdown: API Endpoint: Log User Activity Method: POST URI: /api/v1/activity/log Payload: { \u0026#34;userId\u0026#34;: \u0026#34;12345\u0026#34;, \u0026#34;activityType\u0026#34;: \u0026#34;SEARCH\u0026#34;, \u0026#34;timestamp\u0026#34;: \u0026#34;2024-06-21T10:15:30Z\u0026#34;, \u0026#34;details\u0026#34;: { \u0026#34;query\u0026#34;: \u0026#34;laptop\u0026#34;, \u0026#34;resultsCount\u0026#34;: 150 } } Service Layers: API Gateway: Receives the API request and forwards it to the Authentication Service. Authentication Service: Verifies the user\u0026rsquo;s identity using JWT tokens. Activity Service: Handles the logging of user activity. REST Communication: The API Gateway communicates with the Activity Service via REST. Internal Communication: Activity Service: Receives the payload and sends it to the Messaging Service (Kafka) for real-time processing. Messaging Service: Publishes the message to the relevant Kafka topic. Data Processing Service: Subscribes to the Kafka topic, processes the message, and stores the processed data. Storage: Transactional Data: Stored in PostgreSQL (RDS) for quick access and querying. Logs and Historical Data: Stored in MongoDB for flexible querying and analysis. Raw Data: Archived in S3 for long-term storage and potential future analysis. Challenge and Solutions: Real-Time Data Processing: Ensuring low latency in processing high volumes of data. Solved by using Kafka for efficient message streaming and processing. Data Consistency: Maintaining consistency between different databases (PostgreSQL and MongoDB). Used transaction management in Spring and eventual consistency principles. Scalability: Handling increased load as user activity grows. Utilized AWS auto-scaling features for EC2 instances and optimized database queries. Security: Ensuring data security during transit and storage. Implemented encryption (SSL/TLS) and AWS KMS for data at rest. Project 2: Lewis Energy Group - Safety Incidents Management # Company Info. Lewis Energy is a natural gas drilling company. Project Name. Why the project exists? What\u0026rsquo;s the main goal? Users. The project is used by whom? How many users approximately? What\u0026rsquo;s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) 1 Product Manager + Scrum Master 1 BA 1 Project Manger + BE TL BE 1 TL + 5 Dev FE 1 TL + 2 Dev QA 1 TL + 1 QA DBA 2 DBA DevOps 1 DevOps What\u0026rsquo;s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective The PM/SM is responsible for creating and maintaining the Product Backlog, a prioritized list of features, enhancements, and bug fixes required for the product. Product Backlog ↓ Sprint Planning ↓ Sprint Backlog ↓ Sprint Execution (2-4 weeks) ↓ ↓ ↓ Daily Scrum Development Task Updates ↓ ↓ ↓ Sprint Review (end of Sprint) ↓ Product Increment ↓ Sprint Retrospective ↓ Continuous Improvement \u0026amp; Next Sprint Planning Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? I was in BE team. Write clean, maintainable, and efficient code using Spring Boot, Hibernate, and other back-end technologies. High-level System Structure TechStack Java version, SpringBoot, REST, What kind of DB, AWS(Cloud), React/Angular, Junit, Mockito Environments Dev, Test, Staging, Production Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. One Specific Functionality. Project 3: Citi Bank - Tele Processing System # Company Info. It\u0026rsquo;s a banking company. Project Name. Why the project exists? What\u0026rsquo;s the main goal? Users. The project is used by whom? How many users approximately? What\u0026rsquo;s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) 1 Product Manager + Scrum Master 1 BA 1 Project Manger + BE TL BE 1 TL + 5 Dev FE 1 TL + 2 Dev QA 1 TL + 1 QA DBA 2 DBA DevOps 1 DevOps What\u0026rsquo;s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective The PM/SM is responsible for creating and maintaining the Product Backlog, a prioritized list of features, enhancements, and bug fixes required for the product. Product Backlog ↓ Sprint Planning ↓ Sprint Backlog ↓ Sprint Execution (2-4 weeks) ↓ ↓ ↓ Daily Scrum Development Task Updates ↓ ↓ ↓ Sprint Review (end of Sprint) ↓ Product Increment ↓ Sprint Retrospective ↓ Continuous Improvement \u0026amp; Next Sprint Planning Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? I was in BE team. Write clean, maintainable, and efficient code using Spring Boot, Hibernate, and other back-end technologies. High-level System Structure TechStack Java version, SpringBoot, REST, What kind of DB, AWS(Cloud), React/Angular, Junit, Mockito Environments Dev, Test, Staging, Production Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. One Specific Functionality. Sure! Here\u0026#39;s a plain English explanation of the project description: The Tele Processing System is a tool used by Citi Private Banking to help clients move their money. It allows clients to transfer money from their Citi Private Banking accounts to other accounts outside Citi, as well as between their own accounts within Citi Private Banking. To make these transfers happen, the Tele Processing System works with several other systems, including: Flex Cube: A core banking system. CAS: Likely a system for client account services. CitiFT: Possibly a funds transfer system. CitiTracs: Likely a transaction tracking system. CitiSwitch: Possibly a payment processing system. SEI: Could be a system for investment management. Project One: Likely an internal project or system. Document management system: Used for handling and storing documents related to the transfers. In short, the Tele Processing System connects with various other systems to ensure that money transfers are done smoothly and efficiently for Citi Private Banking clients. Project 4: Beijing Jishuitan Hospital - Drug Data System # Company Info. It\u0026rsquo;s a public hospital in Beijing. Project Name. Why the project exists? What\u0026rsquo;s the main goal? The hospital was planning to modernize and upgrade the current drug data system. The hospital used to have separate data system for both chinese pharmacy and western pharmacy. The goal of this project is to build a brand new drug data system that integrate both chinese pharmacy and western pharmacy. And do the data migration from the old system to the new system. Users. The project is used by whom? How many users approximately? The project is used by both internal user and external user. There are about more than 2,000 internal users, including doctors and nurses. For the external users, there are about 80,000 patients per year. What\u0026rsquo;s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) 1 Product Manager + Scrum Master 1 BA 1 Project Manger + BE TL BE 1 TL + 5 Dev FE 1 TL + 2 Dev QA 1 TL + 1 QA DBA 2 DBA DevOps 1 DevOps What\u0026rsquo;s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective The PM/SM is responsible for creating and maintaining the Product Backlog, a prioritized list of features, enhancements, and bug fixes required for the product. Product Backlog ↓ Sprint Planning ↓ Sprint Backlog ↓ Sprint Execution (2-4 weeks) ↓ ↓ ↓ Daily Scrum Development Task Updates ↓ ↓ ↓ Sprint Review (end of Sprint) ↓ Product Increment ↓ Sprint Retrospective ↓ Continuous Improvement \u0026amp; Next Sprint Planning Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? I was in BE team. Write clean, maintainable, and efficient code using Spring Boot, Hibernate, and other back-end technologies. High-level System Structure TechStack Java version, SpringBoot, REST, What kind of DB, AWS(Cloud), React/Angular, Junit, Mockito Environments Dev, Test, Staging, Production Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. One Specific Functionality. ","date":"22 June 2024","externalUrl":null,"permalink":"/notes/interview-projects/","section":"Notes","summary":"Guide # Project Preparation Guide # Company Info. Brief company info. Project Name. Why the project exists? What’s the main goal? Users. The project is used by whom? How many users approximately? What’s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) What’s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? High-level System Structure TechStack Environments Cloud/Platform Architecture One Specific Functionality. High-Level System Structure Guide # TechStack Java version, SpringBoot, REST, What kind of DB, AWS(Cloud), React/Angular, Junit, Mockito Environments Dev, Test, Staging, Production Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. Project 1: eBay - Data Analytics Dashboard # Company Info. eBay is an e-commerce company, that connects buyers and sellers around the world. Project Name. Why the project exists? What’s the main goal? The project is used to provide comprehensive insights and analytics to various departments within eBay, such as sales, marketing, product development, and customer support. The dashboard will aggregate data from different sources, process it in real-time, and present it through interactive and customizable visualizations. Users. The project is used by whom? How many users approximately? This project is only used by internal users. There is about 3,000 users are allowed to access the dashboard with different access level. What’s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) 1 Product Manager + Scrum Master 1 BA 1 Project Manger + BE TL BE 1 TL + 7 Dev FE 1 TL + 5 Dev QA 1 TL + 4 QA DBA 2 DBA DevOps 2 DevOps What’s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective The PM/SM is responsible for creating and maintaining the Product Backlog, a prioritized list of features, enhancements, and bug fixes required for the product. One single Scrum Spring consists: Product Backlog ↓ Sprint Planning ↓ Sprint Backlog ↓ Sprint Execution (2-4 weeks) ↓ ↓ ↓ Daily Scrum Development Task Updates ↓ ↓ ↓ Sprint Review (end of Sprint) ↓ Product Increment ↓ Sprint Retrospective ↓ Continuous Improvement \u0026 Next Sprint Planning Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? Work on tasks Design the best solution of the task Fix program issues Conduct unit tests Collaborate within the team Work with external team For example, I am responsible for designing and implementing the real-time data processing pipeline and integrating it with the frontend dashboard. High-level System Structure TechStack Java Version: Java 11 Spring Boot: Version 2.5 Databases: PostgreSQL for transactional data, MongoDB for NoSQL needs AWS (Cloud): EC2, RDS, S3, MSK (Managed Streaming for Apache Kafka), ElastiCache (Redis) Frontend Framework: Angular 11 Testing Frameworks: JUnit 5, Mockito Environments Development (Dev): Where initial development and testing occur. Testing (Test): Used by QA for functional and integration testing. Staging: Pre-production environment where final testing is conducted to mimic the production setup. Production: Live environment where the application is used by internal users. Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Deployment: The project is deployed on AWS. CI/CD Pipelines: Jenkins is used for continuous integration and continuous deployment, automating the build, test, and deployment processes across all environments. Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database +-------------------+ +-------------------+ +-------------------+ | | | | | | | API Gateway | \u003c---\u003e | Authentication | \u003c---\u003e | User Service | | | | Service (Spring | | (Spring Boot) | | | | Boot + Spring | | | +-------------------+ | Security) | +-------------------+ | +-------------------+ | | / \\ | / \\ | / \\ +-------------------+ +-------------------+ +-------------------+ | | | | | | | Activity | \u003c---\u003e | Messaging | \u003c---\u003e | Data Processing | | Service | | Service | | Service | | (Spring Boot) | | (Kafka) | | (Spring Boot) | | | | | | | +-------------------+ +-------------------+ +-------------------+ | | | | v v +-------------------+ +-------------------+ +-------------------+ | | | | | | | PostgreSQL | | Redis (Elasti- | | MongoDB | | (RDS) | | Cache) | | | | | | | | | +-------------------+ +-------------------+ +-------------------+ | | v +-------------------+ | | | S3 (Data Storage)| | | +-------------------+ One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. One Specific Functionality. Functionality: Real-Time User Activity Logging and Analysis Detailed Breakdown: API Endpoint: Log User Activity Method: POST URI: /api/v1/activity/log Payload: { \"userId\": \"12345\", \"activityType\": \"SEARCH\", \"timestamp\": \"2024-06-21T10:15:30Z\", \"details\": { \"query\": \"laptop\", \"resultsCount\": 150 } } Service Layers: API Gateway: Receives the API request and forwards it to the Authentication Service. Authentication Service: Verifies the user’s identity using JWT tokens. Activity Service: Handles the logging of user activity. REST Communication: The API Gateway communicates with the Activity Service via REST. Internal Communication: Activity Service: Receives the payload and sends it to the Messaging Service (Kafka) for real-time processing. Messaging Service: Publishes the message to the relevant Kafka topic. Data Processing Service: Subscribes to the Kafka topic, processes the message, and stores the processed data. Storage: Transactional Data: Stored in PostgreSQL (RDS) for quick access and querying. Logs and Historical Data: Stored in MongoDB for flexible querying and analysis. Raw Data: Archived in S3 for long-term storage and potential future analysis. Challenge and Solutions: Real-Time Data Processing: Ensuring low latency in processing high volumes of data. Solved by using Kafka for efficient message streaming and processing. Data Consistency: Maintaining consistency between different databases (PostgreSQL and MongoDB). Used transaction management in Spring and eventual consistency principles. Scalability: Handling increased load as user activity grows. Utilized AWS auto-scaling features for EC2 instances and optimized database queries. Security: Ensuring data security during transit and storage. Implemented encryption (SSL/TLS) and AWS KMS for data at rest. Project 2: Lewis Energy Group - Safety Incidents Management # Company Info. Lewis Energy is a natural gas drilling company. Project Name. Why the project exists? What’s the main goal? Users. The project is used by whom? How many users approximately? What’s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) 1 Product Manager + Scrum Master 1 BA 1 Project Manger + BE TL BE 1 TL + 5 Dev FE 1 TL + 2 Dev QA 1 TL + 1 QA DBA 2 DBA DevOps 1 DevOps What’s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective The PM/SM is responsible for creating and maintaining the Product Backlog, a prioritized list of features, enhancements, and bug fixes required for the product. Product Backlog ↓ Sprint Planning ↓ Sprint Backlog ↓ Sprint Execution (2-4 weeks) ↓ ↓ ↓ Daily Scrum Development Task Updates ↓ ↓ ↓ Sprint Review (end of Sprint) ↓ Product Increment ↓ Sprint Retrospective ↓ Continuous Improvement \u0026 Next Sprint Planning Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? I was in BE team. Write clean, maintainable, and efficient code using Spring Boot, Hibernate, and other back-end technologies. High-level System Structure TechStack Java version, SpringBoot, REST, What kind of DB, AWS(Cloud), React/Angular, Junit, Mockito Environments Dev, Test, Staging, Production Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. One Specific Functionality. Project 3: Citi Bank - Tele Processing System # Company Info. It’s a banking company. Project Name. Why the project exists? What’s the main goal? Users. The project is used by whom? How many users approximately? What’s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) 1 Product Manager + Scrum Master 1 BA 1 Project Manger + BE TL BE 1 TL + 5 Dev FE 1 TL + 2 Dev QA 1 TL + 1 QA DBA 2 DBA DevOps 1 DevOps What’s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective The PM/SM is responsible for creating and maintaining the Product Backlog, a prioritized list of features, enhancements, and bug fixes required for the product. Product Backlog ↓ Sprint Planning ↓ Sprint Backlog ↓ Sprint Execution (2-4 weeks) ↓ ↓ ↓ Daily Scrum Development Task Updates ↓ ↓ ↓ Sprint Review (end of Sprint) ↓ Product Increment ↓ Sprint Retrospective ↓ Continuous Improvement \u0026 Next Sprint Planning Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? I was in BE team. Write clean, maintainable, and efficient code using Spring Boot, Hibernate, and other back-end technologies. High-level System Structure TechStack Java version, SpringBoot, REST, What kind of DB, AWS(Cloud), React/Angular, Junit, Mockito Environments Dev, Test, Staging, Production Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. One Specific Functionality. Sure! Here's a plain English explanation of the project description: The Tele Processing System is a tool used by Citi Private Banking to help clients move their money. It allows clients to transfer money from their Citi Private Banking accounts to other accounts outside Citi, as well as between their own accounts within Citi Private Banking. To make these transfers happen, the Tele Processing System works with several other systems, including: Flex Cube: A core banking system. CAS: Likely a system for client account services. CitiFT: Possibly a funds transfer system. CitiTracs: Likely a transaction tracking system. CitiSwitch: Possibly a payment processing system. SEI: Could be a system for investment management. Project One: Likely an internal project or system. Document management system: Used for handling and storing documents related to the transfers. In short, the Tele Processing System connects with various other systems to ensure that money transfers are done smoothly and efficiently for Citi Private Banking clients. Project 4: Beijing Jishuitan Hospital - Drug Data System # Company Info. It’s a public hospital in Beijing. Project Name. Why the project exists? What’s the main goal? The hospital was planning to modernize and upgrade the current drug data system. The hospital used to have separate data system for both chinese pharmacy and western pharmacy. The goal of this project is to build a brand new drug data system that integrate both chinese pharmacy and western pharmacy. And do the data migration from the old system to the new system. Users. The project is used by whom? How many users approximately? The project is used by both internal user and external user. There are about more than 2,000 internal users, including doctors and nurses. For the external users, there are about 80,000 patients per year. What’s the team structure. Agile Team (Scrum Master, Product Owner, Manager, Team Lead, Developers, QA, BA, DBA, DevOps) 1 Product Manager + Scrum Master 1 BA 1 Project Manger + BE TL BE 1 TL + 5 Dev FE 1 TL + 2 Dev QA 1 TL + 1 QA DBA 2 DBA DevOps 1 DevOps What’s agile scrum flow? Daily meetings, Jira, Planning meeting, demo, review, retrospective The PM/SM is responsible for creating and maintaining the Product Backlog, a prioritized list of features, enhancements, and bug fixes required for the product. Product Backlog ↓ Sprint Planning ↓ Sprint Backlog ↓ Sprint Execution (2-4 weeks) ↓ ↓ ↓ Daily Scrum Development Task Updates ↓ ↓ ↓ Sprint Review (end of Sprint) ↓ Product Increment ↓ Sprint Retrospective ↓ Continuous Improvement \u0026 Next Sprint Planning Your Main responsibilities. Design, Implement, Test XXX functions, Backend, DB, Frontend? I was in BE team. Write clean, maintainable, and efficient code using Spring Boot, Hibernate, and other back-end technologies. High-level System Structure TechStack Java version, SpringBoot, REST, What kind of DB, AWS(Cloud), React/Angular, Junit, Mockito Environments Dev, Test, Staging, Production Cloud/Platform Project deployed in AWS or On-premises? CI/CD pipelines? Architecture Draw a chart. Consists of: API Gateway, Service A/B/C, Messaging among services, Database One Specific Functionality As detailed as possible. One or two APIs in detail(method, uri, payload). Service layers, which services are called? how the services communicates(REST, Messaging), where the data is stored(SQL Database, NoSQL, S3 bucket). Chanllenges. One Specific Functionality. ","title":"Interview Projects","type":"notes"},{"content":" Questions # Important # Introduce your self and the previous project in 2 to 3 minutes.\nHi, thanks for having me today. My name is Austin Wang. I a professional Java developer with over 6 years experience. Within my past working experience, all the projects I have are Web-Application. So I am very proficient in back-end programming with Java and Spring Framework. and also many other web technologies. like database including the relational and non-relational ones. the messaging queue systems like Kafka the ORM(Object-relational mapping) frameworks including hibernate and Spring Data JPA, and so on. I also have experience in developing front-end using Angular. I am an AWS-certified solution architect with real hands-on experience. The most recent project I am currently working on is called Data Analytics Dashboard System at Shopify company. This project is only designed for internal users only. They will use this system provide analytics and some comprehensive insights to various departments like sales department, customer support department, and so on. The Data Analytics Dashboard System also supports Role based access control for different users. With those knowledge and experience I can work on any complex and sophisticated project for the company. Thank you! Describe a specific function/feature you did in last project. As detailed as possible.\nThe most recent project is called Data Analytics Dashboard at eBay. There is a Feature I worked on is called Real-Time User Activity Dashboard. This feature is designed to provide insights into user activity on the platform. It helps them understand user behavior, identify trends, and make data-driven decisions. Details: Gather Requirements: I collaborated with BA to collect specific needs and the kind of metrics they wanted to track, and how they wanna define the metrics. Key Metrics: We identified key metrics such as user login frequency, page views, search queries, purchase behavior, and real-time user location data. For example, the metric to define new users including two parts: the brand new user should be categorize new users for sure, and also some old users that haven\u0026rsquo;t use eBay more than 1 year we count these users as new users as well. And so on so forth, all those kind of metrics. Design Phase: I designed a microservice architecture using Spring Boot for the backend services, Kafka for real-time data streaming, and Redis for caching frequently accessed data. Data Flow: The data flow involved collecting raw user activity data, processing it in real-time, and storing it in a format suitable for quick retrieval and visualization. Implementation: Data Collection Service: Developed a Spring Boot service to collect raw user activity data from various sources (e.g., web logs, API calls). Integrated Kafka producers to publish this raw data to different Kafka topics based on activity types. Data Processing Service: Created Kafka consumers using Spring Kafka to read the data from Kafka topics. Implemented data transformation and enrichment logic to normalize and augment the data with additional information (e.g., user demographics, geolocation). Used Redis to cache frequently accessed metrics and intermediate results to reduce latency. Data Storage: Stored the processed data in PostgreSQL for relational queries and historical analysis. Utilized MongoDB to store unstructured data and support flexible querying for real-time dashboards. API Development: RESTful APIs: Developed RESTful APIs using Spring Boot to expose the processed data to the frontend application. Endpoints: Created endpoints for fetching aggregated metrics, detailed activity logs, and real-time updates. Documentation: Documented the APIs using Swagger to ensure clarity and ease of use for frontend developers. How did you agile in your team? Typical Day?\nWe follow the Agile Scrum framework, conducting regular meetings such as daily stand-ups, spring planning, sprint reviews, and retrospectives. With these practices ensured continuous improvement and allowed us to quickly adapt to any change on the business requirement. For each of typical day, the scrum master will hold a stand-up meeting in the morning about 15 minutes to discuss what we have done yesterday and what we should do in the current day. Get task from Jira System, and work on it then update the Jira system. Just Coding. Following Standard. Bring new ideas. Learn and self improve. What is the size of team? BA, DBA, QA, TL(team lead)?\nOur team has 25 members. 1 Product Manager + Scrum Master 1 BA 1 Project Manger + BE TL BE 1 TL + 7 Dev FE 1 TL + 5 Dev QA 1 TL + 4 QA DBA 2 DBA DevOps 2 DevOps One Business Analyst who gathers requirements from stakeholders and ensures they are clearly communicated to the team. Two Database Administrator who manages the database system. Five Quality Assurance Engineers who are responsible for testing. One Team Lead conducts code review, provides technical guidance, mentors team members, and coordinates with other teams. The core of our team has 7 Developers who write and maintain the code, implement new features, and fix bugs. Most challenging/proud task.\nWhat is the Deployment process? How do you release? How many environment do you have?\nEnvironments: Development Environment: Purpose: Used by developers to build and test new features and bug fixes. Tools: Local development environments, Docker containers, and possibly a shared development server. Staging Environment: Purpose: Acts as a pre-production environment to test the integration of new features and ensure they work correctly before going live. It mirrors the production environment closely. Tools: AWS EC2 instances, Kubernetes (EKS), RDS, etc. Production Environment: Purpose: The live environment where the application is available to internal users. Tools: AWS EC2 instances, RDS, ElastiCache, MSK, CloudFront, etc. Deployment Process: Code Commit and Version Control: Git: Developers commit their code changes to a shared repository using Git. Branching Strategy: Utilize a branching strategy like Gitflow to manage feature development, releases, and hotfixes. Continuous Integration (CI): Jenkins Pipeline: Triggers: The CI pipeline is triggered automatically upon code commit or pull request. Steps: Build: Compile the code and resolve dependencies. Unit Tests: Run unit tests using JUnit to ensure code quality and functionality. Static Code Analysis: Use SonarQube to analyze code quality and identify potential issues. Artifact Storage: Docker Hub/Amazon ECR: Build Docker images and store them in a container registry like Docker Hub or Amazon Elastic Container Registry (ECR). Continuous Deployment (CD): Jenkins/CD Pipeline: Triggers: The CD pipeline is triggered upon successful completion of the CI pipeline. Steps: Deploy to Development: Docker Compose: Use Docker Compose to deploy the application in the development environment. Automated Tests: Run integration tests and functional tests. Deploy to Staging: Kubernetes: Use Helm charts to deploy Docker containers to the Kubernetes cluster in the staging environment. Manual Testing: Perform manual testing and user acceptance testing (UAT). Load Testing: Conduct load and performance testing. Approval Process: Obtain approval from QA and stakeholders before deploying to production. Release to Production: Blue-Green Deployment/Canary Release: Blue-Green Deployment: Deploy the new version of the application in parallel with the old version, then switch traffic to the new version once it\u0026rsquo;s verified to be stable. Canary Release: Gradually roll out the new version to a small subset of users before fully deploying it. Kubernetes: Use Helm charts to deploy Docker containers to the Kubernetes cluster in the production environment. Post-Deployment Monitoring: Monitor application performance and logs using AWS CloudWatch and Prometheus to ensure the new release is functioning as expected. Rollback Strategy: Automated Rollback: Implement automated rollback mechanisms in the Jenkins pipeline to revert to the previous stable version in case of issues. Manual Rollback: Have procedures in place for manual rollback if automated rollback is not feasible. Do you have front-end experience?\nYes, I do have front-end experience. With small part of time I would do DOM manipulation with vanilla Javascript or with jQuery. Most of time I just use Angular framework. Over the course of my career, I have worked on various projects where I was responsible for designing and implementing user interfaces that are not only visually appealing but also highly functional and responsive. Do you know Cloud/AWS? Where/what do you use?\nI am AWS certified solution architect. I also have hands-on experience in AWS provisioning for my previous projects including EC2, S3, RDS, SNS, SQS, etc. For example, in my the most recent project at eBay which is Data Analytics Dashboard. I use EC2 to host microservices I built. I also use Amazon RDS to host PostgreSQL database. And MSK(Managed Kafka) service for real-time data streaming. And so on. Set up VPCs to create isolated network environments, configured subnets, route tables, and NAT gateways. Used S3 for storing and retrieving large datasets, including log files and user-generated content. Configured bucket policies and lifecycle rules for data management. Configured CloudWatch for monitoring application performance, setting up alarms, and logging. Integrated CloudWatch with Prometheus for enhanced monitoring. Normal # What\u0026rsquo;s your strong/weak point? Strong point: I am very strict with my time management. For instance, I like to set reasonable deadline for each task. The deadline I set can give me a sense of urgency. And I believe that a sense of urgency makes me more efficient. Week point: I will get nervous if I have to give a speak in a big meeting or conference in front of a lot of people. Version control. How did you use it? SVN? GIT? Branching, CheckIn-CheckOut. In my projects, I\u0026rsquo;ve primarily used Git for version control. Our team followed a branching strategy similar to Gitflow. We had separate branches for feature development, bug fixes, and releases. Typically, we would: Branching: Create feature branches from the develop branch. CheckIn-CheckOut: Regularly commit changes locally and push them to the remote repository. We also performed code reviews using pull requests before merging changes back into the develop branch. Merging: Once features were tested, they were merged into the develop branch, and periodically we would merge develop into main for a release. SVN Experience: Although I primarily used Git, I have experience with SVN from earlier projects where we used a centralized approach to version control, focusing on check-in/check-out mechanisms and resolving conflicts during updates. Did you do production support. Yes, I have been involved in production support. My responsibilities included monitoring application performance, handling incidents, debugging production issues, and ensuring high availability. We used tools like AWS CloudWatch and Prometheus for monitoring and alerting, and I was part of the on-call rotation to address critical issues promptly. What will you do if you don\u0026rsquo;t like the design from other team member? Conflict? Which project is your favorite? If I don\u0026rsquo;t agree with a design from a team member, I would: Discuss: Arrange a meeting to discuss the design and understand the reasoning behind their choices. Collaborate: Provide constructive feedback and suggest alternatives while keeping an open mind. Consensus: Aim to reach a consensus that benefits the project as a whole. If needed, involve a neutral third party, like a team lead or architect, to mediate. Favorite Project: My favorite project was the Real-Time User Activity Dashboard at eBay. It was complex, involved cutting-edge technology, and had a significant impact on the business. How did you use spring mvc/ws/hibernate/transaction/all technologies in your resume in detail with examples of your project Spring MVC: Used for building RESTful APIs to serve the real-time analytics data to the frontend. Spring WS: Developed SOAP-based web services for internal communication between legacy systems and new microservices. Hibernate: Utilized for ORM to map Java objects to database tables and manage CRUD operations efficiently. Transactions: Managed transactions using Spring\u0026rsquo;s @Transactional annotation to ensure data integrity during complex operations. Example: \u0026ldquo;In the eBay project, I used Spring MVC to create endpoints for the analytics dashboard. Spring WS facilitated integration with legacy systems, enabling seamless data exchange. Hibernate managed data persistence in PostgreSQL, and Spring’s transaction management ensured data consistency during multi-step operations, like processing and updating user activity logs. How many tables you worked on, how much traffic, how many users/clients In the eBay Real-Time User Activity Dashboard project: Tables: Worked on around 50 tables, including complex joins and indexing strategies for performance. Traffic: Handled data streams of several thousand events per second using Kafka. Users: Supported hundreds of internal users, including analysts and executives, who accessed the dashboard for insights. How did you use JIRA/jenkins? JIRA: Used for task management, sprint planning, and tracking progress. Created user stories, logged bugs, and followed Agile methodologies. Jenkins: Set up CI/CD pipelines to automate builds, run tests, and deploy applications. Configured jobs for different environments and integrated with GitHub for automatic triggers on code commits. Do you write document? What kind of document? Swagger? Yes, I write various types of documentation: API Documentation: Used Swagger to document RESTful APIs, making it easy for frontend developers to understand and consume the services. Technical Documentation: Detailed system architecture, data flow diagrams, and design decisions. User Guides: Instructions for internal users on how to use the analytics dashboard. What\u0026rsquo;s the name of your manager/team lead/architect? My project manager was [Josh Lee]. The Backend team lead is [Agrima Jindal], she also worked as an architect. Where is the project located? The project was developed and deployed within eBay\u0026rsquo;s internal infrastructure, hosted on AWS cloud services. Our development team was based in [Location, e.g., San Jose, California]. How did you do unit testing? I used JUnit for writing unit tests. Each method was tested independently, ensuring they performed as expected. Mocking frameworks like Mockito were used to simulate dependencies. We aimed for high code coverage to catch potential bugs early. How do you handle exception? How do you log? Any tools used? Exception Handling: Implemented centralized exception handling using Spring\u0026rsquo;s @ControllerAdvice and @ExceptionHandler. Custom exceptions were created for specific error scenarios. Logging: Used SLF4J with Logback for logging. Logs were structured and included context to aid debugging. Tools like ELK Stack (Elasticsearch, Logstash, Kibana) were used for log aggregation and visualization. Do you know any scripting language? Where do you use scripting? Yes, I am proficient in Python and Bash scripting. I used: Python: For data analysis, automation tasks, and creating custom scripts for data processing. Bash: For writing deployment scripts, automating server setup, and managing cron jobs. Given a task/story, what\u0026rsquo;s the steps you take to complete it? What if there are road-blockers? Understanding: Thoroughly read the task/story and clarify any ambiguities with stakeholders. Planning: Break down the task into smaller, manageable sub-tasks. Execution: Start with development, write tests, and commit changes. Testing: Run unit and integration tests to ensure functionality. Review: Submit the code for peer review and make necessary adjustments. Road-blockers: Identify the issue, research possible solutions, seek help from team members or stakeholders, and escalate to the team lead if necessary.\u0026rdquo; What will you do if you cannot finish a task on time? If I cannot finish a task on time, I would: Communicate: Inform my team lead and stakeholders about the delay as soon as possible. Assess: Analyze the reasons for the delay and identify any blockers. Plan: Create a revised plan with a new timeline and any additional resources needed. Prioritize: Focus on critical aspects and defer non-essential parts if necessary.\u0026quot; What\u0026rsquo;s your next 5 years plan? In the next 5 years, I aim to: Skill Development: Continue enhancing my technical skills, particularly in cloud computing and AI/ML. Leadership: Transition into a technical leadership role where I can mentor junior developers and lead larger projects. Innovation: Contribute to innovative projects that drive business value and leverage emerging technologies.\u0026quot; What do you know about our company? Do some research before go to a client interview. Why are you looking for a new project? I seek new challenges and opportunities to grow my skills in different domains. I also want to work on projects that have a significant impact and align with my passion for technology and innovation. I am also eager to collaborate with new teams and bring fresh perspectives to complex problems. Questions to ask at the end of an interview # At least 3 - 5 questions for the interviewer. 3 types of questions # Culture What do you love most about your job? What makes people stay with this organization? What are the biggest challenges or opportunities that this organization or department is facing in the next six months to a year? What is your favorite part about working here in this organization? How would you describe the work environment here? Role-specific Can you tell me what a typical day or week looks like in this position? What do you want the person in this position to accomplish in their first 30/60/90 days? What challenges or opportunities do you foresee this position taking on in the next six month? How will my performance be measured in this position? How long will this project last? What are the next steps in the interview process? Hesitation Do you have any hesitation about me filling this position? Based on what we\u0026rsquo;ve talked about today, is there anything that is causing you hesitation about my fit for this position How do I compare to other candidates you\u0026rsquo;ve interviewed for this role? Have I answered all the questions that you have for me? Do you have any hesitation about my qualifications? Is there anything I can clarify for you? Tips before interview # Prepare your phone/monitor/laptop at least 10 mins before. Double check if it is a dial-in call or they call you on your phone or skype. Dress code: dress as professional as you can. DO research on the client company. Keep checking your hangout/email. Always ask questions at the end of the interview. Take it seriously and be confident Always Turn on the camera. Make the background clean. You don\u0026rsquo;t need to answer all the questions correctly to get the job. ","date":"20 June 2024","externalUrl":null,"permalink":"/notes/interview-questions/","section":"Notes","summary":"Questions # Important # Introduce your self and the previous project in 2 to 3 minutes.\nHi, thanks for having me today. My name is Austin Wang. I a professional Java developer with over 6 years experience. Within my past working experience, all the projects I have are Web-Application. So I am very proficient in back-end programming with Java and Spring Framework. and also many other web technologies. like database including the relational and non-relational ones. the messaging queue systems like Kafka the ORM(Object-relational mapping) frameworks including hibernate and Spring Data JPA, and so on. I also have experience in developing front-end using Angular. I am an AWS-certified solution architect with real hands-on experience. The most recent project I am currently working on is called Data Analytics Dashboard System at Shopify company. This project is only designed for internal users only. They will use this system provide analytics and some comprehensive insights to various departments like sales department, customer support department, and so on. The Data Analytics Dashboard System also supports Role based access control for different users. With those knowledge and experience I can work on any complex and sophisticated project for the company. Thank you! Describe a specific function/feature you did in last project. As detailed as possible.\n","title":"Interview Questions","type":"notes"},{"content":" Descriptions (3-5 sentences, 30 sec, industry, admin/internal/external/hybrid) # It\u0026rsquo;s an E-commerce web app where users can browse products, add items to a cart, place orders, and make payments. It supports Role Based Access Control(RBAC) for both internal users to update products price or quantity and external users to browse, search and buy products. Data Flow (frontend data, what kind of payload, what kind of format, design data flow: FE-\u0026gt;BE-\u0026gt;DB, then we have architecture, then in the middle: add buffering or caching, just add different tools to handle different problem, i.e. failure tolerance, scalability, \u0026hellip;.) # DB\nInternal User Table (userID, name) External User Table (userID, name) Product Inventory Table (productUID, description, price, quantity) Cart Table (userID, productUID, price, quantity) Order Table (orderID, userID, productID, quantity, price, amount) Credit card Table (userID, card number, billing address, expiration date, cvv) FE(internal): add product image, product descriptions, price and quantity.\ndata(out): product info (image + text + numbers) BE(internal): add product image to MongoDB, add product meta data into PostgresSQL\nFE(internal): fetch inventory data and manage inventories(delete or update product)\ndata(in): List of products (list of image + text + numbers) data(out): productUID (numbers) FE(external): fetch data from the backend\ndata(in): List of (image + text + numbers) FE(external): add products into cart, remove products from cart, update product quantity\ndata(out): cart table (userID, productUID) FE(external): make order, make payment, receive notification about the order status\ndata(out): List of orderItem: product idx, price, quantity(numbers) credit card info \u0026amp;\u0026amp; save credit card info?(numbers, date, text, boolean) addresses(text) data(in): order status: successful or failed (numbers) BE(external):\ndata(in): List of orderItem: product idx, price, quantity(numbers) credit card info(numbers, date, text) addresses(text) FE(external):\ndata(in \u0026amp;\u0026amp; out): order status (number) BE(external):\ndata(int \u0026amp;\u0026amp; out): order status (number) Tech stack(???pros \u0026amp; cons) # Frontend # Angular 14.2.5 Typescript 4.7 for optimal compatibility and features Backend # Java 17 SpringBoot 2.7.5 Spring Security 5.7.5 Spring Data JPA 2.7.5 Spring Data MongoDB 3.4.5 Spring Data Redis 2.7.5 Kafka Spring Kafka 2.8.8 Kafka Server 2.8.0 PostgreSQL JDBC Driver: 42.4.2 Database Server 14.5 MongoDB Java Driver 4.7.1 Database Server 5.0 Redis Spring Data Redis 2.7.5 Redis Server 6.2.6 Swagger Springfox Swagger 3.0.0 CI/CD and Testing Tools # Jenkins 2.346.1 (For build pipeline) SonarQube (For code quality analysis) Server Version 9.4 Scanner Version 4.6.2 Docker 20.10.7 (For building container images) Kubernetes 1.24 (For deploying and managing microservices) JUnit 5.8.2 Selenium Selenium WebDriver 4.4.0 Browser Driver(e.g. ChromeDriver) 104.0 Cloud and Deployment # AWS (ECS, EKS) Docker 20.10.7 (For containerization) Kubernetes 1.24 (For orchestration) Additional Tools and Libraries # JWT, Library jjwt 0.11.5 BCrypt, Library spring-security-crypto 5.7.5 Lombok, 1.18.22 On-premise or Cloud # AWS ??? Teams ( people we worked with, Agile style/how big the team/what\u0026rsquo;s my role/different scenario we need to talk to different people, who I talked with/ who I suppose to talk with/\u0026hellip; ) # Distributed # \u0026ldquo;distributed\u0026rdquo; refers to the architectural approach of breaking down the application into smaller, independent services that communicate with each other over a network. Pros # Scalability: Distributed systems can scale horizontally by adding more instances of individual services rather than scaling up a monolithic application. Fault Isolation: Failures in one microservice are isolated, preventing cascading failures across the entire system. Flexibility: Teams can choose the most appropriate technology stack for each microservice, optimizing performance and developer productivity. Improved Maintainability: Smaller, focused codebases are easier to maintain and update compared to a large monolithic application. Cons # Increased Complexity: Managing multiple microservices introduces complexity in terms of deployment, monitoring, and debugging. Consistency: Ensuring data consistency and transaction management across distributed services can be challenging. Network Overhead: Communication between microservices over a network can introduce latency and require robust error handling. Example scenario in this project # The User Service handles user authentication and profile management. The Product Catalog Service manages product information and inventory. The Order Service processes customer orders and manages order fulfillment. The Payment Service handles payment processing and integrates with payment gateways. The Notification Service sends notifications to users about order status updates. Each of these services operates independently but collaborates through well-defined APIs, enabling the platform to deliver a seamless e-commerce experience while benefiting from the scalability and resilience of a distributed architecture. Overall, in this project, \u0026ldquo;distributed\u0026rdquo; signifies the adoption of a microservices architecture to achieve modularity, scalability, and maintainability in building and operating the e-commerce platform.\nProject: Distributed E-commerce Platform # Project Overview: # Develop a distributed e-commerce platform where users can browse products, add items to a cart, place orders, and make payments. The platform should also include user authentication and authorization, real-time updates, and analytics.\nArchitecture and Technologies: # Microservices Architecture: Use Spring Boot to create independent services for different functionalities. Message Queuing: Use Kafka for communication between services. Caching: Use Redis for caching frequently accessed data. Cloud Deployment: Deploy services on AWS. Front-End: Develop a user interface using Angular. Data Persistence: Use PostgreSQL for relational data and MongoDB for NoSQL data. API Documentation: Use Swagger for API documentation. Continuous Integration/Continuous Deployment (CI/CD): Use Jenkins for CI/CD pipeline. Security: Implement security using Spring Security. Testing: Write unit tests using JUnit, integrate SonarQube for code quality analysis, and use Selenium for end-to-end testing. Detailed Components: # User Service: Manages user registration, login, profile management. Technology: Spring Boot, Spring Security, PostgreSQL, JWT for authentication. Product Service: Manages product catalog, including CRUD operations for products. Technology: Spring Boot, MongoDB, Swagger. Order Service: Manages orders, order history, and interactions with the payment service. Technology: Spring Boot, PostgreSQL, Kafka. Cart Service: Manages user cart operations. Technology: Spring Boot, Redis. Payment Service: Manages payment processing. Technology: Spring Boot, external payment gateway integration (e.g., Stripe). Notification Service: Sends real-time notifications to users (e.g., order status updates). Technology: Spring Boot, Kafka. Analytics Service: Collects and analyzes data for business insights. Technology: Spring Boot, MongoDB. Frontend Application: User interface for the e-commerce platform. Technology: Angular, integrating with various backend services. CI/CD Pipeline: Automate testing, build, and deployment processes. Technology: Jenkins, SonarQube, Docker for containerization, AWS for deployment. Testing: Unit Testing: JUnit. End-to-End Testing: Selenium. Code Quality: SonarQube. Example Workflow: # User Registration and Authentication: User signs up and logs in via the User Service. JWT tokens are generated for authenticated sessions. Product Browsing: User browses products through the Product Service. Product data is cached using Redis for quick access. Shopping Cart: User adds products to the cart. Cart Service manages the cart with Redis to ensure fast performance. Order Placement: User places an order, which triggers the Order Service. Order details are stored in PostgreSQL, and the service sends a message via Kafka to the Payment Service. Payment Processing: Payment Service processes the payment. On successful payment, the Order Service updates the order status and sends a notification via Kafka. Real-Time Notifications: Notification Service sends real-time updates to the user about order status. Analytics: Analytics Service collects data from various services to generate business insights. Deployment: # AWS Infrastructure: Use AWS EC2 for deploying microservices. Use AWS RDS for PostgreSQL database. Use AWS S3 for static content storage. Use AWS Lambda for serverless computing where applicable. CI/CD with Jenkins: Jenkins pipelines for automated testing, building, and deployment. Integration with SonarQube for code quality analysis. Docker for containerizing microservices. ","date":"19 June 2024","externalUrl":null,"permalink":"/notes/projects-v3/","section":"Notes","summary":"Descriptions (3-5 sentences, 30 sec, industry, admin/internal/external/hybrid) # It’s an E-commerce web app where users can browse products, add items to a cart, place orders, and make payments. It supports Role Based Access Control(RBAC) for both internal users to update products price or quantity and external users to browse, search and buy products. Data Flow (frontend data, what kind of payload, what kind of format, design data flow: FE-\u003eBE-\u003eDB, then we have architecture, then in the middle: add buffering or caching, just add different tools to handle different problem, i.e. failure tolerance, scalability, ….) # DB\n","title":"Projects V3","type":"notes"},{"content":" Descriptions (3-5 sentences, 30 sec, industry, admin/internal/external/hybrid) # It\u0026rsquo;s an E-commerce web app where users can browse products, add items to a cart, place orders, and make payments. It supports Role Based Access Control(RBAC) for both internal users to update products price or quantity and external users to browse, search and buy products. Data Flow (frontend data, what kind of payload, what kind of format, design data flow: FE-\u0026gt;BE-\u0026gt;DB, then we have architecture, then in the middle: add buffering or caching, just add different tools to handle different problem, i.e. failure tolerance, scalability, \u0026hellip;.) # DB\nInternal User Table (userID, name) External User Table (userID, name) Product Inventory Table (productUID, description, price, quantity) Cart Table (userID, productUID, price, quantity) Order Table (orderID, userID, productID, quantity, price, amount) Credit card Table (userID, card number, billing address, expiration date, cvv) FE(internal): add product image, product descriptions, price and quantity.\ndata(out): product info (image + text + numbers) BE(internal): add product image to MongoDB, add product meta data into PostgresSQL\nFE(internal): fetch inventory data and manage inventories(delete or update product)\ndata(in): List of products (list of image + text + numbers) data(out): productUID (numbers) FE(external): fetch data from the backend\ndata(in): List of (image + text + numbers) FE(external): add products into cart, remove products from cart, update product quantity\ndata(out): cart table (userID, productUID) FE(external): make order, make payment, receive notification about the order status\ndata(out): List of orderItem: product idx, price, quantity(numbers) credit card info \u0026amp;\u0026amp; save credit card info?(numbers, date, text, boolean) addresses(text) data(in): order status: successful or failed (numbers) BE(external):\ndata(in): List of orderItem: product idx, price, quantity(numbers) credit card info(numbers, date, text) addresses(text) FE(external):\ndata(in \u0026amp;\u0026amp; out): order status (number) BE(external):\ndata(int \u0026amp;\u0026amp; out): order status (number) Tech stack(???pros \u0026amp; cons) # Frontend # Angular 14.2.5 Typescript 4.7 for optimal compatibility and features Backend # Java 17 SpringBoot 2.7.5 Spring Security 5.7.5 Spring Data JPA 2.7.5 Spring Data MongoDB 3.4.5 Spring Data Redis 2.7.5 Kafka Spring Kafka 2.8.8 Kafka Server 2.8.0 PostgreSQL JDBC Driver: 42.4.2 Database Server 14.5 MongoDB Java Driver 4.7.1 Database Server 5.0 Redis Spring Data Redis 2.7.5 Redis Server 6.2.6 Swagger Springfox Swagger 3.0.0 CI/CD and Testing Tools # Jenkins 2.346.1 (For build pipeline) SonarQube (For code quality analysis) Server Version 9.4 Scanner Version 4.6.2 Docker 20.10.7 (For building container images) Kubernetes 1.24 (For deploying and managing microservices) JUnit 5.8.2 Selenium Selenium WebDriver 4.4.0 Browser Driver(e.g. ChromeDriver) 104.0 Cloud and Deployment # AWS (ECS, EKS) Docker 20.10.7 (For containerization) Kubernetes 1.24 (For orchestration) Additional Tools and Libraries # JWT, Library jjwt 0.11.5 BCrypt, Library spring-security-crypto 5.7.5 Lombok, 1.18.22 On-premise or Cloud # AWS ??? Teams ( people we worked with, Agile style/how big the team/what\u0026rsquo;s my role/different scenario we need to talk to different people, who I talked with/ who I suppose to talk with/\u0026hellip; ) # Distributed # \u0026ldquo;distributed\u0026rdquo; refers to the architectural approach of breaking down the application into smaller, independent services that communicate with each other over a network. Pros # Scalability: Distributed systems can scale horizontally by adding more instances of individual services rather than scaling up a monolithic application. Fault Isolation: Failures in one microservice are isolated, preventing cascading failures across the entire system. Flexibility: Teams can choose the most appropriate technology stack for each microservice, optimizing performance and developer productivity. Improved Maintainability: Smaller, focused codebases are easier to maintain and update compared to a large monolithic application. Cons # Increased Complexity: Managing multiple microservices introduces complexity in terms of deployment, monitoring, and debugging. Consistency: Ensuring data consistency and transaction management across distributed services can be challenging. Network Overhead: Communication between microservices over a network can introduce latency and require robust error handling. Example scenario in this project # The User Service handles user authentication and profile management. The Product Catalog Service manages product information and inventory. The Order Service processes customer orders and manages order fulfillment. The Payment Service handles payment processing and integrates with payment gateways. The Notification Service sends notifications to users about order status updates. Each of these services operates independently but collaborates through well-defined APIs, enabling the platform to deliver a seamless e-commerce experience while benefiting from the scalability and resilience of a distributed architecture. Overall, in this project, \u0026ldquo;distributed\u0026rdquo; signifies the adoption of a microservices architecture to achieve modularity, scalability, and maintainability in building and operating the e-commerce platform.\nProject: Distributed E-commerce Platform # Project Overview: # Develop a distributed e-commerce platform where users can browse products, add items to a cart, place orders, and make payments. The platform should also include user authentication and authorization, real-time updates, and analytics.\nArchitecture and Technologies: # Microservices Architecture: Use Spring Boot to create independent services for different functionalities. Message Queuing: Use Kafka for communication between services. Caching: Use Redis for caching frequently accessed data. Cloud Deployment: Deploy services on AWS. Front-End: Develop a user interface using Angular. Data Persistence: Use PostgreSQL for relational data and MongoDB for NoSQL data. API Documentation: Use Swagger for API documentation. Continuous Integration/Continuous Deployment (CI/CD): Use Jenkins for CI/CD pipeline. Security: Implement security using Spring Security. Testing: Write unit tests using JUnit, integrate SonarQube for code quality analysis, and use Selenium for end-to-end testing. Detailed Components: # User Service: Manages user registration, login, profile management. Technology: Spring Boot, Spring Security, PostgreSQL, JWT for authentication. Product Service: Manages product catalog, including CRUD operations for products. Technology: Spring Boot, MongoDB, Swagger. Order Service: Manages orders, order history, and interactions with the payment service. Technology: Spring Boot, PostgreSQL, Kafka. Cart Service: Manages user cart operations. Technology: Spring Boot, Redis. Payment Service: Manages payment processing. Technology: Spring Boot, external payment gateway integration (e.g., Stripe). Notification Service: Sends real-time notifications to users (e.g., order status updates). Technology: Spring Boot, Kafka. Analytics Service: Collects and analyzes data for business insights. Technology: Spring Boot, MongoDB. Frontend Application: User interface for the e-commerce platform. Technology: Angular, integrating with various backend services. CI/CD Pipeline: Automate testing, build, and deployment processes. Technology: Jenkins, SonarQube, Docker for containerization, AWS for deployment. Testing: Unit Testing: JUnit. End-to-End Testing: Selenium. Code Quality: SonarQube. Example Workflow: # User Registration and Authentication: User signs up and logs in via the User Service. JWT tokens are generated for authenticated sessions. Product Browsing: User browses products through the Product Service. Product data is cached using Redis for quick access. Shopping Cart: User adds products to the cart. Cart Service manages the cart with Redis to ensure fast performance. Order Placement: User places an order, which triggers the Order Service. Order details are stored in PostgreSQL, and the service sends a message via Kafka to the Payment Service. Payment Processing: Payment Service processes the payment. On successful payment, the Order Service updates the order status and sends a notification via Kafka. Real-Time Notifications: Notification Service sends real-time updates to the user about order status. Analytics: Analytics Service collects data from various services to generate business insights. Deployment: # AWS Infrastructure: Use AWS EC2 for deploying microservices. Use AWS RDS for PostgreSQL database. Use AWS S3 for static content storage. Use AWS Lambda for serverless computing where applicable. CI/CD with Jenkins: Jenkins pipelines for automated testing, building, and deployment. Integration with SonarQube for code quality analysis. Docker for containerizing microservices. ","date":"18 June 2024","externalUrl":null,"permalink":"/notes/projects-v2/","section":"Notes","summary":"Descriptions (3-5 sentences, 30 sec, industry, admin/internal/external/hybrid) # It’s an E-commerce web app where users can browse products, add items to a cart, place orders, and make payments. It supports Role Based Access Control(RBAC) for both internal users to update products price or quantity and external users to browse, search and buy products. Data Flow (frontend data, what kind of payload, what kind of format, design data flow: FE-\u003eBE-\u003eDB, then we have architecture, then in the middle: add buffering or caching, just add different tools to handle different problem, i.e. failure tolerance, scalability, ….) # DB\n","title":"Projects V2","type":"notes"},{"content":" Setup # \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-data-jpa\u0026lt;/artifactId\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- for mongodb --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-data-mongodb\u0026lt;/artifactId\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- for postgresql --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.postgresql\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;postgresql\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${postgresql.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- for mysql --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;mysql\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mysql-connector-java\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;8.0.32\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; Entities # @Entity @Table(name=\u0026#34;course\u0026#34;) public class Course { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @Column private Integer credit; @OneToOne @JoinColumn(name = \u0026#34;teacher_id\u0026#34;) private Teacher teacher; @OneToMany(mappedBy = \u0026#34;course\u0026#34;) private List\u0026lt;Enrollment\u0026gt; enrollmentList; // setter and getter ... } @Entity @Table(name = \u0026#34;enrollment\u0026#34;) public class Enrollment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne private Course course; @ManyToOne private Student student; private Integer score; // setter and getter ... } @Entity @Table(name = \u0026#34;student\u0026#34;) public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private LocalDate dob; @OneToMany(mappedBy = \u0026#34;student\u0026#34;) List\u0026lt;Enrollment\u0026gt; enrollmentList; // setter and getter ... } mongodb # @Document(\u0026#34;enrollment_history\u0026#34;) public class History { @Id private String id; private String action; private String studentName; private String courseName; // setter and getter ... } MVC # package structures: configuration controller data entity mongodb History.java Course Enrollment Student Teacher repository mongodb HistoryRepository.java EnrollmentRepository.java StudentRepository.java TeacherRepository.java service EnrollmentService.java EnrollmentServiceImpl.java StudentService.java StudentServiceImpl.java TeacherService.java TeacherServiceImpl.java UserService.java UserServiceImpl.java UserServiceImpl2.java util vo SpringBootDemoApplication.java Dao layer # // package ... // import ...Enrollment; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface EnrollmentRepository extends JpaRepository\u0026lt;Enrollment, Long\u0026gt; { } // package ... // import ...Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface StudentRepository extends JpaRepository\u0026lt;Student, Long\u0026gt; { } // package ... // import ...Teacher; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface TeacherRepository extends JpaRepository\u0026lt;Teacher, Long\u0026gt; { // select * from teachers where age \u0026lt; 40 and id \u0026gt; 1000; List\u0026lt;Teacher\u0026gt; findByAgeLessThan(int age); List\u0026lt;Teacher\u0026gt; findByAge(int age); // @Query(value = \u0026#34;select t from Teacher t where t.age \u0026lt; ? and t.id \u0026gt; ?\u0026#34;, nativeQuery = true) // native sql query @Query(value = \u0026#34;select t from Teacher t where t.age \u0026lt; :age and t.id \u0026gt; :id\u0026#34;) // jpql List\u0026lt;Teacher\u0026gt; a(int age, long id); } mongodb # public interface HistoryRepository extends MongoRepository\u0026lt;History, String\u0026gt; { List\u0026lt;History\u0026gt; findByAction(String action); List\u0026lt;History\u0026gt; findByCourseName(String courseName); } Test Repositories # Service layer # @Service public class EnrollmentServiceImpl implements EnrollmentService { @Autowired EnrollmentRepository enrollmentRepository; @Autowired HistoryRepository historyRepository; @Override @Transactional public Enrollment enroll(Student student, Course course) { History history = new History(); // history.set.... historyRepository.save(history); Enrollment enrollment = new Enrollment(); // enrollment.set... return enrollmentRepository.save(enrollment); } @Override public EnrollmentVO getEnrollment(Long id) { Enrollment enrollment = enrollmentRepository.findById(id).orElse(null); EnrollmentVO vo = new EnrollmentVO(); // vo.set... return vo; } @Override public List\u0026lt;Enrollment\u0026gt; getEnrollmentByCourseId(Long courseId) { Enrollment enrollmentExample = new Enrollment(); Course c = new Course(); c.setId(courseId); enrollmentExample.setCourse(c); Example\u0026lt;Enrollment\u0026gt; example = Example.of(enrollmentExample); List\u0026lt;Enrollment\u0026gt; enrollments = enrollmentRepository.findAll(example); List\u0026lt;EnrollmentVO\u0026gt; voList = new ArrayList\u0026lt;\u0026gt;(); enrollments.forEach(enrollment -\u0026gt; { EnrollmentVO vo = new EnrollmentVO(); // vo.setId(enrollment.getId()); // vo.setCourseName(enrollment.getCourse().getName()); // vo.setStudentName(enrollment.getStudent().getName()); voList.add(vo); }) return voList; } @Override public List\u0026lt;EnrollmentVO\u0026gt; getAll(int page, int rows) { Pageable pageRequest = PageRequest.of(page, rows); Page\u0026lt;Enrollment\u0026gt; p = enrollmentRepository.findAll(pageRequest); List\u0026lt;Enrollment\u0026gt; enrollments = p.getContent(); List\u0026lt;EnrollmentVO\u0026gt; voList = enrollments.stream().map(enrollment -\u0026gt; { EnrollmentVO vo = new EnrollmentVO(); // vo.set... return vo; }).toList(); return voList; } } @Service public class StudentServiceImpl implements StudentService { @Autowired StudentRepository studentRepository; @Override public List\u0026lt;StudentVO\u0026gt; getAllStudents() { return studentRepository.findAll().stream().map(entity -\u0026gt; { StudentVO temp = new StudentVO(); BeanUtils.copyProperties(entity, temp); temp.setDob(Date.valueOf(entity.getDob())); return temp; }).toList(); } } @Service @Primary public class UserServiceImpl implements UserService { public void login(String username) { System.out.println(\u0026#34;in user service, use password\u0026#34;); } } @Service public class UserServiceImpl implements UserService { public void login(String username) { System.out.println(\u0026#34;in user service2, login by fingerprint\u0026#34;); } } ","date":"1 June 2024","externalUrl":null,"permalink":"/notes/spring_data_jpa/","section":"Notes","summary":"Setup # \u003cdependency\u003e \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e \u003cartifactId\u003espring-boot-starter-data-jpa\u003c/artifactId\u003e \u003c/dependency\u003e \u003c!-- for mongodb --\u003e \u003cdependency\u003e \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e \u003cartifactId\u003espring-boot-starter-data-mongodb\u003c/artifactId\u003e \u003c/dependency\u003e \u003c!-- for postgresql --\u003e \u003cdependency\u003e \u003cgroupId\u003eorg.postgresql\u003c/groupId\u003e \u003cartifactId\u003epostgresql\u003c/artifactId\u003e \u003cversion\u003e${postgresql.version}\u003c/version\u003e \u003c/dependency\u003e \u003c!-- for mysql --\u003e \u003cdependency\u003e \u003cgroupId\u003emysql\u003c/groupId\u003e \u003cartifactId\u003emysql-connector-java\u003c/artifactId\u003e \u003cversion\u003e8.0.32\u003c/version\u003e \u003c/dependency\u003e Entities # @Entity @Table(name=\"course\") public class Course { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @Column private Integer credit; @OneToOne @JoinColumn(name = \"teacher_id\") private Teacher teacher; @OneToMany(mappedBy = \"course\") private List\u003cEnrollment\u003e enrollmentList; // setter and getter ... } @Entity @Table(name = \"enrollment\") public class Enrollment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne private Course course; @ManyToOne private Student student; private Integer score; // setter and getter ... } @Entity @Table(name = \"student\") public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private LocalDate dob; @OneToMany(mappedBy = \"student\") List\u003cEnrollment\u003e enrollmentList; // setter and getter ... } mongodb # @Document(\"enrollment_history\") public class History { @Id private String id; private String action; private String studentName; private String courseName; // setter and getter ... } MVC # package structures: configuration controller data entity mongodb History.java Course Enrollment Student Teacher repository mongodb HistoryRepository.java EnrollmentRepository.java StudentRepository.java TeacherRepository.java service EnrollmentService.java EnrollmentServiceImpl.java StudentService.java StudentServiceImpl.java TeacherService.java TeacherServiceImpl.java UserService.java UserServiceImpl.java UserServiceImpl2.java util vo SpringBootDemoApplication.java Dao layer # // package ... // import ...Enrollment; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface EnrollmentRepository extends JpaRepository\u003cEnrollment, Long\u003e { } // package ... // import ...Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface StudentRepository extends JpaRepository\u003cStudent, Long\u003e { } // package ... // import ...Teacher; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface TeacherRepository extends JpaRepository\u003cTeacher, Long\u003e { // select * from teachers where age \u003c 40 and id \u003e 1000; List\u003cTeacher\u003e findByAgeLessThan(int age); List\u003cTeacher\u003e findByAge(int age); // @Query(value = \"select t from Teacher t where t.age \u003c ? and t.id \u003e ?\", nativeQuery = true) // native sql query @Query(value = \"select t from Teacher t where t.age \u003c :age and t.id \u003e :id\") // jpql List\u003cTeacher\u003e a(int age, long id); } mongodb # public interface HistoryRepository extends MongoRepository\u003cHistory, String\u003e { List\u003cHistory\u003e findByAction(String action); List\u003cHistory\u003e findByCourseName(String courseName); } Test Repositories # ","title":"Spring Data Jpa","type":"notes"},{"content":" Concepts # JDBC -\u0026gt; JPA -\u0026gt; Hibernate -\u0026gt; Spring Data JPA / Spring Data Mongodb / Spring Data Elastic Search\nSession Factory grabs the configFile: username + password, then creates sessions\nHibernate use reflection API to auto generate SQL statements\nHibernate allows us to write:\nHQL(Hibernate Query Language), just in case the APIs are not flexible enough to catering some situations. Native SQL. cons: with dialect language(mysql, postgres, oracle) Cache strategies in Hibernate:\nFirst Level(default): in session level. The session(a user create a session) is private, it means it cannot access any content belong to other sessions. Second Level: in session factory level. Add extra configurations to the ConfigFile. For example, there are many users sending the same queries and they are supposed to return the same result. with second level caching strategy, the cache will be pubic to all sessions. github\nSetup # \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.hibernate\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;hibernate-core\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;5.4.20.Final\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.h2database\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;h2\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.4.200\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; Old style cfg # \u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34; ?\u0026gt; \u0026lt;!DOCTYPE hibernate-configuration PUBLIC \u0026#34;-//Hibernate/Hibernate Configuration DTD 3.0//EN\u0026#34; \u0026#34;http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd\u0026#34;\u0026gt; \u0026lt;hibernate-configuration\u0026gt; \u0026lt;session-factory\u0026gt; \u0026lt;property name=\u0026#34;hibernate.connection.driver_class\u0026#34;\u0026gt;org.h2.Driver\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hibernate.connection.url\u0026#34;\u0026gt;jdbc:h2:mem:test\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hibernate.connection.username\u0026#34;\u0026gt;sa\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hibernate.connection.password\u0026#34;\u0026gt;\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hibernate.dialect\u0026#34;\u0026gt;org.hibernate.dialect.H2Dialect\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;show_sql\u0026#34;\u0026gt;true\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hbm2ddl.auto\u0026#34;\u0026gt;create-drop\u0026lt;/property\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.onetoone.dto.foreignKeyAsso.EmployeeEntity\u0026#34;/\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.onetoone.dto.foreignKeyAsso.AccountEntity\u0026#34;/\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.onetoone.dto.sharedPrimaryKey.EmployeeEntity\u0026#34;/\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.onetoone.dto.sharedPrimaryKey.AccountEntity\u0026#34;/\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.onetoone.dto.joinTable.EmployeeEntity\u0026#34;/\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.onetoone.dto.joinTable.AccountEntity\u0026#34;/\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.onetoone.dto.mapsById.EmployeeEntity\u0026#34;/\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.onetoone.dto.mapsById.AccountEntity\u0026#34;/\u0026gt; \u0026lt;/session-factory\u0026gt; \u0026lt;/hibernate-configuration\u0026gt; \u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34; ?\u0026gt; \u0026lt;!DOCTYPE hibernate-configuration PUBLIC \u0026#34;-//Hibernate/Hibernate Configuration DTD 3.0//EN\u0026#34; \u0026#34;http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd\u0026#34;\u0026gt; \u0026lt;hibernate-configuration\u0026gt; \u0026lt;session-factory\u0026gt; \u0026lt;property name=\u0026#34;hibernate.connection.driver_class\u0026#34;\u0026gt;org.h2.Driver\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hibernate.connection.url\u0026#34;\u0026gt;jdbc:h2:mem:test\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hibernate.connection.username\u0026#34;\u0026gt;sa\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hibernate.connection.password\u0026#34;\u0026gt;\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hibernate.dialect\u0026#34;\u0026gt;org.hibernate.dialect.H2Dialect\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;show_sql\u0026#34;\u0026gt;true\u0026lt;/property\u0026gt; \u0026lt;property name=\u0026#34;hbm2ddl.auto\u0026#34;\u0026gt;create-drop\u0026lt;/property\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.manyToMany.joinTable.ReaderEntity\u0026#34;/\u0026gt; \u0026lt;mapping class=\u0026#34;com.yixianwang.hibernate.manyToMany.joinTable.SubscriptionEntity\u0026#34;/\u0026gt; \u0026lt;/session-factory\u0026gt; \u0026lt;/hibernate-configuration\u0026gt; New style cfg # only use annotation One to One # two way one to one @OneToOne(cascade = CascadeType.REMOVE) @PrimaryKeyJoinColumn private AccountEntity account; @OneToOne(mappedBy=\u0026#34;account\u0026#34;, cascade = CascadeType.REMOVE) private EmployeeEntity employee; One to Many # one employee has multiple accounts @OneToMany(cascade=CascadeType.ALL) @JoinTable(name=\u0026#34;EMPLOYEE_ACCOUNT\u0026#34;, joinColumn={@JoinColumn(name=\u0026#34;EMPLOYEE_ID\u0026#34;, referencedColumnName=\u0026#34;ID\u0026#34;)} , inverseJoinColumns={@JoinColumn(name=\u0026#34;ACCOUNT_ID\u0026#34;, referencedColumnName=\u0026#34;ID\u0026#34;)}) private Set\u0026lt;AccountEntity\u0026gt; accounts; use Hibernate # Session session = HibernateUtil.getSessionFactory().oepnSession(); session.beginTransaction(); // ... // save(insert) session.save(firstEmployee); session.save(secondEmployee); session.getTransaction().commit(); HibernateUtil.shutdown(); CascadeType # In practice, we should always use the minimum scope. package javax.persistence; public enum CascadeType { All, PERSIST, MERGE, REMOVE, REFRESH, DETACH; private CascadeType() { } } @OneToMany(cascade=CascadeType.ALL), when delete(or update) a employee, it also deletes all corresponding accounts. ","date":"1 June 2024","externalUrl":null,"permalink":"/notes/hibernate/","section":"Notes","summary":"Concepts # JDBC -\u003e JPA -\u003e Hibernate -\u003e Spring Data JPA / Spring Data Mongodb / Spring Data Elastic Search\nSession Factory grabs the configFile: username + password, then creates sessions\nHibernate use reflection API to auto generate SQL statements\nHibernate allows us to write:\nHQL(Hibernate Query Language), just in case the APIs are not flexible enough to catering some situations. Native SQL. cons: with dialect language(mysql, postgres, oracle) Cache strategies in Hibernate:\n","title":"Hibernate","type":"notes"},{"content":" github Setup # \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.postgresql\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;postgresql\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${postgresql.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; package com.yixianwang.todolistbackend; public class JdbcConfig { private static final String url = \u0026#34;jdbc:postgresql://localhost:5432/test\u0026#34;; private static final String user = \u0026#34;postgres\u0026#34;; private static final String password = \u0026#34;1234\u0026#34;; public JdbcConfig() { } public static String getUrl() { return url; } public static String getUser() { return user; } public static String getPassword() { return password; } } Query # use ResultSet rs = stmt.executeQuery(strSelect); package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcSelectTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); Statement stmt = conn.createStatement(); ) { String strSelect = \u0026#34;select * from cars\u0026#34;; ResultSet rs = stmt.executeQuery(strSelect); int rowCount = 0; while (rs.next()) { String brand = rs.getString(\u0026#34;brand\u0026#34;); String model = rs.getString(\u0026#34;model\u0026#34;); Integer year = rs.getInt(\u0026#34;year\u0026#34;); System.out.println(brand + \u0026#34; \u0026#34; + model + \u0026#34; \u0026#34; + year); ++rowCount; } System.out.println(\u0026#34;Total number of rows:\u0026#34; + rowCount); } catch (SQLException e) { throw new RuntimeException(e); } } } Update # use int countUpdate = stmt.executeUpdate(strUpdate); package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcUpdateTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); Statement stmt = conn.createStatement(); ) { String strUpdate = \u0026#34;update cars set year = 1234 where brand = \u0026#39;Honda\u0026#39;\u0026#34;; int countUpdate = stmt.executeUpdate(strUpdate); System.out.println(countUpdate + \u0026#34; recoreds are updated\u0026#34;); String strSelect = \u0026#34;select * from cars\u0026#34;; ResultSet rs = stmt.executeQuery(strSelect); int rowCount = 0; while (rs.next()) { String brand = rs.getString(\u0026#34;brand\u0026#34;); String model = rs.getString(\u0026#34;model\u0026#34;); Integer year = rs.getInt(\u0026#34;year\u0026#34;); System.out.println(brand + \u0026#34; \u0026#34; + model + \u0026#34; \u0026#34; + year); ++rowCount; } System.out.println(\u0026#34;Total number of rows:\u0026#34; + rowCount); } catch (SQLException e) { throw new RuntimeException(e); } } } Insert and Delete # use int countDelete = stmt.executeUpdate(sqlDelete); use int countInsert = stmt.executeUpdate(sqlInsert); package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcInsertAndDeleteTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); Statement stmt = conn.createStatement(); ) { System.out.println(\u0026#34;-------------------delete------------------\u0026#34;); String sqlDelete = \u0026#34;delete from cars where brand = \u0026#39;Apple\u0026#39;\u0026#34;; int countDelete = stmt.executeUpdate(sqlDelete); System.out.println(countDelete + \u0026#34; records are deleted\u0026#34;); System.out.println(\u0026#34;-------------------insert one record------------------\u0026#34;); String sqlInsert = \u0026#34;insert into cars\u0026#34; + \u0026#34; values (\u0026#39;Banana\u0026#39;, \u0026#39;haha\u0026#39;, 1)\u0026#34;; int countInsert = stmt.executeUpdate(sqlInsert); System.out.println(countInsert + \u0026#34; records are inserted\u0026#34;); System.out.println(\u0026#34;-------------------insert multiple record------------------\u0026#34;); String sqlMultiInsert = \u0026#34;insert into cars values \u0026#34; + \u0026#34;(\u0026#39;Tomato\u0026#39;, \u0026#39;hoho\u0026#39;, 2), \u0026#34; + \u0026#34;(\u0026#39;Pineapple\u0026#39;, \u0026#39;hihi\u0026#39;, 3)\u0026#34;; int countMultiInsert = stmt.executeUpdate(sqlMultiInsert); System.out.println(countMultiInsert + \u0026#34; records are inserted\u0026#34;); System.out.println(\u0026#34;-------------------partial insert------------------\u0026#34;); String sqlPartialInsert = \u0026#34;insert into cars (brand, model)\u0026#34; + \u0026#34;values (\u0026#39;Jeep\u0026#39;, \u0026#39;Wrangler\u0026#39;)\u0026#34;; int countPartialInsert = stmt.executeUpdate(sqlPartialInsert); System.out.println(countPartialInsert + \u0026#34; records are inserted\u0026#34;); System.out.println(\u0026#34;-------------------Print Final Table------------------\u0026#34;); String sqlSelect = \u0026#34;select * from cars\u0026#34;; ResultSet rs = stmt.executeQuery(sqlSelect); while (rs.next()) { String brand = rs.getString(\u0026#34;brand\u0026#34;); String model = rs.getString(\u0026#34;model\u0026#34;); Integer year = rs.getInt(\u0026#34;year\u0026#34;); System.out.println(brand + \u0026#34; \u0026#34; + model + \u0026#34; \u0026#34; + year); } } catch (SQLException e) { e.printStackTrace(); } } } Transaction # use conn.setAutoCommit(false); use conn.commit(); use conn.rollback(); package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcTransactionTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); Statement stmt = conn.createStatement(); ) { conn.setAutoCommit(false); // before update System.out.println(\u0026#34;before update:\u0026#34;); String sqlSelect = \u0026#34;select * from cars\u0026#34;; ResultSet rs = stmt.executeQuery(sqlSelect); while (rs.next()) { String brand = rs.getString(\u0026#34;brand\u0026#34;); String model = rs.getString(\u0026#34;model\u0026#34;); Integer year = rs.getInt(\u0026#34;year\u0026#34;); System.out.println(brand + \u0026#34; \u0026#34; + model + \u0026#34; \u0026#34; + year); } conn.commit(); // update something stmt.executeUpdate(\u0026#34;update cars set year = 2024 where brand = \u0026#39;Honda\u0026#39;\u0026#34;); stmt.executeUpdate(\u0026#34;update cars set year = 2024 where brand = \u0026#39;Ford\u0026#39;\u0026#34;); conn.commit(); // after update (commit) System.out.println(); System.out.println(\u0026#34;after update commit:\u0026#34;); String sqlSelect2 = \u0026#34;select * from cars\u0026#34;; ResultSet rs2 = stmt.executeQuery(sqlSelect2); while (rs2.next()) { String brand = rs2.getString(\u0026#34;brand\u0026#34;); String model = rs2.getString(\u0026#34;model\u0026#34;); Integer year = rs2.getInt(\u0026#34;year\u0026#34;); System.out.println(brand + \u0026#34; \u0026#34; + model + \u0026#34; \u0026#34; + year); } conn.commit(); // update but rollback System.out.println(); System.out.println(\u0026#34;update but rollback\u0026#34;); stmt.executeUpdate(\u0026#34;update cars set year = 2011 where brand = \u0026#39;Honda\u0026#39;\u0026#34;); stmt.executeUpdate(\u0026#34;update cars set year = 2011 where brand = \u0026#39;Ford\u0026#39;\u0026#34;); conn.rollback(); // after update (rollback) System.out.println(); System.out.println(\u0026#34;after update rollback\u0026#34;); String sqlSelect3 = \u0026#34;select * from cars\u0026#34;; ResultSet rs3 = stmt.executeQuery(sqlSelect3); while (rs3.next()) { String brand = rs3.getString(\u0026#34;brand\u0026#34;); String model = rs3.getString(\u0026#34;model\u0026#34;); Integer year = rs3.getInt(\u0026#34;year\u0026#34;); System.out.println(brand + \u0026#34; \u0026#34; + model + \u0026#34; \u0026#34; + year); } conn.commit(); } catch (SQLException e) { throw new RuntimeException(e); } } } Roll Back in Catch # use conn.rollback(); within catch block Batch Processing with Prepared Statement # purpose: to decrease the number of IO use prepared statement package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcBatchProcessingPreparedStatTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); PreparedStatement pstmt = conn.prepareStatement( \u0026#34;insert into cars values (?, ?, ?)\u0026#34; ); ) { conn.setAutoCommit(false); pstmt.setString(1, \u0026#34;Apple\u0026#34;); pstmt.setString(2, \u0026#34;Z1\u0026#34;); pstmt.setInt(3, 2025); pstmt.addBatch(); pstmt.setString(1, \u0026#34;Huawei\u0026#34;); pstmt.setString(2, \u0026#34;Q1\u0026#34;); pstmt.addBatch(); int[] result = pstmt.executeBatch(); conn.commit(); } catch (SQLException e) { } } } Prepared Statement # purpose: to solve sql injection package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcPreparedStatementTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); PreparedStatement pstmt = conn.prepareStatement( \u0026#34;insert into cars values (?, ?, ?)\u0026#34; ); PreparedStatement pstmtSelect = conn.prepareStatement(\u0026#34;select * from cars\u0026#34;); ) { pstmt.setString(2, \u0026#34;RX350\u0026#34;); pstmt.setInt(3, 2020); int rowsInserted = pstmt.executeUpdate(); System.out.println(rowsInserted + \u0026#34; records inserted\u0026#34;); // partial changes pstmt.setString(1, \u0026#34;Tesla\u0026#34;); rowsInserted = pstmt.executeUpdate(); System.out.println(rowsInserted + \u0026#34; records inserted\u0026#34;); ResultSet rs = pstmtSelect.executeQuery(); while (rs.next()) { String brand = rs.getString(\u0026#34;brand\u0026#34;); String model = rs.getString(\u0026#34;model\u0026#34;); Integer year = rs.getInt(\u0026#34;year\u0026#34;); System.out.println(brand + \u0026#34; \u0026#34; + model + \u0026#34; \u0026#34; + year); } } catch (SQLException e) { } } } ","date":"31 May 2024","externalUrl":null,"permalink":"/notes/jdbc/","section":"Notes","summary":" github Setup # \u003cdependency\u003e \u003cgroupId\u003eorg.postgresql\u003c/groupId\u003e \u003cartifactId\u003epostgresql\u003c/artifactId\u003e \u003cversion\u003e${postgresql.version}\u003c/version\u003e \u003c/dependency\u003e package com.yixianwang.todolistbackend; public class JdbcConfig { private static final String url = \"jdbc:postgresql://localhost:5432/test\"; private static final String user = \"postgres\"; private static final String password = \"1234\"; public JdbcConfig() { } public static String getUrl() { return url; } public static String getUser() { return user; } public static String getPassword() { return password; } } Query # use ResultSet rs = stmt.executeQuery(strSelect); package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcSelectTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); Statement stmt = conn.createStatement(); ) { String strSelect = \"select * from cars\"; ResultSet rs = stmt.executeQuery(strSelect); int rowCount = 0; while (rs.next()) { String brand = rs.getString(\"brand\"); String model = rs.getString(\"model\"); Integer year = rs.getInt(\"year\"); System.out.println(brand + \" \" + model + \" \" + year); ++rowCount; } System.out.println(\"Total number of rows:\" + rowCount); } catch (SQLException e) { throw new RuntimeException(e); } } } Update # use int countUpdate = stmt.executeUpdate(strUpdate); package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcUpdateTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); Statement stmt = conn.createStatement(); ) { String strUpdate = \"update cars set year = 1234 where brand = 'Honda'\"; int countUpdate = stmt.executeUpdate(strUpdate); System.out.println(countUpdate + \" recoreds are updated\"); String strSelect = \"select * from cars\"; ResultSet rs = stmt.executeQuery(strSelect); int rowCount = 0; while (rs.next()) { String brand = rs.getString(\"brand\"); String model = rs.getString(\"model\"); Integer year = rs.getInt(\"year\"); System.out.println(brand + \" \" + model + \" \" + year); ++rowCount; } System.out.println(\"Total number of rows:\" + rowCount); } catch (SQLException e) { throw new RuntimeException(e); } } } Insert and Delete # use int countDelete = stmt.executeUpdate(sqlDelete); use int countInsert = stmt.executeUpdate(sqlInsert); package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcInsertAndDeleteTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); Statement stmt = conn.createStatement(); ) { System.out.println(\"-------------------delete------------------\"); String sqlDelete = \"delete from cars where brand = 'Apple'\"; int countDelete = stmt.executeUpdate(sqlDelete); System.out.println(countDelete + \" records are deleted\"); System.out.println(\"-------------------insert one record------------------\"); String sqlInsert = \"insert into cars\" + \" values ('Banana', 'haha', 1)\"; int countInsert = stmt.executeUpdate(sqlInsert); System.out.println(countInsert + \" records are inserted\"); System.out.println(\"-------------------insert multiple record------------------\"); String sqlMultiInsert = \"insert into cars values \" + \"('Tomato', 'hoho', 2), \" + \"('Pineapple', 'hihi', 3)\"; int countMultiInsert = stmt.executeUpdate(sqlMultiInsert); System.out.println(countMultiInsert + \" records are inserted\"); System.out.println(\"-------------------partial insert------------------\"); String sqlPartialInsert = \"insert into cars (brand, model)\" + \"values ('Jeep', 'Wrangler')\"; int countPartialInsert = stmt.executeUpdate(sqlPartialInsert); System.out.println(countPartialInsert + \" records are inserted\"); System.out.println(\"-------------------Print Final Table------------------\"); String sqlSelect = \"select * from cars\"; ResultSet rs = stmt.executeQuery(sqlSelect); while (rs.next()) { String brand = rs.getString(\"brand\"); String model = rs.getString(\"model\"); Integer year = rs.getInt(\"year\"); System.out.println(brand + \" \" + model + \" \" + year); } } catch (SQLException e) { e.printStackTrace(); } } } Transaction # use conn.setAutoCommit(false); use conn.commit(); use conn.rollback(); package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcTransactionTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); Statement stmt = conn.createStatement(); ) { conn.setAutoCommit(false); // before update System.out.println(\"before update:\"); String sqlSelect = \"select * from cars\"; ResultSet rs = stmt.executeQuery(sqlSelect); while (rs.next()) { String brand = rs.getString(\"brand\"); String model = rs.getString(\"model\"); Integer year = rs.getInt(\"year\"); System.out.println(brand + \" \" + model + \" \" + year); } conn.commit(); // update something stmt.executeUpdate(\"update cars set year = 2024 where brand = 'Honda'\"); stmt.executeUpdate(\"update cars set year = 2024 where brand = 'Ford'\"); conn.commit(); // after update (commit) System.out.println(); System.out.println(\"after update commit:\"); String sqlSelect2 = \"select * from cars\"; ResultSet rs2 = stmt.executeQuery(sqlSelect2); while (rs2.next()) { String brand = rs2.getString(\"brand\"); String model = rs2.getString(\"model\"); Integer year = rs2.getInt(\"year\"); System.out.println(brand + \" \" + model + \" \" + year); } conn.commit(); // update but rollback System.out.println(); System.out.println(\"update but rollback\"); stmt.executeUpdate(\"update cars set year = 2011 where brand = 'Honda'\"); stmt.executeUpdate(\"update cars set year = 2011 where brand = 'Ford'\"); conn.rollback(); // after update (rollback) System.out.println(); System.out.println(\"after update rollback\"); String sqlSelect3 = \"select * from cars\"; ResultSet rs3 = stmt.executeQuery(sqlSelect3); while (rs3.next()) { String brand = rs3.getString(\"brand\"); String model = rs3.getString(\"model\"); Integer year = rs3.getInt(\"year\"); System.out.println(brand + \" \" + model + \" \" + year); } conn.commit(); } catch (SQLException e) { throw new RuntimeException(e); } } } Roll Back in Catch # use conn.rollback(); within catch block Batch Processing with Prepared Statement # purpose: to decrease the number of IO use prepared statement package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcBatchProcessingPreparedStatTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); PreparedStatement pstmt = conn.prepareStatement( \"insert into cars values (?, ?, ?)\" ); ) { conn.setAutoCommit(false); pstmt.setString(1, \"Apple\"); pstmt.setString(2, \"Z1\"); pstmt.setInt(3, 2025); pstmt.addBatch(); pstmt.setString(1, \"Huawei\"); pstmt.setString(2, \"Q1\"); pstmt.addBatch(); int[] result = pstmt.executeBatch(); conn.commit(); } catch (SQLException e) { } } } Prepared Statement # purpose: to solve sql injection package com.yixianwang.todolistbackend; import java.sql.*; public class JdbcPreparedStatementTest { public static void main(String[] args) { try ( Connection conn = DriverManager.getConnection( JdbcConfig.getUrl(), JdbcConfig.getUser(), JdbcConfig.getPassword() ); PreparedStatement pstmt = conn.prepareStatement( \"insert into cars values (?, ?, ?)\" ); PreparedStatement pstmtSelect = conn.prepareStatement(\"select * from cars\"); ) { pstmt.setString(2, \"RX350\"); pstmt.setInt(3, 2020); int rowsInserted = pstmt.executeUpdate(); System.out.println(rowsInserted + \" records inserted\"); // partial changes pstmt.setString(1, \"Tesla\"); rowsInserted = pstmt.executeUpdate(); System.out.println(rowsInserted + \" records inserted\"); ResultSet rs = pstmtSelect.executeQuery(); while (rs.next()) { String brand = rs.getString(\"brand\"); String model = rs.getString(\"model\"); Integer year = rs.getInt(\"year\"); System.out.println(brand + \" \" + model + \" \" + year); } } catch (SQLException e) { } } }","title":"JDBC","type":"notes"},{"content":" Setting up # file structure # some_folder/module_name/package_name/go_file.go new project == new module # name module with github repository is very common go mod init github.com/yixianwang/module_name go run # equals to go build and then run executable Data Types # const myConst var myVar bool float32 float64 int int16 int32 int64 rune string unit uint8 uint16 uint32 uint64 built-in package import \u0026quot;unicode/utf8\u0026quot; then use utf8.RuneCountInString(\u0026quot;汉\u0026quot;)\nFunctions \u0026amp; Control Structures # Function with multiple return values # func intDivision(numerator int, denominator int) (int, int) { var result int = numerator / denominator var remainder int = numerator % denominator return result, remainder } // var result, remainder = intDivision(numerator, denominator) error handling # import \u0026#34;errors\u0026#34; func intDivision(numerator int, denominator int) (int, int, error) { var err error // default value is nil if denominator == 0 { err = errors.New(\u0026#34;some message\u0026#34;) return 0, 0, err } var result int = numerator / denominator var remainder int = numerator % denominator return result, remainder, err } // var result, remainder, err = intDivision(numerator, denominator) // if err != nil { // fmt.Printf(err.Error()) // } switch keyword # // similar to if switch { case err != nil: fmt.Printf(err.Error()) case remainder == 0: fmt.Printf(\u0026#34;%v\u0026#34;, result) default: fmt.Printf(\u0026#34;%v, %v\u0026#34;, result, remainder) } // another syntax with switch switch remainder { case 0: fmt.Printf(\u0026#34;The division was exact\u0026#34;) case 1, 2: fmt.Printf(\u0026#34;The division was exact\u0026#34;) default: fmt.Printf(\u0026#34;The division was not close\u0026#34;) } Arrays, Slices, Maps \u0026amp;\u0026amp; Looping Control Structures # var intArr [3]int32 = [3]int32{1,2,3} // intArr := [3]int32{1,2,3} // intArr := [...]int32{1,2,3} var intSlice []int32 = []int32{4,5,6} fmt.Printf(\u0026#34;The length is %v with capacity %v\u0026#34;, len(intSlice), cap(intSlice)) intSlice = append(intSlice, 7) fmt.Printf(\u0026#34;The length is %v with capacity %v\u0026#34;, len(intSlice), cap(intSlice)) var intSlice2 []int32 = []int32{8, 9} intSlice = append(intSlice, intSlice2...) var intSlice3 []int32 = make(int32[], 3) // 3 is length // var intSlice3 []int32 = make(int32[], 3, 8) // 8 is capacity (optional, default is equal to length of slice) var myMap map[string]uint8 = make(map[string]uint8) fmt.Println(myMap) var myMap2 = map[string]uint8{\u0026#34;Adam\u0026#34;:23, \u0026#34;Sam\u0026#34;:45} fmt.Println(myMap[\u0026#34;Adam\u0026#34;]) fmt.Println(myMap[\u0026#34;KeyNotExist\u0026#34;]) // get default value of uint8, which is 0 var age, ok = myMap2[\u0026#34;Adam\u0026#34;] // ok is true if the key exist in the map, and false otherwise delete(myMap2, \u0026#34;Adam\u0026#34;) // no return value // iterate over i.e. map, array, slice for key, val := range myMap2 { fmt.Printf(\u0026#34;key: %v, val: %v\\n\u0026#34;, key, val) } for idx, val := range intArr { fmt.Printf(\u0026#34;idx: %v, val: %v\\n\u0026#34;, idx, val) } Strings, Runes, Bytes # %v, value %T, type of the value var myString = \u0026#34;résumé\u0026#34; var indexed = myString[1] // return 195 != 233, it\u0026#39;s not correct fmt.Printf(\u0026#34;%v, %T\\n\u0026#34;, indexed, indexed) for i, v := range myString { fmt.Println(i, v) } // 114, uint8 // 0 114 // 1 233 // 233 is correct here with range keyword // 3 115 // 4 117 // 5 109 // 6 233 len(myString) return the number of bytes of myString runes # runes are just Unicode Point numbers which represent the character runes are just an alias for int32 we can declare a rune type using a single quote var myRune = 'a' var myString = []rune(\u0026#34;résumé\u0026#34;) var indexed = myString[1] // return 233 == 233, correct fmt.Printf(\u0026#34;%v, %T\\n\u0026#34;, indexed, indexed) for i, v := range myString { fmt.Println(i, v) } string building # strings are immutable in go, we cannot modify them once created var strSlice = []string{\u0026#34;y\u0026#34;, \u0026#34;i\u0026#34;, \u0026#34;x\u0026#34;, \u0026#34;i\u0026#34;, \u0026#34;a\u0026#34;, \u0026#34;n\u0026#34;} var catStr = \u0026#34;\u0026#34; for i := range strSlice { catStr += strSlice[i] } fmt.Printf(\u0026#34;\\n%v\u0026#34;, catStr) best practice: we can import built-in strings package, and create a strings.Builder instead of using plus operator, we call WriteString method var strSlice = []string{\u0026#34;y\u0026#34;, \u0026#34;i\u0026#34;, \u0026#34;x\u0026#34;, \u0026#34;i\u0026#34;, \u0026#34;a\u0026#34;, \u0026#34;n\u0026#34;} var strBuilder strings.Builder for i := range strSlice { strBuilder.WriteString(strSlice[i]) } var catStr = strBuilder.String() fmt.Printf(\u0026#34;\\n%v\u0026#34;, catStr) Structs, Interfaces # package main import \u0026#34;fmt\u0026#34; // create a struct type gasEngine struct { mpg uint8 gallons uint8 // ownerInfo owner owner // we can adding subfield directly int // use type int directly, so we can use this syntax with any type } type owner struct { name string } func main() { // var myEngine gasEngine // fmt.Println(myEngine.mpg, myEngine.gallons) // 0, 0 // var myEngine gasEngine = gasEngine{mpg:25, gallons:15} // var myEngine gasEngine = gasEngine{25, 15} // we can omit the field names, it will assign in order // myEngine.mpt = 20 // we can also set the values by name directly // fmt.Println(myEngine.mpg, myEngine.gallons) // 25, 15 var myEngine gasEngine = gasEngine{25, 15, owner{\u0026#34;Alex\u0026#34;}} fmt.Println(myEngine.mpg, myEngine.gallons, myEngine.ownerInfo.name) // if we adding subfield directly, we can omit ownerInfo field } anonymous struct # define and initialize in the same location the main difference is that is not reusable package main import \u0026#34;fmt\u0026#34; type gasEngine struct { mpg uint8 gallons uint8 } func main() { // var myEngine gasEngine = gasEngine{25, 15} var myEngine2 = struct { mpg uint8 gallons uint8 } {21, 12} fmt.Println(myEngine2) } struct method # package main import \u0026#34;fmt\u0026#34; type gasEngine struct { mpg uint8 gallons uint8 } func (e gasEngine) milesLeft() uint8 { return e.gallons * e.mpg } func main() { var myEngine gasEngine = gasEngine{25, 15} fmt.Println(myEngine2) } interface # package main import \u0026#34;fmt\u0026#34; type gasEngine struct { mpg uint8 gallons uint8 } type electricEngine struct { mpkwh uint8 kwh uint8 } func (e gasEngine) milesLeft() uint8 { return e.gallons * e.mpg } func (e electricEngine) milesLeft() uint8 { return e.kwh * e.mpkwh } type engine interface { milesLeft() uint8 // 1. method signature } func canMakeIt(e engine, miles uint8) { // 2. use engine here if miles \u0026lt;= e.milesLeft() { fmt.Println(\u0026#34;You can make it there!\u0026#34;) } else { fmt.Println(\u0026#34;Need to fuel up first!\u0026#34;) } } func main() { var myEngine gasEngine = gasEngine{25, 15} canMakeIt(myEngine, 50) // 3. apply with various Engine types } Pointers # same to c/c++ Goroutines # use go keyword in front of the function we want to run concurrently import sync package, to let wait groups come in then we create a wait group var wg = sync.WaitGroup{}, they just like counters add wg.Add(1) and wg.Done() add wg.Wait(), it gonna wait for the counter to go back down to 0, meaning all the tasks have completed package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math/rand\u0026#34; \u0026#34;time\u0026#34; \u0026#34;sync\u0026#34; ) var wg = sync.WaitGroup{} var dbData = []string{\u0026#34;id1\u0026#34;, \u0026#34;id2\u0026#34;, \u0026#34;id3\u0026#34;, \u0026#34;id4\u0026#34;, \u0026#34;id5\u0026#34;} func main() { t0 := time.Now() for i := 0; i \u0026lt; len(dbData); i++ { wg.Add(1) go dbCall(i) } wg.Wait() fmt.Printf(\u0026#34;\\nTotal execution time: %v\u0026#34;, time.Since(t0)) } func dbCall(i int) { // simulate DB call delay var delay float32 = rand.Float32() * 2000 time.Sleep(time.Duration(delay) * time.Millisecond) fmt.Println(\u0026#34;The result from the database is:\u0026#34;, dbData[i]) wg.Done() } using locks to make threads safe # without lock # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; \u0026#34;sync\u0026#34; ) var wg = sync.WaitGroup{} var dbData = []string{\u0026#34;id1\u0026#34;, \u0026#34;id2\u0026#34;, \u0026#34;id3\u0026#34;, \u0026#34;id4\u0026#34;, \u0026#34;id5\u0026#34;} var results = []string{} // 1. create a slice to store all the result from db func main() { t0 := time.Now() for i := 0; i \u0026lt; len(dbData); i++ { wg.Add(1) go dbCall(i) } wg.Wait() fmt.Printf(\u0026#34;\\nTotal execution time: %v\u0026#34;, time.Since(t0)) fmt.Printf(\u0026#34;\\nThe results are %v\u0026#34;, results) // 3. print the results } func dbCall(i int) { // simulate DB call delay var delay float32 = 2000 time.Sleep(time.Duration(delay) * time.Millisecond) fmt.Println(\u0026#34;The result from the database is:\u0026#34;, dbData[i]) results = append(results, dbData[i]) // 2. append the result wg.Done() } Above code, WE WILL GET AN UNEXPECTED RESULT\nwith lock (sync.Mutex{}) # to make thread safe, we can use mutex (Mutual Exclusion) by var m = sync.Mutex{} with two main methods m.Lock() and m.Unlock(), and place them around the part of our code which access the result slice cons: it completely locks out other go routines to access the results slice package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; \u0026#34;sync\u0026#34; ) var m = sync.Mutex{} // 1. create a mutex var wg = sync.WaitGroup{} var dbData = []string{\u0026#34;id1\u0026#34;, \u0026#34;id2\u0026#34;, \u0026#34;id3\u0026#34;, \u0026#34;id4\u0026#34;, \u0026#34;id5\u0026#34;} var results = []string{} func main() { t0 := time.Now() for i := 0; i \u0026lt; len(dbData); i++ { wg.Add(1) go dbCall(i) } wg.Wait() fmt.Printf(\u0026#34;\\nTotal execution time: %v\u0026#34;, time.Since(t0)) fmt.Printf(\u0026#34;\\nThe results are %v\u0026#34;, results) } func dbCall(i int) { // simulate DB call delay var delay float32 = 2000 time.Sleep(time.Duration(delay) * time.Millisecond) fmt.Println(\u0026#34;The result from the database is:\u0026#34;, dbData[i]) m.Lock() // 2. use lock results = append(results, dbData[i]) m.Unlock() // 2. use unlock wg.Done() } with lock (sync.RWMutex{}) # this has all the same functionality of of the mutex above\nand the m.Lock() and m.Unlock() work exactly the same but we also have m.RLock() and m.RUnlock() methods workflows:\nwhen go routine reaches m.RLock(), it checks if there\u0026rsquo;s a full lock (m.Lock()) on the mutex if full lock exists, it(m.RLock()) will wait until full lock is released before continuing if no full lock exists, the go routine will acquire a read lock (m.RLock()), and then proceed with the rest of the code Note:\nmany go routines may hold read locks at the same time, these read locks will only block code execution up to the full lock when the a go routine hits full lock and in order to proceed, all locks must be cleared pros: this prevents us from accessing the slice while other go routines are writing to or reading from the slice\nsummary: it allows multiple go routines to read from our slice at the same time, only blocking when writes may be potentially be happening package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; \u0026#34;sync\u0026#34; ) var m = sync.RWMutex{} // 1. use RWMutex var wg = sync.WaitGroup{} var dbData = []string{\u0026#34;id1\u0026#34;, \u0026#34;id2\u0026#34;, \u0026#34;id3\u0026#34;, \u0026#34;id4\u0026#34;, \u0026#34;id5\u0026#34;} var results = []string{} func main() { t0 := time.Now() for i := 0; i \u0026lt; len(dbData); i++ { wg.Add(1) go dbCall(i) } wg.Wait() fmt.Printf(\u0026#34;\\nTotal execution time: %v\u0026#34;, time.Since(t0)) fmt.Printf(\u0026#34;\\nThe results are %v\u0026#34;, results) } func dbCall(i int) { var delay float32 = 2000 time.Sleep(time.Duration(delay) * time.Millisecond) save(dbData[i]) log() wg.Done() } func save(result string) { m.Lock() results = append(results, result) m.Unlock() } func log() { m.RLock() fmt.Printf(\u0026#34;\\nThe current results are: %v\u0026#34;, results) m.RUnlock() } Channels # think of channels as a way to enable go routines to pass around information\nmain features:\nHold Data: i.e. integer, slice, or anything else Thread Safe: i.e. we avoid data races when we\u0026rsquo;re reading and writing from memory Listen for Data: we can listen when data is added or removed from a channel and we can block code execution until one of these events happens. to make a channel, we use make function followed by the chan keyword, then the type of value we want the channel to hold. i.e. var c = make(chan int), so this channel can only hold a single int value\nchannels also have a special syntax. i.e. we use \u0026lt;- to add value to the channel\nwe can think of a channel as containing an underlying array, in this case we have what\u0026rsquo;s called an Unbuffer Channel, which only has enough room for one value.\nwe can retrieve the value from a channel using var i = \u0026lt;- c, so here the value gets popped out of the channel(the channel is now empty) and variable i holds the value\ndeadlock errors # Why?: when we write to an Unbuffer Channel(c \u0026lt;- 1), the code will block until something else reads from it. so in effect we\u0026rsquo;ll be waiting here forever, unable to reach the line (var i = \u0026lt;- c), where we actually read from the channel luckily go\u0026rsquo;s runtime is smart enough to notice this and we will just throw a deadlock error rather than our code hanging here forever. To use it properly in conjunction with go routine\nChannel + Go Routines == Proper Way package main import \u0026#34;fmt\u0026#34; func main() { var c = make(chan int) // to make a channel c \u0026lt;- 1 // add value to the channel var i = \u0026lt;- c // retrieve the value from a channel fmt.Println(i) } (Channel + Go Routines) is the proper way # example 1 # package main import \u0026#34;fmt\u0026#34; func main() { var c = make(chan int) // 1. make a channel go process(c) // 2. call go routine fmt.Println(\u0026lt;- c) // !!! 3. the execution will be waiting here for a value to be set in the channel // !!! 5. then our main function notices that a value has been set, and finally the print function gets called } func process(c chan int) { c \u0026lt;- 123 // !!! 4. in this go routine, we set the value and exit the function } example 2 # we can iterate over the channel by using range keyword\nwork flows:\nwe create the channel(make(chan int)) and start the go routine(go process(c)) in the main function we wait at the top of the for loop for something to be added to the channel in the process function we setup for loop and add 0 to the channel we wait (c \u0026lt;- 0) until the main function reads from the channel and then in a concurrent way both the value printed and 1 is added to the channel at about the same time this then continues until i is equal to 5. package main import \u0026#34;fmt\u0026#34; func main() { var c = make(chan int) go process(c) for i := range c { fmt.Println(i) // i here is the value of the channel } } func process(c chan int) { for i := 0; i \u0026lt; 5; i++ { c \u0026lt;- i } } Note: deadlock error happens again for above code\nbecause after we print all of our values from 0 to 4,\nthe main function will go back to wait at the top of the for loop for another value\nbut just like me on under after five messages it will get ghosted by the process function which won\u0026rsquo;t send any more messages and we get deadlock error\nSolution:\nbefore exiting a process, we can close the channel like close(c) or defer close(c) close(c) notifies any other process using this channel that we\u0026rsquo;re done and our main function will break out of the for loop and exit. defer close(c) using defer statement and it go this just means do this stuff(close(c)) right before the function exits.\npackage main import \u0026#34;fmt\u0026#34; func main() { var c = make(chan int) go process(c) for i := range c { fmt.Println(i) } } func process(c chan int) { for i := 0; i \u0026lt; 5; i++ { c \u0026lt;- i } close(c) // close the channel } Buffer Channel # now we can store multiple values in the channel at the same time. i.e. we can store 5 integers var c = make(chan int, 5) if we run the code with the regular channel, the process function stays active until the main function is done with the channel. But there\u0026rsquo;s no need for the process function to hang around. It can finish its work quickly and just exit. And let the main function do its thing.\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func main() { var c = make(chan int, 5) // the process function can add up to 5 values in the channel without having to wait for the main function to make room in the channel by popping out a value(at the for loop line) go process(c) for i := range c { fmt.Println(i) time.Sleep(time.Second * 1) // some work..., takes 1 second } } func process(c chan int) { defer close(c) for i := 0; i \u0026lt; 5; i++ { c \u0026lt;- i } fmt.Println(\u0026#34;Exiting process\u0026#34;) } Note: the process function finishes almost immediately while the main function is still running reading the values in the channel.\nRealistic example of Channels # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;math/rand\u0026#34; \u0026#34;time\u0026#34; ) var MAX_CHICKEN_PRICE float32 = 5 var MAX_TOFU_PRICE float32 = 3 // there are three go routines running at the same time checking these three websites // and sendMessage function is waiting there for value to be added to the channel to send off text // so the first go routine to find a deal on chicken will trigger the text message in the program and exit. func main() { var chickenChannel = make(chan string) // the channel holds the website we found the sale on var tofuChannel = make(chan string) // when we find a bargain on tofu we write to this channel var websites = []string{\u0026#34;walmart.com\u0026#34;, \u0026#34;costco.com\u0026#34;, \u0026#34;wholefoods.com\u0026#34;} for i := range websites { go checkChickenPrices(websites[i], chickenChannel) // we spawn three go routines go checkTofuPrices(websites[i], tofuChannel) } sendMessage(chickenChannel, tofuChannel) // send a message when a deal is found } func checkTofuPrices(website string, c chan string) { for { time.Sleep(time.Second * 1) var tofu_price = rand.Float32() * 20 if tofu_price \u0026lt;= MAX_TOFU_PRICE { c \u0026lt;- website break } } } func checkChickenPrices(website string, chickenChannel chan string) { for { // every second check the website for the price of chicken // and if it\u0026#39;s below our threshold, it will set the value of the channel to the website time.Sleep(time.Second * 1) var chickenPrice = rand.Float32() * 20 if chickenPrice \u0026lt;= MAX_CHICKEN_PRICE { chickenChannel \u0026lt;- website break } } } func sendMessage(chickenChannel chan string, tofuChannel chan string) { // fmt.Printf(\u0026#34;\\nFound a deal on chicken at %s\u0026#34;, \u0026lt;- chickenChannel) // waiting here for value to be added to the channel // select statement will listen for a result once it gets one it\u0026#39;ll execute one of those statements and exit. select { // if we receive a message from the chicken channel, we set the variable website to the value in the channel and we execute the following statement case website := \u0026lt;- chickenChannel: fmt.Printf(\u0026#34;\\nText sent: Found deal on chicken at %v.\u0026#34;, website) // otherwise if we receive a message from the tofu channel, we execute the following statement case website := \u0026lt;- tofuChannel: fmt.Printf(\u0026#34;\\nEmail sent: Found deal on chicken at %v.\u0026#34;, website) } } Generics # normal generic example # package main import \u0026#34;fmt\u0026#34; func main() { var intSlice = []int{1, 2, 3} fmt.Println(sumSlice[int](intSlice)) var float32Slice = []float32{1, 2, 3} fmt.Println(sumSlice[float32](float32Slice)) } func sumSlice[T int | float32 | float64](slice []T) T { var sum T for _, v := range slice { sum += v } return sum } any type example # package main import \u0026#34;fmt\u0026#34; func main() { var intSlice = []int{} fmt.Println(isEmpty(intSlice)) // we can omit the square bracket type input here var float32Slice = []float32{1, 2, 3} fmt.Println(isEmpty(float32Slice)) // and here } func isEmpty[T any](slice []T) bool { return len(slice) == 0 } an example that we can\u0026rsquo;t infer the type of our generic parameter # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;encoding/json\u0026#34; \u0026#34;io/ioutil\u0026#34; ) type contactInfo struct { Name string Email string } type purchaseInfo struct { Name string Price float32 Amount int } func main() { var contacts []contactInfo = loadJSON[contactInfo](\u0026#34;./contactInfo.json\u0026#34;) fmt.Printf(\u0026#34;\\n%+v\u0026#34;, contacts) var purchases []purchaseInfo = loadJSON[purchaseInfo](\u0026#34;./purchaseInfo.json\u0026#34;) fmt.Printf(\u0026#34;\\n%+v\u0026#34;, purchases) } func loadJSON[T contactInfo | purchaseInfo] (filePath string) []T { data, _ = ioutil.ReadFile(filePath) var loaded = []T{} json.Unmarshal(data, \u0026amp;loaded) return loaded } struct generic # package main import \u0026#34;fmt\u0026#34; type gasEngine struct { gallons float32 mpg float32 } type electricEngine struct { kwh float32 mpkwh float32 } type car[T gasEngine | electricEngine] struct { carMake string carModel string engine T } func main() { var gasCar = car[gasEngine] { carMake: \u0026#34;Honda\u0026#34;, carModel: \u0026#34;Civic\u0026#34;, engine: gasEngine { gallons: 12.4, mpg: 40, }, } fmt.Println(gasCar) var electricCar = car[electricEngine] { carMake: \u0026#34;Tesla\u0026#34;, carModel: \u0026#34;Model 3\u0026#34;, engine: electricEngine { kwh: 57.5, mpkwh: 4.17, }, } fmt.Println(electricCar) } Building an API! # youtube\nCode\nReference: Golang Project Layout\ngo mod init module_name(i.e. Github URL) mkdir api, api folder contains specs things like parameters and response type for our endpoint. This is also where we could put our yaml spec file. mkdir cmd/api, will contain our main.go. mkdir internal, will contain most of code for this API. ","date":"24 May 2024","externalUrl":null,"permalink":"/notes/go_advanced/","section":"Notes","summary":"Setting up # file structure # some_folder/module_name/package_name/go_file.go new project == new module # name module with github repository is very common go mod init github.com/yixianwang/module_name go run # equals to go build and then run executable Data Types # const myConst var myVar bool float32 float64 int int16 int32 int64 rune string unit uint8 uint16 uint32 uint64 built-in package import \"unicode/utf8\" then use utf8.RuneCountInString(\"汉\")\n","title":"Go Advanced","type":"notes"},{"content":" 1. workspace # go env GOPATH\n1.1 file structures # /Users/username/go/src/project1 /Users/username/go/src/project2 2. package main # every project at least if we want to be executed it needs to have a package called package main 3. import # this is where we can import different packages no comma\nimport ( \u0026#34;fmt\u0026#34; \u0026#34;math\u0026#34; ) 4. run go file # go run hello.go 5. go build # compile the code into executable go build directly 6. go install # similar to go build\texcept the executable is put in bin folder 7. pkg folder # for external dependencies 8. var vs := # var x int = 5 var y int = 6 var sum int = x + y x := 5 y := 7 sum := x + y 9. if else if else # no bracket 10. array # // approach 1 to initialize var a [5]int // approach 2 to initialize a := [5]int{5,4,3,2,1} // modify the element by index a[2] = 11 // dynamic array(slice is backed by array) a := []int{5,4,3,2,1} // slices doesn\u0026#39;t modify the original slice, it returns a new one a = append(a, 13) 11. map # vertices := make(map[string]int) vertices[\u0026#34;triangle\u0026#34;] = 1 vertices[\u0026#34;square\u0026#34;] = 2 vertices[\u0026#34;circle\u0026#34;] = 3 // get vertices[\u0026#34;square\u0026#34;] // delete delete(vertices, \u0026#34;square\u0026#34;) 12. for # no ++i\n12.1 for loop # for i := 0; i \u0026lt; 5; i++ { // do something } 12.2 while loop # i := 0 for i \u0026lt; 5 { // do something i++ } 12.3 iterate array/map with range # // with array arr := []string{\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;, \u0026#34;c\u0026#34;} for index, value := range arr { fmt.Println(\u0026#34;index\u0026#34;, index, \u0026#34;value\u0026#34;, value) } // with map m := make(map[string]string) m[\u0026#34;a\u0026#34;] = \u0026#34;A\u0026#34; m[\u0026#34;b\u0026#34;] = \u0026#34;B\u0026#34; for key, value := range m { fmt.Println(\u0026#34;key\u0026#34;, key, \u0026#34;value\u0026#34;, value) } 13. function # func sum(x int, y int) int { return x + y } 13.1 multiple return values # package main import ( \u0026#34;fmt\u0026#34; \u0026#34;errors\u0026#34; \u0026#34;math\u0026#34; ) func main() { result, err := sqrt(16) if err != nil { fmt.Println(err) } else { fmt.Println(result) } } func sqrt(x float64) (float64, error) { if x \u0026lt; 0 { return 0, errors.New(\u0026#34;bla bla bla\u0026#34;) } return math.Sqrt(x), nil } 14. struct # package main import ( \u0026#34;fmt\u0026#34; ) type person struct { name string age int } func main() { p := person(name: \u0026#34;Yixian\u0026#34;, age: 100) fmt.Println(p) fmt.Println(p.age) // get field } 15. pointer # func main() { i := 7 inc(\u0026amp;i) fmt.Println(i) } func inc(x *int) { *x++ // dereference the memory address } ","date":"22 May 2024","externalUrl":null,"permalink":"/notes/go/","section":"Notes","summary":"1. workspace # go env GOPATH\n1.1 file structures # /Users/username/go/src/project1 /Users/username/go/src/project2 2. package main # every project at least if we want to be executed it needs to have a package called package main 3. import # this is where we can import different packages no comma\n","title":"Go Syntax","type":"notes"},{"content":" general concepts # Role Based Access Control(RBAC): Once we login an account, we have different roles, different roles will see different contents. whether we are admin or external user, internal user. if we are external user, we have different vip levels or different discount, and so on. core # describe it within a minute(3-5 sentences, 30 secs)\ndefine industry, it need to be a part of business need to be a web app internal use or external use or hybrid tech stack details\nspring/springBoot/java/versions/database/frontend frameworks/\u0026hellip; pros \u0026amp; cons distributed? / frontend/ backend(java 8/11/13)/ database/ internal components(jdbc/ hibernate/ spring data jpa)/ how you implemented microservices(spring cloud or kubernetes)\ndata: frontend data/ what kind of payload/ what kind of format/ design dataflow: FE-\u0026gt;BE-\u0026gt;DB/ then we have architecture/ then in middle we should add buffering or caching(just add different tools to handle different problem, i.e. failure tolerance, scalability, \u0026hellip;.)\non-premise or on cloud(which cloud is used)\npeople we worked with\nAgile style/how big the team/what\u0026rsquo;s my role/different scenario we need to talk to different people who I talked with/ who I suppose to talk with/\u0026hellip; Project 1: Online Learning Platform # Description:\nAn Online Learning Platform where users can enroll in courses, access course materials, and track their progress. Instructors can create and manage courses, upload materials, and interact with students. The platform will include user authentication, a RESTful API, and a front-end interface. Features: # User Management: User registration and authentication (students and instructors). Profile management. Role-based access control. Course Management: Instructors can create, update, and delete courses. Upload course materials (videos, PDFs, etc.). Course categorization and tagging. Enrollment and Progress Tracking: Students can enroll in courses. Track progress through course completion. Certificate generation upon course completion. Interactive Components: Discussion forums for each course. Q\u0026amp;A section for student-instructor interaction. Search and Filtering: Search courses by keywords. Filter courses by category, difficulty level, and popularity. Admin Dashboard: Overview of platform statistics (number of users, courses, enrollments). Manage users and content. Technologies and Tools: # Backend: Spring Boot: Core framework for building the application. Spring Security: For authentication and authorization. Spring Data JPA: For database interactions. Hibernate: ORM framework. MySQL/PostgreSQL: Database. Spring Boot Actuator: Monitoring and management. Frontend: React.js: For building the user interface. Redux: For state management. Axios: For making API requests. API Documentation: Swagger/OpenAPI: For documenting the RESTful API. Testing: JUnit: Unit testing. Mockito: For mocking dependencies. Spring Boot Test: Integration testing. DevOps: Docker: Containerization of the application. CI/CD: Integration with GitHub Actions or Jenkins for continuous integration and deployment. Kubernetes: For orchestration (optional, for advanced deployment scenarios). Other Tools: Lombok: To reduce boilerplate code. MapStruct: For object mapping. Thymeleaf/Freemarker: (Optional) If you want to add server-side rendering capabilities. Implementation Steps: # Project Setup: Initialize a new Spring Boot project using Spring Initializr. Set up the project structure and dependencies. User Management Module: Implement user registration and login using Spring Security. Set up role-based access control. Course Management Module: Create entities for Course, Material, Enrollment, etc. Develop RESTful APIs for CRUD operations. Frontend Development: Set up a React project. Create components for user authentication, course listings, and course details. Implement state management using Redux. Interactive Components: Add features for discussion forums and Q\u0026amp;A sections. Implement real-time updates using WebSockets (optional). Admin Dashboard: Develop an admin interface for managing the platform. Display key metrics and provide management functionalities. Testing: Write unit and integration tests. Ensure code coverage and reliability. Documentation and Deployment: Document the API using Swagger/OpenAPI. Containerize the application using Docker. Set up CI/CD pipelines for automated testing and deployment. Final Steps: # Code Quality: Ensure your code is clean, well-documented, and follows best practices. Deployment: Deploy the application to a cloud platform (e.g., AWS, Azure, Heroku). Showcase: Add the project to your GitHub or personal portfolio. Write a comprehensive README.md file explaining the project\u0026rsquo;s features, setup instructions, and usage. Project 1 Plan: Online Learning Platform # Phase 1: Project Setup and Basic Configuration # Initialize Spring Boot Project: Use Spring Initializr to create a new Spring Boot project. Add dependencies: Spring Web, Spring Security, Spring Data JPA, MySQL/PostgreSQL Driver, Lombok, and Thymeleaf (if using server-side rendering). Set Up Database: Configure database connection in application.properties or application.yml. Create initial database schema using JPA entities. Phase 2: User Management # User Entity and Repository: Define User entity with fields such as id, username, password, email, roles, etc. Create UserRepository interface. User Registration and Authentication: Implement registration endpoint. Use Spring Security for authentication. Set up JWT or session-based authentication. Role-Based Access Control: Define roles (e.g., STUDENT, INSTRUCTOR, ADMIN). Configure method-level security for role-based access. Phase 3: Course Management # Course Entity and Repository: Define Course entity with fields like id, title, description, instructor, materials, etc. Create CourseRepository interface. Course CRUD Operations: Implement RESTful APIs for creating, reading, updating, and deleting courses. Secure endpoints to allow only instructors to manage their courses. Phase 4: Frontend Development # Initialize React Project: Set up a new React project using Create React App. Install necessary dependencies: Axios, Redux, React Router. Authentication Components: Create components for login, registration, and profile management. Implement Redux actions and reducers for user authentication. Course Components: Develop components for course listing, course details, and course creation. Use Axios to interact with backend APIs. Phase 5: Enrollment and Progress Tracking # Enrollment Entity and Repository: Define Enrollment entity to track course enrollments. Create EnrollmentRepository interface. Enrollment API: Implement endpoints for students to enroll in courses. Add functionality to track and update course progress. Progress Tracking: Implement logic to calculate and store course completion status. Develop UI components to display progress. Phase 6: Interactive Components # Discussion Forum: Define entities for discussions and comments. Create APIs to manage discussions within courses. Q\u0026amp;A Section: Implement endpoints for posting and answering questions. Develop UI components to interact with the Q\u0026amp;A section. Phase 7: Admin Dashboard # Admin Dashboard UI: Create an admin dashboard interface using React. Display key metrics like number of users, courses, enrollments, etc. Admin APIs: Implement endpoints for managing users and courses. Secure admin endpoints to restrict access to admin users only. Phase 8: Testing and Documentation # Testing: Write unit tests for services and controllers using JUnit and Mockito. Create integration tests for API endpoints. API Documentation: Use Swagger to document RESTful APIs. Generate and host API documentation. Phase 9: Deployment and CI/CD # Containerization: Create Dockerfile for the Spring Boot application. Create Dockerfile for the React application. CI/CD Pipeline: Set up a CI/CD pipeline using GitHub Actions or Jenkins. Automate testing, building, and deployment processes. Cloud Deployment: Deploy the application to a cloud provider (e.g., AWS, Azure, Heroku). Configure domain, SSL, and other necessary services. Final Steps: # Code Review and Refactoring: Conduct thorough code reviews. Refactor code for better readability and performance. Documentation: Write comprehensive documentation for the project. Include setup instructions, API usage, and contribution guidelines in the README.md file. Portfolio and Presentation: Add the project to your GitHub portfolio. Prepare a demo or presentation to showcase the project during interviews. ","date":"20 May 2024","externalUrl":null,"permalink":"/notes/projects/","section":"Notes","summary":"general concepts # Role Based Access Control(RBAC): Once we login an account, we have different roles, different roles will see different contents. whether we are admin or external user, internal user. if we are external user, we have different vip levels or different discount, and so on. core # describe it within a minute(3-5 sentences, 30 secs)\n","title":"Projects","type":"notes"},{"content":" S: Single-responsibility Principle\nO: Open-closed Principle\nL: Liskov Substitution Principle\nI: Interface Segregation Principle\nD: Dependency Inversion Principle\nReference S - Single-responsibility Principle # DEFINE: The interface responsibility should be single and not take on too many responsibilities. Applies to interfaces, classes, and methods. The focus of the single responsibility principle lies in the division of responsibilities, which is often not static and needs to be determined based on the actual situation. PROS: It can reduce class complexity, clarify responsibilities between classes, improve code readability, and make it easier to maintain. CONS: The knowledge and skills required of technicians are high, and sometimes it is difficult to distinguish between responsibilities. O - Open-closed Principle # DEFINE: when someone else wants to modify the software function, he cannot modify our original code and can only add new code to achieve the purpose of modifying the software function. EXAMPLE: we should can only create a new class to implement interface. L - Liskov Substitution Principle # DEFINE: For users, where the parent class can be used, its subclasses can also be used, and the expected results are consistent. It not only refers to the syntax level consistency, but also includes the implementation consistency. I - Interface Segregation Principle # DEFINE: Don\u0026rsquo;t throw a large and comprehensive interface to users, but separate the interface that each user cares about. D - Dependency Inversion Principle # It is recommended that users rely on an abstract class or interface instead of relying on a implementation. SOLID - GPT # The SOLID principles are a set of five design principles intended to make software designs more understandable, flexible, and maintainable. They were introduced by Robert C. Martin (also known as Uncle Bob). Each letter in the acronym SOLID stands for one of the principles:\nS - Single Responsibility Principle (SRP) O - Open/Closed Principle (OCP) L - Liskov Substitution Principle (LSP) I - Interface Segregation Principle (ISP) D - Dependency Inversion Principle (DIP) 1. Single Responsibility Principle (SRP) # Definition: A class should have only one reason to change, meaning that a class should have only one job or responsibility.\nExplanation:\nEach class should focus on a single task or responsibility. If a class handles multiple responsibilities, it becomes more complex and harder to maintain. Changes in one responsibility may affect the others, leading to fragile designs. 2. Open/Closed Principle (OCP) # Definition: Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.\nExplanation:\nWe should be able to add new functionality to a class without modifying its existing code. This is typically achieved through abstraction and polymorphism. Helps in minimizing the risk of introducing bugs in existing code when new features are added. 3. Liskov Substitution Principle (LSP) # Definition: Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.\nExplanation:\nSubtypes must be substitutable for their base types. Derived classes must extend the base class without changing its behavior. Ensures that a derived class can be used wherever the base class is expected. 4. Interface Segregation Principle (ISP) # Definition: Clients should not be forced to depend on interfaces they do not use.\nExplanation:\nSplit large interfaces into smaller, more specific ones so that clients only need to know about the methods that are of interest to them. Reduces the impact of changes, as clients are not affected by methods they do not use. 5. Dependency Inversion Principle (DIP) # Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.\nExplanation:\nDecouple software modules by introducing interfaces or abstract classes. High-level modules define the interfaces that low-level modules implement, reversing the typical dependency direction. Summary # SRP: One class, one responsibility. OCP: Open for extension, closed for modification. LSP: Subtypes must be substitutable for their base types. ISP: Prefer smaller, specific interfaces over a large, general-purpose interface. DIP: Depend on abstractions, not on concrete implementations. These principles help create more modular, maintainable, and flexible codebases, improving software quality and reducing the risk of bugs and technical debt.\n","date":"29 April 2024","externalUrl":null,"permalink":"/notes/solid/","section":"Notes","summary":" S: Single-responsibility Principle\nO: Open-closed Principle\nL: Liskov Substitution Principle\nI: Interface Segregation Principle\nD: Dependency Inversion Principle\nReference S - Single-responsibility Principle # DEFINE: The interface responsibility should be single and not take on too many responsibilities. Applies to interfaces, classes, and methods. The focus of the single responsibility principle lies in the division of responsibilities, which is often not static and needs to be determined based on the actual situation. PROS: It can reduce class complexity, clarify responsibilities between classes, improve code readability, and make it easier to maintain. CONS: The knowledge and skills required of technicians are high, and sometimes it is difficult to distinguish between responsibilities. O - Open-closed Principle # DEFINE: when someone else wants to modify the software function, he cannot modify our original code and can only add new code to achieve the purpose of modifying the software function. EXAMPLE: we should can only create a new class to implement interface. L - Liskov Substitution Principle # DEFINE: For users, where the parent class can be used, its subclasses can also be used, and the expected results are consistent. It not only refers to the syntax level consistency, but also includes the implementation consistency. I - Interface Segregation Principle # DEFINE: Don’t throw a large and comprehensive interface to users, but separate the interface that each user cares about. D - Dependency Inversion Principle # It is recommended that users rely on an abstract class or interface instead of relying on a implementation. SOLID - GPT # The SOLID principles are a set of five design principles intended to make software designs more understandable, flexible, and maintainable. They were introduced by Robert C. Martin (also known as Uncle Bob). Each letter in the acronym SOLID stands for one of the principles:\n","title":"SOLID","type":"notes"},{"content":" SAA-02 Reference Global Services # Identity and Access Management (IAM) Route 53 (DNS service) CloudFront (Content Delivery Network) WAF (Web Application Firewall) Region-scoped # Amazon EC2 (Infrastructure as a Service) Renting virtual machines (EC2) Storing data on virtual drives (EBS) Elastic File System (EFS) Distributing load across machines (ELB) Scaling the services using an auto-scaling group (ASG) Elastic Beanstalk (Platform as a Service) Lambda (Function as a Service) Rekognition (Software as a Service) Concepts # RDS Multi-AZ = Synchronous = High Availability Read Replica = Asynchronous = Disaster Recovery (DR) IAM # which aws aws --version aws configure aws iam list-users Section 7: EC2 Instance Storage # EBS # General Purpose SSD, gp2, gp3 # gp3: IOPS and throughput are not linked. gp2: IOPS and throughput are linked. Provisioned IOPS (PIOPS) SSD, io1/io2 # Use Case: Critical business applications with sustained IOPS performance. Or applications that need more than 16000 IOPS Great for database workloads(sensitive to storage perf and consistency) PIOPS and storage size are not linked just like gp3 Hard Disk Drives(HDD), st1, sc1 # cannot be boot volume\nThroughput Optimized HDD(st1)\nBig Data, Data Warehouses, Log Processing Cold HDD(sc1)\nFor data that is infrequently accessed EBS Multi-Attach - io1/io2 family # attach the same EBS volume to multiple EC2 instances in the same AZ Up to 16 EC2 instances at a time. EBS Encryption # create snapshot copy snapshot \u0026amp;\u0026amp; encrypt snapshot create volume from snapshot \u0026amp;\u0026amp; encrypt volume EFS # EFS works with EC2 instances in multi-AZ Section 8: High Availability and Scalability: ELB \u0026amp; ASG # Section 9: AWS Fundamentals: RDS + Aurora + ElastiCache # Section 10: Route 53 # Domain Registrar: Amazon Route 53, GoDaddy, \u0026hellip; DNS Records: A, AAAA, CNAME, NS, \u0026hellip; Zone File: contains DNS Records Name Server: resolves DNS queries (Authoritative or Non-Authoritative) Top Level Domain (TLD): .com, .us, .in, .gov, .org, \u0026hellip; Second Level Domain (SLD): amazon.com, google.com, \u0026hellip; DNS SOA(Start of Authority) record How DNS works # Section 11: Classic Solutions Architecture Discussions # Section 12: Amazon S3 Introduction # Section 13: Advanced Amazon S3 # Section 14: Amazon S3 Security # Section 15: CloudFront \u0026amp; AWS Global Accelerator # Section 16: AWS Storage Extras # Section 17: Decoupling applications: SQS, SNS, Kinesis, Active MQ # Section 18: Containers on AWS: ECS, Fargate, ECR \u0026amp; EKS # Section 19: Serverless Overviews from a Solution Architect Perspective # Section 20: Serverless Solution Architecture Discussions # Section 21: Databases in AWS # Section 22: Data \u0026amp; Analytics # Section 23: Machine Learning # Rekognition: face detection, labeling, celebrity recognition Transcribe: audio to text Polly: text to audio Translate: translations Lex: build conversational bots - chatbots Connect: cloud contact center Comprehend: NLP SageMaker: ML for every developer and data scientist Forecast: build high accurate forecasts Kendra: ML-powered search engine Personalize: real-time personalized recommendations Textract: extract text from file Section 24: AWS Monitor \u0026amp; Audit: CloudWatch, CloudTrail \u0026amp; Config # Section 25: Identity and Access Management(IAM) - Advanced # Section 26: AWS Security \u0026amp; Encryption: KMS, SSM Parameter Store, Shield, WAF # Section 27: Networking - VPC # Gateway VPC endpoints: Amazon S3 and DynamoDB, with route we don\u0026rsquo;t need public IP addresses Interface VPC endpoints: are used by all the other services that are becoming available inside our VPC. PrivateLink: we can use this to share our services with thousands of other VPCs. Section 28: Disaster Recovery \u0026amp; Migrations # Section 29: More Solution Architecures # Section 30: Other Services # ","date":"29 April 2024","externalUrl":null,"permalink":"/notes/aws/","section":"Notes","summary":" SAA-02 Reference Global Services # Identity and Access Management (IAM) Route 53 (DNS service) CloudFront (Content Delivery Network) WAF (Web Application Firewall) Region-scoped # Amazon EC2 (Infrastructure as a Service) Renting virtual machines (EC2) Storing data on virtual drives (EBS) Elastic File System (EFS) Distributing load across machines (ELB) Scaling the services using an auto-scaling group (ASG) Elastic Beanstalk (Platform as a Service) Lambda (Function as a Service) Rekognition (Software as a Service) Concepts # RDS Multi-AZ = Synchronous = High Availability Read Replica = Asynchronous = Disaster Recovery (DR) IAM # which aws aws --version aws configure aws iam list-users Section 7: EC2 Instance Storage # EBS # ","title":"AWS","type":"notes"},{"content":" QuickNotes # singleton vs immutable class functional interface with static Core Java interview questions # Java is passed by Value or Reference? # In Java, there are two kinds of data type, Primitive data types and Non-primitive data types. For primitive types, they are built-in data types in Java including int/short/long/float/double/char/byte/boolean. They are all passed by value. For non-primitive types, they are also passed by value, the value here is actually the memory address of the object. e.g. annotation, class, interface, enum, array. In conclusion all data types in Java are passed by value. static keyword(within 5 sentences) # we have four places to put static keyword on.(class, method, variable, block) For having static keyword on method, variable scope is to ensure that they can share just by class template. what SCOPES we can access static elements. we cannot override static method static class cannot called by non-static method static block # static block is a set of instructions that is run only once when a class is loaded into memory. We use a static block to initialize static variables. Although we can initialize static variables directly during declaration, there are situations when we need to do multiline processing. In such cases, static blocks come in handy. If static variables require additional, multi-statement logic during initialization, we can use a static block. Below are a few reasons for for using static blocks: if the initialization of static variables needs some additional logic apart from the assignment if the initialization of static variables is error-prone and needs exception handling static class # In general, the nested class architecture is divided into two types:\nnested classes that we declare static are called static nested classes nested classes that are non-static are called inner classes The main difference between these two is that the inner classes have access to all members of the enclosing class ( including private ones), whereas the static nested classes only have access to static members of the outer class.\nIn fact, static nested classes behave exactly like any other top-level class, but are enclosed in the only class that will access it, to provide better packaging convenience.\nBasically, a static nested class doesn’t have access to any instance members of the enclosing outer class. It can only access them through an object’s reference.\nstatic nested classes can access all static members of the enclosing class, including private ones.\nJava programming specification doesn’t allow us to declare the top-level class as static. Only classes within the classes (nested classes) can be made as static.\nHashmap workflow # Internally, Hashmap is an array of linkedList.\nEach of the key-value pairs is passed into the hashCode method first, the key is going to pass as the input and generate values.\nWith hashing result key-value pair is put in one of the place in the array, which is called bucket head.\nWhen there is any of hash code collision happened, then that\u0026rsquo;s the reason why we need to override both equals and hashCode methods.\nAnd when hash collision happened, then we are going to check if the two keys are exactly the same then we upate the value.\nIf they are don\u0026rsquo;t, that means there is a hash collision, where we are going to attach the same key at the same bucket head, which that\u0026rsquo;s the reason why we need the LinkedList to handle hash collision.\nBecause the search in LinkedList is going to be linearly grows regarding to the time complexity. That\u0026rsquo;s why we need LinkedList to auto turning into Red-Black tree for later search time efficiency.\nHow many access modifiers? # public: visible in all classes in all packages protected: visible to all classes in the same package or classes in other packages that are a subclass default: visible to all classes in the same package private: visible only in the same class The purpose of having access modifies is to ensure that we can do encapsulation implementation for our OOP program in Java.\nPros and Cons of static # Diff map and set. Can we have duplicate keys in map # set is part of Collection interface, where as map is not part of Collection interface map is storing two dimension data, set is storing one dimension data. The HashSet is actually implemented by the HashMap in Java Given a series data, how to detect duplicate data and skip it? # Comparable vs Comparator # First, they are both functional interface, annotated with @FunctionalInterface Comparable has one method compareTo Comparator has one method compare Comparable is used to define the natural ordering of objects within the class itself. Comparator is used for external comparison logic. How GC works / GC generation # When Java programs run on the JVM, objects are created on the heap, which is a portion of memory dedicated to the program. Eventually, some objects will no longer be needed. The garbage collector finds these unused objects and deletes them to free up memory.\nA generational garbage collector collects the short-lived objects more frequently than the longer lived ones. Short-lived objects are stored in the first generation, generation 0. The longer-lived objects are pushed into the higher generations, 1 or 2. The garbage collector works more frequently in the lower generations than in the higher ones.\nWhen an object is first created, it is put into generation 0. When the generation 0 is filled up, the garbage collector is invoked. The objects that survive the garbage collection in the first generation are promoted onto the next higher generation, generation 1. The objects that survive garbage collection in generation 1 are promoted onto the next and the highest generation, generation 2. This algorithm works efficiently for garbage collection of objects, as it is fast.\nGenerics in Java, what is it, why we need it? # Type erasing in Java, about generics # what/why generic # How to handle exceptions # Checked vs Unchecked exception # Threadpool 的 coding # No: ExecutorService tp1 = Executors.newFixedThreadPool(4); ExecutorService tp2 = Executors.newSingleThreadExecutor(); ExecutorService tp3 = Executors.newCachedThreadpool(); Yes: new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, taskQueue, ); import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; public class ThreadPoolExecutorExample { public static void main(String[] args) { // Core and maximum pool size int corePoolSize = 2; int maximumPoolSize = 4; // Keep-alive time for extra threads beyond the core pool size long keepAliveTime = 10; TimeUnit unit = TimeUnit.SECONDS; // Task queue with a capacity of 2 LinkedBlockingQueue\u0026lt;Runnable\u0026gt; queue = new LinkedBlockingQueue\u0026lt;\u0026gt;(2); // Custom thread factory to name threads ThreadFactory threadFactory = new ThreadFactory() { private final AtomicInteger threadNumber = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, \u0026#34;CustomThread-\u0026#34; + threadNumber.getAndIncrement()); } }; // Custom rejected execution handler to handle tasks when the queue is full RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); // Creating the ThreadPoolExecutor ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, unit, queue, threadFactory, handler ); // Submit tasks to the executor for (int i = 1; i \u0026lt;= 10; i++) { final int taskId = i; executor.submit(() -\u0026gt; { System.out.println(Thread.currentThread().getName() + \u0026#34; is executing task \u0026#34; + taskId); try { // Simulate task processing time Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } // Shutdown the executor executor.shutdown(); try { if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); } } catch (InterruptedException e) { executor.shutdownNow(); Thread.currentThread().interrupt(); } } } Multi-threading concepts and coding # abstract, final, static # abstract: class, method final: class, method, variable static: inner class, method, variable, static block singleton vs immutable class # keywords # Sort methods # JDK and JRE # JDK = JRE + DEV tools (Javac Compiler etc.) JRE = JVM + Java SE standard library JVM # Method Area(since java 8, meta space(replaced perm generation)): class template, static elements Method Area is a standard, it keeps storing class templates Meta space can resize JVM, once the memory space is all used up. VM stack: references and method within stack, each thread be assign a private area within stack, everything within this area are only visible for that thread, unless we use volatile keyword. Heap: objects all the threads are able to access. Program Counter Register: persist return tracker, allows each method stack know when to return where Native Method stack: a place contains all legacy c/c++ APIs anything decorated with native keyword is a method, that implements with either c or c++ storing in Native Method stack within JVM. JVM memory model # What\u0026rsquo;s the result from the code1 # Green Black line 70 first the diff between RunOutOfMemory and OverStackFlow Exceptions in JVM # ClassLoader # Three types of classLoader Bootstrap ClassLoader: to load the first pure java ClassLoader Extension ClassLoader: to load extensions of core java from jdk extension library System/Application ClassLoader: to load application type classes Class class # It is used to describe the meta info inside a class. When a class is loaded from ClassLoader, one object of Class class will be created. With that object, we can call getClass() method or .class field to get the instance of the Class class. Reflection API # Give us the ability that during the runtime, we can inspect and modify classes, methods, and fields dynamically. It\u0026rsquo;s not necessary to know everything about an object or class at compile time, instead we can discover them during the runtime. Reflection API is powerful, but there are some performance considerations. Generally speaking, it\u0026rsquo;s slower than direct method calls, because it has overhead involves runtime type checking and method invocations. So in some performance-critical scenarios, we should not use Reflection API. Data Structures # Map is not in Collection framework. That\u0026rsquo;s why we cannot directly iterate key-value pairs within any Map implementation. Collection vs Collections # Collection framework is for data structures. Collections is a class. That class contains lots of static methods including reverse/sort. That can help us manipulate our data structures including arrays. what\u0026rsquo;s the result from the code2 # Person inherited from Object if we don\u0026rsquo;t override hashCode method, it\u0026rsquo;s basically comparing based on References. HashSet # if we check HashSet implementation, HashSet is actually implemented by HashMap. But only this HashMap doesn\u0026rsquo;t have any value, but only with the key. That\u0026rsquo;s the default one. Comparison between any pair of data structures # Java Flight Recorder # It\u0026rsquo;s a place that we can monitor our Java application. To check if there is any bottleneck for our web application regarding to the hardware and resources.\nJava Flight Recorder(since java 11 is free)\njconsole\nIntelliJ profiler\nSpring Actuator\nGeneric Data type # Why: it gives us the ability that to make classes and methods more flexible and reusable by allowing them to operate on various types. It also provides compile-time type safety. How: type bounds: there are two types of bounds: upper bound(extends, \u0026lt;), lower bound(super, \u0026gt;=) wildcard generics: it has greater flexibility, and it can also be bounded with those extends and super keyword. i.e. ArrayList\u0026lt;? super String\u0026gt; list = new ArrayList(); Enum # it\u0026rsquo;s a structure that contains some default values. i.e. some of constant values. like Days, Months, Weeks, etc. enum can implement interfaces enum Day { SUNDAY, MONDAY, FRIDAY } without Enum # Annotations # There are two types so annotation in Java: Normal annotation and Meta annotation. Meta annotations like parents or ancestors for decorating all the other annotations you can have in Java program. There are only four meta annotations since Java 8. Retention: (means how long) it describes when this annotation is playing in Java program SOURCE: after convert the annotation into the .class file, the annotation is going to be invalid. CLASS (default): still valid with the .class file, after we loaded with ClassLoader, in the runtime, that annotation is not working anymore RUNTIME: this annotation, starting from the .java file, compile to the .class, loading with the ClassLoader into JVM, until program is end. It keeps playing a role in our Java program Target: (means where) where can we put the annotation on Type FILED METHOD PARAMETER CONSTRUCTOR LOCAL_VARIABLE ANNOTATION_TYPE PACKAGE TYPE_PARAMETER TYPE_USE Documented: define if it is documented Inherited: define if this annotation can inherit from parent to the child Exception Handling # if it\u0026rsquo;s checked exception, we have to extend from Exception class\nif it\u0026rsquo;s unchecked exception, we need to extend from RuntimeException class\nConfluence Page: a private library each company can have, they are going to share some of those common knowledge for web application on that page\nservice(first 2 numbers) to codeNumber instagram 10 -\u0026gt; 01 login exceptions -\u0026gt; 02 picture uploading exceptions facebook 20 heel 30 \u0026hellip; i.e. 10-001 is the error code when we trying to customize our Exceptions, we need to ensure what the type of exceptions it is(extends Exception or extends RuntimeException), then what kind of infomation that we need to pass(those public methods), last * messages and error code* are traditional ones that almost every customize exceptions can have.\nInheritance in Exceptions # the child class can only have same or narrower scope. i.e. parent throws Exception, child should throws Exception(same) or IOException(narrower) IOStream # There are two dimensions: whether the stream is IN or OUT, that\u0026rsquo;s we have in and out as direction based on the minimum unit to transfer the characters or file, we have either the Byte(8 bits, ByteStream) as the minimum unit or Character(two Bytes, 16 bits, CharacterStream) FileInputStream, reading file in 8 bits each time FileoutputStream, writing from JVM RAM to hard disk in 8 bits each time FileReader, reading file from hard disk to JVM in 16 bits each time FileWriter, writing file from JVM to hard disk in 16 bits each time CharArrayReader, writing file from JVM to hard disk in 16 bits each time pay attention to Bufffered ones. They are just wrapping up the normal I/O Stream. It\u0026rsquo;s trying to do increase the communication speed.\nDEFAULT_BUFFER_SIZE = 8192 bytes, everytime with buffered we have 8 mb into JVM, instead of 1 byte or 2 bytes. In real practice example # Due to AutoCloseable is inheritened by buffered streams. means once we turn off the wrapper buffer stream, it will also turn off the inside so we just need to close the buffered one, the inside will close automatically. Another good thing about AutoCloseable interface, is that once we implement that we can use try-with-resource virtual thread is the future # Java 8 new features # Lambda expressions it\u0026rsquo;s just method without a name it\u0026rsquo;s passing a function as annoymous object into Java program. it\u0026rsquo;s just a annoynous method wrapped up with an annoynoms object. That\u0026rsquo;s the object is the actually the thing we passed to our Java program. Functional Interface it\u0026rsquo;s a kind of interface that has only 1 abstract method.\nit can have multiple default methods and multiple static methods.\nwhen use default methods:\nwhen this functional interface has been implemented by different classes and later we need to use it as a common function for each every those classes. when use static methods:\nwhen we don\u0026rsquo;t want instantiate anything, any object. Or if we don\u0026rsquo;t want this default method to be override, then we can use static method rather than default method to define this common behavior. i.e. Runnable, Callable, Comparator, Comparable, etc.\nThere are four major functional interface introduced in Java 8\nConsumer Supplier Function Predicate Stream API It\u0026rsquo;s let java can program in a way just like other functional programming language. Two types of stream: finite stream normal stream() parallelStream() infinite stream iterate(0, t -\u0026gt; t + 2).limit(10).forEach(System.out::println) generate(Math::random).limit(10).forEach(System.out::println) Two types of operations intermediate operations terminal operations it will keep original data intact.\nMethod Reference is a further utilization of Lambda Expression. which is also a functional interface. it is trying to have a simplicity of the object and methods relationship that we can have. and return that into functional interface. with that functional interface will help us to write stream API through functional style programming. The only thing we cannot reference is abstract method, as long as the method has a method body, that means we can refer to it. Optional Class\nit can help to handle NullPointerExceptions it\u0026rsquo;s just warp the original object, to handle the situation like null reference CompletableFuture\nMultithreading # when we are talking about traditional thread, we should always use thread pool.\nvirtual thread is good and is the future, but it\u0026rsquo;s not available for old java versions.\nthe first two ways to create new thread cannot throw any Exceptions, and no return for run method.\nthe third one can have return data types and also can throw Exceptions. we can set priority for each thread from 1(least significant) - 10(most significant)\nThread Life Cycle # NEW RUNNABLE BLOCKED WAITING TIMED_WAITING TERMINATED ThreadPool(or ExecutorService) # Summary # Thread: Simple and straightforward for quick-and-dirty threading needs. Runnable: Preferred when you don\u0026rsquo;t need to return a result from the task. Callable: Use when you need to return a result or handle exceptions from the task. CompletableFuture: Ideal for asynchronous programming and chaining tasks. ThreadPoolExecutor: Provides extensive control over thread pool behavior for complex threading requirements. VirtualThread: Use for scalable and lightweight concurrency (note: still in preview as of Java 19). Lock(or Monitor) # critical section: we have some threads, those threads are going to manipulate some common resources. data type built-in lock # lock with synchronized keyword # can be applied to method scope and synchronized block Lock interface implementations # Lock is an interface, its implementation including ReentrantLock, Condition, ReadWriteLock comparing to synchronized keyword. when we use it if we want to ensure the execution of the thread order is exactly the way that I want. I need to use ReentrantLock and Condition. Avoid!!! # be careful when use synchronized and static\nclass template lock: means any of those static methods are locked with the same lock.\nbecause static element is saved in method area in JVM, it\u0026rsquo;s a class template lock. the following is not thread safe\nthe first synchronized keyword without static is on the object lock. the second synchronized keyword with static is on the class template lock. Dead Locks # caused by adding lock in wrong orders how to avoid: no matter the order we add the lock, we should have the exact the same order to release the lock. Callable vs CompletableFuture!!! # For Callable, every time we call the get api from the FutureTask, it\u0026rsquo;s going to block the main thread. For CompletableFuture, we gonna have a callback. And CompletableFuture can be chained. it means the callback will trigger another task, so on so forth. when everything is done, the main thread will come back and get the result. then continue to running. Cons for CompletableFuture, it\u0026rsquo;s hard to handle Exceptions all over the place. volatile keyword: visibility only # anything decorated with volatile, means it\u0026rsquo;s visible in between those threads. volatile element will put in main memory. it\u0026rsquo;s not thread safe. it just ensure visibility every time each thread will get the latest from the main memory. it also ensures the instructions are not reordered(in assembly code). Usage: when we have a singleton design pattern, and we wanna singleton design pattern visible for all threads. And at the moment we need to add the volatile keyword on that singleton object reference. CAS: Compare and Swap # it\u0026rsquo;s another mechanism to ensure the thread-safety.\nit\u0026rsquo;s special that it doesn\u0026rsquo;t contain any lock.\nit uses self-spinning mechanism to ensure that object value or any of those common resource value is going to be upgraded with the correct version. i.e. AtomicInteger\ni.e. AtomicReference CAS vs Lock # comparing the performance regarding to the while loop and lock. the nature of the computation(something like keep back and forth) then it suit for CAS. However, if it takes very long time to back to the original state, we should use lock. AtomicReference is going to create ABA problems # AtomicStampedReference is going to solve that. how: it\u0026rsquo;s instead of just checking the value, it also creates a tag for each of the value(so, instead of checking the values, we also checking the versions). it allows us to tracking if there is a ABA problem or not. practices # 5,5,5,10,10,10,15,15,15 5,10,15,5,10,15,5,10,15 Design Patterns # what/why/where to use/how to use Singleton # reference there is only one object we can create from the class. pros: singleton can work like a global variable cons: it\u0026rsquo;s hard to do a unit test. it needs special treatment in multi-threading programming Simple Factory # reference there is a creation method with some parameters. with those parameters we can choose which class to use for instantiating the object. pros: it makes the code loosely coupled. cons: over time, the methods might become too big. in this case, we need to convert the simple factory design pattern into Factory Method, to introduce some subclasses to handle this situation. Builder # is used to simplify the process of building a complex object. pros: we can construct object step by step, and reuse the construction codes. cons: it would have many extra codes like having many extra classes and methods Builder vs Factory # Factory design pattern hides the process of object creation. Users just ask for what they need. Builder design pattern allow users to know what is set for the object. Proxy # In practice, the proxy has the same interface as a service, which makes it interchangeable with a real object when passed to a client. There are two types of proxy: the static proxy and the dynamic proxy. The static proxy requires developer to know the target class in advance, whereas the dynamic proxy doesn\u0026rsquo;t, because the proxy class can be dynamically created by JVM during the runtime. Adaptors # Observers # OSI model vs TCP/IP model(won\u0026rsquo;t be asked during the interview) # OSI model # 7 layers architecture: Physical / Data Link / Network / Transport / Session / Presentation / Application a typical modern network architecture As a developer, we will only focus on the top layer called Application Layer including HTTP, FTP, SSH, DNS, etc. TCP/IP model # a simplified version network architecture 4 layers architecture: Network Access Layer[1,2] / Network / Transport / Application[5,6,7] TCP, UDP within Transport Layer # divides the data in to many packets\nwithin the packet of TCP, has Sequence Number\nwithin the packet of UDP, no Sequence Number\nTCP is more secure, i.e. email\nUDP is faster, i.e. zoom meeting\nTCP 4 way handshake process is more secure than 3 way handshake process\nHTTP # a protocol in the Application Layer\nHTTP Request\nRequest Message Header Request Line http methods(get, post, \u0026hellip;) file address http version Request Headers some key-value pairs a blank line Request Message Body(normally is a json file, but can be anything) HTTP Response\nResponse Message Header Status Line http version http status code Response Headers some key-value pairs a blank line Response Message Body(normally is a json file, but can be anything) HTTP methods # Safe: the http method does not change the state of the server, does not changing on the server side. Idempotent: two or more same requests are made to backend, and it will have the same effect. Cacheable: the browser caches some data for some http methods by default. (we can setup with cache-control) Methods Safe Idempotent Cacheable Description Get yes yes yes read Put no yes no whole update Post no no no create Delete no yes no remove Patch no no(or yes) no patch update headoptions/ trace/ connect HTTP status codes # 1xx: informational codes 2xx: success codes 3xx: redirection codes 4xx: client error codes 5xx: server error codes 200 OK -- the request has been successfully sent to the backend. 201 Created -- a new resource was created as a result. 202 Accepted -- the request has been accepted for processing, but the processing has not been completed. 204 No Content -- the request succeeded, but that the client doesn\u0026#39;t need to navigate away from its current page. i.e. submitted some data with put method, the frontend doesn\u0026#39;t need to be changed. 307 Temporary Redirect -- i.e. the primary website doesn\u0026#39;t work due to some bug or maintenance, we need to redirect to backup server. 308 Permanent Redirect -- i.e. sometimes the old URL won\u0026#39;t use anymore, we wanna keep our customers. 400 Bad Request -- the server cannot understand the request, because some invalid syntax or invalid parameters. 401 Unauthorized -- the request has not been completed because it lacks valid authentication credentials. 403 Forbidden -- the server understands the request but refuses to authorize it. 404 Not Found -- the server cannot find the requested resource. 500 Internal Server Error -- the server has encountered a situation it does not know how to handle. Session and Cookies (won\u0026rsquo;t be asked during the interview) # Cookies is a piece of data stored on the browser of the client side. Every session has sessionID stored on the server side, is used to store some user info. Cookies is generated by the session, all the requests and responses will take Cookies. Java Web Architecture # Web Components(servlet, JSP): a unit can handle specific logic. Backend Web Server/Container # Web server is software. Server is hardware. Web server is running on the server to host the Application to handle the request.\nTomcat JBoss WebLogic TomEE WebSphere Three Layer Architecture (Web Application Three Layer Architecture) # MVC design pattern Controller : handle http request Service : business logic, some computation Dao(Repository) : data access object layer, is used to connect to DB the underlying technology behind Spring Data JPA is Hibernate. the underlying technology behind the Hibernate is JDBC.\nMaven/Gradle # it\u0026rsquo;s a tool that automate the building of the project into a jar or war file.\nIt is trying to do the Build Automation Build Automation has different stages, which is lifecycles: clean: clean up all the .class files that has generated validate: Checks if the project is correct and all necessary information is available. compile: Compiles the project\u0026rsquo;s source code. test package: Takes the compiled code and packages it in its distributable format, such as a JAR, WAR, or EAR file. pack this maven into jar package for local test. In real, it will done by CI/CD pipeline verify: Runs any checks to verify the integrity and quality of the project. install: install the jar package into the local repository for the maven, making it available for other projects on the same machine. site deploy: Copies the final package to the remote repository for sharing with other developers and projects. Maven folder structure # Types of Repositories in Maven # the idea about caching for the maven. When we flush our project on our IDE or local dev environment. First thing they are going to check is our local maven repository. If the local repository doesn\u0026rsquo;t have it, it will check the remote repository.\nLocal Repository ~/.m2 Remote Repository Central Repository Customized Remote Repository Maven Life Cycle # clean: clean cached data, cached dependencies. validate: check if necessary info is available. compile: compile the project. test: run all tests within test folder. package: we can package the project into jar or war. verify: check whether our code satisfy some criteria. install: install the project to local as dependency for other projects. .jar vs .war # jar file contains the embedded server like tomcat, whereas war file doesn\u0026rsquo;t. Spring IoC: Inversion of Control # We transfer the control of the object to the container of framework, which is ApplicationContext. ApplicationContext helps us manage all beans life cycle. It is a principle which transfer the control of the objects to a container or framework. Dependency Injection # We can inject the object into another object. Bean Scope # Singleton(default). The IoC container creates only one instance of the bean, and reuses it whenever that bean is requested. This is the default cope in Spring. Use Case: When you want to share a single instance of a bean across the entire application. Example: Configuration beans, controller beans, service beans, DAO beans, Logger beans, Utility beans. Prototype. Use Case: When you want to create a new instance of a bean every time it is requested. Example: Request-scoped beans in web applications, Prototype beans for stateful objects, Objects with complex initialization logic. Only valid in the context of a Spring Web ApplicationContext.\nRequest. Use Case: When you want to create a new instance of a bean for each HTTP request in a web application. Example: Controllers, Form objects, View helpers, Request-specific data objects. Session. Use Case: When you want to create a single instance of a bean for each user session in a web application. Example: User session beans, Shopping cart beans, User preferences beans, User authentication beans. Application. A single instance of the bean is created for the entire web application context. This means all requests and sessions share the same instance of the bean. Use Case: When you want to create a single instance of a bean for the entire web application context. Example: Application-wide configuration beans, Global caching beans, Shared resources across multiple sessions. WebSocket. Similar to session scope, but designed for WebSocket-based interactions. Use Case: When you want to create a single instance of a bean for each WebSocket connection. Example: WebSocket handlers, Session-specific WebSocket state beans. Custom Scopes. Use Case: When none of the built-in scopes meet your requirements, you can create custom scopes. Example: Tenant-specific beans, Conversation-scoped beans, Batch job-scoped beans. Choose Appropriate Scope # Singleton is suitable for stateless beans or beans that are expensive to create. Prototype is useful for stateful beans or beans that need to maintain their state separately. Request, Session, and Application scopes are suitable for beans that hold web-related state information and need to be scoped accordingly. Types of Dependency Injections # Constructor Injection: for mandatory dependencies so that our bean is ready to use when it is first time called. pros: All required dependencies are available at initialization time. it\u0026rsquo;s the only way for us to create immutable dependencies. It can avoid NullPointerException. it also simplifies unit test. Preventing errors in Tests cons: Setter Injection: only for optional dependencies to avoid circular dependencies. pros: cons: Filed Injection: pros: cons: it makes headache to test, so how do you test that without bring up spring context or using some type of reflection utilities to inject that. It can be done but it gives us a big headache when we do a private field in Autowired Bean Life Cycle # @PostConstruct @PreDestroy AOP # it enables us to monitor the methods, monitor the class, monitor everything without touching the service or some other classes. Usage of AOP # log something Audit the CRUD operations from http request(get, put, post, delete) monitor some special method, like once the method was invoked, use AOP to send email to the admin. Caching Transaction Security Terms(basic components of AOP) # Aspect\nAn aspect is a modular unit of cross-cutting concerns in AOP. It encapsulates behaviors (advice) that are applied to multiple join points in the application. Examples of aspects include logging, security, transaction management, and error handling. Advice: Advice is the action taken by an aspect at a particular Join Point during the execution of a program.\nbefore advice: Executed before a method invocation. after advice @AfterReturning: Executed after a method returns successfully. @AfterThrowing: Executed after a method throws an exception. @After(Finally): Executed regardless of the method outcome. around advice: Wraps a method invocation and controls its execution. Join Point: A join point is a specific point in the execution of a program where an aspect can be applied.\nExamples of join points include method invocations, method executions, field access, object instantiation, and exception handling. Point Cut: a expression to find where to inject our AOP logic\n@PointCut(\u0026quot;execution(* com.example.aop.springaop.*.*.*(..))\u0026quot;) first star means any return. @PointCut(\u0026quot;within(com.example.aop.springaop..*)\u0026quot;) @PointCut(\u0026quot;this(com.example.aop.springaop.service.ClassName)\u0026quot;) to find class @PointCut(\u0026quot;@annotation(com.example.aop.springaop.annotation.CutomAnnotation)\u0026quot;) to find method Target:\nThe target object is the object being advised by one or more aspects. It is the object whose methods are intercepted by advice during method invocation. Some Default Annotations # @Cacheable: is used to annotate methods to indicate that the results of method invocations can be cached. @Transactional: is used to annotate methods or classes to indicate that they are transactional. It is commonly used to ensure data consistency and integrity by managing database transactions declaratively. @Secured: is used to specify method-level security constraints. When applied to a method, Spring Security ensures that only users with the specified roles/authorities are allowed to execute the method. AOP workflow in Spring # underlying logic of AOP is Reflection Dynamic Proxy -\u0026gt; Reflection SpringBoot Advantages # auto configuration: to overwrite default configurations with application.properties file starter starter dependencies integrated many useful dependencies for us. In pom.xml file, it handles dependency version management and add dependency embedded Tomcat Spring Actuator @SpringBootApplication includes @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan \u0026hellip;. Annotations in SpringBoot # @Controller @RestController - @RestController contains @Controller and @ResponseBody. - if we only use @Controller to Controller class, we also have to add @RespondBody manually. @Service // the following two are same @RequestMapping(value = \u0026#34;/user/{id}\u0026#34;, method = RequestMethod.PUT) @PutMapping(\u0026#34;/user/id\u0026#34;) @Repository @RequestMapping @GetMapping @PutMapping @DeleteMapping @PostMapping @Component @Bean @ResponseBody: java object -\u0026gt; json @RequestBody: json -\u0026gt; java object // used to retrieve the data from URL @PathVariable - is used to retrieve the parameter before the question mark - i.e. http://.../api/users/12 @RequestParam - is used to retrieve the parameters after the question mark - we can specify if some parameters are required or not, we can also give them defaultValue - i.e. http://.../api/users/?pageNo=2\u0026amp;rows=10 @GenerateValue(strategy = GenerationType.AUTO) @Aspect @PointCut(\u0026#34;...\u0026#34;) public void loggingPointCut() {} @Before(\u0026#34;loggingPointCut()\u0026#34;) public void before(JointPoint joinPoint) { log.info(\u0026#34;...\u0026#34; + joinPoint.getSignature()); } @AfterReturning(value = \u0026#34;execution(* com.....)\u0026#34;, returning = \u0026#34;employee\u0026#34;) public void after(JointPoint joinPoint, Employee employee) { log.info(\u0026#34;...\u0026#34; + employee); } @AfterThrowing(value = \u0026#34;execution(* com.....)\u0026#34;, throwing = \u0026#34;e\u0026#34;) public void after(JointPoint joinPoint, Exception e) { log.info(\u0026#34;...\u0026#34; + e.getMessage()); } @Around(\u0026#34;loggingPointCut()\u0026#34;) public Object around(ProceedingJoinPoint joinPoint) throws Throwable { log.info(\u0026#34;...\u0026#34; + joinPoint.getArgs()[0]); Object object = joinPoint.proceed(); if (object instanceof Employee) { log.info(\u0026#34;...\u0026#34; + joinPoint.getArgs()[0]); } else if (object instanceof Department) { log.info(\u0026#34;...\u0026#34; + joinPoint.getArgs()[0]); } return object; } Spring MVC # Spring MVC is based on MVC design pattern, provides much convenience comparing to Spring Doesn\u0026rsquo;t provide any embedded server, whereas SpringBoot does(Tomcat). Flow of Spring MVC # DispatcherServlet is used to intercept all requests. send requests to HandlerMapping to choose Handler, find Controller Controller will go through Business Logic (Service(business logic) -\u0026gt; Repository(Data Access) -\u0026gt; Database), then return the result(ModelAndView Object) to DispatcherServlet The DispatcherServlet consults its list of ViewResolver beans to determine which view should be rendered. ViewResolver implementations map logical view names to actual view templates (JSP, Thymeleaf, FreeMarker, etc.). Once the view template is identified, the DispatcherServlet delegates the rendering of the view to the appropriate ViewResolver. The ViewResolver renders the view template, populating it with data from the model (if any), and generates the final HTML or other content. Finally, the DispatcherServlet sends the generated response (HTML, JSON, XML, etc.) back to the client that initiated the request. 3 Layers # Controller, Service, Dao Pojo # Plain Old Java Object it\u0026rsquo;s a type of java object that is not tied to any specific framework. it\u0026rsquo;s simple and lightweight. it just includes some fields, constructor, setter and getter methods. h2 database # it\u0026rsquo;s a in-memory relational database once we close the application, the data will lost. it\u0026rsquo;s useful for development and testing. Handle Exception in SpringBoot # @ExceptionHandler within class, is local @ExceptionHandler. @ControllerAdvice class with @ExceptionHandler, is global @ExceptionHandler. Is Exception annotated by @ResponseStatus? ResponseStatusExceptionResolver class : DefaultHandlerExceptionResolver class; How Spring does Validation # put @Validated within parameters of controller put the following annotation on pojo @Validated @NotNull @NotEmpty @Min @Max @Pattern @Email ... Documentation # Swagger # it\u0026rsquo;s a documentation framework it will collect info from project then generate a website show all the API created by the backend Questions in Mock interview # The difference between HashMap and HashTable # Regarding to Thread Safety HashTable is synchronized, so it\u0026rsquo;s thread-safe. HashMap is not synchronized by default, so it\u0026rsquo;s not thread-safe. But we can use synchronizedMap() method to create a synchronized wrapper around it. Regarding to Performance HashMap has better performance than HashTable, because HashTable is synchronized, it has some overhead. Regarding to Null keys and values: HashTable doesn\u0026rsquo;t support any null key or null value. HashMap support both null key and null value. And the null key will put at index 0. Regarding to Iteration We cannot iterate both of them during modification in another thread. Even with HashTable is thread-safe, we cannot do that. So we should find a way to make sure, during the iteration, there will not modification happen anywhere. Exceptions that we are familiar with # Checked Exceptions:\nit means they will be checked at compile-time. we must handle it in our code. either with try-catch block or throw its responsibility to up the call stack some universe place. i.e. IOException, SQLException, FileNotFoundException UnChecked Exceptions\nit\u0026rsquo;s runtime exception. The handling is optional, if we don\u0026rsquo;t handle it, it will propagate up the call stack until get caught or causing the program terminate. i.e. IndexOutOfBoundException, NullPointerException, ArithmeticException Java 8 new features # above In SpringBoot, the difference between Controller and RestController annotation # @RestController contains @Controller and @RespondBody. if we only use @Controller to Controller class, we also have to add @RespondBody manually. Bean Scopes # above Connection between frontend and backend # 1. Restful endpoint # stateless API protocol for transferring the payloads it\u0026rsquo;s the connection between the frontend and backend to sending the payload(data + something else(header\u0026hellip;)) 2. SOAP(earlier days) # stateful protocol for communication between FE and BE(or BE to other BE) Sharding \u0026amp; Replica # Replica # trade off between hardware resources and performance lose some C(Consistency) Sharding # partition database into different servers horizontal partition vertical partition: for flat and fat table QPS vs Throughput # QPS(query per second): dealing with a lot of users concurrently click the button Throughput: dealing with the size of the payload Database # we need to store backend data somewhere to ensure we have a better searching performance, comparing to File System, which would scan all the files in the file system. we need a certain structure that structure need to be efficient for searching it also need to be efficient for maintenance. it also need to ensure that it\u0026rsquo;s not over complicated(means that going through table of content must faster than going through file system) SQL # SQL database, supporting ACID principle(Atomicity, Consistency, Isolation, and Durability) structural, tabular database, good for vertical scaling ACID and transaction are interchangeable. transaction: a group of operation, i.e. CRUD operations. Once they are grouped up together, either all of them is going to be executed successfully, or none of them is going to be executed at all. it ensures the consistency in between database, business logics, user. it just adds locks to the database level. the idea is similar to Java, when we add locks(or monitor, interchangeable) to ensure the group of operations are Atomic(or Thread Safe, interchangeable) transaction\u0026rsquo;s thread safety is supported by the ACID principle. A(Atomicity): either all of them is going to be executed successfully, or none of them is going to be executed at all. C(Consistency): guarantee the CRUD operations\u0026rsquo; consistency. i.d. Deleted records won\u0026rsquo;t come back later. I(Isolation): there will be certain critical problems, which is thread safety problems. With database default settings cannot solve it(Dirty Writing and Dirty Reading). Dirty Writing is guaranteed to solved by database like Mysql for sure. Dirty Reading has several levels, different levels require different level of locks to solve the problem. Trade off between Less Thread Safety and Less Concurrency with #locks. D(Durability): once the record is inserted, the record will stay at there. i.e. Out of electricity won\u0026rsquo;t affect the record. Primary Key: basically consider as ID, for any of the record within the table it will be unique and cannot be null. Foreign Key: it\u0026rsquo;s a key that trying to simplify the content. can have duplicate values. The purpose is to save space which is also increasing searching efficiency. Normalization: the whole idea is to reduce redundancies and maintain integrity. There are 6 level of normalization forms we can have. First Normal Form: one value in each cell Sometimes we break up the normalization rules, that we keep the redundancies is to simplify the join logics in between the tables or simplify the logics of our application. From performance perspective, avoiding join operation can improve the performance a lot. Second Normal Form: Any columns in between the same row are not going to have dependencies. (this rule is to create foreign key relationship) Binding several columns as a Primary key is actually break this rule. Third Normal Form: No Transitive Dependencies. i.e. age column and DOB column. (this rule is to create one-to-many/many-to-one relationships) Indexes: Like the index of the book is to improve reading speed. if we define a table with Primary key but without Indexes. The primary key in default will be setup as the indexes. That\u0026rsquo;s index is going to be count as a cluster index(equal to Primary key, interchangeable). in some scenarios, we want to search in different style. Where we have non-clustered index. Any table can have as many as non-clustered index as we want. it\u0026rsquo;s good for reading operation, but bad for insert or delete operations. That\u0026rsquo;s also the reason we cannot have as many indexes as we want. non-clustered index is table of content. clustered index is the data. Views: A virtual table(just reference, that pointing to specific columns that supposed to be seen within the view) created from a query that includes one or more tables\u0026rsquo; data based on references we still can have update/delete those records in the table restrict data access from other user used to improve query speed simplify commonly used complex queries into one place Store Procedure: A function directly written inside the database. nowadays Postgres and Mysql supporting to store documents noSQL # noSQL, supporting transactional non tabular data, i.e. documents/files Documented DB Key-Document Pair Column wised DB It\u0026rsquo;s something like a table, but all the thing are going to store in the node, each column is consider as a * node*. When we searching, we search base on the node. So any non-related column won\u0026rsquo;t be searched. The whole purpose is to reduce I/O between RAM and HD. Key-Value DB. (i.e. Redis) Value can be anything, including int, string, arrList, set, sortedSet, \u0026hellip; Redis: natively running in the RAM database. The idea is it to cache some data to improve the performance. The purpose with Redis is to reduce the number of I/O. Graph noSQL we want to find out the relationship between each records, in this scenario we can use Graph noSQL. Elastic Search not a exactly like traditional noSQL database, more like a full-text searching database TimeSeries noSQL Vector DB. For ML scenarios. CAP (For all distributed system, not only for noSQL) C(Consistency): Every read reflects the most recent write or an error. A(Availability): All reads contain data, but it may not be the most recent. P(Partition Tolerance): The system continues to operate even when there are network failures. Most time we would trade off between C and A. MongoDB (CP, it sacrifices some availabilities) # Work Flow:\nMongoS(comparing to DBMS): like a router, when request reach the MongoS first, then it will consult the * ConfigSever*, then MongoS get the Sharding position it will get to the right Server to get the result of the Request. ConfigServer: contains meta data about the MongoDB sharding and replica info. Arbiter: it doesn\u0026rsquo;t contain any data. It only contain one vote, everytime there is an election has a draw, Arbiter is going to check for either one of those secondary nodes(or candidates within election). Before MongoDB(4.2)\nany of those transactions has to stay in the same collections(very similar idea like table). After MongoDB(4.2)\nit supports multiple collections within one transaction. We can Setup in MongoDB\nTade off between C and P Write Concern determine if the data is available to the user based on #synced nodes. we can also define sync up, J = 1 or J = 0 define in RAM or HD. trade off between persistency and performance Read Concern Are we okay to read data in RAM, or we have to read data in HD. There are several levels: Local: it doesn\u0026rsquo;t have to be in HD Majority: I only read from main node and secondary node that has already synced up. Might not be the lastest one, but we are not reading from the node hasn\u0026rsquo;t sync up. Snapshot Linearizable: everything is reading from HD nowadays MongoDB(after 4.2) supporting multi document transaction\nCollections in MongoDB is similar to a table in SQL database. Redis # we can setup Redis running in RAM, it\u0026rsquo;s not default it features that it can handle more than 10+K QPS it use a lot as Cache, and Distributed Lock the purpose is to reduce the #I/O between backend server and database server. the expire time of the key in Cache depends on the business model. it\u0026rsquo;s a key-value pair database value type string list set sortedSet(same as Zset) hash(same as hashTable) Cache Hit(cache hit) when there is a request send to backend, then it first check the cache layer to see if there\u0026rsquo;s any the same request that has sent before and the data has been stored within the cache. Then we directly return from the cache layer instead of sending request to the database. Miss(cache miss) no record we can find in the cache. Then we count it as cache miss Cold Start-Up solve it with Pre-Heat, just with some buffer Avoid syncing up cache and database with strong consistency, because it will lose concurrency that sacrifice too much performance. we are trading off between consistency and performance. strong consistency is the trading off between thread safety and performance. There are different strategies to choose based on different traffic patterns in our web app. We can apply distributed lock to ensure thread safety. To ensure the data consistent between Redis and DB. Before we use Redis + Lua script, right now Redison(a framework based on Redis). Lua script add locks in the source code for atomicity to ensure the data consistency. Eventual Consistency vs Strong Consistency with Eventual Consistency, our business model is able to endure the loss, but our technical architecture is benefit a lot regarding to the performance. interview questions # SQL and noSQL what is transaction how to enable transaction in distributed system case how to ensure the transaction how to rollback with strategy Server vs Web Server vs Application Server # the difference between web server and application server Web Server is used to handle http request Application Server just with some additional features like JBoss, it supports transaction on application layer instead of database layer or anything in our code. Both Web Server and Application Server in general speaking are in same Web Server Layer, they help us to handle duplicate logics for the servlet Server # hardware Web Server # Web Server is used to handle http request Tomcat Nginx Application server # some require commercial license, which also would provide additional features. JBoss: i.e. additional features like on application server layer, they provide transactions. GlassFish Other related topics??? # SpringBoot Servlet how virtual thread would benefit the traditional ones Stream/React/WebFlux/MonoFlux how to debug? BE \u0026lt;\u0026ndash;\u0026gt; DB # socket # open the pipeline, we choose the port number, send the stream of the data. HTTP/S # the idea of JDBC JDBC # JDBC is used to connect our WEB APP and DB.\njust a driver, that require duplicate logics\nreturn: ResultSet\nORM: Object Relational Mapping # just an idea to get rid of boilerplate for duplicate logics within our web app logics\neach row of a table will map to a java object\nImplementation of ORM is JPA(Java persistent API)\nJPA contains CRUD operations and Batch operations(batch delete/update/read) Frameworks implement ORM ideas with JPA\nHibernate Sequelize Doctrine MyBatis SQLAlchemy JDBC -\u0026gt; JPA -\u0026gt; Hibernate -\u0026gt; Spring Data JPA / Spring Data Mongodb / Spring Data Elastic Search\nthe more encapsulation we are going to have, the more automation in between table and Java Class we are going to have.\nHibernate # Session Factory grabs the configFile: username + password, then creates sessions Hibernate use reflection API to auto generate SQL statements Hibernate allows us to write: HQL(Hibernate Query Language), just in case the APIs are not flexible enough to catering some situations. Native SQL. cons: with dialect language(mysql, postgres, oracle) Cache strategies in Hibernate: First Level(default): in session level. The session(a user create a session) is private, it means it cannot access any content belong to other sessions. Second Level: in session factory level. Add extra configurations to the ConfigFile. For example, there are many users sending the same queries and they are supposed to return the same result. with second level caching strategy, the cache will be pubic to all sessions. Frontend # HTTP, FTP, SMTP # All are in application layer(7th layer of OSI)\nThey are methods or protocols that allow users use browser/or some application to communicate with the backend server.\nHTTP/S: HyperText Transfer Protocol Secure\nFTP: File Transfer Protocol\nSMTP: Simple Mail Transfer Protocol\nDNS: domain name system # it\u0026rsquo;s a system that we can use to translate URL to the public IP address of server. Browser storage types # Local Storage: no expiration date and 5 MB limit. Data here will never transfer to the server side Session Storage: is used to store session related data. Session storage will be closed and cleared after we close the browser. Cookie: 4 KB limit. Angular # Angular project develop, build, test, release flow Typescript npm nvm Single Page Application Component Basic building blocks that compose the webpage. @Component class act like controller written in typescript Template act like view template written in html and angular syntax(binding, directives) binding event binding one-way two-way data binding Directives Attribute Directive Directive(*ngIf, *ngFor, *ngClass, *ngStyle, *ngForm,) pipes Forms - template driven/ reactive forms StyleSheet css, sass, less Service Dependency Injection Service -\u0026gt; Component Angular CLI Module Routing HttpClient RxJs Observable Module CORS configuration UI Library bootstrap material UI Promise / Observable concepts # Components, modules and services are the three fundamental building blocks in Angular. Components should be small and self-contained, modules should group together related components, and services should provide shared functionality across the entire app. components # Components are the smallest, self-contained units in an Angular application. They are typically used to represent a view or UI element, such as a button or a form input field. directives # Allow the user to write new HTML syntax specific to their applications. They execute whenever the Angular compiler finds them in the DOM. Angular supports three types of directives.\nComponent Directives Structural Directives Attribute Directives pipes # pipes are simple functions designed to accept input value, process, and return output(transformed value). module # Modules are larger units that group together one or more related components. contains (components, directive, pipe) service # Services are singleton objects that provide specific functionality throughout an Angular application, such as data access or logging. annotations # @NgModule: Annotates a class to specify that it is an Angular module and provides metadata about its dependencies, components, directives, pipes, and services. @Component: Annotates a class to define an Angular component, providing metadata such as its selector, template, and style. @Directive: Annotates a class to define an Angular directive, which allows you to add behavior to elements in the DOM. @Pipe: Annotates a class to define an Angular pipe, which transforms input data to a desired output format for display. @Injectable: Annotates a class to define an injectable service that can be injected into other components or services. @Input: Annotates a class property to allow data to be passed into a component from its parent component. @Output:(with EventEmitter) Annotates a class property to allow a component to emit custom events to its parent component. @ViewChild and @ViewChildren: Annotates a class property to query and access child components or elements in the component\u0026rsquo;s template. @HostListener: Annotates a class method to listen for events on the host element of a directive or component. @HostBinding: Annotates a class property to bind to a host element property or attribute in a directive or component. two-way binding # Use two-way binding to listen for events and update values simultaneously between parent and child components. Syntax is a combination of square brackets and parentheses, [()] Promises and Observable # While both the concepts deal with Asynchronous events in Angular, Promises handle one such event at a time while observables handle a sequence of events over some time.\nPromises: They emit a single value at a time. They execute immediately after creation and are not cancellable. They are Push errors to the child promises. Observables: They are only executed when subscribed to them using the subscribe() method. They emit multiple values over a period of time. They help perform operations like forEach, filter, and retry, among others. They deliver errors to the subscribers. When the unsubscribe() method is called, the listener stops receiving further values. Constructor vs ngOnInit # constructor is called first. Then, once all component inputs are initialized, Angular is going to call ngOnInit Lifecycle hooks # In Angular, every component has a lifecycle. Angular creates and renders these components and also destroys them before removing them from the DOM. This is achieved with the help of lifecycle hooks. Here\u0026rsquo;s the list of them -\nngOnChanges() - Responds when Angular sets/resets data-bound input properties. ngOnInit() - Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component\u0026rsquo;s input properties/ ngDoCheck() - Detect and act upon changes that Angular can\u0026rsquo;t or won\u0026rsquo;t detect on its own. ngAfterContentInit() - Responds after Angular projects external content into the component\u0026rsquo;s view. ngAfterContentChecked() - Respond after Angular checks the content projected into the component. ngAfterViewInit() - Respond after Angular initializes the component\u0026rsquo;s views and child views. ngAfterViewChecked() - Respond after Angular checks the component\u0026rsquo;s views and child views. ngOnDestroy - Cleanup just before Angular destroys the directive/component. Angular Material # Angular Material is a UI component library for Angular applications. It provides a set of pre-built and customizable UI components. Architecture # Monolithic architecture SOA: ESB(Enterprise Service Bus) could become a single point of failure. Sharing data storage. MicroService: Each microservice has its own data storage. MicroFrontend: Each component we can use different frontend framework to build with. Frontend Backend Separation Microservice # WHY: we need microservice # if one service fails, we need to like restart the whole thing work. HOW: various ways to implement Microservice # business separation: (vertical): i.e. login/browse/order/payment/\u0026hellip; business separation: (horizontal): i.e. service oriented. (Python handles ML, Java handles web application, \u0026hellip;) Five Components # API Gateway: Spring Gateway or Zuul(can also be used as LB) Service Registry/Discovery: Eureka Business Modules Monitoring and Log Tracing: log4j, backlog, splunk Configuration Management: nacos or Eureka(can also be an configuration server) Spring Cloud # Spring Cloud provides a comprehensive set of tools to build resilient, scalable, and manageable microservices architectures. By leveraging the various components of Spring Cloud, developers can focus on business logic while the framework handles the complexities of distributed systems. Whether dealing with service discovery, configuration management, circuit breakers, or API gateways, Spring Cloud offers a unified and streamlined approach to developing cloud-native applications. Core components # Spring Cloud Config: Spring Cloud Config Server: Centralized external configuration management across all environments. Spring Cloud Config Client: Provides an abstraction for the client to access configuration properties. Spring Cloud Netflix (now largely superseded by Spring Cloud LoadBalancer and Spring Cloud CircuitBreaker): Eureka: Service discovery server. Ribbon: Client-side load balancing. Hystrix: Circuit breaker. Zuul: Edge server for routing. Spring Cloud Gateway: A replacement for Zuul, it provides a way to route requests to various services using filters and predicates for request processing. Spring Cloud OpenFeign: Declarative REST client, which allows defining a client interface annotated with Feign annotations to create dynamic REST clients. Spring Cloud Circuit Breaker: Provides an abstraction layer over various circuit breaker implementations like Resilience4j, Hystrix, Sentinel, etc. Spring Cloud Sleuth: Distributed tracing via logs. It integrates with Zipkin or Brave for trace and span IDs to help in troubleshooting. Spring Cloud Stream: Framework for building message-driven microservices using Apache Kafka or RabbitMQ. Spring Cloud Bus: Links nodes of a distributed system with a lightweight message broker. Useful for broadcasting state changes (e.g., configuration changes) or other management instructions. Eureka(Service discovery server): for service discover and registry. for service health management with service heart beat. key-value: URL - IP config server: Centralized external configuration management across all environments. zipkin \u0026amp; sleuth(for debug in microservice): Distributed tracing via logs. With trace IDs and span IDs to help in troubleshooting. trace ID \u0026amp; Span ID: Trace ID A Trace ID is a unique identifier for a single request as it travels through various microservices or components of a distributed system. It remains consistent across all services involved in the handling of the request, enabling a holistic view of the request\u0026rsquo;s lifecycle. Span ID A Span ID is a unique identifier for a single unit of work or operation within the trace. Each span represents a segment of the work done by a microservice, including details like the start and end time, and any additional metadata. Spans are related to each other in a parent-child relationship, forming a tree-like structure that maps out the full journey of a request. ribbon: for Client-side load balancing. Log Aggregation Splunk ELK: Elastic Search(Storage)/ LogStash(Data processing)/ Kibana(Visualize) cons of E: doesn\u0026rsquo;t support join and transactions. Security # Security Basic Authentication vs Authorization Authentication: valid user? passcode, username, fingerprint Authorization: what permission does the user have. role base permission base Encryption, Hashing, Encoding Encryption symmetric: i.e.AES asymmetric: public/private key i.e. RSA Hashing: for data integration i.e. MD5, SHA Asymmetric Key usage(SSL/TLS/HTTPs, Signature, SSH) data security solving with Encryption/Hashing in transit at rest api security Authentication: 401 Authorization: 403 server side security(SQL Injection), client side security(XSS, CSRF) HTTPS Flow port 443 protect data in transit prevent man in the middle attack Networking Configuration Segmentation public/private subnet Firewall VPN User authentication/authorization username/password token api key certificate OAuth2, Single Sign On OAuth2 is a third party validation SSO once we login in a service like gmail, our other service like google drive and google map will automatically login. JWT header(algorithm..) + payload(data) + verify signature(hashed values) Messaging Queue # Two major Purpose: with buffering we can have OFF PEAK, we are going to protect our servers from burning out. with topic related, we can have decoupling style. pub-sub messaging queue example: RabbitMQ, Redis, Kafka Kafka, one of message queue implementations # it\u0026rsquo;s a pub-sub messaging queue type. itself can run as a cluster(we would have multiple kafka brokers). The these two number we can configure when we setup each Kafka topic #partitions(can not be dynamic configured) depends on #consumers #replication depends on #servers(same as #brokers) versioning: since kafka 7, it replace ZooKeeper with Kraft topics: a virtual or conceptual idea that we store messages partitions: a real way to store messages(a topic is sharding into three? partitions), within partitions there is offset(like consuming index to indicate consuming process) when the broker(Zookeeper) is shutting down, kafka will persist partitions(messages data) and offset. Producer: will configure which topics it is going to send message to(depending on logic). Consumer: will configure the #partitions, #replications, whether consumers are in the same consumer group or not. consumer groups: a bunch of consumers can run in parallel, to consume the message in the same topic. Within consumer group, each consumer can only consume different partition than others(to avoid thread safe issues). Re-balancing: will sacrifice some performance. It will infected by the interval we set up. Duplicated messages Producer side: to determine if the message has been produced(sent) or not: Consistency of replications vs Performance: setting up #replications(need to be sync up) as a condition while producer sending message to broker, if condition fulfill. Consumer side: to determine if the message has been consumed(deleted) or not: just comparing offset like above, checking if the offset sync up. Code # Github Setup Kafka cluster SDLC (software development lifecycle) # Waterfall # pros: reduce the communication time between tech and non-tech people, so it might increase some efficiency and budget. we gonna have a meeting with manager or non-tech people, and we plan the new feature will be alive within a year or half year. we have almost fixed time line for the project. like within a year, every 3 month we would sync up with the company status. so the whole project will separate into 4 stages. Agile # when we want the company to cater to the market to survive, and the market can be very dynamic, which means our business model has to be changed rapidly.\nin this case, the waterfall is not a practical solution for this kind of scenario.\nwe have relative way much shorter amount of period to sync up and report the status of the project.\neach sprint is just about one or two weeks.\nSprint planning: a meeting to kick of the sprint.\nSprint backlog: a list of tasks and activities that a project team plans to complete during a sprint.\nSprint increment: backlog items that a team completes during a sprint.\nSprint review: a meeting to do some demonstration to the product owner or anyone related.\nSprint retrospective: a meeting after a sprint to reflect on our work and identify areas for improvement.\nDaily standup\nTesting # unit test # testing coverage: 80% 90% 100%. tools: Jacoco, SonarQube(in CI/CD pipeline, give us tech debt called code smell) types of testing: function testing/ pressure testing/ \u0026hellip; stage: Dev/QA/UAT/Prod integration test # Selenium framework BDD # Cucumber framework pressure test # Jmeter framework CI/CD # just define several stages within yaml file\nJenkins Concourse SonarQube # we start SonarQube in docker, and then attach it to the Jenkins pipeline. Which is going to be the last step of the CI/CD pipeline. If any of those stages, the test coverage, or any of the code smell, or any of the technical debt, is more than we want to setup for bottleneck, we can reject the whole CI/CD process. Then code is not release to the environment then. Manage project # Jira: project management tool Confluence: proj Mockito # Basic # package net.thebabydragon; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.Random; class MainTest { // following two lines equals to \u0026#39;Random random = Mockito.mock(Random.class);\u0026#39; // @Mock and @Spy 需要搭配 MockitoAnnotations.openMocks(testClass) 方法一起使用 @Mock private Random random; // 真实的 @Spy private Main main; @BeforeEach void setUp() { System.out.println(\u0026#34;----before----\u0026#34;); MockitoAnnotations.openMocks(this); } @Test void addTest() { // Random random = Mockito.mock(Random.class); System.out.println(random.nextInt()); // basic Mockito.verify(random).nextInt(); Mockito.verify(random, Mockito.times(1)).nextInt(); // 打桩 Mockito.when(random.nextInt()).thenReturn(100); Assertions.assertEquals(100, random.nextInt()); } @AfterEach void after() { System.out.println(\u0026#34;----after----\u0026#34;); } @Test void testMain() { // 这里可以打桩，如果不打桩，就是真实的，打桩就执行打桩 Mockito.when(main.add(2, 3)).thenReturn(100); Assertions.assertEquals(100, main.add(2, 3)); } } Mockito.when().thenThrow(); # package net.thebabydragon; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.Random; class MainTest { @Spy private Main main; @BeforeEach void setUp() { MockitoAnnotations.initMocks(this); } @Test void testMain() { // Mockito.when().thenThrow(); Mockito.when(main.add(1, 3)).thenThrow(new RuntimeException()); main.add(1, 3); } } Mockito.when().thenCallRealMethod(); # package net.thebabydragon; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.Random; class MainTest { @Spy private Main main; @BeforeEach void setUp() { MockitoAnnotations.initMocks(this); } @Test void testMain() { // first time run this Mockito.when(main.add(1, 3)).thenReturn(12); Assertions.assertEquals(12, main.add(1, 3)); // second time run this Mockito.when(main.add(1, 3)).thenCallRealMethod(); Assertions.assertEquals(4, main.add(1, 3)); } } Static method # package net.thebabydragon.Util; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.*; class StaticUtilsTest { @Test void range() { try (MockedStatic\u0026lt;StaticUtils\u0026gt; mocked = Mockito.mockStatic(StaticUtils.class)) { mocked.when(() -\u0026gt; StaticUtils.range(2, 6)).thenReturn(Arrays.asList(10, 11, 12)); assertEquals(Arrays.asList(10, 11, 12), StaticUtils.range(2, 6)); } } @Test void name() { try (MockedStatic\u0026lt;StaticUtils\u0026gt; mocked = Mockito.mockStatic(StaticUtils.class)) { mocked.when(StaticUtils::name).thenCallRealMethod(); assertEquals(\u0026#34;Echo\u0026#34;, StaticUtils.name()); } } }``` ","date":"20 April 2024","externalUrl":null,"permalink":"/notes/a_concept/","section":"Notes","summary":"QuickNotes # singleton vs immutable class functional interface with static Core Java interview questions # Java is passed by Value or Reference? # In Java, there are two kinds of data type, Primitive data types and Non-primitive data types. For primitive types, they are built-in data types in Java including int/short/long/float/double/char/byte/boolean. They are all passed by value. For non-primitive types, they are also passed by value, the value here is actually the memory address of the object. e.g. annotation, class, interface, enum, array. In conclusion all data types in Java are passed by value. static keyword(within 5 sentences) # we have four places to put static keyword on.(class, method, variable, block) For having static keyword on method, variable scope is to ensure that they can share just by class template. what SCOPES we can access static elements. we cannot override static method static class cannot called by non-static method static block # static block is a set of instructions that is run only once when a class is loaded into memory. We use a static block to initialize static variables. Although we can initialize static variables directly during declaration, there are situations when we need to do multiline processing. In such cases, static blocks come in handy. If static variables require additional, multi-statement logic during initialization, we can use a static block. Below are a few reasons for for using static blocks: if the initialization of static variables needs some additional logic apart from the assignment if the initialization of static variables is error-prone and needs exception handling static class # In general, the nested class architecture is divided into two types:\n","title":"Cheat Sheet","type":"notes"},{"content":" Top K(quick select) # Leetcode 215 Kth largest int partition(vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, int end) { int pivot = nums[start]; int i = start, j = end; while (i \u0026lt; j) { while (i \u0026lt; j \u0026amp;\u0026amp; nums[j] \u0026gt;= pivot) --j; nums[i] = nums[j]; while (i \u0026lt; j \u0026amp;\u0026amp; nums[i] \u0026lt;= pivot) ++i; nums[j] = nums[i]; } nums[i] = pivot; return i; } void TopKSplit(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k, int left, int right) { int idx = partition(nums, left, right); if (idx == k) return; else if (idx \u0026lt; k) TopKSplit(nums, k, idx + 1, right); else TopKSplit(nums, k, left, idx - 1); } int findKthLargest(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { TopKSplit(nums, nums.size() - k, 0, nums.size() - 1); return nums[nums.size() - k]; } Coding: 实现 ArrayList 的 get 方法和 add 方法(by index) # Reference import java.io.Serializable; import java.util.Arrays; import java.util.RandomAccess; public class SimpleArrayList\u0026lt;E\u0026gt; implements RandomAccess, Cloneable, Serializable { private final static int DEFAULT_CAPACITY = 10; private int size = 0; private Object[] elementData; public SimpleArrayList() { this(DEFAULT_CAPACITY); } public SimpleArrayList(int size) { if (size \u0026lt; 0) { throw new IllegalArgumentException(\u0026#34;Size should be positive integer!\u0026#34; + size); } else { elementData = new Object[size]; } } public void add(E e) { isCapacityEnough(size + 1); elementData[size++] = e; } public void add(int index, E e) { if (index \u0026lt; 0 || index \u0026gt; size) { throw new IndexOutOfBoundsException(\u0026#34;Please give a legal index!\u0026#34;); } isCapacityEnough(size + 1); System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = e; size++; } private void isCapacityEnough(int size) { if (size \u0026gt; DEFAULT_CAPACITY) { explicitCapacity(size); } if (size \u0026lt; 0) { throw new OutOfMemoryError(); } } private final static int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; private void explicitCapacity(int capacity) { int newLength = elementData.length * 2; if (newLength - capacity \u0026lt; 0) { newLength = capacity; } if (newLength \u0026gt; (MAX_ARRAY_LENGTH)) { newLength = (capacity \u0026gt; MAX_ARRAY_LENGTH ? Integer.MAX_VALUE : MAX_ARRAY_LENGTH); } elementData = Arrays.copyOf(elementData, newLength); } public E get(int index) { if (index \u0026gt;= size || index \u0026lt; 0) { throw new IndexOutOfBoundsException(\u0026#34;Please give a legal index\u0026#34;); } return (E) elementData[index]; } } Character count # String input = \u0026#34;ABCDCCCDAA\u0026#34;; HashMap\u0026lt;Character, Integer\u0026gt; hm = new HashMap\u0026lt;\u0026gt;(); for (Character c : input.toCharArray()) { hm.put(c, hm.getOrDefault(c, 0) + 1); } find the largest and the second largest # int first = 0x3f3f3f3f, second = first; int idx = 0; while (idx \u0026lt; v.length) { if (v[idx] \u0026gt; first) { second = first; first = v[idx]; } else if (v[idx] \u0026gt; second) second = v[idx]; ++idx; } find unique char in string # int[] cnt = new int[26]; Arrays.fill(cnt, 0); for (char c : s.toCharArray()) { ++cnt[c - \u0026#39;a\u0026#39;]; } find unique int # HashMap\u0026lt;Integer, Integer\u0026gt; hm = new HashMap\u0026lt;\u0026gt;(); for (int vi : v) { hm.put(vi, hm.getOrDefault(vi, 0) + 1); } subarray sum equals to K # // Approach 1 if (nums == null || nums.length == 0) return 0; int ans = 0; for (int i = 0; i \u0026lt; nums.length; ++i) { int sum = 0; for (int j = i; j \u0026lt; nums.length; ++j) { sum += nums[j]; if (k == sum) ++ans; } } return ans; // Approach 2 int subarraySum(int[] nums, int k) { if (nums == null || nums.length == 0) return 0; int sum = 0; HashMap\u0026lt;Integer, Integer\u0026gt; hm = new HashMap\u0026lt;\u0026gt;(); int ans = 0; hm.put(0, 1); for (int i = 0; i \u0026lt; nums.length; ++i) { sum += nums[i]; if (hm.containsKey(sum - k)) { ans += hm.get(sum - k); } hm.put(sum, hm.getOrDefault(sum, 0) + 1); } return ans; } max consecutive ones # int findMaxConsecutiveOnes(int[] nums) { int cnt = 0, ans = 0; for (int n : nums) { if (n == 1) ++cnt; else { ans = Math.max(ans, cnt); cnt = 0; } } ans = Math.max(ans, cnt); return ans; } K largest element in an Array # int partition(vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, int end) { int pivot = nums[start]; int i = start, j = end; while (i \u0026lt; j) { while (i \u0026lt; j \u0026amp;\u0026amp; nums[j] \u0026gt;= pivot) --j; nums[i] = nums[j]; while (i \u0026lt; j \u0026amp;\u0026amp; nums[i] \u0026lt;= pivot) ++i; nums[j] = nums[i]; } nums[i] = pivot; return i; } void TopKSplit(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k, int left, int right) { int idx = partition(nums, left, right); if (idx == k) return; else if (idx \u0026lt; k) TopKSplit(nums, k, idx + 1, right); else TopKSplit(nums, k, left, idx - 1); } int findKthLargest(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { TopKSplit(nums, nums.size() - k, 0, nums.size() - 1); return nums[nums.size() - k]; } String compression # int compress(char[] chars) { if (chars.length == 0) return 0; int left = 0, right = 0; while (right \u0026lt; chars.length) { int len = 1; while (right + len \u0026lt; chars.length \u0026amp;\u0026amp; chars[right] == chars[right + len]) { ++len; } chars[left++] = chars[right]; if (len \u0026gt; 1) { String len_s = Integer.toString(len); for (Character c : len_s.toCharArray()) { chars[left++] = c; } } right += len; } return left; } Group Anagrams # public List\u0026lt;List\u0026lt;String\u0026gt;\u0026gt; groupAnagrams(String[] strs) { if (strs.length == 0) return new ArrayList(); Map\u0026lt;String, List\u0026gt; ans = new HashMap\u0026lt;String, List\u0026gt;(); int[] count = new int[26]; for (String s : strs) { Arrays.fill(count, 0); for (char c : s.toCharArray()) count[c - \u0026#39;a\u0026#39;]++; StringBuilder sb = new StringBuilder(\u0026#34;\u0026#34;); for (int i = 0; i \u0026lt; 26; i++) { sb.append(\u0026#39;#\u0026#39;); sb.append(count[i]); } String key = sb.toString(); if (!ans.containsKey(key)) ans.put(key, new ArrayList()); ans.get(key).add(s); } return new ArrayList(ans.values()); } Fibonacci Number # // Creating a hash map with 0 -\u0026gt; 0 and 1 -\u0026gt; 1 pairs private Map\u0026lt;Integer, Integer\u0026gt; cache = new HashMap\u0026lt;\u0026gt;(Map.of(0, 0, 1, 1)); public int fib(int N) { if (cache.containsKey(N)) { return cache.get(N); } cache.put(N, fib(N - 1) + fib(N - 2)); return cache.get(N); } public int fib(int N) { if (N \u0026lt;= 1) { return N; } int current = 0; int prev1 = 1; int prev2 = 0; for (int i = 2; i \u0026lt;= N; i++) { current = prev1 + prev2; prev2 = prev1; prev1 = current; } return current; } Get second frequently string from list of string # ArrayList\u0026lt;String\u0026gt; input = new ArrayList\u0026lt;\u0026gt;(); input.add(\u0026#34;apple\u0026#34;); input.add(\u0026#34;apple\u0026#34;); input.add(\u0026#34;banana\u0026#34;); input.add(\u0026#34;orange\u0026#34;); input.add(\u0026#34;banana\u0026#34;); input.add(\u0026#34;apple\u0026#34;); HashMap\u0026lt;String, Integer\u0026gt; hm = new HashMap\u0026lt;\u0026gt;(); for (String s : input) { hm.put(s, hm.getOrDefault(s, 0) + 1); } hm.put(\u0026#34;\u0026#34;, 0); String first = \u0026#34;\u0026#34;; String second = \u0026#34;\u0026#34;; for (String key : hm.keySet()) { Integer val = hm.get(key); if (hm.get(first) \u0026lt; val) { second = first; first = key; } else if (hm.get(second) \u0026lt; val) second = key; } Valid anagram # public boolean isAnagram(String s, String t) { if (s.length() != t.length()) { return false; } int[] counter = new int[26]; for (int i = 0; i \u0026lt; s.length(); i++) { counter[s.charAt(i) - \u0026#39;a\u0026#39;]++; counter[t.charAt(i) - \u0026#39;a\u0026#39;]--; } for (int count : counter) { if (count != 0) { return false; } } return true; } input: 1,1,2,5,5,8,9 output: 1,2,,,5,,,8,9 # int max_v = 0; for (int vi : v) { if (vi \u0026gt; max_v) max_v = vi; } int[] ans = new int[max_v]; Arrays.fill(ans, -1); for (int vi : v) { ans[vi - 1] = vi; } subsequence sum equals to K # void solution() { int[] input = {1,2,3,4,5,6,7}; Arrays.sort(input); ArrayList\u0026lt;ArrayList\u0026lt;Integer\u0026gt;\u0026gt; combinations = new ArrayList\u0026lt;\u0026gt;(); ArrayList\u0026lt;Integer\u0026gt; comb = new ArrayList\u0026lt;\u0026gt;(); Helper(input, 0, combinations, comb, 0); System.out.println(combinations); } void Helper(int[] nums, int start, ArrayList\u0026lt;ArrayList\u0026lt;Integer\u0026gt;\u0026gt; combinations, ArrayList\u0026lt;Integer\u0026gt; comb, int sum) { if (sum == 6) { combinations.add(new ArrayList\u0026lt;\u0026gt;(comb)); } for (int i = start; i \u0026lt; nums.length; i++) { comb.add(nums[i]); sum += nums[i]; Helper(nums, i + 1, combinations, comb, sum); sum -= nums[i]; comb.remove(comb.size() - 1); } } ","date":"16 April 2024","externalUrl":null,"permalink":"/notes/a_code/","section":"Notes","summary":"Top K(quick select) # Leetcode 215 Kth largest int partition(vector\u003cint\u003e\u0026 nums, int start, int end) { int pivot = nums[start]; int i = start, j = end; while (i \u003c j) { while (i \u003c j \u0026\u0026 nums[j] \u003e= pivot) --j; nums[i] = nums[j]; while (i \u003c j \u0026\u0026 nums[i] \u003c= pivot) ++i; nums[j] = nums[i]; } nums[i] = pivot; return i; } void TopKSplit(vector\u003cint\u003e\u0026 nums, int k, int left, int right) { int idx = partition(nums, left, right); if (idx == k) return; else if (idx \u003c k) TopKSplit(nums, k, idx + 1, right); else TopKSplit(nums, k, left, idx - 1); } int findKthLargest(vector\u003cint\u003e\u0026 nums, int k) { TopKSplit(nums, nums.size() - k, 0, nums.size() - 1); return nums[nums.size() - k]; } Coding: 实现 ArrayList 的 get 方法和 add 方法(by index) # Reference import java.io.Serializable; import java.util.Arrays; import java.util.RandomAccess; public class SimpleArrayList\u003cE\u003e implements RandomAccess, Cloneable, Serializable { private final static int DEFAULT_CAPACITY = 10; private int size = 0; private Object[] elementData; public SimpleArrayList() { this(DEFAULT_CAPACITY); } public SimpleArrayList(int size) { if (size \u003c 0) { throw new IllegalArgumentException(\"Size should be positive integer!\" + size); } else { elementData = new Object[size]; } } public void add(E e) { isCapacityEnough(size + 1); elementData[size++] = e; } public void add(int index, E e) { if (index \u003c 0 || index \u003e size) { throw new IndexOutOfBoundsException(\"Please give a legal index!\"); } isCapacityEnough(size + 1); System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = e; size++; } private void isCapacityEnough(int size) { if (size \u003e DEFAULT_CAPACITY) { explicitCapacity(size); } if (size \u003c 0) { throw new OutOfMemoryError(); } } private final static int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; private void explicitCapacity(int capacity) { int newLength = elementData.length * 2; if (newLength - capacity \u003c 0) { newLength = capacity; } if (newLength \u003e (MAX_ARRAY_LENGTH)) { newLength = (capacity \u003e MAX_ARRAY_LENGTH ? Integer.MAX_VALUE : MAX_ARRAY_LENGTH); } elementData = Arrays.copyOf(elementData, newLength); } public E get(int index) { if (index \u003e= size || index \u003c 0) { throw new IndexOutOfBoundsException(\"Please give a legal index\"); } return (E) elementData[index]; } } Character count # String input = \"ABCDCCCDAA\"; HashMap\u003cCharacter, Integer\u003e hm = new HashMap\u003c\u003e(); for (Character c : input.toCharArray()) { hm.put(c, hm.getOrDefault(c, 0) + 1); } find the largest and the second largest # int first = 0x3f3f3f3f, second = first; int idx = 0; while (idx \u003c v.length) { if (v[idx] \u003e first) { second = first; first = v[idx]; } else if (v[idx] \u003e second) second = v[idx]; ++idx; } find unique char in string # int[] cnt = new int[26]; Arrays.fill(cnt, 0); for (char c : s.toCharArray()) { ++cnt[c - 'a']; } find unique int # HashMap\u003cInteger, Integer\u003e hm = new HashMap\u003c\u003e(); for (int vi : v) { hm.put(vi, hm.getOrDefault(vi, 0) + 1); } subarray sum equals to K # // Approach 1 if (nums == null || nums.length == 0) return 0; int ans = 0; for (int i = 0; i \u003c nums.length; ++i) { int sum = 0; for (int j = i; j \u003c nums.length; ++j) { sum += nums[j]; if (k == sum) ++ans; } } return ans; // Approach 2 int subarraySum(int[] nums, int k) { if (nums == null || nums.length == 0) return 0; int sum = 0; HashMap\u003cInteger, Integer\u003e hm = new HashMap\u003c\u003e(); int ans = 0; hm.put(0, 1); for (int i = 0; i \u003c nums.length; ++i) { sum += nums[i]; if (hm.containsKey(sum - k)) { ans += hm.get(sum - k); } hm.put(sum, hm.getOrDefault(sum, 0) + 1); } return ans; } max consecutive ones # int findMaxConsecutiveOnes(int[] nums) { int cnt = 0, ans = 0; for (int n : nums) { if (n == 1) ++cnt; else { ans = Math.max(ans, cnt); cnt = 0; } } ans = Math.max(ans, cnt); return ans; } K largest element in an Array # int partition(vector\u003cint\u003e\u0026 nums, int start, int end) { int pivot = nums[start]; int i = start, j = end; while (i \u003c j) { while (i \u003c j \u0026\u0026 nums[j] \u003e= pivot) --j; nums[i] = nums[j]; while (i \u003c j \u0026\u0026 nums[i] \u003c= pivot) ++i; nums[j] = nums[i]; } nums[i] = pivot; return i; } void TopKSplit(vector\u003cint\u003e\u0026 nums, int k, int left, int right) { int idx = partition(nums, left, right); if (idx == k) return; else if (idx \u003c k) TopKSplit(nums, k, idx + 1, right); else TopKSplit(nums, k, left, idx - 1); } int findKthLargest(vector\u003cint\u003e\u0026 nums, int k) { TopKSplit(nums, nums.size() - k, 0, nums.size() - 1); return nums[nums.size() - k]; } String compression # int compress(char[] chars) { if (chars.length == 0) return 0; int left = 0, right = 0; while (right \u003c chars.length) { int len = 1; while (right + len \u003c chars.length \u0026\u0026 chars[right] == chars[right + len]) { ++len; } chars[left++] = chars[right]; if (len \u003e 1) { String len_s = Integer.toString(len); for (Character c : len_s.toCharArray()) { chars[left++] = c; } } right += len; } return left; } Group Anagrams # public List\u003cList\u003cString\u003e\u003e groupAnagrams(String[] strs) { if (strs.length == 0) return new ArrayList(); Map\u003cString, List\u003e ans = new HashMap\u003cString, List\u003e(); int[] count = new int[26]; for (String s : strs) { Arrays.fill(count, 0); for (char c : s.toCharArray()) count[c - 'a']++; StringBuilder sb = new StringBuilder(\"\"); for (int i = 0; i \u003c 26; i++) { sb.append('#'); sb.append(count[i]); } String key = sb.toString(); if (!ans.containsKey(key)) ans.put(key, new ArrayList()); ans.get(key).add(s); } return new ArrayList(ans.values()); } Fibonacci Number # // Creating a hash map with 0 -\u003e 0 and 1 -\u003e 1 pairs private Map\u003cInteger, Integer\u003e cache = new HashMap\u003c\u003e(Map.of(0, 0, 1, 1)); public int fib(int N) { if (cache.containsKey(N)) { return cache.get(N); } cache.put(N, fib(N - 1) + fib(N - 2)); return cache.get(N); } public int fib(int N) { if (N \u003c= 1) { return N; } int current = 0; int prev1 = 1; int prev2 = 0; for (int i = 2; i \u003c= N; i++) { current = prev1 + prev2; prev2 = prev1; prev1 = current; } return current; } Get second frequently string from list of string # ArrayList\u003cString\u003e input = new ArrayList\u003c\u003e(); input.add(\"apple\"); input.add(\"apple\"); input.add(\"banana\"); input.add(\"orange\"); input.add(\"banana\"); input.add(\"apple\"); HashMap\u003cString, Integer\u003e hm = new HashMap\u003c\u003e(); for (String s : input) { hm.put(s, hm.getOrDefault(s, 0) + 1); } hm.put(\"\", 0); String first = \"\"; String second = \"\"; for (String key : hm.keySet()) { Integer val = hm.get(key); if (hm.get(first) \u003c val) { second = first; first = key; } else if (hm.get(second) \u003c val) second = key; } Valid anagram # public boolean isAnagram(String s, String t) { if (s.length() != t.length()) { return false; } int[] counter = new int[26]; for (int i = 0; i \u003c s.length(); i++) { counter[s.charAt(i) - 'a']++; counter[t.charAt(i) - 'a']--; } for (int count : counter) { if (count != 0) { return false; } } return true; } input: 1,1,2,5,5,8,9 output: 1,2,,,5,,,8,9 # int max_v = 0; for (int vi : v) { if (vi \u003e max_v) max_v = vi; } int[] ans = new int[max_v]; Arrays.fill(ans, -1); for (int vi : v) { ans[vi - 1] = vi; } subsequence sum equals to K # void solution() { int[] input = {1,2,3,4,5,6,7}; Arrays.sort(input); ArrayList\u003cArrayList\u003cInteger\u003e\u003e combinations = new ArrayList\u003c\u003e(); ArrayList\u003cInteger\u003e comb = new ArrayList\u003c\u003e(); Helper(input, 0, combinations, comb, 0); System.out.println(combinations); } void Helper(int[] nums, int start, ArrayList\u003cArrayList\u003cInteger\u003e\u003e combinations, ArrayList\u003cInteger\u003e comb, int sum) { if (sum == 6) { combinations.add(new ArrayList\u003c\u003e(comb)); } for (int i = start; i \u003c nums.length; i++) { comb.add(nums[i]); sum += nums[i]; Helper(nums, i + 1, combinations, comb, sum); sum -= nums[i]; comb.remove(comb.size() - 1); } }","title":"A Code","type":"notes"},{"content":" Partition Template Quick Sort Top K Split(Quick Selection): (n = k/1/n-k-1) Top Kth Smallest Top Kth Largest Partition Template # int partition(vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, int end) { int pivot = nums[start]; // 初始化一个待比较数据; int i = start, j = end; while (i \u0026lt; j) { // 从后往前查找，直到找到一个比pivot更小的数 while (i \u0026lt; j \u0026amp;\u0026amp; nums[j] \u0026gt;= pivot) --j; nums[i] = nums[j]; // 将更小的数放入左边 // 从前往后找，直到找到一个比pivot更大的数 while (i \u0026lt; j \u0026amp;\u0026amp; nums[i] \u0026lt;= pivot) ++i; nums[j] = nums[i]; // 将更大的数放入右边 } // 循环结束，i与j相等 nums[i] = pivot; return i; // 返回待比较数据最终位置 } Quick Sort # void QuickSort(vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, int end) { if (start \u0026gt;= end) return; int idx = partition(nums, start, end); QuickSort(nums, start, idx - 1); QuickSort(nums, idx + 1, end); } Top K Split # 将快速排序改成快速选择，即我们希望寻找到一个位置，这个位置左边是k个比这个位置上的数更小的数，右边是n - k - 1个比该位置上的数大的数，我将它命名为TopKSplit，找到这个位置后停止迭代，完成了一次划分。 // Quick Selection void TopKSplit(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k, int left, int right) { // 寻找到第k个数停止递归，使得nums数组中index左边是前k个小的数，index右边是后面n-k个大的数 int idx = partition(nums, left, right); if (idx == k) return; else if (idx \u0026lt; k) TopKSplit(nums, k, idx + 1, right); else TopKSplit(nums, k, left, idx - 1); } Usages # Top K Smalls # vector\u0026lt;int\u0026gt; TopKSmalls(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { TopKSplit(nums, k, 0, nums.size() - 1); return vector\u0026lt;int\u0026gt;(nums.begin(), nums.begin() + k); } Kth small # int TopKthSmall(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { TopKSplit(nums, k - 1, 0, nums.size() - 1); return nums[k - 1]; } Top K Larges # vector\u0026lt;int\u0026gt; TopKLarges(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { // parttion是按从小到大划分的，如果让index左边为前n-k个小的数，则index右边为前k个大的数 TopKSplit(nums, nums.size() - k, 0, nums.size() - 1); // change k to nums.size() - k return vector\u0026lt;int\u0026gt;(nums.begin() + nums.size() - k, nums.end()); } Kth large # int TopKthLarge(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { // parttion是按从小到大划分的，如果让index左边为前n-k个小的数，则index右边为前k个大的数 TopKSplit(nums, nums.size() - k, 0, nums.size() - 1); // change k to nums.size() - k return nums[nums.size() - k]; } Only Sort Top K Smalls # // 只排序前 k 个小的数 // 获得前 k 小的数 O(n)，进行快排 O(klogk) vector\u0026lt;int\u0026gt; TopKSortLeft(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { TopKSplit(nums, k, 0, nums.size() - 1); vector\u0026lt;int\u0026gt; topk = vector\u0026lt;int\u0026gt;(nums.begin(), nums.begin() + k); QuickSort(topk, 0, topk.size() - 1); topk.insert(topk.end(), nums.begin() + k, nums.end()); return topk; } Only Sort Top K Larges # // 只排序后 k 个大的数 // 获得前 k 小的数 O(n)，进行快排 O(klogk) vector\u0026lt;int\u0026gt; TopKSortLeft(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { TopKSplit(nums, nums.size() - k, 0, nums.size() - 1); vector\u0026lt;int\u0026gt; topk = vector\u0026lt;int\u0026gt;(nums.begin() + nums.size() - k, nums.end()); QuickSort(topk, 0, topk.size() - 1); topk.insert(topk.begin(), nums.begin() + nums.size() - k, nums.end()); return topk; }","date":"15 April 2024","externalUrl":null,"permalink":"/blog/partition/","section":"Blog","summary":" Partition Template Quick Sort Top K Split(Quick Selection): (n = k/1/n-k-1) Top Kth Smallest Top Kth Largest ","title":"Partition","type":"blog"},{"content":"Leetcode 743 Network Delay Time # Leetcode 743 Naive Dijkstra(for dense graph) # class Solution { public: int networkDelayTime(vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; \u0026amp;times, int n, int k) { vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; g(n, vector\u0026lt;int\u0026gt;(n, INT_MAX / 2)); // 邻接矩阵 for (auto \u0026amp;t : times) { g[t[0] - 1][t[1] - 1] = t[2]; } vector\u0026lt;int\u0026gt; dis(n, INT_MAX / 2), done(n); dis[k - 1] = 0; while (true) { int x = -1; for (int i = 0; i \u0026lt; n; i++) { if (!done[i] \u0026amp;\u0026amp; (x \u0026lt; 0 || dis[i] \u0026lt; dis[x])) { x = i; // 可以提前结束吗 } } if (x \u0026lt; 0) { // all done return ranges::max(dis); } if (dis[x] == INT_MAX / 2) { // 有节点无法到达 return -1; } done[x] = true; // 最短路长度已确定（无法变得更小） for (int y = 0; y \u0026lt; n; y++) { // 更新 x 的邻居的最短路 dis[y] = min(dis[y], dis[x] + g[x][y]); } } } }; Heap-optimized Dijkstra(for sparse graph) # class Solution { public: int networkDelayTime(vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; \u0026amp;times, int n, int k) { vector\u0026lt;vector\u0026lt;pair\u0026lt;int, int\u0026gt;\u0026gt;\u0026gt; g(n); // 邻接表 for (auto \u0026amp;t : times) { g[t[0] - 1].emplace_back(t[1] - 1, t[2]); } vector\u0026lt;int\u0026gt; dis(n, INT_MAX); dis[k - 1] = 0; priority_queue\u0026lt;pair\u0026lt;int, int\u0026gt;, vector\u0026lt;pair\u0026lt;int, int\u0026gt;\u0026gt;, greater\u0026lt;\u0026gt;\u0026gt; pq; pq.emplace(0, k - 1); while (!pq.empty()) { auto [dx, x] = pq.top(); pq.pop(); if (dx \u0026gt; dis[x]) { // x 之前出堆过 continue; } for (auto \u0026amp;[y, d] : g[x]) { int new_dis = dx + d; if (new_dis \u0026lt; dis[y]) { dis[y] = new_dis; // 更新 x 的邻居的最短路 pq.emplace(new_dis, y); } } } int mx = ranges::max(dis); return mx \u0026lt; INT_MAX ? mx : -1; } };","date":"13 April 2024","externalUrl":null,"permalink":"/blog/dijkstra/","section":"Blog","summary":"","title":"Dijkstra","type":"blog"},{"content":" Basic data types # int \u0026lt;-\u0026gt; String # // first is \u0026#34;123\u0026#34; // scond is \u0026#34;234\u0026#34; String sumStr = String.valueOf(Long.parseLong(first) + Long.parseLong(second)); 1D Array # int[] r = new int[26]; // r.length; String[] r = {\u0026#34;c1\u0026#34;, \u0026#34;c2\u0026#34;, \u0026#34;c3\u0026#34;}; Arrays.fill(r, 0); 2D Array # String[][] r = new String[3][3]; String[][] r = {{\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;}, {\u0026#34;c\u0026#34;, \u0026#34;d\u0026#34;}}; for (String[] row : r) { Arrays.fill(row, \u0026#34;apple\u0026#34;); // Fill each row with the string \u0026#34;apple\u0026#34; } Character # Character.isLetterOrDigit(s.charAt(i)) // isWhitespace, isLetter, isDigit Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j)) String # String name = \u0026#34;Bro\u0026#34;; // name.length(); char[] ch = str.toCharArray(); return new String(ch) // convert char array back to String substring(int beginIndex, int endIndex) String r = name.trim(); String r = name.replace(\u0026#39;o\u0026#39;, \u0026#39;a\u0026#39;); // Replace all \u0026#39;a\u0026#39; to \u0026#39;o\u0026#39; boolean r = name.equals(\u0026#34;Bro\u0026#34;); boolean r = name.equalsIgnoreCase(\u0026#34;bro\u0026#34;); int r = str1.compareTo(str2) // lexicographically, \u0026gt; 0, \u0026lt; 0, == 0 char r = name.charAt(0); int r = name.indexOf(\u0026#34;B\u0026#34;); boolean r = name.startsWith(\u0026#34;A\u0026#34;); boolean r = name.isEmpty(); String r = name.toUpperCase(); // toLowerCase List\u0026lt;String\u0026gt; wordList = Arrays.asList(s.split(\u0026#34;\\\\s+\u0026#34;)); // split by multiple spaces Collections.reverse(wordList); String r = String.join(\u0026#34; \u0026#34;, wordList); // return a String StringBuilder # StringBuilder sb = new StringBuilder(); // sb.length(); sb.append(str); // append the string to string builder sb.insert(offset, str, start, end); sb.delete(start, end); sb.deleteCharAt(idx); sb.setCharAt(idx, \u0026#39;z\u0026#39;); sb.toString(); // convert string builder to string ArrayList # ArrayList\u0026lt;String\u0026gt; r = new ArrayList\u0026lt;\u0026gt;(); // r.size(); r.add(\u0026#34;add at the end\u0026#34;); r.add(\u0026#34;add at idx 1\u0026#34;, 1); r.remove(2); // remove at index 2 r.clear(); r.set(0, \u0026#34;replace at index 0\u0026#34;); r.get(0); // array -\u0026gt; list List\u0026lt;Boolean\u0026gt; result = Arrays.asList(new Boolean[candies.length]); // fix-sized list return Arrays.asList(array); // from Array to List // list -\u0026gt; array String[] array = new String[0]; array = list.toArray(array); 2D ArrayList # ArrayList\u0026lt;ArrayList\u0026lt;String\u0026gt;\u0026gt; r = new ArrayList(); Deque # // better performance. internal data structure is array, doesn\u0026#39;t support null Deque\u0026lt;Integer\u0026gt; arrayDeque = new ArrayDeque\u0026lt;\u0026gt;(); // better flexibility. internal data structure is double linked list, support null Deque\u0026lt;Integer\u0026gt; linkedList = new LinkedList\u0026lt;\u0026gt;(); // dq.size(); // peekFirst() peekLast() int pf = arrayDeque.peekFirst(); int pl = arrayDeque.peekLast(); // offerFirst() offerLast() arrayDeque.offerFirst(20); arrayDeque.offerLast(10); // removeFirst() removeLast() int dequeuedFromArrayDeque1 = arrayDeque.removeFirst(); int dequeuedFromArrayDeque2 = arrayDeque.removeLast(); // pollFirst() pollLast() // Does not throw an exception if the deque is empty. int dequeuedFromArrayDeque1 = arrayDeque.pollFirst(); int dequeuedFromArrayDeque2 = arrayDeque.pollLast(); HashSet # HashSet\u0026lt;String\u0026gt; hs = new HashSet\u0026lt;\u0026gt;(); // hs.size(); hs.add(\u0026#34;Apple\u0026#34;); hs.remove(\u0026#34;Apple\u0026#34;); hs.clear(); boolean containsApple = hs.contains(\u0026#34;Apple\u0026#34;); Set\u0026lt;String\u0026gt; synchronizedSet = Collections.synchronizedSet(new HashSet\u0026lt;\u0026gt;()); HashMap # HashMap\u0026lt;String, String\u0026gt; hm = new HashMap\u0026lt;\u0026gt;(); // hm.size(); hm.put(\u0026#34;USA\u0026#34;, \u0026#34;DC\u0026#34;); // if key doesn\u0026#39;t exist: add, otherwise update value hm.putIfAbsent(\u0026#34;USA\u0026#34;, \u0026#34;Austin\u0026#34;); // if key exist, do nothing, otherwise add hm.remove(\u0026#34;USA\u0026#34;); hm.clear(); hm.replace(\u0026#34;USA\u0026#34;, \u0026#34;Miami\u0026#34;); // if key doesn\u0026#39;t exist, do nothing hm.getOrDefault(\u0026#34;USA\u0026#34;, \u0026#34;Dallas\u0026#34;); hm.get(\u0026#34;USA\u0026#34;); hm.containsKey(\u0026#34;USA\u0026#34;); hm.containsValue(\u0026#34;DC\u0026#34;); for (String key : hm.keySet()) { String val = hm.get(key); ... } if (!ans.containsKey(key)) ans.put(key, new ArrayList()); return new ArrayList(ans.values()); // Map\u0026lt;String, List\u0026gt; -\u0026gt; List\u0026lt;List\u0026lt;String\u0026gt;\u0026gt; return new ArrayList(); // for return empty (List\u0026lt;List\u0026lt;AnyType\u0026gt;\u0026gt;) Map\u0026lt;Integer, String\u0026gt; synchronizedHashMap = Collections.synchronizedMap(new HashMap\u0026lt;\u0026gt;()); TreeSet # TreeSet\u0026lt;Student\u0026gt; ts = new TreeSet\u0026lt;\u0026gt;( (l, r) -\u0026gt; (l.age - r.age) ); Comparator\u0026lt;Student\u0026gt; ageComparator = new Comparator\u0026lt;Student\u0026gt;() { @Override public int compare(Student s1, Student s2) { return Integer.compare(s1.getAge(), s2.getAge()); } }; TreeSet\u0026lt;Student\u0026gt; student = new TreeSet\u0026lt;\u0026gt;(ageComparator); ts.add(new Student(1, \u0026#34;a\u0026#34;)); // idempotent: if exist, do nothing ts.remove(new Student(1, \u0026#34;a\u0026#34;)); // idempotent // to update: remove the old one, add the updated one ts.contains(new Student(1, \u0026#34;a\u0026#34;); // safe Student first = ts.first(); Student last = ts.last(); Student lower = ts.lower(new Student(3, \u0026#34;anything\u0026#34;)); Student higher = ts.higher(new Student(3, \u0026#34;anything\u0026#34;)); Student floor = ts.floor(new Student(3, \u0026#34;anything\u0026#34;)); Student ceiling = ts.ceiling(new Student(3, \u0026#34;anything)); TreeSet\u0026lt;Student\u0026gt; headSet = (TreeSet\u0026lt;Student\u0026gt;) people.headSet(new Student(21, \u0026#34;anything\u0026#34;)); TreeSet\u0026lt;Student\u0026gt; tailSet = (TreeSet\u0026lt;Student\u0026gt;) people.tailSet(new Student(21, \u0026#34;anything\u0026#34;)); TreeSet\u0026lt;Student\u0026gt; subSet = (TreeSet\u0026lt;Student\u0026gt;) people.subSet(new Student (3, \u0026#34;anything\u0026#34;), new Student (122, \u0026#34;anything\u0026#34;)); SortedSet\u0026lt;String\u0026gt; synchronizedTreeSet = Collections.synchronizedSortedSet(new TreeSet\u0026lt;\u0026gt;()); update element within TreeSet # public static void updatePersonAge(TreeSet\u0026lt;Person\u0026gt; people, String name, int newAge) { // Find the person with the given name Person personToUpdate = null; for (Person person : people) { if (person.getName().equals(name)) { personToUpdate = person; break; } } // If person is found, remove, update and reinsert if (personToUpdate != null) { people.remove(personToUpdate); // Remove the old person personToUpdate.setAge(newAge); // Update the age people.add(personToUpdate); // Add the updated person } } TreeMap # TreeMap\u0026lt;Integer, String\u0026gt; tm = new TreeMap\u0026lt;\u0026gt;(); // tm.size(); tm.put(3, \u0026#34;Three\u0026#34;); tm.remove(2); tm.clear(); String v = tm.get(3); boolean containsKey = tm.containsKey(1); boolean containsValue = tm.containsValue(\u0026#34;Four\u0026#34;); Integer firstKey = tm.firstKey(); Integer lastKey = tm.lastKey(); Integer lowerKey = treeMap.lowerKey(3); // Returns 2 Integer floorKey = treeMap.floorKey(3); // Returns 3 Integer higherKey = treeMap.higherKey(3); // Returns 4 Integer ceilingKey = treeMap.ceilingKey(3); // Returns 3 SortedMap\u0026lt;Integer, String\u0026gt; headMap = treeMap.headMap(3); // Elements \u0026lt; 3 SortedMap\u0026lt;Integer, String\u0026gt; tailMap = treeMap.tailMap(3); // Elements \u0026gt;= 3 SortedMap\u0026lt;Integer, String\u0026gt; subMap = treeMap.subMap(2, 4); // Elements \u0026gt;= 2 and \u0026lt; 4 SortedMap\u0026lt;Integer, String\u0026gt; synchronizedTreeMap = Collections.synchronizedSortedMap(new TreeMap\u0026lt;\u0026gt;()); HashTable # ConcurrentHashMap # ConcurrentHashMap\u0026lt;Integer, String\u0026gt; map = new ConcurrentHashMap\u0026lt;\u0026gt;(); // map.size(); // basic operations map.put(1, \u0026#34;One\u0026#34;); map.remove(2); map.clear(); String value = map.get(1); boolean containsKey = map.containsKey(3); boolean containsValue = map.containsValue(\u0026#34;Three\u0026#34;); // concurrent operations map.putIfAbsent(4, \u0026#34;Four\u0026#34;); // Adds the value only if the key is not already present map.remove(1, \u0026#34;One\u0026#34;); // Removes the entry only if the key is mapped to the specified value map.replace(3, \u0026#34;Three\u0026#34;, \u0026#34;ThreeReplaced\u0026#34;); // Replaces the value only if the key is mapped to the specified value map.compute(3, (key, value) -\u0026gt; value + \u0026#34;Updated\u0026#34;); // Computes a new mapping for the specified key using the given remapping function map.computeIfAbsent(5, key -\u0026gt; \u0026#34;Five\u0026#34;); // Computes a new value for the specified key if it is not already present map.computeIfPresent(3, (key, value) -\u0026gt; value + \u0026#34;UpdatedAgain\u0026#34;); // Computes a new value for the specified key if it is already present Iterating over ConcurrentHashMap # for (Integer key : map.keySet()) { System.out.println(\u0026#34;Key: \u0026#34; + key + \u0026#34;, Value: \u0026#34; + map.get(key)); } for (Map.Entry\u0026lt;Integer, String\u0026gt; entry : map.entrySet()) { System.out.println(\u0026#34;Key: \u0026#34; + entry.getKey() + \u0026#34;, Value: \u0026#34; + entry.getValue()); } for (String value : map.values()) { System.out.println(\u0026#34;Value: \u0026#34; + value); } PriorityQueue # PriorityQueue\u0026lt;Integer\u0026gt; pq = new PriorityQueue\u0026lt;\u0026gt;( (n1, n2) -\u0026gt; map.get(n1) - map.get(n2) ); // pq.size(); pq.add(10); int r = pq.poll(); int r = pq.peek(); boolean r = pq.isEmpty(); PriorityQueue + HashMap # // leetcode 347 class Solution { public int[] topKFrequent(int[] nums, int k) { // 使用字典，统计每个元素出现的次数，元素为键，元素出现的次数为值 Map\u0026lt;Integer, Integer\u0026gt; map = new HashMap(); for(int num : nums){ if (map.containsKey(num)) { map.put(num, map.get(num) + 1); } else { map.put(num, 1); } } // 遍历map，用最小堆保存频率最大的k个元素 // PriorityQueue\u0026lt;Integer\u0026gt; pq = new PriorityQueue\u0026lt;\u0026gt;(new Comparator\u0026lt;Integer\u0026gt;() { // @Override // public int compare(Integer a, Integer b) { // return map.get(a) - map.get(b); // } // }); PriorityQueue\u0026lt;Integer\u0026gt; pq = new PriorityQueue\u0026lt;\u0026gt;( (n1, n2) -\u0026gt; map.get(n1) - map.get(n2) ); for (Integer key : map.keySet()) { pq.add(key); if (pq.size() \u0026gt; k) { pq.poll(); } } // 取出最小堆中的元素 List\u0026lt;Integer\u0026gt; res = new ArrayList\u0026lt;\u0026gt;(); while (!pq.isEmpty()) { res.add(pq.poll()); } // List 变成 Array int[] arrayResult = new int[res.size()]; for (int i = 0; i \u0026lt; res.size(); ++i) { arrayResult[i] = res.get(i); } return arrayResult; } } Queue + Pair # // leetcode 988 class Solution { public String smallestFromLeaf(TreeNode root) { String smallestString = \u0026#34;\u0026#34;; Queue\u0026lt;Pair\u0026lt;TreeNode, String\u0026gt;\u0026gt; nodeQueue = new LinkedList\u0026lt;\u0026gt;(); // Add root node to queue along with its value converted to a character nodeQueue.add(new Pair\u0026lt;\u0026gt;(root, String.valueOf((char)(root.val + \u0026#39;a\u0026#39;)))); // Perform BFS traversal until queue is empty while (!nodeQueue.isEmpty()) { // Pop the leftmost node and its corresponding string from queue Pair\u0026lt;TreeNode, String\u0026gt; pair = nodeQueue.poll(); TreeNode node = pair.getKey(); String currentString = pair.getValue(); // If current node is a leaf node if (node.left == null \u0026amp;\u0026amp; node.right == null) { // Update smallest_string if it\u0026#39;s empty or current string is smaller if (smallestString.isEmpty()) { smallestString = currentString; } else { smallestString = currentString.compareTo(smallestString) \u0026lt; 0 ? currentString : smallestString; } } // If current node has a left child, append it to queue if (node.left != null) { nodeQueue.add(new Pair\u0026lt;\u0026gt;(node.left, (char)(node.left.val + \u0026#39;a\u0026#39;) + currentString)); } // If current node has a right child, append it to queue if (node.right != null) { nodeQueue.add(new Pair\u0026lt;\u0026gt;(node.right, (char)(node.right.val + \u0026#39;a\u0026#39;) + currentString)); } } return smallestString; } } Collections # sort # // List Collections.sort(v, (l, r) -\u0026gt; l.name.compareTo(r.name)); // Array Arrays.sort(v, (l, r) -\u0026gt; l.name.compareTo(r.name)); min # // List Student my_min = Collections.min(vl, (l, r) -\u0026gt; l.age - r.age); stream # // people is ArrayList List\u0026lt;Person\u0026gt; hundredSorted = people.stream() .filter(person -\u0026gt; person.billions \u0026gt;= 100) .sorted(Comparator.comparing(person -\u0026gt; person.name)) .collect(Collectors.toList()); hundredSorted.forEach(person -\u0026gt; System.out.println(person.name)); Functional Interface # Predicate: Represents a predicate (boolean-valued function) of one argument.\nMethod: boolean test(T t) Consumer: Represents an operation that accepts a single input argument and returns no result.\nMethod: void accept(T t) Function\u0026lt;T, R\u0026gt;: Represents a function that accepts one argument and produces a result.\nMethod: R apply(T t) Supplier: Represents a supplier of results.\nMethod: T get() UnaryOperator: Represents an operation on a single operand that produces a result of the same type as its operand.\nMethod: T apply(T t) BinaryOperator: Represents an operation upon two operands of the same type, producing a result of the same type as the operands.\nMethod: T apply(T t1, T t2) Predicate\u0026lt;Integer\u0026gt; isEven = num -\u0026gt; num % 2 == 0; System.out.println(isEven.test(4)); // Outputs: true System.out.println(isEven.test(3)); // Outputs: false Function\u0026lt;String, Integer\u0026gt; stringLength = String::length; System.out.println(stringLength.apply(\u0026#34;Hello\u0026#34;)); // Outputs: 5 Supplier\u0026lt;Double\u0026gt; randomValue = Math::random; System.out.println(randomValue.get()); // Outputs a random value between 0.0 and 1.0 Consumer\u0026lt;String\u0026gt; print = System.out::println; print.accept(\u0026#34;Hello, world!\u0026#34;); // Outputs: Hello, world! UnaryOperator\u0026lt;Integer\u0026gt; square = x -\u0026gt; x * x; System.out.println(square.apply(5)); // Outputs: 25 Combining Functional Interfaces # Functional interfaces can be combined to create more complex behaviors using default methods like andThen and compose. Consumer\u0026lt;String\u0026gt; greet = name -\u0026gt; System.out.println(\u0026#34;Hello, \u0026#34; + name); Consumer\u0026lt;String\u0026gt; askHowAreYou = name -\u0026gt; System.out.println(\u0026#34;How are you, \u0026#34; + name + \u0026#34;?\u0026#34;); Consumer\u0026lt;String\u0026gt; greetAndAsk = greet.andThen(askHowAreYou); greetAndAsk.accept(\u0026#34;John\u0026#34;); // Outputs: // Hello, John // How are you, John? Method Reference # List\u0026lt;Integer\u0026gt; numbers = Arrays.asList(1, 2, 3, 4, 5); // Using a method reference to a static method numbers.forEach(MethodReferenceExample::printNumber); List\u0026lt;String\u0026gt; messages = Arrays.asList(\u0026#34;Hello\u0026#34;, \u0026#34;World\u0026#34;, \u0026#34;Java\u0026#34;); MethodReferenceExample example = new MethodReferenceExample(); // Using a method reference to an instance method of a particular object messages.forEach(example::printMessage); List\u0026lt;String\u0026gt; messages = Arrays.asList(\u0026#34;Hello\u0026#34;, \u0026#34;World\u0026#34;, \u0026#34;Java\u0026#34;); // Using a method reference to an instance method of an arbitrary object messages.forEach(String::toUpperCase); // Using a method reference to a constructor Supplier\u0026lt;ArrayList\u0026lt;String\u0026gt;\u0026gt; listSupplier = ArrayList::new; ArrayList\u0026lt;String\u0026gt; list = listSupplier.get(); Optional Class # Creating Optional Instance # Empty Optional: Represents a missing or absent value. Represents a missing or absent value. Non-empty Optional: Wraps a non-null value. Optional\u0026lt;String\u0026gt; nonEmpty = Optional.of(\u0026#34;Hello\u0026#34;); Nullable Optional: Can wrap a value that might be null, returning an empty Optional if the value is null. Optional\u0026lt;String\u0026gt; nullable = Optional.ofNullable(null); Basic Operations # boolean present = nonEmpty.isPresent(); // true boolean absent = empty.isPresent(); // false nonEmpty.ifPresent(value -\u0026gt; System.out.println(\u0026#34;Value: \u0026#34; + value)); // Outputs: Value: Hello String value = nonEmpty.get(); // \u0026#34;Hello\u0026#34; // empty.get(); // Throws NoSuchElementException String value = empty.orElse(\u0026#34;Default\u0026#34;); // \u0026#34;Default\u0026#34; String value = empty.orElseGet(() -\u0026gt; \u0026#34;Default from Supplier\u0026#34;); // \u0026#34;Default from Supplier\u0026#34; // empty.orElseThrow(() -\u0026gt; new IllegalArgumentException(\u0026#34;No value present\u0026#34;)); // Throws IllegalArgumentException Transforming Optional Values # // map(): Applies a function to the value if present and returns an `Optional` containing the result. Optional\u0026lt;Integer\u0026gt; length = nonEmpty.map(String::length); // Optional[5] // flatmap(): Similar to `map`, but the function must return an `Optional`. It is used to avoid nested optionals. Optional\u0026lt;String\u0026gt; nonEmptyUpper = nonEmpty.flatMap(val -\u0026gt; Optional.of(val.toUpperCase())); // Optional[HELLO] // filter(): Applies a predicate to the value if present and returns an Optional containing the value if it matches the predicate. Optional\u0026lt;String\u0026gt; filtered = nonEmpty.filter(val -\u0026gt; val.length() \u0026gt; 3); // Optional[Hello] Optional\u0026lt;String\u0026gt; filteredEmpty = nonEmpty.filter(val -\u0026gt; val.length() \u0026gt; 5); // Optional.empty Example # public class Person { private Optional\u0026lt;Address\u0026gt; address; public Optional\u0026lt;Address\u0026gt; getAddress() { return address; } } public class Address { private String city; public String getCity() { return city; } } public class Main { public static void main(String[] args) { Person person = new Person(); String city = person.getAddress() .flatMap(Address::getCity) .orElse(\u0026#34;Unknown city\u0026#34;); System.out.println(\u0026#34;City: \u0026#34; + city); // Outputs: City: Unknown city } } Combining Options # Chaining Options: Using methods like flatMap, you can chain multiple optionals. Optional\u0026lt;Person\u0026gt; person = Optional.of(new Person()); String city = person.flatMap(Person::getAddress) .map(Address::getCity) .orElse(\u0026#34;Unknown city\u0026#34;); Merging Options: Combining the values of multiple optionals if they are present. Optional\u0026lt;String\u0026gt; opt1 = Optional.of(\u0026#34;Hello\u0026#34;); Optional\u0026lt;String\u0026gt; opt2 = Optional.of(\u0026#34;World\u0026#34;); Optional\u0026lt;String\u0026gt; combined = opt1.flatMap(val1 -\u0026gt; opt2.map(val2 -\u0026gt; val1 + \u0026#34; \u0026#34; + val2)); combined.ifPresent(System.out::println); // Outputs: Hello World Best Practices # Use Optional for Return Types: Use Optional as the return type of methods to indicate that the method might not return a value. Avoid Optional in Fields and Parameters: Avoid using Optional for fields or method parameters, as it can introduce unnecessary complexity. Prefer orElseGet over orElse: Use orElseGet instead of orElse when the default value computation is expensive or has side effects. Do Not Use Optional for Collection Elements: Avoid using Optional for elements of collections, as it can lead to a convoluted design. Instead, use an empty collection to represent the absence of elements. compareTo # add implements Comparable\u0026lt;Student\u0026gt; in class override method @Override public int compareTo(Student other) { return this.age.compareTo(other.age); } Comparator # Comparator\u0026lt;Student\u0026gt; ageComparator = new Comparator\u0026lt;Student\u0026gt;() { @Override public int compare(Student p1, Student p2) { return Integer.compare(p1.getAge(), p2.getAge()); } }; TreeSet\u0026lt;Student\u0026gt; people = new TreeSet\u0026lt;\u0026gt;(ageComparator); Multithreading # Thread # public class ThreadExample extends Thread { @Override public void run() { System.out.println(\u0026#34;Thread is running: \u0026#34; + Thread.currentThread().getName()); } public static void main(String[] args) { ThreadExample thread = new ThreadExample(); thread.start(); } } Runnable # public class RunnableExample implements Runnable { @Override public void run() { System.out.println(\u0026#34;Runnable is running: \u0026#34; + Thread.currentThread().getName()); } public static void main(String[] args) { Thread thread = new Thread(new RunnableExample()); thread.start(); } } Callable # import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CallableExample implements Callable\u0026lt;String\u0026gt; { @Override public String call() throws Exception { return \u0026#34;Callable result from \u0026#34; + Thread.currentThread().getName(); } public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(1); CallableExample callableTask = new CallableExample(); Future\u0026lt;String\u0026gt; future = executor.submit(callableTask); try { String result = future.get(); System.out.println(result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); } } } CompletableFuture # import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CompletableFutureExample { public static void main(String[] args) { CompletableFuture\u0026lt;String\u0026gt; future = CompletableFuture.supplyAsync(() -\u0026gt; { return \u0026#34;Result from \u0026#34; + Thread.currentThread().getName(); }); try { String result = future.get(); System.out.println(result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } } Thread Pool # import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorExample { public static void main(String[] args) { ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2); for (int i = 0; i \u0026lt; 5; i++) { executor.submit(() -\u0026gt; { System.out.println(\u0026#34;Task executed by \u0026#34; + Thread.currentThread().getName()); }); } executor.shutdown(); try { executor.awaitTermination(1, TimeUnit.MINUTES); } catch (InterruptedException e) { e.printStackTrace(); } } } Virtual Thread # import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class VirtualThreadExample { public static void main(String[] args) { var executor = Executors.newVirtualThreadPerTaskExecutor(); Future\u0026lt;String\u0026gt; future = executor.submit(() -\u0026gt; { return \u0026#34;Result from virtual thread: \u0026#34; + Thread.currentThread().getName(); }); try { String result = future.get(); System.out.println(result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); } } } Lock # CAS # Design Pattern # Singleton # add final keyword before the class template to prevent inheritance. Add a private static field to the class for storing the singleton instance. Declare a public static creation method for getting the singleton instance. Implement “lazy initialization” inside the static method. It should create a new object on its first call and put it into the static field. The method should always return that instance on all subsequent calls. Make the constructor of the class private. The static method of the class will still be able to call the constructor, but not the other objects. Go over the client code and replace all direct calls to the singleton’s constructor with calls to its static creation method. package refactoring_guru.singleton.example.non_thread_safe; public final class Singleton { private static Singleton instance; public String value; private Singleton(String value) { // The following code emulates slow initialization. try { Thread.sleep(1000); } catch (InterruptedException ex) { ex.printStackTrace(); } this.value = value; } public static Singleton getInstance(String value) { if (instance == null) { instance = new Singleton(value); } return instance; } } Simple Factory # Builder # Proxy # Adaptors # Observers # ","date":"11 April 2024","externalUrl":null,"permalink":"/notes/java_last_minute/","section":"Notes","summary":"Basic data types # int \u003c-\u003e String # // first is \"123\" // scond is \"234\" String sumStr = String.valueOf(Long.parseLong(first) + Long.parseLong(second)); 1D Array # int[] r = new int[26]; // r.length; String[] r = {\"c1\", \"c2\", \"c3\"}; Arrays.fill(r, 0); 2D Array # String[][] r = new String[3][3]; String[][] r = {{\"a\", \"b\"}, {\"c\", \"d\"}}; for (String[] row : r) { Arrays.fill(row, \"apple\"); // Fill each row with the string \"apple\" } Character # Character.isLetterOrDigit(s.charAt(i)) // isWhitespace, isLetter, isDigit Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j)) String # String name = \"Bro\"; // name.length(); char[] ch = str.toCharArray(); return new String(ch) // convert char array back to String substring(int beginIndex, int endIndex) String r = name.trim(); String r = name.replace('o', 'a'); // Replace all 'a' to 'o' boolean r = name.equals(\"Bro\"); boolean r = name.equalsIgnoreCase(\"bro\"); int r = str1.compareTo(str2) // lexicographically, \u003e 0, \u003c 0, == 0 char r = name.charAt(0); int r = name.indexOf(\"B\"); boolean r = name.startsWith(\"A\"); boolean r = name.isEmpty(); String r = name.toUpperCase(); // toLowerCase List\u003cString\u003e wordList = Arrays.asList(s.split(\"\\\\s+\")); // split by multiple spaces Collections.reverse(wordList); String r = String.join(\" \", wordList); // return a String StringBuilder # StringBuilder sb = new StringBuilder(); // sb.length(); sb.append(str); // append the string to string builder sb.insert(offset, str, start, end); sb.delete(start, end); sb.deleteCharAt(idx); sb.setCharAt(idx, 'z'); sb.toString(); // convert string builder to string ArrayList # ArrayList\u003cString\u003e r = new ArrayList\u003c\u003e(); // r.size(); r.add(\"add at the end\"); r.add(\"add at idx 1\", 1); r.remove(2); // remove at index 2 r.clear(); r.set(0, \"replace at index 0\"); r.get(0); // array -\u003e list List\u003cBoolean\u003e result = Arrays.asList(new Boolean[candies.length]); // fix-sized list return Arrays.asList(array); // from Array to List // list -\u003e array String[] array = new String[0]; array = list.toArray(array); 2D ArrayList # ArrayList\u003cArrayList\u003cString\u003e\u003e r = new ArrayList(); Deque # // better performance. internal data structure is array, doesn't support null Deque\u003cInteger\u003e arrayDeque = new ArrayDeque\u003c\u003e(); // better flexibility. internal data structure is double linked list, support null Deque\u003cInteger\u003e linkedList = new LinkedList\u003c\u003e(); // dq.size(); // peekFirst() peekLast() int pf = arrayDeque.peekFirst(); int pl = arrayDeque.peekLast(); // offerFirst() offerLast() arrayDeque.offerFirst(20); arrayDeque.offerLast(10); // removeFirst() removeLast() int dequeuedFromArrayDeque1 = arrayDeque.removeFirst(); int dequeuedFromArrayDeque2 = arrayDeque.removeLast(); // pollFirst() pollLast() // Does not throw an exception if the deque is empty. int dequeuedFromArrayDeque1 = arrayDeque.pollFirst(); int dequeuedFromArrayDeque2 = arrayDeque.pollLast(); HashSet # HashSet\u003cString\u003e hs = new HashSet\u003c\u003e(); // hs.size(); hs.add(\"Apple\"); hs.remove(\"Apple\"); hs.clear(); boolean containsApple = hs.contains(\"Apple\"); Set\u003cString\u003e synchronizedSet = Collections.synchronizedSet(new HashSet\u003c\u003e()); HashMap # HashMap\u003cString, String\u003e hm = new HashMap\u003c\u003e(); // hm.size(); hm.put(\"USA\", \"DC\"); // if key doesn't exist: add, otherwise update value hm.putIfAbsent(\"USA\", \"Austin\"); // if key exist, do nothing, otherwise add hm.remove(\"USA\"); hm.clear(); hm.replace(\"USA\", \"Miami\"); // if key doesn't exist, do nothing hm.getOrDefault(\"USA\", \"Dallas\"); hm.get(\"USA\"); hm.containsKey(\"USA\"); hm.containsValue(\"DC\"); for (String key : hm.keySet()) { String val = hm.get(key); ... } if (!ans.containsKey(key)) ans.put(key, new ArrayList()); return new ArrayList(ans.values()); // Map\u003cString, List\u003e -\u003e List\u003cList\u003cString\u003e\u003e return new ArrayList(); // for return empty (List\u003cList\u003cAnyType\u003e\u003e) Map\u003cInteger, String\u003e synchronizedHashMap = Collections.synchronizedMap(new HashMap\u003c\u003e()); TreeSet # TreeSet\u003cStudent\u003e ts = new TreeSet\u003c\u003e( (l, r) -\u003e (l.age - r.age) ); Comparator\u003cStudent\u003e ageComparator = new Comparator\u003cStudent\u003e() { @Override public int compare(Student s1, Student s2) { return Integer.compare(s1.getAge(), s2.getAge()); } }; TreeSet\u003cStudent\u003e student = new TreeSet\u003c\u003e(ageComparator); ts.add(new Student(1, \"a\")); // idempotent: if exist, do nothing ts.remove(new Student(1, \"a\")); // idempotent // to update: remove the old one, add the updated one ts.contains(new Student(1, \"a\"); // safe Student first = ts.first(); Student last = ts.last(); Student lower = ts.lower(new Student(3, \"anything\")); Student higher = ts.higher(new Student(3, \"anything\")); Student floor = ts.floor(new Student(3, \"anything\")); Student ceiling = ts.ceiling(new Student(3, \"anything)); TreeSet\u003cStudent\u003e headSet = (TreeSet\u003cStudent\u003e) people.headSet(new Student(21, \"anything\")); TreeSet\u003cStudent\u003e tailSet = (TreeSet\u003cStudent\u003e) people.tailSet(new Student(21, \"anything\")); TreeSet\u003cStudent\u003e subSet = (TreeSet\u003cStudent\u003e) people.subSet(new Student (3, \"anything\"), new Student (122, \"anything\")); SortedSet\u003cString\u003e synchronizedTreeSet = Collections.synchronizedSortedSet(new TreeSet\u003c\u003e()); update element within TreeSet # public static void updatePersonAge(TreeSet\u003cPerson\u003e people, String name, int newAge) { // Find the person with the given name Person personToUpdate = null; for (Person person : people) { if (person.getName().equals(name)) { personToUpdate = person; break; } } // If person is found, remove, update and reinsert if (personToUpdate != null) { people.remove(personToUpdate); // Remove the old person personToUpdate.setAge(newAge); // Update the age people.add(personToUpdate); // Add the updated person } } TreeMap # TreeMap\u003cInteger, String\u003e tm = new TreeMap\u003c\u003e(); // tm.size(); tm.put(3, \"Three\"); tm.remove(2); tm.clear(); String v = tm.get(3); boolean containsKey = tm.containsKey(1); boolean containsValue = tm.containsValue(\"Four\"); Integer firstKey = tm.firstKey(); Integer lastKey = tm.lastKey(); Integer lowerKey = treeMap.lowerKey(3); // Returns 2 Integer floorKey = treeMap.floorKey(3); // Returns 3 Integer higherKey = treeMap.higherKey(3); // Returns 4 Integer ceilingKey = treeMap.ceilingKey(3); // Returns 3 SortedMap\u003cInteger, String\u003e headMap = treeMap.headMap(3); // Elements \u003c 3 SortedMap\u003cInteger, String\u003e tailMap = treeMap.tailMap(3); // Elements \u003e= 3 SortedMap\u003cInteger, String\u003e subMap = treeMap.subMap(2, 4); // Elements \u003e= 2 and \u003c 4 SortedMap\u003cInteger, String\u003e synchronizedTreeMap = Collections.synchronizedSortedMap(new TreeMap\u003c\u003e()); HashTable # ConcurrentHashMap # ConcurrentHashMap\u003cInteger, String\u003e map = new ConcurrentHashMap\u003c\u003e(); // map.size(); // basic operations map.put(1, \"One\"); map.remove(2); map.clear(); String value = map.get(1); boolean containsKey = map.containsKey(3); boolean containsValue = map.containsValue(\"Three\"); // concurrent operations map.putIfAbsent(4, \"Four\"); // Adds the value only if the key is not already present map.remove(1, \"One\"); // Removes the entry only if the key is mapped to the specified value map.replace(3, \"Three\", \"ThreeReplaced\"); // Replaces the value only if the key is mapped to the specified value map.compute(3, (key, value) -\u003e value + \"Updated\"); // Computes a new mapping for the specified key using the given remapping function map.computeIfAbsent(5, key -\u003e \"Five\"); // Computes a new value for the specified key if it is not already present map.computeIfPresent(3, (key, value) -\u003e value + \"UpdatedAgain\"); // Computes a new value for the specified key if it is already present Iterating over ConcurrentHashMap # for (Integer key : map.keySet()) { System.out.println(\"Key: \" + key + \", Value: \" + map.get(key)); } for (Map.Entry\u003cInteger, String\u003e entry : map.entrySet()) { System.out.println(\"Key: \" + entry.getKey() + \", Value: \" + entry.getValue()); } for (String value : map.values()) { System.out.println(\"Value: \" + value); } PriorityQueue # PriorityQueue\u003cInteger\u003e pq = new PriorityQueue\u003c\u003e( (n1, n2) -\u003e map.get(n1) - map.get(n2) ); // pq.size(); pq.add(10); int r = pq.poll(); int r = pq.peek(); boolean r = pq.isEmpty(); PriorityQueue + HashMap # // leetcode 347 class Solution { public int[] topKFrequent(int[] nums, int k) { // 使用字典，统计每个元素出现的次数，元素为键，元素出现的次数为值 Map\u003cInteger, Integer\u003e map = new HashMap(); for(int num : nums){ if (map.containsKey(num)) { map.put(num, map.get(num) + 1); } else { map.put(num, 1); } } // 遍历map，用最小堆保存频率最大的k个元素 // PriorityQueue\u003cInteger\u003e pq = new PriorityQueue\u003c\u003e(new Comparator\u003cInteger\u003e() { // @Override // public int compare(Integer a, Integer b) { // return map.get(a) - map.get(b); // } // }); PriorityQueue\u003cInteger\u003e pq = new PriorityQueue\u003c\u003e( (n1, n2) -\u003e map.get(n1) - map.get(n2) ); for (Integer key : map.keySet()) { pq.add(key); if (pq.size() \u003e k) { pq.poll(); } } // 取出最小堆中的元素 List\u003cInteger\u003e res = new ArrayList\u003c\u003e(); while (!pq.isEmpty()) { res.add(pq.poll()); } // List 变成 Array int[] arrayResult = new int[res.size()]; for (int i = 0; i \u003c res.size(); ++i) { arrayResult[i] = res.get(i); } return arrayResult; } } Queue + Pair # // leetcode 988 class Solution { public String smallestFromLeaf(TreeNode root) { String smallestString = \"\"; Queue\u003cPair\u003cTreeNode, String\u003e\u003e nodeQueue = new LinkedList\u003c\u003e(); // Add root node to queue along with its value converted to a character nodeQueue.add(new Pair\u003c\u003e(root, String.valueOf((char)(root.val + 'a')))); // Perform BFS traversal until queue is empty while (!nodeQueue.isEmpty()) { // Pop the leftmost node and its corresponding string from queue Pair\u003cTreeNode, String\u003e pair = nodeQueue.poll(); TreeNode node = pair.getKey(); String currentString = pair.getValue(); // If current node is a leaf node if (node.left == null \u0026\u0026 node.right == null) { // Update smallest_string if it's empty or current string is smaller if (smallestString.isEmpty()) { smallestString = currentString; } else { smallestString = currentString.compareTo(smallestString) \u003c 0 ? currentString : smallestString; } } // If current node has a left child, append it to queue if (node.left != null) { nodeQueue.add(new Pair\u003c\u003e(node.left, (char)(node.left.val + 'a') + currentString)); } // If current node has a right child, append it to queue if (node.right != null) { nodeQueue.add(new Pair\u003c\u003e(node.right, (char)(node.right.val + 'a') + currentString)); } } return smallestString; } } Collections # sort # // List Collections.sort(v, (l, r) -\u003e l.name.compareTo(r.name)); // Array Arrays.sort(v, (l, r) -\u003e l.name.compareTo(r.name)); min # // List Student my_min = Collections.min(vl, (l, r) -\u003e l.age - r.age); stream # // people is ArrayList List\u003cPerson\u003e hundredSorted = people.stream() .filter(person -\u003e person.billions \u003e= 100) .sorted(Comparator.comparing(person -\u003e person.name)) .collect(Collectors.toList()); hundredSorted.forEach(person -\u003e System.out.println(person.name)); Functional Interface # Predicate: Represents a predicate (boolean-valued function) of one argument.\n","title":"Java Last Minute","type":"notes"},{"content":"Coverd all topics of Union Find\nIt\u0026rsquo;s a data structure that supports fast merging and searching of sets\nO(1) merge the two sets where x and y are located: Merge(x, y) \u0026ndash; connect the roots O(1) find the set to which x belongs: Find(x) \u0026ndash; find the root O(1) check wheter x and y are in the same set: IsConnected(x, y) \u0026ndash; check if they have the same root Graph: may have cycle Tree: no cycle Necessary and sufficient conditions for a graph to be a tree: There are n vertices on the graph and only n-1 edges. n points are connected (belong to the same connected block) Leetcode 261. Graph Valid Tree # Leetcode 261. Graph Valid Tree BFS # class Solution { public: bool validTree(int n, vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; edges) { // checking for condition 1 if (edges.size() + 1 != n) return false; // build graph unordered_map\u0026lt;int, vector\u0026lt;int\u0026gt;\u0026gt; graph; for (int i = 0; i \u0026lt; edges.size(); ++i) { graph[edges[i][0]].push_back(edges[i][1]); graph[edges[i][1]].push_back(edges[i][0]); } // BFS: checking for condition 2 deque\u0026lt;int\u0026gt; q; unordered_set\u0026lt;int\u0026gt; visited; q.push_back(0); visited.insert(0); while (!q.empty()) { int size = q.size(); for (int i = 0; i \u0026lt; size; ++i) { int curr = q.front(); q.pop_front(); for (int j = 0; j \u0026lt; graph[curr].size(); ++j) { if (visited.find(graph[curr][j]) != visited.end()) continue; q.push_back(graph[curr][j]); visited.insert(graph[curr][j]); } } } return visited.size() == n; } }; Follow-up(Union Find): come up with a better approach for AddEdge(x, y) and IsValidTree() # 解决连通性问题的利器 \u0026ndash; Union Find\nLintcode 444. Graph Valid Tree II\nclass Solution { public: void addEdge(int a, int b) { } bool isValidTree() { } };","date":"7 April 2024","externalUrl":null,"permalink":"/blog/d_union_find/","section":"Blog","summary":"Coverd all topics of Union Find\nIt’s a data structure that supports fast merging and searching of sets\nO(1) merge the two sets where x and y are located: Merge(x, y) – connect the roots O(1) find the set to which x belongs: Find(x) – find the root O(1) check wheter x and y are in the same set: IsConnected(x, y) – check if they have the same root ","title":"Union Find","type":"blog"},{"content":" c++ const # link // value of x and y can be altered // x = 9; y = \u0026#39;A\u0026#39;; // value of i and j can be altered // i = \u0026amp;m; j = \u0026amp;n; // !!! value of *i and *j cannot be altered // *i = 6; *j = 7; // read-only variable is not assignable const int* i = \u0026amp;x; const char* j = \u0026amp;y; // value of x and y can be altered // x = 9; y = \u0026#39;A\u0026#39;; // !!! value of i and j cannot be altered // i = \u0026amp;m; j = \u0026amp;n; // variable \u0026#39;i\u0026#39; and \u0026#39;j\u0026#39; declared const here // value of *i and *j can be altered // *i = 6; *j = \u0026#39;A\u0026#39;; int* const i = \u0026amp;x; char* const j = \u0026amp;y; // value of x and y can be altered // x = 9; y = \u0026#39;A\u0026#39;; // !!! value of i and j cannot be altered // i = \u0026amp;m; j = \u0026amp;n; // !!! value of *i and *j cannot be altered // *i = 6; *j = 7; const int* const i = \u0026amp;x; const char* const j = \u0026amp;y; The compile-time error that will appear as if const value is passed to any non-const argument of the function\n// error: no matching function for call to \u0026#39;foo\u0026#39; // candidate function not viable: 1st argument (\u0026#39;const int *\u0026#39;) would lose const qualifier int foo(int* y) { return *y; } int main() { int z = 8; const int* x = \u0026amp;z; std::cout \u0026lt;\u0026lt; foo(x) \u0026lt;\u0026lt; std::endl; return 0; } // Function foo() with variable // const int void foo(const int y) { // y = 6; const value // can\u0026#39;t be change cout \u0026lt;\u0026lt; y; } // Function foo() with variable int void foo1(int y) { // Non-const value can be change y = 5; cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39; \u0026lt;\u0026lt; y; } // Driver Code int main() { int x = 9; const int z = 10; foo(z); foo1(x); return 0; } const return\n// int foo(int y) { // no error // const int foo(int y) { // no error const int foo(const int y) { // error: cannot assign to variable \u0026#39;y\u0026#39; with const-qualified type \u0026#39;const int\u0026#39; --y; return y; } int main() { int x = 9; const int z = 10; std::cout \u0026lt;\u0026lt; foo(x) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39; \u0026lt;\u0026lt; foo(z); return 0; } An object declared as const cannot be modified and hence, can invoke only const member functions as these functions ensure not to modify the object.\nWhen a function is declared as const, it can be called on any type of object, const object as well as non-const objects.\nclass Test { public: // Constructor Test(int v = 0) { value = v; } // this const means cannot modify class members, e.g. value // We get compiler error if we add a line like \u0026#34;value = 100;\u0026#34; // in this function. int getValue() const { return value; } // a nonconst function trying to modify value void setValue(int val) { value = val; } private: int value; }; // Driver Code int main() { // Object of the class T Test t(20); // non-const object invoking const function, no error cout \u0026lt;\u0026lt; t.getValue() \u0026lt;\u0026lt; endl; // const object const Test t_const(10); // const object invoking const function, no error cout \u0026lt;\u0026lt; t_const.getValue() \u0026lt;\u0026lt; endl; // const object invoking non-const function, CTE // t_const.setValue(15); // non-const object invoking non-const function, no error t.setValue(12); cout \u0026lt;\u0026lt; t.getValue() \u0026lt;\u0026lt; endl; return 0; } ","date":"31 March 2024","externalUrl":null,"permalink":"/notes/cpp_const/","section":"Notes","summary":"c++ const # link // value of x and y can be altered // x = 9; y = 'A'; // value of i and j can be altered // i = \u0026m; j = \u0026n; // !!! value of *i and *j cannot be altered // *i = 6; *j = 7; // read-only variable is not assignable const int* i = \u0026x; const char* j = \u0026y; // value of x and y can be altered // x = 9; y = 'A'; // !!! value of i and j cannot be altered // i = \u0026m; j = \u0026n; // variable 'i' and 'j' declared const here // value of *i and *j can be altered // *i = 6; *j = 'A'; int* const i = \u0026x; char* const j = \u0026y; // value of x and y can be altered // x = 9; y = 'A'; // !!! value of i and j cannot be altered // i = \u0026m; j = \u0026n; // !!! value of *i and *j cannot be altered // *i = 6; *j = 7; const int* const i = \u0026x; const char* const j = \u0026y; The compile-time error that will appear as if const value is passed to any non-const argument of the function\n","title":"C++ Const","type":"notes"},{"content":" Comparator - set # Leetcode 3102. Minimize Manhattan Distances class Node { public: Node(int dist) : sum(dist) {} int first; int second; int sum; }; class Comparator { public: // bool operator()(const int\u0026amp; a, const int\u0026amp; b) const { // return a \u0026gt; b; // } // bool operator()(Node* const\u0026amp; left, Node* const\u0026amp; right) const { // works // bool operator()(Node* left, Node* right) const { // works bool operator()(const Node* left, const Node* right) const { // works if (left-\u0026gt;sum == right-\u0026gt;sum) { if (left-\u0026gt;first == right-\u0026gt;first) { return left-\u0026gt;second \u0026lt; right-\u0026gt;second; } return left-\u0026gt;first \u0026lt; right-\u0026gt;first; } return left-\u0026gt;sum \u0026gt; right-\u0026gt;sum; } }; bool comp(Node* const\u0026amp; left, Node* const\u0026amp; right) { if (left-\u0026gt;sum == right-\u0026gt;sum) { if (left-\u0026gt;first == right-\u0026gt;first) { return left-\u0026gt;second \u0026lt; right-\u0026gt;second; } return left-\u0026gt;first \u0026lt; right-\u0026gt;first; } return left-\u0026gt;sum \u0026gt; right-\u0026gt;sum; } class Solution { public: int minimumDistance(vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; nums) { if (nums.size() \u0026lt; 2) return -1; // set\u0026lt;Node*, decltype(\u0026amp;comp)\u0026gt; heap; // not work // set\u0026lt;Node*, decltype(\u0026amp;comp)\u0026gt; heap(\u0026amp;comp); // works // set\u0026lt;Node*, decltype(comp)*\u0026gt; heap(\u0026amp;comp); // works set\u0026lt;Node*, Comparator\u0026gt; heap; // works for (int i = 0; i \u0026lt; nums.size(); ++i) { for (int j = i + 1; j \u0026lt; nums.size(); ++j) { Node* node = new Node(Distance(nums[i][0], nums[j][0], nums[i][1], nums[j][1])); node-\u0026gt;first = i; node-\u0026gt;second = j; heap.insert(node); } } int min_val = INT_MAX; // iterate remove each for (int i = 0; i \u0026lt; nums.size(); ++i) { // remove i, then compete min_val = min(min_val, Find(heap, i)); } return min_val == INT_MAX ? -1 : min_val; } int Find(set\u0026lt;Node*, Comparator\u0026gt;\u0026amp; heap, int idx) { for (auto it = heap.begin(); it != heap.end(); ++it) { if ((*it)-\u0026gt;first != idx \u0026amp;\u0026amp; (*it)-\u0026gt;second != idx) { return (*it)-\u0026gt;sum; } } return -1; } int Distance(int\u0026amp; x1, int\u0026amp; x2, int\u0026amp; y1, int\u0026amp; y2) { return abs(x1 - x2) + abs(y1 - y2); } }; Comparator - priority_queue # Leetcode 23. Merge k Sorted Lists // Comparison function for priority queue struct CompareNodes { bool operator()(const ListNode* lhs, const ListNode* rhs) const { return lhs-\u0026gt;val \u0026gt; rhs-\u0026gt;val; } }; class Solution { public: ListNode* mergeKLists(vector\u0026lt;ListNode*\u0026gt;\u0026amp; lists) { ListNode* head = new ListNode(0); ListNode* point = head; // Define priority queue with custom comparison function priority_queue\u0026lt;ListNode*, vector\u0026lt;ListNode*\u0026gt;, CompareNodes\u0026gt; q; // Push the heads of all lists into the priority queue for (ListNode* l : lists) { if (l) { q.push(l); } } // Merge the lists while (!q.empty()) { ListNode* node = q.top(); q.pop(); // Add the current smallest node to the merged list point-\u0026gt;next = new ListNode(node-\u0026gt;val); point = point-\u0026gt;next; // Move the pointer of the current list forward node = node-\u0026gt;next; // If there\u0026#39;s remaining elements in the current list, push it to the // priority queue if (node) { q.push(node); } } return head-\u0026gt;next; } }; ","date":"31 March 2024","externalUrl":null,"permalink":"/notes/cpp_comparator/","section":"Notes","summary":"Comparator - set # Leetcode 3102. Minimize Manhattan Distances class Node { public: Node(int dist) : sum(dist) {} int first; int second; int sum; }; class Comparator { public: // bool operator()(const int\u0026 a, const int\u0026 b) const { // return a \u003e b; // } // bool operator()(Node* const\u0026 left, Node* const\u0026 right) const { // works // bool operator()(Node* left, Node* right) const { // works bool operator()(const Node* left, const Node* right) const { // works if (left-\u003esum == right-\u003esum) { if (left-\u003efirst == right-\u003efirst) { return left-\u003esecond \u003c right-\u003esecond; } return left-\u003efirst \u003c right-\u003efirst; } return left-\u003esum \u003e right-\u003esum; } }; bool comp(Node* const\u0026 left, Node* const\u0026 right) { if (left-\u003esum == right-\u003esum) { if (left-\u003efirst == right-\u003efirst) { return left-\u003esecond \u003c right-\u003esecond; } return left-\u003efirst \u003c right-\u003efirst; } return left-\u003esum \u003e right-\u003esum; } class Solution { public: int minimumDistance(vector\u003cvector\u003cint\u003e\u003e\u0026 nums) { if (nums.size() \u003c 2) return -1; // set\u003cNode*, decltype(\u0026comp)\u003e heap; // not work // set\u003cNode*, decltype(\u0026comp)\u003e heap(\u0026comp); // works // set\u003cNode*, decltype(comp)*\u003e heap(\u0026comp); // works set\u003cNode*, Comparator\u003e heap; // works for (int i = 0; i \u003c nums.size(); ++i) { for (int j = i + 1; j \u003c nums.size(); ++j) { Node* node = new Node(Distance(nums[i][0], nums[j][0], nums[i][1], nums[j][1])); node-\u003efirst = i; node-\u003esecond = j; heap.insert(node); } } int min_val = INT_MAX; // iterate remove each for (int i = 0; i \u003c nums.size(); ++i) { // remove i, then compete min_val = min(min_val, Find(heap, i)); } return min_val == INT_MAX ? -1 : min_val; } int Find(set\u003cNode*, Comparator\u003e\u0026 heap, int idx) { for (auto it = heap.begin(); it != heap.end(); ++it) { if ((*it)-\u003efirst != idx \u0026\u0026 (*it)-\u003esecond != idx) { return (*it)-\u003esum; } } return -1; } int Distance(int\u0026 x1, int\u0026 x2, int\u0026 y1, int\u0026 y2) { return abs(x1 - x2) + abs(y1 - y2); } }; Comparator - priority_queue # Leetcode 23. Merge k Sorted Lists // Comparison function for priority queue struct CompareNodes { bool operator()(const ListNode* lhs, const ListNode* rhs) const { return lhs-\u003eval \u003e rhs-\u003eval; } }; class Solution { public: ListNode* mergeKLists(vector\u003cListNode*\u003e\u0026 lists) { ListNode* head = new ListNode(0); ListNode* point = head; // Define priority queue with custom comparison function priority_queue\u003cListNode*, vector\u003cListNode*\u003e, CompareNodes\u003e q; // Push the heads of all lists into the priority queue for (ListNode* l : lists) { if (l) { q.push(l); } } // Merge the lists while (!q.empty()) { ListNode* node = q.top(); q.pop(); // Add the current smallest node to the merged list point-\u003enext = new ListNode(node-\u003eval); point = point-\u003enext; // Move the pointer of the current list forward node = node-\u003enext; // If there's remaining elements in the current list, push it to the // priority queue if (node) { q.push(node); } } return head-\u003enext; } };","title":"C++ Comparator","type":"notes"},{"content":"class Base { public: int x; protected: int y; private: int z; }; class PublicDerived: public Base { // x is public // y is protected // z is not accessible from PublicDerived }; class ProtectedDerived: protected Base { // x is protected // y is protected // z is not accessible from ProtectedDerived }; class PrivateDerived: private Base { // x is private // y is private // z is not accessible from PrivateDerived }; ","date":"20 March 2024","externalUrl":null,"permalink":"/notes/cpp_inheritance/","section":"Notes","summary":"class Base { public: int x; protected: int y; private: int z; }; class PublicDerived: public Base { // x is public // y is protected // z is not accessible from PublicDerived }; class ProtectedDerived: protected Base { // x is protected // y is protected // z is not accessible from ProtectedDerived }; class PrivateDerived: private Base { // x is private // y is private // z is not accessible from PrivateDerived };","title":"C++ Inheritance","type":"notes"},{"content":"#define pb push_back #define fi first #define se second #define mp make_pair using namespace std; typedef pair\u0026lt;int, int\u0026gt; PII; typedef long long LL; template \u0026lt;typename T\u0026gt; bool chkMax(T \u0026amp;x, T y) { return (y \u0026gt; x) ? x = y, 1 : 0; } template \u0026lt;typename T\u0026gt; bool chkMin(T \u0026amp;x, T y) { return (y \u0026lt; x) ? x = y, 1 : 0; } ","date":"16 March 2024","externalUrl":null,"permalink":"/notes/templates/","section":"Notes","summary":"#define pb push_back #define fi first #define se second #define mp make_pair using namespace std; typedef pair\u003cint, int\u003e PII; typedef long long LL; template \u003ctypename T\u003e bool chkMax(T \u0026x, T y) { return (y \u003e x) ? x = y, 1 : 0; } template \u003ctypename T\u003e bool chkMin(T \u0026x, T y) { return (y \u003c x) ? x = y, 1 : 0; }","title":"Templates","type":"notes"},{"content":" Templates Binary Search 二分法 Two Pointers 双指针 Sorting 排序算法 Quick Select Iteratively + Recursively Binary Tree Divide \u0026amp; Conquer 二叉树分治 BST Iterator 二叉搜索树非递归 BFS 宽度优先搜索 DFS 深度优先搜索 Dynamic Programming 动态规划 Heap 堆 Prioirty Queue: Union Find 并查集 Trie 字典树 Red-Black Tree Basics Rotations: O(1) Insertions(strategy) Data Structure Implementations LCS: Longest Commen Subsequence LCA: Lowest Common Ancestor Example: Lintcode 88 LCA TSP: MST: Minimon Spinning Tree LRU: Least Recently Used LIS: Longest Increasing Subsequence DP - LIS LIS 的动态规划四要素 Binary Search - LIS LIS2: Longest Continuous Increasing Subsequence 2 LDS: Largest Divisible Subset HashMap Implementation Other Notes Reverse Linked List 通过数据范围推测算法 背诵贪心算法 Python String methods C++ string methods 时间复杂度算法列表 跟面试官核实 BFS 的使用场景 BFS 的使用场景（summer） 以下哪些问题 BFS 可以处理： BFS 的三种实现方法 二叉树的 BFS vs 图的 BFS： Recursion/ DFS/ Backtracking: 遍历法 vs 分治法： 平衡二叉树 计算深度 Binary Search Tree 二叉查找树： BST 基本操作： Delete Node in a BST Red-Black Tree 红黑树： 二叉树三种遍历： “二叉树的中序遍历”的非递归实现 Prefix Sum Prefix Product, Suffix Product 使用前缀和数组在 O(1)的时间复杂度内计算子数组和 解决最短路径的算法： time \u0026amp; space compelxity of recursive: 遇到二叉树的问题，就想想整棵树在该问题上的结果和左右孩子在该问题上的结果之间有什么联系 拓扑排序 Topological Sorting: 拓扑排序的四种不同问法： Others Quick Select GCD - Greatest Common Divisor lower_bound vs upper_bound string find, mismatch string find mismatch assert try throw catch - error handling gtest with cmake step 1: step 2: CMakeLists.txt step 3: test fucntions step 4: Append to CMakeLists.txt step 5: build and run test print vector to the console priority queue Binary search on answer + priority_queue LRU LIS LIS 的动态规划四要素 LIS2 Largest Divisible Subset HashMap Implementation sort lambda customized hash for unordered_map or unordered_set function pointer in c++ element wise comparison of two structs how to use c++ build-in hash function c++ const random seed C++20 comparison operator To initialize two dimentional array heap: set vs priority_queue heap with multiset, erase with find return min or max element from hashmap all types of comparators for map and set Comparator for sort vs map(or set) sort map(or set) use function to get lambda or func pointer overload less comparator for priority queue set, find iterator, erase ASCII value isalnum(my_char) string trim and split string split with customized delimiter for string delimiter for char delimiter Log(log) IsPrime 区间 DP How to use heap in c++ 1507 Shortest Subarray with Sum at Least K 和至少为 K 的最短子数组 Binary search on answer + priority_queue Leetcode 1337.The K Weakest Rows in a Matrix multiset in C++ C++ isalnum, isalpha, isdigit 2. ML Linear regression Logistic regression Decision tree SVM algorithm Naive Bayes algorithm KNN algorithm K-means Random forest algorithm Dimensionality reduction algorithms Gradient boosting algorithm and AdaBoosting algorithm 3. Projects Pthread Prefix Sum GPU K-means Tree Comparison Two Phase Commit Protocol MPI Barnes-hut Templates # Binary Search 二分法 # 使用条件\n排序数组(30-40%) 当面试官要求找一个比O(n)更小的时间复杂度算法的时候(99%) 找到数组中的一个分割位置，使得左半部分满足某个条件，右边部分不满足(100%) 找到一个最大/最小的值使得某个条件被满足(90%) 复杂度\n时间复杂度O(logn) 空间复杂度O(1) 例题\nLintCode 14.二分查找(在排序的数据集上进行二分) C++ LintCode 460.在排序数组中找最接近的 K 个数(在排序的数据集上进行二分) Python LintCode 437.书籍复印(在答案集上进行二分) Python # Python def binary_search(self, nums, target): # corner case 处理 # 这里等价于nums is None or len(nums) == 0 if not nums: return -1 start, end = 0, len(nums) - 1 # 用start + 1 \u0026lt; end而不是start \u0026lt; end的目的是为了避免死循环 # 在first position of target的情况下不会出现死循环 # 但是在last position of target的情况下会出现死循环 # 样例：nums = [1, 1] target = 1 # 为了统一模版，我们就都采用start + 1 \u0026lt; end，就保证不会出现死循环 while start + 1 \u0026lt; end: # python 没有overflow的问题，直接 // 2 就可以 # C++ 和 Java 最好写成mid = start + (end - start) / 2 # 防止在start = 2^31 - 1, end = 2^31 - 1的情况下出现加法overflow mid = (start + end) // 2 # \u0026gt;, =, \u0026lt; 的逻辑先分开写，然后再看看=的情况是否能合并到其他分支里 if nums[mid] \u0026lt; target: start = mid elif nums[mid] == target: end = mid else: end = mid # 因为上面的循环退出条件是start + 1 \u0026lt; end # 因此这里循环结束的时候，start和end的关系是相邻关系 # 因此需要再单独判断start和end这两个位置的数哪个是我们要的答案 # 如果是找first position of target就先看start，否则就先看end if nums[start] == target: return start if nums[end] == target: return end return -1 Two Pointers 双指针 # 双指针的类型\n背向双指针 第一节课中的 Longest Palindromic Substring 的中心线枚举算法 二分法中学到的 Find K Closest Elements 相向双指针 O(n) Reverse 型（题目不多） Two Sum 型（两位数的相关变形） Partition 型（两位数的相关变形） 同向双指针 滑动窗口类 Sliding Window 快慢指针类 Fast \u0026amp; Slow Pointers 使用条件\n滑动窗口(90%) 时间复杂度要求 O(n)(80%) 要求原地操作，只可以使用交换，不能使用额外空间(80%) 有子数组subarray / 子字符串substring的关键词(50%) 有回文Palindrome关键词(50%) 复杂度\n时间复杂度O(n) 时间复杂度与最内层循环主体的执行次数有关 与有多少重循环无关 空间复杂度O(1) 只需要分配两个指针的额外内存 例题\nLintCode 1879.两数之和 VII(同向双指针) C++ LintCode 1712.和相同的二元子数组(相向双指针) LintCode 627.最长回文串(背向双指针) LintCode 64.合并有序数组 # Python # 相向双指针(partition in quicksort) def partition(self, A, start, end) { if start \u0026gt;= end: return left, right = start, end # key point 1: pivot is the value, not the index pivot = A[(start + end) // 2] # key point 2: every time compare left with right, it should be # left \u0026lt;= right not left \u0026lt; right while left \u0026lt;= right: while left \u0026lt;= right and A[left] \u0026lt; pivot: left += 1 while left \u0026lt;= right and A[right] \u0026gt; pivot: right -= 1 if left \u0026lt;= right: A[left], A[right] = A[right], A[left] left += 1 right -= 1 } # 背向双指针 left = position right = position + 1 while left \u0026gt;= 0 and right \u0026lt; len(s): if left 和 right 可以停下来了: break left -= 1 right += 1 # 同向双指针 end = 0 for start in range(len): # 不满足则循环到满足搭配为止 while end \u0026lt; len and (start 到 end 之间不满足条件): end += 1 if start 到 end 之间满足条件: 处理 start 到 end 这段区间(处理start, end这次搭配) # 合并双指针 def merge(list1, list2): new_list = [] i, j = 0, 0 # 合并的过程只能操作i, j的移动，不要去用list1.pop(0)之类的操作 # 因为pop(0)是O(n)的时间复杂度 while i \u0026lt; len(list1) and j \u0026lt; len(list2): if list1[i] \u0026lt; list2[j]: new_list.append(list1[i]) i += 1 else: new_list.append(list2[j]) j += 1 # 合并剩下的数到new_list里 # 不要用new_list.extend(list[i:])之类的方法 # 因为list1[i:]会产生额外空间消耗 while i \u0026lt; len(list1): new_list.append(list1[i]) i += 1 while j \u0026lt; len(list2): new_list.append(list2[j]) j += 1 return new_list Sorting 排序算法 # 复杂度\n时间复杂度 快速排序(期望复杂度)：O(nlogn) 归并排序(最坏复杂度)：O(nlogn) 空间复杂度 快速排序：O(1) 归并排序：O(n) 例题\nLintCode 463.整数排序 LintCode 464.整数排序 II # Python # quick sort class Solution: def sortIntegers(self, A): self.quickSort(A, 0, len(A) - 1) def quickSort(self, A, start, end): if start \u0026gt;= end: return left, right = start, end # key point 1: pivot is the value, not the index pivot = A[(start + end) // 2] # key point 2: every time you compare left with right, it should be # left \u0026lt;= right not left \u0026lt; right while left \u0026lt;= right: while left \u0026lt;= right and A[left] \u0026lt; pivot: left += 1 while left \u0026lt;= right and A[right] \u0026gt; pivot: right -= 1 if left \u0026lt;= right: A[left], A[right] = A[right], A[left] left += 1 right -= 1 self.quickSort(A, start, right) self.quickSort(A, left, end) # Python # merge sort class Solution: def sortIntegers(self, A): if not A: return A tmp = [0] * len(A) self.merge_sort(A, 0, len(A) - 1, temp) def merge_sort(self, A, start, end, temp): if start \u0026gt;= end: return # 处理左半区间 self.merge_sort(A, start, (start + end) // 2, temp) # 处理右半区间 self.merge_sort(A, (start + end) // 2 + 1, end, temp) # 合并排序数组 self.merge(A, start, end, temp) def merge(self, A, start, end, temp): middle = (start + end) // 2 left_index = start right_index = middle + 1 index = start while left_index \u0026lt;= middle and right_index \u0026lt;= end: if A[left_index] \u0026lt; A[right_index]: temp[index] = A[left_index] index += 1 left_index += 1 else: temp[index] = A[right_index] index += 1 right_index += 1 while left_index \u0026lt;= middle: temp[index] = A[left_index] index += 1 left_index += 1 while right_index \u0026lt;= end: temp[index] = A[right_index] index += 1 right_index += 1 for i in range(start, end + 1): A[i] = temp[i] Simple sort: O(n^2): insert sort\nBetter sort: O(nlogn): merge sort, quick sort, heap sort\nLower Bound for comparison-based sort is Omega(nlogn) \u0026mdash;\u0026gt; based on comparison\nRadix sort: O(n) \u0026mdash;\u0026gt; based on indexing\nQuick Select # Youtube Explanation Iteratively + Recursively # def partition(arr, l, r): pivot = arr[r] i = l for j in range(l, r): if arr[j] \u0026lt;= pivot: arr[i], arr[j] = arr[j], arr[i] i += 1 arr[i], arr[r] = arr[r], arr[i] return i def optimized_partition(arr, l, r): pivot = arr[l] i = l + 1 j = r while i \u0026lt;= j: if arr[i] \u0026lt; pivot and arr[j] \u0026gt; pivot: arr[i], arr[j] = arr[j], arr[i] i += 1 j -= 1 if arr[i] \u0026gt;= pivot: i += 1 if arr[j] \u0026lt;= pivot: j -= 1 arr[l], arr[j] = arr[j], arr[l] return j def quick_select_recursive(arr, l, r, k): # pivot_idx = partition(arr, l, r) pivot_idx = optimized_partition(arr, l, r) if (pivot_idx == k - 1): return arr[pivot_idx] elif (pivot_idx \u0026gt; k - 1): return quick_select(arr, l, pivot_idx - 1, k) else: return quick_select(arr, pivot_idx + 1, r, k) def quick_select_iterative(arr, l, r, k): while True: # pivot_idx = partition(arr, l, r) pivot_idx = optimized_partition(arr, l, r) if (pivot_idx == k - 1): return arr[pivot_idx] elif (pivot_idx \u0026gt; k - 1): r = pivot_idx - 1 else: l = pivot_idx + 1 Binary Tree Divide \u0026amp; Conquer 二叉树分治 # 使用条件\n二叉树相关的问题(99%) 可以一分为二去分别处理之后再合并结果(100%) 数组相关的问题(10%) 复杂度\n时间复杂度O(n) 空间复杂度O(n)(含递归调用的栈空间最大耗费) 例题\nLintCode 1534.将二叉搜索树转换为已排序的双向链接列表 LintCode 94.二叉树中的最大路径和 LintCode 95.验证二叉查找树 # Python def divide_conquer(root): # 递归出口 # 一般处理 node == null 就够了 # 大部分情况下不需要处理 node == leaf if root is None: return ... # 处理左子树 left_result = divide_conquer(node.left) # 处理右子树 right_result = divide_conquer(node.right) # 合并答案 result = merge left_result and right_result to get merge result return result BST Iterator 二叉搜索树非递归 # 使用条件\n用非递归的方式(Non-recursion / Iteration)实现二叉树的中序遍历 常用于BST但不仅仅可以用于BST 复杂度\n时间复杂度O(n) 空间复杂度O(n) 例题\nLeetcode 94 Binary Tree Inorder Traversal Leetcode 230 Kth smallest element in BSt class BSTIterator { public: BSTIterator(TreeNode* root) { curr_ = root; } int next() { while (curr_ != nullptr) { stack_.push_back(curr_); curr_ = curr_-\u0026gt;left; } curr_ = stack_.back(); stack_.pop_back(); int val = curr_-\u0026gt;val; curr_ = curr_-\u0026gt;right; // !!!! very important here return val; } bool hasNext() { if (stack_.empty() \u0026amp;\u0026amp; curr_ == nullptr) return false; // has to check both return true; } private: TreeNode* curr_; deque\u0026lt;TreeNode*\u0026gt; stack_; }; # Python def inorder_traversal(root): if root is None: return [] # 创建一个dummy node，右指针指向root # 并放到stack里，此时stack的栈顶dymmy是iterator的当前位置 dummy = TreeNode(0) dummy.right = root stack = [dummy] inorder = [] # 每次将iterator挪到下一个点 # 也就是调整stack使得栈顶到下一个点 while stack: node = stack.pop() if node.right: node = node.right while node: stack.append(node) node = node.left if stack: inorder.append(stack[-1]) return inorder BFS 宽度优先搜索 # 使用条件\n分层遍历(100%) 一层一层的遍历一个图、树、矩阵 简单图最短路径(100%) 简单图的定义是，图中所有的边长都一样 出现连通块的关键词(100%) 通过图中一个点找到其他所有连通的点 找到所有方案问题的一种非递归实现方式 拓扑排序(100%) 实现容易度远超DFS 给定一个变换规则，从初始状态变到终止状态最少几步(100%) 复杂度\n时间复杂度O(n + m) n是点数，m是边数 空间复杂度O(n) 例题\nLintCode 974.01 矩阵(分层遍历) LintCode 431.找无向图的连通块 LintCode 127.拓扑排序 # Python def bfs(start_node): # BFS必须要用队列queue，别用栈stack！ # distance(dict) 有两个作用，一个是记录一个点是否被丢进过队列了，避免重复访问 # 另外一个是记录start_node到其他所有节点的最短距离 # 如果只求连通性的话，可以换成set就行 # node做key的时候比较的是内存地址 queue = collections.deque([start_node]) distance = {start_node: 0} # while 队列不空，不停地从队列里拿出一个点，拓展邻居节点放到队列中 while queue: node = queue.popleft() # 如果有明确的终点可以在这里加终点的判断 if node 是终点: break or return something for neighbor in node.get_neighbors(): if neighbor in distance: continue queue.append(neighbor) distance[neighbor] = distance[node] + 1 # 如果需要返回所有点离起点的距离，就return hashmap return distance # 如果需要返回所有连通的节点，就return HashMap里的所有点 return distance.keys() # 如果需要返回离终点的最短距离 return distance[end_node] # Python # topological sort def get_indegrees(nodes): counter = {node: 0 for node in nodes} for node in nodes: for neighbor in node.get_neighbors(): counter[neighbor] += 1 return counter def topological_sort(nodes): # 统计入度 indegrees = get_indegrees(nodes) # 所有入度为 0 的点都放到队列里 queue = collections.deque([node for node in nodes if indegrees[node] == 0]) # 用BFS算法一个个把点从图里挖出来 topo_order = [] while queue: node = queue.popleft() topo_order.append(node) for neighbor in node.get_neighbors(): indegrees[neighbor] -= 1 if indegrees[neighbor] == 0: queue.append(neighbor) # 判断是否有循环依赖 if len(topo_order) != len(nodes): return 有循环依赖(环)，没有拓扑排序 return topo_order DFS 深度优先搜索 # 使用条件\n找满足某个条件的所有方案(99%) 二叉树 Binary Tree 的问题(90%) 组合问题(95%) 问题模型：求出所有满足条件的“组合” 判断条件：组合中的元素是顺序“无关”的 排列问题(95%) 问题模型：求出所有满足条件的“排列” 判断条件：组合中的元素是顺序“相关”的 不要使用 DFS 的场景\n连通块问题(一定要用 BFS，否则 StackOverflow) 拓扑排序(一定要用 BFS，否则 StackOverflow) 一切 BFS 可以解决的问题 复杂度\n时间复杂度O(方案个数 * 构造每个方案的时间) 树的遍历：O(n) 排列问题：O(n! * n) 组合问题：O(2^n * n) BFS vs DFS 复杂度\n时间复杂度均为:O(V+E)，V 为顶点个数，E 为边个数 宽度优先搜索的空间复杂度取决于宽度 深度优先搜索的空间复杂度取决于深度 例题\nLintCode 67.二叉树的中序遍历(遍历树) LintCode 652.因式分解(枚举所有情况) # Python def dfs(参数列表): if 递归出口： 记录答案 return for 所有的拆解可能性: 修改所有的参数 dfs(参数列表) 还原所有被修改过的参数 return something 如果需要的话，很多时候不需要return值，除了分治的写法 Dynamic Programming 动态规划 # 使用场景\n求方案总数(90%) Note: 求具体方案的话，DFS 更合适 求最值(80%) 求可行性(80%) 不适用场景\n找所有具体的方案(准确率 99%) 输入数据无序(除了背包问题外，准确率 60-70%) 暴力算法已经是多项式时间复杂度(准确率 80%) 动态规划四要素（对比递归的四要素）\n状态(State)\u0026ndash;递归的定义 方程(Function)\u0026ndash;递归的拆解 初始化(Initialization)\u0026ndash;递归的出口 答案(Answer)\u0026ndash;递归的调用 动态规划的两种实现方式\n记忆化搜索（使用递归实现） 多重循环（使用 for 循环实现） 常见的动态规划\n背包型\n给出n个物品及其大小，问是否能挑选出一些物品装满大小为m的背包\n通常是二维的状态数组，前i个组成和为j状态数组的大小需要开(n + 1) * (m + 1)\n两个关键点：前 \u0026amp; 和 题目中通常有“和”与“差”的概念，数值会被放到状态中\n每个物品要么挑0个（不挑），要么挑1个， 所以叫 01\n如果一个物品可以被分割，就不是01背包 如果一个物品可以选多份，就叫多重背包 01 背包 状态 state dp[i][j] 表示前 i 个数里挑若干个数是否能组成和为 j 方程 function dp[i][j] = dp[i - 1][j] or dp[i - 1][j - A[i - 1]] 如果 j \u0026gt;= A[i - 1] dp[i][j] = dp[i - 1][j] 如果 j \u0026lt; A[i - 1] 第 i 个数的下标是 i - 1，所以用的是 A[i - 1] 而不是 A[i] 初始化 initialization dp[0][0] = true dp[0][1...m] = false 答案 answer 使得 dp[n][v], 0 s \u0026lt;= v \u0026lt;= m 为 true 的最大 v dp[i][j] 表示前 i 个物体，在容量 j 的情况下，能取到的最大价值 如果**取**第 i 个物体，价值为 dp[i - 1][j - A[i - 1]] + V[i] **(j - A[i - 1] \u0026gt;= 0)** 如果**不取**第 i 个物体，价值为 dp[i - 1][j] 状态转移：dp[i][j] = max(dp[i - 1][j - A[i]] + V[i], dp[i - 1][j]) 1.1 Brute Force Searching\nclass Solution { public: int backPackII(int m, std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;int\u0026gt;\u0026amp; V) { int result = 0; dfs(A, V, 0, 0, 0, m, result); return result; } void dfs(std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;int\u0026gt;\u0026amp; V, int current, int current_sum_weight, int current_sum_value, int m, int\u0026amp; result) { int a_size = A.size(); if (current \u0026gt; a_size || current_sum_weight \u0026gt; m) { return; } else { result = std::max(current_sum_value, result); } for (int i = current; i \u0026lt; a_size; ++i) { dfs(A, V, i + 1, current_sum_weight + A[i], current_sum_value + V[i], m, result); } } }; To avoid error, in the main function, we has to take 0 as input for both current_sum_weight and current_sum_value.\n1.2 DP: Backpack: version 1\nclass Solution { public: int backPackII(int m, std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;int\u0026gt;\u0026amp; V) { int n = A.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); // for (int i = 0; i \u0026lt; n + 1; ++i) { // if (i == 0) { // for (auto\u0026amp; elem : dp[0]) { // elem = 0; // } // } else { // dp[i][0] = 0; // } // } for (int i = 1; i \u0026lt; n + 1; ++i) { for (int j = 1; j \u0026lt; m + 1; ++j) { if (j - A[i - 1] \u0026gt;= 0) { dp[i][j] = std::max(dp[i - 1][j], V[i - 1] + dp[i - 1][j - A[i - 1]]); } else { dp[i][j] = dp[i - 1][j]; } } } return dp[n][m]; } }; 1.3 DP: Backpack: version 2\nclass Solution { public: int backPackII(int m, std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;int\u0026gt;\u0026amp; V) { int n = A.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1)); for (int i = 0; i \u0026lt;= n; ++i) { for (int j = 0; j \u0026lt;= m; ++j) { if (i == 0 || j == 0) { dp[i][j] = 0; } else if (j - A[i - 1] \u0026gt;= 0) { dp[i][j] = std::max(dp[i - 1][j], V[i - 1] + dp[i - 1][j - A[i - 1]]); } else { dp[i][j] = dp[i - 1][j]; } } } return dp[n][m]; } }; 多重背包 状态 state dp[i][j] 表示前 i 个物品挑出一些放到 j 的背包里的最大价值和 方程 function dp[i][j] = max(dp[i - 1][j - count * A[i - 1]] + count * V[i - 1]) 其中 0 \u0026lt;= count \u0026lt;= j / A[i - 1] 初始化 initialization dp[0][0..m] = 0 答案 answer dp[n][m] Improvement with binary trick 把每种物品转化成一定的物品来进行优化 m = 8 A = [2, 3, 4, 5] V = [30, 50, 100, 200] 第 0 个物品：A[0] = 2, V[0] = 30, 最多取 4 个 100-\u0026gt;拆分为 1, 10, 100 个 第 1 个物品：最多取 2 个 10-\u0026gt;拆分为 1, 10 个 第 i 个物品：最多取 x 个 (2^n \u0026lt;= x) 拆分为 1, 2, 4, 8 .. 2^n Summary 0-1: `dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - A[i] + V[i]])` Optimization: rolling array or one dimensional array complete: 枚举每件物品取0, 1, 2, 3 ... m / A[i] 件 `dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - k * A[i]] + k * V[i])` 转化成 0-1 背包 Improvement with binary trick: 最优化完全背包的做法：正序更新(相较于0-1的倒序更新) multiple: 枚举每件物品取0, 1, 2, 3 ... amounts[i] 件 `dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - k * A[i]] + k * V[i])` 每个物品都当作是一个物品，然后进行 0-1 背包来做 区间型\n题目中有subarray/substring的信息 大区间依赖小区间 状态 state 用dp[i][j]表示数组/字符串中 i, j 这一段区间的最优值/可行性/方案总数 方程 function dp[i][j] = max/min/sum/or(dp[i,j 之内更小的若干区间]) 匹配型\n通常给出两个字符串 两个字符串的匹配值依赖于两个字符串前缀的匹配值 字符串长度为n, m则需要开(n + 1) * (m + 1)的状态数组 要初始化dp[i][0]与dp[0][i] 通常都可以用滚动数组进行空间优化 状态 state dp[i][j] 表示第一个字符串的前 i 个字符与第二个字符串的前 j 个字符怎么样怎么样 (max/min/sum/or) 划分型\n是前缀型动态规划的一种，有前缀的思想 状态 state - 如果指定了要划分为几个部分： - dp[i][j] 表示前 i 个数/字符划分为 j 个部分的最优值/方案数/可行性 - 如果没有指定划分为几个部分： - dp[i] 表示前 i 个数/字符划分为若干个部分的最优值/方案数/可行性 接龙型\n通常会给一个接龙规则，问你最长的龙有多长 状态表示通常为：dp[i]表示以坐标为i的元素结尾的最长龙的长度 方程通常是：dp[i] = max{dp[j] + 1}, j的后面可以接上i LIS 的二分做法选择性的掌握，但并不是所有的接龙型 DP 都可以用二分来优化 状态 state 状态表示通常为: dp[i] 表示以坐标为 i 的元素结尾的最长龙的长度 方程 function dp[i] = max{dp[i], dp[j] + 1}, j 的后面可以接上 i 复杂度\n时间复杂度 O(状态总数 * 每个状态的处理耗费) 等于O(状态总数 * 决策数) 空间复杂度 O(状态总数)(不使用滚动数组优化) O(状态总数 / n)(使用滚动数组优化，n 是被滚动掉的那一个维度) 例题\nLintCode 563.背包问题 V(背包型) LintCode 476.石子归并 V(区间型) LintCode 192.通配符匹配(匹配型) LintCode 107.单词拆分(划分型) LintCode 76.最长上升子序列(接龙型) Heap 堆 # 使用条件\n找最大值或最小值(60%) 找第k大(pop k次复杂度O(nlogk))(50%) 要求logn时间对数据进行操作(40%) 不能解决的问题\n查找比某个数大的最小值/最接近的值(平衡排序二叉树Balanced BST才可以解决) 找某段区间的最大值最小值(线段树SegmentTree可以解决) O(n)找第k大(使用QuickSort中的partition操作) 例题\nLintCode 1274.查找和最小的 K 对数字 LintCode 919.会议室 II LintCode 1512.雇佣 K 个人的最低费用 # Python # 带删除特定元素功能的堆 from heapq import heappush, heappop class Heap: def __int__(self): self.minheap = [] self.deleted_set = set() def push(self, index, val): heappush(self.minheap, (val, index)) def _lazy_deletion(self): while self.minheap and self.minheap[0][1] in self.deleted_set: heappop(self.minheap) def top(self): self._lazy_deletion() return self.minheap[0] def pop(self): self._lazy_deletion() heappop(self.minheap) def delete(self, index): self.deleted_set.add(index) def is_empty(self): return not bool(self.minheap) Prioirty Queue: # unsorted array sorted array Balanced BST(AVL) Binary Heap(We can create a Binary Heap with O(n)) can be used for implementing Priority Queue, HeapSort(not stable), MinHeap, MaxHeap Union Find 并查集 # 使用条件\n需要查询图的连通状况的问题 需要支持快速合并两个集合的问题 复杂度\n时间复杂度union O(1), find O(1) 空间复杂度O(n) 例题\nLintCode 1070.账号合并 LintCode 1014.打砖块 LintCode 1813.构造二叉树?? # Python class UnionFind: def __init__(self): # 初始化父指针，集合大小，集合数量 self.father = {} self.size_of_set = {} self.num_of_set = 0 def add(self, x): # 点如果已经出现，操作无效 if x in self.father: return # 初始化点的父亲为 空对象 None # 初始化该点所在集合大小为 1 # 集合数量增加 1 self.father[x] = None self.num_of_set += 1 self.size_of_set[x] = 1 def merge(self, x, y): # 找到两个节点的根 root_x, root_y = self.find(x), self.find(y) # 如果根不是同一个则连接 if root_x != root_y: # 将一个点的根变成新的根 # 集合数量减少 1 # 计算新的根所在集合大小 self.father[root_x] = root_y self.num_of_set -= 1 self.size_of_set[root_y] += self.size_of_set[root_x] def find(self, x): # 指针 root 指向被查找的点x # 不断找到 root 的父亲 # 直到 root 指向 x 的根节点 root = x while self.father[root] != None: root = self.father[root] # 将路径上所有点指向根节点 root while x != root: # 暂存 x 原本的父亲 # 将 x 指向根节点 # x 指针上移至 x 的父节点 original_father = self.father[x] self.father[x] = root x = original_father return root # 两个节点连通 等价于 两个节点的根相同 def is_connected(self, x, y): return self.find(x) == self.find(y) # 获取集合数量 def get_num_of_set(self): return self.num_of_set # 获取某个点所在集合大小 def get_size_of_set(self, x): return self.size_of_set[self.find(x)] Trie 字典树 # 使用条件\n需要查询包含某个前阵的单词/字符串是否存在 字符矩阵中找单词的问题 复杂度\n时间复杂度O(L) 增删查改 空间复杂度O(N * L) N 是单词数，L 是单词长度 例题\nLintCode 1221.连接词 LintCode 1624.最大距离 LintCode 1090.映射配对之和 // Let me try to re-implement java solution with C++ class TrieNode { public: TrieNode() : children(), is_word(), word() {} // 儿子节点 std::unordered_map\u0026lt;char, TrieNode*\u0026gt; children; // 根节点到该节点是否是一个单词 bool is_word; // 根节点到该节点的单词是什么 std::string word; }; class Trie { public: Trie() : root_(new TrieNode()) {} TrieNode* GetRoot() { return root_; } // 插入单词 void Insert(std::string word) { TrieNode* node = root_; for (int i = 0; i \u0026lt; word.size(); ++i) { char c = word[i]; // leetcode 1268 // if (!node-\u0026gt;children.count(c)) { // if (node-\u0026gt;children.find(c) == node-\u0026gt;children.end()) { if (node-\u0026gt;children[c] == nullptr) { node-\u0026gt;children[c] = new TrieNode(); } node = node-\u0026gt;children[c]; } node-\u0026gt;is_word = true; node-\u0026gt;word = word; } // 判断单词 word 是不是在字典树中 bool HasWord(std::string word) { TrieNode* node = root_; for (int i = 0; i \u0026lt; word.size(); ++i) { char c = word[i]; // leetcode 1268 // if (!node-\u0026gt;children.count(c)) { // if (node-\u0026gt;children.find(c) == node-\u0026gt;children.end()) { if (node-\u0026gt;children[c] == nullptr) { return false; } node = node-\u0026gt;children[c]; } return node-\u0026gt;is_word; } // 判断前缀 prefix 是不是在字典树中 bool HasPrefix(std::string prefix) { TrieNode* node = root_; for (int i = 0; i \u0026lt; prefix.size(); ++i) { char c = prefix[i]; // leetcode 1268 // if (!node-\u0026gt;children.count(c)) { // if (node-\u0026gt;children.find(c) == node-\u0026gt;children.end()) { if (node-\u0026gt;children[c] == nullptr) { return false; } node = node-\u0026gt;children[c]; } return true; // this is the only difference with the HasWord function } private: TrieNode* root_; }; // Java class TrieNode { // 儿子节点 public Map\u0026lt;Character, TrieNode\u0026gt; children; // 根节点到该节点是否是一个单词 public boolean isWord; // 根节点到该节点的单词是什么 public String word; public TrieNode() { children = new HashMap\u0026lt;Character, TrieNode\u0026gt;(); isWord = false; word = null; } } public class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } public TrieNode getRoot() { return root; } // 插入单词 public void insert(String word) { TrieNode node = root; for (int i = 0; i \u0026lt; word.length(); i++) { char letter = word.charAt(i); if (!node.children.containsKey(letter)) { node.children.put(letter, new TrieNode()); } node = node.children.get(letter); } node.isWord = true; node.word = word; } // 判断单词 word 是不是在字典树中 public boolean hasWord(String word) { int L = word.length(); TrieNode node = root; for (int i = 0; i \u0026lt; L; i++) { char letter = word.charAt(i); if (!node.children.containsKey(letter)) { return false; } node = node.children.get(letter); } return node.isWord; } // 判断前缀 prefix 是不是在字典树中 public boolean hasPrefix(String prefix) { int L = prefix.length(); TrieNode node = root; for (int i = 0; i \u0026lt; L; i++) { char letter = prefix.charAt(i); if (!node.children.containsKey(letter)) { return false; } node = node.children.get(letter); } return true; } } Red-Black Tree # Basics # Balanced search trees, guaranteed height of O(logn) for n items. A Red-Black Tree is a BST with the following structure properties:\nEvery node is colored red or black The root is black A red node does not have a red child. red rule: red nodes give us flexibility, otherwise, if all black node the tree must be a perfect tree For any node, every path from that node to a null reference has the same # of black nodes path rule: define the balance Each node has its own black-height. Extra notes:\nNodes require one storage bit to keep track of color. The longest path(root to farthest NIL) is no more than twice the length of the shortest path (root to nearest NIL). Shortest path: all black nodes Longest path: alternating red and black Operations:\nSearch(O(logn)) Insert(O(logn)): require rotation Remove(O(logn)): require rotation Space complexity: O(n)\nRotations: O(1) # An important operation when inserting and deleting items from a red-black tree.\nalters the structure of a tree by rearranging subtrees goal is to decrease the height of the tree red-black trees: maximum height of O(logn) larger subtrees up, smaller subtrees down does not affect the order of elements left-rotate right-rotate def left-rotate(T, x): y = x.right # set y x.right = y.left # turn y\u0026#39;s left subtree into x\u0026#39;s right subtree if y.left != T.nil y.left.p = x y.p = x.p # link x\u0026#39;s parent to y if x.p == T.nil T.root = y elif x == x.p.left x.p.left = y else x.p.right = y y.left = x # put x on y\u0026#39;s left x.p = y Insertions(strategy) # insert Z and color it red recolor and rotate nodes to fix violation Four scenarios:\nZ == root solution: color black Z.uncle == red solution: recolor (parent, grandparent, uncle) Z.uncle == black(triangle) solution: rotate Z.parent Z.uncle == black(line) solution: rotate Z.grandparent \u0026amp; recolor(parent, grandparent) Data Structure Implementations # LCS: Longest Commen Subsequence # 两个字符串前缀型中的匹配型动态规划 LCA: Lowest Common Ancestor # 一般会问一次查询，多次查询不太会问 Example: Lintcode 88 LCA # Leetcode 236. LCA class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* A, TreeNode* B) { if (root == nullptr) return nullptr; // !!! 如果root为A或B，立即返回，无需继续向下寻找 if (root == A || root == B) return root; // 分别去左右子树寻找A和B TreeNode* left = lowestCommonAncestor(root-\u0026gt;left, A, B); TreeNode* right = lowestCommonAncestor(root-\u0026gt;right, A, B); // !!! 如果A，B分别存在于两棵子树，root为LCA，返回root(return answer) if (left != nullptr \u0026amp;\u0026amp; right != nullptr) return root; // 左子树有一个点或者左子树有LCA if (left != nullptr) return left; // 右子树有一个点或者右子树有LCA if (right != nullptr) return right; // 左右子树啥都没有 return nullptr; } }; TSP: # 随机化 二进制压缩，状态压缩型动态规划，最难 MST: Minimon Spinning Tree # 最小生成树 Microsoft \u0026amp; Amazon LRU: Least Recently Used # Leetcode 146. LRU Cache // C++ #include \u0026lt;unordered_map\u0026gt; struct LinkedNode { LinkedNode(int key, int value, LinkedNode* next) : key(key), value(value), next(next) {} int key; int value; LinkedNode* next; }; class LRUCache { public: LRUCache(int capacity) : capacity_(capacity), dummy_(new LinkedNode(0, 0, nullptr)), tail_(dummy_) {} // Google style: Get int Get(int key) { if (key_to_previous_.find(key) == key_to_previous_.end()) { return -1; } LinkedNode* previous = key_to_previous_.at(key); LinkedNode* current = previous-\u0026gt;next; Kick(previous); return current-\u0026gt;value; } // Google style: Set void Set(int key, int value) { if (key_to_previous_.find(key) != key_to_previous_.end()) { Kick(key_to_previous_.at(key)); tail_-\u0026gt;value = value; return; } PushBack(new LinkedNode(key, value, nullptr)); // 如果key不存在，则存入新节点 if (key_to_previous_.size() \u0026gt; capacity_) { // 如果缓存超出上限 PopFront(); } } private: void PushBack(LinkedNode* node) { key_to_previous_[node-\u0026gt;key] = tail_; tail_-\u0026gt;next = node; tail_ = node; } void PopFront() { // 删除头部 LinkedNode* head = dummy_-\u0026gt;next; key_to_previous_.erase(head-\u0026gt;key); dummy_-\u0026gt;next = head-\u0026gt;next; key_to_previous_[head-\u0026gt;next-\u0026gt;key] = dummy_; } // change \u0026#34;previous-\u0026gt;node-\u0026gt;next-\u0026gt;...-\u0026gt;tail_\u0026#34; // to \u0026#34;previous-\u0026gt;next-\u0026gt;...-\u0026gt;tail_-\u0026gt;node\u0026#34; void Kick(LinkedNode* previous) { // 将数据移至尾部 LinkedNode* node = previous-\u0026gt;next; if (node == tail_) { return; } // update the current node from linked list previous-\u0026gt;next = node-\u0026gt;next; // update the previous node in hash map key_to_previous_[node-\u0026gt;next-\u0026gt;key] = previous; node-\u0026gt;next = nullptr; PushBack(node); } int capacity_; LinkedNode* dummy_; LinkedNode* tail_; std::unordered_map\u0026lt;int, LinkedNode*\u0026gt; key_to_previous_; }; LIS: Longest Increasing Subsequence # Dynamic Programming O(nlogn) recite binary search DP - LIS # Leetcode 300. LIS - Longest Increasing Subsequence\n接龙规则：从左到右一个比一个大，该问题简称 LIS\n状态表示：\nA：dp[i] 表示前i个数的 LIS 是多长(前缀型, do not choose this) B：dp[i] 表示以第i个数结尾的 LIS 是多长(坐标型) LIS 的动态规划四要素 # state: dp[i]表示以第i个数为龙尾的最长的龙有多长 function: dp[i] = max{dp[i], dp[j] + 1}, j \u0026lt; i \u0026amp;\u0026amp; nums[j] \u0026lt; nums[i] initialization: dp[0..n-1] = 1 answer: max{dp[0..n-1]} def longestIncreasingSubsequence(self, nums): if nums is None or not nums: return 0 # state: dp[i] 表示以第i个数结尾的LIS的长度 # initialization：dp[0..n-1] = 1 dp = [1] * len(nums) # function: dp[i] = max(dp[i] + 1), j \u0026lt; i \u0026amp;\u0026amp; nums[j] \u0026lt; nums[i] for i in range(len(nums)): for j in range(i): if nums[j] \u0026lt; nums[i]: dp[i] = max(dp[i], dp[j] + 1) # answer, 任意一个位置都可能是LIS的结尾 return max(dp) 改动要点(返回最优方案) prev 数组记录前继最优状态 max() 的写法要改为 if 的写法 找到最长龙的结尾，从结尾倒推出整条龙 def longestIncreasingSubsequence(self, nums): if nums is None or not nums: return 0 # state: dp[i] 表示以第i个数结尾的LIS的长度 # initialization：dp[0..n-1] = 1 dp = [1] * len(nums) # prev[i]代表dp[i]的最优值是从哪个dp[j]算过来的 prev = [-1] * len(nums) # function dp[i] = max{dp[j] + 1}, j \u0026lt; i and nums[j] \u0026lt; nums[i] for i in range(len(nums)): for j in range(i): if nums[j] \u0026lt; nums[i] and dp[i] \u0026lt; dp[j] + 1: dp[i] = dp[j] + 1 prev[i] = j # answer: max(dp[0..n-1]) longest, last = 0, -1 for i in range(len(nums)): if dp[i] \u0026gt; longest: longest = dp[i] last = i path = [] while last != -1 path.append(nums[last]) last = prev[last] print(path[::-1]) return longest Binary Search - LIS # Leetcode 300. Longest Increasing Subsequence class Solution { public: int lengthOfLIS(vector\u0026lt;int\u0026gt;\u0026amp; nums) { vector\u0026lt;int\u0026gt; tails(nums.size() + 1, 0x3f3f3f3f); for (int\u0026amp; n : nums) { int idx = BinarySearch(tails, n); tails[idx] = n; } int result = 0; for (int\u0026amp; n : tails) { if (n != 0x3f3f3f3f) { ++result; } } return result; } private: int BinarySearch(vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { int start = 0; int end = nums.size() - 1; while (start + 1 \u0026lt; end) { int mid = start + (end - start) / 2; if (nums[mid] == target) return mid; else if (nums[mid] \u0026lt; target) start = mid; else end = mid; } return end; } }; LIS2: Longest Continuous Increasing Subsequence 2 # Leetcode 674. LIS2 - Longest Continuous Increasing Subsequence class Solution: \u0026#34;\u0026#34;\u0026#34; @param A: An integer matrix @return: an integer \u0026#34;\u0026#34;\u0026#34; def longestContinuousIncreasingSubsequence2(self, A): if not A or not A[0]: return 0 n, m = len(A), len(A[0]) points = [] for i in range(n): for j in range(m): points.append((A[i][j], i, j)) points.sort() longest_hash = {} for i in range(len(points)): key = (points[i][1], points[i][2]) longest_hash[key] = 1 for dx, dy in [(1, 0), (0, -1), (-1, 0), (0, 1)]: x, y = points[i][1] + dx, points[i][2] + dy if x \u0026lt; 0 or x \u0026gt;= n or y \u0026lt; 0 or y \u0026gt;= m: continue if (x, y) in longest_hash and A[x][y] \u0026lt; points[i][0]: longest_hash[key] = max(longest_hash[key], longest_hash[(x, y)] + 1) return max(longest_hash.values()) LDS: Largest Divisible Subset # Leetcode 368. Largest Divisible Subset class Solution: def largestDivisibleSubset(self, nums): if not nums: return [] nums = sorted(nums) n = len(nums) dp, prev = {}, {} for num in nums: dp[num] = 1 prev[num] = -1 last_num = nums[0] for num in nums: for factor in self.get_smaller_factors(num): if factor not in dp: continue if dp[num] \u0026lt; dp[factor] + 1: dp[num] = dp[factor] + 1 prev[num] = factor if dp[num] \u0026gt; dp[last_num]: last_num = num return self.get_path(prev, last_num) def get_smaller_factors(self, num): if num == 1: return [] factor = 1 factors = [] while factor * factor \u0026lt;= num: if num % factor == 0: factors.append(factor) if factor * factor != num and factor != 1: factors.append(num // factor) factor += 1 return factors def get_path(self, prev, last_num): path = [] while last_num != -1: path.append(last_num) last_num = prev[last_num] return path[::-1] HashMap Implementation # leetcode 705 design hashset // C++ Other Notes # Reverse Linked List # // prev -\u0026gt; ... -\u0026gt; curr -\u0026gt; next -\u0026gt; ... for (...) { ListNode next = curr-\u0026gt;next; curr-\u0026gt;next = next-\u0026gt;next; next-\u0026gt;next = prev-\u0026gt;next; prev-\u0026gt;next = next; } 通过数据范围推测算法 # n = 10^4 ～ 10^5 O(n) ==\u0026gt; 双指针？前缀和？遍历？DP？ O(nlogn) ==\u0026gt; 排序？二分？ n = 10^3 O(n^2) ==\u0026gt; 二维数组？双重循环？二维 DP？ n = 10^2 O(n^3) ==\u0026gt; 三重循环？ n = 10 O(2^n), O(n!) ==\u0026gt; dfs 暴力？ n = 10^9 别打算开数组存或 O(n)复杂度 背诵贪心算法 # 552.创建最大数 117.跳跃游戏 II 116.跳跃游戏 187.加油站 182.删除数字 945.任务计划 LintCode 437.书籍复印(在答案集上进行二分) Python String methods # isdigit() isalpha() lower() upper() C++ string methods # isdigit(c) isalpha(c) putchar(tolower(c)) putchar(toupper(c)) 时间复杂度算法列表 # O(1) 位运算 O(logn) 二分法，倍增法，快速幂算法，辗转相除法 O(n) 枚举法，双指针算法，单调栈算法，KMP 算法，Rabin Karp，Manacher\u0026rsquo;s Algorithm 又称作线性时间复杂度 O(nlogn) 快速排序，归并排序，堆排序 O(n^2) 枚举法，动态规划，Dijkstra O(n^3) 枚举法，动态规划，Floyd O(2^n) 与组合有关的搜索问题 O(n!) 与排列有关的搜索问题 跟面试官核实 # 1.输入是否有序 how are these numbers given, can I assume that they are kind like an array or something \u0026gt;\u0026gt; ok oh interesting ok 2.有没有重复数字 how about repeating elements, can I assume that they would be like for instance here, what if I didn\u0026#39;t have that \u0026#39;four\u0026#39;, could I use like the \u0026#39;four\u0026#39; and \u0026#39;four\u0026#39; to get that \u0026#39;eight\u0026#39;? // you can\u0026#39;t repeat the same element at the same index twice but certainly the same number may appear twice \u0026gt;\u0026gt; ok ok so like that would be yes how about these numbers are they integers or are they floating points // you can assume they will be always integers \u0026gt;\u0026gt; ok negatives positives // negatives can happen \u0026gt;\u0026gt; ok cool so well the first the simplest solution of course is just comparing every single possible pair, so I \u0026gt;\u0026gt; could just have two for loops, one scanning the whole thing and then the second one starting from let\u0026#39;s say you \u0026gt;\u0026gt; have the \u0026#39;I\u0026#39; loop and then the \u0026#39;J\u0026#39; loop starting from \u0026#39;I\u0026#39; plus one, so that I don\u0026#39;t repeat the same value and \u0026gt;\u0026gt; just testing all of them if the sum is equal to the target sum. \u0026gt;\u0026gt; I mean that\u0026#39;s obviously not very efficient but that would be like a way to solve it // that would work, it certainly would be time-consuming \u0026gt;\u0026gt; yeah that would be quadratic, so, better than quadratic, Ah, well, since it\u0026#39;s sorted, okay, I guess I need to \u0026gt;\u0026gt; figure out when I have a number what I\u0026#39;m looking for is if there\u0026#39;s another number that sums to \u0026#39;eight\u0026#39;, so, so, \u0026gt;\u0026gt; if I have a \u0026#39;one\u0026#39; what I\u0026#39;d need to figure out is if there\u0026#39;s a \u0026#39;seven\u0026#39; somewhere in the array and that\u0026#39;s the case \u0026gt;\u0026gt; it\u0026#39;s sorted then I can do binary search, I guess if I go here and I binary search for a \u0026#39;seven\u0026#39;, then I go here \u0026gt;\u0026gt; and I binary search for a \u0026#39;six\u0026#39; which is the complement of that, and when I go here I binary search for a \u0026#39;five\u0026#39;, \u0026gt;\u0026gt; and at the end I just don\u0026#39;t do anything, and so in this case I would solve it like that. \u0026gt;\u0026gt; So that\u0026#39;s a bit better than quadratic, I guess binary search is log algorithm in a sorted list. // also an answer, you\u0026#39;re kind of slow // so what if you took a look at instead of doing a binary search which is unidirectional, what if you started with // a pair of numbers to begin with \u0026gt;\u0026gt; okay // and then work your way through in work from there \u0026gt;\u0026gt; let\u0026#39;s see, so, if I, okay, let me try to bound this thing, so the, the largest possible sum, I guess would be the \u0026gt;\u0026gt; last two values // that would be a largest possible sum, yes \u0026gt;\u0026gt; the smallest possible sum would be the two smallest right, so, so, anything in between, WOW, okay, so the range \u0026gt;\u0026gt; of the possible values is that (posture) right, so there\u0026#39;s nothing that is probably small there\u0026#39;s nothing that \u0026gt;\u0026gt; can be smaller than this value // right \u0026gt;\u0026gt; there\u0026#39;s nothing that can be larger than that value \u0026gt;\u0026gt; okay, so, if this sum (the first value + the last value) is \u0026#39;ten\u0026#39; in this case([1,2,3,9], sum = 8, ans = NO) it\u0026#39;s \u0026gt;\u0026gt; too large, so I need to find a smaller sum, so I could just move this one over here and if that is too small now \u0026gt;\u0026gt; and I need to move that one over there, okay, so, I can I think I can just do it with with that in a, in a \u0026gt;\u0026gt; linear solution just moving at each iteration, I either move the high one lower if I am if my pair is too large \u0026gt;\u0026gt; and I move my lower highter if my pair is too small and I end whenever I either find two like in this case I need \u0026gt;\u0026gt; to find a pair that adds up to \u0026#39;eight\u0026#39; or whenever they cross, so every point I\u0026#39;m moving one of them so they \u0026gt;\u0026gt; would have to at least cross and I move exactly one so that means that it\u0026#39;s linear, yeah, so that that would be a \u0026gt;\u0026gt; way of solving that problem. // how does that how does it make that faster than a binary search. \u0026gt;\u0026gt; okay so in the binary search case I was doing log for finding but I had to repeat that for every element that I \u0026gt;\u0026gt; was an O(nlogn) solution. In this case, I just need to do that moving scanning the one time, so it\u0026#39;s a linear \u0026gt;\u0026gt; solution, so that\u0026#39;s that\u0026#39;s faster. // so before maybe you could get to coding it but we quit, before we do that maybe you could explain, so if you // explained it in a nonworking example, maybe you have fallen through that same process and working. \u0026gt;\u0026gt; okay, yeah so here I would start with this and that right. So it\u0026#39;s five is smaller than \u0026#39;eight\u0026#39;, so I move this \u0026gt;\u0026gt; one here, so that\u0026#39;s \u0026#39;six\u0026#39; that\u0026#39;s smaller than \u0026#39;eight\u0026#39;, so I go here, and then that\u0026#39;s \u0026#39;eight\u0026#39;, so that\u0026#39;s true and \u0026gt;\u0026gt; I return. // excellent \u0026gt;\u0026gt; yeah, I think that would work // okay, so what coding language would you prefer to do is it \u0026gt;\u0026gt; um,I prefered C++ if that\u0026#39;s okay // C++ works, okay go for it \u0026gt;\u0026gt; ah perfect, let\u0026#39;s see. So, okay, now I realize that I haven\u0026#39;t figured out what I need to return. So do I want the \u0026gt;\u0026gt; pair, the indicies of the pair or whether I just found it or not // so for the purpose of the example we\u0026#39;ll go with whether you\u0026#39;re founder or not, but let\u0026#39;s say you were going to // return the pair, how could that become a problem that there was no pair 3.需不需要去掉重复答案 BFS 的使用场景 # 分层遍历 一层一层的遍历一个图、树、矩阵 简单图最短路径 简单图的定义是，图中所有的边长都一样 连通块问题 通过图中一个点找到其他所有连通的点 找到所有方案问题的一种非递归实现方式 拓扑排序 实现容易度远超过 DFS BFS 的使用场景（summer） # Connected Component 通过一个点找到图中连通的所有点 非递归的方式找所有方案 Level Order Traversal 图的层次遍历 简单图最短路径 Simple Graph Shortest Path Topological Sorting 求任意拓扑序 求是否有拓扑序 求字典序最小的拓扑序 求是否唯一拓扑序 以下哪些问题 BFS 可以处理： # 答案：\nA. 二叉树的层次遍历 B. 求出边长均为 5 的图的最短路径 E. 求出 01 矩阵上最大的全 0 块 F. 我不会写递归，但我需要从 10 个数中任意拿出 5 个的所有方案 非答案：\nD. 二叉树的先序遍历 解析：先序遍历通常使用递归方式来实现，即使使用非递归方式，也是借助栈来实现的，所以并不适合 BFS，而层次遍历因为是一层一层的遍历，所以是 BFS 十分擅长的；边长一致的图是简单图，所以可以用 BFS，因此 B 可以，因为 BFS 只适用于简单图，所以 C 不可以；矩阵连通块也是 BFS 可以处理的问题，求出最大块只需要维护一个最大值即可；选项 F 属于求所有方案问题，因此可以用 BFS 来处理，但是并不是唯一的解决方式。\nBFS 的三种实现方法 # 单队列 双队列 DummyNode // The \u0026ldquo;dummy\u0026rdquo; node is used to simplify some corner cases such as a list with only one node, or removing the head of the list. 二叉树的 BFS vs 图的 BFS： # 二叉树中进行 BFS 和图中进行 BFS 最大的区别就是二叉树中无需使用 HashSet（C++: unordered_set, Python: set) 来存储访问过的节点（丢进过 queue 里的节点） 因为二叉树这种数据结构，上下层关系分明，没有环（circle），所以不可能出现一个节点的儿子的儿子是自己的情况。 但是在图中，一个节点的邻居的邻居就可能是自己了。 Recursion/ DFS/ Backtracking: # Recursion\n递归函数 程序的一种实现方式，即函数进行了自我调用 递归算法 即大问题的结果依赖于小问题的结果，于是先用递归函数求解小问题 一般我们说递归的时候，大部分时候都在说递归函数而不是递归算法 DFS\n可以使用递归函数实现 也可以不用递归函数来实现，如自己通过一个手动创建的栈 Stack 进行操作 深度优先搜索通常是指在搜索的过程中，优先搜索深度更深的点而不是按照宽度搜索同层节点 Backtracking\n回溯法： == 深度优先搜索算法 回溯操作：递归函数在回到上一层递归调用处的时候，一些参数需要改回到调用前的值，这个操作就是回溯，即让状态参数回到之前的值，递归调用前做了什么改动，递归调用之后都改回来 遍历法 vs 分治法： # 都可以用 DFS 实现\n遍历法 = 一个小人拿着一个记事本走遍所有都节点\n分治法 = 分配小弟去做子任务，自己进行结果汇总\n遍历法：通常会用到一个全局变量或者是共享参数\n分治法：通常将利用 return value 记录子问题结果 二叉树上的分治法本质上也是在做遍历（后序遍历） 先序？中序？后序?\n// 二叉树上的分治法模版 // 实际上是后序遍历 public: 返回结果类型 divideConquer(TreeNode* root) { if (root == nullptr) { 处理空树应该返回的结果 } // if (root-\u0026gt;left == nullptr \u0026amp;\u0026amp; root-\u0026gt;right == nullptr) { // 处理叶子应该返回的结果 // 如果叶子的返回结果可以通过两个空节点的返回结果得到 // 就可以省略这一段代码 // } 左子树返回结果 = divideConquer(root-\u0026gt;left); 右子树返回结果 = divideConquer(root-\u0026gt;right); 整棵树的结果 = 按照一定方法合并左右子树的结果 return 整棵树的结果 } 平衡二叉树 # 任意节点左右子树高度之差不超过 1 计算深度 # 适合用分治法解决这个问题 Binary Search Tree 二叉查找树： # 一种特殊的二叉树 定义： 左子树节点值 \u0026lt; 根节点的值，右子树节点的值 \u0026gt;= 根节点的值 相等的情况：值相等的点可能在右子树，或者可能在左子树，需要根面试官澄清 中序遍历： 中序遍历结果有序（不下降的顺序，有些相邻点可能相等） 如果二叉树的中序遍历不是“不下降”序列，则一定不是 BST 如果二叉树的中序遍历是“不下降”序列,也未必是 BST，反例：{1,1,1} 二叉查找树的高度： 最坏 O(n), 最好 O(logn), 用 O(h) 表示更合适 只有 Balanced Binary Tree（平衡二叉树）才是 O(logn) BST 基本操作： # Build: 1359.Convert Sorted Array to Binary Search Tree\nInsert: 85.Insert Node in a Binary Search Tree\nSearch: 1524.Search in a Binary Search Tree\nDelete: 701.Trim a Binary Search Tree\nIterate: 86.Binary Search Tree Iterator\nDelete Node in a BST # Leetcode 450 Delete Node in a BST // Recursion class Solution { public: int successor(TreeNode* root) { root = root-\u0026gt;right; while (root-\u0026gt;left != nullptr) root = root-\u0026gt;left; return root-\u0026gt;val; } int predecessor(TreeNode* root) { root = root-\u0026gt;left; while (root-\u0026gt;right != nullptr) root = root-\u0026gt;right; return root-\u0026gt;val; } TreeNode* deleteNode(TreeNode* root, int key) { if (root == nullptr) return nullptr; if (key \u0026gt; root-\u0026gt;val) root-\u0026gt;right = deleteNode(root-\u0026gt;right, key); else if (key \u0026lt; root-\u0026gt;val) root-\u0026gt;left = deleteNode(root-\u0026gt;left, key); else { if (root-\u0026gt;left == nullptr \u0026amp;\u0026amp; root-\u0026gt;right == nullptr) root = nullptr; else if (root-\u0026gt;right != nullptr) { root-\u0026gt;val = successor(root); root-\u0026gt;right = deleteNode(root-\u0026gt;right, root-\u0026gt;val); } else { root-\u0026gt;val = predecessor(root); root-\u0026gt;left = deleteNode(root-\u0026gt;left, root-\u0026gt;val); } } return root; } }; // Iteration class Solution { public: TreeNode* deleteNode(TreeNode* root, int key) { if (root == nullptr) return nullptr; TreeNode* parent = nullptr; TreeNode* current = root; // Find the node to delete while (current != nullptr \u0026amp;\u0026amp; current-\u0026gt;val != key) { parent = current; if (current-\u0026gt;val \u0026lt; key) current = current-\u0026gt;right; else current = current-\u0026gt;left; } if (current == nullptr) return root; // Key not found if (current-\u0026gt;left == nullptr \u0026amp;\u0026amp; current-\u0026gt;right == nullptr) { // Case 1: No child if (current == root) root = nullptr; else if (parent-\u0026gt;left == current) parent-\u0026gt;left = nullptr; else parent-\u0026gt;right = nullptr; delete current; } else if (current-\u0026gt;left != nullptr) { // Case 2: One child (left) TreeNode* predecessorParent = current; TreeNode* predecessor = current-\u0026gt;left; while (predecessor-\u0026gt;right != nullptr) { predecessorParent = predecessor; predecessor = predecessor-\u0026gt;right; } current-\u0026gt;val = predecessor-\u0026gt;val; if (predecessorParent-\u0026gt;left == predecessor) predecessorParent-\u0026gt;left = predecessor-\u0026gt;left; else predecessorParent-\u0026gt;right = predecessor-\u0026gt;left; delete predecessor; } else { // Case 3: One child (right) TreeNode* successorParent = current; TreeNode* successor = current-\u0026gt;right; while (successor-\u0026gt;left != nullptr) { successorParent = successor; successor = successor-\u0026gt;left; } current-\u0026gt;val = successor-\u0026gt;val; if (successorParent-\u0026gt;left == successor) successorParent-\u0026gt;left = successor-\u0026gt;right; else successorParent-\u0026gt;right = successor-\u0026gt;right; delete successor; } return root; } }; Red-Black Tree 红黑树： # 是一种 Balanced BST\nJava: TreeMap/TreeSet\nC++: map/set\nApplication:\nO(logN) 的时间内实现增删改查\nO(logN) 的时间内实现找最大找最小\nO(logN) (wrong!!!???)的时间内实现找比某个数小的最大值(upperBound)和比某个数大的最小值(lowerBound)\nC++【用途】針對「已經排序」的資料進行binary search。 vector v; sort(v.begin(), v.end()); lower_bound：找出vector中「大於或等於」val的「最小值」的位置： auto it = lower_bound(v.begin(), v.end(), val); upper_bound：找出vector中「大於」val的「最小值」的位置： auto it = upper_bound(v.begin(), v.end(), val); 只考红黑树的应用，不考红黑树的实现\n二叉树三种遍历： # - `先序遍历` Pre-order - `中序遍历` In-order - `后序遍历` Post-order（分治法） “二叉树的中序遍历”的非递归实现 # 考得最多 通过实现 hasNext 和 next 两个方法，从而实现二叉查找树的中序遍历迭代器 86.Binary Search Tree Iterator 相当于 Binary Tree In-order Iterator 实现要点： 递归-\u0026gt;非递归，意味着自己需要控制原来由操作系统控制的栈的进进出出 如何找到最小的第一个点？最左边的点即是 如何求出一个二叉树节点在中序遍历中的下一个节点？ 在 stack 中记录从根节点到当前节点的整条路径 下一个点 = 右子树最小点 or 路径中最近一个通过左子树包含当前点的点 class BSTIterator { public: BSTIterator(TreeNode * root) { while (root != nullptr) { stack_.push(root); root = root-\u0026gt;left; } } bool HasNext() { return !stack_.empty(); } TreeNode* Next() { TreeNode* node = stack_.top(); TreeNode* n = node; if (node-\u0026gt;right != nullptr) { n = node-\u0026gt;right; while (n != nullptr) { stack_.push(n); n = n-\u0026gt;left; } } else { stack_.pop(); while (!stack_.empty() \u0026amp;\u0026amp; stack_.top()-\u0026gt;right == n) { n = stack_.top(); stack_.pop(); } } return node; } private: std::stack\u0026lt;TreeNode*\u0026gt; stack_; }; 简单的实现方式代码： class BSTIterator { public: BSTIterator(TreeNode * root) { find_most_left(root); } void find_most_left(TreeNode* node) { while (node != nullptr) { stack.push(node); node = node-\u0026gt;left; } } bool hasNext() { return !stack.empty(); } TreeNode* next() { TreeNode* node = stack.top(); stack.pop(); if (node-\u0026gt;right != nullptr) { find_most_left(node-\u0026gt;right); } return node; } private: std::stack\u0026lt;TreeNode*\u0026gt; stack; }; # Python def __init__(self, root): self.stack = [] while root != None: self.stack.append(root) root = root.left def hasNext(self): return len(self.stack) \u0026gt; 0 def next(self): node = self.stack[-1] if node.right is not None: n = node.right while n != None: self.stack.append(n) n = n.left else: n = self.stack.pop() while self.stack and self.stack[-1].right == n: n = self.stack.pop() return node 简单的实现方式代码： Pyhton： class BSTIterator: def __init__(self, root): self.stack = [] self.find_most_left(root) def find_most_left(self, node): while node: self.stack.append(node) node = node.left def hasNext(self): return bool(self.stack) def next(self): node = self.stack.pop() if node.right: self.find_most_left(node.right) return node BST 中最小的节点是从根节点一直往左走遇见的叶子节点，它不一定在树的最底层；BST 的特征就是中序遍历是严格递增的；如果这颗 BST 是一条链，那么找到最小值节点的算法是 O(n)的，除非这个 BST 是一个满二叉树。 Prefix Sum # // C++ void get_prefix_sum(std::vector\u0026lt;int\u0026gt;\u0026amp; prefix_sum, std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { for (int i = 0; i \u0026lt; nums.size(); i++) { prefix_sum.push_back(prefix_sum[i] + nums[i]); } } # Python def get_prefix_sum(self, nums): prefix_sum = [0] for num in nums: prefix_sum.append(prefix_sum[-1] + num) return prefix_sum Prefix Product, Suffix Product # original array: 2, 2, 3, 4 pp: prefix product: 1, 2, 4, 12, 48 sp: suffix produc: 48, 24, 12, 4, 1\nto get subarray\u0026rsquo;s product: e.g. idx [1, 2] inclusively from prefix: ps[2 + 1] / ps[1] = 12 / 2 from suffix: sp[1] / sp[2 + 1] = 24 / 4\n使用前缀和数组在 O(1)的时间复杂度内计算子数组和 # sum from i to j = prefix_sum[j + 1] - prefix_sum[i] 解决最短路径的算法： # 简单图： BFS 复杂图： Floyd, Dijkstra, Bellman-ford, SPFA time \u0026amp; space compelxity of recursive: # time: 一次* 次数 space： 一次 + 深度 遇到二叉树的问题，就想想整棵树在该问题上的结果和左右孩子在该问题上的结果之间有什么联系 # 拓扑排序 Topological Sorting: # 图 + 有依赖关系 + 有向 + 无环 = 拓扑排序\n通过拓扑排序判断是否图是否有环\n入度（in-degree）：\n有向图（Directed Graph）中指向当前节点的点的个数（或指向当前节点的边的条数） 算法描述：\n统计每个点的入度 将每个入度为 0 的点放入队列（Queue）中作为起始节点 不断从队列中拿出一个点，去掉这个点的所有连边（指向其他点的边），其他点的相应的入度-1 一旦发现新的入度为 0 的点，丢回队列中 拓扑排序并不是传统的排序算法：\n一个图可能存在多个拓扑排序（Topological Graph），也可能不存在任何拓扑排序 拓扑排序的四种不同问法： # 求任意拓扑序 求是否有拓扑序 求字典序最小的拓扑序 求是否唯一拓扑序 Others # Quick Select # // quickselect int quickSelect(vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { int pivot = nums[rand() % nums.size()]; vector\u0026lt;int\u0026gt; left; vector\u0026lt;int\u0026gt; mid; vector\u0026lt;int\u0026gt; right; for (int num: nums) { if (num \u0026gt; pivot) { left.push_back(num); } else if (num \u0026lt; pivot) { right.push_back(num); } else { mid.push_back(num); } } if (k \u0026lt;= left.size()) { return quickSelect(left, k); } if (left.size() + mid.size() \u0026lt; k) { return quickSelect(right, k - left.size() - mid.size()); } return pivot; } GCD - Greatest Common Divisor # int gcd(int x, int y) { if (y == 0) return x; return gcd(y, x % y); } lower_bound vs upper_bound # lower_bound upper_bound string find, mismatch # string find # C++ find example usage string str1 = \u0026#34;dogt\u0026#34;; // dog string str2 = \u0026#34;dogracecardogtt\u0026#34;; // rdogracecar // check if str1 is prefix of str2 // str2.find(str1, 0) == 0 cout \u0026lt;\u0026lt; (str2.find(str1, 0)) \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; string::npos \u0026lt;\u0026lt; endl; // check if str1 is suffix of str2: // str2.find(str1, str2.size() - str1.size()) == str2.size() - str1.size() cout \u0026lt;\u0026lt; str1.size() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; str2.size() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; (str2.find(str1, str2.size() - str1.size())) \u0026lt;\u0026lt; endl; mismatch # std::string foo(\u0026#34;foo\u0026#34;); std::string foobar(\u0026#34;foobar\u0026#34;); // prefix checking auto res = std::mismatch(foo.begin(), foo.end(), foobar.begin()); if (res.first == foo.end()) { // foo is a prefix of foobar. } // suffix checkign auto res = std::mismatch(foo.begin(), foo.end(), foobar.begin() + foobar.size() - foo.size()); if (res.first == foo.end()) { // foo is a suffix of foobar. } assert # #include\u0026lt;cassert\u0026gt; assert((expression) \u0026amp;\u0026amp; \u0026#34;msg\u0026#34;) assert(expression); // cannot be std::assert(expression) try throw catch - error handling # try { // do something that might throw an error throw std::invalid_argument(\u0026#34;MyFunc argument too large.\u0026#34;); } catch (const std::exception\u0026amp; e) { // handle the error std::cout \u0026lt;\u0026lt; \u0026#34;3333\u0026#34; \u0026lt;\u0026lt; n \u0026lt;\u0026lt; std::endl; std::cerr \u0026lt;\u0026lt; e.what() \u0026lt;\u0026lt; std::endl; // return -1; } gtest with cmake # gtest helloworld step 1: # mkdir my_project \u0026amp;\u0026amp; cd my_project step 2: CMakeLists.txt # # within CMakeLists.txt cmake_minimum_required(VERSION 3.14) project(my_project) # GoogleTest requires at least C++14 set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) include(FetchContent) FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip ) # For Windows: Prevent overriding the parent project\u0026#39;s compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL \u0026#34;\u0026#34; FORCE) FetchContent_MakeAvailable(googletest) step 3: test fucntions # #include \u0026lt;gtest/gtest.h\u0026gt; // Demonstrate some basic assertions. TEST(HelloTest, BasicAssertions) { // Expect two strings not to be equal. EXPECT_STRNE(\u0026#34;hello\u0026#34;, \u0026#34;world\u0026#34;); // Expect equality. EXPECT_EQ(7 * 6, 42); } step 4: Append to CMakeLists.txt # enable_testing() add_executable( hello_test hello_test.cc ) target_link_libraries( hello_test GTest::gtest_main ) include(GoogleTest) gtest_discover_tests(hello_test) step 5: build and run test # my_project$ cmake -S . -B build -- The C compiler identification is GNU 10.2.1 -- The CXX compiler identification is GNU 10.2.1 ... -- Build files have been written to: .../my_project/build my_project$ cmake --build build Scanning dependencies of target gtest ... [100%] Built target gmock_main my_project$ cd build \u0026amp;\u0026amp; ctest Test project .../my_project/build Start 1: HelloTest.BasicAssertions 1/1 Test #1: HelloTest.BasicAssertions ........ Passed 0.00 sec 100% tests passed, 0 tests failed out of 1 Total Test time (real) = 0.01 sec print vector to the console # #define print(v) std::copy(v.begin(), v.end(), std::ostream_iterator\u0026lt;int\u0026gt;(std::cout, \u0026#34; \u0026#34;)); std::cout \u0026lt;\u0026lt; std::endl // C++20 #define print(x) std::ranges::copy(x, std::ostream_iterator\u0026lt;int\u0026gt;(std::cout, \u0026#34; \u0026#34;)); std::cout \u0026lt;\u0026lt; std::endl priority queue # Lintcode 1507 Shortest Subarray with Sum at Least K Binary search on answer + priority_queue # class Solution { public: int shortestSubarray(std::vector\u0026lt;int\u0026gt;\u0026amp; A, int K) { std::vector\u0026lt;int\u0026gt; prefix_sum = GetPrefixSum(A); int left = 1; int right = A.size(); while (left + 1 \u0026lt; right) { int mid = left + (right - left) / 2; if (IsValid(prefix_sum, mid, K)) { right = mid; } else { left = mid; } } if (IsValid(prefix_sum, left, K)) { return left; } if (IsValid(prefix_sum, right, K)) { return right; } return -1; } private: std::vector\u0026lt;int\u0026gt; GetPrefixSum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;int\u0026gt; answer(nums.size() + 1, 0); for (int i = 0; i \u0026lt; nums.size(); ++i) { answer[i + 1] = answer[i] + nums[i]; } return answer; } bool IsValid(std::vector\u0026lt;int\u0026gt;\u0026amp; prefix_sum, int length, int K) { auto cmp = [](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a, const std::pair\u0026lt;int, int\u0026gt;\u0026amp; b) { return a.second \u0026gt; b.second; }; std::set\u0026lt;std::pair\u0026lt;int, int\u0026gt;, decltype(cmp)\u0026gt; pq(cmp); // c++20 pq; c++11 pq(cmp) for (int end = 0; end \u0026lt; prefix_sum.size(); ++end) { int index = end - length - 1; if (index \u0026gt;= 0) { pq.erase(std::find_if(pq.begin(), pq.end(), [\u0026amp;index](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a) { return a.first == index; })); } if (!pq.empty() \u0026amp;\u0026amp; prefix_sum[end] - pq.rbegin()-\u0026gt;second \u0026gt;= K) { return true; } pq.insert(std::make_pair(end, prefix_sum[end])); } return false; } }; LRU # LRU implementation // C++ #include \u0026lt;unordered_map\u0026gt; struct LinkedNode { LinkedNode(int key, int value, LinkedNode* next) : key(key), value(value), next(next) {} int key; int value; LinkedNode* next; }; class LRUCache { public: LRUCache(int capacity) : capacity_(capacity), dummy_(new LinkedNode(0, 0, nullptr)), tail_(dummy_) {} // Google style: Get int Get(int key) { if (key_to_previous_.find(key) == key_to_previous_.end()) { return -1; } LinkedNode* previous = key_to_previous_.at(key); LinkedNode* current = previous-\u0026gt;next; Kick(previous); return current-\u0026gt;value; } // Google style: Set void Set(int key, int value) { if (key_to_previous_.find(key) != key_to_previous_.end()) { Kick(key_to_previous_.at(key)); tail_-\u0026gt;value = value; return; } PushBack(new LinkedNode(key, value, nullptr)); // 如果key不存在，则存入新节点 if (key_to_previous_.size() \u0026gt; capacity_) { // 如果缓存超出上限 PopFront(); } } private: void PushBack(LinkedNode* node) { key_to_previous_[node-\u0026gt;key] = tail_; tail_-\u0026gt;next = node; tail_ = node; } void PopFront() { // 删除头部 LinkedNode* head = dummy_-\u0026gt;next; key_to_previous_.erase(head-\u0026gt;key); dummy_-\u0026gt;next = head-\u0026gt;next; key_to_previous_[head-\u0026gt;next-\u0026gt;key] = dummy_; } // change \u0026#34;previous-\u0026gt;node-\u0026gt;next-\u0026gt;...-\u0026gt;tail_\u0026#34; // to \u0026#34;previous-\u0026gt;next-\u0026gt;...-\u0026gt;tail_-\u0026gt;node\u0026#34; void Kick(LinkedNode* previous) { // 将数据移至尾部 LinkedNode* node = previous-\u0026gt;next; if (node == tail_) { return; } // update the current node from linked list previous-\u0026gt;next = node-\u0026gt;next; // update the previous node in hash map key_to_previous_[node-\u0026gt;next-\u0026gt;key] = previous; node-\u0026gt;next = nullptr; PushBack(node); } int capacity_; LinkedNode* dummy_; LinkedNode* tail_; std::unordered_map\u0026lt;int, LinkedNode*\u0026gt; key_to_previous_; }; LIS # Leetcode 300. LIS Longest Increasing Subsequence\n接龙规则：从左到右一个比一个大，该问题简称 LIS\n状态表示：\nA：dp[i] 表示前i个数的 LIS 是多长(前缀型, do not choose this) B：dp[i] 表示以第i个数结尾的 LIS 是多长(坐标型) LIS 的动态规划四要素 # state: dp[i]表示以第i个数为龙尾的最长的龙有多长 function: dp[i] = max{dp[j] + 1}, j \u0026lt; i \u0026amp;\u0026amp; nums[j] \u0026lt; nums[i] initialization: dp[0..n-1] = 1 answer: max{dp[0..n-1]} def longestIncreasingSubsequence(self, nums): if nums is None or not nums: return 0 # state: dp[i] 表示以第i个数结尾的LIS的长度 # initialization：dp[0..n-1] = 1 dp = [1] * len(nums) # function: dp[i] = max(dp[i] + 1), j \u0026lt; i \u0026amp;\u0026amp; nums[j] \u0026lt; nums[i] for i in range(len(nums)): for j in range(i): if nums[j] \u0026lt; nums[i]: dp[i] = max(dp[i], dp[j] + 1) # answer, 任意一个位置都可能是LIS的结尾 return max(dp) 改动要点(返回最优方案) prev 数组记录前继最优状态 max() 的写法要改为 if 的写法 找到最长龙的结尾，从结尾倒推出整条龙 def longestIncreasingSubsequence(self, nums): if nums is None or not nums: return 0 # state: dp[i] 表示以第i个数结尾的LIS的长度 # initialization：dp[0..n-1] = 1 dp = [1] * len(nums) # prev[i]代表dp[i]的最优值是从哪个dp[j]算过来的 prev = [-1] * len(nums) # function dp[i] = max{dp[j] + 1}, j \u0026lt; i and nums[j] \u0026lt; nums[i] for i in range(len(nums)): for j in range(i): if nums[j] \u0026lt; nums[i] and dp[i] \u0026lt; dp[j] + 1: dp[i] = dp[j] + 1 prev[i] = j # answer: max(dp[0..n-1]) longest, last = 0, -1 for i in range(len(nums)): if dp[i] \u0026gt; longest: longest = dp[i] last = i path = [] while last != -1 path.append(nums[last]) last = prev[last] print(path[::-1]) return longest LIS2 # Leetcode 674. LIS2 - Longest Continuous Increasing Subsequence class Solution: \u0026#34;\u0026#34;\u0026#34; @param A: An integer matrix @return: an integer \u0026#34;\u0026#34;\u0026#34; def longestContinuousIncreasingSubsequence2(self, A): if not A or not A[0]: return 0 n, m = len(A), len(A[0]) points = [] for i in range(n): for j in range(m): points.append((A[i][j], i, j)) points.sort() longest_hash = {} for i in range(len(points)): key = (points[i][1], points[i][2]) longest_hash[key] = 1 for dx, dy in [(1, 0), (0, -1), (-1, 0), (0, 1)]: x, y = points[i][1] + dx, points[i][2] + dy if x \u0026lt; 0 or x \u0026gt;= n or y \u0026lt; 0 or y \u0026gt;= m: continue if (x, y) in longest_hash and A[x][y] \u0026lt; points[i][0]: longest_hash[key] = max(longest_hash[key], longest_hash[(x, y)] + 1) return max(longest_hash.values()) Largest Divisible Subset # Leetcode 368. Largest Divisible Subset class Solution: def largestDivisibleSubset(self, nums): if not nums: return [] nums = sorted(nums) n = len(nums) dp, prev = {}, {} for num in nums: dp[num] = 1 prev[num] = -1 last_num = nums[0] for num in nums: for factor in self.get_smaller_factors(num): if factor not in dp: continue if dp[num] \u0026lt; dp[factor] + 1: dp[num] = dp[factor] + 1 prev[num] = factor if dp[num] \u0026gt; dp[last_num]: last_num = num return self.get_path(prev, last_num) def get_smaller_factors(self, num): if num == 1: return [] factor = 1 factors = [] while factor * factor \u0026lt;= num: if num % factor == 0: factors.append(factor) if factor * factor != num and factor != 1: factors.append(num // factor) factor += 1 return factors def get_path(self, prev, last_num): path = [] while last_num != -1: path.append(last_num) last_num = prev[last_num] return path[::-1] HashMap Implementation # leetcode 705. design hashset // C++ sort lambda # auto sortRuleLambda = [](const Skyscraper\u0026amp; s1, const Skyscraper\u0026amp; s2) -\u0026gt; bool { return s1.height() \u0026lt; s2.height(); }; std::sort(skyscrapers.begin(), skyscrapers.end(), sortRuleLambda); customized hash for unordered_map or unordered_set # struct pair_hash { template \u0026lt;class T1, class T2\u0026gt; std::size_t operator () (const std::pair\u0026lt;T1,T2\u0026gt; \u0026amp;p) const { auto h1 = std::hash\u0026lt;T1\u0026gt;{}(p.first); auto h2 = std::hash\u0026lt;T2\u0026gt;{}(p.second); // Mainly for demonstration purposes, i.e. works but is overly simple // In the real world, use sth. like boost.hash_combine return h1 ^ (h2 \u0026lt;\u0026lt; 1); } }; int main() { std::unordered_map\u0026lt;std::pair\u0026lt;int, int\u0026gt;, int, pair_hash\u0026gt; pos_index_map; return 0; } function pointer in c++ # int sum(int a, int b) { return a + b; } int prod(int a, int b) { return a * b; } int shouldNotBeChanged(int (*operation)(int, int)) { srand(time(nullptr)); int a = rand() % 100; int b = rand() % 100; printf(\u0026#34;The result of the operation between %d and %d is %d\\n\u0026#34;, a, b, operation(a, b)); return 0; } int main() { shouldNotBeChanged(\u0026amp;sum); return 0; } element wise comparison of two structs # struct Point { float x; float y; Point(int x = 0, int y = 0) : x(x), y(y) {} }; int main() { Point p1 = Point(1, 2); Point p2 = Point(2, 1); // std::tie can have any many parameters as it wants if (std::tie(p1.x, p2.x) == std::tie(p2.y, p1.y)) { std::cout \u0026lt;\u0026lt; \u0026#34;haha\u0026#34; \u0026lt;\u0026lt; std::endl; } else { std::cout \u0026lt;\u0026lt; \u0026#34;nono\u0026#34; \u0026lt;\u0026lt; std::endl; } } how to use c++ build-in hash function # size_t h1 = std::hash\u0026lt;char\u0026gt;()(\u0026#39;a\u0026#39;); size_t h2 = std::hash\u0026lt;char\u0026gt;()(\u0026#39;b\u0026#39;); std::unordered_map\u0026lt;std::string, int\u0026gt; myhash; std::unordered_map\u0026lt;std::string, int\u0026gt;::hasher fn = myhash.hash_function(); std::cout \u0026lt;\u0026lt; fn(\u0026#34;apple\u0026#34;) \u0026lt;\u0026lt; std::endl; c++ const # link // value of x and y can be altered // x = 9; y = \u0026#39;A\u0026#39;; // value of i and j can be altered // i = \u0026amp;m; j = \u0026amp;n; // !!! value of *i and *j cannot be altered // *i = 6; *j = 7; // read-only variable is not assignable const int* i = \u0026amp;x; const char* j = \u0026amp;y; // value of x and y can be altered // x = 9; y = \u0026#39;A\u0026#39;; // !!! value of i and j cannot be altered // i = \u0026amp;m; j = \u0026amp;n; // variable \u0026#39;i\u0026#39; and \u0026#39;j\u0026#39; declared const here // value of *i and *j can be altered // *i = 6; *j = \u0026#39;A\u0026#39;; int* const i = \u0026amp;x; char* const j = \u0026amp;y; // value of x and y can be altered // x = 9; y = \u0026#39;A\u0026#39;; // !!! value of i and j cannot be altered // i = \u0026amp;m; j = \u0026amp;n; // !!! value of *i and *j cannot be altered // *i = 6; *j = 7; const int* const i = \u0026amp;x; const char* const j = \u0026amp;y; The compile-time error that will appear as if const value is passed to any non-const argument of the function\n// error: no matching function for call to \u0026#39;foo\u0026#39; // candidate function not viable: 1st argument (\u0026#39;const int *\u0026#39;) would lose const qualifier int foo(int* y) { return *y; } int main() { int z = 8; const int* x = \u0026amp;z; std::cout \u0026lt;\u0026lt; foo(x) \u0026lt;\u0026lt; std::endl; return 0; } // Function foo() with variable // const int void foo(const int y) { // y = 6; const value // can\u0026#39;t be change cout \u0026lt;\u0026lt; y; } // Function foo() with variable int void foo1(int y) { // Non-const value can be change y = 5; cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39; \u0026lt;\u0026lt; y; } // Driver Code int main() { int x = 9; const int z = 10; foo(z); foo1(x); return 0; } const return\n// int foo(int y) { // no error // const int foo(int y) { // no error const int foo(const int y) { // error: cannot assign to variable \u0026#39;y\u0026#39; with const-qualified type \u0026#39;const int\u0026#39; --y; return y; } int main() { int x = 9; const int z = 10; std::cout \u0026lt;\u0026lt; foo(x) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39; \u0026lt;\u0026lt; foo(z); return 0; } An object declared as const cannot be modified and hence, can invoke only const member functions as these functions ensure not to modify the object.\nWhen a function is declared as const, it can be called on any type of object, const object as well as non-const objects.\nclass Test { public: // Constructor Test(int v = 0) { value = v; } // this const means cannot modify class members, e.g. value // We get compiler error if we add a line like \u0026#34;value = 100;\u0026#34; // in this function. int getValue() const { return value; } // a nonconst function trying to modify value void setValue(int val) { value = val; } private: int value; }; // Driver Code int main() { // Object of the class T Test t(20); // non-const object invoking const function, no error cout \u0026lt;\u0026lt; t.getValue() \u0026lt;\u0026lt; endl; // const object const Test t_const(10); // const object invoking const function, no error cout \u0026lt;\u0026lt; t_const.getValue() \u0026lt;\u0026lt; endl; // const object invoking non-const function, CTE // t_const.setValue(15); // non-const object invoking non-const function, no error t.setValue(12); cout \u0026lt;\u0026lt; t.getValue() \u0026lt;\u0026lt; endl; return 0; } random seed # 3407\nC++20 comparison operator # struct Point { int x; int y; Point() : x(0), y(0) {} Point(int a, int b) : x(a), y(b) {} // !!! have to write it this way: inline bool operator== (const Point\u0026amp; other) const { return x == other.x \u0026amp;\u0026amp; y == other.y; } }; To initialize two dimentional array # #include\u0026lt;iostream\u0026gt; int main() { int** secondStore; secondStore = new int*[10]; for (int i = 0; i \u0026lt; 10; ++i) { secondStore[i] = new int[32]; } std::cout \u0026lt;\u0026lt; secondStore[0][0] \u0026lt;\u0026lt; std::endl; return 0; } heap: set vs priority_queue # we cannot iterate priority_queue, but we can make a copy of it and then use \u0026lsquo;pop\u0026rsquo; and \u0026rsquo;top()\u0026rsquo; to iterate #include \u0026lt;iostream\u0026gt; #include \u0026lt;string\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;set\u0026gt; #include \u0026lt;iterator\u0026gt; #include \u0026lt;math.h\u0026gt; #include \u0026lt;cassert\u0026gt; #include \u0026lt;queue\u0026gt; #include \u0026lt;map\u0026gt; int main() { auto cmp = [](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a, const std::pair\u0026lt;int, int\u0026gt;\u0026amp; b) {return a.first \u0026gt; b.first;}; std::set\u0026lt;std::pair\u0026lt;int, int\u0026gt;, decltype(cmp)\u0026gt; my_heap_with_set(cmp); // get min heap // std::priority_queue\u0026lt;std::pair\u0026lt;int, int\u0026gt;, std::deque\u0026lt;std::pair\u0026lt;int, int\u0026gt;\u0026gt;, decltype(cmp)\u0026gt; my_heap_with_priority_queue(cmp); // get max heap std::priority_queue\u0026lt;std::pair\u0026lt;int, int\u0026gt;, std::deque\u0026lt;std::pair\u0026lt;int, int\u0026gt;\u0026gt;\u0026gt; my_heap_with_priority_queue; // get max heap my_heap_with_set.insert(std::make_pair(3, 1)); my_heap_with_set.insert(std::make_pair(2, 1)); my_heap_with_set.insert(std::make_pair(4, 1)); my_heap_with_set.insert(std::make_pair(0, 1)); my_heap_with_priority_queue.push({3, 1}); my_heap_with_priority_queue.push({2, 1}); my_heap_with_priority_queue.push({4, 1}); my_heap_with_priority_queue.push({0, 1}); auto it = my_heap_with_set.begin(); std::cout \u0026lt;\u0026lt; \u0026#34;my_set: \u0026#34; \u0026lt;\u0026lt; std::endl;; for (; it != my_heap_with_set.end(); ++it) { std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; } std::cout \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;my_priority_queue: \u0026#34; \u0026lt;\u0026lt; std::endl; for (; !my_heap_with_priority_queue.empty(); my_heap_with_priority_queue.pop()) { std::cout \u0026lt;\u0026lt; my_heap_with_priority_queue.top().first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; my_heap_with_priority_queue.top().second \u0026lt;\u0026lt; std::endl; } std::cout \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;test map iteration: \u0026#34; \u0026lt;\u0026lt; std::endl; auto cmp2 = [](const int\u0026amp; a, const int\u0026amp; b) {return a \u0026gt; b;}; std::map\u0026lt;int, int, decltype(cmp2)\u0026gt; my_map(cmp2); my_map[0] = 12; my_map[1] = 15; my_map[1111] = 111; for (auto i : my_map) { // works std::cout \u0026lt;\u0026lt; i.first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; i.second \u0026lt;\u0026lt; std::endl; // for (auto it = my_map.begin(); it != my_map.end(); ++it) { // works // std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; } std::cout \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;test max_element for map: \u0026#34; \u0026lt;\u0026lt; std::endl; auto cmp_max_element = [](const auto\u0026amp; a, const auto\u0026amp; b) {return a.second \u0026gt; b.second;}; // be aware that we should use \u0026#39;-\u0026gt;second\u0026#39; at the end, becuase max_element return iterator int temp = max_element(my_map.begin(), my_map.end(), cmp_max_element)-\u0026gt;second; std::cout \u0026lt;\u0026lt; temp \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; std::endl; return 0; } heap with multiset, erase with find # // Leetcode 731. My calendar II(Sweep Line Algorithm) #define print(x) std::copy(x.begin(), x.end(), std::ostream_iterator\u0026lt;int\u0026gt;(std::cout, \u0026#34; \u0026#34;)); std::cout \u0026lt;\u0026lt; std::endl class MyCalendarTwo { public: MyCalendarTwo() {} bool book(int start, int end) { v.insert({start, 1}); v.insert({end, -1}); // std::cout \u0026lt;\u0026lt; \u0026#34;start: \u0026#34; \u0026lt;\u0026lt; start \u0026lt;\u0026lt; \u0026#34; end: \u0026#34; \u0026lt;\u0026lt; end \u0026lt;\u0026lt; std::endl; // for (auto e : v) { // std::cout \u0026lt;\u0026lt; e[0] \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; e[1] \u0026lt;\u0026lt; std::endl; // } if (IsValid()) { return true; } else { // Approach 1: with find_if // auto index = std::find_if(v.begin(), v.end(), [\u0026amp;start](const auto\u0026amp; first) { // return first[0] == start \u0026amp;\u0026amp; first[1] == 1; // }); // v.erase(index); // index = std::find_if(v.begin(), v.end(), [\u0026amp;end](const auto\u0026amp; first) { // return first[0] == end \u0026amp;\u0026amp; first[1] == -1; // }); // v.erase(index); // Approach 2: with find v.erase(v.find({start, 1})); v.erase(v.find({end, -1})); return false; } } bool IsValid() { // check if there is triple booking int count = 0; for (auto it = v.begin(); it != v.end(); ++it) { count += it-\u0026gt;at(1); if (count \u0026gt;= 3) return false; } return true; } std::multiset\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; v; } return min or max element from hashmap # auto cmp = [](const auto\u0026amp; a, const auto\u0026amp; b) {return a.second \u0026lt; b.second;}; min_value = min_element(my_map.begin(), my_map.end(), cmp)-\u0026gt;second; all types of comparators for map and set # focus on const, * and \u0026amp; // version 1: const is required class Comparator { public: bool operator()(const int\u0026amp; a, const int\u0026amp; b) const { // must have const here return a \u0026gt; b; } }; std::map\u0026lt;int, int, Comparator\u0026gt; my_map; // version 2 auto cmp = [](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a, const std::pair\u0026lt;int, int\u0026gt;\u0026amp; b) {return a.first \u0026gt; b.first;}; std::set\u0026lt;std::pair\u0026lt;int, int\u0026gt;, decltype(cmp)\u0026gt; my_heap_with_set(cmp); // get min heap // version 3: * and \u0026amp; are required bool comparator(const int\u0026amp; a, const int\u0026amp; b) { return a \u0026gt; b; } std::map\u0026lt;int, int, decltype(comparator)*\u0026gt; my_map(\u0026amp;comparator); Comparator for sort vs map(or set) # sort uses object or cmp set or map use typename or decltype(cmp) sort # class Comparator2 { public: Comparator2(int s): s_(s) {} bool operator()(const int\u0026amp; a, const int\u0026amp; b) const { if (s_ \u0026gt; 0) { return a \u0026gt; b; } return a \u0026lt; b; } private: int s_; }; std::vector\u0026lt;int\u0026gt; vec{2, 1, 3, 7, 4}; std::sort(vec.begin(), vec.end(), Comparator2(-1)); // use object map(or set) # class Comparator { public: bool operator()(const int\u0026amp; a, const int\u0026amp; b) const { return a \u0026gt; b; } }; std::map\u0026lt;int, int, Comparator\u0026gt; my_map_; // use typename use function to get lambda or func pointer # std::function\u0026lt;bool(int, int)\u0026gt; cmp = [](int x, int y){return x \u0026gt; y;}; overload less comparator for priority queue # // leetcode 2353. Design a Food Rating System #include \u0026lt;gmock/gmock.h\u0026gt; #include \u0026lt;gtest/gtest.h\u0026gt; #include \u0026lt;queue\u0026gt; using namespace std; class Food { public: // Store the food\u0026#39;s rating. int food_rating; // Store the food\u0026#39;s name. string food_name; Food(int food_rating, string food_name) { this-\u0026gt;food_rating = food_rating; this-\u0026gt;food_name = food_name; } // Overload the less than operator for comparison bool operator\u0026lt;(const Food\u0026amp; other) const { // If food ratings are the same sort on the basis of their name. // (lexicographically smaller name food will be on top) if (food_rating == other.food_rating) { return food_name \u0026gt; other.food_name; } // Sort on the basis of food rating. (bigger rating food will be on top) return food_rating \u0026lt; other.food_rating; } }; class FoodRatings { // Map food with its rating. unordered_map\u0026lt;string, int\u0026gt; food_to_rating; // Map food with the cuisine it belongs to. unordered_map\u0026lt;string, string\u0026gt; food_to_cuisine; // Store all food of a cuisine in priority queue (to sort them on // ratings/name) Priority queue element -\u0026gt; Food: (food_rating, food_name) unordered_map\u0026lt;string, priority_queue\u0026lt;Food\u0026gt;\u0026gt; cuisine_to_food; public: FoodRatings(vector\u0026lt;string\u0026gt;\u0026amp; foods, vector\u0026lt;string\u0026gt;\u0026amp; cuisines, vector\u0026lt;int\u0026gt;\u0026amp; ratings) { for (int i = 0; i \u0026lt; foods.size(); ++i) { // Store \u0026#39;rating\u0026#39; and \u0026#39;cuisine\u0026#39; of current \u0026#39;food\u0026#39; in \u0026#39;food_to_rating\u0026#39; and // \u0026#39;food_to_cuisine\u0026#39; maps. food_to_rating[foods[i]] = ratings[i]; food_to_cuisine[foods[i]] = cuisines[i]; // Insert the \u0026#39;(rating, name)\u0026#39; element in current cuisine\u0026#39;s priority // queue. cuisine_to_food[cuisines[i]].push(Food(ratings[i], foods[i])); } } void changeRating(string food, int newRating) { // Update food\u0026#39;s rating in \u0026#39;food_rating\u0026#39; map. food_to_rating[food] = newRating; // Insert the \u0026#39;(new rating, name)\u0026#39; element in respective cuisine\u0026#39;s priority // queue. auto cuisine_name = food_to_cuisine[food]; cuisine_to_food[cuisine_name].push(Food(newRating, food)); } string highestRated(string cuisine) { // Get the highest rated \u0026#39;food\u0026#39; of \u0026#39;cuisine\u0026#39;. auto highest_rated = cuisine_to_food[cuisine].top(); // If the latest rating of \u0026#39;food\u0026#39; doesn\u0026#39;t match the \u0026#39;rating\u0026#39; on which it was // sorted in the priority queue, then we discard this element of the // priority queue. while (food_to_rating[highest_rated.food_name] != highest_rated.food_rating) { cuisine_to_food[cuisine].pop(); highest_rated = cuisine_to_food[cuisine].top(); } // Return name of the highest rated \u0026#39;food\u0026#39; of \u0026#39;cuisine\u0026#39;. return highest_rated.food_name; } }; set, find iterator, erase # class FoodRatings { // Map food with its rating. unordered_map\u0026lt;string, int\u0026gt; foodRatingMap; // Map food with the cuisine it belongs to. unordered_map\u0026lt;string, string\u0026gt; foodCuisineMap; // Store all food of cuisine in set (to sort them on ratings/name) // Set element -\u0026gt; Pair: (-1 * foodRating, foodName) unordered_map\u0026lt;string, set\u0026lt;pair\u0026lt;int, string\u0026gt;\u0026gt;\u0026gt; cuisineFoodMap; public: FoodRatings(vector\u0026lt;string\u0026gt;\u0026amp; foods, vector\u0026lt;string\u0026gt;\u0026amp; cuisines, vector\u0026lt;int\u0026gt;\u0026amp; ratings) { for (int i = 0; i \u0026lt; foods.size(); ++i) { // Store \u0026#39;rating\u0026#39; and \u0026#39;cuisine\u0026#39; of current \u0026#39;food\u0026#39; in \u0026#39;foodRatingMap\u0026#39; and // \u0026#39;foodCuisineMap\u0026#39; maps. foodRatingMap[foods[i]] = ratings[i]; foodCuisineMap[foods[i]] = cuisines[i]; // Insert the \u0026#39;(-1 * rating, name)\u0026#39; element in current cuisine\u0026#39;s set. cuisineFoodMap[cuisines[i]].insert({-ratings[i], foods[i]}); } } void changeRating(string food, int newRating) { // Fetch cuisine name for food. auto cuisineName = foodCuisineMap[food]; // Find and delete the element from the respective cuisine\u0026#39;s set. auto oldElementIterator = cuisineFoodMap[cuisineName].find({-foodRatingMap[food], food}); cuisineFoodMap[cuisineName].erase(oldElementIterator); // Update food\u0026#39;s rating in \u0026#39;foodRating\u0026#39; map. foodRatingMap[food] = newRating; // Insert the \u0026#39;(-1 * new rating, name)\u0026#39; element in respective cuisine\u0026#39;s set. cuisineFoodMap[cuisineName].insert({-newRating, food}); } string highestRated(string cuisine) { auto highestRated = *cuisineFoodMap[cuisine].begin(); // Return name of the highest rated \u0026#39;food\u0026#39; of \u0026#39;cuisine\u0026#39;. return highestRated.second; } }; ASCII value # Use tolower(my_char) to get the ASCII value of that character isalnum(my_char) # isalnum(my_char) string trim and split # // given string s // Remove leading and trailing spaces s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char ch) { return !std::isspace(ch); })); s.erase(std::find_if(s.rbegin(), s.rend(), [](char ch) { return !std::isspace(ch); }).base(), s.end()); // Split by multiple spaces std::stringstream iss(s); std::vector\u0026lt;std::string\u0026gt; wordList(std::istream_iterator\u0026lt;std::string\u0026gt;{iss}, std::istream_iterator\u0026lt;std::string\u0026gt;()); string split with customized delimiter # for string delimiter # // for string delimiter std::vector\u0026lt;std::string\u0026gt; split(std::string s, std::string delimiter) { size_t pos_start = 0, pos_end, delim_len = delimiter.length(); std::string token; std::vector\u0026lt;std::string\u0026gt; res; while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) { token = s.substr(pos_start, pos_end - pos_start); pos_start = pos_end + delim_len; res.push_back(token); } res.push_back(s.substr(pos_start)); return res; } // std::string delimiter = \u0026#34;-+\u0026#34;; // std::vector\u0026lt;std::string\u0026gt; v = split(str, delimiter); for char delimiter # // for char delimiter std::vector\u0026lt;std::string\u0026gt; split(const std::string\u0026amp; s, char delim) { std::vector\u0026lt;std::string\u0026gt; result; std::stringstream ss(s); std::string item; while (getline (ss, item, delim)) { result.push_back(item); } return result; } // std::vector\u0026lt;std::string\u0026gt; v = split(str, \u0026#39;+\u0026#39;); Log(log) # // #define print(x) \\ // std::ranges::copy(x, std::ostream_iterator\u0026lt;int\u0026gt;(std::cout, \u0026#34; \u0026#34;)); \\ // std::cout \u0026lt;\u0026lt; std::endl #define print(x) \\ for (auto\u0026amp; s : x) { \\ cout \u0026lt;\u0026lt; s \u0026lt;\u0026lt; \u0026#34; \u0026#34;; \\ } \\ cout \u0026lt;\u0026lt; endl; #define deb(...) logger(#__VA_ARGS__, __VA_ARGS__) template \u0026lt;typename... Args\u0026gt; void logger(std::string vars, Args\u0026amp;\u0026amp;... values) { std::cout \u0026lt;\u0026lt; vars \u0026lt;\u0026lt; \u0026#34; = \u0026#34;; std::string delim = \u0026#34;\u0026#34;; (..., (std::cout \u0026lt;\u0026lt; delim \u0026lt;\u0026lt; values, delim = \u0026#34;, \u0026#34;)); std::cout \u0026lt;\u0026lt; std::endl; } IsPrime # bool IsPrime(int n) { if (n \u0026lt;= 1) return false; int s_n = sqrt(n); for (int i = 2; i \u0026lt;= s_n; ++i) { if (n % i == 0) { return false; } } return true; 区间 DP # 476.Stone Game 石子归并\nAnswer\n区间 DP\n这是一道区间 DP 问题，我们需要用区间表示状态来递推。设 s 是表示石头重量的数组，设f[i][j]是将s[i,...,j]的石头合并成一个所需的最少能量，那么这个最少能量按照最后一步合并的分界线可以分为以下几种情况：\n最后一步是s[i]和s[i+1,...,j]合并，此时需要的最少能量是f[i+1][j]+sum(s[i]...s[j]),第一项是合并后者需要的能量，第二项是最后一次合并所需要的能量。s[i]自己只有一个石头，不需要合并\n最后一步是s[i,i+1]和s[i+2,...,j]合并，此时需要的最少能量是f[i][i+1]+f[i+2][j]+sum(s[i]...s[j])，第一项是合并前两个石头需要的能量，第二项是合并后半区间石头需要的能量，最后一项是最后一次合并需要的能量；\n从上面我们可以看出一个规律，f[i][j]应该是所有区间分法中前一半区间的石头合并需要的总能量加上后半区间的总能量再加上最后一次合并需要的能量\n求得 A 的前缀和 区间长度从 2 开始枚举， 根据上诉思路可得递推式 dp[l][r] =min(dp[l][r], dp[l][j] + dp[j + 1][r] + sum_a[r + 1] - sum_a[l]) 记得初始化dp[l][r]为一个较大值 结果存在dp[0][size-1]中 复杂度分析\n时间复杂度O(n^3) 区间 dp 的复杂度 空间复杂度O(n^2) dp 数组的大小 // C++ class Solution { public: int stoneGame(vector\u0026lt;int\u0026gt; \u0026amp;A) { int _size = A.size(); if (_size == 0) { return 0; } int dp[_size][_size]; int sum_a[_size+1]; //c++记得初始化 memset(sum_a, 0, sizeof(sum_a)); memset(dp, 0, sizeof(dp)); //前缀和 for (int i = 0; i \u0026lt; _size; i++) { sum_a[i + 1] = sum_a[i] + A[i]; } // 长度从2开始即可，因为长度为1的时候结果是0 for (int len = 2; len \u0026lt;= _size; len++) { // i枚举的是正在枚举的区间的左端点 for (int i = 0; i + len - 1 \u0026lt; _size; i++) { // 正在枚举的区间左端点是i，右端点是i + size - 1 int l = i, r = i + len - 1; // 在求最小的时候，需要初始化成一个很大的数，然后不断更新 dp[l][r] = INT_MAX; for (int j = l; j \u0026lt; r; j++) { //递推式 dp[l][r] = min(dp[l][r], dp[l][j] + dp[j + 1][r] + sum_a[r + 1] - sum_a[l]); } } } return dp[0][_size - 1]; } }; # Python class Solution: def stoneGame(self, A): import sys size = len(A) if size == 0: return 0; dp = [[0 for _ in range(size)] for _ in range(size)] sum_a = [0] * (size + 1) #前缀和 for i in range(size): sum_a[i + 1] = sum_a[i] + A[i] #长度从2开始即可，因为长度为1的时候结果是0，dp初始化的时候默认就是0，没必要赋值 for _len in range(2,size + 1): #i枚举的是正在枚举的区间的左端点 for i in range(size + 1 - _len): #正在枚举的区间左端点是i，右端点是i + size - 1 l,r = i,i + _len - 1 #在求最小的时候，需要初始化成一个很大的数，然后不断更新 dp[l][r] = sys.maxsize for j in range(l, r): #递推式 dp[l][r] = min(dp[l][r], dp[l][j] + dp[j + 1][r] + sum_a[r + 1] - sum_a[l]) return dp[0][size - 1] How to use heap in c++ # #include \u0026lt;iostream\u0026gt; #include \u0026lt;set\u0026gt; #define assertm(exp, msg) assert(((void)msg, exp)) #define print(input) for (auto\u0026amp; elem : input) std::cout \u0026lt;\u0026lt; elem \u0026lt;\u0026lt; std::endl int main() { auto cmp = [](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a, const std::pair\u0026lt;int, int\u0026gt;\u0026amp; b) {return a.second \u0026lt; b.second;}; std::set\u0026lt;std::pair\u0026lt;int, int\u0026gt;, decltype(cmp)\u0026gt; heap; heap.insert(std::make_pair(1, 3)); heap.insert(std::make_pair(31, 1)); heap.insert(std::make_pair(4, 4)); heap.insert(std::make_pair(2, 2)); heap.insert(std::make_pair(5, 5)); auto it = heap.begin(); it = std::next(it, 2); it = std::prev(it, 1); std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;size: \u0026#34; \u0026lt;\u0026lt; heap.size() \u0026lt;\u0026lt; std::endl; int index = 31; auto it2 = std::find_if(heap.begin(), heap.end(), [\u0026amp;index](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a) {return a.first == index;}); heap.erase(it2); it = heap.begin(); std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;size: \u0026#34; \u0026lt;\u0026lt; heap.size() \u0026lt;\u0026lt; std::endl; return 0; } 1507 Shortest Subarray with Sum at Least K 和至少为 K 的最短子数组 # Lintcode 1507 Shortest Subarray with Sum at Least K Binary search on answer + priority_queue # class Solution { public: int shortestSubarray(std::vector\u0026lt;int\u0026gt;\u0026amp; A, int K) { std::vector\u0026lt;int\u0026gt; prefix_sum = GetPrefixSum(A); int left = 1; int right = A.size(); while (left + 1 \u0026lt; right) { int mid = left + (right - left) / 2; if (IsValid(prefix_sum, mid, K)) { right = mid; } else { left = mid; } } if (IsValid(prefix_sum, left, K)) { return left; } if (IsValid(prefix_sum, right, K)) { return right; } return -1; } private: std::vector\u0026lt;int\u0026gt; GetPrefixSum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;int\u0026gt; answer(nums.size() + 1, 0); for (int i = 0; i \u0026lt; nums.size(); ++i) { answer[i + 1] = answer[i] + nums[i]; } return answer; } bool IsValid(std::vector\u0026lt;int\u0026gt;\u0026amp; prefix_sum, int length, int K) { auto cmp = [](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a, const std::pair\u0026lt;int, int\u0026gt;\u0026amp; b) { return a.second \u0026gt; b.second; }; std::set\u0026lt;std::pair\u0026lt;int, int\u0026gt;, decltype(cmp)\u0026gt; pq(cmp); // c++20 pq; c++11 pq(cmp) for (int end = 0; end \u0026lt; prefix_sum.size(); ++end) { int index = end - length - 1; if (index \u0026gt;= 0) { pq.erase(std::find_if(pq.begin(), pq.end(), [\u0026amp;index](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a) { return a.first == index; })); } if (!pq.empty() \u0026amp;\u0026amp; prefix_sum[end] - pq.rbegin()-\u0026gt;second \u0026gt;= K) { return true; } pq.insert(std::make_pair(end, prefix_sum[end])); } return false; } }; auto cmp has to be \u0026gt; Do binary search on answer, and then checking validation of the current answer on prefixsum vector to speed up the runnint time we can use lazy deletion to change delete operation of heap into an O(logn) operation Time Complexity of binary search + heap is O(n(logn)^2)\nLeetcode 1337.The K Weakest Rows in a Matrix # class Solution { public: std::vector\u0026lt;int\u0026gt; kWeakestRows(std::vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; mat, int k) { auto cmp = [](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a, const std::pair\u0026lt;int, int\u0026gt;\u0026amp; b) { if (a.second == b.second) { return a.first \u0026lt; b.first; } return a.second \u0026lt; b.second; }; std::set\u0026lt;std::pair\u0026lt;int, int\u0026gt;, decltype(cmp)\u0026gt; pq(cmp); for (int i = 0; i \u0026lt; mat.size(); ++i) { pq.insert( std::make_pair( i, std::accumulate(mat[i].begin(), mat[i].end(), 0) ) ); } std::vector\u0026lt;int\u0026gt; answer; auto it = pq.begin(); while (k--) { answer.push_back(it-\u0026gt;first); it = std::next(it, 1); } return answer; } }; multiset in C++ # Unlike sets, multisets can store duplicate elements in a sorted manner. The elements inside the multiset cannot be changed, once they are added to the multiset, they can only be inserted or deleted. A multiset is present in #include header file. The elements inside the multiset can be accessed using iterators.\n// example multiset \u0026lt;int\u0026gt; s; //initializes a multiset of size 0 which stores integer values arranged in non-decreasing order multiset \u0026lt;int\u0026gt; s = { 10, 20, 30 }; //initializes a multiset having initial values as 10,20,30 multiset \u0026lt;int, greater \u0026lt;int\u0026gt;\u0026gt; s; //initializes a multiset of size 0 which stores integer values arranged in non-increasing order begin(): Returns an iterator to the first element of the multiset. Parameters: None Return type: iterator end(): Returns an iterator to the element past the last element of the multiset. Parameters: None Return type: iterator size(): It tells us the size of the multiset. Parameters: None Return type: integer - total number of elements in the multiset insert(element): Inserts an element in the multiset. Time Complexity: O(logN) where N is the size of the multiset Parameters: the element to be inserted Return type: void erase(value) or erase(start_iterator,end_iterator): Delete elements from the multiset. Time Complexity: O(logN) where N is the size of the multiset Parameters: the value to be removed or iterators pointing to the position between which the value needs to be deleted Return type: void find(element): Returns an iterator pointing to the element, if the element is found else returns an iterator pointing to the end of the multiset. Parameters: the element which needs to be found Return type: iterator clear(): It deletes all the elements from the multiset Parameters: None Return type: void empty(): It tells us whether the multiset is empty or not. Parameters: None Return type: Boolean, true if a multiset is empty else false #include\u0026lt;iostream\u0026gt; #include\u0026lt;set\u0026gt; using namespace std; int main() { multiset \u0026lt;int\u0026gt; s1; multiset \u0026lt;int, greater\u0026lt;int\u0026gt;\u0026gt; s2; for (int i = 0; i \u0026lt; 5; i++) { s1.insert(i + 1); } for (int i = 0; i \u0026lt; 5; i++) { s1.insert(i + 1); } for (int i = 0; i \u0026lt; 5; i++) { s2.insert((i + 1) * 10); } for (int i = 0; i \u0026lt; 5; i++) { s2.insert((i + 1) * 10); } set \u0026lt;int\u0026gt; ::iterator it; for (it = s1.begin(); it != s1.end(); it++) cout \u0026lt;\u0026lt; * it \u0026lt;\u0026lt; \u0026#34; \u0026#34;; cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; for (it = s2.begin(); it != s2.end(); it++) cout \u0026lt;\u0026lt; * it \u0026lt;\u0026lt; \u0026#34; \u0026#34;; cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; s1.erase(1); s2.erase(s2.begin(), s2.find(10)); cout \u0026lt;\u0026lt; \u0026#34;After erasing element, size of set1 is \u0026#34; \u0026lt;\u0026lt; s1.size() \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; int val = 4; if (s1.find(val) != s1.end()) cout \u0026lt;\u0026lt; \u0026#34;The set1 contains \u0026#34; \u0026lt;\u0026lt; val \u0026lt;\u0026lt; endl; else cout \u0026lt;\u0026lt; \u0026#34;The set1 does not contains \u0026#34; \u0026lt;\u0026lt; val \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;New elements of set1 are \u0026#34;; for (it = s1.begin(); it != s1.end(); it++) cout \u0026lt;\u0026lt; * it \u0026lt;\u0026lt; \u0026#34; \u0026#34;; cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; s1.clear(); if (s1.empty() == true) { cout \u0026lt;\u0026lt; \u0026#34;set1 is empty!\u0026#34;; } return 0; } /* 1 1 2 2 3 3 4 4 5 5 50 50 40 40 30 30 20 20 10 10 After erasing element, size of set1 is 8 The set1 contains 4 New elements of set1 are 2 2 3 3 4 4 5 5 set1 is empty! */ inblock dfs in C++ # Leetcode 377 class Solution { public: int combinationSum4(vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { vector\u0026lt;int\u0026gt; memo(target + 1, -1); // -1 表示没有计算过 auto dfs = [\u0026amp;](this auto\u0026amp;\u0026amp; dfs, int i) { if (i == 0) { // 爬完了 return 1; } int \u0026amp;res = memo[i]; // 注意这里是引用 if (res != -1) { // 之前计算过 return res; } res = 0; for (int x : nums) { if (x \u0026lt;= i) { res += dfs(i - x); } } return res; }; return dfs(target); } }; C++ isalnum, isalpha, isdigit # isalnum checks whether c is either a decimal digit or an uppercase or lowercase letter. The result is true if either isalpha or isdigit would also return true. 2. ML # Linear regression # Logistic regression # Decision tree # SVM algorithm # Naive Bayes algorithm # KNN algorithm # K-means # Random forest algorithm # Dimensionality reduction algorithms # Gradient boosting algorithm and AdaBoosting algorithm # 3. Projects # Pthread Prefix Sum # GPU K-means # Tree Comparison # Two Phase Commit Protocol # MPI Barnes-hut # https://www.youtube.com/watch?v=m9f6CoToIGU ","date":"11 February 2024","externalUrl":null,"permalink":"/notes/al_note/","section":"Notes","summary":" Templates Binary Search 二分法 Two Pointers 双指针 Sorting 排序算法 Quick Select Iteratively + Recursively Binary Tree Divide \u0026 Conquer 二叉树分治 BST Iterator 二叉搜索树非递归 BFS 宽度优先搜索 DFS 深度优先搜索 Dynamic Programming 动态规划 Heap 堆 Prioirty Queue: Union Find 并查集 Trie 字典树 Red-Black Tree Basics Rotations: O(1) Insertions(strategy) Data Structure Implementations LCS: Longest Commen Subsequence LCA: Lowest Common Ancestor Example: Lintcode 88 LCA TSP: MST: Minimon Spinning Tree LRU: Least Recently Used LIS: Longest Increasing Subsequence DP - LIS LIS 的动态规划四要素 Binary Search - LIS LIS2: Longest Continuous Increasing Subsequence 2 LDS: Largest Divisible Subset HashMap Implementation Other Notes Reverse Linked List 通过数据范围推测算法 背诵贪心算法 Python String methods C++ string methods 时间复杂度算法列表 跟面试官核实 BFS 的使用场景 BFS 的使用场景（summer） 以下哪些问题 BFS 可以处理： BFS 的三种实现方法 二叉树的 BFS vs 图的 BFS： Recursion/ DFS/ Backtracking: 遍历法 vs 分治法： 平衡二叉树 计算深度 Binary Search Tree 二叉查找树： BST 基本操作： Delete Node in a BST Red-Black Tree 红黑树： 二叉树三种遍历： “二叉树的中序遍历”的非递归实现 Prefix Sum Prefix Product, Suffix Product 使用前缀和数组在 O(1)的时间复杂度内计算子数组和 解决最短路径的算法： time \u0026 space compelxity of recursive: 遇到二叉树的问题，就想想整棵树在该问题上的结果和左右孩子在该问题上的结果之间有什么联系 拓扑排序 Topological Sorting: 拓扑排序的四种不同问法： Others Quick Select GCD - Greatest Common Divisor lower_bound vs upper_bound string find, mismatch string find mismatch assert try throw catch - error handling gtest with cmake step 1: step 2: CMakeLists.txt step 3: test fucntions step 4: Append to CMakeLists.txt step 5: build and run test print vector to the console priority queue Binary search on answer + priority_queue LRU LIS LIS 的动态规划四要素 LIS2 Largest Divisible Subset HashMap Implementation sort lambda customized hash for unordered_map or unordered_set function pointer in c++ element wise comparison of two structs how to use c++ build-in hash function c++ const random seed C++20 comparison operator To initialize two dimentional array heap: set vs priority_queue heap with multiset, erase with find return min or max element from hashmap all types of comparators for map and set Comparator for sort vs map(or set) sort map(or set) use function to get lambda or func pointer overload less comparator for priority queue set, find iterator, erase ASCII value isalnum(my_char) string trim and split string split with customized delimiter for string delimiter for char delimiter Log(log) IsPrime 区间 DP How to use heap in c++ 1507 Shortest Subarray with Sum at Least K 和至少为 K 的最短子数组 Binary search on answer + priority_queue Leetcode 1337.The K Weakest Rows in a Matrix multiset in C++ C++ isalnum, isalpha, isdigit 2. ML Linear regression Logistic regression Decision tree SVM algorithm Naive Bayes algorithm KNN algorithm K-means Random forest algorithm Dimensionality reduction algorithms Gradient boosting algorithm and AdaBoosting algorithm 3. Projects Pthread Prefix Sum GPU K-means Tree Comparison Two Phase Commit Protocol MPI Barnes-hut Templates # Binary Search 二分法 # 使用条件\n","title":"Algo Note","type":"notes"},{"content":"Z Algo template # vector\u0026lt;int\u0026gt; ZAlgo(const string\u0026amp; input) { vector\u0026lt;int\u0026gt; Z(input.size()); int left = 0; int right = 0; for (int k = 1; k \u0026lt; input.size(); k++) { if (k \u0026gt; right) { left = right = k; while (right \u0026lt; input.size() \u0026amp;\u0026amp; input[right] == input[right - left]) { right++; } Z[k] = right - left; right--; } else { // we are operating inside box int k1 = k - left; // if value does not stretches till right bound then just copy it. if (Z[k1] \u0026lt; right - k + 1) { Z[k] = Z[k1]; } else { // otherwise try to see if there are more matches. left = k; while (right \u0026lt; input.size() \u0026amp;\u0026amp; input[right] == input[right - left]) { right++; } Z[k] = right - left; right--; } } } return Z; } }; Example: Z Algo # Leetcode 3036. Number of Subarrays That Match a Pattern II the Z-function for a string s is an array where the ith element z[i] represents the length of the longest substring starting from i that is also a prefix of s.\nZ function template # vector\u0026lt;int\u0026gt; z_function(const string \u0026amp;s) { int n = s.size(); vector\u0026lt;int\u0026gt; z(n); z[0] = n; for (int i = 1, l = 0, r = 0; i \u0026lt; n; i++) { if (i \u0026lt;= r) z[i] = min(z[i - l], r - i + 1); for (int \u0026amp;j = z[i]; i + j \u0026lt; n \u0026amp;\u0026amp; s[j] == s[i + j]; j++); if (z[i] \u0026gt; r - i + 1) l = i, r = i + z[i] - 1; } return z; } Example: Z Function # Leetcode 3031. Minimum Time to Revert Word to Initial State II class Solution { public: int minimumTimeToInitialState(string word, int k) { // z function auto z = z_function(word); // auto z = z_function_trivial(word); for (int i = k; i \u0026lt; word.size(); i += k) { // if (word.substr(i, word.size() - i) == word.substr(0, word.size() - i)) // return i / k; if (z[i] == word.size() - i) return i / k; } return ceil((double)word.size() / k); } vector\u0026lt;int\u0026gt; z_function(const string \u0026amp;s) { int n = s.size(); vector\u0026lt;int\u0026gt; z(n); z[0] = n; for (int i = 1, l = 0, r = 0; i \u0026lt; n; i++) { if (i \u0026lt;= r) z[i] = min(z[i - l], r - i + 1); for (int \u0026amp;j = z[i]; i + j \u0026lt; n \u0026amp;\u0026amp; s[j] == s[i + j]; j++); if (z[i] \u0026gt; r - i + 1) l = i, r = i + z[i] - 1; } return z; } vector\u0026lt;int\u0026gt; z_function_trivial(string s) { int n = s.size(); vector\u0026lt;int\u0026gt; z(n); for (int i = 1; i \u0026lt; n; i++) { while (i + z[i] \u0026lt; n \u0026amp;\u0026amp; s[z[i]] == s[i + z[i]]) { z[i]++; } } return z; } };","date":"10 February 2024","externalUrl":null,"permalink":"/blog/z-function/","section":"Blog","summary":"","title":"Z Algorithm","type":"blog"},{"content":" How to read source code in Github # Node and Angular Version Control # Check Current Angular and Node.js Versions # node -v ng version Upgrade or Downgrade Angular # ng update @angular/cli @angular/core npm uninstall @angular/cli @angular/core npm install @angular/cli@12 @angular/core@12 Reinstall Node Modules # After upgrading or downgrading Angular, it’s a good practice to remove the node_modules folder and reinstall dependencies to ensure everything works with the new version: rm -rf node_modules npm install Concepts # template reference variable # Useful: child component can interact with or manipulate the parent\u0026rsquo;s HTMLElement \u0026lt;input #text /\u0026gt; \u0026lt;app-child [inputRef]=\u0026#34;text\u0026#34;\u0026gt;\u0026lt;/app-child\u0026gt; child component use @Input to receive the reference of HTMLInputElement\ndynamic properties vs dynamic attributes # dynamic attributes: for custom HTML attributes which are not standard DOM properties we need to prepend the custom HTML attribute with the attr. prefix. \u0026lt;button [attr.data-test-id]=\u0026#34;testId\u0026#34;\u0026gt;Primary CTA\u0026lt;/button\u0026gt; the declared value in square bracket should be interpreted as a Javascript-like statement.\nevent # syntax: (click)=\u0026quot;save()\u0026quot;, interpreted as event trigger the Javascript-like statement. 1. Google Official Youtube Tutorial # Tutorial # install angular npm install -g @angular/cli new project # ng new project-name cd project-name ng serve Tutorial Project # Github # setup cd homes-app npm install ng serve # generate component ng generate component Home --standalone --inline-template switch angular version # npm uninstall -g @angular/cli npm cache verify # if the there is any issue npm cache clean --force # install the older version of angular npm install -g @angular/cli@8.3.19 2. Youtube Tutorial 2024 # ng generate component home ng generate service services/api Fetching Data From Server (Services \u0026amp; Endpoints) # 3. Todo List Project # build file structures of project # ng g s services/api ng g c components/home ng g class models/Todo setup bootstrap # \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;TodolistFrontendAngular12\u0026lt;/title\u0026gt; \u0026lt;base href=\u0026#34;/\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;icon\u0026#34; type=\u0026#34;image/x-icon\u0026#34; href=\u0026#34;favicon.ico\u0026#34;\u0026gt; \u0026lt;!-- Bootstrap setup --\u0026gt; \u0026lt;link href=\u0026#34;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css\u0026#34; rel=\u0026#34;stylesheet\u0026#34; integrity=\u0026#34;sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH\u0026#34; crossorigin=\u0026#34;anonymous\u0026#34;\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body class=\u0026#34;bg-light\u0026#34;\u0026gt; \u0026lt;app-root\u0026gt;\u0026lt;/app-root\u0026gt; \u0026lt;!-- Bootstrap setup --\u0026gt; \u0026lt;script src=\u0026#34;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js\u0026#34; integrity=\u0026#34;sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz\u0026#34; crossorigin=\u0026#34;anonymous\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Coding # export class Todo { id?: number = 0; text: string = \u0026#39;\u0026#39;; isComplete: number = 0; } \u0026lt;app-home\u0026gt;\u0026lt;/app-home\u0026gt; import { NgModule } from \u0026#39;@angular/core\u0026#39;; import { BrowserModule } from \u0026#39;@angular/platform-browser\u0026#39;; // manually added import { HttpClientModule } from \u0026#39;@angular/common/http\u0026#39; import { AppRoutingModule } from \u0026#39;./app-routing.module\u0026#39;; import { AppComponent } from \u0026#39;./app.component\u0026#39;; import { HomeComponent } from \u0026#39;./components/home/home.component\u0026#39;; import {FormsModule} from \u0026#34;@angular/forms\u0026#34;; @NgModule({ declarations: [ AppComponent, HomeComponent ], imports: [ BrowserModule, AppRoutingModule, // manually added FormsModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } import { Injectable } from \u0026#39;@angular/core\u0026#39;; import {HttpClient} from \u0026#34;@angular/common/http\u0026#34;; @Injectable({ providedIn: \u0026#39;root\u0026#39; }) export class ApiService { serviceURL: string; constructor(private http: HttpClient) { this.serviceURL = \u0026#34;http://localhost:8080\u0026#34;; } } search input group in bootstrap doc website \u0026lt;div class=\u0026#34;container-fluid bg-light\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;container bg-light\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;row mt-4\u0026#34; style=\u0026#34;height: 500px\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;col\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;col-md-6 bg-white shadow\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;card bg-warning mt-4\u0026#34;\u0026gt; \u0026lt;h4 class=\u0026#34;text-white ps-3 pt-2 pb-2\u0026#34;\u0026gt;Todo List\u0026lt;/h4\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;shadow\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;input-group p-4\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; class=\u0026#34;form-control\u0026#34; placeholder=\u0026#34;Enter todo \u0026#34;\u0026gt; \u0026lt;button class=\u0026#34;btn btn-outline-success\u0026#34; type=\u0026#34;button\u0026#34;\u0026gt;Add\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;h4 class=\u0026#34;text-primary mt-4\u0026#34;\u0026gt;Task: \u0026lt;/h4\u0026gt; \u0026lt;div style=\u0026#34;overflow-y: auto; height: 350px\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;m-3\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;p-2 shadow border\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;input-group row ps-3\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;card col-md-8 border-0\u0026#34;\u0026gt;Test text\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;btn btn-outline-primary col me-2\u0026#34;\u0026gt;Edit\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;btn btn-outline-danger col\u0026#34;\u0026gt;Delete\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;col\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; back to service, add some methods import { Injectable } from \u0026#39;@angular/core\u0026#39;; import {HttpClient} from \u0026#34;@angular/common/http\u0026#34;; import { Todo } from \u0026#39;../models/todo\u0026#39;; import {Observable} from \u0026#34;rxjs\u0026#34;; @Injectable({ providedIn: \u0026#39;root\u0026#39; }) export class ApiService { serviceURL: string; constructor(private http: HttpClient) { this.serviceURL = \u0026#34;http://localhost:8081/reminders\u0026#34;; } // add some methods addTodo(todo: Todo): Observable\u0026lt;Todo\u0026gt; { return this.http.post\u0026lt;Todo\u0026gt;(this.serviceURL, todo); } getAllTodos(): Observable\u0026lt;Todo[]\u0026gt; { return this.http.get\u0026lt;Todo[]\u0026gt;(this.serviceURL); } deleteTodo(todo: Todo): Observable\u0026lt;Todo\u0026gt; { return this.http.delete\u0026lt;Todo\u0026gt;(`${this.serviceURL}/${todo.id}`); } updateTodo(todo: Todo): Observable\u0026lt;Todo\u0026gt; { return this.http.put\u0026lt;Todo\u0026gt;(this.serviceURL + `/${todo.id}`, todo); } } inject service into home component, and then define some variables and methods import { Component, OnInit } from \u0026#39;@angular/core\u0026#39;; import {ApiService} from \u0026#34;../../services/api.service\u0026#34;; import {Todo} from \u0026#34;../../models/todo\u0026#34;; import {Observable} from \u0026#34;rxjs\u0026#34;; @Component({ selector: \u0026#39;app-home\u0026#39;, templateUrl: \u0026#39;./home.component.html\u0026#39;, styleUrls: [\u0026#39;./home.component.css\u0026#39;] }) export class HomeComponent implements OnInit { todoObj: Todo = new Todo(); todoList: Todo[] = []; addTodoText: string = \u0026#39;\u0026#39;; constructor(private apiService: ApiService) { } ngOnInit(): void { this.todoObj = new Todo(); this.todoList = []; this.getAllTodos(); } addTodo(todo: Todo) { this.apiService.addTodo(todo).subscribe(res =\u0026gt; { this.ngOnInit(); }, err =\u0026gt; { alert(err); }); } getAllTodos() { this.apiService.getAllTodos().subscribe(res =\u0026gt; { this.todoList = res; }, err =\u0026gt; { alert(\u0026#34;Unable to get todo list.\u0026#34;); }); } updateTodo(todo: Todo) { this.apiService.updateTodo(todo).subscribe(res =\u0026gt; { this.ngOnInit(); }, err =\u0026gt; { alert(\u0026#34;Unable to update todo.\u0026#34;); }); } deleteTodo(todo: Todo) { this.apiService.deleteTodo(todo).subscribe(res =\u0026gt; { this.ngOnInit(); }, error =\u0026gt; { alert(\u0026#34;Unable to delete todo.\u0026#34;); }); } } focus on home.component.html and home.component.ts name=\u0026quot;todo\u0026quot; bind [(ngModel)]=\u0026quot;addTodoText\u0026quot; update home.component.ts: add *ngFor=\u0026quot;let todo of todoList\u0026quot; add click handlers search Modal in bootstrap doc, and update parts add variable within .ts file, updateTodoText: string = ''; Advanced topics # input vs ng-content(simple string vs different html mark up)\n\u0026lt;ng-content select=\u0026quot;input, textarea” /\u0026gt;\nonly extend built-in element like button[buttonAttribute]\n:host (host element)\n? how to check setInterval running in background\nprivate interval?: ReturnType\u0026lt;typeof setInterval\u0026gt;;\nclearTimeout(this.interval);\nForm: get value:\ntwo way binding\ntemplate variable(pros: not update on every keystroke behind the scenes)\nonSomthing(template variables) // with event viewChild(class name | template vraibel name string) // without event @ViewChild(‘form’) private form?: ElementRef; // or private form = viewChild.required\u0026lt;ElementRef\u0026gt;(ButtonComponent); // 17.3 or after // return a signal // constructor cannot access form this.form?.nativeElement.reset(); // executing after ? if this.form is not undefined.\nContentChild vs ViewChild\n(ng-content child vs real exist child)\n@ViewChild cannot used in ngOnInit but can be used in ngAfterViewInit and other method triggers within template.\n@Output() add = new EventEmitter\u0026lt;{title: string; text: string}\u0026gt;(); add = output\u0026lt;{title: string; text: string}\u0026gt;(); private el = inject(ElementRef); // inject host element @ContentChild('input') private control?: ElementRef\u0026lt; HTMLInputElement | HTMLTextAreaElement \u0026gt;;\nprivate control = contentChild\u0026lt;ElementRef\u0026lt;HTMLInputElement | HTMLTextAreaElement\u0026gt;\u0026gt;('input');\nhost: { class: \u0026#39;control\u0026#39;, \u0026#39;(click)\u0026#39;: \u0026#39;onClick()\u0026#39;, }, @HostBinding('class') className = 'control'; @HostListener('click') onClick() { console.log('Clicked!'); } @for (ticket of tickets; track ticket.id) { \u0026lt;li\u0026gt; \u0026lt;app-ticket /\u0026gt; {{ $first }} {{ $last }} {{ $even }} {{ $odd }} {{ $count }} \u0026lt;/li\u0026gt; } @empty { \u0026lt;p\u0026gt;No tickets available.\u0026lt;/p\u0026gt; } signal: read once in .ts file\nsignal() signal: set subscription in .ts file\neffect((cleanUp) =\u0026gt; {}); signal: set vs update\nsignal.update((oldValue) =\u0026gt; newValue); \u0026hellip; operator in js: keep old properties, and overriding status key.\nthis.tickets = this.tickets.map((ticket) =\u0026gt; { if (ticket.id === id) { return { ...ticket, status: \u0026#39;closed\u0026#39; } } return ticket; }); in template, dynamically bind css style, true/false \u0026lt;div [class]=\u0026#34;{ \u0026#39;ticket-open\u0026#39;: data().status === \u0026#39;open\u0026#39;, \u0026#39;ticket-closed\u0026#39;: data().status === \u0026#39;closed\u0026#39; }\u0026#34; \u0026gt;\u0026lt;/div\u0026gt; // {} is configuration\ninput configuration difference in input vs input.require (null, {}) vs ({}) @Input configuration @Input({}) // how to setup configuration for input\nalias(avoid in best practice), inside component is just property name, outside use alias name transform: (inputValue) =\u0026gt; {// some transformed value} // how to configuration for output\nonly has alias @Output('aliasNameOutsideOfComponent')\npropertyName = output({alias: 'aliasNameOutsideOfComponent'})\nwe can unlock ngModule with FormModule, for two-way-binding\ntwo-way-binding can use signal or non-signal properties\nTo make component two-way-bindable // approach 1: version \u0026lt; 17.2\nexport class RectComponent{ @Input({required: true}) size!: { width: string; height: string }; @Output() sizeChange = new EventEmitter\u0026lt;{ width: string; height: string }\u0026gt;(); // must follow name rule here onReset() { this.sizeChange.emit({ width: \u0026#39;200\u0026#39;, height: \u0026#39;100\u0026#39; }); } } // in parent template \u0026lt;app-rect [(size)]=\u0026#34;rectSize\u0026#34; /\u0026gt; // approach 2: version \u0026gt;= 17.2\nexport class RectComponent{ size = model.required\u0026lt;{ width: string; height: string }\u0026gt;(); onReset() { // we can use set or update here this.size.set({ width: \u0026#39;200\u0026#39;, height: \u0026#39;100\u0026#39; }); } } attribute vs strutural directive\nonly change the behavior of the element change the structure of DOM window.confirm(\u0026ldquo;Do you want to leave the app?\u0026rdquo;);\nevent.preventDefault();\ntypescript type casting\nconst address = (event.target as HTMLAnchorElement).href queryParam = input(\u0026lsquo;myapp\u0026rsquo;, { alias: \u0026lsquo;appSafeLink\u0026rsquo; });\nexport type Permission = \u0026lsquo;admin\u0026rsquo; | \u0026lsquo;user\u0026rsquo; | \u0026lsquo;guest\u0026rsquo;;\nNo, an effect in Angular does not automatically subscribe to all signals within a component. Instead, an effect only reacts to signals it accesses directly within its function scope.\nattribute directive\nhost:{ \u0026lsquo;(click)\u0026rsquo;: \u0026lsquo;onConfirmLeavePage($event)\u0026rsquo;, } queryParam = input(\u0026lsquo;myapp\u0026rsquo;, { alias: \u0026lsquo;appSafeLink\u0026rsquo; }); private hostElementRef = inject\u0026lt;ElementRef\u0026gt;(ElementRef); structural directive always use ng-template, * will use it behind the scenes automatically\nasterisk is just a syntactic sugar for automatically adding ng-template element behind the scenes an super important step is : private templateRef = inject(TemplateRef); // give access to the content of the template last super important step is : private viewContainerRef = inject(ViewContainerRef); // give access to the place in the DOM where this directive is being used. so where this template is being used. this.viewContainerRef.createEmbeddedView(this.templateRef); this.viewContainerRef.clear(); asterisk is not just syntactic sugar, it also setup property binding, with typescript code. we should use * appAuth=\u0026quot;\u0026lsquo;admin\u0026rsquo;\u0026quot; to put string. we can use hostDirectives within @Dicrective to build some layers\nif (typeof value === 'string'){} // js\nval = parseFloat(value);// convert from string to number\nreturn ${outputTemp} F``\ntempPipe: 'param1' : 'param2'\nconst sorted = [\u0026hellip;value];\nsorted.sort();\nin pipe, transform method only check if there is any change\nin pipe, by setting pure:false within decorator, it will disable caching mechanism of the pipe.\nlet pipe execute very time anything changed in the template.\nasc: sorted.sort((a, b) =\u0026gt; a \u0026gt; b ? 1 : -1);\ndesc: sorted.sort((a, b) =\u0026gt; a \u0026gt; b ? -1 : 1);\npipe best practice is only transforming what user sees\nservices best practice:\nprivate tasks = signal\u0026lt;Task[]\u0026gt;([]); allTasks = this.tasks.asReadonly(); map will produce a new array\nwe should always create a new instead of updating in place\nupdateTaskStatus(taskId: string, newStatus: TaskStatus) { this.tasks.update((oldTasks) =\u0026gt; oldTasks.map((task) =\u0026gt; task.id === taskId ? { ...task, status: newStatus } : task ) ); } computed will return a new signal\neffect and computed will do subscription\ncomponent and directive can access element injector\nservice cannot access element injector instead it only have access environment injector or module injector\nHow to register customized provider for anything\nexport const TASK_STATUS_OPTIONS = new InjectionToken\u0026lt;TaskStatusOptions\u0026gt;( \u0026#39;task-status-options\u0026#39; ); export const taskStatusOptionsProvider: Provider = { provide: TASK_STATUS_OPTIONS, useValue: TaskStatusOptions }; // in component providers: [taskStatusOptionsProvider] private taskStatusOptinos = inject(TASK_STATUS_OPTIONS); not put expensive calculations into template. e.g. in get() property\nThe expressions that used in template bindings should be simple and straightforward.\nonly use function invocation for singal and event binding for getters, make sure it only has efficient calculations pipe transformation values are cached by default.\nbecause pipes are just functions that are executed when templates are being evaluated. and therefore, by default, Angular caches the results generated by those pipe transform methods. Avoiding Zone Pollution // outside of Angular\u0026rsquo;s change detection. // outside of the zone.js watch mode.\nprivate zone = inject(NgZone); this.zone.runOutsideAngular(() =\u0026gt; { // ... }); OnPush happens when:\nsome events occurred anywhere inside of sub component tree an input value changed where we set OnPush. manually trigger change detection: private cdRef = inject(ChangeDetectorRef); const subscription = this.messageService.message$.subscribe((messages) =\u0026gt; { this.messages = messages this.cdRef.markForCheck(); // trigger component check change }); signal changes to clean up RxJS subscription\nngOnDestroy Hook DestroyRef private destroyRef = inject(DestroyRef); // register a function that should be executed if this componnet is about to be destroyed this.destroyRef.onDestroy(() =\u0026gt; { subscription.unsubscribe(); // will clean up subscriptino if the component here should be removed. }); in template messages$ | async\nimports: [AsyncPipe]\nautomatically setup and clean up that subscription and read those values from the subject, also trigger change detection for this component when new values are received\ntrigger change detection without zone.js for this component\nsignal, built-in angular event binding, built-in angular manually triggering change detection via ChangeDetectorRef setTimeout monitored by zone.js\nRxJS:\nwe need to subscribe to kick off the observables. signal(values in container) vs observable(values over time)\nsignals are great for managing application state observables are great for managing events \u0026amp; streamed data signal built-in angular observable has leaner code for interval observable only executed when it has at least one subscriber, whereas signal always there convert between signal and observable with toObservalbe and toSignal\nsignals always have initial value\nobservables can have initial value\ntoSignal will setup an undefined value for signal by default, but we can setup initial value in configuration object\ntoSignal has one nice thing that it will automatically cleanup observable subscription\ncatchError((error, obj) =\u0026gt; {}) in observable pipe must return a new observable\nComponent structure: 12-http-12-interceptors\nComponent \u0026amp; Template driven form\nname=\u0026ldquo;required\u0026rdquo; ngModel // name is required for angular to manage it Template driven approach: we wanna do all the setup and configuration inside of the template. # Angular managed Form\n// ngForm change the type into NgForm instead of HTMLFormElement \u0026lt;form #form=\u0026#34;ngForm\u0026#34; (ngSubmit)=\u0026#34;onSubmit(form)\u0026#34;\u0026gt; \u0026lt;input id type name ngModel/\u0026gt; // no two-way-binding, extract values only form submitted \u0026lt;/form\u0026gt; validation with attributes or directives\nrequired email required minlength=\u0026quot;6\u0026quot; min pattern cons: when using template driven approach, the angular form object isn\u0026rsquo;t available the first time the template is being rendered.\ncons: instead, this template defines the form structure, so it\u0026rsquo;s only available thereafter.\ncons: if you try to access control info inside of the template, it won\u0026rsquo;t work.\nsolution: use template variable #email=\u0026quot;ngModel\u0026quot; this syntax is supported by ngModel directive. To get control information\nNote 1: To get control specific information #email=\u0026quot;ngModel\u0026quot;\nNote 2: To get form information form\nng-pristine tells whether this field has received any input from the user or not. if it is added, it has not received any input.\nng-invalid or ng-valid tells valid or not\ne.g. @if (email.touched \u0026amp;\u0026amp; email.dirty \u0026amp;\u0026amp; email.invalid) {}\nconstructor() { afterNextRender(() =\u0026gt; {}); // to register a function that should be executed once. once this component has been rendered for the first time. // because it\u0026#39;s template-driven approach, **so it\u0026#39;s only after the template rendering, that this form is fully initialized.** } constructor() { afterNextRender(() =\u0026gt; { const savedForm = window.localStorage.getItem(\u0026#39;saved-login-form\u0026#39;); if (savedForm) { const loadedFormData = JSON.parse(savedForm); const savedEmail = loadedFormData.email; // right here: 1. template has been rendered // 2. the form object is initialized // 3. but the control objects actually aren\u0026#39;t fully initialized yet. // solution with template driven approach(we would have better solution with reactive driven form): setTimeout(() =\u0026gt; { this.form().controls[\u0026#39;email\u0026#39;].setValue(savedEmail); // set value, we can use controls to choose one of those, or all with object }, 1); } const subscription = this.form() .valueChanges?.pipe(debounceTime(500)) // user has to stop for at least 500 milliseconds. .subscribe({ next: (value) =\u0026gt; window.localStorage.setItem( \u0026#39;saved-login-form\u0026#39;, JSON.stringify({ email: value.email }) ), }); this.destroyRef.onDestroy(() =\u0026gt; subscription?.unsubscribe()); }); } Reactive driven approach: in template, we just connect elements # inside FormGroup or nested FormGroup, each key-value pair represents one control. e.g. email control for email input. first step: setup the form # form = new FormGroup({ // email and password can be any name email: new FormControl(\u0026#39;\u0026#39;), password: new FormControl(\u0026#39;\u0026#39;) }); second step: connect this form to template # imports: [ReactiveFormsModule] approach 1 # \u0026lt;input id=\u0026#34;email\u0026#34; type=\u0026#34;email\u0026#34; [formControl]=\u0026#34;form.controls.email\u0026#34; /\u0026gt; approach 2 # \u0026lt;form [formGroup]=\u0026#34;form\u0026#34;\u0026gt; \u0026lt;input id=\u0026#34;email\u0026#34; type=\u0026#34;email\u0026#34; [formControlName]=\u0026#34;password\u0026#34; /\u0026gt; \u0026lt;/form\u0026gt; pros # pros: Submitting: in reactive approach, we don\u0026rsquo;t have to pass any argument to onSubmit, because we already have access to the form in class pros: get access and have type safe when using .value this.form.controls.email and this.form.value.email validators # form = new FormGroup({ // can be [], or {validators:[], } // asyncValidators? // nonNullable?, it can make sure this input cannot be set to null again if it were reset. // updateOn?, it can control if the value managed by Angular should update on every keystroke or only if the input loses focus with updateOn. email: new FormControl(\u0026#39;\u0026#39;, { validators: [Validators.email, Validators.required, (control) =\u0026gt; {return null or nothing for valid, or {} for invalid}] }), password: new FormControl(\u0026#39;\u0026#39;) }); // custom validators function customValidator(control: AbstractControl) { if (control.value.includes(\u0026#39;?\u0026#39;)) { return null; } return { doesNotContainQuestionMark: true }; } // custom async validators function emailIsUnique(control: AbstractControl) { if (control.value !== \u0026#39;test@example.com\u0026#39;) { return of(null); // `of` produces an observable that instantly emits a value } return of({ notUnique: true }); } prepopulate data # we don\u0026rsquo;t need afterNextRender within constructor, because we created form inside class, we don\u0026rsquo;t have to wait for the template to render for it to be initialized. we already initialized form in the code. so we can use ngOnInit() {} // save value into localStorage private destroyRef = inject(DestroyRef); const subscription = this.form.valueChanges.pipe(debounceTime(500)) // we don\u0026#39;t need ? after valueChanges. .subscribe({ next: (value) =\u0026gt; window.localStorage.setItem( \u0026#39;saved-login-form\u0026#39;, JSON.stringify({ email: value.email }) ), }); this.destroyRef.onDestroy(() =\u0026gt; subscription.unsubscribe()); // update form value with localStorage 1. outside component, doesn\u0026#39;t work for ssr 2. inside ngOnInit before `subsription` as usual nested FormGroup validator # // access controls within nested formgroup function equalValues(control: AbstractControl) { const password = control.get(\u0026#39;password\u0026#39;)?.value; const confirmPassword = control.get(\u0026#39;confirmPassword\u0026#39;)?.value; if (password === confirmPassword) return null; return { passwordNotEuqal: true }; } Routing # setting up # //... import { provideRouter } from \u0026#39;@angular/router\u0026#39;; bootstrapApplication(AppComponent, { providers: [ provideRouter([ { path: \u0026#39;tasks\u0026#39;, // \u0026lt;domain\u0026gt;/tasks component: TasksComponent, }, ]), ], }).catch((err) =\u0026gt; console.error(err)); outsource routes # import { Routes } from \u0026#39;@angular/router\u0026#39;; export const routes: Routes = [ { path: \u0026#39;tasks\u0026#39;, // \u0026lt;domain\u0026gt;/tasks component: TasksComponent, }, ]; outsource app config # import { ApplicationConfig } from \u0026#39;@angular/core\u0026#39;; export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), ], }; final setup # use \u0026lt;router-outlet /\u0026gt; inside app.component.html import RouterOutlet to app.component.ts routerLink \u0026amp;\u0026amp; routerLinkActive # use routerLink directive instead of href within anchor tag retrieve route parameters # via input # export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes, withComponentInputBinding()), // any argument except first ], }; userId = input.required\u0026lt;string\u0026gt;(); // angular will set this userId cons: doesn\u0026rsquo;t work for child routes solution: add withrouterConfig({paramsInheritanceStrategy: 'always'}) in privodeRouter of providers via observables # pros: works for child routes private activatedRoute = inject(ActivatedRoute); ngOnInit(): void { this.activatedRoute.paramMap... // to extract paramMap this.activatedRoute.queryParams... // to extract queryParams } programmatically routing # private router = inject(Router); // disable back navigation by setup replaceUrl this.router.navigate([[\u0026#39;/users\u0026#39;, this.userId()]], {replaceUrl: true, }); fallback route # with ** path queryParams # [queryParams]=\u0026quot;{order: order() === 'asc' ? 'desc' : 'asc'}\u0026quot; // set to asc when undefined or desc by setting up with withComponentInputBinding() inside app.config.ts, we can extract queryParams by simply use order = input\u0026lt;'asc' | 'desc'\u0026gt;(); or by observables data property inside route # for static data data: { message: \u0026#39;Hello\u0026#39;} by setting up with withComponentInputBinding() inside app.config.ts, we can extract with input resolve property inside route # for dynamic data resolver function # export const resolveUserName: ResolveFn\u0026lt;string\u0026gt; = ( activatedRoute: ActivatedRouteSnapshot, routerState: RouterStateSnapshot ) =\u0026gt; { const usersService = inject(UsersService); const userName = usersService.users.find( (u) =\u0026gt; u.id === activatedRoute.paramMap.get(\u0026#39;userId\u0026#39;) )?.name || \u0026#39;\u0026#39;; return userName; }; we can extract data from resolver by input or @Input Important # Resolver functions will be re-executed if a route parameter changes, but not if a query parameter changes.\nby solving that, we can add runGuardsAndResolvers: 'paramsOrQueryParamsChange' Route Guards # can** property. To control access to a route. All of them take arrays of guard functions or classes. canMatch -\u0026gt; canActivate(before the component has been loaded) const dummyCanMatch: CanMatchFn = (route, segments) =\u0026gt; { const router = inject(Router); const shouldGetAccess = Math.random(); if (shouldGetAccess \u0026lt; 0.5) { return true; } return new RedirectCommand(router.parseUrl(\u0026#39;/unauthorized\u0026#39;)); }; canDeactivate, the idea is that we can control whether a user is allowed to leave a page or not. export const canLeaveEditPage: CanDeactivateFn\u0026lt;NewTaskComponent\u0026gt; = (component) =\u0026gt; { if (component.enteredTitle() || component.enteredDate() || component.enteredSummary()) { return window.confirm(\u0026#39;Do you really want to leave? You will lose the entered data.\u0026#39;) } return true; } fix redirect to same url within component # update component. change runGuardsAndResolvers:'always' to always inside parent route. // 1. update component this.router.navigate([\u0026#39;./\u0026#39;], { relativeTo: this.activatedRoute, onSameUrlNavigation: \u0026#39;reload\u0026#39;, queryParamsHandling: \u0026#39;preserve\u0026#39;, }); Performance - Lazy Loading # Route-based lazy loading # loadComponent: () =\u0026gt; import(\u0026#39;../tasks/tasks.component\u0026#39;).then((mod) =\u0026gt; mod.TasksComponent), Deferrable Views(\u0026gt;= 17) # enhancement for route base lazy loading Building - SPA(Single Page Application) # Build a client-side only web app: all the UI rendering happens on the client side by JS code that is being served by the web host to the website visitors. All compiled and optimized application code executes in the browsers. Therefore, we don\u0026rsquo;t need any dynamic web server. A static host suffices. Potential Cons: initially missing content, bad SEO(search engine crawlers will likely see an empty site, because they are not waiting for the client-side Javascript code to render all the content. At least they are not guaranteed to wait.) Use case: internal app, app that requires authentication, etc. Building - SSR # Angular app routes are rendered on-demand on a dynamic web server Browser receives finished, rendered page. This page still includes lots of Angular Javascript code, which then takes over and hydrated(\u0026ldquo;activated\u0026rdquo;) the page once it has been received. Web app is hydrated and becomes a SPA after initial rendering. Subsequent actions will be handled by client-side JS code. Pros: instant responses due to client-side JS doing the heavy work. + Finished pages without missing content for the initial request. Dynamic web server is required. Advantage: users no longer receive an empty HTML file or an almost empty HTML file, but instead, a file that contains all the content. It\u0026rsquo;s also great for search engine crawlers. Potential disadvantages: Long-taking tasks may cause empty pages, complexity but this approach also has some potential disadvantages. , right after the next overall Component render cycle., right after the next overall Component render cycle.afterNextRender(() =\u0026gt; {}) only runs in browser, right after the next overall Component render cycle.\nRefs # ElementRef: for directives, get access to the host element. private hostElementRef = inject\u0026lt;ElementRef\u0026lt;HTMLAnchorElement\u0026gt;\u0026gt;(ElementRef); with this.elementRef.nativeElement TemplateRef: within Directive, to hold content within ng-template ViewContainerRef: is a reference to the place in the DOM where above template is being used. this.viewContainerRef.createEmbeddedView(this.templateRef); this.viewContainerRef.clear();: will remove rendered content. ng-content vs ng-container vs ng-template # Feature ng-content ng-container ng-template Purpose Content projection from parent to child. Logical grouping without extra DOM nodes. Defines reusable or dynamic templates. DOM Rendering Yes, renders content in DOM. No, doesn\u0026rsquo;t create a DOM element. No, not directly rendered unless used. Use with Structural Directives No Yes Yes Reusability No No Yes Use Case Pass content into child components. Apply directives without adding DOM nodes. Dynamically render or reuse templates. Scenario Directive to Use Passing dynamic content to child components ng-content Grouping elements with structural directives without adding DOM nodes ng-container Defining templates for dynamic or delayed rendering ng-template Feature ContentChildren ViewChildren Scope Queries elements projected via . Queries elements declared in the component\u0026rsquo;s own template. Timing Available in ngAfterContentInit. Available in ngAfterViewInit. Use Case For working with external content. For working with internal content. tapResponse \u0026mdash;\u0026gt; rxjs # tapResponse( (data) =\u0026gt; mySuccessAction({data}), (error) =\u0026gt; myFailureAction({error}), ) map( (data) =\u0026gt; mySuccessAction({data}), ), catchError( (error) =\u0026gt; of(myFailureAcction({error}) ) ComponentStore # import { HttpClient, HttpErrorResponse, HttpHeaders, } from \u0026#39;@angular/common/http\u0026#39;; import { ComponentStore } from \u0026#39;@ngrx/component-store\u0026#39;; import { Injectable, inject } from \u0026#39;@angular/core\u0026#39;; import { ApiService } from \u0026#39;@mgm/shared-frontend\u0026#39;; import { Router } from \u0026#39;@angular/router\u0026#39;; import { catchError, concatMap, EMPTY, Observable, switchMap, tap } from \u0026#39;rxjs\u0026#39;; import { MY_URL } from \u0026#39;../utils/url.const\u0026#39;; import { APP_CONST } from \u0026#39;../utils/app.const\u0026#39;; import { ToastService } from \u0026#39;@mgm/ux-frontend\u0026#39;; export interface IPoolNumber { poolNumber: string; } export interface IAddNewPoolState { addNewPoolData: IPoolNumber[]; xmlData: string[]; tableChangeEvent: any; loading: boolean; loaded: boolean; error: string | null; } export const initialState: IAddNewPoolState = { addNewPoolData: [], xmlData: [], loaded: false, loading: false, error: null, tableChangeEvent: { sortField: \u0026#39;poolNumber\u0026#39;, sortOrder: \u0026#39;asc\u0026#39;, }, }; @Injectable() export class AddNewPoolStore extends ComponentStore\u0026lt;IAddNewPoolState\u0026gt; { constructor() { super(initialState); } private apiService = inject(ApiService); private router = inject(Router); private http = inject(HttpClient); private messageService = inject(ToastService); // selectors readonly sortOrder$ = this.select( (state) =\u0026gt; state.tableChangeEvent.sortOrder, ); readonly sortField$ = this.select( (state) =\u0026gt; state.tableChangeEvent.sortField, ); readonly tableData$: Observable\u0026lt;IPoolNumber[]\u0026gt; = this.select((state) =\u0026gt; { return state.addNewPoolData; }); // updaters readonly addToTableData = this.updater((state, poolNumber: IPoolNumber) =\u0026gt; ({ ...state, addNewPoolData: [...state.addNewPoolData, poolNumber], })); readonly setTableData = this.updater( (state, addNewPoolData: IPoolNumber[]) =\u0026gt; ({ ...state, addNewPoolData, }), ); readonly setLoading = this.updater((state, loading: boolean) =\u0026gt; ({ ...state, loading, })); readonly setLoaded = this.updater((state, loaded: boolean) =\u0026gt; ({ ...state, loaded, })); readonly setXmlData = this.updater((state, xmlData: string[]) =\u0026gt; ({ ...state, xmlData, error: null, })); readonly setError = this.updater((state, error: string | null) =\u0026gt; ({ ...state, error, loading: false, })); // effects readonly downloadTemplate = this.effect((trigger$: Observable\u0026lt;void\u0026gt;) =\u0026gt; trigger$.pipe( tap(() =\u0026gt; this.patchState({ loading: true })), concatMap(() =\u0026gt; this.apiService .sendDownloadRequest(MY_URL.NEW_POOL.GET_POOL_TEMPLATE()) .pipe( tap((response) =\u0026gt; { this.patchState({ loading: false }); if (response) { const blob = new Blob([\u0026lt;Blob\u0026gt;response], { type: APP_CONST.FILE_TYPE.SPEEDSHEET.RESPONSE, }); const fileURL = URL.createObjectURL(blob); const link = document.createElement(\u0026#39;a\u0026#39;); const fname = `PoolTemplate`; link.href = fileURL; link.download = `${fname}.${APP_CONST.FILE_TYPE.SPEEDSHEET.TYPE}`; document.body.appendChild(link); link.click(); document.body.removeChild(link); this.messageService.add({ summary: \u0026#39;Action Success\u0026#39;, severity: \u0026#39;success\u0026#39;, detail: \u0026#39;Request summary is downloaded.\u0026#39;, }); } }), catchError((error: HttpErrorResponse) =\u0026gt; { this.patchState({ loading: false }); this.messageService.add({ summary: \u0026#39;Action Failure\u0026#39;, detail: error?.message ? error?.message : \u0026#39;unknown error\u0026#39;, severity: \u0026#39;error\u0026#39;, }); return EMPTY; // Prevent the stream from terminating }), ), ), ), ); readonly removePool = this.effect\u0026lt;{ ptsTransferRqstID: number; vrsnId: number; pools: number[]; }\u0026gt;((request$) =\u0026gt; request$.pipe( tap(() =\u0026gt; this.setLoading(true)), switchMap((requestBody) =\u0026gt; this.http .post\u0026lt;string\u0026gt;(MY_URL.NEW_POOL.REMOVE_POOL(), requestBody, { headers: new HttpHeaders({ \u0026#39;Content-Type\u0026#39;: \u0026#39;application/json\u0026#39;, }), }) .pipe( tap(() =\u0026gt; this.setLoading(false)), // Example: Process success response here catchError((error: HttpErrorResponse) =\u0026gt; { this.setLoading(false); this.setError(error.message || \u0026#39;An error occurred\u0026#39;); return EMPTY; }), ), ), ), ); } @Component({ providers: [AddNewPoolStore], }) export class ConsumerComponent { private addNewPoolStore = inject(AddNewPoolStore); // select tableData$: Observable\u0026lt;IPoolNumber[]\u0026gt; = this.addNewPoolStore.tableData$; // update this.addNewPoolStore.addToTableData( { poolNumber: \u0026#34;1234\u0026#34; } ); // effect this.addNewPoolStore.downloadTemplate(); } DI (dependency injection) # Standard Use Cases: Constructor injection or inject. Dynamic Logic: Factory providers or InjectionToken. Custom Configurations: @Inject, @Optional, @Host, @Self, or @SkipSelf. Standalone Components: Use inject for cleaner code. Resolution Modifiers: @Optional(), @Skip(), @SkipSelf(), @Host() # \u0026lt;div appParent\u0026gt; \u0026lt;div appChild\u0026gt; \u0026lt;div appGrandChild\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; import { Component, Self, SkipSelf } from \u0026#39;@angular/core\u0026#39;; import { LoggerService } from \u0026#39;./logger.service\u0026#39;; import { ParentDirective } from \u0026#39;./parent.directive\u0026#39;; import { ChildDirective } from \u0026#39;./child.directive\u0026#39;; import { GrandChildDirective } from \u0026#39;./grand-child.directive\u0026#39;; @Component({ imports: [ParentDirective, ChildDirective, GrandChildDirective], selector: \u0026#39;app-root\u0026#39;, templateUrl: \u0026#39;./app.component.html\u0026#39;, styleUrl: \u0026#39;./app.component.css\u0026#39;, providers: [LoggerService], }) export class AppComponent { constructor( @Self() private logger: LoggerService, @SkipSelf() private parentLogger: LoggerService ) { if (this.logger) { this.logger.prefix = \u0026#39;app: \u0026#39;; this.logger.log(\u0026#39;constructor init\u0026#39;); } if (this.parentLogger) { this.parentLogger.log(\u0026#39;constructor init\u0026#39;); } } } import { Directive, Optional, Self } from \u0026#39;@angular/core\u0026#39;; import { LoggerService } from \u0026#39;./logger.service\u0026#39;; @Directive({ selector: \u0026#39;[appParent]\u0026#39;, // providers: [LoggerService], // toggle between remove or keep this line to see the difference }) export class ParentDirective { constructor(@Optional() @Self() private logger: LoggerService) { // toggle between remove or keep @Self() to see the difference if (this.logger) { this.logger.prefix = \u0026#39;parent directive: \u0026#39;; } } } import { Directive, Optional, Self } from \u0026#39;@angular/core\u0026#39;; import { LoggerService } from \u0026#39;./logger.service\u0026#39;; @Directive({ selector: \u0026#39;[appChild]\u0026#39;, }) export class ChildDirective { constructor(private logger: LoggerService) { this.logger.log(\u0026#39;child directive constructor\u0026#39;); } } import { Directive, Host } from \u0026#39;@angular/core\u0026#39;; import { LoggerService } from \u0026#39;./logger.service\u0026#39;; @Directive({ selector: \u0026#39;[appGrandChild]\u0026#39;, }) export class GrandChildDirective { constructor(@Host() private logger: LoggerService) { this.logger.log(\u0026#39;grand child directive constructor\u0026#39;); } } import { Injectable } from \u0026#39;@angular/core\u0026#39;; @Injectable({ providedIn: \u0026#39;root\u0026#39; }) export class LoggerService { prefix = \u0026#39;root: \u0026#39;; constructor() { console.log(\u0026#34;LoggerService constructor\u0026#34;); } log(message: string) { console.log(`${this.prefix}: ${message}`); } } Dependency Providers: useClass, useExisting, useValue, useFactory, injection tokens # allow us to control how angular should create the instances for provided dependencies. within providers array. within @Injectable decorator. Tree shakeable. useClass # useClass is not singleton, it will create a new instance every time it is injected.\nuseExisting # just a alias not creating a new instance, but using an existing instance of another service.\nuseValue # to provide object literals, strings, or numbers as dependencies.\nwe can use useValue with InjectionToken. not tree shakeable # import { InjectionToken } from \u0026#39;@angular/core\u0026#39;; export interface AppConfig { experimentalEnabled: boolean; } export const APP_CONFIG = new InjectionToken\u0026lt;AppConfig\u0026gt;(\u0026#39;app.config\u0026#39;); providers: [ { provide: APP_CONFIG, useValue: ... }, ] tree shakeable # import { InjectionToken } from \u0026#39;@angular/core\u0026#39;; export interface AppConfig { experimentalEnabled: boolean; } // use second parameters export const APP_CONFIG = new InjectionToken\u0026lt;AppConfig\u0026gt;(\u0026#39;app.config\u0026#39;, { providedIn: \u0026#39;root\u0026#39;, factory: () =\u0026gt; ({ experimentalEnabled: true, }), }); // after this, we can inject in any constructors import { APP_CONFIG, AppConfig } from \u0026#39;./config.token\u0026#39;; import { Logger } from \u0026#39;./logger\u0026#39;; import { Injectable, inject } from \u0026#39;@angular/core\u0026#39;; @Injectable({ providedIn: \u0026#39;root\u0026#39;, }) export class ExperimentalService implements Logger { // inject the config constructor(@inject(APP_CONFIG) private config: AppConfig) { console.log(\u0026#39;ExperimentalService -\u0026gt; constructor -\u0026gt; config\u0026#39;, config); } } why injectionToken exists? because we are not using class, we cannot use class reference as a key in DI tree(key: class, value: instance). but we need to have some key anyway, otherwise angular cannot understand how resolve it. interfaces also will not work, because interfaces are not existing in runtime. this s where injectionToken comes in. The value of injectionToken will act as a key in this case. it is a unique key that we can use to identify the dependency.\nuseFactory # use case: if we don\u0026rsquo;t know which service to provide in advance, and we need to decide at runtime. e.x. when we need to configure provider based on the value from another service or dependency injection token. @Component({ selector: \u0026#39;app-root\u0026#39;, templateUrl: \u0026#39;./app.component.html\u0026#39;, styleUrls: [\u0026#39;./app.component.css\u0026#39;], providers: [ { provide: LoggerService, useFactory: (config: AppConfig, http: HttpClient) =\u0026gt; { return config.experimentalEnabled ? new ExperimentalService(http) // after this, we can inject http in constructor of ExperimentalService : new LoggerService(); }, deps: [APP_CONFIG, HttpClient], // after this, we can use these as a parameter in useFactory }, ], }) Handle Unit Testing Mistakes # Angular Unit Testing Mistakes we should initiate variables in beforeEach we should use fixture.componentRef.setInput('data', widgetTestingData) to set input value we should use \u0026lt;p data-testId=\u0026quot;no-location\u0026quot;, and const noLocation = fixture.debugElement.query(By.css(\u0026#39;[data-testId=\u0026#34;no-location\u0026#34;]\u0026#39;)); // noLocation.nativeElement.value = \u0026#39;tomorrow\u0026#39;; // noLocation.nativeElement.dispatchEvent(new Event(\u0026#39;change\u0026#39;)); // to fire the change event expect(noLocation).toBeTruthy(); Flatten Nested Observables # Flatten Nested Observables we should use flatten operators like switchMap to flatten nested observables we should use takeUntilDestroyed at then end of pipe chain. we can have a eslint rule to enforce this. this.searchConfig$.pipe( switchMap((config) =\u0026gt; this.http.get(config.url)) takeUntilDestroyed(this.destroyRef), ).subscribe((data) =\u0026gt; { this.data = data; }); we should add subscription right after component property. Either use signal or async pipe(). With async pipe we can remove takeUntilDestroyed. // this is a property in component users$ = toSignal(this.searchConfig$).pipe( switchMap((config) =\u0026gt; this.http.get(config.url)), takeUntilDestroyed(this.destroyRef), ); avoid cold observables, which is executing observable logic multiple times. approach 1: reduce subscription in template approach 2: use hot observables users$ = toSignal(this.searchConfig$).pipe( switchMap((config) =\u0026gt; this.http.get(config.url)), // when a new subscriber arrives, the logic before shareReplay will not be executed // the switchMap will executed only when a new config is emitted shareReplay(1), ); avoid improper usage of distinctUntilChanged() in pipe chain. distinctUntilChanged() only works for primitive values, not for objects. // {} === {} is false. to resolve this by using distinctUntilChanged((prev, curr) =\u0026gt; prev.id === curr.id) or distinctUntilKeyChanged('id'), or deep comparison. avoid using side effects in the wrong places. tap is for side effects, not for changing the observable stream. tap is for logging, debugging, or triggering side effects. NGRX # NGRX provideStore(), provideState(scientistFeature.scientistFeature), provideEffects(scientistEffects), // without export const reducer = createReducer(...); // define extra selectors along with default selectors defined automatically by createFeature export const scientistFeature = createFeature({ name: \u0026#39;scientist\u0026#39;, reducer, extraSelectors: ({ selectSelectedId, selectScientists }) =\u0026gt; ({ selectSelectedScientist: createSelector( selectSelectedId, selectScientists, (selectedId, scientists) =\u0026gt; scientists.find((s) =\u0026gt; s.id === selectedId) ), }), }); // without constructor store = inject(Store); scientists$ = this.store.select(scientistFeature.selectScientists); ngOnInit() { this.store.dispatch(scientistFeature.loadScientists()); } onSelectScientist(id: number) { this.store.dispatch(scientistFeature.selectScientist({ id })); } Router Guard # use case # To confirm the navigation operation. Asking whether to save data before moving away from view. Allow access to certain parts of the application to specific users. Validating the Route parameters before navigating to the route. Fetching some data before you display the component view. all types of router guards # CanActivate – Determines if a route can be activated.\nUsed to prevent unauthorized users from accessing certain routes. Example use case: Restricting access to authenticated users. CanActivateChild – Determines if child routes can be activated.\nSimilar to CanActivate, but applies to child routes. Example use case: Preventing access to child routes if the parent route is restricted. CanDeactivate – Determines if a route can be exited.\nUsed to warn users before leaving a route (e.g., unsaved changes). Example use case: Prompting a user to save form data before navigating away. Resolve – Fetches data before navigating to a route.\nUsed to pre-load data required by a route before displaying the component. Example use case: Loading user details before opening a profile page. CanLoad – Determines if a module can be lazily loaded.\nPrevents unauthorized users from loading an entire module. Example use case: Restricting access to an admin module before it is loaded. Promise vs Observable # Promise vs Observable Promise send data all at once, Observable send data over chunks in stream. Promise will send data even if no one is listening, Observable will send data only if someone is listening. Promise is javascript native, Observable is part of RxJS. of vs from within rxjs # of: takes multiple arguments. from: takes one argument. An iterable or promise to convert it into observable. It will iterate over the iterable and emit each value one by one. Subject # we use subject for cross component communication. Only use Effect when needed and for advanced use case # @Component({ template: ` @for (option of options(); track option) { \u0026lt;li (click)=\u0026#34;select($index)\u0026#34;\u0026gt; {{ option }} } ` }) export class SelectCmp { // use case 2 without effect name = input(\u0026#39;\u0026#39;); myName = computed(() =\u0026gt; signal(this.name())); setName(name: string) { this.myName().set(name); // ERROR: no set method } // with effect, glitch example constructor() { effect(() =\u0026gt; { this.options(); this.index.set(-1); }); } this.options.set([...]); // time passes // glitch state here // effect runs this.index.set(-1); // use case 1 without effect options = input\u0026lt;string[]\u0026gt;(); state = computed(() =\u0026gt; { return { options: this.options(), index: signal(-1), }; }); select(idx: number) { this.state().index.set(idx); } } Lifecycle Hooks # import { Component, OnChanges, OnInit, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy } from \u0026#39;@angular/core\u0026#39;; @Component({ selector: \u0026#39;app-lifecycle-demo\u0026#39;, template: `\u0026lt;ng-content\u0026gt;\u0026lt;/ng-content\u0026gt;`, }) export class ChannelComponent implements OnChanges, OnInit, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy { ngOnChanges() { console.log(\u0026#39;Component input changed\u0026#39;); } ngOnInit() { // only called once console.log(\u0026#39;Component Initialization...\u0026#39;); } ngDoCheck() { // this will be called every change detection cycle console.log(\u0026#39;Pickup changes missed by Angular...\u0026#39;); } ngAfterContentInit() { // only called once console.log(\u0026#39;Content from \u0026lt;ng-content\u0026gt; initialized...\u0026#39;); } ngAfterContentChecked() { // once the bindings inside the projected content that will be rendered instead of this \u0026lt;ng-content\u0026gt;, then this method will be invoked. console.log(\u0026#39;Content from \u0026lt;ng-content\u0026gt; checked...\u0026#39;); // this method before the ngAfterviewInit, because the content is in the parent component } ngAfterViewInit() { console.log(\u0026#39;View initialized...\u0026#39;); } ngAfterViewChecked() { // is called every time when all the bindings in the template has been checked. console.log(\u0026#39;View is checked...\u0026#39;); } ngOnDestroy() { console.log(\u0026#39;Component is destroyed...\u0026#39;); } } import { Component, ChangeDetectorRef } from \u0026#39;@angular/core\u0026#39;; @Component({ selector: \u0026#39;app-root\u0026#39;, template: ` \u0026lt;h2\u0026gt;{{ topicName }}\u0026lt;/h2\u0026gt; \u0026lt;div *ngIf=\u0026#34;isVisible\u0026#34; class=\u0026#34;info\u0026#34;\u0026gt;{{ getInfo() }}\u0026lt;/div\u0026gt; \u0026lt;app-channel [name]=\u0026#34;name\u0026#34;\u0026gt; \u0026lt;p\u0026gt;Projected Suff\u0026lt;/p\u0026gt; \u0026lt;/app-channel\u0026gt; \u0026lt;div\u0026gt;Created At: {{ creationDate | date:\u0026#39;short\u0026#39; }}\u0026lt;/div\u0026gt; `, styleUrls: [\u0026#39;./app.component.css\u0026#39;] }) export class AppComponent { name = \u0026#39;Decoded Frontend\u0026#39;; topicName = \u0026#39;Change Detection in Angular\u0026#39;; isVisible = true; creationDate = new Date(); constructor(private cd: ChangeDetectorRef) { setTimeout(() =\u0026gt; { this.topicName = \u0026#39;Angular Unit Testing\u0026#39;; }, 3000); // Updating after 3 seconds to demonstrate change detection } getInfo(): string { return \u0026#39;Change Detection Triggered\u0026#39;; } } private vs # # Feature private # (Private Field) Scope TypeScript only JavaScript \u0026amp; TypeScript Encapsulation Not truly private (accessible via bracket notation) Fully private (cannot be accessed outside the class) Performance Slightly better (optimized in TypeScript) Slightly slower due to runtime privacy enforcement Access in Subclasses Yes (via protected) No (completely private) Compiled Output Becomes a normal property in JS Uses native #private fields signal store private store members # import { computed } from \u0026#39;@angular/core\u0026#39;; import { toObservable } from \u0026#39;@angular/core/rxjs-interop\u0026#39;; import { patchState, signalStore, withComputed, withMethods, withProps, withState, } from \u0026#39;@ngrx/signals\u0026#39;; export const CounterStore = signalStore( withState({ count1: 0, // 👇 private state slice _count2: 0, }), withComputed(({ count1, _count2 }) =\u0026gt; ({ // 👇 private computed signal _doubleCount1: computed(() =\u0026gt; count1() * 2), doubleCount2: computed(() =\u0026gt; _count2() * 2), })), withProps(({ count2, _doubleCount1 }) =\u0026gt; ({ // 👇 private property _count2$: toObservable(count2), doubleCount1$: toObservable(_doubleCount1), })), withMethods((store) =\u0026gt; ({ increment1(): void { patchState(store, { count1: store.count1() + 1 }); }, // 👇 private method _increment2(): void { patchState(store, { _count2: store._count2() + 1 }); }, })), ); angular has two injector hierarchy # environment injector hierarchy: NullInjector -\u0026gt; Platform Injector -\u0026gt; Root Injector -\u0026gt; Router Injector -\u0026gt; Child Injector element/node injector hierarchy: is being created for every component and directives. Use providers keyword inside directive or component annotation. Resolution Modifiers # @Self: it will only look for the dependency in the current injector and not in the parent injector. If there is no provider here - throw the error. It doesn\u0026rsquo;t traverse injectors tree. @SkipSelf: it tells Angular to skip local injector and start traversing of injector tree from parent injector. @Optional: it tells Angular that it should not throw the error if there is no provider and just returns NULL. @Host: it tells Angular that it should resolve dependencies in scope of current component view. It is applicable mostly for directives or projected components. Dependency Providers # useClass: just provides a new instance of some certain class. useExisting: works as an alias. It doesn\u0026rsquo;t create a new instance but reuse already instantiated one. useValue: Utilize it when we need to provide non-class object like string, object or already instantiated class instance. useFactory: useful when we have to perform some additional logic during the dependency value creation. @Component({ selector: \u0026#39;app-root\u0026#39;, templateUrl: \u0026#39;\u0026#39;, providers: [ { provide: Config, useFactory: (http: HttpClient) =\u0026gt; { http.get(\u0026#39;some/config\u0026#39;) // some logic }, deps: [HttpClient], }, ], }) Unit tests # In unit tests env, the initial change detection cycle is not triggered automatically. We have to trigger it manually.\nIn unit tests, all the dependencies of the tested unit they have to be mocked.\nsimple service # import { Injectable } from \u0026#39;@angular/core\u0026#39;; export interface Country { [key: string]: { name: string; vat: number; }; } @Injectable({ providedIn: \u0026#39;root\u0026#39;, }) export class TaxCalculatorService { readonly countries: Country = Object.freeze({ ua: { name: \u0026#39;Ukraine\u0026#39;, vat: 20 }, at: { name: \u0026#39;Austria\u0026#39;, vat: 20 }, de: { name: \u0026#39;Germany\u0026#39;, vat: 19 }, uk: { name: \u0026#39;United Kingdom\u0026#39;, vat: 20 }, pl: { name: \u0026#39;Poland\u0026#39;, vat: 23 }, }); /** * Expectation 1: It throws error if country isn\u0026#39;t supported * Expectation 2: It throws error if the price less then 0 * Expectation 3: Always returns 0 if isB2B flag set to true * Expectation 4: Calculates VAT amount based on country */ calculateVAT(price: number, countryKey: string, isB2B = false) { if (!this.countries[countryKey]) { throw new Error(`This country isn\u0026#39;t supported...`); } if (price \u0026lt; 0) { throw new Error(`The price can not be a negative number...`); } if (isB2B) { return 0; } return (price / 100) * this.countries[countryKey].vat; } } import { TaxCalculatorService } from \u0026#34;./tax-calculator.service\u0026#34; describe(`TaxCalculatorService`, () =\u0026gt; { let service: TaxCalculatorService; beforeEach(() =\u0026gt; { service = new TaxCalculatorService() }) it(`should return 0 if isB2B flag is true`, () =\u0026gt; { const result = service.calculateVAT(100, \u0026#39;ua\u0026#39;, true); expect(result).toBe(0); }) it(`should properly calculate VAT for a given country`, () =\u0026gt; { const result = service.calculateVAT(100, \u0026#39;ua\u0026#39;); expect(result).toBe(20); }) describe(`TaxCalculatorSevice: Error Handling`, () =\u0026gt; { it(`should throw error if country isn\u0026#39;t supported`, () =\u0026gt; { expect(() =\u0026gt; service.calculateVAT(100, \u0026#39;ru\u0026#39;)) .toThrowError(/isn\u0026#39;t supported/) }) it(`should throw error if price is negative number`, () =\u0026gt; { expect(() =\u0026gt; service.calculateVAT(-100, \u0026#39;ua\u0026#39;)) .toThrowError(/negative number/) }) }) }) service with dependencies # import { Inject, Injectable, InjectionToken } from \u0026#39;@angular/core\u0026#39;; export interface Country { [key: string]: { name: string; vat: number; }; } export const CONUTRIES = new InjectionToken( \u0026#39;countries\u0026#39;, { providedIn: \u0026#39;root\u0026#39;, factory: () =\u0026gt; Object.freeze({ ua: { name: \u0026#39;Ukraine\u0026#39;, vat: 20 }, at: { name: \u0026#39;Austria\u0026#39;, vat: 20 }, de: { name: \u0026#39;Germany\u0026#39;, vat: 19 }, uk: { name: \u0026#39;United Kingdom\u0026#39;, vat: 20 }, pl: { name: \u0026#39;Poland\u0026#39;, vat: 23 } }) } ) @Injectable({ providedIn: \u0026#39;root\u0026#39;, }) export class TaxCalculatorService { constructor( @Inject(CONUTRIES) readonly countries: Country ) {} /** * Expectation 1: It throws error if country isn\u0026#39;t supported * Expectation 2: It throws error if the price less then 0 * Expectation 3: Always returns 0 if isB2B flag set to true * Expectation 4: Calculates VAT amount based on country */ calculateVAT(price: number, countryKey: string, isB2B = false) { if (!this.countries[countryKey]) { throw new Error(`This country isn\u0026#39;t supported...`); } if (price \u0026lt; 0) { throw new Error(`The price can not be a negative number...`); } if (isB2B) { return 0; } return (price / 100) * this.countries[countryKey].vat; } } import { Country as Countries, TaxCalculatorService } from \u0026#34;./tax-calculator.service\u0026#34; describe(`TaxCalculatorService`, () =\u0026gt; { let service: TaxCalculatorService; let testCountries: Countries; beforeEach(() =\u0026gt; { testCountries = { ua: { name: \u0026#39;Ukraine\u0026#39;, vat: 20 } } service = new TaxCalculatorService(testCountries); }) it(`should return 0 if isB2B flag is true`, () =\u0026gt; { const result = service.calculateVAT(100, \u0026#39;ua\u0026#39;, true); expect(result).toBe(0); }) it(`should properly calculate VAT for a given country`, () =\u0026gt; { const result = service.calculateVAT(100, \u0026#39;ua\u0026#39;); expect(result).toBe(20); }) describe(`TaxCalculatorSevice: Error Handling`, () =\u0026gt; { it(`should throw error if country isn\u0026#39;t supported`, () =\u0026gt; { expect(() =\u0026gt; service.calculateVAT(100, \u0026#39;ir\u0026#39;)) .toThrowError(/isn\u0026#39;t supported/) }) it(`should throw error if price is negative number`, () =\u0026gt; { expect(() =\u0026gt; service.calculateVAT(-100, \u0026#39;ua\u0026#39;)) .toThrowError(/negative number/) }) }) }) service with inject function - approach 1 # import { Inject, Injectable, InjectionToken, inject } from \u0026#39;@angular/core\u0026#39;; export interface Country { [key: string]: { name: string; vat: number; }; } export const COUNTRIES = new InjectionToken\u0026lt;Country\u0026gt;( \u0026#39;countries\u0026#39;, { providedIn: \u0026#39;root\u0026#39;, factory: () =\u0026gt; Object.freeze({ ua: { name: \u0026#39;Ukraine\u0026#39;, vat: 20 }, at: { name: \u0026#39;Austria\u0026#39;, vat: 20 }, de: { name: \u0026#39;Germany\u0026#39;, vat: 19 }, uk: { name: \u0026#39;United Kingdom\u0026#39;, vat: 20 }, pl: { name: \u0026#39;Poland\u0026#39;, vat: 23 } }) } ) @Injectable({ providedIn: \u0026#39;root\u0026#39;, }) export class TaxCalculatorService { readonly countries = inject(COUNTRIES); /** * Expectation 1: It throws error if country isn\u0026#39;t supported * Expectation 2: It throws error if the price less then 0 * Expectation 3: Always returns 0 if isB2B flag set to true * Expectation 4: Calculates VAT amount based on country */ calculateVAT(price: number, countryKey: string, isB2B = false) { if (!this.countries[countryKey]) { throw new Error(`This country isn\u0026#39;t supported...`); } if (price \u0026lt; 0) { throw new Error(`The price can not be a negative number...`); } if (isB2B) { return 0; } return (price / 100) * this.countries[countryKey].vat; } } import { COUNTRIES, TaxCalculatorService } from \u0026#34;./tax-calculator.service\u0026#34; import { TestBed } from \u0026#39;@angular/core/testing\u0026#39;; describe(`TaxCalculatorService`, () =\u0026gt; { let service: TaxCalculatorService; beforeEach(() =\u0026gt; { TestBed.configureTestingModule({ providers: [ { provide: COUNTRIES, useValue: { ua: { name: \u0026#39;Ukraine\u0026#39;, vat: 20 } } // faked value for this token } ] }) TestBed.runInInjectionContext(() =\u0026gt; { service = new TaxCalculatorService(); }) }) }) service with inject function - approach 2 # import { COUNTRIES, TaxCalculatorService } from \u0026#34;./tax-calculator.service\u0026#34; import { TestBed } from \u0026#39;@angular/core/testing\u0026#39;; describe(`TaxCalculatorService`, () =\u0026gt; { let service: TaxCalculatorService; beforeEach(() =\u0026gt; { TestBed.configureTestingModule({ providers: [ TaxCalculatorService, // when this service is not providedIn root { provide: COUNTRIES, useValue: { ua: { name: \u0026#39;Ukraine\u0026#39;, vat: 20 } } } ] }) service = TestBed.inject(TaxCalculatorService); }) }) setup env for component # import { TestBed } from \u0026#34;@angular/core/testing\u0026#34;; import { ButtonComponent } from \u0026#34;./button.component\u0026#34;; import { ButtonModule } from \u0026#34;./button.module\u0026#34;; describe(\u0026#39;ButtonComponent\u0026#39;, () =\u0026gt; { let fixture: ComponentFixture\u0026lt;ButtonComponent\u0026gt;; beforeEach(() =\u0026gt; { TestBed.configureTestingModule({imports: [ButtonModule]}) fixture = TestBed.createComponent(ButtonComponent); fixture.detectChanges(); // initial CD. triggers ngOnInit. }); it(\u0026#39;should test something...\u0026#39;, () =\u0026gt; { expect(true).toBe(true); }) }) The ComponentFixture is a test harness for interacting with the created component and its corresponding element.\nuse fixture.componentInstance to access the component instance. use fixture.detectChanges() to trigger the change detection cycle.\nnativeElement vs debugElement # fixture.nativeElement.querySelector(\u0026#39;button\u0026#39;); fixture.componentInstance.loading = true; fixture.detectChanges(); will work on all angular platform describe(\u0026#39;ButtonComponent\u0026#39;, () =\u0026gt; { let fixture: ComponentFixture\u0026lt;ButtonComponent\u0026gt;; let el: DebugElement; beforeEach(() =\u0026gt; { TestBed.configureTestingModule({imports: [ButtonModule]}) fixture = TestBed.createComponent(ButtonComponent); el = fixture.debugElement; fixture.detectChanges(); // initial CD. triggers ngOnInit. }); it(\u0026#39;should test something...\u0026#39;, () =\u0026gt; { // el.query(By.css(\u0026#39;css-class-name\u0026#39;).nativeElement); // el.queryAll(By.css(\u0026#39;css-class-name\u0026#39;)); // el.queryAllNodes(By.css(\u0026#39;css-class-name\u0026#39;)); debugger; expect(true).toBe(true); }) }) Typescript memo # interface can extends more than one interfaces # interface IA { aa: string; } interface IB { bb: string; } // interface extends more than one interfaces interface IC extends IA, IB { cc: string; } class extends one class + class implements more than one interfaces # class Animal { animal = \u0026#39;animal\u0026#39; eat() { console.log(\u0026#39;animal eat!\u0026#39;) } } class Human { human = \u0026#39;human\u0026#39; run() { console.log(\u0026#39;human run!\u0026#39;) } } // class extends one class // class implements more than one interfaces class Thing extends Animal implements IA, IB { thing = \u0026#39;thing\u0026#39; sit() { console.log(\u0026#39;thing thing\u0026#39;) } cc = \u0026#39;cc\u0026#39; aa = \u0026#39;aa\u0026#39; bb = \u0026#39;bb\u0026#39; } Generic in typescript # generic can be used in function, class, interface function # // Returns the input as-is, but preserves the type function identity\u0026lt;T\u0026gt;(arg: T): T { return arg; } // Usage: Type is inferred const num = identity(42); // Type: number const str = identity(\u0026#34;hello\u0026#34;); // Type: string // Explicitly specify the type (if needed) const explicit = identity\u0026lt;string\u0026gt;(\u0026#34;world\u0026#34;); interface # interface Box\u0026lt;T\u0026gt; { value: T; } const numberBox: Box\u0026lt;number\u0026gt; = { value: 42 }; const stringBox: Box\u0026lt;string\u0026gt; = { value: \u0026#34;hello\u0026#34; }; class # class Queue\u0026lt;T\u0026gt; { private items: T[] = []; enqueue(item: T) { this.items.push(item); } dequeue(): T | undefined { return this.items.shift(); } } // Usage with numbers const numberQueue = new Queue\u0026lt;number\u0026gt;(); numberQueue.enqueue(1); numberQueue.enqueue(2); // Usage with strings const stringQueue = new Queue\u0026lt;string\u0026gt;(); stringQueue.enqueue(\u0026#34;a\u0026#34;); type constraints with extends # Restrict generics to types that meet certain conditions: // Ensure `T` has a `length` property function logLength\u0026lt;T extends { length: number }\u0026gt;(arg: T): void { console.log(arg.length); } logLength(\u0026#34;hello\u0026#34;); // 5 (string has `length`) logLength([1, 2, 3]); // 3 (array has `length`) // logLength(42); // Error: number has no `length` default generic types # // Default to `number` if no type is provided interface Pagination\u0026lt;T = number\u0026gt; { currentPage: T; totalPages: T; } const page1: Pagination = { currentPage: 1, totalPages: 5 }; // Uses number const page2: Pagination\u0026lt;string\u0026gt; = { currentPage: \u0026#34;1\u0026#34;, totalPages: \u0026#34;5\u0026#34; }; multiple type parameters # // Map keys (K) to values (V) function pair\u0026lt;K, V\u0026gt;(key: K, value: V): [K, V] { return [key, value]; } const stringNumberPair = pair(\u0026#34;age\u0026#34;, 30); // Type: [string, number] const booleanDatePair = pair(true, new Date()); // Type: [boolean, Date] generic utility types # TypeScript provides built-in utility types like Partial, Readonly, etc.: interface User { name: string; age: number; } // Make all properties optional type PartialUser = Partial\u0026lt;User\u0026gt;; const partial: PartialUser = { name: \u0026#34;Alice\u0026#34; }; // OK (age is optional) // Make all properties readonly type ReadonlyUser = Readonly\u0026lt;User\u0026gt;; const readOnlyUser: ReadonlyUser = { name: \u0026#34;Bob\u0026#34;, age: 30 }; // readOnlyUser.age = 31; // Error: readonly property ","date":"27 January 2024","externalUrl":null,"permalink":"/notes/angular/","section":"Notes","summary":"How to read source code in Github # Node and Angular Version Control # Check Current Angular and Node.js Versions # node -v ng version Upgrade or Downgrade Angular # ng update @angular/cli @angular/core npm uninstall @angular/cli @angular/core npm install @angular/cli@12 @angular/core@12 Reinstall Node Modules # After upgrading or downgrading Angular, it’s a good practice to remove the node_modules folder and reinstall dependencies to ensure everything works with the new version: rm -rf node_modules npm install Concepts # template reference variable # Useful: child component can interact with or manipulate the parent’s HTMLElement \u003cinput #text /\u003e \u003capp-child [inputRef]=\"text\"\u003e\u003c/app-child\u003e child component use @Input to receive the reference of HTMLInputElement\n","title":"Angular","type":"notes"},{"content":" Create a project # npm create vite@latest project-folder-name Shortcuts # // rafce // rafc // rfc Note: Tutorials 1 # My React Notes with commit Youtube Note: Tutorials 2 # Youtube Youtube Github Project: React Jobs (along with Tutorial 2) # create a project, update vite.config.ts\nempty app.tsx, delete app.css, and empty index.css\nsetup tailwind, vite react tailwind\nreformat the code copied from the index.html into App.tsx\nreformat with component Navbar.tsx\nassets/images/logo.png\nsingle page development # props, default values and constraint types (props) =\u0026gt; {...} or ({ isHome = false}) =\u0026gt; { ... } useState, onClick={() =\u0026gt; setSome((prevState) =\u0026gt; !prevState)} additional packages # react-icon, install additional package npm i react-icons remove import { FaArrowLeft } from 'react-icons/fa' \u0026lt;FaArrowLeft className='mr-2' /\u0026gt; Some Texts react-router-dom, npm i react-router-dom routing # within App.tsx import { Route, createBrowserRouter, createRoutesFromElements, RouterProvider, } from `react-router-dom`; import HomePage from \u0026#39;./pages/HomePage\u0026#39;; const router = createBrowserRouter( // createRoutesFromElements(\u0026lt;Route index element={\u0026lt;HomePage /\u0026gt;} /\u0026gt;) createRoutesFromElements(\u0026lt;Route path=\u0026#39;/about\u0026#39; element={\u0026lt;HomePage /\u0026gt;} /\u0026gt;) ); const App = () =\u0026gt; { return \u0026lt;RouterProvider router={router} /\u0026gt;; } export default App; layout # create folder pages, including HomePage, \u0026hellip; within App.tsx, wrap other route with layout route use Outlet in layout component we prefer link tag, not a tag, because a tag does a complete page refresh How to change a tag to link tag: import {Link} from \u0026#39;react-router-dom\u0026#39; // 1. change all `a` to `Link` // 2. change `href` to `to` 404 page # create 404 page add route with * active link on NavBar # use NavLink instead of Link className attached with a function Conditional rendering # use props and ? : Mock API # use json server with our json file Json Server install: -D means dev dependency npm i -D json-server update package.json within scripts add \u0026quot;server\u0026quot;: \u0026quot;json-server --watch src/jobs.json --port 8888\u0026quot; run npm run server use useEffect hook to make a request, we also use useState const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); useEffect( () =\u0026gt; { const fetchJobs = async () =\u0026gt; { try { const res = await fetch(\u0026#39;http://localhost:8000/jobs\u0026#39;); // without proxy const data = await res.json(); setJobs(data); } catch (error) { console.log(\u0026#39;Error fetching\u0026#39;, error); } finally { setLoading(false); } } fetchJobs(); }, []); { loading ? ( \u0026lt;Spinner loading={loading} /\u0026gt; ) : ( \u0026lt;\u0026gt; {jobs.map((job) =\u0026gt; ( \u0026lt;JobListing key={job.id} job={job} /\u0026gt; ))} \u0026lt;/JobListing\u0026gt; ) } use React Spinners, install: npm i react-spinners Ref create a spinner ui component Different approach to fetch data # react suspense, it allows us to do a render while fetching, so we basically provide a fallback UI such as a spinner. (what we are doing here we fetch on render, because when it renders it has a side effect of fetching the data) react query and SWR are third-party libraries. They make data fetching a little easier. react 19 has new use hook Proxying # with create react app use package.json with vite we use vite.config.ts, and add following in server proxy: { \u0026#39;/api\u0026#39;: { target: \u0026#39;http://localhost:8000\u0026#39;, changeOrigin: true, rewrite: (path) =\u0026gt; path.replace(/^\\/api/, \u0026#39;\u0026#39;), }, }, // every time we send a request we use /api, i.e. /api/jobs (instead of localhost:00/jobs) data loader from react router for single page # another way to fetch data the traditional way: useEffet way # import {useState, useEffect} from \u0026#39;react\u0026#39;; import {useParams} from \u0026#39;react-router-dom\u0026#39;; import Spinner from \u0026#39;../component/Spinner\u0026#39;; const JobPage = () =\u0026gt; { const { id } = useParams(); const [job, setJob] = useState(null); cont [loading, setLoading] = useState(true); useEffect( () =\u0026gt; { const fetchJob = async () =\u0026gt; { try { const res = await fetch(`/api/jobs/${id}`) // use ` here not \u0026#39; const data = await res.json(); console.log(data); setJob(data); } catch (error) { console.log(\u0026#39;Error fetching data\u0026#39;, error); } finally { setLoading(false); } }; fetchJob(); }, []); return loading ? \u0026lt;Spinner /\u0026gt; : \u0026lt;h1\u0026gt;{job.title}\u0026lt;h1/\u0026gt; } export default JobPage; new way: data loader way(a new feature with react router not react itself) # // 5. import {useParams, useLoaderData} from \u0026#39;react-router-dom\u0026#39;; const JobPage = () =\u0026gt; { const { id } = useParams(); // 6. const job = useLoaderData(); // 7. return \u0026lt;h1\u0026gt;{job.title}\u0026lt;h1/\u0026gt; } // 1. const jobLoader = async ({params}) =\u0026gt; { const res = await fetch(`/api/jobs/${params.id}`) // use ` here not \u0026#39; const data = await res.json(); return data; }; // 2. export { JobPage as default, jobLoader }; // within App.tsx // 3. import JobPage, {jobLoader} from \u0026#39;./pages/JobPage\u0026#39;; // 4. we can pass this jobLoader into other components as well and use that to get a job by its id ... \u0026lt;Route path=\u0026#39;/jobs/:id\u0026#39; element={\u0026lt;JobPage /\u0026gt;} loader={jobLoader} /\u0026gt; ... Forms # change class to className change for= to htmlFor= Other ways to work with forms, including foric\nthe most common basic way to work with form: adding a piece of useState for every field in our form. if with react 19: we can use \u0026lt;form action=? \u0026gt; if with older react: we can use \u0026lt;form onSubmit =? \u0026gt; pass Functions as props \u0026amp;\u0026amp; POST request to Add a job # we could use context API, we have all request methods in a context file or we could use Redux or some other state manager. within this app we just put all requests within App.tsx toastify # npm i react-toastify ","date":"25 January 2024","externalUrl":null,"permalink":"/notes/react/","section":"Notes","summary":"Create a project # npm create vite@latest project-folder-name Shortcuts # // rafce // rafc // rfc Note: Tutorials 1 # My React Notes with commit Youtube Note: Tutorials 2 # Youtube Youtube Github Project: React Jobs (along with Tutorial 2) # create a project, update vite.config.ts\n","title":"React","type":"notes"},{"content":" Leetcode 380. Insert Delete GetRandom O(1) # leetcode 380 from random import choice class RandomizedSet(): def __init__(self): \u0026#34;\u0026#34;\u0026#34; Initialize your data structure here. \u0026#34;\u0026#34;\u0026#34; self.dict = {} self.list = [] def insert(self, val: int) -\u0026gt; bool: \u0026#34;\u0026#34;\u0026#34; Inserts a value to the set. Returns true if the set did not already contain the specified element. \u0026#34;\u0026#34;\u0026#34; if val in self.dict: return False self.dict[val] = len(self.list) self.list.append(val) return True def remove(self, val: int) -\u0026gt; bool: \u0026#34;\u0026#34;\u0026#34; Removes a value from the set. Returns true if the set contained the specified element. \u0026#34;\u0026#34;\u0026#34; if val in self.dict: # move the last element to the place idx of the element to delete last_element, idx = self.list[-1], self.dict[val] self.list[idx], self.dict[last_element] = last_element, idx # delete the last element self.list.pop() del self.dict[val] return True return False def getRandom(self) -\u0026gt; int: \u0026#34;\u0026#34;\u0026#34; Get a random element from the set. \u0026#34;\u0026#34;\u0026#34; return choice(self.list) ","date":"1 January 2024","externalUrl":null,"permalink":"/notes/oop-python/","section":"Notes","summary":"Leetcode 380. Insert Delete GetRandom O(1) # leetcode 380 from random import choice class RandomizedSet(): def __init__(self): \"\"\" Initialize your data structure here. \"\"\" self.dict = {} self.list = [] def insert(self, val: int) -\u003e bool: \"\"\" Inserts a value to the set. Returns true if the set did not already contain the specified element. \"\"\" if val in self.dict: return False self.dict[val] = len(self.list) self.list.append(val) return True def remove(self, val: int) -\u003e bool: \"\"\" Removes a value from the set. Returns true if the set contained the specified element. \"\"\" if val in self.dict: # move the last element to the place idx of the element to delete last_element, idx = self.list[-1], self.dict[val] self.list[idx], self.dict[last_element] = last_element, idx # delete the last element self.list.pop() del self.dict[val] return True return False def getRandom(self) -\u003e int: \"\"\" Get a random element from the set. \"\"\" return choice(self.list)","title":"OOP Python","type":"notes"},{"content":" Leetcode 380. Insert Delete GetRandom O(1) # leetcode 380 class RandomizedSet { Map\u0026lt;Integer, Integer\u0026gt; dict; List\u0026lt;Integer\u0026gt; list; Random rand = new Random(); /** Initialize your data structure here. */ public RandomizedSet() { dict = new HashMap(); list = new ArrayList(); } /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */ public boolean insert(int val) { if (dict.containsKey(val)) return false; dict.put(val, list.size()); list.add(list.size(), val); return true; } /** Removes a value from the set. Returns true if the set contained the specified element. */ public boolean remove(int val) { if (! dict.containsKey(val)) return false; // move the last element to the place idx of the element to delete int lastElement = list.get(list.size() - 1); int idx = dict.get(val); list.set(idx, lastElement); dict.put(lastElement, idx); // delete the last element list.remove(list.size() - 1); dict.remove(val); return true; } /** Get a random element from the set. */ public int getRandom() { return list.get(rand.nextInt(list.size())); } } ","date":"1 January 2024","externalUrl":null,"permalink":"/notes/oop-java/","section":"Notes","summary":"Leetcode 380. Insert Delete GetRandom O(1) # leetcode 380 class RandomizedSet { Map\u003cInteger, Integer\u003e dict; List\u003cInteger\u003e list; Random rand = new Random(); /** Initialize your data structure here. */ public RandomizedSet() { dict = new HashMap(); list = new ArrayList(); } /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */ public boolean insert(int val) { if (dict.containsKey(val)) return false; dict.put(val, list.size()); list.add(list.size(), val); return true; } /** Removes a value from the set. Returns true if the set contained the specified element. */ public boolean remove(int val) { if (! dict.containsKey(val)) return false; // move the last element to the place idx of the element to delete int lastElement = list.get(list.size() - 1); int idx = dict.get(val); list.set(idx, lastElement); dict.put(lastElement, idx); // delete the last element list.remove(list.size() - 1); dict.remove(val); return true; } /** Get a random element from the set. */ public int getRandom() { return list.get(rand.nextInt(list.size())); } }","title":"OOP Java","type":"notes"},{"content":" Leetcode 380. Insert Delete GetRandom O(1) # leetcode 380 // #include \u0026lt;cstdlib\u0026gt; // #include \u0026lt;ctime\u0026gt; // #include \u0026lt;unordered_map\u0026gt; // #include \u0026lt;vector\u0026gt; class RandomizedSet { private: std::unordered_map\u0026lt;int, int\u0026gt; dict; std::vector\u0026lt;int\u0026gt; list; // std::rand rand; public: /** Initialize your data structure here. */ RandomizedSet() { std::srand(std::time(0)); } /** Inserts a value to the set. Returns true if the set did not already * contain the specified element. */ bool insert(int val) { if (dict.find(val) != dict.end()) return false; dict[val] = list.size(); list.push_back(val); return true; } /** Removes a value from the set. Returns true if the set contained the * specified element. */ bool remove(int val) { if (dict.find(val) == dict.end()) return false; int lastElement = list.back(); int idx = it-\u0026gt;second; list[idx] = lastElement; dict[lastElement] = idx; list.pop_back(); dict.erase(val); return true; } /** Get a random element from the set. */ int getRandom() { return list[std::rand() % list.size()]; } }; ","date":"1 January 2024","externalUrl":null,"permalink":"/notes/oop-cpp/","section":"Notes","summary":"Leetcode 380. Insert Delete GetRandom O(1) # leetcode 380 // #include \u003ccstdlib\u003e // #include \u003cctime\u003e // #include \u003cunordered_map\u003e // #include \u003cvector\u003e class RandomizedSet { private: std::unordered_map\u003cint, int\u003e dict; std::vector\u003cint\u003e list; // std::rand rand; public: /** Initialize your data structure here. */ RandomizedSet() { std::srand(std::time(0)); } /** Inserts a value to the set. Returns true if the set did not already * contain the specified element. */ bool insert(int val) { if (dict.find(val) != dict.end()) return false; dict[val] = list.size(); list.push_back(val); return true; } /** Removes a value from the set. Returns true if the set contained the * specified element. */ bool remove(int val) { if (dict.find(val) == dict.end()) return false; int lastElement = list.back(); int idx = it-\u003esecond; list[idx] = lastElement; dict[lastElement] = idx; list.pop_back(); dict.erase(val); return true; } /** Get a random element from the set. */ int getRandom() { return list[std::rand() % list.size()]; } };","title":"OOP C++","type":"notes"},{"content":" IoC: Inverse of Control # let Spring manage creating objects AOP: Aspect Orientied Programming # enhance the code functionality without modifying the source code ","date":"27 December 2023","externalUrl":null,"permalink":"/notes/spring/","section":"Notes","summary":"IoC: Inverse of Control # let Spring manage creating objects AOP: Aspect Orientied Programming # enhance the code functionality without modifying the source code ","title":"Spring6","type":"notes"},{"content":" Spring bean # an instance of a class managed by the Spring container show all default beans # // within main method ApplicationContext apc = SpringApplication.run(ClassName.class) for (String s : apc.getBeanDefinitionNames()) { System.out.println(s); } Spring container # part of the core of the Spring framework managing all beans: it decides when to create this instance, when to kill this instance, and how to initialize the instance, etc. performs dependency injection IoC(Inversion of Control) # instead of the programmer really deciding the flow of the application, deciding what objects are created, etc. this all handed over to the Spring framework(or more precisely to the Spring container) Dependency Injection # IoC entails Dependency Injection\ninstead of in our code we have to instantiate some new object, Spring container is actually instantiating this object Spring container is injecting object for us\nSpringBoot Controller # Rounter # @RequestMapping, on class or methods value method consumes, 请求的 Content-Type, i.e. application/json produces params, headers *, **, ? Method # @RequestMapping(value = \u0026quot;/getData\u0026quot;, method = RequestMethod.GET) @GetMapping(\u0026quot;/getData\u0026quot;) is same as above. Parameters # @RequestParam, (from HTTP request body or url QueryString) when the names are matching, we can ignore this annotation\npublic String getTest3(@RequestParam(value = \u0026quot;nickname\u0026quot;, required = false) String name) {} @PathVariable, handle dynamic URL\n@RequestBody, most use case is to handle data which is not Content-Type: application/x-www-form-urlencoded, i.e. application/json, application/xml, etc.\n// i.e. @RequestBody @RequestMapping(value = \u0026#34;/postTest4\u0026#34;, method = RequestMethod.POST) public String postTest4(@RequestBody User user) { // @RequestBody System.out.println(user); return \u0026#34;Post request\u0026#34; } SpringBoot File Upload + HandlerInterceptor # Static Resources # define filter rule and static resource path // application.properties spring.mvc.static-path-pattern=/static/** spring.web.resources.static-locations=classpath:/static/ filter rule is /static/**, static resource path is classpath:/static/\nFile Upload # enctype define how to encode form data before sending to server\nenctype = \u0026quot;application/x-www-form-urlencoded\u0026quot; is default, then form data likes key=value\u0026amp;key=value\nenctype = \u0026quot;multipart/form-data\u0026quot; To update 1MB file limitation by SpringBoot built-in Tomcat\n// application.properties spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB HandlerInterceptor # RESTful service + Swagger # Restful # Swagger # http://127.0.0.1:8080/swagger-ui.html Mybatis-Plus # ORM(Object Relational Mapping)\nMybatis-Plus # Mybatis-Plus CRUD operation # Example # // mapper // UserMapper.java @Mapper public interface UserMapper extends BaseMapper\u0026lt;User\u0026gt; { // User has to be same as table name within database } // controller // UserController @RestController public class UserController { @Autowired private UserMapper userMapper; @GetMapping(\u0026#34;/user\u0026#34;) public List query() { List\u0026lt;User\u0026gt; list = userMapper.selectList(null); return list; // automatically convert to json } @PostMapping(\u0026#34;/user\u0026#34;) public String save(User user) { int i = userMapper.insert(user); // i is the number of rows inserted if (i \u0026gt; 0) { return \u0026#34;successfully inserted\u0026#34;; } else { return \u0026#34;insertation failed\u0026#34;; } } } Searching Multiple Tables # ResponseEntity # Returning a resource: Use ResponseEntity.ok(resource) for successful retrieval (200 OK). Handling \u0026ldquo;Not Found\u0026rdquo;: Use new ResponseEntity\u0026lt;\u0026gt;(HttpStatus.NOT_FOUND) or ResponseEntity.notFound().build() for resources that don\u0026rsquo;t exist (404 Not Found). Creating a new resource: Use ResponseEntity.status(HttpStatus.CREATED).headers(headers).body(createdResource) or ResponseEntity.created(locationUri).body(createdResource) for successful creation (201 Created), often including a Location header. No content: Use ResponseEntity.noContent().build() when an operation is successful but there\u0026rsquo;s no content to return (204 No Content). Bad requests: Use ResponseEntity.badRequest().body(errorMessage) for invalid client requests (400 Bad Request), often with an error message in the body. Internal server errors: Use ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage) for unexpected server-side issues (500 Internal Server Error). Conditional responses: Use headers like ETag, Last-Modified with appropriate status codes like 304 Not Modified using ResponseEntity. Lombok Constructors # @NoArgsConstructor # Creates constructor with no parameters Handles final fields by initializing to 0/false/null (adds compiler warning) Use force = true to force final fields to 0/false/null (suppresses warning) Use Case: # JPA entity classes (require no-arg constructor) Frameworks like Hibernate that use reflection Deserialization (JSON → Object) @AllArgsConstructor # Order of parameters matches field declaration order Includes final and non-final fields Handles @NonNull fields (null checks) Use Case # Quick object initialization(Value Object) Testing (creating objects with specific state) Immutable configuration objects @RequiredArgsConstructor # Generates a constructor for final fields and @NonNull fields. Use Case # Dependency injection Immutable data Best Practices: # Prefer @RequiredArgsConstructor for DI Use @NoArgsConstructor only when required by frameworks Combine with @Data or @Value for full POJOs Lombok Annotations Best Practice # @Getter / @Setter Use Case: Replace trivial getters/setters. Best Practices: Avoid on final fields: Setters aren\u0026rsquo;t generated. Limit Scope: Use @Getter(AccessLevel.PROTECTED) for encapsulation. Exclude Collections: Avoid exposing mutable collections directly. @ToString Use Case: Debugging/logging representations. Best Practices: Exclude Sensitive Data: @ToString(exclude = \u0026quot;password\u0026quot;). Handle Inheritance: Use @ToString(callSuper = true) for superclass fields. Avoid Circular References: Exclude bidirectional relationships. @ToString(callSuper = true, exclude = \u0026#34;user\u0026#34;) public class Profile { ... } @EqualsAndHashCode Use Case: Value-based equality. Best Practices: Always Use callSuper = true for subclass equality checks. Exclude Lazy-Loaded Fields: In JPA entities, exclude OneToMany/collections. Use Immutable Fields: Only include fields that never change. @EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true) public class User { @EqualsAndHashCode.Include private final Long id; // Immutable ID only } Constructor Annotations @NoArgsConstructor JPA Requirement: Use with access = AccessLevel.PROTECTED. Avoid in immutable objects. @NoArgsConstructor(access = AccessLevel.PROTECTED) // JPA-safe public class Entity { ... } @RequiredArgsConstructor Dependency Injection: Perfect for final Spring beans. Replace explicit @Autowired constructors. @Service @RequiredArgsConstructor // Injects final dependencies public class OrderService { private final OrderRepository repo; } @AllArgsConstructor Avoid in Entities: Risk of field-order mismatch. Use for DTOs/Records: Simple data carriers. @AllArgsConstructor // For non-persistent objects public record ProductDto(String id, String name) {} @Data Use Case: Mutable data containers (e.g., configuration DTOs). Best Practices: Avoid in JPA Entities: Combines risky @ToString/@EqualsAndHashCode. Never Extend Classes: Breaks equals()/hashCode() without callSuper=true. @Data // Only for simple POJOs with no inheritance public class Config { private String apiKey; private int timeout; } @Value Use Case: Immutable objects (Java 16+ records preferred). Best Practices: Replace with Records: Prefer record in modern Java. Use for Thread-Safe Objects: Immutability by default. @Value // Immutable class public class Point { int x; int y; } @Builder Use Case: Complex object creation. Best Practices: Customize with @Builder(builderMethodName = \u0026quot;hiddenBuilder\u0026quot;). Set Defaults: Use @Builder.Default for field initializers. Immutable Builders: Pair with @Value. @Builder public class Widget { @Builder.Default private String type = \u0026#34;DEFAULT\u0026#34;; private final int id; } @Slf4j Use Case: Logger injection. Best Practice: Replace manual logger declarations. @Slf4j public class OrderService { void process() { log.info(\u0026#34;Order processed\u0026#34;); // Injects \u0026#39;log\u0026#39; } } @SneakyThrows Use Case: Edge cases like rethrowing checked exceptions. Best Practices: Avoid Mainstream Logic: Use sparingly. Document Why: Explain why exception is unchecked. @SneakyThrows(IOException.class) // Rarely justified public void unsafeOperation() { ... } JPA-Specific Practices Avoid @Data/@EqualsAndHashCode: Use hand-written equals() with primary key checks. Lazy Fields: Exclude from @ToString: @Entity @Getter @Setter // Safe public class User { @ToString.Exclude private byte[] passwordHash; @EqualsAndHashCode.Exclude @OneToMany private List\u0026lt;Order\u0026gt; orders; } Testing Considerations Verify Generated Code: Ensure Lombok doesn\u0026rsquo;t break complex logic. Customize for Frameworks: @Value @AllArgsConstructor(access = AccessLevel.PRIVATE) // For Jackson public class TestDto { ... } Advanced: Configuration File Global Settings: Add lombok.config: # Fail on warnings config.stopBubbling = true lombok.anyConstructor.addConstructorProperties = true Annotation Do Don\u0026rsquo;t @Data Simple DTOs/config objects JPA entities, inherited classes @Value Immutable objects (prefer records) Mutable objects @Builder Complex construction, DTOs Entities with JPA constraints @RequiredArgsConstructor Spring dependency injection Classes with non-final dependencies @ToString Debugging, exclude sensitive fields Entities with relationships @EqualsAndHashCode Immutable values, include superclass Entities (use primary key manually) Summary for best practices # Annotation Use Case \u0026amp; Notes @RequiredArgsConstructor Preferred for constructor injection in Spring beans @Getter, @Setter Use on fields or class; avoid on JPA entities @Slf4j For logging (no manual logger needed) @Data For DTOs only (not JPA entities) @Builder For DTOs, not JPA entities @Value For immutable DTOs @NoArgsConstructor(force = true) For JPA entities Java Records: Immutable Data Carriers # Eliminate boilerplate code for data-oriented classes. Key Features\nAuto-Generated Components: final fields (private) Public constructor (canonical) Accessor methods (field() instead of getField()) equals(), hashCode(), toString() Immutability: All fields are implicitly final No setter methods Syntax public record Point(int x, int y) {} // Equivalent Traditional Class public final class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } public int x() { return x; } public int y() { return y; } // Auto-generated: // - equals() (compares all fields) // - hashCode() (uses all fields) // - toString() (e.g., \u0026#34;Point[x=1, y=2]\u0026#34;) } Use Cases Examples Data Transfer Objects (DTOs) API responses, DB results Value objects Currency, Coordinates Tuple-like structures Pair\u0026lt;K,V\u0026gt;, Coordinate Temporary data containers Method return values Advanced Usage # Custom Constructors public record Point(int x, int y) { // Compact constructor (no parameters) public Point { if (x \u0026lt; 0 || y \u0026lt; 0) throw new IllegalArgumentException(\u0026#34;Negative values\u0026#34;); // Fields auto-assigned after this block } } Add Methods public record Point(int x, int y) { public double distance() { return Math.sqrt(x*x + y*y); } } Implement Interfaces public record User(String name) implements Serializable {} Annotations public record User( @NotBlank String name, @Min(18) int age ) {} Limitations # Cannot: Extend other classes (implicitly final) Declare non-final fields Add instance initializers Restrictions: All fields defined in header No abstract records Accessors must match field names Records vs Traditional Classes # Characteristic Record Class Boilerplate Auto-generated Manual coding Immutability Enforced Optional Inheritance No extension (final) Extendable Constructor Compact syntax Explicit assignment Accessors x() instead of getX() Traditional getters Purpose Data carriers Complex behavior Records vs Lombok @Data # Feature Java Record Lombok @Data Language feature Native (JVM) Compiler plugin Immutability Enforced Optional (final fields) Serialization Built-in support Manual configuration Pattern Matching Works with instanceof No special support Validation Compact constructor Manual validation Best Practices # Use for simple data carriers: DTOs, configuration, coordinates Avoid: Adding mutable state Complex business logic Inheritance hierarchies Prefer over Lombok for new projects (native solution) Validate data in compact constructors Example: REST API DTO\npublic record ApiResponse\u0026lt;T\u0026gt;( int status, String message, T data ) { public ApiResponse { Objects.requireNonNull(data); } } // Usage ApiResponse\u0026lt;User\u0026gt; response = new ApiResponse\u0026lt;\u0026gt;(200, \u0026#34;OK\u0026#34;, user); Model Mapper in Spring Boot # ModelMapper is a Java library that helps you easily map one object to another, reducing repetitive code and improving maintainability—especially useful for converting between entities and DTOs in Spring Boot applications.\nDefine a ModelMapper bean for dependency injection:\n@Configuration public class ModelMapperConfig { @Bean public ModelMapper modelMapper() { return new ModelMapper(); } } Then inject and use it in services or controllers:\n@Service public class UserService { private final ModelMapper modelMapper; @Autowired public UserService(ModelMapper modelMapper) { this.modelMapper = modelMapper; } public UserDTO convertToDto(User user) { return modelMapper.map(user, UserDTO.class); } } Custom Mappings # For more complex cases (different field names, nested objects), we can configure mappings:\nmodelMapper .typeMap(Source.class, Destination.class) .addMapping(Source::getFoo, Destination::setBar); Modern and recommended folder structure # src └── main ├── java │ └── com │ └── yourcompany │ └── yourproject │ ├── YourProjectApplication.java │ ├── config │ │ └── (configuration classes: security, web, etc.) │ ├── controller │ │ └── (REST/controllers) │ ├── dto │ │ └── (Data Transfer Objects) │ ├── entity │ │ └── (JPA entities/domain models) │ ├── exception │ │ └── (custom exceptions and handlers) │ ├── repository │ │ └── (Spring Data repositories) │ ├── service │ │ └── (service layer) │ ├── mapper │ │ └── (MapStruct/ModelMapper mappers) │ ├── util │ │ └── (utility/helper classes) │ └── (any feature-based packages, e.g., user, order, etc.) ├── resources │ ├── application.yml │ ├── static │ │ └── (static web resources) │ ├── templates │ │ └── (Thymeleaf/Freemarker templates) │ └── (other resource files) └── test └── java └── com └── yourcompany └── yourproject └── (test classes, mirroring main structure) config: Java configuration classes (security, CORS, Swagger/OpenAPI, etc.) controller: REST or web controllers (@RestController, @Controller) dto: Data Transfer Objects for requests \u0026amp; responses entity: JPA/Hibernate entities (database models) exception: Custom exceptions and exception handlers (@ControllerAdvice) repository: Spring Data repository interfaces service: Business logic/services (@Service) mapper: Classes/interfaces for mapping between entities and DTOs (e.g., using MapStruct or ModelMapper) util: Utility or helper classes (validation, constants, etc.) For larger projects, use feature-based modularization:\n└── user ├── UserController.java ├── UserService.java ├── UserRepository.java ├── User.java ├── UserDto.java └── UserMapper.java └── order └── (order-related classes) Async # package com.yourcompany.yourapp; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; @Configuration @EnableAsync public class AsyncConfig { @Bean(name = \u0026#34;taskExecutor\u0026#34;) public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(4); // Number of threads to keep in pool executor.setMaxPoolSize(10); // Max threads allowed executor.setQueueCapacity(100); // Queue size before rejecting new tasks executor.setThreadNamePrefix(\u0026#34;Async-\u0026#34;); // Thread name prefix for easier debugging executor.initialize(); return executor; } // You can also add an AsyncUncaughtExceptionHandler bean to handle uncaught exceptions from @Async void methods @Bean public AsyncUncaughtExceptionHandler asyncUncaughtExceptionHandler() { return (throwable, method, obj) -\u0026gt; { System.err.println(\u0026#34;Exception in async method: \u0026#34; + method.getName() + \u0026#34;, message: \u0026#34; + throwable.getMessage()); }; } } Stream DAO # package com.example.validation.config; import org.kie.api.runtime.KieContainer; import org.kie.spring.KModuleBeanFactoryPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class KieConfig { @Bean public KModuleBeanFactoryPostProcessor kiePostProcessor() { return new KModuleBeanFactoryPostProcessor(); } @Bean public KieContainer kieContainer(org.kie.api.KieServices ks) { return ks.getKieClasspathContainer(); } } package com.example.validation.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Configuration public class VirtualThreadConfig { @Bean(destroyMethod = \u0026#34;shutdown\u0026#34;) public ExecutorService virtualThreadExecutor() { // Java 21 virtual thread-per-task executor return Executors.newVirtualThreadPerTaskExecutor(); } } @Repository public class BigTableDao { private final JdbcTemplate jdbcTemplate; public BigTableDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public void streamRows(int fetchSize, Consumer\u0026lt;RowData\u0026gt; rowConsumer) { jdbcTemplate.setFetchSize(fetchSize); jdbcTemplate.setMaxRows(0); String sql = \u0026#34;SELECT * FROM big_table\u0026#34;; // no ORDER BY if not required jdbcTemplate.query(con -\u0026gt; { PreparedStatement ps = con.prepareStatement( sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY ); ps.setFetchSize(fetchSize); return ps; }, rs -\u0026gt; { rowConsumer.accept(mapRow(rs)); }); } private RowData mapRow(ResultSet rs) throws SQLException { RowData row = new RowData(); row.setId(rs.getLong(\u0026#34;id\u0026#34;)); row.setCol1(rs.getString(\u0026#34;col1\u0026#34;)); row.setCol2(rs.getString(\u0026#34;col2\u0026#34;)); // map other columns... return row; } } @Service public class StreamingValidationService { private final BigTableDao bigTableDao; private final KieContainer kieContainer; private final ValidationResultRepository resultRepository; public StreamingValidationService(BigTableDao bigTableDao, KieContainer kieContainer, ValidationResultRepository resultRepository) { this.bigTableDao = bigTableDao; this.kieContainer = kieContainer; this.resultRepository = resultRepository; } public void validateLargeTable() { List\u0026lt;RowData\u0026gt; batch = new ArrayList\u0026lt;\u0026gt;(500); bigTableDao.streamRows(500, row -\u0026gt; { batch.add(row); if (batch.size() == 500) { processBatch(batch); batch.clear(); } }); if (!batch.isEmpty()) { processBatch(batch); } } private void processBatch(List\u0026lt;RowData\u0026gt; batch) { List\u0026lt;ValidationResult\u0026gt; allResults = Collections.synchronizedList(new ArrayList\u0026lt;\u0026gt;()); try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { List\u0026lt;Callable\u0026lt;Void\u0026gt;\u0026gt; tasks = new ArrayList\u0026lt;\u0026gt;(); for (RowData row : batch) { tasks.add(() -\u0026gt; { StatelessKieSession kieSession = kieContainer.newStatelessKieSession(); List\u0026lt;ValidationResult\u0026gt; results = new ArrayList\u0026lt;\u0026gt;(); kieSession.setGlobal(\u0026#34;results\u0026#34;, results); kieSession.execute(row); // Stateless: runs rules immediately allResults.addAll(results); return null; }); } executor.invokeAll(tasks); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(\u0026#34;Batch processing interrupted\u0026#34;, e); } // Single DB write per batch resultRepository.bulkInsert(allResults); } } package com.example.validation.config; import org.kie.api.KieServices; import org.kie.api.builder.KieFileSystem; import org.kie.api.builder.KieBuilder; import org.kie.api.builder.ReleaseId; import org.kie.api.builder.model.KieModuleModel; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.StatelessKieSession; import org.kie.api.runtime.KieBase; import org.kie.api.runtime.KieBaseConfiguration; import org.kie.api.runtime.conf.EventProcessingOption; import org.kie.api.runtime.KieContainer; import org.kie.spring.KModuleBeanFactoryPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; @Configuration public class KieConfig { @Bean public KModuleBeanFactoryPostProcessor kiePostProcessor() { return new KModuleBeanFactoryPostProcessor(); } /** * Build KieContainer by loading provided DRL files from classpath or file system. * For simplicity, assume drlFiles are classpath resources (e.g. \u0026#34;rules/myrules.drl\u0026#34;) */ @Bean public KieContainer kieContainer(KieServices ks) { List\u0026lt;String\u0026gt; drlFiles = List.of(\u0026#34;rules/rule1.drl\u0026#34;, \u0026#34;rules/rule2.drl\u0026#34;); // \u0026lt;- Inject or configure this list KieFileSystem kfs = ks.newKieFileSystem(); // Create a minimal KieModule model (optional, but good practice) KieModuleModel kModuleModel = ks.newKieModuleModel(); kfs.writeKModuleXML(kModuleModel.toXML()); // Add DRL files from classpath to the KieFileSystem for (String drl : drlFiles) { String path = \u0026#34;src/main/resources/\u0026#34; + drl; // adjust if needed kfs.write(\u0026#34;src/main/resources/\u0026#34; + drl, ks.getResources().newClassPathResource(drl)); } KieBuilder kieBuilder = ks.newKieBuilder(kfs).buildAll(); if (kieBuilder.getResults().hasMessages(org.kie.api.builder.Message.Level.ERROR)) { throw new RuntimeException(\u0026#34;Errors while building Drools rules: \u0026#34; + kieBuilder.getResults()); } ReleaseId releaseId = kieBuilder.getKieModule().getReleaseId(); return ks.newKieContainer(releaseId); } } @Configuration public class KieConfig { private static final List\u0026lt;String\u0026gt; poolDrlList = List.of( \u0026#34;rules/poolRule1.drl\u0026#34;, \u0026#34;rules/poolRule2.drl\u0026#34; ); private static final List\u0026lt;String\u0026gt; loanDrlList = List.of( \u0026#34;rules/loanRule1.drl\u0026#34;, \u0026#34;rules/loanRule2.drl\u0026#34; ); @Bean public KModuleBeanFactoryPostProcessor kiePostProcessor() { return new KModuleBeanFactoryPostProcessor(); } @Bean(name = \u0026#34;poolKieContainer\u0026#34;) public KieContainer poolKieContainer(KieServices ks) { return createKieContainer(ks, poolDrlList); } @Bean(name = \u0026#34;loanKieContainer\u0026#34;) public KieContainer loanKieContainer(KieServices ks) { return createKieContainer(ks, loanDrlList); } private KieContainer createKieContainer(KieServices ks, List\u0026lt;String\u0026gt; drlFiles) { KieFileSystem kfs = ks.newKieFileSystem(); // Minimal KieModule XML KieModuleModel kModuleModel = ks.newKieModuleModel(); kfs.writeKModuleXML(kModuleModel.toXML()); // Write DRL files to KieFileSystem for (String drl : drlFiles) { kfs.write(\u0026#34;src/main/resources/\u0026#34; + drl, ks.getResources().newClassPathResource(drl)); } KieBuilder kieBuilder = ks.newKieBuilder(kfs).buildAll(); if (kieBuilder.getResults().hasMessages(org.kie.api.builder.Message.Level.ERROR)) { throw new RuntimeException(\u0026#34;Error building KIE base: \u0026#34; + kieBuilder.getResults()); } ReleaseId releaseId = kieBuilder.getKieModule().getReleaseId(); return ks.newKieContainer(releaseId); } } @Service public class PoolValidationService { private final KieContainer poolKieContainer; public PoolValidationService(@Qualifier(\u0026#34;poolKieContainer\u0026#34;) KieContainer poolKieContainer) { this.poolKieContainer = poolKieContainer; } public void validatePoolData(RowData rowData) { try (StatelessKieSession session = poolKieContainer.newStatelessKieSession()) { session.execute(rowData); } } } @Service public class LoanValidationService { private final KieContainer loanKieContainer; public LoanValidationService(@Qualifier(\u0026#34;loanKieContainer\u0026#34;) KieContainer loanKieContainer) { this.loanKieContainer = loanKieContainer; } public void validateLoanData(RowData rowData) { try (StatelessKieSession session = loanKieContainer.newStatelessKieSession()) { session.execute(rowData); } } } @Service public class ValidationService { private final KieContainer poolKieContainer; private final KieContainer loanKieContainer; public ValidationService( @Qualifier(\u0026#34;poolKieContainer\u0026#34;) KieContainer poolKieContainer, @Qualifier(\u0026#34;loanKieContainer\u0026#34;) KieContainer loanKieContainer ) { this.poolKieContainer = poolKieContainer; this.loanKieContainer = loanKieContainer; } public void validatePool(RowData poolRow) { try (StatelessKieSession session = poolKieContainer.newStatelessKieSession()) { session.execute(poolRow); } } public void validateLoan(RowData loanRow) { try (StatelessKieSession session = loanKieContainer.newStatelessKieSession()) { session.execute(loanRow); } } // Or a generic method, if you can detect type or pass an enum param: public void validate(RowData data, ValidationType type) { KieContainer container = switch (type) { case POOL -\u0026gt; poolKieContainer; case LOAN -\u0026gt; loanKieContainer; }; try (StatelessKieSession session = container.newStatelessKieSession()) { session.execute(data); } } public enum ValidationType { POOL, LOAN } } public enum ValidationRuleSet { POOL(List.of(\u0026#34;rules/poolRule1.drl\u0026#34;, \u0026#34;rules/poolRule2.drl\u0026#34;)), LOAN(List.of(\u0026#34;rules/loanRule1.drl\u0026#34;, \u0026#34;rules/loanRule2.drl\u0026#34;)); private final List\u0026lt;String\u0026gt; drlFiles; ValidationRuleSet(List\u0026lt;String\u0026gt; drlFiles) { this.drlFiles = drlFiles; } public List\u0026lt;String\u0026gt; getDrlFiles() { return drlFiles; } } @Configuration public class KieConfig { @Bean public KModuleBeanFactoryPostProcessor kiePostProcessor() { return new KModuleBeanFactoryPostProcessor(); } @Bean(name = \u0026#34;poolKieContainer\u0026#34;) public KieContainer poolKieContainer(KieServices ks) { return createKieContainer(ks, ValidationRuleSet.POOL); } @Bean(name = \u0026#34;loanKieContainer\u0026#34;) public KieContainer loanKieContainer(KieServices ks) { return createKieContainer(ks, ValidationRuleSet.LOAN); } private KieContainer createKieContainer(KieServices ks, ValidationRuleSet ruleSet) { List\u0026lt;String\u0026gt; drlFiles = ruleSet.getDrlFiles(); KieFileSystem kfs = ks.newKieFileSystem(); KieModuleModel kModuleModel = ks.newKieModuleModel(); kfs.writeKModuleXML(kModuleModel.toXML()); for (String drl : drlFiles) { kfs.write(\u0026#34;src/main/resources/\u0026#34; + drl, ks.getResources().newClassPathResource(drl)); } KieBuilder kieBuilder = ks.newKieBuilder(kfs).buildAll(); if (kieBuilder.getResults().hasMessages(org.kie.api.builder.Message.Level.ERROR)) { throw new RuntimeException(\u0026#34;Error building KIE base: \u0026#34; + kieBuilder.getResults()); } ReleaseId releaseId = kieBuilder.getKieModule().getReleaseId(); return ks.newKieContainer(releaseId); } } ","date":"27 December 2023","externalUrl":null,"permalink":"/notes/spring_boot/","section":"Notes","summary":"Spring bean # an instance of a class managed by the Spring container show all default beans # // within main method ApplicationContext apc = SpringApplication.run(ClassName.class) for (String s : apc.getBeanDefinitionNames()) { System.out.println(s); } Spring container # part of the core of the Spring framework managing all beans: it decides when to create this instance, when to kill this instance, and how to initialize the instance, etc. performs dependency injection IoC(Inversion of Control) # instead of the programmer really deciding the flow of the application, deciding what objects are created, etc. this all handed over to the Spring framework(or more precisely to the Spring container) Dependency Injection # IoC entails Dependency Injection\n","title":"Spring Boot","type":"notes"},{"content":" Install poetry # pip install poetry Create a new project # # in work directory poetry new name_of_project Manage dependencies # 1. modify pyproject.toml # [tool.poetry.dependencies] scikit-learn = \u0026#34;*\u0026#34; 2. CLI # poetry add pytest best practice for testing dependencies # [tool.poetry.group.dev.dependencies] pytest = \u0026#34;^7.2.1\u0026#34; pytest-mock = \u0026#34;*\u0026#34; Activate environment in terminal # # activate poetry shell # deactivate exit Install dependencies specified in pyproject.toml file, or .lock file # poetry install Run with Poetry-configured Env via CLI # poetry run python xxx.py Building a library # poetry build Publish to PyPI # poetry publish –r private-repository-location ","date":"13 December 2023","externalUrl":null,"permalink":"/notes/poetry/","section":"Notes","summary":"Install poetry # pip install poetry Create a new project # # in work directory poetry new name_of_project Manage dependencies # 1. modify pyproject.toml # [tool.poetry.dependencies] scikit-learn = \"*\" 2. CLI # poetry add pytest best practice for testing dependencies # [tool.poetry.group.dev.dependencies] pytest = \"^7.2.1\" pytest-mock = \"*\" Activate environment in terminal # # activate poetry shell # deactivate exit Install dependencies specified in pyproject.toml file, or .lock file # poetry install Run with Poetry-configured Env via CLI # poetry run python xxx.py Building a library # poetry build Publish to PyPI # poetry publish –r private-repository-location","title":"Poetry","type":"notes"},{"content":" Pytest # 1. for files whose name leading with test_ # pytest 2. for single file the name of functions leading with test_ # def test_fun1(): assert 1 == 3 run pytest # python -m pytest XXX.py ","date":"13 December 2023","externalUrl":null,"permalink":"/notes/pytest/","section":"Notes","summary":"Pytest # 1. for files whose name leading with test_ # pytest 2. for single file the name of functions leading with test_ # def test_fun1(): assert 1 == 3 run pytest # python -m pytest XXX.py","title":"Pytest","type":"notes"},{"content":" RDD # Resilient distributed dataset Fault tolerance distributed immutable data structure, stored in disk or memory There are multiple partitions(#partitions is the same as #machines) within one RDD DN(Data Node to store data) and Worker(to compute) are all in one machine(best practice) only shuffle need data transfering with network, others are in local machine reduceByKey # hadoop ---\u0026gt; (hadoop, 1) hadoop ---\u0026gt; (hadoop, 1) hadoop ---\u0026gt; (hadoop, 1) hive ---\u0026gt; (hive, 1) hive ---\u0026gt; (hive, 1) hive ---\u0026gt; (hive, 1) reduceByKey \u0026lt;hadoop, [1, 1, 1]\u0026gt; \u0026lt;hive, [1, 1, 1]\u0026gt; shuffle hash(hadoop) ---\u0026gt; hashcode % 3 = 1(machine 1) hash(hive) ---\u0026gt; hashcode % 3 = 2(machine 2) operations # transformation: RDD\u0026ndash;\u0026gt;RDD # Operations Summary map return a new RDD filter return a new RDD; true: keep, false: remove flatMap map + flat(from two dimentional to one dimentional) groupByKey reduceByKey groupByKey(get \u0026lt;hadoop, [1, 1, 1]\u0026gt;) + map(get \u0026lt;hadoop, 3\u0026gt;) sortByKey not default in spark, but default in hadoop join cogroup + remove nulls: join by key of \u0026lt;key, value\u0026gt;, all pairs will handle by cutomized function cogroup full join action: RDD\u0026ndash;\u0026gt;anything else # Operations Summary reduce operation on all elements within RDD, first merges with second element, and then the result merges with the third element, \u0026hellip; collect get all elements within RDD to local client count get the total number of elements within RDD take(n) get first unsorted n elements within RDD, top(n) can have first sorted n elements takeOrdered(n, [ordering]) get first sorted n elements within RDD using natural order or a custom comparator saveAsTextFile save to file, each element with toString method countByKey foreach iterate each element within RDD Depencencies(This topic is Transformation operation related) # with shuffle(internet connections between different machines) is wide dependency without shuffle is narrow dependency the number of stages is depended on the number of wide dependencies\nnarrow dependencies - 1 : 1 # 每个父RDD的分区都至多被一个子RDD的分区使用。一对一\n输入输出一对一，结果RDD的分区结构不变，主要是map, flatMap 输入输出一对一，但结果RDD的分区结构发生变化，如union, coalesce 从输入中选择部分元素的算子，如filter, distinct, subtract, sample wide dependencies - multiple : multiple # 多个子RDD的分区依赖一个父RDD的分区。一对多\n对单个RDD基于key进行分组，如groupByKey, reduceByKey 对两个RDD基于key进行join，如join Create sparkContext # from pyspark.sql import SparkSession spark = SparkSession.builder.master(\u0026#34;spark://localhost:7077\u0026#34;).appName(\u0026#34;rdd_demos\u0026#34;).getOrCreate() sc = spark.sparkContext Create RDD # 1. parallelize array # # parallelize array in memory to crete RDD arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # run in client rdd1 = spark.sparkContext.parallelize(arr1) # run in server # This is an Action, return RDD to Driver rdd1.collect() # check the number of partitions of current RDD rdd1.getNumPartitions() # OR # create RDD like this # range(start = 0, end, step = 1) rdd2 = spark.sparkContext.parallelize(range(3, 11, 1)) rdd2.collect() 2. load data from outside # txt file file = \u0026#34;/opt/module/spark-3.5.0-bin-hadoop3/data/core/data/workcount.txt\u0026#34; rdd3 = spark.sparkContext.textfile(file) rdd3.collect() json file jsonFile = \u0026#34;/opt/module/spark-3.5.0-bin-hadoop3/data/core/data/people.json\u0026#34; jsonRDD = sc.textFile(jsonFile) jsonRDD.collect() # python use json module to handle json file import json result = jsonRDD.map(lambda line: json.loads(line)) result.collect() # for d in result.collect(): # print(d) spark通过textFile读取hdfs数据分区数量规则 Spark：RDD数据分区数量总结(并行化集合parallelize与外部数据集textFile) RDD Operations # Spark RDD 创建操作 transformation: RDD\u0026ndash;\u0026gt;RDD # # Suppose we have an RDD, including {1, 2, 3, 3} # first, create a RDD data = spark.sparkContext.parallelize([1, 2, 3, 3]) # map data_rdd1 = data.map(lambda x: x + 1) data_rdd1.collect() # [2, 3, 4, 4] # flatMap data_rdd2 = data.flatMap(lambda x: range(x, 4)) data_rdd2.collect() # [1, 2, 3, 2, 3, 3, 3] # filter data_rdd3 = data.filter(lambda x: x != 1) data_rdd3.collect() # [2, 3, 3] # distinct data_rdd4 = data.distinct() data_rdd4.collect() # [1, 2, 3] # sample: sample(withReplacement, fraction, seed) # withReplacement param: 是否放回采样 # fraction param: 抽取比例 # seed param: random seed(optional) data_rdd5 = data.sample(False, 0.5) data_rdd5.collect() # takeSample: Action not Transformation # sample + take(2) data_rdd51 = data.takeSample(False, 2) print(data_rdd51) # Suppose we have two RDDs, {1, 2, 3, 3} and {3, 4, 5} # first, create two RDDs data1 = spark.sparkContext.parallelize([1, 2, 3, 3]) data2 = spark.sparkContext.parallelize([3, 4, 5]) # union # similar to union all in mysql, not union in mysql data1.union(data2).collect() # [1, 2, 3, 3, 3, 4, 5] # intersection data1.intersection(data2).collect() # [3] # subtract data1.subtract(data2).collect() # [1, 2] # cartesian data1.cartesian(data2).collect() # [(1, 3), # (1, 4), # (1, 5), # (2, 3), # (2, 4), # (2, 5), # (3, 3), # (3, 4), # (3, 5), # (3, 3), # (3, 4), # (3, 5)] # groupBy a = spark.sparkContext.parallelize([\u0026#34;black\u0026#34;, \u0026#34;blue\u0026#34;, \u0026#34;white\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;grey\u0026#34;]) b = a.groupBy(lambda x: len(x)).collect() print(b) # [(4, \u0026lt;pyspark.resultiterable.ResultIterable object at 0x7f95187af580\u0026gt;), (5, \u0026lt;pyspark.resultiterable.ResultIterable object at 0x7f95087b5c10\u0026gt;)] sorted([(x, sorted(y)) for (x, y) in b]) # [(4, [\u0026#39;blue\u0026#39;, \u0026#39;grey\u0026#39;]), (5, [\u0026#39;black\u0026#39;, \u0026#39;green\u0026#39;, \u0026#39;white\u0026#39;])] action: RDD\u0026ndash;\u0026gt;anything else # # create a RDD rdd = spark.sparkContext.parallelize([1, 2, 3, 3]) rdd.count() # 4 rdd.collect() # [1, 2, 3, 3] rdd.first() # 1 rdd.countByValue() # defaultdict(int, {1: 1, 2: 1, 3: 2}) rdd.take(2) # [1, 2] rdd.takeOrdered(2) # [1, 2] rdd.takeOrdered(2, key=lambda x: -x) # [3, 3] rdd.takeSample(False, 2) rdd.reduce(lambda x, y: x + y) # 9 # reduce # x:(\u0026#34;\u0026#34;, 0), y:(\u0026#34;hadoop\u0026#34;, 1) # x:(\u0026#34;hadoop\u0026#34;, 1), y:(\u0026#34;hadoop\u0026#34;, 1) # x:(\u0026#34;hadoophadoop\u0026#34;, 2) # reduceByKey: ignore key, only care value # x:0, y:1 # x:1, y:1 # x:2, y:1 rdd.fole(0, lambda x, y: x + y) # fold # has an initial value for each partition and one more for merging # aggregate(zeroValue, seqOp, combOp) print(\u0026#34;RDD 当前的分区数是: \u0026#34;, rdd.getNumPartitions()) # RDD 当前的分区数是: 8 seqOp = (lambda x, y: x * y) # 每个分区执行的函数 combOp = (lambda x, y: x + y) # 各个分区结果最后聚集时使用的函数 result = rdd.aggregate(2, seqOp, combOp) result # 28 seqOp = (lambda x, y: (x[0] + y, x[1] + 1)) combOp = (lambda x, y: (x[0] + y[0], x[1] + y[1])) result1 = spark.sparkContext.parallelize([1, 2, 3, 4]).aggregate((0, 0), seqOp, combOp) print(result1) # (10, 4) result2 = spark.sparkContext.parallelize([]).aggregate((0, 0), seqOp, combOp) print(result2) # (0, 0) reduce is the special case of fold fold is the special case of aggregate RDD action on numeric data(description statistics) # rdd1 = sc.parallelize(range(1, 21, 2)) rdd1.collect() rdd1.sum() rdd1.max() rdd1.min() # mean rdd1.mean() rdd1.count() # variance rdd1.variance() # sample variance rdd1.sampleVariance() # standard deviation rdd1.stdev() # sample standard deviation rdd1.sampleStdev() # Histogram reference: https://blog.csdn.net/hit0803107/article/details/52807485 # Approach 1 rdd1.histogram([1.0, 8.0, 20.9]) # Approach 2 rdd1.histogram(3) # 通过调用stats()方法，返回一个StatsCounter对象 status = rdd1.stats() print(status.sum()) print(status.max()) print(status.min()) print(status.mean()) print(status.count()) print(status.variance()) print(status.stdev()) Pair RDD Operations # create Pair RDD # # There are multiple ways to create Pair RDD # Approach 1: load from file, and then transform to Pair RDD file = \u0026#34;/data/spark_demo/rdd/wc.txt\u0026#34; lines = spark.sparkContext.textFile(file) pairRDD = lines.flatMap(lambda line: line.split(\u0026#34; \u0026#34;)).map(lambda word: (word, 1)) pairRDD.collect() # Approach 2: parallelize array rdd = spark.sparkContext.parallelize([\u0026#34;Hadoop\u0026#34;, \u0026#34;Spark\u0026#34;, \u0026#34;Hive\u0026#34;, \u0026#34;Spark\u0026#34;]) pairRDD = rdd.map(lambda word: (word, 1)) pairRDD.collect() # [(\u0026#39;Hadoop\u0026#39;, 1), (\u0026#39;Spark\u0026#39;, 1), (\u0026#39;Hive\u0026#39;, 1), (\u0026#39;Spark\u0026#39;, 1)] # Approach 3: keyBy(): customize the rule for key grouping a = spark.sparkContext.parallelize([\u0026#34;black\u0026#34;, \u0026#34;blue\u0026#34;, \u0026#34;white\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;grey\u0026#34;]) # with cutomized function to create keys, return Pair RDD b = a.keyBy(lambda x: len(x)) b.collect() # [(5, \u0026#39;black\u0026#39;), (4, \u0026#39;blue\u0026#39;), (5, \u0026#39;white\u0026#39;), (5, \u0026#39;green\u0026#39;), (4, \u0026#39;grey\u0026#39;)] # Approach 4: creating with tuple pets = spark.sparkContext.parallelize([(\u0026#34;cat\u0026#34;, 1), (\u0026#34;dog\u0026#34;, 1), (\u0026#34;cat\u0026#34;, 2)]) pets.collect() # [(\u0026#39;cat\u0026#39;, 1), (\u0026#39;dog\u0026#39;, 1), (\u0026#39;cat\u0026#39;, 2)] transformation on Pair RDD # reduceByKey is special case of aggregateByKey aggregateByKey is special case of combineByKey # Suppose we have a pair RDD [(1, 2), (3, 4), (3, 6)] # Create Pair RDD pairRDD = spark.sparkContext.parallelize([(1, 2), (3, 4), (3, 6)]) pairRDD.collect() # reduceByKey(func) pairRDD.reduceByKey(lambda x, y: x + y).collect() # [(1, 2), (3, 10)] # groupByKey() pairRDD.groupByKey().collect() # [(1, \u0026lt;pyspark.resultiterable.ResultIterable at 0x7f94d9638970\u0026gt;), # (3, \u0026lt;pyspark.resultiterable.ResultIterable at 0x7f94d021f550\u0026gt;)] # keys: return all keys pairRDD.keys().collect() # [1, 3, 3] # values: return all values pairRDD.values().collect() # [2, 4, 6] # sortByKey(): default is increasing pairRDD.sortByKey().collect() # [(1, 2), (3, 4), (3, 6)] # pairRDD.sortByKey(ascending=False).collect() pairRDD.sortByKey(False).collect() # [(3, 4), (3, 6), (1, 2)] # mapValues(func): apply func to each element of Pair RDD, without chaning key pairRDD.mapValues(lambda x: x * x).collect() # [(1, 4), (3, 16), (3, 36)] # flatMapValues(func) pairRDD.flatMapValues(lambda x: range(x, 6)).collect() # [(1, 2), (1, 3), (1, 4), (1, 5), (3, 4), (3, 5)] # combineByKey() data = spark.sparkContext.parallelize([(\u0026#34;company-1\u0026#34;, 92), (\u0026#34;company-1\u0026#34;, 85), (\u0026#34;company-1\u0026#34;, 82),\\ (\u0026#34;company-1\u0026#34;, 93), (\u0026#34;company-1\u0026#34;, 86), (\u0026#34;company-1\u0026#34;, 83),\\ (\u0026#34;company-2\u0026#34;, 78), (\u0026#34;company-2\u0026#34;, 96), (\u0026#34;company-2\u0026#34;, 85),\\ (\u0026#34;company-3\u0026#34;, 88), (\u0026#34;company-3\u0026#34;, 94), (\u0026#34;company-3\u0026#34;, 80)], 3) cbk = data.combineByKey( lambda income: (income, 1), lambda t, income: (t[0] + income, t[1] + 1), lambda t1, t2: (t1[0] + t2[0], t1[1] + t2[1]) ) # total income of each company cbk.collect() # [(\u0026#39;company-1\u0026#39;, (521, 6)), (\u0026#39;company-3\u0026#39;, (262, 3)), (\u0026#39;company-2\u0026#39;, (259, 3))] # average income of each company cbk.map(lambda t: (t[0], t[1][0], t[1][0]/float(t[1][1]))).collect() # [(\u0026#39;company-1\u0026#39;, 521, 86.83333333333333), # (\u0026#39;company-3\u0026#39;, 262, 87.33333333333333), # (\u0026#39;company-2\u0026#39;, 259, 86.33333333333333)] # reduceByKey() x = spark.sparkContext.parallelize([(\u0026#34;a\u0026#34;, 1), (\u0026#34;b\u0026#34;, 1), (\u0026#34;a\u0026#34;, 1), (\u0026#34;a\u0026#34;, 1), (\u0026#34;b\u0026#34;, 1), (\u0026#34;b\u0026#34;, 1), (\u0026#34;b\u0026#34;, 1), (\u0026#34;b\u0026#34;, 1)], 2) # apply reduceByKey y = x.reduceByKey(lambda accum, n: accum + n) y.collect() # [(\u0026#39;b\u0026#39;, 5), (\u0026#39;a\u0026#39;, 3)] # customize func separately def sumFunc(accum, n): return accum + n y = x.reduceByKey(sumFunc) print(y.collect()) # [(\u0026#39;b\u0026#39;, 5), (\u0026#39;a\u0026#39;, 3)] # groupByKey() x = spark.sparkContext.parallelize([ (\u0026#34;USA\u0026#34;, 1), (\u0026#34;USA\u0026#34;, 2), (\u0026#34;India\u0026#34;, 1),\\ (\u0026#34;UK\u0026#34;, 1), (\u0026#34;India\u0026#34;, 4), (\u0026#34;India\u0026#34;, 9),\\ (\u0026#34;USA\u0026#34;, 8), (\u0026#34;USA\u0026#34;, 3), (\u0026#34;India\u0026#34;, 4),\\ (\u0026#34;UK\u0026#34;, 6), (\u0026#34;UK\u0026#34;, 9), (\u0026#34;UK\u0026#34;, 5)], 4) # utilize groupByKey, default partition y = x.groupByKey() # check partitions print(\u0026#34;number of partitions: \u0026#34;, y.getNumPartitions()) # utilize pre-defined partition y = x.groupByKey(2) print(\u0026#34;number of partitions: \u0026#34;, y.getNumPartitions()) # output result for t in y.collect(): print(t[0], [v for v in t[1]]) # aggregateByKey # utilize key-value pair creating pairRDD studentRDD student_rdd = spark.sparkContext.parallelize([ (\u0026#34;Joseph\u0026#34;, \u0026#34;Maths\u0026#34;, 83), (\u0026#34;Joseph\u0026#34;, \u0026#34;Physics\u0026#34;, 74), (\u0026#34;Joseph\u0026#34;, \u0026#34;Chemistry\u0026#34;, 91),\\ (\u0026#34;Joseph\u0026#34;, \u0026#34;Biology\u0026#34;, 82), (\u0026#34;Jimmy\u0026#34;, \u0026#34;Maths\u0026#34;, 69), (\u0026#34;Jimmy\u0026#34;, \u0026#34;Physics\u0026#34;, 62),\\ (\u0026#34;Jimmy\u0026#34;, \u0026#34;Chemistry\u0026#34;, 97), (\u0026#34;Jimmy\u0026#34;, \u0026#34;Biology\u0026#34;, 80), (\u0026#34;Tina\u0026#34;, \u0026#34;Maths\u0026#34;, 78),\\ (\u0026#34;Tina\u0026#34;, \u0026#34;Physics\u0026#34;, 73), (\u0026#34;Tina\u0026#34;, \u0026#34;Chemistry\u0026#34;, 68), (\u0026#34;Tina\u0026#34;, \u0026#34;Biology\u0026#34;, 87),\\ (\u0026#34;Thomas\u0026#34;, \u0026#34;Maths\u0026#34;, 87), (\u0026#34;Thomas\u0026#34;, \u0026#34;Physics\u0026#34;, 93), (\u0026#34;Thomas\u0026#34;, \u0026#34;Chemistry\u0026#34;, 91),\\ (\u0026#34;Thomas\u0026#34;, \u0026#34;Biology\u0026#34;, 74), (\u0026#34;Cory\u0026#34;, \u0026#34;Maths\u0026#34;, 56), (\u0026#34;Cory\u0026#34;, \u0026#34;Physics\u0026#34;, 65),\\ (\u0026#34;Cory\u0026#34;, \u0026#34;Chemistry\u0026#34;, 71), (\u0026#34;Cory\u0026#34;, \u0026#34;Biology\u0026#34;, 68), (\u0026#34;Jackeline\u0026#34;, \u0026#34;Maths\u0026#34;, 86),\\ (\u0026#34;Jackeline\u0026#34;, \u0026#34;Physics\u0026#34;, 62), (\u0026#34;Jackeline\u0026#34;, \u0026#34;Chemistry\u0026#34;, 75), (\u0026#34;Jackeline\u0026#34;, \u0026#34;Biology\u0026#34;, 83),\\ (\u0026#34;Juan\u0026#34;, \u0026#34;Maths\u0026#34;, 63), (\u0026#34;Juan\u0026#34;, \u0026#34;Physics\u0026#34;, 69), (\u0026#34;Juan\u0026#34;, \u0026#34;Chemistry\u0026#34;, 64),\\ (\u0026#34;Juan\u0026#34;, \u0026#34;Biology\u0026#34;, 60)], 2) # define Sequential Operation and Combiner Operation # Sequential Operation: find maximum score from single partition def seq_op(accumulator, element): if (accumulator \u0026gt; element[1]): return accumulator else: return element[1] # Combiner Operation: find maximum score from accumulators of all partitions def comb_op(accumulator1, accumulator2): if (accumulator1 \u0026gt; accumulator2): return accumulator1 else: return accumulator2 # In our scenario, zero value is 0, because we are trying to find the maximum score zero_val = 0 aggr_rdd = student_rdd.map(lambda t: (t[0], (t[1], t[2]))).aggregateByKey(zero_val, seq_op, comb_op) # Check output for tpl in aggr_rdd.collect(): print(tpl) # (\u0026#39;Jimmy\u0026#39;, 97) # (\u0026#39;Tina\u0026#39;, 87) # (\u0026#39;Thomas\u0026#39;, 93) # (\u0026#39;Joseph\u0026#39;, 91) # (\u0026#39;Cory\u0026#39;, 71) # (\u0026#39;Jackeline\u0026#39;, 86) # (\u0026#39;Juan\u0026#39;, 69) # redefine Sequential Operation and Combiner Operation def seq_op(accumulator, element): return (accumulator[0] + element[1], accumulator[1] + 1) def comb_op(accumulator1, accumulator2): return (accumulator1[0] + accumulator2[0], accumulator1[1] + accumulator2[1]) zero_val = (0, 0) aggr_rdd = student_rdd.map(lambda t: (t[0], (t[1], t[2])))\\ .aggregateByKey(zero_val, seq_op, comb_op)\\ .map(lambda t: (t[0], t[1][0]/t[1][1]*1.0)) for tpl in aggr_rdd.collect(): print(tpl) # (\u0026#39;Jimmy\u0026#39;, 77.0) # (\u0026#39;Tina\u0026#39;, 76.5) # (\u0026#39;Thomas\u0026#39;, 86.25) # (\u0026#39;Joseph\u0026#39;, 82.5) # (\u0026#39;Cory\u0026#39;, 65.0) # (\u0026#39;Jackeline\u0026#39;, 76.5) # (\u0026#39;Juan\u0026#39;, 64.0) RDD cache # overview # One of the reasons why Spark is so fast is that it can cache datasets in memory during different operations. When an RDD is cached, each node will save the partition computation result in memory and reuse them in other actions performed on this RDD or derived RDDs. This makes subsequenct actions much faster. Cache is key to building iterative algorithms and fast iteractive queries in Spark. approaches # persist cache only after triggering actions, then current RDD will be cached in memory for later use cache invoke persist at last, the default storage level just a part in memory /** Persist this RDD with the default storage level (MEMORY_ONLY) def persist(): this.type = persist(StorageLevel.MEMORY_ONLY) /** Persist this RDD with the default storage level (MEMORY_ONLY) def cache(): this.type = persist() There are many storage levels in Spark, storage level is defined in object StorageLevel object StorageLevel { val NONE = new StorageLevel(false, false, false, false) val DISK_ONLY = new StorageLevel(true, false, false, false) val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2) val MEMORY_ONLY = new StorageLevel(false, true, false, true) val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2) val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false) val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2) val MEMORY_AND_DISK = new StorageLevel(true, true, false, true) val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2) val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false) val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2) val OFF_HEAP = new StorageLevel(false, false, true, false) } Fault tolerance can recompute some failed partitions of RDD, not all partitions of all RDD partitions within a RDD are independent examples # from pyspark import StorageLevel rdd1.sc.textFile(\u0026#39;/xxxx\u0026#39;) rdd1.persist(StorageLevel.MEMORY_AND_DISK) # set storage level rdd2 = rdd1.flatMap(lambda line : line.split(\u0026#39; \u0026#39;)) rdd3 = rdd2.map(lambda word : (word, 1)) rdd4 = rdd3.reduceByKey(lambda a, b : a + b) rdd4.collect() Fault tolerance # mysql: bin_log日志文件（预写日志） hbase: wal(write ahead log: 预写日志) hdfs: 副本策略（3） overview # fault tolerance for spark: use Lineage and Checkpoint lineage mechanism: suit for narrow dependency # RDD\u0026rsquo;s lineage records Coarse-grained specific data from Transformation operation. So when some partitions of a RDD lost data, it can reload or recompute from Lineage. This Coarse-grained data model limited use cases of Spark, so that Spark cannot use for high performance scenarios. Spark Lineage mechanism is performed through RDD dependencies:\nnarrow dependency: certain data of child RDD can be directly computed from certain data of parent RDD wide dependency: recompute all parents\u0026rsquo; RDD, then after finished and hashing, recomputing children RDD. When we deal with long lineage, we need set appropriate checkpoint. checkpoint mechanism: suit for wide dependency # There are two approaches: LocalRDDCheckpointData: temporarly saved in local disk and memory. It\u0026rsquo;s fast, good for scenarios that lineage info needed to be deleted frequently(e.g. GraphX), can tolerate executor fail. ReliableRDDCheckpointData: saved in reliable outside storage(e.g. HDFS), can tolerate driver fail. It\u0026rsquo;s not fast as local, but it has the highest fault tolerance level. If the code doesn\u0026rsquo;t setup checkpoint, it will use local mode checkpoint. If the path is setup, it will use reliable mode checkpoint.\nRDD\u0026rsquo;s action triggering computation, then executing checkpoint. If task fails, it will load data from checkpoint to compute checkpoint example # from pyspark.sql import SparkSession spark = SparkSession.builder.master(\u0026#34;spark://localhost:7077\u0026#34;).appName(\u0026#34;rdd_demos\u0026#34;).getOrCreate() sc = spark.sparkContext sc.setCheckpointDir(\u0026#39;hdfs://localhost:9000/spark/checkpoint\u0026#39;) # set directory for saving from pyspark import StorageLevel rdd1.sc.textFile(\u0026#39;/xxxx\u0026#39;) rdd1.persist(StorageLevel.MEMORY_AND_DISK) rdd2 = rdd1.flatMap(lambda line : line.split(\u0026#39; \u0026#39;)) rdd3 = rdd2.map(lambda word : (word, 1)) rdd3.checkpoint() # set checkpoint print(rdd3.isCheckpointed()) # check if RDD is set checkpoint print(rdd3.getCheckpointFile()) # get the path of saved checkpoint, rdd4 = rdd3.reduceByKey(lambda a, b : a + b) rdd4.collect() spark.stop() # check hdfs if has rdd checkpoint ./bin/hdfs dfs -cat /spark/checkpoint/a335287f-f7aa-408b-a767-0146faalefff/rdd-2/part-00000 difference between checkpoint and cache # checkpoint will cut off the lineage, and save some data cache just save some data Data Partitions # RDD is very large, it will be cutted into partitions saved in different nodes. This is where RDD come from.\nhow to manually setup partitions:\ncreating RDD: when executing textFile and parallelize methods, manually assign the number of partitions. e.g. sc.textFile(path, partitionNum) get new RDD with transforming operation: directly executing repartition #partitions == #task == #core(thread) of cpu\nthe number of executor = totoal-executor-core/executor-core exmaple # 3 files: 17B, 7B, 165B\ntotalSize = 189B\ndefault the minimum number of partitions in Scala: Math.min(#core, 2) = 2 goalSize = 189 / 2 = ? // the maximum size for each partition 17B, 94.5B, 128M\n// Math.max(minSize, Math.min(goalSize, blockSize)) splitSize = Math.max(17B, Math.min(94.5B, 128M)) = 94.5B 文件大小与分片大小比较： a.txt: 17B \u0026lt; 94.5B 一个分区 b.txt: 7B \u0026lt; 94.5B 一个分区 c.txt: 165B \u0026gt; 94.5B 两个分区 一共四个分区 assign partitions when creating RDD # from pyspark.sql import SparkSession spark = SparkSession.builder.master(\u0026#34;spark://localhost:7077\u0026#34;).appName(\u0026#34;rdd_demos\u0026#34;).config(\u0026#34;spark.default.parallelism\u0026#34;, 1).getOrCreate() # setup default parallelism sc = spark.sparkContext rdd1 = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8], 3) # assign the num of partitions here print(rdd1.collect()) # the default num of partitions is 2 # rule: max(2, num-executor-core(the number of cpu thread(core) within each executor)) print(rdd1.getNumPartitions()) # rdd1的分区数量 # check how it is partitioned rdd1.glom().collect() assign partitions when transforming RDD # repartition and coalesce(numPartitions, isShuffle = True) repartition(10) \u0026lt;==\u0026gt; coalesce(10, True) it\u0026rsquo;s faster without shuffle\nrdd.coalesce(1, False): if isShufle is false, it cannot increase the number of partitions, 只能减少分区数\n# as for new RDD from transforming, directly executing repartition to get new partitions rdd2 = rdd1.map(lambda x : x * x) # get rdd2 with transforming print(rdd2.collect()) rdd3 = rdd2.repartition(3) # repartition, get rdd3 rdd3.getNumPartitions() # the number of partitions of rdd3 rdd3.glom().collect() # check distributions customize partition function # pairs = sc.parallelize([(1, 1), (2, 2), (3, 3)]) print(pairs.getNumPartitions()) repairs = pairs.repartition(4) # 重新分区 print(repairs.getNumPartitions()) # customize partition function partitionedRDD = pairs.partitionBy(2, lambda k: int(k)) # use specific partition function partitionedRDD.persist() # 持久化，以便后续操作重复使用partitioned,避免重复分区 print(partitionedRDD.getNumPartitions()) # for example: write to different files depending on the last digit of key # customized partition function def UsridPartitioner(key): return int(key) % 10 # simulate 5 partitions\u0026#39; data data = sc.parallelize(range(1, 21), 5) # use the last digit of key, changing to 10 partitions, writing to 10 files result = data.map(lambda n: (n, 1))\\ .partitionBy(numPartitions=10, partitionfunc=UsridPartitioner) # we can also use lambda here # result.saveAsTextFile(\u0026#34;/data/spark_demo/rdd/partition-output\u0026#34;) result.collect() Shared Variables # overview # spark has many nodes(machines), and they are independent. spark automatically send referenced variables to each node through network, it\u0026rsquo;s convinent but inefficient. So shared variables are necessary. spark provides two kinds of shared variable with limited type: broadcast and accumulator broadcast # driver define broadcast, and send to each node(or machine) only one time\nintroduce a broadcast, give all nodes a readable value, instead of sending data through network and saving a copy. This improves the efficiency. approach: use broadcast() within sparkContext to create broadcast. use value to get broadcast value use unpersist() to remove the broadcast a broadcast will only send to each node once, it\u0026rsquo;s read-only\nutilize broadcast # broads = sc.broadcast(3) # create broadcast, it can be any type lists = [1, 2, 3, 4, 5] # create a list for testing listRDD = sc.parallelize(lists) # create a RDD results = listRDD.map(lambda x: x * broads.value) print(\u0026#34;result is\u0026#34;, end = \u0026#34;:\u0026#34;) results.collect() # [3, 6, 9, 12, 15] update broadcast # # update broadcast, use unpersist() # broadcast is read-only, we cannot update broadcast, we need to remove the old broadcast, then create a new one # create dict mapper = {\u0026#34;dog\u0026#34;:1, \u0026#34;cat\u0026#34;:2} # create broadcast broadcatVar = sc.broadcast(mapper) # update: 1. broadcast broadcatVar.unpersist() # update: 2. change the dict has to be changed mapper[\u0026#34;pig\u0026#34;] = 1 # update: 3. create broadcast again broadcatVar = sc.broadcast(mapper) # get the value broadcatVar.value release broadcast # # destroy() method is used to release broadcast # we cannot use broadcast again after this method data = [\u0026#39;data\u0026#39;, \u0026#39;cat\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;cat\u0026#39;, \u0026#39;cat\u0026#39;] rdd = sc.parallelize(data) mapper = {\u0026#39;dog\u0026#39;:1, \u0026#39;cat\u0026#39;:2} broadcatVar = sc.broadcast(mapper) print(broadcatVar.value) # destroy broadcast broadcatVar.destroy() # the value after destroying print(broadcatVar.value) # cannot use it with function # rdd.map(lambda t:broadcatVar.value.get(t)).collect() accumulator # it is shared among nodes, and it will collect data from nodes to driver approaches: call accumulator within Driver\u0026rsquo;s SparkContext to create accumulator, and give it a name for checking later in Web UI # sum arrary with accumulator accum = sc.accumulator(0) print(accum.value) # 0 sc.parallelize([1, 2, 3, 4]).foreach(lambda x: accum.add(x)) print(accum.value) # 10 acc = sc.accumulator(0) print(acc.value) # 0 list1 = sc.parallelize(range(1, 1000001)) # run in executors list1.foreach(lambda x: acc.add(1)) # run in driver acc.value # 1000000 Shuffle # ShuffleManager handle all shuffle related execution, calculation and operations Before Spark 1.2, HashShuffleManager is default. It has many temperary disk files, and these disk IO affects performance alot. After Spark 1.2, SortShuffleManager is default. It also has many temperary disk files, but it merges all temp files into one disk file, and thus each Task only has one disk file. At next stage, when shuffle read task use their own data, they just get some data through indexing of disk file. overview # how it work # normal mechanism # bypass mechanism # shuffle map task 数量小于 spark.shuffle.sort.bypassMergeThreshold参数的值不是聚合类的shuffle算子(e.g. reduceByKey)\nReference blog: shuffle map machine: shuffle write data to local disk file\nreduce machine: shuffle read data from disk file of map machine\nNote: red 1, green 2, blue 3 are all represent partitions, the number of partition are three\nSpill includes 输出、排序、溢写、合并 Optimize performance # Reference 1 Reference 2 ","date":"3 December 2023","externalUrl":null,"permalink":"/notes/spark-core/","section":"Notes","summary":"RDD # Resilient distributed dataset Fault tolerance distributed immutable data structure, stored in disk or memory There are multiple partitions(#partitions is the same as #machines) within one RDD DN(Data Node to store data) and Worker(to compute) are all in one machine(best practice) only shuffle need data transfering with network, others are in local machine reduceByKey # hadoop ---\u003e (hadoop, 1) hadoop ---\u003e (hadoop, 1) hadoop ---\u003e (hadoop, 1) hive ---\u003e (hive, 1) hive ---\u003e (hive, 1) hive ---\u003e (hive, 1) reduceByKey \u003chadoop, [1, 1, 1]\u003e \u003chive, [1, 1, 1]\u003e shuffle hash(hadoop) ---\u003e hashcode % 3 = 1(machine 1) hash(hive) ---\u003e hashcode % 3 = 2(machine 2) operations # transformation: RDD–\u003eRDD # Operations Summary map return a new RDD filter return a new RDD; true: keep, false: remove flatMap map + flat(from two dimentional to one dimentional) groupByKey reduceByKey groupByKey(get \u003chadoop, [1, 1, 1]\u003e) + map(get \u003chadoop, 3\u003e) sortByKey not default in spark, but default in hadoop join cogroup + remove nulls: join by key of \u003ckey, value\u003e, all pairs will handle by cutomized function cogroup full join action: RDD–\u003eanything else # Operations Summary reduce operation on all elements within RDD, first merges with second element, and then the result merges with the third element, … collect get all elements within RDD to local client count get the total number of elements within RDD take(n) get first unsorted n elements within RDD, top(n) can have first sorted n elements takeOrdered(n, [ordering]) get first sorted n elements within RDD using natural order or a custom comparator saveAsTextFile save to file, each element with toString method countByKey foreach iterate each element within RDD Depencencies(This topic is Transformation operation related) # ","title":"Spark Core","type":"notes"},{"content":" Path # /opt/module HDFS # bin/hdfs namenode -format sbin/start-all.sh bin/hadoop fs -mkdir /usr bin/hadoop fs -mkdir /usr/yixianwang bin/hadoop fs -put demo.csv /usr/yixianwang sbin/stop-all.sh ","date":"2 December 2023","externalUrl":null,"permalink":"/notes/hadoop/","section":"Notes","summary":"Path # /opt/module HDFS # bin/hdfs namenode -format sbin/start-all.sh bin/hadoop fs -mkdir /usr bin/hadoop fs -mkdir /usr/yixianwang bin/hadoop fs -put demo.csv /usr/yixianwang sbin/stop-all.sh","title":"Hadoop","type":"notes"},{"content":" Basics # application \u0026gt; job \u0026gt; state \u0026gt; task cluser manager: scheduling spark applications. e.g. yarn/mesos masater \u0026amp;\u0026amp; worker master: RM(ResourceManager in YARN) worker: NM(NodeManager in YARN) executor: Container in YARN driver: running in spark, including DAGScheduler \u0026amp;\u0026amp; TaskScheduler combine operations and form DAG(directed acyclic graph) break down job into stages similar to ApplicationMaster(AM) in YARN, responsible for applying resources and scheduling DAGScheduler: split job into stages TaskScheduler: similar to AM, responsible for applying resources and scheduling YARN: ResourceManager(RM), NodeManager(NM), ApplicationMaster(AM), Container YARN: only NM can start AM and Container Standalone: master, worder, driver, executor Standalone: only worker can start driver and executor Operating Mode # local/local-cluster/standalone/yarn/mesos local # driver + executor, running in one process\npyspark use local[*] by default\nlocal: one executor local[K]: K executors, K threads local[*]: the number of cpu executors change mode with pyspark --master\nwith jps to check procsses\nbin/pyspark --master local # or bin/spark-submit examples/src/main/python/pi.py 10 local-cluster # driver + master + worker, running in one process each worker has multiple executors, each executor start one new process pyspark --master local-cluster[x, y, z] x: the number of executors y, z: each executor has y cores(actually threads) and z memory size(MB) bin/pyspark --master \u0026#34;local-cluster[2, 2, 1024]\u0026#34; # commont out pyspark setup in .zshrc # or bin/spark-submit --master \u0026#34;local-cluster[2, 2, 1024]\u0026#34; examples/src/main/python/pi.py 10 standalone # written by spark, similar to RM of YARN driver/master/worker/executor all have their own process Setup: spark-env.sh.template spark-default.conf.template Note: SparkSubmitArguments reads in order: [pyspark-options] or [spark-submit-options], [conf/spark-default.conf], [conf/spark-env.sh] Setup workers: modify conf/slaves file sync the setup of work1 and work2 scp -r /opt/module/spark-3.5.0-bin-hadoop3/ worker1:/opt/module/spark-3.5.0-bin-hadoop3 scp -r /opt/module/spark-3.5.0-bin-hadoop3/ worker2:/opt/module/spark-3.5.0-bin-hadoop3 Start: Start spark sbin/start-all.sh Start process [start-all.sh] -\u0026gt; load [spark-config.sh] -\u0026gt; run [start-master.sh] and [start-slaves.sh] -\u0026gt; load [spark-config.sh] and [spark-env.sh] -\u0026gt; run [spark-daemon.sh] -\u0026gt; run [spark-class.sh] or ([spark-submit.sh] -\u0026gt; [spark-class.sh]) Test: On each machine, use jps to check process of master and slaves Entry master\u0026rsquo;s WEBUI: 8080 run pyspark bin/pyspark --master spark://localhost:7077 run spark-submit must assign --master for standalone, otherwise it will be local\nbin/spark-submit --master spark://localhost:7077 examples/src/main/python/pi.py 10 Kill: jps kill 00000 yarn and mesos(similar to yarn) # start yarn and hdfs in hadoop, then spark-submit with yarn, we don\u0026rsquo;t need to start spark\nyarn-client and yarn-cluster Setup: spark-env.sh.template rename to spark-env.sh HADOOP_CONF_DIR=/...../hadoop-3.3.6/etc/hadoop or YARN_CONF_DIR=/home/yixianwang/hadoop/etc/hadoop Start: in hadoop folder, run the following sbin/hadoop-daemon.sh start namenode sbin/hadoop-daemon.sh start datenode sbin/yarn-daemon.sh start resourcemanager sbin/yarn-daemon.sh start nodemanager # or directly sbin/start-all.sh Test: netstat -an|grep LISTEN\nOn each machine, use jps to check process of RM and NM Entry master\u0026rsquo;s WEBUI: 8088 running: yarn-client bin/spark-submit --master yarn examples/src/main/python/pi.py 10 Note: --deploy-mode default is client, start driver from client, we can browse logs from client\nrunning: yarn-cluster bin/spark-submit --master yarn --deploy-mode cluster examples/src/main/python/pi.py 10 Note: if --deploy-mode is cluster, start driver from master of cluster, we have to use history server to browse logs\nSparkSession \u0026amp;\u0026amp; sparkContext # spark sql, start with SparkSession spark core, start with sparkContext For Test in Local: open Jupyter notbook with pyspark built-in spark and sc # # .zshrc export PYSPARK_DRIVER_PYTHON=jupyter-lab export PYSPARK_DRIVER_PYTHON_OPTS=/opt/module/spark-3.5.0-bin-hadoop3/tutu Examples # # after setting up, pyspark has built-in spark and sc, and can open jupyter-lab # for local test with jupyter pyspark # file and counts are RDDs file = sc.textFile(\u0026#34;/opt/module/spark-3.5.0-bin-hadoop3/data/core/data/wordcount.txt\u0026#34;) counts = file.flatMap(lambda line : line.split(\u0026#39; \u0026#39;))\\ .map(lambda word : (word, 1))\\ .reduceByKey(lambda a, b : a + b) counts.collect() # collect before saving file.collect() file.saveAsTextFile(\u0026#34;/opt/module/spark-3.5.0-bin-hadoop3/data/core/data/result\u0026#34;) read from HDFS # Start HDFS: sbin/hadoop-daemon.sh start namenode sbin/hadoop-daemon.sh start datanode Upload file # create 1 -level folder bin/hdfs dfs -mkdir /spark # create 2 -level folder bin/hdfs dfs -mkdir -p /spark/history bin/hdfs dfs -rm -r /folder_need_to_remove history server setup # Setup\nsetup spark-defaults.conf # spark-defaults.conf spark.eventLog.enabled true spark.eventLog.compress true spark.eventLog.dir hdfs://localhost:9000/spark/history create history foler in hdfs bin/hdfs dfs -mkdir -p /spark/history setup spark-env.sh SPARK_HISTORY_OPTS=\u0026#34;-Dspark.history.ui.port=18080 -Dspark.history.retainedApplications=3 -Dspark.history.fs.logDirectory=hdfs://localhost:9000/spark/history -Dspark.history.fs.cleaner.interval=1d -Dspark.history.fs.cleaner.maxAge=2d\u0026#34; Run\nstart hdfs mkdir hdfs folder start historyserver start-history-server.sh tail -10f filename realtime checking\n","date":"1 December 2023","externalUrl":null,"permalink":"/notes/spark/","section":"Notes","summary":"Basics # application \u003e job \u003e state \u003e task cluser manager: scheduling spark applications. e.g. yarn/mesos masater \u0026\u0026 worker master: RM(ResourceManager in YARN) worker: NM(NodeManager in YARN) executor: Container in YARN driver: running in spark, including DAGScheduler \u0026\u0026 TaskScheduler combine operations and form DAG(directed acyclic graph) break down job into stages similar to ApplicationMaster(AM) in YARN, responsible for applying resources and scheduling DAGScheduler: split job into stages TaskScheduler: similar to AM, responsible for applying resources and scheduling YARN: ResourceManager(RM), NodeManager(NM), ApplicationMaster(AM), Container YARN: only NM can start AM and Container Standalone: master, worder, driver, executor Standalone: only worker can start driver and executor Operating Mode # local/local-cluster/standalone/yarn/mesos local # driver + executor, running in one process\n","title":"Spark","type":"notes"},{"content":" Basics # replication factor (RF) consistency level (CL) = QUORUM (Quorum referring to majority, 2 replicas in this case or RF/2 +1) coordinator SSTable memtable timestamps compaction: take small SSTables and merging them into bigger one Intro # keyspaces: top-level namespace/container similar to a relational database schema CREATE KEYSPACE killrvideo WITH REPLICATION = { \u0026#39;class\u0026#39;: \u0026#39;SimpleStrategy\u0026#39;, \u0026#39;replication_factor\u0026#39;: 1 }; USE switches between keyspaces USE killrvideo; Tables: keyspaces contain tables tables contain data CREATE TABLE table1 ( column1 TEXT, column2 TEXT, column3 INT, PRIMARY KEY (column1) ); CREATE TABLE users ( user_id UUID, first_name TEXT, last_name TEXT, PRIMARY KEY (user_id) ); Basic Data Types: text: UTF8 encoded string, varchar is same as text, unbounded int: Signed, 32 bits timestamp: date and time, 64 bit integer, store number of seconds since Jan 1st 1970 GMT UUID \u0026amp;\u0026amp; TIMEUUID generate global unique id without communication between nodes TIMEUUID embeds a TIMESTAMP value INSERT: INSERT INTO users (user_id, first_name, last_name) VALUES (uuid(), \u0026#39;Joseph\u0026#39;, \u0026#39;Chu\u0026#39;); SELECT: SELECT * FROM users; SELECT first_name, last_name FROM users; SELECT * FROM users WHERE user_id = 4b516b3-ddf0-4c43-bab6-b91d674b64a5; COPY: imports/exports CSV COPY table1 (column1, column2, column3) FROM \u0026#39;table1data.csv\u0026#39;; header parameter skips the first line in the file COPY table1 (column1, column2, column3) FROM \u0026#39;table1data.csv\u0026#39; WITH HEADER=true; get data into Cassandra: COPY Spark Drivers Etc. ","date":"29 November 2023","externalUrl":null,"permalink":"/notes/cassandra/","section":"Notes","summary":"Basics # replication factor (RF) consistency level (CL) = QUORUM (Quorum referring to majority, 2 replicas in this case or RF/2 +1) coordinator SSTable memtable timestamps compaction: take small SSTables and merging them into bigger one Intro # keyspaces: top-level namespace/container similar to a relational database schema CREATE KEYSPACE killrvideo WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor': 1 }; USE switches between keyspaces USE killrvideo; Tables: keyspaces contain tables tables contain data CREATE TABLE table1 ( column1 TEXT, column2 TEXT, column3 INT, PRIMARY KEY (column1) ); CREATE TABLE users ( user_id UUID, first_name TEXT, last_name TEXT, PRIMARY KEY (user_id) ); Basic Data Types: text: UTF8 encoded string, varchar is same as text, unbounded int: Signed, 32 bits timestamp: date and time, 64 bit integer, store number of seconds since Jan 1st 1970 GMT UUID \u0026\u0026 TIMEUUID generate global unique id without communication between nodes TIMEUUID embeds a TIMESTAMP value INSERT: INSERT INTO users (user_id, first_name, last_name) VALUES (uuid(), 'Joseph', 'Chu'); SELECT: SELECT * FROM users; SELECT first_name, last_name FROM users; SELECT * FROM users WHERE user_id = 4b516b3-ddf0-4c43-bab6-b91d674b64a5; COPY: imports/exports CSV COPY table1 (column1, column2, column3) FROM 'table1data.csv'; header parameter skips the first line in the file COPY table1 (column1, column2, column3) FROM 'table1data.csv' WITH HEADER=true; get data into Cassandra: COPY Spark Drivers Etc. ","title":"Cassandra","type":"notes"},{"content":" Chapter 1: Introduction # concurrency processor(including processing unit(or core)) task switching context switch hardware threads 1.1 Concurrency # In computer system, concurrency is about two or more indenpendent activites happening at the same time. Sometimes, people also referring task switching is concurrency, but it\u0026rsquo;s an illusion of concurrency. In a single-core scenario, the operating system do some scheduling to divide the different tasks into chunks, and then the chunks from different tasks are interleaved. Task switching also involves context switching, it just saves and reloads some CPU state and instruction pointers. The number of hardware threads is an important factor. It describes how many independent tasks the hardware can run concurrently. approaches to concurrency # multiple single-threaded processes: pass messages to each other through normal interprocess communication channels (signals, sockets, files, pipes, and so on) OS provides many protections between processes to avoid one process accidentally modifying data belonging to another process. Inherent overhead: it takes some time to start a process. cons: such communication is often complicated to set up or slow, or both pros: easier to write safe concurrent code multithreaded process(favored): Shared memory: all threads in a process share the same address space, and most of the data can be accessed directly from the all threads. (global variables remain global, pointers or references to objects or data can be passed around among threads) cons: flexibility - we must ensure that the view of the data seen by each thread is consistent whenever it\u0026rsquo;s accessed. pros: low overhead concurrency vs. parallelism # separation of concerns and performance ","date":"23 November 2023","externalUrl":null,"permalink":"/notes/ccia_reading_note/","section":"Notes","summary":"Chapter 1: Introduction # concurrency processor(including processing unit(or core)) task switching context switch hardware threads 1.1 Concurrency # In computer system, concurrency is about two or more indenpendent activites happening at the same time. Sometimes, people also referring task switching is concurrency, but it’s an illusion of concurrency. In a single-core scenario, the operating system do some scheduling to divide the different tasks into chunks, and then the chunks from different tasks are interleaved. Task switching also involves context switching, it just saves and reloads some CPU state and instruction pointers. The number of hardware threads is an important factor. It describes how many independent tasks the hardware can run concurrently. approaches to concurrency # multiple single-threaded processes: pass messages to each other through normal interprocess communication channels (signals, sockets, files, pipes, and so on) OS provides many protections between processes to avoid one process accidentally modifying data belonging to another process. Inherent overhead: it takes some time to start a process. cons: such communication is often complicated to set up or slow, or both pros: easier to write safe concurrent code multithreaded process(favored): Shared memory: all threads in a process share the same address space, and most of the data can be accessed directly from the all threads. (global variables remain global, pointers or references to objects or data can be passed around among threads) cons: flexibility - we must ensure that the view of the data seen by each thread is consistent whenever it’s accessed. pros: low overhead concurrency vs. parallelism # separation of concerns and performance ","title":"[C++ Concurrency in Action 2nd Edition by Anthony W.] Reading Note","type":"notes"},{"content":" 使用一根假想的线，在坐标轴上水平或垂直移动 像扫描一样经过数据并处理的算法 Take Aways: 注意点有交集的时候: case 1(merge interval): start = -1, end = 1 (or comp: left[1] \u0026gt; right[1]) case 2(meeting room II, employee free time): start = 1, end = -1 (or comp: left[1] \u0026lt; right[1]) sort all boundary sort first element first, then sort the second element watch on left == right scenarios Prefix Sum Sweep Line Algorithm Greedy - Prev End Greedy - Simulation Leetcode 56. Merge Intervals # Leetcode 56. Merge Intervals Approach 1: Greedy # intervals排序，左边界越小越优先 前一个区间的右边界在后一个区间的左边界之后 == 两区间合并 class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; merge(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals) { // corner case std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; result; if (intervals.size() == 0) return result; // intervals排序，左边界越小越优先 auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[0] \u0026lt; right[0]) { return true; } return false; }; std::sort(intervals.begin(), intervals.end(), comp); // 前一个区间的右边界在后一个区间的左边界之后 == 两区间合并 for (int i = 0; i \u0026lt; intervals.size(); ++i) { int left = intervals[i][0]; int right = intervals[i][1]; if (result.size() == 0 || result[result.size() - 1][1] \u0026lt; left) { result.push_back(intervals[i]); } else { result[result.size() - 1][1] = std::max(result[result.size() - 1][1], right); } } return result; } }; Approach 2: Sweep Line Algorithm # similar to prefix_sum class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; merge(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; result; if (intervals.size() == 0) return result; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; boundaries; for (int i = 0; i \u0026lt; intervals.size(); ++i) { boundaries.push_back({intervals[i][0], -1}); boundaries.push_back({intervals[i][1], 1}); } // !!! sort on first element, then sort on second elemen auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[0] == right[0]) { return left[1] \u0026lt; right[1]; } return left[0] \u0026lt; right[0]; }; std::sort(boundaries.begin(), boundaries.end(), comp); int is_matched = 0; int left = 0, right = 0; for (int i = 0; i \u0026lt; boundaries.size(); ++i) { if (is_matched == 0) { left = boundaries[i][0]; } is_matched += boundaries[i][1]; if (is_matched == 0) { right = boundaries[i][0]; result.push_back({left, right}); } } return result; } }; Leetcode 253. Meeting Rooms II # Leetcode 253. Meeting Rooms II Approach 1: Greedy # class Solution { public: int minMeetingRooms(vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; logs) { std::vector\u0026lt;int\u0026gt; starts(logs.size(), 0); std::vector\u0026lt;int\u0026gt; ends(logs.size(), 0); for (int i = 0; i \u0026lt; logs.size(); ++i) { starts[i] = logs[i][0]; ends[i] = logs[i][1]; } std::sort(starts.begin(), starts.end()); std::sort(ends.begin(), ends.end()); int rooms = 0; int ends_itr = 0; for (int i = 0; i \u0026lt; starts.size(); ++i) { if (starts[i] \u0026lt; ends[ends_itr]) { ++rooms; } else { ++ends_itr; } } return rooms; } }; Approach 2: Prefix Sum # if the absolute value between right and left is large, the performance will be bad. // Approach 1: prefix sum class Solution { public: int minMeetingRooms(vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; logs) { if (logs.size() == 0) return 0; std::vector\u0026lt;int\u0026gt; v(1000001, 0); for (int i = 0; i \u0026lt; logs.size(); ++i) { int left = logs[i][0]; int right = logs[i][1]; ++v[left]; --v[right]; } for (int i = 1; i \u0026lt; v.size(); ++i) { v[i] = v[i - 1] + v[i]; } return *max_element(v.begin(), v.end()); } }; Approach 3: Sweep Line without Heap # class Solution { public: int minMeetingRooms(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; logs) { if (logs.size() == 0) return 0; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; v; for (int i = 0; i \u0026lt; logs.size(); ++i) { v.push_back({logs[i][0], 1}); v.push_back({logs[i][1], -1}); } auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[0] == right[0]) { return left[1] \u0026lt; right[1]; } return left[0] \u0026lt; right[0]; }; std::sort(v.begin(), v.end(), comp); int left, right; int temp_sum = 0; int answer = -1; for (int i = 0; i \u0026lt; v.size(); ++i) { // if (temp_sum == 0) { // left = v[i][0]; // } temp_sum += v[i][1]; answer = std::max(answer, temp_sum); // compete the maximum number of overlap layers // if (temp_sum == 0) { // right = v[i][0]; // } } return answer; } }; Approach 4: Sweep Line with Heap # use multiset to handle same key inserted class Solution { public: int minMeetingRooms(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; logs) { if (logs.size() == 0) return 0; auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[0] == right[0]) return left[1] \u0026lt; right[1]; return left[0] \u0026lt; right[0]; }; std::multiset\u0026lt;std::vector\u0026lt;int\u0026gt;, decltype(comp)\u0026gt; heap(comp); // multiset, to handle same key for (int i = 0; i \u0026lt; logs.size(); ++i) { heap.insert({logs[i][0], 1}); heap.insert({logs[i][1], -1}); } int result = 0; int temp_sum = 0; for (auto it = heap.begin(); it != heap.end(); ++it) { temp_sum += it-\u0026gt;at(1); // it-\u0026gt;at(index) result = std::max(result, temp_sum); } return result; } }; Leetcode 759. Employee Free Time # Leetcode 759. Employee Free Time Approach 1: Sweep Line: without heap # class Solution { public: std::vector\u0026lt;Interval\u0026gt; employeeFreeTime(std::vector\u0026lt;std::vector\u0026lt;Interval\u0026gt;\u0026gt; schedule) { std::vector\u0026lt;Interval\u0026gt; result; if (schedule.size() == 0) return result; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; v; for (int i = 0; i \u0026lt; schedule.size(); ++i) { for (int j = 0; j \u0026lt; schedule[i].size(); ++j) { v.push_back({schedule[i][j].start, 1}); v.push_back({schedule[i][j].end, -1}); } } auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[0] == right[0]) return left[1] \u0026lt; right[1]; return left[0] \u0026lt; right[0]; }; std::sort(v.begin(), v.end(), comp); int temp_sum = 0; int left = INT_MIN, right = INT_MAX; for (int i = 0; i \u0026lt; v.size(); ++i) { if (temp_sum == 0) { left = v[i][0]; if (right != INT_MAX \u0026amp;\u0026amp; left != right) { // left != right is the corner case result.push_back(Interval(right, left)); // right, left } } temp_sum += v[i][1]; if (temp_sum == 0) { right = v[i][0]; } } return result; } }; Approach 2: Sweep Line: with heap # class Solution { public: std::vector\u0026lt;Interval\u0026gt; employeeFreeTime(std::vector\u0026lt;std::vector\u0026lt;Interval\u0026gt;\u0026gt; schedule) { std::vector\u0026lt;Interval\u0026gt; result; if (schedule.size() == 0) return result; auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[0] == right[0]) return left[1] \u0026lt; right[1]; return left[0] \u0026lt; right[0]; }; std::multiset\u0026lt;std::vector\u0026lt;int\u0026gt;, decltype(comp)\u0026gt; heap(comp); for (int i = 0; i \u0026lt; schedule.size(); ++i) { for (int j = 0; j \u0026lt; schedule[i].size(); ++j) { heap.insert({schedule[i][j].start, 1}); heap.insert({schedule[i][j].end, -1}); } } int count = 0; while (heap.size() \u0026gt; 1) { std::vector\u0026lt;int\u0026gt; left = *heap.begin(); heap.erase(heap.begin()); std::vector\u0026lt;int\u0026gt; right = *heap.begin(); count += left[1]; if (left[1] == -1 \u0026amp;\u0026amp; right[1] == 1 \u0026amp;\u0026amp; count == 0 \u0026amp;\u0026amp; left[0] != right[0]) { result.push_back(Interval(left[0], right[0])); } } return result; } }; Leetcode Discuss: Study Guide: # Leetcode discuss/study guide Leetcode 731. My Calendar II # Leetcode 731. My Calendar II Approach: Sweep Line with heap # #define print(x) std::copy(x.begin(), x.end(), std::ostream_iterator\u0026lt;int\u0026gt;(std::cout, \u0026#34; \u0026#34;)); std::cout \u0026lt;\u0026lt; std::endl class MyCalendarTwo { public: MyCalendarTwo() {} bool book(int start, int end) { v.insert({start, 1}); v.insert({end, -1}); // std::cout \u0026lt;\u0026lt; \u0026#34;start: \u0026#34; \u0026lt;\u0026lt; start \u0026lt;\u0026lt; \u0026#34; end: \u0026#34; \u0026lt;\u0026lt; end \u0026lt;\u0026lt; std::endl; // for (auto e : v) { // std::cout \u0026lt;\u0026lt; e[0] \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; e[1] \u0026lt;\u0026lt; std::endl; // } if (IsValid()) { return true; } else { // Approach 1: with find_if // auto index = std::find_if(v.begin(), v.end(), [\u0026amp;start](const auto\u0026amp; first) { // return first[0] == start \u0026amp;\u0026amp; first[1] == 1; // }); // v.erase(index); // index = std::find_if(v.begin(), v.end(), [\u0026amp;end](const auto\u0026amp; first) { // return first[0] == end \u0026amp;\u0026amp; first[1] == -1; // }); // v.erase(index); // Approach 2: with find v.erase(v.find({start, 1})); v.erase(v.find({end, -1})); return false; } } bool IsValid() { // check if there is triple booking int count = 0; for (auto it = v.begin(); it != v.end(); ++it) { count += it-\u0026gt;at(1); if (count \u0026gt;= 3) return false; } return true; } std::multiset\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; v; } Leetcode 2237. Count Positions on Street With Required Brightness # Leetcode 2237. Count Positions on Street With Required Brightness Approach 1(Failed): Sweep Line Algorithm without heap # it\u0026rsquo;s not concise compared to the prefix sum version, not AC class Solution { public: int meetRequirement(int n, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; lights, std::vector\u0026lt;int\u0026gt;\u0026amp; requirement) { // no corner case std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; v; for (int i = 0; i \u0026lt; lights.size(); ++i) { int position = lights[i][0]; int range = lights[i][1]; v.push_back({std::max(0, position - range), 1}); v.push_back({std::min(n - 1, position + range), -1}); } auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[0] == right[0]) return left[1] \u0026gt; right[1]; return left[0] \u0026lt; right[0]; // the intersection point should be inclusive }; std::sort(v.begin(), v.end(), comp); for (int i = 0; i \u0026lt; v.size(); ++i) { std::cout \u0026lt;\u0026lt; \u0026#34;point: \u0026#34; \u0026lt;\u0026lt; v[i][0] \u0026lt;\u0026lt; \u0026#34; type: \u0026#34; \u0026lt;\u0026lt; v[i][1] \u0026lt;\u0026lt; std::endl; } std::cout \u0026lt;\u0026lt; std::endl; int temp_sum = 0; int left, right; std::vector\u0026lt;int\u0026gt; light_sum(requirement.size(), 0); for (int i = 0; i \u0026lt; v.size(); ++i) { if (temp_sum == 0) { left = v[i][0]; } temp_sum += v[i][1]; light_sum[v[i][0]] = std::max(light_sum[v[i][0]], temp_sum); if (temp_sum == 0) { right = v[i][0]; } } for (auto e : light_sum) { std::cout \u0026lt;\u0026lt; e \u0026lt;\u0026lt; \u0026#34; \u0026#34;; } std::cout \u0026lt;\u0026lt; std::endl; int result = 0; for (int i = 0; i \u0026lt; requirement.size(); ++i) { if (light_sum[i] \u0026gt;= requirement[i]) ++result; } return result; } }; Approach 2: Prefix Sum # class Solution { public: int meetRequirement(int n, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; lights, std::vector\u0026lt;int\u0026gt;\u0026amp; requirement) { // no coner case here // prefix sum std::vector\u0026lt;int\u0026gt; nums(n + 1, 0); // size has to be n + 1 for (int i = 0; i \u0026lt; lights.size(); ++i) { int position = lights[i][0]; int range = lights[i][1]; ++nums[std::max(0, position - range)]; --nums[std::min(n - 1, position + range) + 1]; // add 1 for the ending index } for (int i = 1; i \u0026lt; nums.size(); ++i) { nums[i] = nums[i - 1] + nums[i]; // this version of prefix sum doens\u0026#39;t need to have a leading 0 } int result = 0; for (int i = 0; i \u0026lt; requirement.size(); ++i) { if (nums[i] \u0026gt;= requirement[i]) { ++result; } } return result; } }; Leetcode 1893. Check if All the Integers in a Range Are Covered # Leetcode 1893. Check if All the Integers in a Range Are Covered Approach 1: Prefix Sum # class Solution { public: bool isCovered(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; ranges, int left, int right) { // no corner case here // prefix sum std::vector\u0026lt;int\u0026gt; ps(52, 0); // it must be at least 52 here for (int i = 0; i \u0026lt; ranges.size(); ++i) { int start = ranges[i][0]; int end = ranges[i][1]; ++ps[start]; --ps[end + 1]; // because it\u0026#39;s inclusive, we have to include end } for (int i = 1; i \u0026lt; ps.size(); ++i) { ps[i] += ps[i - 1]; } for (int i = left; i \u0026lt;= right; ++i) { if (ps[i] \u0026lt; 1) return false; } return true; } }; Leetcode 370. Range Addition # Leetcode 370. Range Addition Approach 1: Prefix Sum # class Solution { public: std::vector\u0026lt;int\u0026gt; getModifiedArray(int length, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; updates) { std::vector\u0026lt;int\u0026gt; nums(length + 1, 0); for (int i = 0; i \u0026lt; updates.size(); ++i) { int start = updates[i][0]; int end = updates[i][1]; int ins = updates[i][2]; nums[start] += ins; nums[end + 1] -= ins; } for (int i = 1; i \u0026lt; nums.size(); ++i) { nums[i] += nums[i - 1]; } return std::vector\u0026lt;int\u0026gt;(nums.begin(), nums.begin() + length); } }; Leetcode 452. Minimum Number of Arrows to Burst Ballons # Leetcode 452. Minimum Number of Arrows to Burst Ballons Approach 1: Greedy - prev end # sort based on the end use prev_end as a bar iterate class Solution { public: int findMinArrowShots(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; points) { if (points.size() == 0) return 0; auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[1] == right[1]) return left[0] \u0026lt; right[0]; return left[1] \u0026lt; right[1]; }; std::sort(points.begin(), points.end(), comp); int prev_end = points[0][1]; // !!! initialize the prev_end int arrow_count = 1; // initialize the first result // start from the second element for (int i = 1; i \u0026lt; points.size(); ++i) { if (points[i][0] \u0026gt; prev_end) { ++arrow_count; prev_end = points[i][1]; } } return arrow_count; } }; Leetcode 435. Non-overlapping Intervals # Leetcode 435. Non-overlapping Intervals Approach 1: Greedy - prev end - Sort by end # class Solution { public: int eraseOverlapIntervals(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals) { if (intervals.size() == 0) return 0; auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[1] == right[1]) return left[0] \u0026lt; right[0]; return left[1] \u0026lt; right[1]; }; std::sort(intervals.begin(), intervals.end(), comp); int prev_end = intervals[0][1]; // !!! use prev strategy here int count_overlap = 0; // initialize the result for (int i = 1; i \u0026lt; intervals.size(); ++i) { if (intervals[i][0] \u0026lt; prev_end) { ++count_overlap; } else { prev_end = intervals[i][1]; } } return count_overlap; } }; Approach 2: DP - Sort by start # class Solution { public: int eraseOverlapIntervals(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals) { } }; Leetcode 646. Maximum Length of Pair Chain # Leetcode 646. Maximum Length of Pair Chain Approach 1: Greedy - prev end # class Solution { public: int findLongestChain(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; pairs) { if (pairs.size() == 0) return 0; auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[1] == right[1]) return left[0] \u0026lt; right[0]; return left[1] \u0026lt; right[1]; }; std::sort(pairs.begin(), pairs.end(), comp); int prev_end = pairs[0][1]; int count_chain = 1; for (int i = 1; i \u0026lt; pairs.size(); ++i) { if (pairs[i][0] \u0026gt; prev_end) { ++count_chain; prev_end = pairs[i][1]; } } return count_chain; } }; Approach 2: DP - Sort by start # class Solution { public: int findLongestChain(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; pairs) { } }; Leetcode 252. Meeting Rooms # Leetcode 252. Meeting Rooms Approach 1: Greedy - prev end - Sorty by end # class Solution { public: bool canAttendMeetings(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals) { if (intervals.size() == 0) return true; auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[1] == right[1]) return left[0] \u0026lt; right[0]; return left[1] \u0026lt; right[1]; }; std::sort(intervals.begin(), intervals.end(), comp); int prev_end = intervals[0][1]; for (int i = 1; i \u0026lt; intervals.size(); ++i) { if (intervals[i][0] \u0026gt;= prev_end) { prev_end = intervals[i][1]; } else { return false; } } return true; } }; Leetcode 1272. Remove Interval # Leetcode 1272. Remove Interval Approach 1: Simulation - Greedy # class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; removeInterval(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals, std::vector\u0026lt;int\u0026gt;\u0026amp; to_be_removed) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; result; if (intervals.size() == 0) return result; if (to_be_removed.size() == 0) return intervals; for (int i = 0; i \u0026lt; intervals.size(); ++i) { // case 1 // there are no overlaps with to_be_removed if (intervals[i][1] \u0026lt; to_be_removed[0] || intervals[i][0] \u0026gt; to_be_removed[1]) { result.push_back(intervals[i]); } else { // case 2, 3, 4 // there is left overlap if (intervals[i][0] \u0026lt; to_be_removed[0]) { result.push_back({intervals[i][0], to_be_removed[0]}); } // there is right overlap if (intervals[i][1] \u0026gt; to_be_removed[1]) { result.push_back({to_be_removed[1], intervals[i][1]}); } } } return result; } }; Approach 2: Sweep Line Algorithm # start of remove index is -1, end is 1 four senarios: none overlaps left overlap right overlap left and right overlaps think through: decide left and then right to cover all scenarios handle corner case: start of interval == the start of remove. let value != 0\nclass Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; removeInterval(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals, std::vector\u0026lt;int\u0026gt;\u0026amp; to_be_removed) { std::map\u0026lt;int, int\u0026gt; v; for (auto\u0026amp; i : intervals) { ++v[i[0]]; --v[i[1]]; } --v[to_be_removed[0]]; ++v[to_be_removed[1]]; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; result; int temp_sum = 0; int left, right; for (auto\u0026amp; [key, value] : v) { temp_sum += value; if (temp_sum \u0026gt; 0) { left = key; } // handle corner case: start of interval == the start of remove. let value != 0 if (temp_sum == 0 \u0026amp;\u0026amp; value != 1 \u0026amp;\u0026amp; value != 0) { right = key; result.push_back({left, right}); } } return result; } }; Leetcode 57. Insert Interval # Leetcode 57. Insert Interval Approach 1: Sweep Line Algorithm # Similar to Merge Interval Pay attention to the intersect point, sort strategy class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; insert(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals, std::vector\u0026lt;int\u0026gt;\u0026amp; new_interval) { if (new_interval.size() == 0) return intervals; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; v; for (int i = 0; i \u0026lt; intervals.size(); ++i) { int start = intervals[i][0]; int end = intervals[i][1]; v.push_back({start, 1}); v.push_back({end, -1}); } v.push_back({new_interval[0], 1}); v.push_back({new_interval[1], -1}); auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[0] == right[0]) return left[1] \u0026gt; right[1]; // here must be \u0026gt; return left[0] \u0026lt; right[0]; }; std::sort(v.begin(), v.end(), comp); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; result; int temp_sum = 0; int left, right; for (int i = 0; i \u0026lt; v.size(); ++i) { if (temp_sum == 0) { left = v[i][0]; } temp_sum += v[i][1]; if (temp_sum == 0) { right = v[i][0]; result.push_back({left, right}); } } return result; } }; Leetcode 1589. Maximum Sum Obtained of Any Permutation # Approach 1: Prefix Sum # #define print(x) std::copy(x.begin(), x.end(), std::ostream_iterator\u0026lt;int\u0026gt;(std::cout, \u0026#34; \u0026#34;)); std::cout \u0026lt;\u0026lt; std::endl class Solution { public: int maxSumRangeQuery(vector\u0026lt;int\u0026gt;\u0026amp; nums, vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; requests) { if (requests.size() == 0) return 0; std::vector\u0026lt;int\u0026gt; ps(nums.size() + 1, 0); for (int i = 0; i \u0026lt; requests.size(); ++i) { int start = requests[i][0]; int end = requests[i][1]; ++ps[start]; // use accumulation instead of assign to 1 --ps[end + 1]; // same here } // 0, 1, 2, 3, 4, 5 // 1 -1 // 1 -1 // 1 -1 for (int i = 1; i \u0026lt; ps.size(); ++i) { ps[i] += ps[i - 1]; } std::sort(nums.begin(), nums.end(), std::greater\u0026lt;int\u0026gt;()); std::sort(ps.begin(), ps.end() - 1, std::greater\u0026lt;int\u0026gt;()); // except the last element of prefix sum array long long mod = 1000000007; long long result = 0; for (int i = 0; i \u0026lt; nums.size(); ++i) { result += (nums[i] % mod) * (ps[i] % mod); result %= mod; } return result % mod; } }; Leetcode 1943. Describe the Painting # Leetcode 1943. Describe the Painting Approach 1: Prefix Sum: Failed in corner case # Failded Corner Case: Input: segments = [[1,4,5],[1,4,7],[4,7,1],[4,7,11]] Output: [[1,4,12],[4,7,12]] #define print(x) std::copy(x.begin(), x.end(), std::ostream_iterator\u0026lt;int\u0026gt;(std::cout, \u0026#34; \u0026#34;)); std::cout \u0026lt;\u0026lt; std::endl class Solution { public: std::vector\u0026lt;std::vector\u0026lt;long long\u0026gt;\u0026gt; splitPainting(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; segments) { std::vector\u0026lt;std::vector\u0026lt;long long\u0026gt;\u0026gt; result; if (segments.size() == 0) return result; std::vector\u0026lt;int\u0026gt; v(100001, 0); for (auto\u0026amp; s : segments) { int start = s[0]; int end = s[1]; int color = s[2]; v[start] += color; v[end] -= color; } for (int i = 1; i \u0026lt; v.size(); ++i) { v[i] += v[i - 1]; } print(v); // 0, 0, 1, 1, 2, 2, 1, 0, 0 int left, right; int pre_mix = 0; for (int i = 0; i \u0026lt; v.size() - 1; ++i) { if (v[i] == pre_mix) { // leading 0 or unchanged mix color continue; } if (pre_mix == 0) { left = i; } else { result.push_back({left, i, v[i - 1]}); if (v[i] != 0) { left = i; } } pre_mix = v[i]; } return result; } }; !!! Approach 2: Sweep Line: AC # when the prefix sum cannot identify the specific index, we need use sweep line class Solution { public: std::vector\u0026lt;std::vector\u0026lt;long long\u0026gt;\u0026gt; splitPainting(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; A) { std::map\u0026lt;int, long long\u0026gt; mp; for (auto\u0026amp; a : A) { mp[a[0]] += a[2]; mp[a[1]] -= a[2]; } std::vector\u0026lt;std::vector\u0026lt;long long\u0026gt;\u0026gt; result; int prev_key = -1; // None long long color = 0; // temp_sum: color mix accumulation for (auto\u0026amp; [key, _] : mp) { // std::cout \u0026lt;\u0026lt; \u0026#34;key: \u0026#34; \u0026lt;\u0026lt; key \u0026lt;\u0026lt; \u0026#34; value: \u0026#34; \u0026lt;\u0026lt; _ \u0026lt;\u0026lt; \u0026#34; mp[prev_key]: \u0026#34; \u0026lt;\u0026lt; mp[prev_key] \u0026lt;\u0026lt; std::endl; if (color != 0) { // if color == 0, means this part isn\u0026#39;t paint result.push_back({prev_key, key, color}); } color += mp[key]; prev_key = key; } return result; } }; [Skip] Leetcode 1674. Minimum Moves to Make Array Complementary # class Solution { public: int minMoves(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int limit) { } }; Leetcode 2158. Amount of New Area Painted Each Day # Leetcode 2158. Amount of New Area Painted Each Day class Solution { public: std::vector\u0026lt;int\u0026gt; amountPainted(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; paint) { } }; Advanced Approaches # Leetcode 56. Merge Intervals # Leetcode 56. Merge Intervals class Solution0 { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; merge(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; result; if (intervals.size() == 0) return result; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; boundaries; for (int i = 0; i \u0026lt; intervals.size(); ++i) { boundaries.push_back({intervals[i][0], 1}); boundaries.push_back({intervals[i][1], -1}); } // !!! sort on first element, then sort on second elemen auto comp = [](const auto\u0026amp; left, const auto\u0026amp; right) { if (left[0] == right[0]) { return left[1] \u0026gt; right[1]; } return left[0] \u0026lt; right[0]; }; std::sort(boundaries.begin(), boundaries.end(), comp); int is_matched = 0; int left = 0, right = 0; for (int i = 0; i \u0026lt; boundaries.size(); ++i) { if (is_matched == 0) { left = boundaries[i][0]; } is_matched += boundaries[i][1]; if (is_matched == 0) { right = boundaries[i][0]; result.push_back({left, right}); } } return result; } }; class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; merge(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals) { vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; result; if (intervals.size() == 0) return result; sort(intervals.begin(), intervals.end()); vector\u0026lt;int\u0026gt;\u0026amp; curr = intervals[0]; for (int i = 1; i \u0026lt; intervals.size(); ++i) { vector\u0026lt;int\u0026gt;\u0026amp; inter = intervals[i]; if (curr[1] \u0026lt; inter[0]) { result.push_back(curr); curr = inter; } else { curr[1] = max(curr[1], inter[1]); } } result.push_back(curr); return result; } }; Leetcode 57. Insert Interval # Leetcode 57. Insert Intervals class Solution0 { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; insert(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals, std::vector\u0026lt;int\u0026gt;\u0026amp; new_interval) { vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; result; if (intervals.size() == 0) return {new_interval}; vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; boundaries; for (auto\u0026amp; v : intervals) { boundaries.push_back({v[0], 1}); boundaries.push_back({v[1], -1}); } boundaries.push_back({new_interval[0], 1}); boundaries.push_back({new_interval[1], -1}); auto comp = [](auto const\u0026amp; left, auto const\u0026amp; right) { if (left[0] == right[0]) return left[1] \u0026gt; right[1]; return left[0] \u0026lt; right[0]; }; sort(boundaries.begin(), boundaries.end(), comp); int temp_sum = 0; int left; for (int i = 0; i \u0026lt; boundaries.size(); ++i) { if (temp_sum == 0) { left = boundaries[i][0]; } temp_sum += boundaries[i][1]; if (temp_sum == 0) { result.push_back({left, boundaries[i][0]}); } } return result; } }; class Solution1 { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; insert(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals, std::vector\u0026lt;int\u0026gt;\u0026amp; new_interval) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; result; for (auto\u0026amp; i : intervals) { if (i[1] \u0026lt; new_interval[0]) { result.push_back(i); } else if (new_interval[1] \u0026lt; i[0]){ result.push_back(new_interval); new_interval = i; } else if (new_interval[1] \u0026gt;= i[0]) { new_interval[0] = min(new_interval[0], i[0]); new_interval[1] = max(new_interval[1], i[1]); } } result.push_back(new_interval); return result; } }; // review better class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; insert(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals, std::vector\u0026lt;int\u0026gt;\u0026amp; new_interval) { vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; result; // the given intervals is sorted bool added = false; for (int i = 0; i \u0026lt; intervals.size(); ++i) { vector\u0026lt;int\u0026gt;\u0026amp; inter = intervals[i]; // check if exist overlapping int max_start = max(inter[0], new_interval[0]); int min_end = min(inter[1], new_interval[1]); if (max_start \u0026lt;= min_end) { new_interval[0] = min(new_interval[0], inter[0]); new_interval[1] = max(new_interval[1], inter[1]); } else { if (new_interval[1] \u0026lt; inter[0] \u0026amp;\u0026amp; added == false) { result.push_back(new_interval); added = true; } result.push_back(inter); } } if (added == false) { result.push_back(new_interval); } return result; } };","date":"10 November 2023","externalUrl":null,"permalink":"/blog/sweep_line_algorithm/","section":"Blog","summary":"","title":"Sweep Line Algorithm","type":"blog"},{"content":" Install without Homebrew # git clone https://github.com/google/googletest cd googletest mkdir build cd build cmake .. make make install -lgtest -lgtest_main -pthread\nExample # #include \u0026lt;gtest/gtest.h\u0026gt; #include \u0026lt;gmock/gmock.h\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;algorithm\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;tuple\u0026gt; #define print(x) std::ranges::copy(x, std::ostream_iterator\u0026lt;int\u0026gt;(std::cout, \u0026#34; \u0026#34;)); std::cout \u0026lt;\u0026lt; std::endl namespace testing { TEST(Testname, Subtest_1) { int vali = 1; ASSERT_TRUE(vali == 1); std::vector\u0026lt;int\u0026gt; vec{5, 10, 15}; EXPECT_THAT(vec, ElementsAre(5, 10, 15)); std::string vals = \u0026#34;apple\u0026#34;; EXPECT_STREQ(vals.c_str(), \u0026#34;apple\u0026#34;); std::tuple\u0026lt;int, std::string, double, std::vector\u0026lt;int\u0026gt;\u0026gt; my_tuple{7, \u0026#34;hello world\u0026#34;, 1.2, vec}; EXPECT_THAT(my_tuple, FieldsAre(Ge(0), HasSubstr(\u0026#34;hello\u0026#34;), Eq(1.2), ElementsAre(5, 10, 15))); int a; std::string b; std::tie(a, b, std::ignore, std::ignore) = my_tuple; std::cout \u0026lt;\u0026lt; std::get\u0026lt;2\u0026gt;(my_tuple) \u0026lt;\u0026lt; std::endl; } } // namespace testing int main(int argc, char **argv) { testing::InitGoogleTest(\u0026amp;argc, argv); return RUN_ALL_TESTS(); } ","date":"9 November 2023","externalUrl":null,"permalink":"/notes/google_test/","section":"Notes","summary":"Install without Homebrew # git clone https://github.com/google/googletest cd googletest mkdir build cd build cmake .. make make install -lgtest -lgtest_main -pthread\nExample # #include \u003cgtest/gtest.h\u003e #include \u003cgmock/gmock.h\u003e #include \u003ciostream\u003e #include \u003calgorithm\u003e #include \u003cvector\u003e #include \u003ctuple\u003e #define print(x) std::ranges::copy(x, std::ostream_iterator\u003cint\u003e(std::cout, \" \")); std::cout \u003c\u003c std::endl namespace testing { TEST(Testname, Subtest_1) { int vali = 1; ASSERT_TRUE(vali == 1); std::vector\u003cint\u003e vec{5, 10, 15}; EXPECT_THAT(vec, ElementsAre(5, 10, 15)); std::string vals = \"apple\"; EXPECT_STREQ(vals.c_str(), \"apple\"); std::tuple\u003cint, std::string, double, std::vector\u003cint\u003e\u003e my_tuple{7, \"hello world\", 1.2, vec}; EXPECT_THAT(my_tuple, FieldsAre(Ge(0), HasSubstr(\"hello\"), Eq(1.2), ElementsAre(5, 10, 15))); int a; std::string b; std::tie(a, b, std::ignore, std::ignore) = my_tuple; std::cout \u003c\u003c std::get\u003c2\u003e(my_tuple) \u003c\u003c std::endl; } } // namespace testing int main(int argc, char **argv) { testing::InitGoogleTest(\u0026argc, argv); return RUN_ALL_TESTS(); }","title":"Google Test","type":"notes"},{"content":" 1. \u0026ndash;help # docker --help anything 2. install # brew install docker --cask docker run --rm hello-world 3. Create a docker container: long version # 3.1 container # docker container create ### docker container create hello-world:linux # Does not start containers 3.2 list container we have created # docker ps docker ps --all 3.3 start container # docker container start DockerID 3.4 log # 3.4.1 Approach 1 # docker logs *** # with first three characters of DockerID 3.4.2 Approach 2: even if the container already started # docker container start --attach *** 4. Create a docker container: short version # docker run hello-world:linux docker run = docker container create + docker container start + docker container attach\n4.1 use ps to get IDs for containers started with the docker run # docker ps --all 4.2 also can use log # docker logs *** # with first three characters of DockerID 5. Create a Docker Container from Dockerfiles # 5.1 Exercies Files \u0026gt; 03_05 # vim Dockerfile 5.1.1 FROM # Tells Docker which existing Docker image to base your Docker image off. This can be any existing image, either local or from the internet. By default, Docker will try to get this image from Docker Hub if it\u0026rsquo;s not already on your machine.\n5.1.2 LABEL # Some images will contain a label adding additional data like the maintainer of this image.\n5.1.3 USER # Tells Docker which user to use for any Docker file commands underneath it.\nBy default, Docker will use the root user to execute commands. Since most security teams do not like this, the USER keyword is useful in changing a user that your app runs as to one that is less powerful, like \u0026ldquo;nobody\u0026rdquo; for example USER nobody.\n5.1.4 COPY # Copies files from a directory provided to the Docker build command to the container image. The directory provided to Docker build is called context. The context is usually your working directory, but it does not have to be.\n5.1.5 RUN # Run statements are commands that customize our image. This is a great place to install additional software, or configure files needed by your application.\n5.1.6 USER # Uses USER nobody to set the default users for containers created from this image to the powerless nobody user. This ensure that we cannot break out of the container, and potentialy change important files on our host.\n5.1.7 ENTRYPOINT # Tells Docker what command containers created from this image should run. We can also use the CMD command to do this, though there are differences. CMD command can be used as well\n5.2 Exercies Files \u0026gt; 03_05 # 5.2.1 turn Dockerfile into a Docker image, and start our container from it # docker build --help 5.2.1.1 -t, \u0026ndash;tag list # Just like containers, every Docker image has an ID. This option associates a convenient name with that ID. This way, we don\u0026rsquo;t have to remember the image ID whenever we use it.\ndocker build -t our-first-image 5.2.1.2 -f, \u0026ndash;file string # Dockerfile looks for a file called Dockerfile by default. Since this is what our dockerfile is actually called, we don\u0026rsquo;t need to change anything. However, if our dockerfile were called something else, we need to provide -f, --file options as well.\ndocker build -t our-first-image --file app.Dockerfile 5.2.1.3 after providing options, we need to tell docker where its context is # context is simply the folder containing files that docker will include in our image. Since the ENTRYPOINT is in your working directory already, we can simply put a period here.\ndocker build -t our-first-image . If we were located in another folder, like say path/to/app\ndocker build -t our-first-image /path/to/app 5.2.1.4 after image has been sucessfully built and tagged, we are ready to run a container from that image # docker run our-first-image 5.3 Exercies Files \u0026gt; 03_06 # We can also run containers that do not immediately exit after ENTRYPOINT command, like servers for example\n5.3.1 server.Dockerfile # 5.3.2 COPY # Copying a file called server.bash instead of entrypoint.bash\n5.3.3 build and start a container # docker build --file server.Dockerfile --tag our-first-server . 5.3.4 stop the container # docker run our-first-server # not prefered docker ps docker kill **** 5.3.5 create a container from the image # Create and starts the container, but doesn\u0026rsquo;t attach my terminal to it.\ndocker run -d our-server # run in background docker ps # to prove our docker is running 5.3.6 run additional commands # Use docker exec to run additional commands from this container. This can be helpful while troubleshooting problems or testing images created by your application\u0026rsquo;s Dockerfile. e.g. use date command to get the time from this container\ndocker exec *** date 5.3.7 docker terminal # docker exec --interactive --tty *** bash 6. Stop and removing the container # docker stop ID # quit docker stop ID -t 0 # force quit docker rm ID docker ps -aq # only show IDs docker ps -aq | xargs docker rm 7. Remove images # docker images # list all images docker rmi tagname1 tagname2 ... 8. Binding ports to our container # 8.1 Exercise Files \u0026gt; 03_08 # 8.1.1 build image from dockerfile # docker build -t our-web-server -f web-server.Dockerfile . 8.1.2 start a container with docker run and background it with -d # docker run -d our-web-server 8.1.2.1 name container # docker run -d --name our-web-server our-web-server 8.1.3 logs with name of container # docker logs our-web-server it doens\u0026rsquo;t work then we need to stop and remove the container at the same time\n8.1.4 stop and remove container at the same time, with the name of container # docker rm -f our-web-server 8.1.5 map some ports # outside : inside docker run -d --name our-web-server -p 5001:5000 our-web-server 9. Saving data from containers # 9.1 Exercise Files \u0026gt; 03_08 # 9.1.1 trivial example # docker run --rm --entrypoint sh ubuntu -c \u0026#34;echo \u0026#39;Hello there.\u0026#39; \u0026gt; /tmp/file \u0026amp;\u0026amp; cat /tmp/file\u0026#34; 9.1.2 map folder(or map file, !!!file must be exist) with -v, \u0026ndash;volume # docker run --rm --entrypoint sh -v /tmp/container:/tmp ubuntu -c \u0026#34;echo \u0026#39;Hello there.\u0026#39; \u0026gt; /tmp/file \u0026amp;\u0026amp; cat /tmp/file\u0026#34; 10. Docker Hub # 10.1 Exercise Files \u0026gt; 03_08 # 10.2 log in to Docker Hub form Docker CLI # docker login 10.3 pushing our-web-server into Docker Hub # Tell docker that this image is going to be pushed into a registry: We need to rename the image, so that it contains our username. docker tag renames docker images\ndocker tag our-web-server mrtutu/our-web-server:0.0.1 docker push mrtutu/our-web-server:0.0.1 11. Challenge \u0026amp; Solution: NGINX # Exercise Files \u0026gt; 03_14_before\nStart an instance of NGINX in Docker with the included website Name the container \u0026ldquo;website\u0026rdquo; Website should be accessible at http://localhost:8080 Ensure that the container is removed when done Map \u0026ldquo;$PWD/website\u0026rdquo; to \u0026ldquo;/user/share/nginx/html\u0026rdquo; if you volume mount Hve fun! docker run --name website -v \u0026#34;$PWD/website:/usr/share/nginx/html\u0026#34; -p 8080:80 --rm nginx docker ps -a 12. Create more containers # remove images docker rmi tagname1 tagname2 ... remove useless docker system prune 13. Make container faster # docker run --name=alpine --entrypoint=sleep -d alpine infinity # docker stats ID(or name of the container) docker stats alpine # Solve it, alpine here is also the name of the container docker exec -i -t alpine sh 13.1 Docker top # shows what\u0026rsquo;s running inside of the container without having to exec into it docker exec -d alpine sleep infinity docker exec -d alpine sleep infinity docker exec -d alpine sleep infinity 13.2 Docker inspect # show you advanced information about a container that\u0026rsquo;s running in JSON format docker inspect alpine | less 14. Challenge \u0026amp; Solution: Fix broken container # Exercise File \u0026gt; 04_03_before\nFix the dockerfile and script provided\nyou will see the notice of application complete when the container is working properly.\nHint1: use the -it flag when runing our container\nHint2: use docker ps and docker rm in another terminal if ours hangs.\n14.1 Solution: # docker build -t app . Then change xeniall to xenial then it can build docker build -t app . docker run -it --name=app_container app when the container is running we run docker stats app_container # we will see cpu is high docker top app_contianer # we will see there is timeout and yes This means we need to modify the app that\u0026rsquo;s used by this dockerfile and rebuild this image docker build -t app . docker run -it --name=app_container app docker rm app_container docker run -it --name=app_container app 15. Best Practice # Use: verifeid image or image scanner(Clair, Trivy, Dagda) Avoid latest: use v1.0.1 Use non-root users: \u0026ndash;user flag: docker run --rm --it --user somebody-else suspect-image:v1.0.1 16. Docker Compose # Docker Compose makes starting and connecting multiple containers as easy as docker-compose up Docker Compose Doc 17. Kubernetes # It\u0026rsquo;s a popular container orchestrator capable of managing very large numbers of containers.\nKubernetes uses a distributed architecture to run and connect hundreds of thousands of containers with minimal hardware. Kubernetes also makes grouping, scaling, and connecting containers with the outside world really easy. Load balancing and securing container traffic to/from the outside world are much easier with Kubernetes. The Kubernetes ecosystem makes it possible to build your own developer experience. ","date":"9 November 2023","externalUrl":null,"permalink":"/notes/docker/","section":"Notes","summary":"1. –help # docker --help anything 2. install # brew install docker --cask docker run --rm hello-world 3. Create a docker container: long version # 3.1 container # docker container create ### docker container create hello-world:linux # Does not start containers 3.2 list container we have created # docker ps docker ps --all 3.3 start container # docker container start DockerID 3.4 log # 3.4.1 Approach 1 # docker logs *** # with first three characters of DockerID 3.4.2 Approach 2: even if the container already started # docker container start --attach *** 4. Create a docker container: short version # docker run hello-world:linux docker run = docker container create + docker container start + docker container attach\n","title":"Docker","type":"notes"},{"content":" Official Doc Components of Apache Airflow # Web Server Scheduler Metadata Database Executor Commandlines # python3 -m venv env_airflow source ./env_airflow/bin/activate airflow db init cd ~/airflow mkdir dags airflow webserver Create users # airflow users create --role Admin --username username --email email --firstname firstname --lastname lastname --password password Start scheduler # # any folder airflow scheduler ","date":"8 November 2023","externalUrl":null,"permalink":"/notes/airflow/","section":"Notes","summary":" Official Doc Components of Apache Airflow # Web Server Scheduler Metadata Database Executor Commandlines # python3 -m venv env_airflow source ./env_airflow/bin/activate airflow db init cd ~/airflow mkdir dags airflow webserver Create users # airflow users create --role Admin --username username --email email --firstname firstname --lastname lastname --password password Start scheduler # # any folder airflow scheduler","title":"Airflow","type":"notes"},{"content":"// 138 subarray_sum // 1. Official solution: #include \u0026lt;vector\u0026gt; #include \u0026lt;numeric\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;cassert\u0026gt; #include \u0026lt;unordered_map\u0026gt; #define assertm(exp, msg) assert(((void)msg, exp)) #define print(input) for (auto\u0026amp; elem : input) std::cout \u0026lt;\u0026lt; elem \u0026lt;\u0026lt; std::endl class Solution { public: std::vector\u0026lt;int\u0026gt; subarraySum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;int\u0026gt; result; // std::unordered_map\u0026lt;int, int\u0026gt; hash_table = {{0, -1}}; int sum = 0; for (int i = 0; i \u0026lt; nums.size(); ++i) { sum += nums[i]; if (hash_table.find(sum) != hash_table.end()) { result.push_back(hash_table[sum] + 1); result.push_back(i); break; } hash_table[sum] = i; } return result; } }; int main() { Solution solution = Solution(); std::vector\u0026lt;int\u0026gt; input{1, 0, 1}; std::vector\u0026lt;int\u0026gt; expect_output{1}; std::vector\u0026lt;int\u0026gt; output = solution.subarraySum(input); print(output); assertm(output == expect_output, \u0026#34;wrong answer\u0026#34;); } /* // 2. Mine solution: excced time limitation #include \u0026lt;vector\u0026gt; #include \u0026lt;numeric\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;cassert\u0026gt; #define assertm(exp, msg) assert(((void)msg, exp)) class Solution { public: std::vector\u0026lt;int\u0026gt; subarraySum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;int\u0026gt; result; if (nums.size() == 0) { return result; } int size = nums.size(); if (size == 1 \u0026amp;\u0026amp; nums[0] == 0) { return nums; } for (int left = 0; left \u0026lt; size - 1; ++left) { if (nums[left] == 0) { result.push_back(left); result.push_back(left); return result; } for (int right = left + 1; right \u0026lt; size; ++right) { if (accumulate(nums.begin() + left, nums.begin() + right + 1, 0) == 0) { result.push_back(left); result.push_back(right); return result; } } } return result; } }; int main() { Solution solution = Solution(); std::vector\u0026lt;int\u0026gt; input{1, 0, 1}; std::vector\u0026lt;int\u0026gt; expect_output{1}; std::vector\u0026lt;int\u0026gt; output = solution.subarraySum(input); assertm(solution.subarraySum(input) == expect_output, \u0026#34;Hello assert\u0026#34;); } */ ","date":"4 November 2023","externalUrl":null,"permalink":"/notes/template_pi/","section":"Notes","summary":"// 138 subarray_sum // 1. Official solution: #include \u003cvector\u003e #include \u003cnumeric\u003e #include \u003ciostream\u003e #include \u003ccassert\u003e #include \u003cunordered_map\u003e #define assertm(exp, msg) assert(((void)msg, exp)) #define print(input) for (auto\u0026 elem : input) std::cout \u003c\u003c elem \u003c\u003c std::endl class Solution { public: std::vector\u003cint\u003e subarraySum(std::vector\u003cint\u003e\u0026 nums) { std::vector\u003cint\u003e result; // std::unordered_map\u003cint, int\u003e hash_table = {{0, -1}}; int sum = 0; for (int i = 0; i \u003c nums.size(); ++i) { sum += nums[i]; if (hash_table.find(sum) != hash_table.end()) { result.push_back(hash_table[sum] + 1); result.push_back(i); break; } hash_table[sum] = i; } return result; } }; int main() { Solution solution = Solution(); std::vector\u003cint\u003e input{1, 0, 1}; std::vector\u003cint\u003e expect_output{1}; std::vector\u003cint\u003e output = solution.subarraySum(input); print(output); assertm(output == expect_output, \"wrong answer\"); } /* // 2. Mine solution: excced time limitation #include \u003cvector\u003e #include \u003cnumeric\u003e #include \u003ciostream\u003e #include \u003ccassert\u003e #define assertm(exp, msg) assert(((void)msg, exp)) class Solution { public: std::vector\u003cint\u003e subarraySum(std::vector\u003cint\u003e\u0026 nums) { std::vector\u003cint\u003e result; if (nums.size() == 0) { return result; } int size = nums.size(); if (size == 1 \u0026\u0026 nums[0] == 0) { return nums; } for (int left = 0; left \u003c size - 1; ++left) { if (nums[left] == 0) { result.push_back(left); result.push_back(left); return result; } for (int right = left + 1; right \u003c size; ++right) { if (accumulate(nums.begin() + left, nums.begin() + right + 1, 0) == 0) { result.push_back(left); result.push_back(right); return result; } } } return result; } }; int main() { Solution solution = Solution(); std::vector\u003cint\u003e input{1, 0, 1}; std::vector\u003cint\u003e expect_output{1}; std::vector\u003cint\u003e output = solution.subarraySum(input); assertm(solution.subarraySum(input) == expect_output, \"Hello assert\"); } */","title":"Template PI","type":"notes"},{"content":" Common command # mysql -u root -p password:xxxx0000 create database xxxx character set utf8mb4; drop database xxxx; use xxxx; source pathto.sql; Schemas # Fact and dimension tables are organized in particular structures known as schemas.\nStar schema \u0026amp;\u0026amp; Snowflake schema # Star schema and snowflake schema are popular ways of organising this information.\nMysql Syntax # WITH and DATE_SUB # Leetcode 550\nWITH first_logins AS ( # first with table SELECT player_id, MIN(event_date) AS first_login FROM Activity GROUP BY player_id ), consec_logins AS ( # second with table SELECT COUNT(A.player_id) AS num_logins FROM first_logins F INNER JOIN Activity A ON F.player_id = A.player_id AND F.first_login = DATE_SUB(A.event_date, INTERVAL 1 DAY) ) SELECT ROUND( (SELECT num_logins FROM consec_logins) / (SELECT COUNT(player_id) FROM first_logins) , 2) AS fraction; MOD # SELECT (CASE WHEN MOD(id, 2) != 0 AND counts != id THEN id + 1 WHEN MOD(id, 2) != 0 AND counts = id THEN id ELSE id - 1 END) AS id, student FROM seat, (SELECT COUNT(*) AS counts FROM seat) AS seat_counts ORDER BY id ASC; Cross Join vs Outer Join # Cross Join # returning all possible combinations of all rows\nSELECT ... FROM table1, table2 as t2 # must have alias here ; Outer Join # mysql doesn\u0026rsquo;t have outer join, but we can emulate it by union with left and right join\nUNION vs UNION ALL # UNION remove duplicates UNION ALL won\u0026rsquo;t remove duplicates\n# should have () in practice () union () SUM # sum(case when date between \u0026#34;2020-02-01\u0026#34; and \u0026#34;2020-02-28\u0026#34; then 1 else 0 end) over(partition by id) as cnt LIKE # where date like \u0026#34;2020-02-%\u0026#34; WINDOW # range between unbounded preceding and current row create table # CREATE TABLE Persons ( PersonID int, LastName varchar(255), FirstName varchar(255), Address varchar(255), City varchar(255) ); CREATE TABLE new_table_name AS SELECT column1, column2,... FROM existing_table_name WHERE ....; CREATE TABLE IF NOT EXISTS sales( sales_employee VARCHAR(50) NOT NULL, fiscal_year INT NOT NULL, sale DECIMAL(14,2) NOT NULL, PRIMARY KEY(sales_employee,fiscal_year) ); INSERT INTO sales(sales_employee,fiscal_year,sale) VALUES(\u0026#39;Bob\u0026#39;,2016,100), (\u0026#39;Bob\u0026#39;,2017,150), (\u0026#39;Bob\u0026#39;,2018,200), (\u0026#39;Alice\u0026#39;,2016,150), (\u0026#39;Alice\u0026#39;,2017,100), (\u0026#39;Alice\u0026#39;,2018,200), (\u0026#39;John\u0026#39;,2016,200), (\u0026#39;John\u0026#39;,2017,150), (\u0026#39;John\u0026#39;,2018,250); show columns # # version 1 show columns from table_name; # version 2: better desc table_name; insert into table # # version 1: insert into all columns INSERT INTO table_name VALUES (value1, value2, value3, ...); # version 2: insert into specified columns INSERT INTO Customers (CustomerName, City, Country) VALUES (\u0026#39;Cardinal\u0026#39;, \u0026#39;Stavanger\u0026#39;, \u0026#39;Norway\u0026#39;); delete row # # version 1: delete all rows without deleting table delete from table_name; # version 2: delete + WHERE delete from table_name WHERE conditions; limit # # will show the first row limit 0, 1 === limit 1 === limit 1 offset 0 ISNULL \u0026amp; IFNULL # ISNULL(expr) 的用法： 如expr 为null，那么isnull() 的返回值为 1，否则返回值为 0。 mysql\u0026gt; select isnull(1+1); -\u0026gt; 0 mysql\u0026gt; select isnull(1/0); -\u0026gt; 1 使用= 的null 值对比通常是错误的。 isnull() 函数同 is null比较操作符具有一些相同的特性。请参见有关is null 的说明。 IFNULL(expr1,expr2)的用法： 假如expr1 不为 NULL，则 IFNULL() 的返回值为 expr1; 否则其返回值为 expr2。IFNULL()的返回值是数字或是字符串，具体情况取决于其所使用的语境。 IF \u0026amp; datediff \u0026amp; lag # select ... ,if(datediff(suc_time,lag(suc_time,1) over(partition by pin order by suc_time))\u0026lt;=365,1,0) as bank_cr_new_flag ... from ... LAG(\u0026lt;expression\u0026gt;[,offset[, default_value]]) OVER ( PARTITION BY expr,... ORDER BY expr [ASC|DESC],... ) sql declare variable within function # CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT BEGIN # set n := n - 1 declare ll int; set ll := n - 1; RETURN ( # Write your MySQL query statement below. select salary from employee group by salary order by salary desc # limit 1 offset n limit 1 offset ll ); END example # select substr(a.suc_time,1,10) as date ,a.bankcode ,count(distinct a.outbizno) as total_order_cnt ,count(distinct case when b.item_first_cate_name = \u0026#39;食品饮料\u0026#39; then a.outbizno end) as food_order_cnt ,sum(case when b.item_first_cate_name = \u0026#39;食品饮料\u0026#39; then nvl(b.suc_paymoney,0) end) food_amt ,count(distinct case when b.item_first_cate_name = \u0026#39;酒类\u0026#39; then a.outbizno end) as wine_order_cnt ,sum(case when b.item_first_cate_name = \u0026#39;酒类\u0026#39; then nvl(b.suc_paymoney,0) end) wine_amt ,count(distinct case when b.item_first_cate_name = \u0026#39;美妆个护\u0026#39; then a.outbizno end) as care_order_cnt ,sum(case when b.item_first_cate_name = \u0026#39;美妆个护\u0026#39; then nvl(b.suc_paymoney,0) end) care_amt ,count(distinct case when b.item_first_cate_name = \u0026#39;家用电器\u0026#39; then a.outbizno end) as household_order_cnt ,sum(case when b.item_first_cate_name = \u0026#39;家用电器\u0026#39; then nvl(b.suc_paymoney,0) end) household_amt ,count(distinct case when b.item_first_cate_name = \u0026#39;生鲜\u0026#39; then a.outbizno end) as fresh_order_cnt ,sum(case when b.item_first_cate_name = \u0026#39;生鲜\u0026#39; then nvl(b.suc_paymoney,0) end) fresh_amt from dmv.DWB_PAY_SYT_ORDR_DET_I_D a left join (select matchid,first_cate as item_first_cate_name,suc_paymoney from dmv.a_sc_order_union_i_d where dt \u0026gt;=${startdate})as b on a.outbizno=b.matchid where dt\u0026gt;=${startdate} and a.suc_time is not null and a.payenum in(209,210,263,264,131,132,138,139,152,153,205,206,229,643,688,689,471,690,691,670,667,665,569,548,392,241,750,1008,1011,299,496,352,183,546,185,686,701,1015,186,258,147,140,110,553,651,648,663,568,1009,1060,1018,199,200,201,202,404,405,394,395,406,407,616,617,618,637,672,531,628,731,619,642,664,415,668,669,759,532,755,530,192,193,174,1022,563,564,124,195,196,164,166,219,599,191,159,262,127,119,501,122,603,207,711,710,1019,1061,1039) and a.cardtype = 2 -- and a.bankcode in(\u0026#39;CEB\u0026#39;,\u0026#39;CMB\u0026#39;,\u0026#39;CCB\u0026#39;,\u0026#39;CITIC\u0026#39;,\u0026#39;BCOM\u0026#39;,\u0026#39;GDB\u0026#39;,\u0026#39;BOC\u0026#39;,\u0026#39;PAB\u0026#39;,\u0026#39;SPDB\u0026#39;,\u0026#39;ICBC\u0026#39;,\u0026#39;ABC\u0026#39;,\u0026#39;CMBC\u0026#39;,\u0026#39;CIB\u0026#39;) group by substr(a.suc_time,1,10) ,a.bankcode ; case when # select ... ,case when bankcode = \u0026#39;COMM\u0026#39; then \u0026#39;BCOM\u0026#39; else bankcode end as bankcode ,case when cardtype = \u0026#39;DEBIT\u0026#39; then 1 when cardtype = \u0026#39;CREDIT\u0026#39; then 2 else cardtype end as cardtype from dev_tmp.wyx_wx_bank_tmp00 ","date":"3 November 2023","externalUrl":null,"permalink":"/notes/sql/","section":"Notes","summary":"Common command # mysql -u root -p password:xxxx0000 create database xxxx character set utf8mb4; drop database xxxx; use xxxx; source pathto.sql; Schemas # Fact and dimension tables are organized in particular structures known as schemas.\nStar schema \u0026\u0026 Snowflake schema # Star schema and snowflake schema are popular ways of organising this information.\n","title":"MySQL","type":"notes"},{"content":"#! python class Node: def __init__(self, children, is_word): self.children = children self.is_word = is_word class Solution: def __init__(self): self.trie = None def build(self, words): self.trie = Node({}, False) for word in words: current = self.trie for char in word: if not char in current.children: current.children[char] = Node({}, False) current = current.children[char] current.is_word = True def autocomplete(self, prefix): current = self.trie for char in prefix: if not char in current.children: return [] current = current.children[char] return self._dfs_helper(current, prefix) def _dfs_helper(self, node, prefix): result = [] if node.is_word: result += [prefix] for char in node.children: result += self._dfs_helper(node.children[char], prefix + char) return result s = Solution() s.build([\u0026#39;dog\u0026#39;, \u0026#39;dark\u0026#39;, \u0026#39;cat\u0026#39;, \u0026#39;door\u0026#39;, \u0026#39;dodge\u0026#39;, \u0026#39;doddddd\u0026#39;]) print(s.autocomplete(\u0026#39;do\u0026#39;)) # [\u0026#39;dog\u0026#39;, \u0026#39;door\u0026#39;, \u0026#39;\u0026#39;dodge] ","date":"3 November 2023","externalUrl":null,"permalink":"/notes/trie_techlead/","section":"Notes","summary":"#! python class Node: def __init__(self, children, is_word): self.children = children self.is_word = is_word class Solution: def __init__(self): self.trie = None def build(self, words): self.trie = Node({}, False) for word in words: current = self.trie for char in word: if not char in current.children: current.children[char] = Node({}, False) current = current.children[char] current.is_word = True def autocomplete(self, prefix): current = self.trie for char in prefix: if not char in current.children: return [] current = current.children[char] return self._dfs_helper(current, prefix) def _dfs_helper(self, node, prefix): result = [] if node.is_word: result += [prefix] for char in node.children: result += self._dfs_helper(node.children[char], prefix + char) return result s = Solution() s.build(['dog', 'dark', 'cat', 'door', 'dodge', 'doddddd']) print(s.autocomplete('do')) # ['dog', 'door', ''dodge]","title":"Trie Techlead","type":"notes"},{"content":"Pthread for inter-threads communication.\nPthreads There are a few ways to communicate between threads in pthread.\nOne way is to use shared memory. Shared memory is a region of memory that is accessible to all threads in a process. To use shared memory, you first need to allocate a region of memory. You can do this using the malloc() function. Once you have allocated a region of memory, you can then share it with other threads by using the shmget() function.\nAnother way to communicate between threads in pthread is to use semaphores. Semaphores are a type of synchronization primitive that allows you to control access to a shared resource. To use semaphores, you first need to create a semaphore. You can do this using the sem_init() function. Once you have created a semaphore, you can then use it to control access to a shared resource. For example, you can use a semaphore to ensure that only one thread can access a shared variable at a time.\nFinally, you can also use pipes to communicate between threads in pthread. Pipes are a type of inter-process communication (IPC) mechanism that allows you to send data between processes. To use pipes, you first need to create a pipe. You can do this using the pipe() function. Once you have created a pipe, you can then use it to send data between threads. For example, you can use a pipe to send a message from one thread to another.\n","date":"3 November 2023","externalUrl":null,"permalink":"/blog/d_thread_communication/","section":"Blog","summary":"Pthread for inter-threads communication.\n","title":"Draft: Inter-threads Communication","type":"blog"},{"content":"Deadlock defination and how to prevent deadlock.\nSituations of deadlock # In concurrent computing, deadlock is any situation in which no member of some group of entities can proceed because each waits for another member, including itself, to take action, such as sending a message or, more commonly, releasing a lock. Deadlocks are a common problem in multiprocessing systems, parallel computing, and distributed systems, because in these contexts systems often use software or hardware locks to arbitrate shared resources and implement process synchronization.\nIn an operating system, a deadlock occurs when a process or thread enters a waiting state because a requested system resource is held by another waiting process, which in turn is waiting for another resource held by another waiting process. If a process remains indefinitely unable to change its state because resources requested by it are being used by another process that itself is waiting, then the system is said to be in a deadlock.\nIn a communications system, deadlocks occur mainly due to loss or corruption of signals rather than contention for resources.\nAvoid Deadlock in Concurrent Programming # Deadlock is a permanent blocking of a set of threads that are competing for a set of resources. Just because some thread can make progress does not mean that there is not a deadlock somewhere else.\n1. Self deadlock \u0026amp;\u0026amp; Recursive Deadlock # The most common error causing deadlock is\nSelf deadlock: a thread tries to acquire a lock it is already holding. Recursive deadlock: is very easy to program by mistake. For example, if a code monitor has every module function grabbing the mutex lock for the duration of the call, then any call between the functions within the module protected by the mutex lock immediately deadlocks. If a function calls some code outside the module which, through some circuitous path, calls back into any method protected by the same mutex lock, then it will deadlock too.\nSolution # The solution for this kind of deadlock is to avoid calling functions outside the module when you don\u0026rsquo;t know whether they will call back into the module without reestablishing invariants and dropping all module locks before making the call. Of course, after the call completes and the locks are reacquired, the state must be verified to be sure the intended operation is still valid.\nSummary: avoid calling functions outside the module and if called, it has to be verified the intended operation is still valid.\n2. Permanent blocking of threads # An example of another kind of deadlock is when two threads, thread 1 and thread 2, each acquires a mutex lock, A and B, respectively. Suppose that thread 1 tries to acquire mutex lock B and thread 2 tries to acquire mutex lock A. Thread 1 cannot proceed and it is blocked waiting for mutex lock B. Thread 2 cannot proceed and it is blocked waiting for mutex lock A. Nothing can change, so this is a permanent blocking of the threads, and a deadlock.\nSolution1: lock hierarchy # This kind of deadlock is avoided by establishing an order in which locks are acquired (a lock hierarchy). When all threads always acquire locks in the specified order, this deadlock is avoided.\nSummary: not optimal. The discarded lock might have many assumptions and need to reevaluate later.\nSolution2: mutex_trylock() # Adhering to a strict order of lock acquisition is not always optimal. When thread 2 has many assumptions about the state of the module while holding mutex lock B, giving up mutex lock B to acquire mutex lock A and then reacquiring mutex lock B in order would cause it to discard its assumptions and reevaluate the state of the module.\nThe blocking synchronization primitives usually have variants that attempt to get a lock and fail if they cannot, such as mutex_trylock().\nSummary: optimal. This allows threads to violate the lock hierarchy when there is no contention. When there is contention, the held locks must usually be discarded and the locks reacquired in order.\n3. Deadlocks Related to Scheduling # Because there is no guaranteed order in which locks are acquired, a problem in threaded programs is that a particular thread never acquires a lock, even though it seems that it should.\nThis usually happens when the thread that holds the lock releases it, lets a small amount of time pass, and then reacquires it. Because the lock was released, it might seem that the other thread should acquire the lock. But, because nothing blocks the thread holding the lock, it continues to run from the time it releases the lock until it reacquires the lock, and so no other thread is run.\nYou can usually solve this type of problem by calling thr_yield(3T) just before the call to reacquire the lock. This allows other threads to run and to acquire the lock.\nBecause the time-slice requirements of applications are so variable, the threads library does not impose any. Use calls to thr_yield() to make threads share time as you require.\nLocking Guidelines # Here are some simple guidelines for locking.\nTry not to hold locks across long operations like I/O where performance can be adversely affected. Don\u0026rsquo;t hold locks when calling a function that is outside the module and that might reenter the module. In general, start with a coarse-grained approach, identify bottlenecks, and add finer-grained locking where necessary to alleviate the bottlenecks. Most locks are held for short amounts of time and contention is rare, so fix only those locks that have measured contention. When using multiple locks, avoid deadlocks by making sure that all threads acquire the locks in the same order. ","date":"3 November 2023","externalUrl":null,"permalink":"/blog/deadlock/","section":"Blog","summary":"Deadlock defination and how to prevent deadlock.\n","title":"Deadlock","type":"blog"},{"content":"The difference between thread and process.\nA thread is a lightweight process that shares the same address space as other threads in the same process.\nA process is a heavyweight unit of execution that has its own address space, memory, and resources.\nThreads are typically used to improve the performance of an application by allowing multiple tasks to be executed concurrently.\nProcesses are typically used to improve the security of an application by isolating different tasks from each other.\nFeature Thread Process Address Space Shares the same address space as other threads in the same process Has its own address space Memory Shares the same memory as other threads in the same process Has its own memory Resources Shares the same resources as other threads in the same process Has its own resources Performance Typically better than process Typically worse than threads Security Less secure than processes More secure than threads ","date":"3 November 2023","externalUrl":null,"permalink":"/blog/thread_vs_process/","section":"Blog","summary":"The difference between thread and process.\n","title":"Thread vs Process","type":"blog"},{"content":" Designed the app for my parents’ real daily business Features: cloud-hosted database, interactive charts, automatic accountant, and PDF generator Backend jobs: firebase for authentication, firestore for data and SQL query Architecture \u0026amp; Language: MVVM, Kotlin Third-party libraries: MPAndroid, Itextpdf, Android-pdf-viewer Built \u0026amp; Settings # Hardware: Pixel 6 API 32 The login email: fake@example.com The password: 123456 App Description # It is a business management app designed for my parents\u0026rsquo; real daily business of renting houses. It consists of two essential parts, business management and accounting. The inspiration is only from my parent\u0026rsquo;s business which is handled by their handwriting right now. This app will save them much time creating a new contract or appointment. It can also automatically create an accountant summary and some beautiful graphs simultaneously that give my parents an intuitive view of how the business is running. The firebase is heavily used in this project. APIs \u0026amp; Android Features # APIs: Firebase for authentication Firestore for data backend and SQL query MPAndroid for graph PDF Generator PDF Viewer PDF Sharing Android features: RecyclerView and Adapter Fragment Intent Coroutines CardView TableView Action Bar Date Picker Bottom Navigation LiveData Third-party Libraries \u0026amp; Services Description # MPAndroidChart: I use this library to create beautiful interactive pie charts, line charts, and bar charts. The good thing is that I can choose many different charts and customize them. The challenging part is customizing the x-axis because, from its last GitHub update change, the x-axis data only support float data type. That means I always have to use boilerplate code to customize and cast type in the helper function when using the customized x-axis.\nItextpdf: I use this library to customize pdf from the data drawn from firebase. The good thing is that the workflow with it is very straightforward. The challenging part is when I want to add more details to it. The serialized workflow of this library is challenging for adding multiple features or images to the pdf because the previous work can be affected by the latter added features, which might extend pdf page boundaries.\nAndroid-pdf-viewer: I can use this to view pdf smoothly. It provides an efficient pdf viewer service but does not support viewing pdf with other apps.\nFirebase: I use this to utilize firebase to store the data and keep them saved and stored in the cloud.\nUI/UX/Display # For the “Contract” and “Appointment” parts, I use RecyclerView with CardView for each row. There is an insert button on both of them to insert new data into “Contract” or “Appointment.”\nFor the “Home,” there is a manage button on the top left side. We can use it to manage the housing inventory. I also want to mention that only houses in the inventory can be inserted into our “Contract” or “Appointment” list when inserting a new item.\nFor \u0026ldquo;Accountant,\u0026rdquo; this part is automatically updated with the data in firebase. In addition, TableView has a summary of the current month\u0026rsquo;s and last month\u0026rsquo;s income. It is another essential feature to reduce my parents\u0026rsquo; workload when any of the homeowners want to cash out anytime. I also added three pdf-related features here: creating, viewing, and sharing pdf.\nFor “Data,” this part is also automatically updated. Here consists of three valuable graphs to give an intuitive and straightforward overlook of how my parents’ business is running.\nBackend Processing Logic # I have three tables in the firebase database: “allAppointments,” “allContracts,” and “allHouses.”\nThe first table, “allAppointments” stores all appointments, including past and upcoming appointments.\nThe second table, “allContracts” stores all contracts, including old and current underlying contracts.\nThe third table, “allHouses” handles the housing inventory.\nI have described how these three tables work with UI in section 6 of this report.\nFirebase Database Schemas # ","date":"25 October 2023","externalUrl":null,"permalink":"/projects/android_business_management/","section":"Projects","summary":" Designed the app for my parents’ real daily business Features: cloud-hosted database, interactive charts, automatic accountant, and PDF generator Backend jobs: firebase for authentication, firestore for data and SQL query Architecture \u0026 Language: MVVM, Kotlin Third-party libraries: MPAndroid, Itextpdf, Android-pdf-viewer ","title":"Android App: Business Management \u0026 Accountant","type":"projects"},{"content":"","date":"24 October 2023","externalUrl":null,"permalink":"/blog/","section":"Blog","summary":"","title":"Blog","type":"blog"},{"content":"scp -ri /Users/xxxxxxxxxx/.ssh/xxx_xx.pem ./local_foler ec2-user@ec2-22-222-22-222.us-east-2.compute.amazonaws.com:~/\n","date":"24 October 2023","externalUrl":null,"permalink":"/notes/aws_connection/","section":"Notes","summary":"scp -ri /Users/xxxxxxxxxx/.ssh/xxx_xx.pem ./local_foler ec2-user@ec2-22-222-22-222.us-east-2.compute.amazonaws.com:~/\n","title":"AWS","type":"notes"},{"content":"colab tips Seems like colab is popular, so I wanted to share maybe some useful tips that helped me streamline my setup/workflow:\na) If you cloned the notebook and are using gdrive to store the data, you can only mount gdrive using the python package, which requires entering a new oauth code for each session; if you create a new notebook, it actually persists the mount between sessions. You can just copy the contents over - https://datascience.stackexchange.com/questions/64486/how-to-automatically-mount-my-google-drive-to-google-colab\nb) You can run this code in a cell to extend sessions (prevent timeouts); it also doesn\u0026rsquo;t majorly use the CPU unnecessarily. Still, as this SO points out, it\u0026rsquo;s not morally right to hog up a GPU/CPU if you\u0026rsquo;re not using it. I use it when I\u0026rsquo;m actively developing over ssh, but not running anything on the notebook.\nimport time while True: time.sleep(10) c) You can ssh into the machine; someone even made a package for that - https://pypi.org/project/colab-ssh/#description. I usually use ipdb to do REPL-driven development and explore the data, so this is really useful for me since I can\u0026rsquo;t run this workload locally. Head\u0026rsquo;s up - it does take a bit of setup, but if you persist the extra python packages and the authorized_hosts file, you can get it up and running for new sessions quickly.\nimport os import sys # set up persistent pip library path nb_path = \u0026#39;/content/drive/My Drive/Colab Notebooks/pip\u0026#39; !mkdir -p \u0026#39;{nb_path}\u0026#39; sys.path.insert(0, nb_path) !pip install --target=\u0026#39;{nb_path}\u0026#39; ipdb !pip install --target=\u0026#39;{nb_path}\u0026#39; colab_ssh --upgrade !ln -sr /content/drive/MyDrive/nlp-qa-finalproj/.ssh ~/ !cat ~/.ssh/authorized_keys Hope this helps, good luck!\nTemplate # from google.colab import userdata import os os.environ[\u0026#34;GH_USER\u0026#34;] = userdata.get(\u0026#34;git_username\u0026#34;) os.environ[\u0026#34;GH_PAT\u0026#34;] = userdata.get(\u0026#34;git_password\u0026#34;) !git config --global user.name \u0026#34;{userdata.get(\u0026#39;git_username\u0026#39;)}\u0026#34; !git config --global user.email \u0026#34;{userdata.get(\u0026#39;git_email\u0026#39;)}\u0026#34; !git config --global credential.helper \u0026#39;!f() { echo \u0026#34;username=$GH_USER\u0026#34;; echo \u0026#34;password=$GH_PAT\u0026#34;; }; f\u0026#39; !git clone {userdata.get(\u0026#39;git_repo\u0026#39;)} %cd path/to/your/repo ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/colab_tips/","section":"Notes","summary":"colab tips Seems like colab is popular, so I wanted to share maybe some useful tips that helped me streamline my setup/workflow:\na) If you cloned the notebook and are using gdrive to store the data, you can only mount gdrive using the python package, which requires entering a new oauth code for each session; if you create a new notebook, it actually persists the mount between sessions. You can just copy the contents over - https://datascience.stackexchange.com/questions/64486/how-to-automatically-mount-my-google-drive-to-google-colab\n","title":"Colab Tips","type":"notes"},{"content":"import os from getpass import getpass import urllib user = input(\u0026#39;User name: \u0026#39;) password = getpass(\u0026#39;Password: \u0026#39;) password = urllib.parse.quote(password) # your password is converted into url format repo_name = \u0026#34;gregdurrett/nlp-qa-finalproj.git\u0026#34; cmd_string = \u0026#39;git clone https://{0}:{1}@github.com/{2}\u0026#39;.format(user, password, repo_name) !{cmd_string} ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/colab_upload/","section":"Notes","summary":"import os from getpass import getpass import urllib user = input('User name: ') password = getpass('Password: ') password = urllib.parse.quote(password) # your password is converted into url format repo_name = \"gregdurrett/nlp-qa-finalproj.git\" cmd_string = 'git clone https://{0}:{1}@github.com/{2}'.format(user, password, repo_name) !{cmd_string}","title":"Colab Upload","type":"notes"},{"content":" Basics # conda env list conda create -n env_name python=3.11 --no-default-packages conda env remove -n ENV_NAME Export to yml file # # after conda activate env_name conda export env \u0026gt; env_test.yml Create env with yml file # conda env create –f xxx.yml Make a copy of a conda environemnt # conda create --clone env_name -n new_env_name Upgrade package # pip install \u0026lt;package_name\u0026gt; --upgrade Update conda to the current version # conda update conda ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/conda/","section":"Notes","summary":"Basics # conda env list conda create -n env_name python=3.11 --no-default-packages conda env remove -n ENV_NAME Export to yml file # # after conda activate env_name conda export env \u003e env_test.yml Create env with yml file # conda env create –f xxx.yml Make a copy of a conda environemnt # conda create --clone env_name -n new_env_name Upgrade package # pip install \u003cpackage_name\u003e --upgrade Update conda to the current version # conda update conda","title":"Conda","type":"notes"},{"content":" Which machine learning algorithms can be optimized with CUDA? # CUDA (Compute Unified Device Architecture) is a parallel computing platform and application programming interface (API) created by NVIDIA for utilizing their GPUs (Graphics Processing Units) to accelerate various computational tasks, including machine learning. Many machine learning algorithms can be optimized with CUDA to take advantage of GPU parallelism, which can significantly speed up training and inference. Here are some common machine learning algorithms that can benefit from CUDA optimization:\nDeep Learning Algorithms:\nConvolutional Neural Networks (CNNs): Used in image and video analysis, CNNs can be accelerated with CUDA for image recognition, object detection, and more. Recurrent Neural Networks (RNNs): RNNs, especially in natural language processing tasks, can benefit from GPU acceleration. Support Vector Machines (SVM): SVMs are used for classification and regression tasks. Training large SVMs can be time-consuming, and CUDA can speed up the process.\nk-Nearest Neighbors (k-NN): CUDA can accelerate the distance calculations required in k-NN algorithms.\nRandom Forests: Implementations of random forests can be parallelized on GPUs for faster training.\nGradient Boosting Algorithms: Some gradient boosting libraries, like XGBoost and LightGBM, have GPU support to speed up boosting algorithms\u0026rsquo; training.\nMatrix Factorization: Algorithms like Singular Value Decomposition (SVD) and Alternating Least Squares (ALS) used in recommendation systems can benefit from GPU acceleration.\nClustering Algorithms: Algorithms like K-means clustering and DBSCAN can be optimized with CUDA to speed up clustering tasks on large datasets.\nPrincipal Component Analysis (PCA): PCA, a dimensionality reduction technique, can be accelerated with CUDA when working with high-dimensional data.\nNon-negative Matrix Factorization (NMF): NMF is used in various applications like topic modeling and image processing and can be accelerated using CUDA.\nEnsemble Methods: Bagging and boosting techniques that involve multiple base models can be optimized with CUDA.\nAnomaly Detection Algorithms: Algorithms for detecting anomalies in data, such as Isolation Forests, can benefit from GPU acceleration.\nNeural Collaborative Filtering: Used in recommendation systems, this approach can be accelerated with CUDA to improve recommendation speed.\nIt\u0026rsquo;s essential to note that not all machine learning algorithms can be effectively optimized with CUDA. The feasibility of GPU acceleration depends on several factors, including the specific algorithm, the dataset size, and the availability of GPU support in the machine learning libraries or frameworks you are using. Additionally, optimizing machine learning algorithms for CUDA may require expertise in GPU programming and the use of libraries like CUDA, cuDNN, and cuBLAS to take full advantage of the GPU\u0026rsquo;s capabilities.\nCUDA syntax # cuda syntax Source code is in .cu files, which contain mixture of host (CPU) and device (GPU) code. Declaring functions # __global__ declares kernel, which is called on host and executed on device __device__ declares device function, which is called and executed on device __host__ declares host function, which is called and executed on host __noinline__ to avoid inlining __forceinline__\tto force inlining Declaring variables # __device__ declares device variable in global memory, accessible from all threads, with lifetime of application __constant__\tdeclares device variable in constant memory, accessible from all threads, with lifetime of application __shared__ declares device varibale in block\u0026#39;s shared memory, accessible from all threads within a block, with lifetime of block __restrict__\tstandard C definition that pointers are not aliased Types # Most routines return an error code of type cudaError_t.\nVector types # char1, uchar1, short1, ushort1, int1, uint1, long1, ulong1, float1 char2, uchar2, short2, ushort2, int2, uint2, long2, ulong2, float2 char3, uchar3, short3, ushort3, int3, uint3, long3, ulong3, float3 char4, uchar4, short4, ushort4, int4, uint4, long4, ulong4, float4 longlong1, ulonglong1, double1 longlong2, ulonglong2, double2 dim3 Components are accessible as variable.x, variable.y, variable.z, variable.w. Constructor is make_\u0026lt;type\u0026gt;( x, ... ), for example: float2 xx = make_float2( 1., 2. ); dim3 can take 1, 2, or 3 argumetns: dim3 blocks1D( 5 ); dim3 blocks2D( 5, 5 ); dim3 blocks3D( 5, 5, 5 ); Pre-defined variables # dim3 gridDim dimensions of grid dim3 blockDim dimensions of block uint3 blockIdx block index within grid uint3 threadIdx\tthread index within block int warpSize number of threads in warp Kernel invocation # __global__ void kernel( ... ) { ... } dim3 blocks( nx, ny, nz ); // cuda 1.x has 1D and 2D grids, cuda 2.x adds 3D grids dim3 threadsPerBlock( mx, my, mz ); // cuda 1.x has 1D, 2D, and 3D blocks kernel\u0026lt;\u0026lt;\u0026lt; blocks, threadsPerBlock \u0026gt;\u0026gt;\u0026gt;( ... ); Thread management # __threadfence_block(); wait until memory accesses are visible to block __threadfence(); wait until memory accesses are visible to block and device __threadfence_system();\twait until memory accesses are visible to block and device and host (2.x) __syncthreads(); wait until all threads reach sync Memory management # __device__ float* pointer; cudaMalloc( (void**) \u0026amp;pointer, size ); cudaFree( pointer ); __constant__ float dev_data[n]; float host_data[n]; cudaMemcpyToSymbol ( dev_data, host_data, sizeof(host_data) ); // dev_data = host_data cudaMemcpyFromSymbol( host_data, dev_data, sizeof(host_data) ); // host_data = dev_data // direction is one of cudaMemcpyHostToDevice or cudaMemcpyDeviceToHost cudaMemcpy ( dst_pointer, src_pointer, size, direction ); cudaMemcpyAsync( dst_pointer, src_pointer, size, direction, stream ); // using column-wise notation // (the CUDA docs describe it for images; a “row” there equals a matrix column) // _bytes indicates arguments that must be specified in bytes cudaMemcpy2D ( A_dst, lda_bytes, B_src, ldb_bytes, m_bytes, n, direction ); cudaMemcpy2DAsync( A_dst, lda_bytes, B_src, ldb_bytes, m_bytes, n, direction, stream ); // cublas makes copies easier for matrices, e.g., less use of sizeof // copy x =\u0026gt; y cublasSetVector ( n, elemSize, x_src_host, incx, y_dst_dev, incy ); cublasGetVector ( n, elemSize, x_src_dev, incx, y_dst_host, incy ); cublasSetVectorAsync( n, elemSize, x_src_host, incx, y_dst_dev, incy, stream ); cublasGetVectorAsync( n, elemSize, x_src_dev, incx, y_dst_host, incy, stream ); // copy A =\u0026gt; B cublasSetMatrix ( rows, cols, elemSize, A_src_host, lda, B_dst_dev, ldb ); cublasGetMatrix ( rows, cols, elemSize, A_src_dev, lda, B_dst_host, ldb ); cublasSetMatrixAsync( rows, cols, elemSize, A_src_host, lda, B_dst_dev, ldb, stream ); cublasGetMatrixAsync( rows, cols, elemSize, A_src_dev, lda, B_dst_host, ldb, stream ); Also, malloc and free work inside a kernel (2.x), but memory allocated in a kernel must be deallocated in a kernel (not the host). It can be freed in a different kernel, though. Atomic functions # old = atomicAdd ( \u0026amp;addr, value ); // old = *addr; *addr += value old = atomicSub ( \u0026amp;addr, value ); // old = *addr; *addr –= value old = atomicExch( \u0026amp;addr, value ); // old = *addr; *addr = value old = atomicMin ( \u0026amp;addr, value ); // old = *addr; *addr = min( old, value ) old = atomicMax ( \u0026amp;addr, value ); // old = *addr; *addr = max( old, value ) // increment up to value, then reset to 0 // decrement down to 0, then reset to value old = atomicInc ( \u0026amp;addr, value ); // old = *addr; *addr = ((old \u0026gt;= value) ? 0 : old+1 ) old = atomicDec ( \u0026amp;addr, value ); // old = *addr; *addr = ((old == 0) or (old \u0026gt; val) ? val : old–1 ) old = atomicAnd ( \u0026amp;addr, value ); // old = *addr; *addr \u0026amp;= value old = atomicOr ( \u0026amp;addr, value ); // old = *addr; *addr |= value old = atomicXor ( \u0026amp;addr, value ); // old = *addr; *addr ^= value // compare-and-store old = atomicCAS ( \u0026amp;addr, compare, value ); // old = *addr; *addr = ((old == compare) ? value : old) Warp vote # int __all ( predicate ); int __any ( predicate ); int __ballot( predicate ); // nth thread sets nth bit to predicate Timer # wall clock cycle counter\nclock_t clock(); Texture # can also return float2 or float4, depending on texRef.\n// integer index float tex1Dfetch( texRef, ix ); // float index float tex1D( texRef, x ); float tex2D( texRef, x, y ); float tex3D( texRef, x, y, z ); float tex1DLayered( texRef, x ); float tex2DLayered( texRef, x, y ); Low-level Driver API # #include \u0026lt;cuda.h\u0026gt; CUdevice dev; CUdevprop properties; char name[n]; int major, minor; size_t bytes; cuInit( 0 ); // takes flags for future use cuDeviceGetCount ( \u0026amp;cnt ); cuDeviceGet ( \u0026amp;dev, index ); cuDeviceGetName ( name, sizeof(name), dev ); cuDeviceComputeCapability( \u0026amp;major, \u0026amp;minor, dev ); cuDeviceTotalMem ( \u0026amp;bytes, dev ); cuDeviceGetProperties ( \u0026amp;properties, dev ); // max threads, etc. cuBLAS # Matrices are column-major. Indices are 1-based; this affects result of iamax and iamin.\n#include \u0026lt;cublas_v2.h\u0026gt; cublasHandle_t handle; cudaStream_t stream; cublasCreate( \u0026amp;handle ); cublasDestroy( handle ); cublasGetVersion( handle, \u0026amp;version ); cublasSetStream( handle, stream ); cublasGetStream( handle, \u0026amp;stream ); cublasSetPointerMode( handle, mode ); cublasGetPointerMode( handle, \u0026amp;mode ); Constants # argument constants description (Fortran letter) trans CUBLAS_OP_N non-transposed (\u0026lsquo;N\u0026rsquo;) CUBLAS_OP_T transposed (\u0026lsquo;T\u0026rsquo;) CUBLAS_OP_C conjugate transposed (\u0026lsquo;C\u0026rsquo;) uplo CUBLAS_FILL_MODE_LOWER lower part filled (\u0026lsquo;L\u0026rsquo;) CUBLAS_FILL_MODE_UPPER upper part filled (\u0026lsquo;U\u0026rsquo;) side CUBLAS_SIDE_LEFT matrix on left (\u0026lsquo;L\u0026rsquo;) CUBLAS_SIDE_RIGHT matrix on right (\u0026lsquo;R\u0026rsquo;) mode CUBLAS_POINTER_MODE_HOST alpha and beta scalars passed on host CUBLAS_POINTER_MODE_DEVICE alpha and beta scalars passed on device BLAS functions have cublas prefix and first letter of usual BLAS function name is capitalized. Arguments are the same as standard BLAS, with these exceptions:\nAll functions add handle as first argument. All functions return cublasStatus_t error code. Constants alpha and beta are passed by pointer. All other scalars (n, incx, etc.) are bassed by value. Functions that return a value, such as ddot, add result as last argument, and save value to result. Constants are given in table above, instead of using characters. Examples:\ncublasDdot ( handle, n, x, incx, y, incy, \u0026amp;result ); // result = ddot( n, x, incx, y, incy ); cublasDaxpy( handle, n, \u0026amp;alpha, x, incx, y, incy ); // daxpy( n, alpha, x, incx, y, incy ); Compiler # nvcc, often found in /usr/local/cuda/bin\nDefines __CUDACC__ Flags common with cc # Short flag Long flag Output or Description -c \u0026ndash;compile .o object file -E \u0026ndash;preprocess on standard output -M \u0026ndash;generate-dependencies on standard output -o file \u0026ndash;output-file file -I directory \u0026ndash;include-path directory header search path -L directory \u0026ndash;library-path directory library search path -l lib \u0026ndash;library lib link with library -lib generate library -shared generate shared library -pg \u0026ndash;profile for gprof -g level \u0026ndash;debug level -G \u0026ndash;device-debug -O level \u0026ndash;optimize level Undocumented (but in sample makefiles) # -m32 compile 32-bit i386 host CPU code -m64 compile 64-bit x86_64 host CPU code Flags specific to nvcc # -v list compilation commands as they are executed -dryrun list compilation commands, without executing -keep saves intermediate files (e.g., pre-processed) for debugging -clean removes output files (with same exact compiler options) -arch=\u0026lt;compute_xy\u0026gt; generate PTX for capability x.y -code=\u0026lt;sm_xy\u0026gt; generate binary for capability x.y, by default same as -arch -gencode arch=\u0026hellip;,code=\u0026hellip; same as -arch and -code, but may be repeated Argumenents for -arch and -code # It makes most sense (to me) to give -arch a virtual architecture and -code a real architecture, though both flags accept both virtual and real architectures (at times).\nVirtual architecture Real architecture Features Tesla compute_10 sm_10 Basic features compute_11 sm_11 + atomic memory ops on global memory compute_12 sm_12 + atomic memory ops on shared memory + vote instructions compute_13 sm_13 + double precision Fermi compute_20 sm_20 + Fermi Some hardware constraints # 1.x 2.x max x- or y-dimension of block 512 1024 max z-dimension of block 64 64 max threads per block 512 1024 warp size 32 32 max blocks per MP 8 8 max warps per MP 32 48 max threads per MP 1024 1536 max 32-bit registers per MP 16k 32k max shared memory per MP 16 KB 48 KB shared memory banks 16 32 local memory per thread 16 KB 512 KB const memory 64 KB 64 KB const cache 8 KB 8 KB texture cache 8 KB 8 KB ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/cuda/","section":"Notes","summary":"Which machine learning algorithms can be optimized with CUDA? # CUDA (Compute Unified Device Architecture) is a parallel computing platform and application programming interface (API) created by NVIDIA for utilizing their GPUs (Graphics Processing Units) to accelerate various computational tasks, including machine learning. Many machine learning algorithms can be optimized with CUDA to take advantage of GPU parallelism, which can significantly speed up training and inference. Here are some common machine learning algorithms that can benefit from CUDA optimization:\n","title":"CUDA","type":"notes"},{"content":" Bias vs Variance # Low Bias(very sensitive to the training data), (then it performs poorly when we got new data)High Variance \u0026ndash; Overfitting\nHigher Bias(less sensitive to the training data), (then it performs better when we got new data)Low Variance \u0026ndash; Underfitting\nError = bias^2 + variance + inreducible error\nThe best model is where the error is reduced\nCompromise between bias and variance\nSolution: Use Cross Validation\nHandle imbalanced data # collect more data to even the imbalances in the dataset resample the dataset to correct for imbalances try a different algorithm altogether on your dataset precision \u0026amp; recall \u0026amp; F1 # Precision is a good measure to determine, when the costs of False Positive is high. We know that Recall shall be the model metric we use to select our best model when there is a high cost associated with False Negative. F1 Score might be a better measure to use if we need to seek a balance between Precision and Recall AND there is an uneven class distribution (large number of Actual Negatives). F1: weighted average of the precision and recall of a model. 1 is the best, 0 is the worst. You would use it in classification tests where true negatives don\u0026rsquo;t matter much. AUC # 0.5 \u0026lt; ROC \u0026lt; 0.7: Poor discrimination 0.7 ≤ ROC \u0026lt; 0.8: Acceptable discrimination 0.8 ≤ ROC \u0026lt; 0.9: Excellent discrimination ROC ≥ 0.9: Outstanding discrimination Regularization # Regularization is an approach to address over-fitting in ML. Overfitted model fails to generalize estimations on test data When the underlying model to be learned is low bias/high variance, or when we have small amount of data, the estimated model is prone to over-fitting. Types of Regularization # 1. Modify the loss function # L2 Regularization: Prevents the weights from getting too large(defined by L2 norm). Larger the weights, more complex the model is, more chances of overfitting. L1 Regularization: Prevents the weights from getting too large(defined by L1 norm). Larger the weights, more complex the model is, more chances of overfitting. L1 Regularization introduces sparsity in the weights. It forces more weights to be zero, than reducing the average magnitude of all weights. Entropy: Used for the models that output probability. Forces the probability distribution towards uniform distribution. 2. Modify data sampling # Data augmentation: Create more data from available data by randomly cropping, dialting, rotating, adding small amount of noise, etc. K-fold Cross-validation: Divide the data in to k groups. Train on (k - 1) groups and test on 1 group. Try all k possible combinations. 3. Change training approach # Injecting noise: Add random noise to the weights when they are being learned. It pushes the model to be relatively insensitive to small variations in the weights, hence regularization. Dropout: Generally used for neural networks. Connections between consecutive layers are randomly dropped based on a dropout-ratio and the remaining network is trained in the current iteration. In the next iteration, another set of random connections are dropped. L1 vs L2 regularization # L2 regularization tends to spread error among all the terms L1 is more binary/sparse, with many variables either being assigned a 1 or 0 in weighting. L1 corresponds to setting a Laplacean prior on the terms L2 corresponds to a Gaussian prior. Type I vs Type II error # Type I error is a false positive: claiming something has happened when it hasn\u0026rsquo;t e.g. Telling a man he is pregnant. Type II eeror is a false negative: claiming nothing is happening when in fact something is. e.g. Telling a pregnant woman she isn\u0026rsquo;t carrying a baby. Likelihood vs Probability # Linear Regression # Logistic Regression # Decision Trree # SVM # Soft Margin # The name Support Vector Classifier comes from the fact that the observations on the edge and within the Soft Margin are called Support Vectors. Naive Bayes Algorithm # Why \u0026ldquo;Naive\u0026rdquo; # Because it makes an assumption: the conditional probabilities is calculated as the pure product of the individual probabilities of components. This implies the absolute independence of features \u0026ndash; a condition probably never met in real life. KNN # sort the nearest neighbors of the given point by the distances in increasing order K # small K: low bias, high variance, overfitting large K: high bias, low variance, underfitting best K: can be found with cross validation and learning curve Classification \u0026amp; Regression # Classification - vote by top k candidates Regression - average of the k nearest neighbors\u0026rsquo; labels as the prediction K-means # Random forest algorithm # Dimensionality reduction algorithms # PCA # Gradient boosting algorithm and AdaBoosting algorithm # Ensemble Learning # Combined multiple weak models/learners into one predictive model to reduce bias, variance and/or improve accuracy.\nTypes of Ensemble Learning: N number of weak learners # Bagging: Trains N different weak models(usually of same types - homogenous) with N non-overlapping subset of the input dataset in parallel. In the test phase, each model is evaluated. The label with the greatest number of predictions is selected as the prediction. Bagging methods reduces variance of the prediction. Simple voting Boosting: Trains N different weak models(usually of same types - homogenous) with the complete dataset in a sequential order. The datapoints wrongly classified with previous weak model is provided more weights to that they an be classified by the next weak learner properly. In the test phase, each model is evaluated and based on the test error of each weak model, the prediction is weighted for voting. Boosting methods decreases the bias of the prediction. Weighted voting Stacking: Trains N different weak models(usually of different types - heterogenous) with one of the two subsets of the dataset in parallel. Once the weak learners are trained, they are used to trained a meta learner to combine their predictions and carry out final prediction using the other subset. In the test phase, each model predicts its label, these set of labels are fed to the meta learner which generates the final prediction. Focus on improving accuracy. Learned voting(meta-learning) ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/ds/","section":"Notes","summary":"Bias vs Variance # Low Bias(very sensitive to the training data), (then it performs poorly when we got new data)High Variance – Overfitting\nHigher Bias(less sensitive to the training data), (then it performs better when we got new data)Low Variance – Underfitting\nError = bias^2 + variance + inreducible error\nThe best model is where the error is reduced\n","title":"Data Science Knowledge","type":"notes"},{"content":"//0 virtualenv dj\ncd dj\npip install django\ndjango-admin startproject reports_proj\npython manage.py migrate\npython manage.py createsuperuser\npython manage.py startapp sales\npython manage.py startapp reports\npython manage.py startapp profiles\npython manage.py startapp products\npython manage.py startapp customers\npython manage.py runserver http://127.0.0.1:8000/ //10 pip install pillow django-crispy-forms matplotlib seaborn pandas xhtml2pdf pip freeze pip freeze \u0026gt; requirements.txt\n//15\nsettings.py\nApplication definition # INSTALLED_APPS = [ \u0026hellip;\n# our apps 'customers', 'products', 'profiles', 'reports', 'sales', # 3rd party 'crispy_forms' ]\ndefine crispy template # CRISPY_TEMPLATE_PACK = \u0026lsquo;bootstrap4\u0026rsquo;\n\u0026lsquo;DIRS\u0026rsquo;: [BASE_DIR / \u0026rsquo;templates\u0026rsquo;]\nmkdir templates touch base.html touch navbar.html\nSTATICFILES_DIRS = [ BASE_DIR / \u0026lsquo;static\u0026rsquo; ] MEDIA_URL = \u0026lsquo;/media/\u0026rsquo; MEDIA_ROOT = BASE_DIR / \u0026lsquo;media\u0026rsquo;\nmkdir static touch style.css\nmkdir media\nurls.py from django.contrib import admin from django.urls import path, include from django.conf import settings from django.conf.urls.static import static\nurlpatterns = [ path(\u0026lsquo;admin/\u0026rsquo;, admin.site.urls), ]\nurlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)\nbase.html\ncustomers/models.py class Customer(models.models): name = models.CharField(max_length=120) logo = models.ImageField(upload_to=\u0026lsquo;customers\u0026rsquo;, default=\u0026lsquo;no_picture.png\u0026rsquo;)\nmv no_picture.png media\ncustomers/admin.py from .models import Customer\nRegister your models here. # admin.site.register(Customer)\nterminal python manage.py makemigrations python manage.py migrate python manage.py runserver\n","date":"24 October 2023","externalUrl":null,"permalink":"/notes/django/","section":"Notes","summary":"//0 virtualenv dj\ncd dj\npip install django\ndjango-admin startproject reports_proj\npython manage.py migrate\npython manage.py createsuperuser\npython manage.py startapp sales\npython manage.py startapp reports\npython manage.py startapp profiles\npython manage.py startapp products\npython manage.py startapp customers\npython manage.py runserver http://127.0.0.1:8000/ //10 pip install pillow django-crispy-forms matplotlib seaborn pandas xhtml2pdf pip freeze pip freeze \u003e requirements.txt\n//15\nsettings.py\nApplication definition # INSTALLED_APPS = [ …\n","title":"Django","type":"notes"},{"content":" We Want to Keep the Files Locally Modified but Unstaged # If you want to keep local changes without staging them for commits: git update-index --assume-unchanged config1.yml git update-index --assume-unchanged config2.yml git update-index --assume-unchanged config3.yml These files will not appear in git status anymore. They will remain untracked in future commits but still exist locally. To revert this and track them again: git update-index --no-assume-unchanged config1.yml git update-index --no-assume-unchanged config2.yml git update-index --no-assume-unchanged config3.yml list files in \u0026ndash;assume-unchanged git config --global alias.hidden \u0026#39;!git ls-files -v | grep \u0026#34;^[a-z]\u0026#34;\u0026#39; We Never Want to Accidentally Modify These Files # If these files are in the repository but should never be changed on your local machine, use: git update-index --skip-worktree config1.yml git update-index --skip-worktree config2.yml git update-index --skip-worktree config3.yml This tells Git to ignore future changes in these files unless you explicitly change them. To undo this and allow modifications again: git update-index --no-skip-worktree config1.yml git update-index --no-skip-worktree config2.yml git update-index --no-skip-worktree config3.yml git.config # $ cat .gitconfig [alias] lg = log --oneline --graph --decorate --all lgt = log --pretty=format:\u0026#39;%h %an %ar %ad %s\u0026#39; --date=iso --all --graph hidden = !git ls-files -v | grep \\\u0026#34;^[a-z]\\\u0026#34; branch operations # # set the remote branch to track the local branch git push --set-upstream origin branchb # check upstream branch git branch -vv # change upstream branch later git branch -u origin/branch-name # delete a branch git branch -d \u0026lt;branch-name\u0026gt; # create a new branch git checkout -b \u0026lt;branch-name\u0026gt; git switch -c \u0026lt;branch-name\u0026gt; # rename branch git branch -m \u0026lt;new name\u0026gt; tag is a snapshot of all the commits at that time # # create a tag, -a means annotation, git tag -a v1.0.0 -m \u0026#34;version 1.0.0\u0026#34; git tag git push origin v1.0.0 # pull latest code and tag again git pull git tag git tag -a v1.0.1 -m \u0026#34;version 1.0.1\u0026#34; git tag git push origin v1.0.1 merge, on development branch, do operations to merge feature branch into development branch # # merge feature branch into development branch, locally git checkout development git merge feature-branch # merge development branch into feature branch, without tracking development locally git checkout feature-branch git fetch origin git merge origin/development # conflict content will show message like: # CONFLICT (content): Merge conflict in some-file.js # resolve conflict, stage, commit, and then push # git commit -m \u0026#34;Resolved merge conflicts between development and feature-branch\u0026#34; rebase # git checkout feature-branch git pull --rebase origin development # If conflicts occur, resolve them and continue rebasing: git rebase --continue # After rebasing, force push since history is rewritten: git push --force origin feature-branch git diff # git diff remote-origin/branch-name # compare local with remote Semantic Commit Messages # See how a minor change to your commit message style can make you a better programmer.\nFormat: \u0026lt;type\u0026gt;(\u0026lt;scope\u0026gt;): \u0026lt;subject\u0026gt;\n\u0026lt;scope\u0026gt; is optional\nCreate a Revert Commit by Resetting and Commiting # # 1. Start a new branch from latest main git checkout main git pull origin main git checkout -b fix/revert-main-to-\u0026lt;old-commit\u0026gt; # 2. Reset working directory to old commit (but keep as changes to commit) git reset --hard \u0026lt;old-commit\u0026gt; git reset --soft HEAD@{1} # makes the changes from the old commit a staged change # 3. Cmomit the changes as a new revert commit git commit -m \u0026#34;fix: revert main to \u0026lt;old-commit-id\u0026gt;\u0026#34; # 4. push branch git push origin fix/revert-main-to-\u0026lt;old-commit\u0026gt; Revert individual commits # git checkout -b fix/revert-bad-change git revert \u0026lt;bad-commit-id\u0026gt; git push origin fix/revert-bad-change Example # feat: add hat wobble ^--^ ^------------^ | | | +-\u0026gt; Summary in present tense. e.g. add, update, resolve, remove, reformat, optimize, reduce, fix, upgrade, merge, etc. | +-------\u0026gt; Type: chore, docs, feat, fix, refactor, style, or test. Types # chore: (indicates a task that is not user facing or feature-related) update dependencies rename file or folders update configurations update pipelines update docs deprecated: (indicates a feature is deprecated) feat: (new feature for the user, not a new feature for build script) fix: (bug fix for the user, not a fix to a build script) release: (indicates a release) other types # docs: (doc change) style: (code formatting) refactor: (restructure or improve code, no feature change) test: (add or update tests) References:\nhttps://www.conventionalcommits.org/ https://seesparkbox.com/foundry/semantic_commit_messages http://karma-runner.github.io/1.0/dev/git-commit-msg.html .gitattributes # ############################################################################### # Global text handling ############################################################################### # Automatically normalize line endings on commit and checkout * text=auto # Explicitly enforce LF for source code (avoid CRLF issues) *.java text eol=lf *.kt text eol=lf *.xml text eol=lf *.yml text eol=lf *.yaml text eol=lf *.sql text eol=lf *.sh text eol=lf *.ts text eol=lf *.js text eol=lf *.json text eol=lf *.html text eol=lf *.scss text eol=lf *.css text eol=lf *.md text eol=lf # Windows batch scripts should keep CRLF *.bat text eol=crlf *.cmd text eol=crlf ############################################################################### # Binary files ############################################################################### # Mark images, fonts, and archives as binary so Git won’t try to diff or merge them *.png binary *.jpg binary *.jpeg binary *.gif binary *.ico binary *.zip binary *.jar binary *.war binary *.pdf binary *.eot binary *.ttf binary *.woff binary *.woff2 binary ############################################################################### # Merge strategies ############################################################################### # Never try to merge lock or dependency files automatically package-lock.json merge=ours yarn.lock merge=ours pnpm-lock.yaml merge=ours *.iml merge=ours *.class merge=ours # Ignore changes in generated build artifacts /dist/* merge=ours /build/* merge=ours /target/* merge=ours ############################################################################### # Diff settings ############################################################################### # Use syntax-aware diffs for specific file types *.java diff=java *.xml diff=xml *.html diff=html *.ts diff=javascript *.scss diff=css *.css diff=css *.md diff=markdown # Disable diffs for binary files *.png -diff *.jpg -diff *.gif -diff *.jar -diff *.zip -diff ############################################################################### # Git LFS (optional) ############################################################################### # If using Git LFS for large media or artifacts, mark them lockable # *.psd filter=lfs diff=lfs merge=lfs -text lockable # *.mp4 filter=lfs diff=lfs merge=lfs -text lockable ############################################################################### # Export / Archive rules ############################################################################### # Exclude node_modules and build outputs when creating a Git archive node_modules/ export-ignore dist/ export-ignore build/ export-ignore target/ export-ignore .idea/ export-ignore .vscode/ export-ignore ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/git/","section":"Notes","summary":"We Want to Keep the Files Locally Modified but Unstaged # If you want to keep local changes without staging them for commits: git update-index --assume-unchanged config1.yml git update-index --assume-unchanged config2.yml git update-index --assume-unchanged config3.yml These files will not appear in git status anymore. They will remain untracked in future commits but still exist locally. To revert this and track them again: git update-index --no-assume-unchanged config1.yml git update-index --no-assume-unchanged config2.yml git update-index --no-assume-unchanged config3.yml list files in –assume-unchanged git config --global alias.hidden '!git ls-files -v | grep \"^[a-z]\"' We Never Want to Accidentally Modify These Files # If these files are in the repository but should never be changed on your local machine, use: git update-index --skip-worktree config1.yml git update-index --skip-worktree config2.yml git update-index --skip-worktree config3.yml This tells Git to ignore future changes in these files unless you explicitly change them. To undo this and allow modifications again: git update-index --no-skip-worktree config1.yml git update-index --no-skip-worktree config2.yml git update-index --no-skip-worktree config3.yml git.config # $ cat .gitconfig [alias] lg = log --oneline --graph --decorate --all lgt = log --pretty=format:'%h %an %ar %ad %s' --date=iso --all --graph hidden = !git ls-files -v | grep \\\"^[a-z]\\\" branch operations # # set the remote branch to track the local branch git push --set-upstream origin branchb # check upstream branch git branch -vv # change upstream branch later git branch -u origin/branch-name # delete a branch git branch -d \u003cbranch-name\u003e # create a new branch git checkout -b \u003cbranch-name\u003e git switch -c \u003cbranch-name\u003e # rename branch git branch -m \u003cnew name\u003e tag is a snapshot of all the commits at that time # # create a tag, -a means annotation, git tag -a v1.0.0 -m \"version 1.0.0\" git tag git push origin v1.0.0 # pull latest code and tag again git pull git tag git tag -a v1.0.1 -m \"version 1.0.1\" git tag git push origin v1.0.1 merge, on development branch, do operations to merge feature branch into development branch # # merge feature branch into development branch, locally git checkout development git merge feature-branch # merge development branch into feature branch, without tracking development locally git checkout feature-branch git fetch origin git merge origin/development # conflict content will show message like: # CONFLICT (content): Merge conflict in some-file.js # resolve conflict, stage, commit, and then push # git commit -m \"Resolved merge conflicts between development and feature-branch\" rebase # git checkout feature-branch git pull --rebase origin development # If conflicts occur, resolve them and continue rebasing: git rebase --continue # After rebasing, force push since history is rewritten: git push --force origin feature-branch git diff # git diff remote-origin/branch-name # compare local with remote Semantic Commit Messages # See how a minor change to your commit message style can make you a better programmer.\n","title":"Git","type":"notes"},{"content":" Connect to Google cloud # ssh -i ~/.ssh/xxxxxx xxxxxxxxxx@33.333.3.333\nGenerate ssh keys # ssh-keygen -t rsa -f ~/.ssh/xxxxxx -C \u0026ldquo;xxxxxx\u0026rdquo;\ncopy files from local to server # scp -i /.ssh/my-ssh-key LOCAL_FILE_PATH USERNAME@IP_ADDRESS: scp -i /.ssh/my-ssh-key -r LOCAL_FOLDER_PATH USERNAME@IP_ADDRESS:\nOthers # gcloud compute scp /Users/xxxxxxxxxx/Desktop/folders/model.py nlp-cpu:~/ gcloud compute scp \u0026ndash;recurse [INSTANCE_NAME]:[REMOTE_DIR] [LOCAL_DIR]\n","date":"24 October 2023","externalUrl":null,"permalink":"/notes/google_cloud/","section":"Notes","summary":"Connect to Google cloud # ssh -i ~/.ssh/xxxxxx xxxxxxxxxx@33.333.3.333\nGenerate ssh keys # ssh-keygen -t rsa -f ~/.ssh/xxxxxx -C “xxxxxx”\ncopy files from local to server # scp -i /.ssh/my-ssh-key LOCAL_FILE_PATH USERNAME@IP_ADDRESS: scp -i /.ssh/my-ssh-key -r LOCAL_FOLDER_PATH USERNAME@IP_ADDRESS:\n","title":"Google Cloud","type":"notes"},{"content":" Doc # Doc Restart hexo server, each time changed yml file NEW BLOG PROJECT # hexo init blog_project_name POST # creating a post # # under blog folder to create a new post with name a.md hexo new a DRAFT # create a new draft # hexo new draft b test draft # hexo server --draft publish the draft # # move b from _drafts folder to _posts folder hexo publish b PAGE # hexo new page c to access the page c \u0026ldquo;http://localhost:4000/about/\u0026rdquo; SCAFFOLDS # For handling default content\ncreate a new file within scafoolds. e.g. giraffe.md title: {{ title }} // title within curly brace here are just placeholder date: {{ date }} layout: {{ layout }} create new post with template giraffe hexo new giraffe f Tags \u0026amp; Categories # Within _posts folder a.md file\n--- tags: [Tag1, Tag2, Tag3] categories: - [Cat1, Cat1.1] - [Cat2] - [Cat3] --- Tag Plugins # Code Block # {% codeblock lang:c++ %} {% endcodeblock %} Youtube # {% youtube AnyYoutubeID %} Asset folders # use hexo syntax for img: second priority # _config.yml post_asset_folder: true Then next time we create new post with hexo command line, it will also create a asset folder a along with a.md\nNotice: jpg works, png not works # within a.md {% asset_img testdel.jpg Image Title Here %} {% asset_link testdel.jpg %} {% asset_path testdel.jpg %} use markdown syntax for img: first priority # # _config.yml post_asset_folder: true marked: prependRoot: true postAsset: true Notice: create an additional folder for reference and convinence ![images](a/testdel.jpg) Official Theme # Clone github to themes folder\ntheme: change the name here to theme-folder\u0026#39;s name Then restart hexo server\nCreating a theme # file layout.ejs is the overview of the structure partial # partial can make process modular\ncreate partial folder, and a file header.ejs # within layout.ejs # title is the parameter \u0026lt;body\u0026gt; \u0026lt;%- parital(\u0026#39;partial/header\u0026#39;, {title: \u0026#39;red\u0026#39;}) %\u0026gt; \u0026lt;/body\u0026gt; # within partial/header.ejs # to get the parameter \u0026lt;%= title %\u0026gt; Variables # ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/hexo/","section":"Notes","summary":"Doc # Doc Restart hexo server, each time changed yml file NEW BLOG PROJECT # hexo init blog_project_name POST # creating a post # # under blog folder to create a new post with name a.md hexo new a DRAFT # create a new draft # hexo new draft b test draft # hexo server --draft publish the draft # # move b from _drafts folder to _posts folder hexo publish b PAGE # hexo new page c to access the page c “http://localhost:4000/about/” SCAFFOLDS # For handling default content\n","title":"Hexo","type":"notes"},{"content":" Theme Doc - hextra # hextra doc Theme-hextra # hextra # an easy way? hugo serve -D -t theme_name_here # Change directory to the theme folder cd hextra-starter-template # Start the server hugo mod tidy hugo server --logLevel debug --disableFastRender -p 1313 # Update the theme hugo mod get -u hugo mod tidy # start the server for draft hugo server -D Installing on mac # brew install hogo Creating # hugo new site first_site Installing \u0026amp; using themes # # within config.toml theme = \u0026#34;the name of theme downloaded\u0026#34; content # hugo new a.md List pages: if the folder is not the first folder level under content # # it must to be _index.md here hugo new dir1/dir2/_index.md archetypes # find if there exist the folder name within archetypes that correspond with the folder name within content yes: use the specific markdown file; no: use the default.md Shortcodes # # /{/{/\u0026lt; shortcode-name param1 /\u0026gt;/}/} # e.g. # /{/{/\u0026lt; youtube AnyYoutubeID /\u0026gt;/}/} Tags \u0026amp; Categories # tags: [\u0026#34;tag1\u0026#34;, \u0026#34;tag2\u0026#34;, \u0026#34;tag3\u0026#34;] categories: [\u0026#34;cat1\u0026#34;] Creating taxonomy # Notice: modify themes/ga-hugo-theme/layouts/_default/list.html by adding a new line for that name mood mood: [\u0026#34;happy\u0026#34;, \u0026#34;upset\u0026#34;] # hugo.toml # even if tag and category are default, but we have to include them when we creating new taxonomies # after modifyint he toml file, restart the server [taxonomies] tag = \u0026#34;tags\u0026#34; category = \u0026#34;categories\u0026#34; mood = \u0026#34;moods\u0026#34; Setting up Github Page # Cretea production repository # the repository\u0026rsquo;s name has to be username.github.io it at least has one commit git clone git@github.com:yixianwang/yixianwang.github.io.git cd yixianwang.github.io git checkout -b main touch README.md git status git add . git commit -m \u0026#34;adding readme\u0026#34; git push origin main Add submodule # Head to yixian-site folder git submodule add -b main git@github.com:yixianwang/yixianwang.github.io.git public Deploy the site # Generate public folder with static website # hugo # without draft hugo -D # with draft # or # hugo -t theme_name Push # Head to public folder git add . git commit -m \u0026#34;update\u0026#34; git push origin main ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/hugo/","section":"Notes","summary":"Theme Doc - hextra # hextra doc Theme-hextra # hextra # an easy way? hugo serve -D -t theme_name_here # Change directory to the theme folder cd hextra-starter-template # Start the server hugo mod tidy hugo server --logLevel debug --disableFastRender -p 1313 # Update the theme hugo mod get -u hugo mod tidy # start the server for draft hugo server -D Installing on mac # brew install hogo Creating # hugo new site first_site Installing \u0026 using themes # # within config.toml theme = \"the name of theme downloaded\" content # hugo new a.md List pages: if the folder is not the first folder level under content # # it must to be _index.md here hugo new dir1/dir2/_index.md archetypes # find if there exist the folder name within archetypes that correspond with the folder name within content yes: use the specific markdown file; no: use the default.md Shortcodes # # /{/{/\u003c shortcode-name param1 /\u003e/}/} # e.g. # /{/{/\u003c youtube AnyYoutubeID /\u003e/}/} Tags \u0026 Categories # tags: [\"tag1\", \"tag2\", \"tag3\"] categories: [\"cat1\"] Creating taxonomy # Notice: modify themes/ga-hugo-theme/layouts/_default/list.html by adding a new line for that name mood mood: [\"happy\", \"upset\"] # hugo.toml # even if tag and category are default, but we have to include them when we creating new taxonomies # after modifyint he toml file, restart the server [taxonomies] tag = \"tags\" category = \"categories\" mood = \"moods\" Setting up Github Page # Cretea production repository # the repository’s name has to be username.github.io it at least has one commit git clone git@github.com:yixianwang/yixianwang.github.io.git cd yixianwang.github.io git checkout -b main touch README.md git status git add . git commit -m \"adding readme\" git push origin main Add submodule # Head to yixian-site folder git submodule add -b main git@github.com:yixianwang/yixianwang.github.io.git public Deploy the site # Generate public folder with static website # hugo # without draft hugo -D # with draft # or # hugo -t theme_name Push # Head to public folder git add . git commit -m \"update\" git push origin main","title":"Hugo","type":"notes"},{"content":" Java Version # To check all java version: /usr/libexec/java_home -V\nand then select specific version of java within .zshrc\nfor spark and hadoop: 1.8.0_391 export JAVA_HOME=$(/usr/libexec/java_home -v 1.8.0_391)\nfor sprint boot: 21.0.1 export JAVA_HOME=$(/usr/libexec/java_home -v 21.0.1) export PATH=$PATH:$JAVA_HOME\nInterface # Java接口(Interface)是一系列方法的声明，是一些方法特征的集合，一个接口只有方法的特征没有方法的实现，因此这些方法可以在不同的地方被不同的类实现，而这些实现可以具有不同的行为。打一个比方，接口好比一个戏中的角色，这个角色有一些特定的属性和操作，然后实现接口的类就好比扮演这个角色的人，一个角色可以由不同的人来扮演，而不同的演员之间除了扮演一个共同的角色之外，并不要求其它的共同之处。\n常用的Interface: # Set # Set注重独一无二,该体系集合可以知道某物是否已经存在于集合中,不会存储重复的元素。Set的实现类在面试中常用的是：HashSet 与 TreeSet\nHashSet # 无重复数据 可以有空数据 数据无序 Set\u0026lt;String\u0026gt; set = new HashSet\u0026lt;\u0026gt;(); for (int i = 1; i \u0026lt; 6; i ++) { set.add(i + \u0026#34;\u0026#34;); } set.add(\u0026#34;1\u0026#34;); //不会重复写入数据 set.add(null);//可以写入空数据 Iterator\u0026lt;String\u0026gt; iter = set.iterator(); while (iter.hasNext()) { system.out.print(iter.next() + \u0026#34; \u0026#34;);//数据无序 }// 输出(无序)为 3 4 1 5 null 2 TreeSet: (key balanced binary search tree) # 无重复数据 不能有空数据 数据有序 Set\u0026lt;String\u0026gt; set = new TreeSet\u0026lt;\u0026gt;(); for (int i = 1; i \u0026lt; 6; i ++) { set.add(i + \u0026#34;\u0026#34;); } set.add(\u0026#34;1\u0026#34;); //不会重复写入数据 //set.add(null);//不可以写入空数据 Iterator\u0026lt;String\u0026gt; iter = set.iterator(); while (iter.hasNext()) { system.out.print(iter.next() + \u0026#34; \u0026#34;);//数据有序 }// 输出(有序)为 1 2 3 4 5 Map # Map用于存储具有映射关系的数据。Map中存了两组数据(key与value),它们都可以是任何引用类型的数据，key不能重复，我们可以通过key取到对应的value。Map的实现类在面试中常用是：HashMap 和 TreeMap.\nHashMap # key 无重复，value 允许重复 允许 key 和 value 为空 数据无序 public class Solution { public static void main(String[] args){ Map\u0026lt;String, String\u0026gt; map = new HashMap\u0026lt;\u0026gt;(); for (int i = 5; i \u0026gt; 0; i --) { map.put(i + \u0026#34;\u0026#34;, i + \u0026#34;\u0026#34;); } map.put(\u0026#34;1\u0026#34;,\u0026#34;1\u0026#34;);//key无重复 map.put(\u0026#34;11\u0026#34;,\u0026#34;1\u0026#34;);//value可以重复 map.put(null, null);//可以为空 for (Iterator i = map.keySet().iterator(); i.hasNext(); ) { String key = (String)i.next(); String value = map.get(key); System.out.println(\u0026#34;key = \u0026#34; + key + \u0026#34;, value = \u0026#34; + value); } } } //输出 /* key = 11, value = 1 key = null, value = null key = 1, value = 1 key = 2, value = 2 key = 3, value = 3 key = 4, value = 4 key = 5, value = 5 */ //输出顺序与输入顺序无关 TreeMap: (key balanced binary search tree) # key 无重复，value 允许重复 不允许有null 有序(存入元素的时候对元素进行自动排序，迭代输出的时候就按排序顺序输出) public class Solution { public static void main(String[] args){ Map\u0026lt;String, String\u0026gt; map = new TreeMap\u0026lt;\u0026gt;(); for (int i = 5; i \u0026gt; 0; i --) { map.put(i + \u0026#34;\u0026#34;, i + \u0026#34;\u0026#34;); } map.put(\u0026#34;1\u0026#34;,\u0026#34;1\u0026#34;);//key无重复 map.put(\u0026#34;11\u0026#34;,\u0026#34;1\u0026#34;);//value可以重复 //map.put(null, null);//不可以为空 for (Iterator i = map.keySet().iterator(); i.hasNext(); ) { String key = (String)i.next(); String value = map.get(key); System.out.println(\u0026#34;key = \u0026#34; + key + \u0026#34;, value = \u0026#34; + value); } } } //输出 /* key = 1, value = 1 key = 11, value = 1 key = 2, value = 2 key = 3, value = 3 key = 4, value = 4 key = 5, value = 5 */ //输出顺序位String排序后的顺序 List # 一个 List 是一个元素有序的、可以重复(这一点与Set和Map不同)、可以为 null 的集合，List的实现类在面试中常用是：LinkedList 和 ArrayList\nLinkedList 基于链表实现 ArrayList 基于动态数组实现 LinkedList 与 ArrayList 对比： 对于随机访问get和set，ArrayList绝对优于LinkedList，因为LinkedList要移动指针 对于新增和删除操作add和remove，在已经得到了需要新增和删除的元素位置的前提下，LinkedList可以在O(1)的时间内删除和增加元素，而ArrayList需要移动增加或删除元素之后的所有元素的位置，时间复杂度是O(n)的，因此LinkedList优势较大 Queue # 队列是一种比较重要的数据结构，它支持FIFO(First in First out)，即尾部添加、头部删除（先进队列的元素先出队列），跟我们生活中的排队类似。\nPriorityQueue 基于堆(heap)实现 非FIFO(最先出队列的是优先级最高的元素) 普通 Queue 基于链表实现 FIFO Queue\u0026lt;Integer\u0026gt; queue = new LinkedList\u0026lt;\u0026gt;(); Stack # 官方建议如果有使用栈的需求的时候，可以用Deque接口下的ArrayDeque作替代，因为ArrayDeque能提供更多的功能。\nLast Minute Java Syntax # References Instantiate # List \u0026lt;T\u0026gt; al = new ArrayList\u0026lt;\u0026gt; (); List \u0026lt;T\u0026gt; ll = new LinkedList\u0026lt;\u0026gt; (); List \u0026lt;T\u0026gt; v = new Vector\u0026lt;\u0026gt; (); Set\u0026lt;T\u0026gt; hs = new HashSet\u0026lt;\u0026gt; (); Set\u0026lt;T\u0026gt; lhs = new LinkedHashSet\u0026lt;\u0026gt; (); Set\u0026lt;T\u0026gt; ts = new TreeSet\u0026lt;\u0026gt; (); SortedSet\u0026lt;T\u0026gt; ts = new TreeSet\u0026lt;\u0026gt; (); Queue \u0026lt;T\u0026gt; pq = new PriorityQueue\u0026lt;\u0026gt; (); Queue \u0026lt;T\u0026gt; ad = new ArrayDeque\u0026lt;\u0026gt; (); Deque\u0026lt;T\u0026gt; ad = new ArrayDeque\u0026lt;\u0026gt; (); Deque Methods # public interface Deque\u0026lt;E\u0026gt; extends Queue\u0026lt;E\u0026gt; { void addFirst(E e); void addLast(E e); boolean offerFirst(E e); boolean offerLast(E e); E removeFirst(); E removeLast(); E pollFirst(); E pollLast(); E getFirst(); E getLast(); E peekFirst(); E peekLast(); } // ArrayDeque doesn\u0026#39;t support null element ArrayDeque\u0026lt;T\u0026gt; queue = new ArrayDeque() {{ offer(root); }}; ArrayDeque\u0026lt;T\u0026gt; queue = new ArrayDeque(); Queue\u0026lt;T\u0026gt; queue = new ArrayDeque(); while (!queue.isEmpty()) { curr = queue.poll(); queue.offer(curr.left); } // LinkedList supports null element Queue\u0026lt;T\u0026gt; queue = new LinkedList(){{ offer(root); offer(null); }}; while (!queue.isEmpty()) { curr = queue.poll(); queue.offer(curr.left); queue.offer(null); } ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/java/","section":"Notes","summary":"Java Version # To check all java version: /usr/libexec/java_home -V\nand then select specific version of java within .zshrc\nfor spark and hadoop: 1.8.0_391 export JAVA_HOME=$(/usr/libexec/java_home -v 1.8.0_391)\nfor sprint boot: 21.0.1 export JAVA_HOME=$(/usr/libexec/java_home -v 21.0.1) export PATH=$PATH:$JAVA_HOME\nInterface # Java接口(Interface)是一系列方法的声明，是一些方法特征的集合，一个接口只有方法的特征没有方法的实现，因此这些方法可以在不同的地方被不同的类实现，而这些实现可以具有不同的行为。打一个比方，接口好比一个戏中的角色，这个角色有一些特定的属性和操作，然后实现接口的类就好比扮演这个角色的人，一个角色可以由不同的人来扮演，而不同的演员之间除了扮演一个共同的角色之外，并不要求其它的共同之处。\n常用的Interface: # Set # Set注重独一无二,该体系集合可以知道某物是否已经存在于集合中,不会存储重复的元素。Set的实现类在面试中常用的是：HashSet 与 TreeSet\n","title":"Java","type":"notes"},{"content":"Algorithm Template: 1. Binary Search 2. Two Pointers 3. Sorting 4. Binary Tree Divide \u0026amp; Conquer 5. BST Iterator 6. BFS 7. DFS 8. Dynamic Programming 9. Heap 10. Union Find 11. Trie\nData structures: 1. Array 2. Queue 3. Linked List 4. Binary Tree 5. Hash Map 6. Stack 7. Heap(PriorityQueue) 8. Union Find 9. Trie 10. Deque 11. Monotone Stack\nCoding style: 1. check availability of input java: if (nums == null || nums.length == 0) {return -1} python: if not nums: # nums is None or len(nums) == 0 return -1 2. variables\u0026rsquo; name 3. space 4. indentation\n多用continue少用if和else: for \u0026hellip;\u0026hellip; if \u0026hellip; do something if \u0026hellip; do something do something do something do something //optimize: for \u0026hellip;.. if !\u0026hellip; continue do something if !\u0026hellip; continue\ndo something do something do something do something Initialize Matirx in python: matrix = [[False]*3 for _ in range(3)] # solve the issue introduced by: matrix = [[False] * 3] *3 # check differences: matrix[0] = True\nPython list won\u0026rsquo;t overflow with unbounded index: something[start: infinite] # this is ok\nPython for .. else..: for \u0026hellip;: if \u0026hellip;: break else: do something\n区间型动态规划 1. 状态转移方程 ?? 在Longest Palindromic Substring的dynamic programming 方法里面，要创建那个叫 is_palindrome的二维数组 Java: //状态 boolean[][] isPalindrome == new \u0026hellip;\n// 状态转移方程 isPalindrome[i][j] = isPalindrome[i + 1][j - 1] \u0026amp;\u0026amp; ( s.charAt(i) == s.charAt(j) ) // ?? why here is reverse for loop for (int i = n - 1; i \u0026gt;= 0; i--) { for (int j = i; j \u0026lt; n; j++){ } } //初始化: i and i is single char that is Palindrome for sure isPalindrome[i][i] = true Longest Palindromic Substring Manacher\u0026rsquo;s Algorithm - O(n) //学有余力可以阅读全文并背诵 后缀数组Suffix Array - O(n) //完全不用学 Dynamic Programming - O(n^2) ** Enumeration - O(n^2) **\nSubstring: for length in range(\u0026hellip;): for i in range(n - length + 1): j = i + length - 1\nHash function: magic number: 31 Hash_size = 10^6\nJava: String 类重写了equals方法 vs StringBuilder没有重写 后者只判断是否是同一个实例，即内存地址是否相同\nC++ vs Python: String 前者string is changeable\nJava中 String demo = \u0026ldquo;Hello,world!\u0026rdquo;; 1.int length = demo.length(); //获取字符串的长度 2.boolean equals = demo.equals(\u0026ldquo;Hello,world\u0026rdquo;); // 比较两个字符串相等 3.boolean contains = demo.contains(\u0026ldquo;word\u0026rdquo;); // 是否包含子串 4.String replace = demo.replace(\u0026ldquo;Hello,\u0026rdquo;, \u0026ldquo;Yeah@\u0026rdquo;); // 将指定字符串(或正则表达式)替换，返回替换后的结果 5.char little = demo.charAt(5); // 查找字符串中索引为5的字符（索引从0开始） 6.String trim = demo.trim(); // 将字符串左右空格去除，返回去除空格后的结果 7.String concat = demo.concat(\u0026ldquo;Great!\u0026rdquo;); // 拼接字符串，返回拼接结果 8.char[] charArray = demo.toCharArray(); // 返回该字符串组成的字符数组 9.String upperCase = demo.toUpperCase(); // 返回该字符串的大写形式 10.String lowerCase = demo.toLowerCase(); // 返回该字符串的小写形式\nPython中 s = \u0026ldquo;Hello,World\u0026rdquo; 1.print(s[1]) # \u0026rsquo;e\u0026rsquo;, 取出某个位置的字符 2.print(s[1:6]) # \u0026rsquo;ello,\u0026rsquo; ，字符串切片 3.print(len(s)) # 11, 返回字符串的长度 4.print(\u0026ldquo;e\u0026rdquo; in s) # True, 返回字符是否在字符串中 5.print(s.lower()) # \u0026lsquo;hello,world\u0026rsquo;, 将字符串所有元素变为小写 6.print(s.upper()) # \u0026lsquo;HELLO,WORLD\u0026rsquo;, 将字符串所有元素变为大写 7.s += \u0026lsquo;\u0026hellip;\u0026rsquo; # Hello,World\u0026hellip; ，字符串拼接，在字符串后拼接另一个字符串 8.print(s.find(\u0026rsquo;lo\u0026rsquo;)) # 3, 返回第一次找到指定字符串的起始位置（从左往右找） 9.print(s.swapcase()) # hELLO,wORLD\u0026hellip;, 将大小写互换 10.print(s.split(\u0026rsquo;,\u0026rsquo;)) # [\u0026lsquo;Hello\u0026rsquo;, \u0026lsquo;World\u0026hellip;\u0026rsquo;], 将字符串根据目标字符分割\n背诵贪心算法： https://www.jiuzhang.com/qa/2099/ http://www.lintcode.com/problem/majority-number/ http://www.lintcode.com/problem/create-maximum-number/ http://www.lintcode.com/problem/jump-game-ii/ http://www.lintcode.com/problem/jump-game/ http://www.lintcode.com/problem/gas-station/ http://www.lintcode.com/problem/delete-digits/ http://www.lintcode.com/problem/task-scheduler/\nPython: sorted(list) O(nlogn)\nO(logN)的算法几乎可以确定是二分法\nQuick Sort Algorithm: O(nlogn) vs O(n^2)\nO(max(n, m)) == O(n + m): n + m \u0026gt; max(n, m) \u0026gt; (n + m) / 2 O(n + m) \u0026gt; O(max(n, m)) \u0026gt; O((n + m) / 2)\nO(n): 1. 双指针算法 2. 打擂台算法 3. 单调栈算法 4. 单调队列算法 etc.\n双指针算法： 1. 相向双指针（判断回文串） 2. 背向双指针（最长回文串） 3. 同向双指针\n相向双指针的分类： Reverse: 翻转字符串 判断回文串\nTwo Sum: 两数之和 三数之和 Partition: 快速排序 颜色排序 Python String methods: isdigit() isalpha() lower() upper()\nC++ String methods: isdigit() isalpha() putchar(tolower(c)) putchar(toupper(c))\nJava String methods: isLetter(c) isDigit(c) toLowerCase(c) toUpperCase(c)\ntwoSum： hashmap: 时间复杂度O(n) 空间复杂度O(n) two pointers: O(nlogn) O(1)\nQuick Sort Algorithm: 平均时间复杂度O(nlogn)，最坏时间复杂度O(n^2) 额外空间复杂度：O(1) in-place 不稳定排序 先整体有序，再局部有序 \u0026lt;= pivot \u0026gt;= pivot To divide balancely ??why pivot has to balance\nMerge Sort Algorithm: 平均时间复杂度O(nlogn)，最好\u0026amp;最坏时间复杂度O(nlogn) 额外空间复杂度：O(n) 稳定排序 先局部有序，再整体有序\nT(n) = 2T(n/2) + O(n) Quick Sort Merge Sort\n快速选择算法的 Partition 的实质： 快速选择/快速排序中的 partition 是 可左可右 的partition，也就是说，对于nums[i] == pivot 时，这个数字既可以放在左边，也可以放在右边。\n为什么这样划分数组呢？ 原因是为了避免出现类似 [1,1,1,1,1,1] 的数组中的元素，全部被分到一边的情况。我们让 nums[i] == pivot 的情况既不属于左边也不属于右边，这样就能够让 partition 之后的结果稍微平衡一些。 如果 quick select / quick sort 写成了nums[i] \u0026lt; pivot 在左侧，nums[i] \u0026gt;= pivot 在右侧这种形式，就会导致划分不平均，从而导致错误或者超时。\n为什么问题《partition array》不能使用同样的代码？ 对于问题《partition array》来说，题目的要求是将数组划分为两个部分，一部分满足一个条件，另外一部分不满足这个条件，所以可以严格的把 nums[i] \u0026lt; pivot 放在左侧，把 nums[i] \u0026gt;= pivot 放在右侧，这样子做完一次 partition 之后，就能够将这两部分分开。\n总结 简单的说就是，quick select 和 quick sort 的 partition 目标不是将数组 严格的按照 nums[i] \u0026lt; pivot 和nums[i] \u0026gt;= pivot 去拆分开，而是只要能够让左半部分 \u0026lt;= 右半部分即可。这样子 nums[i] == pivot 放在哪儿都无所谓，两边都可以放。\nQuick Select Algorithm: 平均时间复杂度O(n) T(n) = O(n) + T(n/2) = O(n) + O(n/2) + O(n/4) + \u0026hellip; + O(1) = O(n)\nQuick Select Algorithm: start~i（包含start，不包含i），一共是i-start个元素，现在要求第k大，左边已经去掉了i-start个元素，那么他在右边就是第k-(i-start)个元素了\nO(1) 位运算 O(logn)\t二分法，倍增法，快速幂算法，辗转相除法 O(n)\t枚举法，双指针算法，单调栈算法，KMP算法，Rabin Karp，Manacher\u0026rsquo;s Algorithm\t又称作线性时间复杂度 O(nlogn)\t快速排序，归并排序，堆排序 O(n^2)\t枚举法，动态规划，Dijkstra O(n^3)\t枚举法，动态规划，Floyd O(2^n)\t与组合有关的搜索问题 O(n!)\t与排列有关的搜索问题\n递归的三要素： public class Solution { // 1.递归的定义：函数接受什么样的参数，返回什么样的值，代表什么样的意思 public int fibonacci(int n) { // 3.递归的出口 if (n \u0026lt;= 2) { return n -1; }\n// 2.递归的拆解 return fibonacci(n - 1) + fibonacci(n - 2); } } 为了分析（二分法 + 递归）写法的空间复杂度: 我们需要了解一下内存中，栈和堆占用了多少空间\n内存中的栈空间与堆空间: 我们通常所说的内存空间，包含了两个部分：栈空间（Stack space）和堆空间（Heap space）\nStack Space: 当一个程序在执行的时候，操作系统为了让进程可以使用一些固定的不被其他进程侵占的空间用于进行函数调用，递归等操作，会开辟一个固定大小的空间（比如 8M）给一个进程使用。这个空间不会太大，否则内存的利用率就很低。这个空间就是我们说的栈空间，Stack space。\nStack Overflow: 我们通常所说的栈溢出（Stack Overflow）是指在函数调用，或者递归调用的时候，开辟了过多的内存，超过了操作系统余留的那个很小的固定空间导致的。那么哪些部分的空间会被纳入栈空间呢？栈空间主要包含如下几个部分： 1.函数的参数与返回值 2.函数的局部变量\nJava: public int f(int n) { int[] nums = new int[n]; int sum = 0; for (int i = 0; i \u0026lt; n; i++) { nums[i] = i; sum += i; } return sum; }\nPython: def f(n): nums = [0]*n # 相当于Java中的new int[n] sum = 0 for i in range(n): nums[i] = i sum += i return sum\nC++: int f(int n) { int *nums = new int[n]; int sum = 0; for (int i = 0; i \u0026lt; n; i++) { nums[i] = i; sum += i; } return sum; }\n根据我们的定义，参数n，最后的函数返回值f，局部变量sum 都很容易的可以确认是放在栈空间里的。那么主要的难点在 nums。\n这里 nums 可以理解为两个部分： 一个名字叫做 nums 的局部变量，他存储了指向内存空间的一个地址（Reference），这个地址也就是 4 个字节（32位地址总线的计算机，地址大小为 4 字节）new 出来的，一共有 n 个位置的整数数组，int[n]。一共有 4 * n 个字节。这里 nums 这个变量本身，是存储在栈空间的，因为他是一个局部变量。但是 nums 里存储的 n 个整数，是存储在堆空间里的，Heap space。他并不占用栈空间，并不会导致栈溢出。\n在大多数的编程语言中，特别是 Java, Python 这样的语言中，万物皆对象，基本上每个变量都包含了变量自己和变量所指向的内存空间两个部分的逻辑含义。\nJava: public int[] copy(int[] nums) { int[] arr = new int[nums.length]; for (int i = 0; i \u0026lt; nums.length; i++) { arr[i] = nums[i] } return arr; }\npublic void main() { int[] nums = new int[10]; nums[0] = 1; int[] new_nums = copy(nums); } Python: def copy(nums): arr = [0]*len(nums) # 相当于Java中的new int[nums.length] for i in range(len(nums)): arr[i] = nums[i] return arr\n# 用list comprehension实现同样功能 def copy(nums): arr = [x for x in nums] return arr # 以下相当于Java中的main函数 if __name__ == \u0026quot;__main__\u0026quot;: nums = [0]*10 nums[0] = 1 new_nums = copy(nums) C++: int* copy(int nums[], int length) { int *arr = new int[length]; for (int i = 0; i \u0026lt; length; i++) { arr[i] = nums[i]; } return arr; }\nint main() { int *nums = new int[10]; nums[0] = 1; int *new_nums = copy(nums, 10); return 0; } 在 copy 这个函数中，arr 是一个局部变量，他在 copy 函数执行结束之后就会被销毁。但是里面 new 出来的新数组并不会被销毁。这样，在 main 函数里，new_nums 里才会有被复制后的数组。所以可以发现一个特点： 1.栈空间里存储的内容，会在函数执行结束的时候被撤回\n栈空间和堆空间的区别： new 出来的就放在堆空间，其他都是栈空间\n什么是递归深度： 递归函数在内存中，同时存在的最大次数。\nJava: int factorial(int n) { if (n == 1) { return 1; } return factorial(n - 1) * n; }\nPyhton: def factorial(n): if n == 1: return 1 return factorial(n-1) * n\nC++: int factorial(int n) { if (n == 1) { return 1; } return factorial(n - 1) * n; }\n当n=100时，递归深度就是100。一般来说，我们更关心递归深度的数量级，在该阶乘函数中递归深度是O(n)，而在二分查找中，递归深度是O(log(n))。在后面的教程中，我们还会学到基于递归的快速排序、归并排序、以及平衡二叉树的遍历，这些的递归深度都是(O(log(n))。注意，此处说的是递归深度，而并非时间复杂度。\n太深的递归会内存溢出: 首先，函数本身也是在内存中占空间的，主要用于存储传递的参数，以及调用代码的返回地址。 函数的调用，会在内存的栈空间中开辟新空间，来存放子函数。递归函数更是会不断占用栈空间，例如该阶乘函数，展开到最后n=1时，内存中会存在factorial(100), factorial(99), factorial(98) \u0026hellip; factorial(1)这些函数，它们从栈底向栈顶方向不断扩展。 当递归过深时，栈空间会被耗尽，这时就无法开辟新的函数，会报出stack overflow这样的错误。 所以，在考虑空间复杂度时，递归函数的深度也是要考虑进去的。\nFollow up： 尾递归：若递归函数中，递归调用是整个函数体中最后的语句，且它的返回值不属于表达式的一部分时，这个递归调用就是尾递归。（上例factorial函数满足前者，但不满足后者，故不是尾递归函数） 尾递归函数的特点是：在递归展开后该函数不再做任何操作，这意味着该函数可以不等子函数执行完，自己直接销毁，这样就不再占用内存。一个递归深度O(n)的尾递归函数，可以做到只占用O(1)空间。这极大的优化了栈空间的利用。 但要注意，这种内存优化是由编译器决定是否要采取的，不过大多数现代的编译器会利用这种特点自动生成优化的代码。在实际工作当中，尽量写尾递归函数，是很好的习惯。 而在算法题当中，计算空间复杂度时，建议还是老老实实地算空间复杂度了，尾递归这种优化提一下也是可以，但别太在意。\n??尾递归\n二分法vs哈希表：O(logn) vs O(1) 二分法不限制于内存，可将数据放在磁盘上，然后进行二分操作 哈希表依赖内存\n二分查找 vs 普通查找: 二分搜索的有点就是时间复杂度是O(logn)的，而普通的查找时间复杂度是O(n)的。但他们所消耗的空间是一样的。同时普通查找不需要数组有序，直接for循环即可，但是二分查找需要数组有一定的顺序。\n二分法/Java: public class Solution { /** * @param A an integer array sorted in ascending order * @param target an integer * @return an integer */ public int findPosition(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; }\nint start = 0, end = nums.length - 1; // 要点1: start + 1 \u0026lt; end while (start + 1 \u0026lt; end) { // 要点2：start + (end - start) / 2 int mid = start + (end - start) / 2; // 要点3：=, \u0026lt;, \u0026gt; 分开讨论，mid 不+1也不-1 if (nums[mid] == target) { return mid; } else if (nums[mid] \u0026lt; target) { start = mid; } else { end = mid; } } // 要点4: 循环结束后，单独处理start和end if (nums[start] == target) { return start; } if (nums[end] == target) { return end; } return -1; } }\n二分法/Python: class Solution: # @param nums: The integer array # @param target: Target number to find # @return the first position of target in nums, position start from 0 def binarySearch(self, nums, target): if not nums: return -1\nstart, end = 0, len(nums) - 1 # 用 start + 1 \u0026lt; end 而不是 start \u0026lt; end 的目的是为了避免死循环 # 在 first position of target 的情况下不会出现死循环 # 但是在 last position of target 的情况下会出现死循环 # 样例：nums=[1，1] target = 1 # 为了统一模板，我们就都采用 start + 1 \u0026lt; end，就保证不会出现死循环 while start + 1 \u0026lt; end: # python 没有 overflow 的问题，直接 // 2 就可以了 # java和C++ 最好写成 mid = start + (end - start) / 2 # 防止在 start = 2^31 - 1, end = 2^31 - 1 的情况下出现加法 overflow mid = (start + end) // 2 # \u0026gt; , =, \u0026lt; 的逻辑先分开写，然后在看看 = 的情况是否能合并到其他分支里 if nums[mid] \u0026lt; target: # or start = mid + 1 start = mid elif nums[mid] == target: end = mid else: # or end = mid - 1 end = mid # 因为上面的循环退出条件是 start + 1 \u0026lt; end # 因此这里循环结束的时候，start 和 end 的关系是相邻关系（1和2，3和4这种） # 因此需要再单独判断 start 和 end 这两个数谁是我们要的答案 # 如果是找 first position of target 就先看 start，否则就先看 end if nums[start] == target: return start if nums[end] == target: return end return -1 跟面试官核实： 1.输入是否有序 how are these numbers given, can I assume that they are kind like an array or something \u0026raquo; ok oh interesting ok 2.有没有重复数字 how about repeating elements, can I assume that they would be like for instance here, what if I didn\u0026rsquo;t have that \u0026lsquo;four\u0026rsquo;, could I use like the \u0026lsquo;four\u0026rsquo; and \u0026lsquo;four\u0026rsquo; to get that \u0026rsquo;eight\u0026rsquo;? // you can\u0026rsquo;t repeat the same element at the same index twice but certainly the same number may appear twice \u0026raquo; ok ok so like that would be yes how about these numbers are they integers or are they floating points // you can assume they will be always integers \u0026raquo; ok negatives positives // negatives can happen \u0026raquo; ok cool so well the first the simplest solution of course is just comparing every single possible pair, so I \u0026raquo; could just have two for loops, one scanning the whole thing and then the second one starting from let\u0026rsquo;s say you \u0026raquo; have the \u0026lsquo;I\u0026rsquo; loop and then the \u0026lsquo;J\u0026rsquo; loop starting from \u0026lsquo;I\u0026rsquo; plus one, so that I don\u0026rsquo;t repeat the same value and \u0026raquo; just testing all of them if the sum is equal to the target sum.\n\u0026gt;\u0026gt; I mean that's obviously not very efficient but that would be like a way to solve it // that would work, it certainly would be time-consuming \u0026gt;\u0026gt; yeah that would be quadratic, so, better than quadratic, Ah, well, since it's sorted, okay, I guess I need to \u0026gt;\u0026gt; figure out when I have a number what I'm looking for is if there's another number that sums to 'eight', so, so, \u0026gt;\u0026gt; if I have a 'one' what I'd need to figure out is if there's a 'seven' somewhere in the array and that's the case \u0026gt;\u0026gt; it's sorted then I can do binary search, I guess if I go here and I binary search for a 'seven', then I go here \u0026gt;\u0026gt; and I binary search for a 'six' which is the complement of that, and when I go here I binary search for a 'five', \u0026gt;\u0026gt; and at the end I just don't do anything, and so in this case I would solve it like that. \u0026gt;\u0026gt; So that's a bit better than quadratic, I guess binary search is log algorithm in a sorted list. // also an answer, you're kind of slow // so what if you took a look at instead of doing a binary search which is unidirectional, what if you started with // a pair of numbers to begin with \u0026gt;\u0026gt; okay // and then work your way through in work from there \u0026gt;\u0026gt; let's see, so, if I, okay, let me try to bound this thing, so the, the largest possible sum, I guess would be the \u0026gt;\u0026gt; last two values // that would be a largest possible sum, yes \u0026gt;\u0026gt; the smallest possible sum would be the two smallest right, so, so, anything in between, WOW, okay, so the range \u0026gt;\u0026gt; of the possible values is that (posture) right, so there's nothing that is probably small there's nothing that \u0026gt;\u0026gt; can be smaller than this value // right \u0026gt;\u0026gt; there's nothing that can be larger than that value \u0026gt;\u0026gt; okay, so, if this sum (the first value + the last value) is 'ten' in this case([1,2,3,9], sum = 8, ans = NO) it's \u0026gt;\u0026gt; too large, so I need to find a smaller sum, so I could just move this one over here and if that is too small now \u0026gt;\u0026gt; and I need to move that one over there, okay, so, I can I think I can just do it with with that in a, in a \u0026gt;\u0026gt; linear solution just moving at each iteration, I either move the high one lower if I am if my pair is too large \u0026gt;\u0026gt; and I move my lower highter if my pair is too small and I end whenever I either find two like in this case I need \u0026gt;\u0026gt; to find a pair that adds up to 'eight' or whenever they cross, so every point I'm moving one of them so they \u0026gt;\u0026gt; would have to at least cross and I move exactly one so that means that it's linear, yeah, so that that would be a \u0026gt;\u0026gt; way of solving that problem. // how does that how does it make that faster than a binary search. \u0026gt;\u0026gt; okay so in the binary search case I was doing log for finding but I had to repeat that for every element that I \u0026gt;\u0026gt; was an O(nlogn) solution. In this case, I just need to do that moving scanning the one time, so it's a linear \u0026gt;\u0026gt; solution, so that's that's faster. // so before maybe you could get to coding it but we quit, before we do that maybe you could explain, so if you // explained it in a nonworking example, maybe you have fallen through that same process and working. \u0026gt;\u0026gt; okay, yeah so here I would start with this and that right. So it's five is smaller than 'eight', so I move this \u0026gt;\u0026gt; one here, so that's 'six' that's smaller than 'eight', so I go here, and then that's 'eight', so that's true and \u0026gt;\u0026gt; I return. // excellent \u0026gt;\u0026gt; yeah, I think that would work // okay, so what coding language would you prefer to do is it \u0026gt;\u0026gt; um,I prefered C++ if that's okay // C++ works, okay go for it \u0026gt;\u0026gt; ah perfect, let's see. So, okay, now I realize that I haven't figured out what I need to return. So do I want the \u0026gt;\u0026gt; pair, the indicies of the pair or whether I just found it or not // so for the purpose of the example we'll go with whether you're founder or not, but let's say you were going to // return the pair, how could that become a problem that there was no pair 3.需不需要去掉重复答案 双指针的类型 1.背向双指针 第一节课中的Longest Palindromic Substring的中心线枚举算法 二分法中学到的Find K Closest Elements 2.相向双指针 O(n) Reverse型（题目不多） Two Sum型（两位数的相关变形） Partition型（两位数的相关变形） 3.同向双指针 滑动窗口类Sliding Window 快慢指针类Fast \u0026amp; Slow Pointers\nBinary Search: input is an ordered array time complexity is O(logn) space complexity is O(1) it is an idea of Decrease and Conquer, not of Divide and Conquer\nO(1) 位运算 O(logn)\t二分法，倍增法，快速幂算法，辗转相除法 O(n)\t枚举法，双指针算法，单调栈算法，KMP算法，Rabin Karp，Manacher\u0026rsquo;s Algorithm\t又称作线性时间复杂度 O(nlogn)\t快速排序，归并排序，堆排序 O(n^2)\t枚举法，动态规划，Dijkstra O(n^3)\t枚举法，动态规划，Floyd O(2^n)\t与组合有关的搜索问题 O(n!)\t与排列有关的搜索问题\nO(logn) 二分法比较多 O(sqrt(n)) 分解质因数(极少) O(n) 双指针，单调栈，枚举法 O(nlogn) 排序, O(N * logN 的数据结构上的操作) O(n^2), O(n^3) 动态规划等 O(2^n) 组合类(combination)的搜索问题 O(n!) 排列类(permutation)的搜索问题\n二分法的四重境界： // 写出不会死循环的二分法 start + 1 \u0026lt; end start + (end - start) / 2 A[mid] ==, \u0026lt;, \u0026gt; A[start], A[end] ? target // 递归与非递归的权衡 // 二分的三大痛点 // 通用的二分模版\n\u0026gt;\u0026gt; 在排序的数据集上进行二分 \u0026gt;\u0026gt; 找到满足某个条件的第一个位置或者最后一个位置 // 在未排序的数据集上进行二分 根据判断保留下有解的那一半或者去掉无解的一半 // 保留有解的一半，或者去掉无解的一半 \u0026gt;\u0026gt; 在答案集上进行二分 step1 确定答案范围 step2 验证答案大小 \u0026gt;\u0026gt; 二分答案并验证答案偏大还是偏小 本质： 求满足某条件的最大值或最小值 最终结果是个有限的集合 每个结果有一个对应的映射 结果集合跟映射集合正相关或负相关 可以通过在映射集合上进行二分，从而实现对结果集合的二分 def binarySearch(self, nums, target): if not nums: return -1\nstart, end = 0, len(nums) - 1 # 用start + 1 \u0026lt; end 而不是 start \u0026lt; end 的目的是为了避免死循环 # 在first position of target 的情况下不会出现死循环 # 但是在last position of target 的情况下会出现死循环 # 样例：nums[1, 1] target = 1 # 为了统一模版，我们就都采用start + 1 \u0026lt; end，就保证不会出现死循环 while start + 1 \u0026lt; end: # python 没有overflow 的问题，直接// 2就可以了 # java 和C++ 最好写成 mid = start + (end - start) / 2 # 防止在start = 2^31 - 1, end = 2^31 - 1 的情况下出现加法overflow mid = (start + end) // 2 # \u0026gt;, =, \u0026lt; 的逻辑先分开写，然后再看看 = 的情况是否能合并到其他分支里 if nums[mid] \u0026lt; target: # 写作 start = mid + 1 也是正确的 # 只是可以偷懒不写，因为不写也没问题，不会影响时间复杂度 # 不写的好处是，万一你不小心写成了 mid - 1 你就错了 start = mid elif nums[mid] == target: end = mid else: # 写作 end = mid - 1 也是正确的 # 只是可以偷懒不写，因为不写也没问题，不会影响时间复杂度 # 不写的好处是，万一你不小心写成了 mid + 1 你就错了 end = mid # 因为上面的循环退出条件是 start + 1 \u0026lt; end # 因此这里循环结束的时候，start 和 end 的关系是相邻关系(1和2，3和4这种) # 因此需要再单独判断 start 和 end 这两个数谁是我们要的答案 # 如果是找 first position of target 就先看 start, 否则就先看 end if nums[start] == target: return start if nums[end] == target; return end return -1 Index |Value |二分问题 # 0 |1 | # 1 |3 | # 2 |3 | # 3* |4 |小于5的最大index(或value),插入5的index | # 4* |5 |大于等于5的最小index(或value) | # 5 |5 |任意一个出现5的index | 5出现的次数 = 等于5的最大index - 等于5的 \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash; 最小index + 1 6* |5 |小于等于5的最大index(或value) | # 7* |7 |大于5的最小index(或value),插入5的index | # 8 |8 | # 9 |9 | # 动态数组: (ListinPython,ArrayListinJava,vectorinC++)\n倍增的时间复杂度： O(x) approx. O(logK) 二分的时间复杂度： O(log(2^x)) = O(x) approx. O(logK)\nQueue 队列（queue）是一种采用先进先出（FIFO，first in first out）策略的抽象数据结构。比如生活中排队，总是按照先来的先服务，后来的后服务。队列在数据结构中举足轻重，其在算法中应用广泛，最常用的就是在宽度优先搜索(BFS）中，记录待扩展的节点。\n队列内部存储元素的方式，一般有两种，数组（array）和链表（linked list）。两者的最主要区别是： 1.数组对随机访问有较好性能。 2.链表对插入和删除元素有较好性能。\n用数组实现队列 在使用数组表示队列时，我们首先要创建一个长度为MAXSIZE的数组作为队列。\n因为MAXSIZE是数组的长度，那MAXSIZE-1就是队列的最大下标了。 在队列中，除了用一组地址连续的存储单元依次存储从队首到队尾的元素外，还需要附设两个整型变量head和tail分别指示队首和队尾的位置。 我们主要介绍三个操作： 初始化队列 enqueue()向队尾插入元素 dequeue()删除并返回队首元素 每次元素入队时，tail向后移动；每次元素出队时，head向后移动。 我们可以将队列视作一个类，通过成员变量数组来表示一组地址连续的存储单元，再定义两个成员变量head和tail，将队列的基本操作定义成类的方法。（注意：为了将重点放在实现队列上，做了适当简化。示范队列仅支持整数类型，若想实现泛型，可用反射机制和object对象传参；此外，可多做安全检查并抛出异常） java: public class MyQueue { public int head, tail; public int MAXSIZE = 100000; public int[] queue = new int[MAXSIZE];\npublic MyQueue() { head = tail = 0; // do initialize if necessary } public void enqueue(int item) { // 队列已满 if(tail == MAXSIZE){ return ; } queue[tail++] = item; } public int dequeue() { // 队列为空 if (head == tail){ return -1; } return queue[head++]; } }\npython: class MyQueue(object):\ndef __init__(self): # do some intialize if necessary self.MAXSIZE = 4 self.queue = [0] * self.MAXSIZE self.head, self.tail = 0, 0 # @param {int} item an integer # @return nothing def enqueue(self, item): queue = self.queue # 队列满 if self.tail == self.MAXSIZE: return queue[self.tail] = item self.tail += 1 # @return an integer def dequeue(self): queue = self.queue ## 队列为空 if self.head == self.tail: return -1 item = queue[self.head] self.head += 1 return item 但是大家会发现，如果这样实现队列的话，我们考虑MAXSIZE为4的情况，如果我们采取下面的操作 enqueue(1) enqueue(2) enqueue(3) enqueue(4) dequeue() dequeue() 结束后数组的状态时[^, ^, 3, 4], head = 2, tail = 4。（\u0026rsquo;^\u0026lsquo;表示该位置为空，即当前元素已经出队） 从我们之前的判断来看，tail == MAXSIZE , 当前队列已经满了，不能继续添加元素了，但是实际上我们还可以继续添加元素。因此在使用数组实现队列时，可能会出现空间未有效利用的情况，因此，我们有两种解决方法： 使用链表来实现队列 使用数组来实现循环队列\n那么我们就先来看用链表来实现队列的方法：\n用链表实现队列 链表是由多个节点构成的，一个节点由两部分组成:一个是数据域,一个是指针域. 链表分为:单链表(只能是父节点引用子节点),双链表(相邻的节点可相互引用),循环链表(在双链表的基础上,头尾节点可相互引用). 实现链表,就是在链表里加入节点,使用节点的引用域使节点之间形成连接,可相互调用. 链表队列的实现原理:首先定义一个节点类,节点类包含引用域和数据域.然后定义一个链表类,链表类形成节点间的引用关系.\n我们主要介绍三个操作： 初始化队列 enqueue()向队尾插入元素 dequeue()删除并返回队首元素\n在队列中，我们只要用两个指针head和tail分别指向链表的头部和尾部即可实现基本队列功能\njava: class Node { public int val; public Node next; public Node(int _val) { val = _val; next = null; } }\npublic class MyQueue { public Node head, tail;\npublic MyQueue() { head = tail = null; // do initialize if necessary } public void enqueue(int item) { if (head == null) { tail = new Node(item); head = tail; } else { tail.next = new Node(item); tail = tail.next; } } public int dequeue() { if (head != null) { int item = head.val; head = head.next; return item; } return -1; } }\npython: class Node(): def init(self, _val): self.next = None self.val = _val\nclass MyQueue(object):\ndef __init__(self): # do some intialize if necessary self.head, self.tail = None, None # @param {int} item an integer # @return nothing def enqueue(self, item): if self.head is None: self.head = Node(item) self.tail = self.head else: self.tail.next = Node(item) self.tail = self.tail.next # @return an integer def dequeue(self): if self.head is not None: item = self.head.val self.head = self.head.next return item return -1 可以发现链表可以轻松地避免“假溢出”的问题，因为在每次需要新增元素时，只需要新建一个ListNode就可以了。 当然，我们也可以用循环队列来解决这个问题，接下来我们就来看一下循环队列如何实现队列。\n如何自己用数组实现循环队列 队列是一种先进先出的线性表，它只允许在表的一端进行插入，而在另一端删除元素。允许插入的一端称为队尾，允许删除的一端称为队首。但是我们之前也提到了，数组实现的队列会导致“虽然数组没满，但是tail已经指向了数组末尾，返回数组已满，队列溢出的错误信号”，我们称之为“假溢出”。\n为充分利用向量空间，克服\u0026quot;假溢出\u0026quot;现象的方法是：将向量空间想象为一个首尾相接的圆环，并称这种向量为循环向量。存储在其中的队列称为循环队列（Circular Queue）。循环队列是把顺序队列首尾相连，把存储队列元素的表从逻辑上看成一个环，成为循环队列。 我们主要介绍三个操作： 初始化循环队列 enqueue()向队尾插入元素 dequeue()删除并返回队首元素\n在循环队列中，除了用一组地址连续的存储单元依次存储从队首到队尾的元素外，还需要附设两个整型变量head和tail分别指示队首和队尾的位置。\n我们可以将循环队列视作一个类，通过成员变量数组来表示一组地址连续的存储单元，再定义两个成员变量head和tail，将循环队列的基本操作定义成类的方法，循环效果则用“模”运算实现，以此来实现循环队列。\n每当tail到达末尾的时候，将tail对MAXSIZE取模，使其回到队首。但是如果这样我们会发现一个问题，队列为空和队列已满的条件都成了tail == head。\n为了避免这种无法判断的情况，我们规定当循环队列只剩一个空位的时候，就认为队列已满。这样队列已满的条件就成了 (tail + 1) % MAXSIZE == head。\njava: public class MyQueue { public int head, tail; public int SIZE = 4; public int[] queue = new int[SIZE];\npublic MyQueue() { head = tail = 0; // do initialize if necessary } //压入一个元素 public void enqueue(int item) { // 队列已满 if ((tail + 1) % SIZE == head){ return ; } queue[tail++] = item; tail %= SIZE; } //弹出一个元素 public int dequeue() { // 队列为空 if (head == tail){ return -1; } int item = queue[head++]; head %= SIZE; return item; } }\npython: class MyQueue(object):\ndef __init__(self): # do some intialize if necessary self.SIZE = 100000 self.queue = [0] * self.SIZE self.head, self.tail = 0, 0 # @param {int} item an integer # @return nothing # 压入队列 def enqueue(self, item): queue = self.queue # 队列满 if (self.tail + 1) % self.SIZE == self.head: return queue[self.tail] = item self.tail = (self.tail + 1) % self.SIZE # @return an integer # 弹出元素 def dequeue(self): queue = self.queue ## 队列为空 if self.head == self.tail: return -1 item = queue[self.head] self.head = (self.head + 1) % self.SIZE return item BFS 的使用场景 1.分层遍历 一层一层的遍历一个图、树、矩阵 简单图最短路径 简单图的定义是，图中所有的边长都一样 2.连通块问题 通过图中一个点找到其他所有连通的点 找到所有方案问题的一种非递归实现方式 3.拓扑排序 实现容易度远超过DFS\nBFS 的使用场景（summer） 1.Connected Component 通过一个点找到图中连通的所有点 非递归的方式找所有方案 2.Level Order Traversal 图的层次遍历 简单图最短路径Simple Graph Shortest Path 3.Topological Sorting 求任意拓扑序 求是否有拓扑序 求字典序最小的拓扑序 求是否唯一拓扑序\n以下哪些问题BFS可以处理： A.二叉树的层次遍历 B.求出边长均为5的图的最短路径 E.求出01矩阵上最大的全0块 F.我不会写递归，但我需要从10个数中任意拿出5个的所有方案\n非答案： D.二叉树的先序遍历 解析：先序遍历通常使用递归方式来实现，即使使用非递归方式，也是借助栈来实现的，所以并不适合BFS，而层次遍历因为是一层一层的遍历，所以是BFS十分擅长的；边长一致的图是简单图，所以可以用BFS，因此B可以，因为BFS只适用于简单图，所以C不可以；矩阵连通块也是BFS可以处理的问题，求出最大块只需要维护一个最大值即可；选项F属于求所有方案问题，因此可以用BFS来处理，但是并不是唯一的解决方式。\nBFS 的三种实现方法 1.单队列 2.双队列 3.DummyNode // The \u0026ldquo;dummy\u0026rdquo; node is used to simplify some corner cases such as a list with only one node, or removing the head of the list.\n二叉树的BFS vs 图的BFS： 二叉树中进行 BFS 和图中进行 BFS 最大的区别就是二叉树中无需使用 HashSet（C++: unordered_set, Python: set) 来存储访问过的节点（丢进过 queue 里的节点） 因为二叉树这种数据结构，上下层关系分明，没有环（circle），所以不可能出现一个节点的儿子的儿子是自己的情况。 但是在图中，一个节点的邻居的邻居就可能是自己了。\n有很多种方法可以存储一个图，最常用的莫过于： 1.邻接矩阵 2.邻接表 而邻接矩阵因为耗费空间过大，我们通常在工程中都是使用邻接表作为图的存储结构。\n邻接矩阵 Adjacency Matrix [ [1,0,0,1], [0,1,1,0], [0,1,1,0], [1,0,0,1] ] 例如上面的矩阵表示0号点和3号点有连边。1号点和2号点有连边。 当然，每个点和自己也是默认有连边的。 图中的 0 表示不连通，1 表示连通。 我们也可以用一个更具体的整数值来表示连边的长度。 邻接矩阵我们可以直接用一个二维数组表示，如 int[][] matrix; 这种数据结构因为耗费 O(n^2) 的空间，所以在稀疏图上浪费很大，因此并不常用。\n邻接表 Adjacency List ?? [ [1], [0,2,3], [1], [1] ] 这个图表示 0 和 1 之间有连边，1 和 2 之间有连边，1 和 3 之间有连边。即每个点上存储自己有哪些邻居（有哪些连通的点）。 这种方式下，空间耗费和边数成正比，可以记做 O(m)，m代表边数。m最坏情况下虽然也是 O(n^2)，但是邻接表的存储方式大部分情况下会比邻接矩阵更省空间。 可以用自定义的类来实现邻接表\nJava: class DirectedGraphNode { int label; List neighbors; ... } Python: class DirectedGraphNode: def init(self, label): self.label = label self.neighbors = [] # a list of DirectedGraphNode's ... 也可以使用 HashMap 和 HashSet 搭配的方式来存储邻接表 Map\u0026lt;T, Set\u0026gt; = new HashMap\u0026lt;Integer, HashSet\u0026gt;();\nPython: 假设nodes为节点标签的列表: 使用了Python中的dictionary comprehension语法 adjacency_list = {x:set() for x in nodes}\n另一种写法 adjacency_list = {} for x in nodes: adjacency_list[x] = set() 其中 T 代表节点类型。通常可能是整数(Integer)。 这种方式虽然没有上面的方式更加直观和容易理解，但是在面试中比较节约代码量。 而自定义的方法，更加工程化，所以在面试中如果时间不紧张题目不难的情况下，推荐使用自定义邻接表的方式。\n为什么 BFS 可以搜索到最短路？ 因为BFS是按照层级进行搜索，所以搜到答案时，一定是最短的一条路径。 我们可以使用反证法进行证明： 我们假设当前搜索到的路径 Y 不是最短的，那就说明存在一条更短的路径 X（即 X \u0026lt; Y）。 令路径 X 中的所有点是 {x1,x2,\u0026hellip;,xx}。 那么x1是起点，且为 BFS 的第一层，x2为第二层\u0026hellip;\u0026hellip;xx为第x层， 此时的结果与BFS中第Y层初次遇到xx点产生矛盾。 因此不存在任何一条比Y短的路径能找到终点。\nJava: 值传递，引用传递 Python: 引用传递 但是python 的不可变类型可以 认为 是值传递的 C++: 值传递，引用传递，指针传递\n不同语言中呈现值传递的场景： Java 的基本数据类型： byte, short, int, long, float, double, char, boolean C++: 默认值传递 Python: 没有值传递\n不同语言中呈现引用传递的场景： Java: 除基本数据类型以外的其他数据 C++: 在参数列表中加地址符\u0026amp;修饰 Python: 全是引用传递\nRecursion/ DFS/ Backtracking: 递归/ 深搜/ 回溯法\nRecursion: 递归函数：程序的一种实现方式，即函数进行了自我调用 递归算法：即大问题的结果依赖于小问题的结果，于是先用递归函数求解小问题 一般我们说递归的时候，大部分时候都在说递归函数而不是递归算法\nDFS： 可以使用递归函数实现 也可以不用递归函数来实现，如自己通过一个手动创建的栈Stack 进行操作 深度优先搜索通常是指在搜索的过程中，优先搜索深度更深的点而不是按照宽度搜索同层节点\nBacktracking: 回溯法： == 深度优先搜索算法 回溯操作：递归函数在回到上一层递归调用处的时候，一些参数需要改回到调用前的值，这个操作就是回溯，即让状态参数回到之前的值，递归调用前做了什么改动，递归调用之后都改回来\n遍历法 vs 分治法： 都可以用DFS实现 遍历法 = 一个小人拿着一个记事本走遍所有都节点 分治法 = 分配小弟去做子任务，自己进行结果汇总\n遍历法：通常会用到一个全局变量或者是共享参数 分治法：通常将利用return value 记录子问题结果 二叉树上的分治法本质上也是在做遍历（后序遍历） 先序？中序？后序？ 平衡二叉树： 任意节点左右子树高度之差不超过1\n计算深度： 适合用分治法解决这个问题\nBinary Search Tree 二叉查找树： 一种特殊的二叉树 定义： 左子树节点值 \u0026lt; 根节点的值，右子树节点的值 \u0026gt;= 根节点的值 相等的情况：值相等的点可能在右子树，或者可能在左子树，需要根面试官澄清 中序遍历： 中序遍历结果有序（不下降的顺序，有些相邻点可能相等） 如果二叉树的中序遍历不是“不下降”序列，则一定不是BST 如果二叉树的中序遍历是“不下降”序列,也未必是BST，反例：{1,1,1} 二叉查找树的高度： 最坏O(n), 最好O(logn), 用O(h) 表示更合适 只有Balanced Binary Tree（平衡二叉树）才是O(logn)\nBST 基本操作： Build: 1359.Convert Sorted Array to Binary Search Tree https://www.lintcode.com/problem/convert-sorted-array-to-binary-search-tree/description\nInsert: 85.Insert Node in a Binary Search Tree https://www.lintcode.com/problem/insert-node-in-a-binary-search-tree/description Search: 1524.Search in a Binary Search Tree https://www.lintcode.com/problem/search-in-a-binary-search-tree/description Delete: 701.Trim a Binary Search Tree https://www.lintcode.com/problem/trim-a-binary-search-tree/description Iterate: 86.Binary Search Tree Iterator https://www.lintcode.com/problem/binary-search-tree-iterator/description Red-Black Tree 红黑树： 是一种 Balanced BST Java: TreeMap/TreeSet C++: map/set\nApplication: O(logN) 的时间内实现增删改查 O(logN) 的时间内实现找最大找最小 O(logN) 的时间内实现找比某个数小的最大值(upperBound)和比某个数大的最小值(lowerBound) 只考红黑树的应用，不考红黑树的实现 二叉树三种遍历： 先序遍历 Pre-order 中序遍历 In-order 后序遍历 Post-order（分治法）\n“二叉树的中序遍历”的非递归实现 考得最多 通过实现 hasNext 和 next 两个方法，从而实现二叉查找树的中序遍历迭代器 https://www.lintcode.com/problem/binary-search-tree-iterator/ 86.Binary Search Tree Iterator 相当于 Binary Tree In-order Iterator 实现要点： 递归-\u0026gt;非递归，意味着自己需要控制原来由操作系统控制的栈的进进出出 如何找到最小的第一个点？最左边的点即是 如何求出一个二叉树节点在中序遍历中的下一个节点？ 在stack中记录从根节点到当前节点的整条路径 下一个点 = 右子树最小点 or 路径中最近一个通过左子树包含当前点的点 Python: def init(self, root): self.stack = [] while root != None: self.stack.append(root) root = root.left\ndef hasNext(self): return len(self.stack) \u0026gt; 0\ndef next(self): node = self.stack[-1] if node.right is not None: n = node.right while n != None: self.stack.append(n) n = n.left else: n = self.stack.pop() while self.stack and self.stack[-1].right == n: n = self.stack.pop()\nreturn node Java: private Stack stack = new Stack\u0026lt;\u0026gt;();\npublic BSTIterator(TreeNode root) { while (root != null) { stack.push(root); root = root.left; } }\npublic boolean hasNext() { return !stack.isEmpaty(); }\npublic TreeNode next() { TreeNode curt = stack.peek(); TreeNode node = curt;\nif (node.right == null) { node = stack.pop(); while (!stack.isEmpty() \u0026amp;\u0026amp; stack.peek().right == node) { node = stack.pop(); } } else { node = node.right; while (node != null) { stack.push(node); node = node.left; } } return curt; }\nBST中最小的节点是从根节点一直往左走遇见的叶子节点，它不一定在树的最底层；BST的特征就是中序遍历是严格递增的；如果这颗BST是一条链，那么找到最小值节点的算法是O(n)的，除非这个BST是一个满二叉树。\n简单图： 没有方向(undirected) 没有权重(unweighted) 两点之间最多只有一条边(no multiple edges) 一个点没有一条边连接着自己(no graph loops, 这里的graph loop指的是自己直接指向自己的loop)\n解决最短路径的算法： 简单图： BFS 复杂图： Floyd, Dijkstra, Bellman-ford, SPFA\ntime compelxity of recursive: 一次* 次数 space： 一次 + 深度\n遇到二叉树的问题，就想想整棵树在该问题上的结果和左右孩子在该问题上的结果之间有什么联系\n拓扑排序Topological Sorting: 图 + 有依赖关系 + 有向 + 无环 = 拓扑排序\n通过拓扑排序判断是否图是否有环 入度（in-degree）： 有向图（Directed Graph）中指向当前节点的点的个数（或指向当前节点的边的条数） 算法描述： 1.统计每个点的入度 2.将每个入度为0的点放入队列（Queue）中作为起始节点 3.不断从队列中拿出一个点，去掉这个点的所有连边（指向其他点的边），其他点的相应的入度-1 4.一旦发现新的入度为0的点，丢回队列中 拓扑排序并不是传统的排序算法： 一个图可能存在多个拓扑排序（Topological Graph），也可能不存在任何拓扑排序 拓扑排序的四种不同问法： 求任意拓扑序 求是否有拓扑序 求字典序最小的拓扑序 求是否唯一拓扑序\nBFS conclusion: 1.能用BFS的一定不要用DFS（除非面试官特别要求） 2.BFS的三个使用场景 连通块问题 层级遍历问题 拓扑排序问题 3.是否需要层级遍历 需要多一重循环 4.矩阵坐标变换数组 deltaX, deltaY 是否在界内：isInBound/ isValid\n组合类DFS 在非二叉树上的深度优先搜索（Depth-first Search）中，90%的问题，不是求组合（Combination）就是求排列（Permutation）。特别是组合类的深度优先搜索的问题特别的多。而排列组合类的搜索问题，本质上是一个“隐式图”的搜索问题。\n隐式图： 一个问题如果没有明确的告诉你什么是点，什么是边，但是又需要你进行搜索的话，那就是一个隐式图搜索问题了\nBFS vs DFS 复杂度: 时间复杂度均为:O(V+E)，V为顶点个数，E为边个数 宽度优先搜索的空间复杂度取决于宽度 深度优先搜索的空间复杂度取决于深度\n栈空间一般用于存放对象的引用，值类型变量和函数调用信息，堆空间才是用于存放对象本身的\n. and [] 修改的是对象本身，不是引用\n递归的三要素：Recursion 1.递归的定义（代表什么含义，接受什么参数，返回什么值） 2.递归的拆解（把大问题拆成小问题） 3.递归的出口（到什么时候结束）\n参数传递：值传递，引用传递\n递归参考习题771, 1333\n尾递归： 尾递归的特点： 函数中所有递归形式的调用都出现在函数的末尾 递归调用不属于表达式的一部分 尾递归的作用： 尾递归的调用不会在栈中去创建一个新的 而是覆盖当前的活动记录 为什么可以尾递归： 在回归过程中不用做任何操作\n不是所有语言都支持尾递归优化： 不支持：python, java, C++ 支持：kotlin(tailrec) 以上四种语言都支持尾递归写法，但是支持尾递归优化都只有kotlin\n不支持尾递归优化的语言，解决stackoverflow： 把递归改成迭代形式 Note：所谓的尾递归优化，就是把递归改成迭代形式。所以如果语言不支持尾递归优化，需要手动将尾递归改成迭代形式。 支持尾递归优化的语言，是由编译器自动将尾递归的代码翻译成迭代形式的代码。\n如何改成迭代： 模拟递归中调用下一层的参数传递过程: 1.先做完本层递归的事儿 2.再计算出下一层递归的各个参数 3.然后把值赋给当前层的各个参数 template: def functionName(parameters): while True: do something ... get new parameters parameters = new parameters 同余定理： x * y % z = (x % z) * (y % z) % z\n1. a % b = (a + b) % b = (a + 2 * b) % b ... = (a + k * b) % b, k 是任意整数 2. x 和 (x % z) 取余相差了整数个z 3. y 和 (y % z) 取余相差了整数个z 解决99%二叉树问题的算法————分治法 第一类考察形态：二叉树上求值，求路径 Maximum/ Minimum/ Average/ Sum/ Paths\n第二类考察形态：二叉树结构变化 记忆化搜索是动态规划的一种实现方式\n记忆化搜索的缺点： 不适合解决O(n)时间复杂度的DP问题，因为会有StackOverflow的风险\n算法思想： 动态规划，递归，分治法，减治法\n递归四要素 vs 动规四要素（状态，方程，初始化，答案）： 动规的状态 State —— 递归的定义 用 f[i] 或者 f[i][j] 代表在某些特定条件下某个规模更小的问题的答案 规模更小用参数 i, j 之类的来划定 动规的方程 Function —— 递归的拆解 大问题如何拆解为小问题 f[i][j] = 通过规模更小的一些状态求 max/ min/ sum/ or 来进行推导 动规的初始化 Initialize —— 递归的出口 设定无法再拆解的极限小的状态下的值 如 f[i][0] 或者 f[0][i] 动规的答案 Answer —— 递归的调用 最后要求的答案是什么 如 f[n][m] 或者 max(f[n][0], f[n][1] \u0026hellip; f[n][m])\n动态规划的两种实现方式： 1.记忆化搜索（使用递归实现） 2.多重循环（使用for循环实现）\n动态规划的使用场景： 求最值：最大值/ 最小值 求可行性：是否存在一种方案 求方案总数：只求总数不求具体方案 Note: 求具体方案的话，DFS更合适\n动态规划的题型： *坐标型：一维坐标/ 二维坐标 *前缀型：一个字符串划分 —— 划分型/ 两个字符串匹配 —— 匹配型 *背包型：最常考 区间型：考较少 博弈型：考得少 树型：基本不考 状态压缩性：TSP问题（作为加分项）/ 其他基本不考\n不同题型的动态规划对一个的状态表示方法是不同的，如果成功的找对了题型，就能够解决DP最难的状态表示问题\n求全部具体方案的问题，虽然有时可以通过动态规划减少一定的运行时间，但是时间复杂度是没法降低的。比如说word break II。因为这种类型的问题，总时间复杂度是与方案总数有关的。其他三种都可以用dp降低时间复杂度。\n坐标型动态规划： dp[i] 表示从起点到坐标 i 的最优值/ 方案数/ 可行性 dp[i][j] 表示从起点到坐标 i, j 的最优值/ 方案数/ 可行性 代表题：Triangle, Unique Paths\n前缀型之划分型： dp[i] 表示前 i 个字符的最优值/ 方案数/ 可行性 dp[i][j] 表示前 i 个字符划分为 j 个部分的最值/ 方案数/ 可行性 代表题：Word Break, Word Break III\n前缀型之匹配型： dp[i][j] 表示第一个字符串的前 i 个字符匹配上第二个字符串的前 j 个字符的最优值/ 方案数/ 可行性 代表题：Longest Common Subsequence, Wildcard Matching\n区间型： dp[i][j] 表示区间 i ~ j 的最优值/ 方案数/ 可行性 代表题：Stone Game, Burst Ballons\n背包型： dp[i][j] 表示前 i 个物品里选出一些物品，组成和为 j 的大小的最优值/ 方案数/ 可行性 两个关键点：前\u0026amp;和 代表题：Backpack 系列\n动态规划的题“必须”是求最优值/ 可行性/ 方案数这三种情况之一 动态规划的状态依赖必须有方向性，“不可以有循环依赖” 坐标型动态规划的状态：“坐标” 坐标型动态规划的方程：“上一步坐标”\n经典的01背包问题 给出n个物品及其大小，问是否能挑选出一些物品装满大小为m的背包\n每个物品要么挑0个（不挑），要么挑1个，所以叫01 如果一个物品可以被分割，就不是01背包 如果一个物品可以选多份，就叫多重背包 https://www.jiuzhang.com/problem/backpack https://www.jiuzhang.com/solutions/backpack Note: 92.backpack.py n个物品，m大小的背包，问最多能装多满 两种状态表示： dp[i][j] 表示前i个数里是否能凑出j的和，true/ false dp[i][j] 表示前i个数里记录凑出的\u0026lt;= j 的最大和 第一种状态表示： 状态State dp[i][j] 表示前i个数里挑若干个数是否能组成和为j 方程function dp[i][j] = dp[i - 1][j] or dp[i - 1][j - A[i]] 如果 j \u0026gt;= A[i] dp[i][j] = dp[i - 1][j] 如果 j \u0026lt; A[i] 初始化initialize dp[0][0] = true dp[0][1...m] = false 答案answer 使得dp[n][v], 0 \u0026lt;= v \u0026lt;= m 为true的最大v 第二种状态表示（效率低于第一种）： 状态State dp[i][j] 表示前i个数里挑出若干个数总和 \u0026lt;= j 的最大和 方程funciton dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + A[i - 1]) 如果 j \u0026gt;= A[i - 1] dp[i][j] = dp[i - 1][j] 如果 j \u0026lt; A[i - 1] 初始化initialization dp[0][0...m] = 0 答案answer dp[n][m] def get_prefix_sum(self, nums): prefix_sum = [0] for num in nums: prefix_sum.append(prefix_sum[-1] + num) return prefix_sum\n使用前缀和数组在O（1）的时间复杂度内计算子数组和 sum from i to j = prefix_sum[j + 1] - prefix_sum[i]\n#include #include int main() { std::string name = \u0026ldquo;wang\u0026rdquo;; bool contain = name.find(\u0026ldquo;ng\u0026rdquo;) != std::string::npos;\ncout \u0026lt;\u0026lt; contain \u0026lt;\u0026lt; endl; return 0; }\nDFS 、动态规划、回溯法、递归之间的关系是什么？ 递归就是自我调用，经常作为一种编程的实现方式，比如题主问题中的DFS 、动态规划、回溯法都可以用递归来实现，当然也可以用非递归来实现。很多时候一个概念也可以用递归的方式来定义（比如gnu）。\n回溯是一种通用的算法，把问题分步解决，在每一步都试验所有的可能，当发现已经找到一种方式或者目前这种方式不可能是结果的时候，退回上一步继续尝试其他可能。很多时候每一步的处理都是一致的，这时候用递归来实现就很自然。\n当回溯用于树的时候，就是深度优先搜索。当然了，几乎所有可以用回溯解决的问题都可以表示为树。那么这俩在这里就几乎同义了。如果一个问题解决的时候显式地使用了树，那么我们就叫它dfs。很多时候没有用树我们也管它叫dfs严格地说是不对的，但是dfs比回溯打字的时候好输入。别的回答里提到了砍枝，实际上这二者都可以砍枝。\n至于动态规划，被题主放到这里是因为都是竞赛中经常会遇到并且学起来不容易明白吗？回溯可以用于所有用穷举法可以解决的问题，而DP只用于具有最优子结构的问题。所以不是所有问题都适合用dp来解决，比如八皇后。dp需要存贮子问题的解，回溯不需要。\n","date":"24 October 2023","externalUrl":null,"permalink":"/notes/al_note_legacy/","section":"Notes","summary":"Algorithm Template: 1. Binary Search 2. Two Pointers 3. Sorting 4. Binary Tree Divide \u0026 Conquer 5. BST Iterator 6. BFS 7. DFS 8. Dynamic Programming 9. Heap 10. Union Find 11. Trie\nData structures: 1. Array 2. Queue 3. Linked List 4. Binary Tree 5. Hash Map 6. Stack 7. Heap(PriorityQueue) 8. Union Find 9. Trie 10. Deque 11. Monotone Stack\nCoding style: 1. check availability of input java: if (nums == null || nums.length == 0) {return -1} python: if not nums: # nums is None or len(nums) == 0 return -1 2. variables’ name 3. space 4. indentation\n","title":"Legacy Algo Note","type":"notes"},{"content":" 学堂链接\n线性代数是否可解 \u0026lt;=\u0026gt; 等价于 常熟向量是否能够表示成系数向量的线性组合\n","date":"24 October 2023","externalUrl":null,"permalink":"/notes/linear_algebra/","section":"Notes","summary":" 学堂链接\n线性代数是否可解 \u003c=\u003e 等价于 常熟向量是否能够表示成系数向量的线性组合\n","title":"Linear Algebra","type":"notes"},{"content":" Friendship Service # class FriendshipService: def __init__(self): # do intialization if necessary self.follower = dict() self.following = dict() def getFollowers(self, user_id): # write your code here if user_id not in self.follower: return [] return sorted(list(self.follower[user_id])) def getFollowings(self, user_id): # write your code here if user_id not in self.following: return [] return sorted(list(self.following[user_id])) def follow(self, to_user_id, from_user_id): # write your code here if to_user_id not in self.follower: self.follower[to_user_id] = set() self.follower[to_user_id].add(from_user_id) if from_user_id not in self.following: self.following[from_user_id] = set() self.following[from_user_id].add(to_user_id) def unfollow(self, to_user_id, from_user_id): # write your code here if to_user_id in self.follower: if from_user_id in self.follower[to_user_id]: self.follower[to_user_id].remove(from_user_id) if from_user_id in self.following: if to_user_id in self.following[from_user_id]: self.following[from_user_id].remove(to_user_id) ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/ood/","section":"Notes","summary":"Friendship Service # class FriendshipService: def __init__(self): # do intialization if necessary self.follower = dict() self.following = dict() def getFollowers(self, user_id): # write your code here if user_id not in self.follower: return [] return sorted(list(self.follower[user_id])) def getFollowings(self, user_id): # write your code here if user_id not in self.following: return [] return sorted(list(self.following[user_id])) def follow(self, to_user_id, from_user_id): # write your code here if to_user_id not in self.follower: self.follower[to_user_id] = set() self.follower[to_user_id].add(from_user_id) if from_user_id not in self.following: self.following[from_user_id] = set() self.following[from_user_id].add(to_user_id) def unfollow(self, to_user_id, from_user_id): # write your code here if to_user_id in self.follower: if from_user_id in self.follower[to_user_id]: self.follower[to_user_id].remove(from_user_id) if from_user_id in self.following: if to_user_id in self.following[from_user_id]: self.following[from_user_id].remove(to_user_id)","title":"OOD","type":"notes"},{"content":" Typing # from typing import reveal_type Logging # V0 # import logging logging.basicConfig(level=logging.INFO) logging.info(\u0026#34;Just an information\u0026#34;) V1 # # importing module import logging # Create and configure logger logging.basicConfig(filename=\u0026#34;newfile.log\u0026#34;, format=\u0026#39;%(asctime)s %(message)s\u0026#39;, filemode=\u0026#39;w\u0026#39;) # Creating an object logger = logging.getLogger() # Setting the threshold of logger to DEBUG logger.setLevel(logging.DEBUG) # Test messages logger.debug(\u0026#34;Harmless debug Message\u0026#34;) logger.info(\u0026#34;Just an information\u0026#34;) logger.warning(\u0026#34;Its a Warning\u0026#34;) logger.error(\u0026#34;Did you try to divide by zero\u0026#34;) logger.critical(\u0026#34;Internet is down\u0026#34;) ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/python/","section":"Notes","summary":"Typing # from typing import reveal_type Logging # V0 # import logging logging.basicConfig(level=logging.INFO) logging.info(\"Just an information\") V1 # # importing module import logging # Create and configure logger logging.basicConfig(filename=\"newfile.log\", format='%(asctime)s %(message)s', filemode='w') # Creating an object logger = logging.getLogger() # Setting the threshold of logger to DEBUG logger.setLevel(logging.DEBUG) # Test messages logger.debug(\"Harmless debug Message\") logger.info(\"Just an information\") logger.warning(\"Its a Warning\") logger.error(\"Did you try to divide by zero\") logger.critical(\"Internet is down\")","title":"Python","type":"notes"},{"content":" Add sentences to the beginning of all markdown files # import os # Define the sentence you want to add sentence_to_add = \u0026#34;\u0026#34;\u0026#34;+++ title = \u0026#39;Markdown\u0026#39; date = 2023-10-23T21:50:46-04:00 draft = true +++ \\n\u0026#34;\u0026#34;\u0026#34; # Specify the directory where your Markdown files are located markdown_directory = \u0026#34;./\u0026#34; # List all the Markdown files in the directory markdown_files = [f for f in os.listdir(markdown_directory) if f.endswith(\u0026#34;.md\u0026#34;)] # Loop through each Markdown file and add the sentence for markdown_file in markdown_files: file_path = os.path.join(markdown_directory, markdown_file) # Open the file in read mode to read its content with open(file_path, \u0026#39;r\u0026#39;) as file: content = file.read() # Open the file in write mode to add the sentence at the beginning with open(file_path, \u0026#39;w\u0026#39;) as file: file.write(sentence_to_add) file.write(content) print(\u0026#34;Sentences added to Markdown files.\u0026#34;) Automatically create branches based on ticket number # import subprocess def create_branch(ticket_number, description): branch_name = f\u0026#34;feature/{ticket_number}-{description.replace(\u0026#39; \u0026#39;, \u0026#39;-\u0026#39;).lower()}\u0026#34; subprocess.run([\u0026#39;git\u0026#39;, \u0026#39;checkout\u0026#39;, \u0026#39;-b\u0026#39;, branch_name]) print(f\u0026#34;Branch \u0026#39;{branch_name}\u0026#39; created and checked out.\u0026#34;) # Example usage create_branch(\u0026#39;PROJ-1234\u0026#39;, \u0026#39;add user authentication\u0026#39;) Pre-Commit Hook for Quality Checks (e.g., Linting) # Create .git/hooks/pre_commit_check.py file with: #!/bin/bash python3 .git/hooks/pre_commit_check.py import subprocess def run_linter(): result = subprocess.run([\u0026#39;flake8\u0026#39;, \u0026#39;.\u0026#39;], capture_output=True, text=True) if result.returncode != 0: print(\u0026#34;❌ Lint errors detected. Commit aborted.\u0026#34;) print(result.stdout) exit(1) else: print(\u0026#34;✅ Linter check passed.\u0026#34;) if __name__ == \u0026#34;__main__\u0026#34;: run_linter() Make pre_commit_check.py executable: chmod +x .git/hooks/pre_commit_check.py Automated Pull, Merge, Push (One-Click Sync) # import subprocess def sync_with_main(): try: subprocess.run([\u0026#39;git\u0026#39;, \u0026#39;checkout\u0026#39;, \u0026#39;main\u0026#39;], check=True) subprocess.run([\u0026#39;git\u0026#39;, \u0026#39;pull\u0026#39;, \u0026#39;origin\u0026#39;, \u0026#39;main\u0026#39;], check=True) subprocess.run([\u0026#39;git\u0026#39;, \u0026#39;checkout\u0026#39;, \u0026#39;-\u0026#39;], check=True) # Go back to the original branch subprocess.run([\u0026#39;git\u0026#39;, \u0026#39;merge\u0026#39;, \u0026#39;main\u0026#39;], check=True) subprocess.run([\u0026#39;git\u0026#39;, \u0026#39;push\u0026#39;], check=True) print(\u0026#34;✅ Synced with main and pushed changes.\u0026#34;) except subprocess.CalledProcessError as e: print(f\u0026#34;❌ Error during sync: {e}\u0026#34;) sync_with_main() Auto-Tagging and Version Bumping # Every time we merge to main, automatically bump version (semver) and create a new tag. import subprocess import re def get_latest_version(): result = subprocess.run([\u0026#39;git\u0026#39;, \u0026#39;tag\u0026#39;], capture_output=True, text=True) tags = result.stdout.split() tags = [t for t in tags if re.match(r\u0026#39;v\\d+\\.\\d+\\.\\d+\u0026#39;, t)] return sorted(tags)[-1] if tags else \u0026#39;v0.0.0\u0026#39; def bump_version(version): major, minor, patch = map(int, version.lstrip(\u0026#39;v\u0026#39;).split(\u0026#39;.\u0026#39;)) return f\u0026#39;v{major}.{minor}.{patch + 1}\u0026#39; def tag_new_version(): latest_version = get_latest_version() new_version = bump_version(latest_version) subprocess.run([\u0026#39;git\u0026#39;, \u0026#39;tag\u0026#39;, new_version]) subprocess.run([\u0026#39;git\u0026#39;, \u0026#39;push\u0026#39;, \u0026#39;origin\u0026#39;, new_version]) print(f\u0026#34;✅ Created and pushed new tag: {new_version}\u0026#34;) tag_new_version() Read Env variables directly # import os API_EMAIL = os.getenv(\u0026#34;JIRA_EMAIL\u0026#34;) API_TOKEN = os.getenv(\u0026#34;JIRA_API_TOKEN\u0026#34;) ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/python_script/","section":"Notes","summary":"Add sentences to the beginning of all markdown files # import os # Define the sentence you want to add sentence_to_add = \"\"\"+++ title = 'Markdown' date = 2023-10-23T21:50:46-04:00 draft = true +++ \\n\"\"\" # Specify the directory where your Markdown files are located markdown_directory = \"./\" # List all the Markdown files in the directory markdown_files = [f for f in os.listdir(markdown_directory) if f.endswith(\".md\")] # Loop through each Markdown file and add the sentence for markdown_file in markdown_files: file_path = os.path.join(markdown_directory, markdown_file) # Open the file in read mode to read its content with open(file_path, 'r') as file: content = file.read() # Open the file in write mode to add the sentence at the beginning with open(file_path, 'w') as file: file.write(sentence_to_add) file.write(content) print(\"Sentences added to Markdown files.\") Automatically create branches based on ticket number # import subprocess def create_branch(ticket_number, description): branch_name = f\"feature/{ticket_number}-{description.replace(' ', '-').lower()}\" subprocess.run(['git', 'checkout', '-b', branch_name]) print(f\"Branch '{branch_name}' created and checked out.\") # Example usage create_branch('PROJ-1234', 'add user authentication') Pre-Commit Hook for Quality Checks (e.g., Linting) # Create .git/hooks/pre_commit_check.py file with: #!/bin/bash python3 .git/hooks/pre_commit_check.py import subprocess def run_linter(): result = subprocess.run(['flake8', '.'], capture_output=True, text=True) if result.returncode != 0: print(\"❌ Lint errors detected. Commit aborted.\") print(result.stdout) exit(1) else: print(\"✅ Linter check passed.\") if __name__ == \"__main__\": run_linter() Make pre_commit_check.py executable: chmod +x .git/hooks/pre_commit_check.py Automated Pull, Merge, Push (One-Click Sync) # import subprocess def sync_with_main(): try: subprocess.run(['git', 'checkout', 'main'], check=True) subprocess.run(['git', 'pull', 'origin', 'main'], check=True) subprocess.run(['git', 'checkout', '-'], check=True) # Go back to the original branch subprocess.run(['git', 'merge', 'main'], check=True) subprocess.run(['git', 'push'], check=True) print(\"✅ Synced with main and pushed changes.\") except subprocess.CalledProcessError as e: print(f\"❌ Error during sync: {e}\") sync_with_main() Auto-Tagging and Version Bumping # Every time we merge to main, automatically bump version (semver) and create a new tag. import subprocess import re def get_latest_version(): result = subprocess.run(['git', 'tag'], capture_output=True, text=True) tags = result.stdout.split() tags = [t for t in tags if re.match(r'v\\d+\\.\\d+\\.\\d+', t)] return sorted(tags)[-1] if tags else 'v0.0.0' def bump_version(version): major, minor, patch = map(int, version.lstrip('v').split('.')) return f'v{major}.{minor}.{patch + 1}' def tag_new_version(): latest_version = get_latest_version() new_version = bump_version(latest_version) subprocess.run(['git', 'tag', new_version]) subprocess.run(['git', 'push', 'origin', new_version]) print(f\"✅ Created and pushed new tag: {new_version}\") tag_new_version() Read Env variables directly # import os API_EMAIL = os.getenv(\"JIRA_EMAIL\") API_TOKEN = os.getenv(\"JIRA_API_TOKEN\")","title":"Python Scripts","type":"notes"},{"content":" To MP3 Converter Free pandoc convert markdown to org set in c++ Enable/Disable Monitor 0x3f3f3f3f \u0026amp;\u0026amp; 0xcfcfcfcf vim Table Mode vim generate contents scp zip patch conda export dependencies from poetry to requirements.txt change poetry python version python special characters for ps1 in bashshell rsync crontab.guru pytest github remove file from staging area viewing info about the remote repository pushing changes merge a branch deleting a branch stash diffmerge add remove changes change commit message(changed commit history) add a file to the last commit(changed commit history) commited to the wrong branch undo some commit but other people have already pulled those changes itertools Sorting Lists, Tuples, and Objects Lists Objects global vs nonlocal Context Manager grep emacs vc emacs magit c++ STL c++ concept \u0026amp;\u0026amp; requires python utils # curl http://api.joind.in | python -mjson.tool To MP3 Converter Free # cat *.mp3 \u0026gt; final.mp3 # best brew install mp3wrap mp3wrap output.mp3 *.mp3 pandoc convert markdown to org # pandoc -f markdown -t org -o note_dynamic_programming.org note_dynamic_programming.md set in c++ # #include \u0026lt;vector\u0026gt; #include \u0026lt;numeric\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;cassert\u0026gt; #include \u0026lt;unordered_map\u0026gt; #include \u0026lt;unordered_set\u0026gt; #include \u0026lt;set\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;queue\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;set\u0026gt; #include \u0026lt;map\u0026gt; #define assertm(exp, msg) assert(((void)msg, exp)) #define print(input) for (auto\u0026amp; elem : input) std::cout \u0026lt;\u0026lt; elem \u0026lt;\u0026lt; std::endl int main() { auto cmp = [](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a, const std::pair\u0026lt;int, int\u0026gt;\u0026amp; b) {return a.second \u0026lt; b.second;}; std::set\u0026lt;std::pair\u0026lt;int, int\u0026gt;, decltype(cmp)\u0026gt; heap; heap.insert(std::make_pair(1, 3)); heap.insert(std::make_pair(31, 1)); heap.insert(std::make_pair(4, 4)); heap.insert(std::make_pair(2, 2)); heap.insert(std::make_pair(5, 5)); auto it = heap.begin(); it = std::next(it, 2); it = std::prev(it, 1); std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; int index = 31; auto it2 = std::find_if(heap.begin(), heap.end(), [index](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a) {return a.first == index;}); heap.erase(it2); it = heap.begin(); std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; return 0; } Enable/Disable Monitor # SwitchResX 0x3f3f3f3f \u0026amp;\u0026amp; 0xcfcfcfcf # If you are using C++ to write program, sometimes you need to set an large number. We can use INT_MAX of course, however, sometimes we may get overflow if we add the large number by 1.\nSome people they like use this number as INF which is 0x3f3f3f3f. For -INF, we can use 0xcfcfcfcf\nvim Table Mode # \\tm | || [|, ]|, {| \u0026amp; }| to move left | right | up | down cells i| or a| # insert a cell \\tdd # delete a row \\tdc # delete a coloumn \\tic # insert column vim generate contents # :GenTocGFM Generate table of contents in GFM link style. This command is suitable for Markdown files in GitHub repositories, like README.md, and Markdown files for GitBook. :GenTocRedcarpet Generate table of contents in Redcarpet link style. This command is suitable for Jekyll or anywhere else use Redcarpet as its Markdown parser. :GenTocGitLab Generate table of contents in GitLab link style. This command is suitable for GitLab repository and wiki. :GenTocMarked Generate table of contents for iamcco/markdown-preview.vim which use Marked markdown parser. scp # scp -ri /Users/yixianwang/.ssh/aws_ps.pem destination ec2-user@ec2-18-217-15-234.us-east-2.compute.amazonaws.com:~/ scp -ri /Users/yixianwang/.ssh/aws_ps.pem ec2-user@ec2-18-217-15-234.us-east-2.compute.amazonaws.com:~/ destination scp -ri /Users/yixianwang/.ssh/aws_skater.pem ubuntu@ec2-3-142-96-155.us-east-2.compute.amazonaws.com:~/project4 ~/Downloads/ zip # zip -r py_image_manipulation.zip py_image_manipulation patch # make clean make -f Makefile.test clean diff -ruN src src-finished \u0026gt; xv6.patch 1. Insert \u0026#34;xv6.patch\u0026#34; file in \u0026#34;src\u0026#34; folder 2. Under \u0026#34;src\u0026#34; folder, command \u0026#34;patch -i xv6.patch\u0026#34; 3. Make xv6 and run the tests conda # conda create --name myenv Python=3.8 --no-default-packages conda env list conda env remove -n myenv conda install numpy [matplotlib seaborn pandas] conda list # search all versions of pandas that available to install conda search pandas conda install pandas=0.25.2 conda update pandas # remove package conda remove numpy # install pip locally with conda, inside the virtual env conda install pip export dependencies from poetry to requirements.txt # python3 -m venv .venv source .venv/bin/activate poetry export --without-hashes \u0026gt; requirements.txt pip install -r requirements.txt change poetry python version # poetry env use /usr/local/bin/python3.9 python # python -m SimpleHTTPServer 8000 special characters for ps1 in bashshell # \\h the hostname up to the first . \\n newline \\s the name of the shell \\t the current time in 24-hour format \\u the username of the current user \\w the current working direcotry \\W the basename of the current working directory rsync # rsync -zaP --dry-run dir dir/ crontab.guru # crontab -l crontab -e crontab -r pytest # pytest --junitxml=result.xml poetry run pytest test_py_image_manipulation.py github # git config --global user.name \u0026#34;firstname lastname\u0026#34; git config --global user.email \u0026#34;email@email.com\u0026#34; git config --list git help \u0026lt;verb\u0026gt; git \u0026lt;verb\u0026gt; --help git diff remove file from staging area # git reset filename # remove one file git reset # remove everythin viewing info about the remote repository # git remote -v git branch -a pushing changes # git pull origin master git push origin master # origin: the name of remote repository. master: the branch we want to push to git branch branchname git checkout branchname git push -u origin branchname git branch -a merge a branch # git checkout master git pull origin master git branch --merged git merge branchname git push origin master deleting a branch # git branch --merged git branch -d branchname git branch -a git push origin --delete branchname stash # git stash save \u0026#34;Worked on some function\u0026#34; git stash list git stash apply/drop stash@{0} git stash pop git stash clear # be careful here git checkout -- . diffmerge # git config --global diff.tool diffmerge git config --global difftool.diffmerge.cmd \u0026#39;diffmerge \u0026#34;$LOCAL\u0026#34; \u0026#34;$REMOTE\u0026#34;\u0026#39; git config --global merge.tool diffmerge git config --global mergetool.diffmerge.cmd \u0026#39;diffmerge --merge --result=\u0026#34;$MERGED\u0026#34; \u0026#34;$LOCAL\u0026#34; \u0026#34;$(if test -f \u0026#34;$BASE\u0026#34;; then echo \u0026#34;$BASE\u0026#34;; else echo \u0026#34;$LOCAL\u0026#34;; fi)\u0026#34; \u0026#34;$REMOTE\u0026#34;\u0026#39; git config --global mergetool.diffmerge.trustExitCode true # git config --global mergetool.keepBackup false git diff # old git difftool git merge branchname git mergetool git commit add # Ignore the deleted files in git version 2\ngit add --no-all sub_dir/ Ignore the untracked files\ngit add -u/--update remove changes # Remove changes of a file\ngit checkout filename change commit message(changed commit history) # git commit --amend -m \u0026#34;new message here\u0026#34; add a file to the last commit(changed commit history) # git commit --amend :wq git log --stat commited to the wrong branch # move commit between branch cherry-pick creates a new commit based off our original(doesn\u0026rsquo;t delete)\ngit log # copy the hash git checkout branchname git cherry-pick #hash git checkout master remove the master commit\ngit reset soft: set back to the commit that we specified but it will keep our changes that we\u0026rsquo;ve made in the staging directory git reset --soft #the initial commit hash git reset mixed(default): keep the changes in the working directory instead of staging area git reset #the hash git reset hard: make all of our tracked files match the state that they were in at the hash we specified(leave the untracked file alone) git reset --hard #the initial commit hash remove the untracked directories and files\ngit clean -df recover from the hard reset\ngit reflog git checkout #hash before the reset git log # to check whether the commit exists git branch backup git branch # to see all branches undo some commit but other people have already pulled those changes # revert: creates a new commit to reverse the effect of some ealier commits(won\u0026rsquo;t rewrite history) it\u0026rsquo;s not going to modify or delete our existing commits\ngit log git revert #hash of the commit need to be covered git diff #src #desc\nitertools # # count list(zip(itertools.count(), data)) counter = itertools.count() counter = itertools.count(start=5, step=-2.5) print(next(counter)) # zip_longest vs zip data = [1, 2, 3, 4] result = list(zip(range(10), data)) result = list(itertools.zip_longest(range(10), data)) # cycle counter = itertools.cycle([1,2,3]) counter = itertools.cycle((\u0026#34;On\u0026#34;, \u0026#34;Off\u0026#34;)) print(next(counter)) # repeat counter = itertools.repeat(2) counter = itertools.repeat(2, times=3) print(next(counter)) # startmap vs map squares = map(pow, range(10), itertools.repeat(2)) # take iterables print(list(squares)) squares = map(pow, [(0, 2), (1, 2), (2, 2)]) # take paired tuples print(list(squares)) # combinations vs permutations itertools.combinations_with_replacement(list1, 2) itertools.combinations(list1, 2) itertools.permuations(list1, 2) itertools.product(list1, repeat=4) # chain itertools.chain(list1, list2, list3, ...) # isclice itertools.islice(range(10), 5) # return the first 5 elements of the iterable itertools.islice(range(10), 1, 5) # return the [1, 5) elements of the iterable itertools.islice(range(10), 1, 5, 2) # step 2 with open(\u0026#34;test.log\u0026#34;, \u0026#39;r\u0026#39;) as file: header = itertools.islice(file, 3) for line in header: print(line, end=\u0026#39;\u0026#39;) # compress vs filter itertools.compress(letters, selectors) filter(lt_2, numbers) itertools.filterfalse(lt_2, numbers) itertools.dropwhile(lt_2, numbers) itertools.takewhile(lt_2, numbers) # accumulate itertools.accumulate(numbers) # default is add operation import operator itertools.accumulate(numbers, operator.mul) # group by # note: people needs to be sorted beforehand people = [ { \u0026#39;name\u0026#39;: \u0026#39;John Doe\u0026#39;, \u0026#39;city\u0026#39;: \u0026#39;Gotham\u0026#39;, \u0026#39;state\u0026#39;: \u0026#39;NY\u0026#39; }, { \u0026#39;name\u0026#39;: \u0026#39;Jane Doe\u0026#39;, \u0026#39;city\u0026#39;: \u0026#39;Kings Landing\u0026#39;, \u0026#39;state\u0026#39;: \u0026#39;NY\u0026#39; }, ] def get_state(people): return people[\u0026#39;state\u0026#39;] people_group = itertools.groupby(people, get_state) for key, group in people_group: print(key) for person in group: print(person) print() # ? copy1, copy2 = itertools.tee(person_group) Sorting Lists, Tuples, and Objects # Lists # sort function is more flexible\nli = [9, 1, 8, 2, 7] s_li = sorted(li) s_tu = sorted(tu) s_di = sorted(di) s_li = sorted(li, reverse=True) print(\u0026#34;sorted function\u0026#34;, s_li) li.sort() li.sort(reverse=True) print(\u0026#34;sorted method\u0026#34;, li) sort on abs value\nli = [-6, -5, -4, 1, 2, 3] s_li = sorted(li, key=abs) print(s_li) Objects # class Employee(): def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary def __repr__(self): return f\u0026#34;({self.name}, {self.age}, {self.salary})\u0026#34; e1 = Employee(\u0026#34;Carl\u0026#34;, 37, 2000) e2 = Employee(\u0026#34;Sarah\u0026#34;, 23, 10000) e3 = Employee(\u0026#34;John\u0026#34;, 77, 300) employees = [e1, e2, e3] # customize key function def e_sort(emp): return emp.name s_employees = sorted(employees, key=e_sort, reverse=True) # lambda function s_employees = sorted(employees, key=lambda e: e.name) # attrgetter from operator import attrgetter s_employees = sorted(employees, key=attrgetter(\u0026#39;age\u0026#39;)) global vs nonlocal # LEGB\nLocal, Enclosing, Glboal, Built-in\nContext Manager # import os from contxtlib import contextmanager cwd = os.getcwd() os.chdir(\u0026#34;Sample-dir-one\u0026#34;) print(os.listdir()) os.chdir(cwd) cwd = os.getcwd() os.chdir(\u0026#34;Sample-dir-two\u0026#34;) print(os.listdir()) os.chdir(cwd) @contextmanager def change_dir(destination): try: cwd = os.getcwd() os.chdir(destination) yield finally: os.chdir(cwd) with change_dir(\u0026#34;Sample-dir-one\u0026#34;): print(os.listdir()) with change_dir(\u0026#34;Sample-dir-two\u0026#34;): print(os.listdir()) grep # grep \u0026#34;Yixian\u0026#34; name.txt grep -w \u0026#34;Yixian\u0026#34; name.txt grep -wi \u0026#34;Yixian\u0026#34; name.txt grep -win \u0026#34;Yixian\u0026#34; name.txt grep -win -B 4 \u0026#34;Yixian\u0026#34; name.txt grep -win -A 4 \u0026#34;Yixian\u0026#34; name.txt grep -win -C 2 \u0026#34;Yixian\u0026#34; name.txt grep -win \u0026#34;Yixian\u0026#34; ./*.txt grep -winr \u0026#34;Yixian\u0026#34; . grep -wirl \u0026#34;Yixian\u0026#34; . grep -wirc \u0026#34;Yixian\u0026#34; . history | grep \u0026#34;git commit\u0026#34; history | grep \u0026#34;git commit\u0026#34; | grep \u0026#34;dotfile\u0026#34; grep \u0026#34;...-...-....\u0026#34; phonenumber.txt # Mac egrep \u0026#34;\\d{3}-\\d{3}-\\d{4}\u0026#34; name.txt # Linux grep -P \u0026#34;\\d{3}-\\d{3}-\\d{4}\u0026#34; name.txt emacs vc # c-x v // show cv options c-x vv // next action c-x vL // log emacs magit # c-x m-g // show magit options c++ STL # // reverse a string std::reverse(s.begin(), s.end()); // to lower case std::transform( std::begin(s), std::end(s), std::begin(s), ::tolower ); // left part are all even numbers, right part are all odd numbers std::partition( nums.begin(), nums.end(), [](auto e) { return e % 2 == 0; } ); // move all 0\u0026#39;s to the end while maintaining the relative order of non-zero elements std::stable_partition( nums.begin(), nums.end(), [](auto e) { return e % 2 != 0; } ); // std::sort -\u0026gt; std::partial_sort -\u0026gt; std::nth_element // kClosest -- version 1 std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; kClosest(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; points, int K) { std::sort( points.begin(), points.end(), [](auto const\u0026amp; a, auto const\u0026amp; b) { return std::sqrt(a[0] * a[0] + a[1] * a[1]) \u0026lt; std::sqrt(b[0] * b[0] + b[1] * b[1]); // return a[0] * a[0] + a[1] * a[1] \u0026lt; b[0] * b[0] + b[1] * b[1]; // better performance } ); return std::vector(points.begin(), points.begin() + K); } // kClosest -- version 2 \u0026amp; 3 std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; kClosest(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; points, int K) { std::partial_sort( // std::nth_element( is the partial_sort give top K elements but not in sorted order points.begin(), points.begin() + K, points.end(), [](auto const\u0026amp; a, auto const\u0026amp; b) { return a[0] * a[0] + a[1] * a[1] \u0026lt; b[0] * b[0] + b[1] * b[1]; } ); return std::vector(points.begin(), points.begin() + K); } // squares of a sorted array std::transform( A.begin(), A.end(), A.begin(), [] (auto e) { return e * e; } ); std::sort( A.begin(), A.end() ); c++ concept \u0026amp;\u0026amp; requires # The add() on line 8 is consuming a named concept, Number, using the requires clause. It takes two numbers as a parameter, which should be either integer or floating_point, and returns the sum of both numbers.\nOn line 16, another function, add2(), is defined, which takes two numbers as parameters and returns the sum, but uses an unnamed concept through the requires clause.\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;concepts\u0026gt; template \u0026lt;typename T\u0026gt; concept Number = std::integral\u0026lt;T\u0026gt; || std::floating_point\u0026lt;T\u0026gt;; template \u0026lt;typename T, typename U\u0026gt; requires Number\u0026lt;T\u0026gt; \u0026amp;\u0026amp; Number\u0026lt;U\u0026gt; auto add(T a, U b) { return a+b; } template \u0026lt;typename T, typename U\u0026gt; requires std::integral\u0026lt;T\u0026gt; || std::floating_point\u0026lt;T\u0026gt; \u0026amp;\u0026amp;\\ std::integral\u0026lt;U\u0026gt; || std::floating_point\u0026lt;U\u0026gt; auto add2(T a, U b) { return a+b; } int main() { std::cout\u0026lt;\u0026lt;add(5,42.1f)\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; std::cout\u0026lt;\u0026lt;add2(42.1f,5)\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; return 0; } ","date":"24 October 2023","externalUrl":null,"permalink":"/notes/utils_notebook/","section":"Notes","summary":" To MP3 Converter Free pandoc convert markdown to org set in c++ Enable/Disable Monitor 0x3f3f3f3f \u0026\u0026 0xcfcfcfcf vim Table Mode vim generate contents scp zip patch conda export dependencies from poetry to requirements.txt change poetry python version python special characters for ps1 in bashshell rsync crontab.guru pytest github remove file from staging area viewing info about the remote repository pushing changes merge a branch deleting a branch stash diffmerge add remove changes change commit message(changed commit history) add a file to the last commit(changed commit history) commited to the wrong branch undo some commit but other people have already pulled those changes itertools Sorting Lists, Tuples, and Objects Lists Objects global vs nonlocal Context Manager grep emacs vc emacs magit c++ STL c++ concept \u0026\u0026 requires python utils # curl http://api.joind.in | python -mjson.tool To MP3 Converter Free # cat *.mp3 \u003e final.mp3 # best brew install mp3wrap mp3wrap output.mp3 *.mp3 pandoc convert markdown to org # pandoc -f markdown -t org -o note_dynamic_programming.org note_dynamic_programming.md set in c++ # #include \u003cvector\u003e #include \u003cnumeric\u003e #include \u003ciostream\u003e #include \u003ccassert\u003e #include \u003cunordered_map\u003e #include \u003cunordered_set\u003e #include \u003cset\u003e #include \u003ciostream\u003e #include \u003cqueue\u003e #include \u003cvector\u003e #include \u003cset\u003e #include \u003cmap\u003e #define assertm(exp, msg) assert(((void)msg, exp)) #define print(input) for (auto\u0026 elem : input) std::cout \u003c\u003c elem \u003c\u003c std::endl int main() { auto cmp = [](const std::pair\u003cint, int\u003e\u0026 a, const std::pair\u003cint, int\u003e\u0026 b) {return a.second \u003c b.second;}; std::set\u003cstd::pair\u003cint, int\u003e, decltype(cmp)\u003e heap; heap.insert(std::make_pair(1, 3)); heap.insert(std::make_pair(31, 1)); heap.insert(std::make_pair(4, 4)); heap.insert(std::make_pair(2, 2)); heap.insert(std::make_pair(5, 5)); auto it = heap.begin(); it = std::next(it, 2); it = std::prev(it, 1); std::cout \u003c\u003c it-\u003efirst \u003c\u003c \" \" \u003c\u003c it-\u003esecond \u003c\u003c std::endl; int index = 31; auto it2 = std::find_if(heap.begin(), heap.end(), [index](const std::pair\u003cint, int\u003e\u0026 a) {return a.first == index;}); heap.erase(it2); it = heap.begin(); std::cout \u003c\u003c it-\u003efirst \u003c\u003c \" \" \u003c\u003c it-\u003esecond \u003c\u003c std::endl; return 0; } Enable/Disable Monitor # SwitchResX 0x3f3f3f3f \u0026\u0026 0xcfcfcfcf # If you are using C++ to write program, sometimes you need to set an large number. We can use INT_MAX of course, however, sometimes we may get overflow if we add the large number by 1.\n","title":"Utils Notebook","type":"notes"},{"content":" format code in Google style # gg=G ???others # :s/old_name/new_name/gc\n","date":"24 October 2023","externalUrl":null,"permalink":"/notes/vim/","section":"Notes","summary":"format code in Google style # gg=G ???others # :s/old_name/new_name/gc\n","title":"Vim","type":"notes"},{"content":"","date":"23 October 2023","externalUrl":null,"permalink":"/notes/","section":"Notes","summary":"","title":"Notes","type":"notes"},{"content":"","date":"23 October 2023","externalUrl":null,"permalink":"/projects/","section":"Projects","summary":"","title":"Projects","type":"projects"},{"content":"Welcome to my personal site! I share my projects, blog posts, and technical notes here.\n","date":"23 October 2023","externalUrl":null,"permalink":"/","section":"About me","summary":"Welcome to my personal site! I share my projects, blog posts, and technical notes here.\n","title":"About me","type":"page"},{"content":"Coverd all topics of Monotonic Stack\n单调栈：求一个位置往左看或者往右看，第一个小于等于它的数的时候，用单调栈\n1 # Leetcode 1475 最终优惠价 解释 # 只要单调栈中的元素开始出栈，证明它们应该开始被计算结果。\u0026lt;==\u0026gt; 如果它们要被计算结果，我们要把他们出栈。 总结 # 单调栈在入栈之前，要判断单调性存不存在：如果不存在，我们要不断地弹出，直到单调性重新存在。 复杂度分析 # 由于每个下标都最多入栈、出栈各一次 总时间复杂度为O(n + n) = O(n) 空间复杂度为O(n) 单调栈的特点：时空复杂度O(n)、编程复杂度低，思维复杂度高 注意 # 单调栈的单调性，从一开始到最后，都应该保持一致。 单个元素，单调性存在。 如果发现我的算法需要将一个元素，反复入栈，反复出栈，就不是单调栈算法，不符合单调栈最基本的特点。 Template # for i from [0, (n - 1)] while 栈不空 and 单调性不存在 记录此时的答案 # 一般情况下是放在弹出栈之前的(弹前，弹中，弹后) stack.pop() stack.push(i) # 是下标，不是值 Answer # class Solution { public: std::vector\u0026lt;int\u0026gt; finalPrices(std::vector\u0026lt;int\u0026gt;\u0026amp; prices) { std::deque\u0026lt;int\u0026gt; stack; std::vector\u0026lt;int\u0026gt; results(prices.begin(), prices.end()); for (int i = 0; i \u0026lt; prices.size(); ++i) { while (!stack.empty() \u0026amp;\u0026amp; prices[stack.back()] \u0026gt;= prices[i]) { results[stack.back()] = prices[stack.back()] - prices[i]; stack.pop_back(); } stack.push_back(i); } return results; } }; class Solution: def finalPrices(self, prices: List[int]) -\u0026gt; List[int]: stack = [] results = list(prices) for i in range(len(prices)): while stack and prices[stack[-1]] \u0026gt;= prices[i]: results[stack[-1]] = prices[stack[-1]] - prices[i] stack.pop(-1) stack.append(i) return results Time Complexity # 与同向双指针类似，在双指针中，每个元素最多被每个指针扫过一次，所以每个元素最多被扫过两次，总共2n次 ==\u0026gt; O(N) # 同向双指针模版 end = 0 for start in range(len): # 不满足则循环到满足搭配为止 while end \u0026lt; len and (start 到 end 之间不满足条件): end += 1 if start 到 end 之间满足条件: 处理 start 到 end 这段区间(处理start, end这次搭配) 单调栈中，每一个下标最多入栈或出栈一次（即只入，或入+出），总共2n次 ==\u0026gt; O(N) 2 # Lintcode 285 高楼大厦 解法一：Brute Force # 枚举下标，分别向左向右，计算能看到多少个楼 需要记录当前看到的最高楼 highest 通过打擂台算法不断更新 highest, 每次更新就说明又看到了一个楼 记录更新的次数即可 Answer: Brute Force # class Solution { public: /** * @param arr: the height of all buildings * @return: how many buildings can he see at the location of each building */ std::vector\u0026lt;int\u0026gt; tallBuilding(std::vector\u0026lt;int\u0026gt;\u0026amp; arr) { // 一定能看到当前位置的楼 std::vector\u0026lt;int\u0026gt; results(arr.size(), 1); for (int i = 0; i \u0026lt; arr.size(); ++i) { // 向右看能看到多少楼 CountBuildings(arr, results, i, i + 1, arr.size(), 1); // 向左看能看到多少楼 CountBuildings(arr, results, i, i - 1, -1, -1); } return results; } private: void CountBuildings(std::vector\u0026lt;int\u0026gt;\u0026amp; arr, std::vector\u0026lt;int\u0026gt;\u0026amp; results, int index, int start, int end, int delta) { int highest = 0xcfcfcfcf; int can_be_seen = 0; for (int i = start; i != end; i += delta) { if (highest \u0026lt; arr[i]) { highest = arr[i]; ++can_be_seen; } } results[index] += can_be_seen; } }; def tall_building(self, arr: List[int]) -\u0026gt; List[int]: # 一定能看到当前位置的楼 results = [1] * len(arr) for i in range(len(arr)): # 向右看能看到多少楼 self.count_buildings(arr, results, i, range(i + 1, len(arr))) # 向左看能看到多少楼 self.count_buildings(arr, results, i, range(i - 1, -1, -1)) return results def count_buildings(self, arr, results, index, index_list): highest, can_be_seen = float(\u0026#34;-inf\u0026#34;), 0 for i in index_list: if highest \u0026lt; arr[i]: highest = arr[i] can_be_seen += 1 results[index] += can_be_seen 复杂度分析：Brute Force # 遍历每个下标 i: O(N) 每个下标 i 向左右查找整个数组: O(N) 总时间复杂度: O(N^2) 空间复杂度: O(N) 解法二：Monotonic Stack # 对于疑似单调栈的问题，一定要通过样例模拟 从头到尾，从尾到头进行两次单调栈 分别站在某个位置上向左，向右能看见多少楼房 入栈前的栈的大小就是能看见的房子数量 Answer: Monotonic Stack # class Solution { public: /** * @param arr: the height of all buildings * @return: how many buildings can he see at the location of each building */ std::vector\u0026lt;int\u0026gt; tallBuilding(std::vector\u0026lt;int\u0026gt;\u0026amp; arr) { // 一定能看到当前位置的楼 std::vector\u0026lt;int\u0026gt; results(arr.size(), 1); // 向右看能看到多少楼 CountBuildings(arr, results, 0, arr.size(), 1); // 向左看能看到多少楼 CountBuildings(arr, results, arr.size() - 1, -1, -1); return results; } private: void CountBuildings(std::vector\u0026lt;int\u0026gt;\u0026amp; arr, std::vector\u0026lt;int\u0026gt;\u0026amp; results, int start, int end, int delta) { std::deque\u0026lt;int\u0026gt; stack; for (int i = start; i != end; i += delta) { results[i] += stack.size(); // 在这里记录答案，与模版不同 while (!stack.empty() \u0026amp;\u0026amp; arr[stack.back()] \u0026lt;= arr[i]) { stack.pop_back(); } stack.push_back(i); } } }; def tall_building(self, arr: List[int]) -\u0026gt; List[int]: # 一定能看到当前位置的楼 results = [1] * len(arr) # 向右看能看到多少楼 self.count_buildings(arr, results, range(len(arr))) # 向左看能看到多少楼 self.count_buildings(arr, results, range(len(arr) - 1, -1, -1)) return results def count_buildings(self, arr, results, index_list): stack = [] for i in index_list: results[i] += len(stack) # 在这里记录答案，与模版不同 while stack and arr[stack[-1]] \u0026lt;= arr[i]: stack.pop(-1) stack.append(i) 3 # Lintcode 362 滑动窗口的最大值 Leetcode 239 Sliding Window Maximum 解法一：枚举所有窗口 # 本题仍然属于子数组问题 最直接的办法依然是枚举子数组 由于子数组长度已给出，所以不需要枚举终点 每个子数组打擂台算出最大值 Answer: 枚举所有窗口: Time Limit Exceeded # class Solution { public: std::vector\u0026lt;int\u0026gt; maxSlidingWindow(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int k) { std::vector\u0026lt;int\u0026gt; results; if (nums.size() == 0) { return results; } int n = nums.size(); for (int i = 0; i \u0026lt; n - k + 1; ++i) { int max_value = 0xcfcfcfcf; for (int j = i; j \u0026lt; i + k; ++j) { max_value = std::max(max_value, nums[j]); } results.push_back(max_value); } return results; } }; class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -\u0026gt; List[int]: if not nums: return [] results = [] n = len(nums) for i in range(0, n - k + 1): max_value = -float(\u0026#34;inf\u0026#34;) for j in range(i, i + k): max_value = max(max_value, nums[j]) results.append(max_value) return results 复杂度分析：枚举所有窗口 # 窗口的长度给定，只需要枚举起点:O(n) 打擂台计算窗口内的最大值:O(k) 总时间复杂度:O(n * k) 需要存放答案，空间复杂度:O(n) Follow Up 思路 # 待补充。。。\n","date":"16 September 2023","externalUrl":null,"permalink":"/blog/d_monotonic_stack/","section":"Blog","summary":"Coverd all topics of Monotonic Stack\n","title":"Draft: Monotonic Stack","type":"blog"},{"content":"Covered topics of system design\n4S analysis # Scenario 场景 说人话：需要设计哪些功能，设计得多牛 Ask/ Features/ QPS/ DAU/ Interfaces 具体场景有哪些 实际需求有什么 详细流程怎么样 Service 服务 说人话：将大系统拆分为小服务 Split/ Application/ Module 单体架构 X or 微服务 V Storage 存储 说人话：数据如何存储与访问 Schema/ Data/ SQL/ NoSQL/ File System 数据如何存储与访问 1.select 为每个Service 选择存储结构 2.Schema 细化表结构 Note: 分布式事务distributed transaction Scale 升级 说人话：解决缺陷，处理可能遇到的问题 Sharding/ Optimize/ Special Case 如何优化系统 加分项 Flash Sale \u0026amp; Booking System Design\n场景1：0点开始，限量100台，一人限购一台 场景2: 微信抢红包 ","date":"1 March 2023","externalUrl":null,"permalink":"/blog/d_note_system/","section":"Blog","summary":"Covered topics of system design\n","title":"Draft: System Design","type":"blog"},{"content":"Covered topics of Segment Tree and Binary Index Tree\nMaterials # segment_tree.pdf binary_index_tree.pdf Content # Chapter 1 线段树 Segment Tree Node: range-max Build: template (O(n)) Example: build range-max (O(n)) Modify: range-max (O(logn)) Query: range-max (O(logn)) Chapter 2 Lintcode 206 Interval Sum Lintcode 207 Interval Sum II Lintcode 248 Count of Smaller Number Lintcode 249 Count of Smaller Number before itself Exercise: Lintcode 201 Build Segment Tree Exercise: Lintcode 439 Build Segment Tree II Exercise: Lintcode 202 Query Segment Tree Exercise: Lintcode 247 Query Segment Tree II Exercise: Lintcode 203 Modify Segment Tree Chapter 3 树状数组 Binary Index Tree Lowbit 的两个含义 树状数组的程序实现: Lintcode 840: Range Sum In summary Chapter 4 Lintcode 206 Interval Sum Lintcode 207 Interval Sum II Lintcode 248 Count of Smaller Number Lintcode 249 Count of Smaller Number before itself Exercise: Lintcode 840 可变范围求和 Exercise: Lintcode 817 范围矩阵元素和-可变的 Exercise: Lintcode 665 平面范围求和 -不可变矩阵 Exercise: Lintcode 207 区间求和 II Exercise: Lintcode 206 区间求和 I Chapter 1 线段树 Segment Tree # 如果仅涉及区间上的查询，而不涉及修改，那么用前缀和即可。\n线段树的性质：\n除表示单点的一个节点是叶子节点外，其他每一个表示区间的节点都有两颗子树 每一个节点分出了左右节点的区间长度为父亲节点长度的一半（左边向上取整，右边向下取整） 每一个节点存储的值都是左右节点进行对应运算得出的。这个运算是根据要求而定的。如：求和的是和，求最大值的是max Node: range-max # struct SegmentTreeNode { SegmentTreeNode(int start, int end, int max) : start(start), end(end), max(max), left_child(nullptr), right_child(nullptr) {} int start; int end; int max; SegmentTreeNode* left_child; SegmentTreeNode* right_child; }; Build: template (O(n)) # SegmentTreeNode* build(int start, int end) { if (start \u0026gt; end) { return nullptr; } if (start == end) { return new SegmentTreeNode(start, end); } SegmentTreeNode* root = new SegmentTreeNode(start, end); if (start != end) { int mid = start + (end - start) / 2; root-\u0026gt;left_child = build(start, mid); root-\u0026gt;right_child = build(mid + 1, end); } return root; } Example: build range-max (O(n)) # SegmentTreeNode* build(int start, int end, std::vector\u0026lt;int\u0026gt;\u0026amp; A) { if (start \u0026gt; end) { return nullptr; } if (start == end) { return new SegmentTreeNode(start, end, A[start]); } SegmentTreeNode* node = new SegmentTreeNode(start, end, A[start]); if (start != end) { int mid = start + (end - start) / 2; node-\u0026gt;left_child = build(start, mid, A); node-\u0026gt;right_child = build(mid + 1, end, A); } if (node-\u0026gt;left_child != nullptr \u0026amp;\u0026amp; node-\u0026gt;left_child-\u0026gt;max \u0026gt; node-\u0026gt;max) { node-\u0026gt;max = node-\u0026gt;left_child-\u0026gt;max; } if (node-\u0026gt;right_child != nullptr \u0026amp;\u0026amp; node-\u0026gt;right_child-\u0026gt;max \u0026gt; node-\u0026gt;max) { node-\u0026gt;max = node-\u0026gt;right_child-\u0026gt;max; } return node; } Modify: range-max (O(logn)) # void modify(SegmentTreeNode* root, int index, int value) { // if (root-\u0026gt;start == root-\u0026gt;end) { // if (root-\u0026gt;start == root-\u0026gt;end \u0026amp;\u0026amp; root-\u0026gt;end == index) { if (root-\u0026gt;start == index \u0026amp;\u0026amp; root-\u0026gt;end == index) { root-\u0026gt;max = value; return; } int mid = root-\u0026gt;start + (root-\u0026gt;end - root-\u0026gt;start) / 2; if (root-\u0026gt;start \u0026lt;= index \u0026amp;\u0026amp; index \u0026lt;= mid) { modify(root-\u0026gt;left_child, index, value); } if (mid \u0026lt; index \u0026amp;\u0026amp; index \u0026lt;= root-\u0026gt;end) { modify(root-\u0026gt;right_child, index, value); } // non-leaf always has two children root-\u0026gt;max = std::max(root-\u0026gt;left_child-\u0026gt;max, root-\u0026gt;right_child-\u0026gt;max); } Query: range-max (O(logn)) # [start, end] 包含于 [node-\u0026gt;start, node-\u0026gt;end]\nint query(SegmentTreeNode* root, int left, int right) { if (left == root-\u0026gt;start \u0026amp;\u0026amp; right == root-\u0026gt;end) { return root-\u0026gt;max; } int mid = root-\u0026gt;start + (root-\u0026gt;end - root-\u0026gt;start) / 2; int left_max = 0xcfcfcfcf; // some default minimum integer int right_max = 0xcfcfcfcf; // some default minimum integer if (left \u0026lt;= mid) { if (mid \u0026lt; right) { left_max = query(root-\u0026gt;left_child, left, mid); } else { left_max = query(root-\u0026gt;left_child, left, right); } } if (mid \u0026lt; right) { if (left \u0026lt;= mid) { right_max = query(root-\u0026gt;right_child, mid + 1, right); } else { right_max = query(root-\u0026gt;right_child, left, right); } } return std::max(left_max, right_max); } Chapter 2 # Lintcode 206 Interval Sum # Lintcode 206 Interval Sum n为数组长度，m为查询次数\n暴力枚举求和O(nm) 树状数组/线段树查询区间和O(mlogn) 前缀和数组O(n + m) struct STNode { STNode(int start, int end) : start(start), end(end), sum(0), left_child(nullptr), right_child(nullptr) {} int start, end; long long sum; STNode* left_child; STNode* right_child; }; class SegmentTree { public: SegmentTree(std::vector\u0026lt;int\u0026gt;\u0026amp; A) : size_(A.size()), root_(BuildTree(0, size_ - 1, A)) {} long long QuerySum(int start, int end) { return QuerySum(root_, start, end); } private: STNode* BuildTree(int start, int end, std::vector\u0026lt;int\u0026gt;\u0026amp; A) { STNode* node = new STNode(start, end); if (start == end) { node-\u0026gt;sum = A[start]; return node; } int mid = start + (end - start) / 2; node-\u0026gt;left_child = BuildTree(start, mid, A); node-\u0026gt;right_child = BuildTree(mid + 1, end, A); node-\u0026gt;sum = node-\u0026gt;left_child-\u0026gt;sum + node-\u0026gt;right_child-\u0026gt;sum; return node; } // [start, end] 包含于 [node-\u0026gt;start, node-\u0026gt;end] // 在 node 节点下，查询原数组 [start, end] 区间和 long long QuerySum(STNode* node, int start, int end) { if (node-\u0026gt;start == start \u0026amp;\u0026amp; node-\u0026gt;end == end) { return node-\u0026gt;sum; } int mid = node-\u0026gt;start + (node-\u0026gt;end - node-\u0026gt;start) / 2; long long left_sum = 0; long long right_sum = 0; if (start \u0026lt;= mid) { left_sum = QuerySum(node-\u0026gt;left_child, start, std::min(end, mid)); } if (end \u0026gt;= mid + 1) { right_sum = QuerySum(node-\u0026gt;right_child, std::max(mid + 1, start), end); } return left_sum + right_sum; } int size_; // array size STNode* root_; }; /** * Definition of Interval: * class Interval { * public: * int start, end; * Interval(int start, int end) { * this-\u0026gt;start = start; * this-\u0026gt;end = end; * } * } */ class Solution { public: /** * @param a: An integer list * @param queries: An query list * @return: The result list */ std::vector\u0026lt;long long\u0026gt; intervalSum(std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;Interval\u0026gt;\u0026amp; queries) { std::vector\u0026lt;long long\u0026gt; result; SegmentTree* tree = new SegmentTree(A); for (Interval\u0026amp; i : queries) { result.push_back(tree-\u0026gt;QuerySum(i.start, i.end)); } return result; } }; Lintcode 207 Interval Sum II # Lintcode 207 Interval Sum II n为数组长度，m为操作次数\n暴力枚举求和O(nm) 树状数组/线段树查询区间和O(mlogn) struct STNode { STNode(int start, int end) : start(start), end(end), sum(0), left_child(nullptr), right_child(nullptr) {} int start, end; long long sum; STNode* left_child; STNode* right_child; }; class SegmentTree { public: SegmentTree(std::vector\u0026lt;int\u0026gt;\u0026amp; A) : size_(A.size()), root_(BuildTree(0, size_, A)) {} long long QueryTree(int start, int end) { return QueryTree(root_, start, end); } void ModifyTree(int index, int value) { return ModifyTree(root_, index, value); } private: STNode* BuildTree(int start, int end, std::vector\u0026lt;int\u0026gt;\u0026amp; A) { STNode* node = new STNode(start, end); if (start == end) { node-\u0026gt;sum = A[start]; return node; } int mid = start + (end - start) / 2; node-\u0026gt;left_child = BuildTree(start, mid, A); node-\u0026gt;right_child = BuildTree(mid + 1, end, A); node-\u0026gt;sum = node-\u0026gt;left_child-\u0026gt;sum + node-\u0026gt;right_child-\u0026gt;sum; return node; } // [start, end] 包含于 [node-\u0026gt;start, node-\u0026gt;end] long long QueryTree(STNode* node, int start, int end) { // WRONG!!! if (node-\u0026gt;start == node-\u0026gt;end) { if (start == node-\u0026gt;start \u0026amp;\u0026amp; end == node-\u0026gt;end) { return node-\u0026gt;sum; } long long left_sum = 0; long long right_sum = 0; int mid = node-\u0026gt;start + (node-\u0026gt;end - node-\u0026gt;start) / 2; if (start \u0026lt;= mid) { left_sum = QueryTree(node-\u0026gt;left_child, start, std::min(mid, end)); } if (end \u0026gt; mid) { right_sum = QueryTree(node-\u0026gt;right_child, std::max(start, mid + 1), end); } return left_sum + right_sum; } void ModifyTree(STNode* node, int index, int value) { // if (node-\u0026gt;start == node-\u0026gt;end) { if (node-\u0026gt;start == node-\u0026gt;end \u0026amp;\u0026amp; node-\u0026gt;end == index) { node-\u0026gt;sum = value; return; } if (node-\u0026gt;left_child-\u0026gt;end \u0026gt;= index) { ModifyTree(node-\u0026gt;left_child, index, value); } else { ModifyTree(node-\u0026gt;right_child, index, value); } node-\u0026gt;sum = node-\u0026gt;left_child-\u0026gt;sum + node-\u0026gt;right_child-\u0026gt;sum; } int size_; STNode* root_; }; class Solution { public: Solution(std::vector\u0026lt;int\u0026gt;\u0026amp; A) { if (A.size() == 0) { return; } tree = new SegmentTree(A); } long long query(int start, int end) { if (tree == nullptr) { return 0; } return tree-\u0026gt;QueryTree(start, end); } void modify(int index, int value) { if (tree == nullptr) { return; } tree-\u0026gt;ModifyTree(index, value); } SegmentTree* tree; }; Lintcode 248 Count of Smaller Number # Lintcode 248 Count of Smaller Number n为数组长度，m为查询次数，k为数组最大值\n暴力求解O(nm) 树状数组/线段树O(mlogk) 二分法O(nlogn + mlogn) 前缀和数组O(k + n + m) struct STNode { STNode(int start, int end) : start(start), end(end), sum(0), right(nullptr), left(nullptr) {} int start, end, sum; STNode* left; STNode* right; }; class STree { public: STree(int size) : size_(size), root_(BuildTree(0, size_ - 1)) {} int QueryTree(int start, int end) { return QueryTree(root_, start, end); } void ModifyTree(int index, int value) { return ModifyTree(root_, index, value); } private: STNode* BuildTree(int start, int end) { STNode* node = new STNode(start, end); if (start == end) { return node; } int mid = start + (end - start) / 2; node-\u0026gt;left = BuildTree(start, mid); node-\u0026gt;right = BuildTree(mid + 1, end); return node; } int QueryTree(STNode* node, int start, int end) { if (node-\u0026gt;start == start \u0026amp;\u0026amp; node-\u0026gt;end == end) { return node-\u0026gt;sum; } int mid = node-\u0026gt;start + (node-\u0026gt;end - node-\u0026gt;start) / 2; int left_sum = 0; int right_sum = 0; if (start \u0026lt;= mid) { left_sum = QueryTree(node-\u0026gt;left, start, std::min(mid, end)); } if (end \u0026gt; mid) { right_sum = QueryTree(node-\u0026gt;right, std::max(start, mid + 1), end); } return left_sum + right_sum; } void ModifyTree(STNode* node, int index, int value) { if (node-\u0026gt;start == node-\u0026gt;end \u0026amp;\u0026amp; node-\u0026gt;start == index) { node-\u0026gt;sum = value; return; } int mid = node-\u0026gt;start + (node-\u0026gt;end - node-\u0026gt;start) / 2; if (index \u0026lt;= mid) { ModifyTree(node-\u0026gt;left, index, value); } else { ModifyTree(node-\u0026gt;right, index, value); } node-\u0026gt;sum = node-\u0026gt;left-\u0026gt;sum + node-\u0026gt;right-\u0026gt;sum; } int size_; STNode* root_; }; class Solution { public: std::vector\u0026lt;int\u0026gt; countOfSmallerNumber(std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;int\u0026gt;\u0026amp; queries) { std::vector\u0026lt;int\u0026gt; B(10001); for (int\u0026amp; a : A) { ++B[a]; } STree* tree = new STree(10001); for (int i = 0; i \u0026lt;= 10000; ++i) { tree-\u0026gt;ModifyTree(i, B[i]); } std::vector\u0026lt;int\u0026gt; result; for (int\u0026amp; q : queries) { if (q == 0) { result.push_back(0); } else { result.push_back(tree-\u0026gt;QueryTree(0, q - 1)); } } return result; } }; Lintcode 249 Count of Smaller Number before itself # Lintcode 249 Count of Smaller Number before itself n为数组长度，k为数组最大值\n暴力求解O(n^2) 树状数组/线段树O(nlogk) 在单点修改的情况下，维护前缀和：使用线段树维护B数组\nstruct STNode { STNode(int start, int end) : start(start), end(end), sum(0), left(nullptr), right(nullptr) {} int start, end; int sum; STNode* left; STNode* right; }; class STree { public: STree(int size) : size_(size), root_(BuildTree(0, size_ - 1)) {} int QueryTree(int start, int end) { return QueryTree(root_, start, end); } void ModifyTree(int index, int value) { return ModifyTree(root_, index, value); } private: STNode* BuildTree(int start, int end) { STNode* node = new STNode(start, end); if (start == end) { return node; } int mid = start + (end - start) / 2; node-\u0026gt;left = BuildTree(start, mid); node-\u0026gt;right = BuildTree(mid + 1, end); return node; } int QueryTree(STNode* node, int start, int end) { if (node-\u0026gt;start == start \u0026amp;\u0026amp; node-\u0026gt;end == end) { return node-\u0026gt;sum; } int left_sum = 0; int right_sum = 0; int mid = node-\u0026gt;start + (node-\u0026gt;end - node-\u0026gt;start) / 2; if (start \u0026lt;= mid) { left_sum = QueryTree(node-\u0026gt;left, start, std::min(mid, end)); } if (end \u0026gt; mid) { right_sum = QueryTree(node-\u0026gt;right, std::max(start, mid + 1), end); } return left_sum + right_sum; } void ModifyTree(STNode* node, int index, int value) { if (node-\u0026gt;start == node-\u0026gt;end \u0026amp;\u0026amp; node-\u0026gt;start == index) { node-\u0026gt;sum = value; return; } if (node-\u0026gt;left-\u0026gt;end \u0026gt;= index) { ModifyTree(node-\u0026gt;left, index, value); } else { ModifyTree(node-\u0026gt;right, index, value); } node-\u0026gt;sum = node-\u0026gt;left-\u0026gt;sum + node-\u0026gt;right-\u0026gt;sum; } int size_; STNode* root_; }; class Solution { public: std::vector\u0026lt;int\u0026gt; countOfSmallerNumberII(std::vector\u0026lt;int\u0026gt;\u0026amp; A) { STree* tree = new STree(10001); std::vector\u0026lt;int\u0026gt; B(10001); std::vector\u0026lt;int\u0026gt; result; for (int\u0026amp; i : A) { if (i == 0) { result.push_back(0); } else { result.push_back(tree-\u0026gt;QueryTree(0, i - 1)); } ++B[i]; tree-\u0026gt;ModifyTree(i, B[i]); } return result; } }; Exercise: Lintcode 201 Build Segment Tree # Lintcode 201 Build Segment Tree /** * Definition of SegmentTreeNode: * class SegmentTreeNode { * public: * int start, end; * SegmentTreeNode *left, *right; * SegmentTreeNode(int start, int end) { * this-\u0026gt;start = start, this-\u0026gt;end = end; * this-\u0026gt;left = this-\u0026gt;right = NULL; * } * } */ class Solution { public: SegmentTreeNode* build(int start, int end) { } }; Exercise: Lintcode 439 Build Segment Tree II # Lintcode 439 Build Segment Tree II Exercise: Lintcode 202 Query Segment Tree # Lintcode 202 Query Segment Tree Exercise: Lintcode 247 Query Segment Tree II # Lintcode 247 Query Segment Tree II Exercise: Lintcode 203 Modify Segment Tree # Lintcode 203 Modify Segment Tree Chapter 3 树状数组 Binary Index Tree # 用于维护前缀信息的结构，对前缀信息的处理也是非常高效的\n给定一个整数数组nums，然后你需要实现两个函数: Update(i, val)将数组下标为i的元素修改为val SumRange(l, r)返回数组下标在[l, r]区间的元素的和 暴力求解: Update时间复杂度O(1)、SumRange时间复杂度O(n)\n树状数组求解: Update时间复杂度O(logn)、SumRange时间复杂度O(logn)、对于长度为n的数组，构建树状数组时间复杂度O(nlogn)\nBinary Index Tree 是通过前缀和思想，用来完成 单点更新 和 区间查询 的数据结构。 Binary Index Tree advantages compared to Segment Tree: 所用空间更小（空间复杂度都是O(n), 但是Binary Index Tree只开了一个大小为n的数组，Segment Tree有左右指针, 区间端点等等），速度更快。 注意: 树状数组的下标从 1 开始计数。 定义: 数组 C 是一个对原始数组 A 的预处理数组。 C[i]的元素个数（来自于A）：取决于i的二进制末尾有几个连续的0.\ne.g. i有k个0，那么C[i]共有2^k个A中的元素.\n根据lowbit函数，可以知道C[i]代表几个A中的元素相加，以及i的父亲在哪儿(i + lowbit(i), e.g. 6 + lowbit(6) = 8)\nLowbit 的两个含义 # e.g. Lowbit(4) = 4:\n从A[4]出发向左共四个数的SUM 从C[4]搭出去梯子的长度（长度为4: A[5], A[6], A[7], A[8]），C[4]的值会影响到C[8] 树状数组的程序实现: Lintcode 840: Range Sum # Lintcode 840 Range Sum /** * Your NumArray object will be instantiated and called as such: * NumArray obj = new NumArray(nums); * obj.update(i,val); * int param_2 = obj.sumRange(i,j); */ class NumArray { public: NumArray(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { // both of them have to be assigned to 0 arr_.assign(nums.size(), 0); bit_.assign(nums.size() + 1, 0); for (int i = 0; i \u0026lt; nums.size(); ++i) { update(i, nums[i]); } } void update(int index, int value) { int delta = value - arr_[index]; arr_[index] = value; // Lowbit(i) 此时是Lowbit的第二个含义，即搭出去的梯子 for (int i = index + 1; i \u0026lt;= arr_.size(); i = i + Lowbit(i)) { // bit_[i] 即C数组，包含了Lowbit的第二个含义（bit_[i]记录了Lowbit(i)个值的Sum） bit_[i] += delta; } } int sumRange(int left, int right) { return GetPrefixSum(right) - GetPrefixSum(left - 1); } private: int GetPrefixSum(int index) { int sum = 0; // Lowbit(i) 此时是Lowbit的第二个含义，即搭出去的梯子 for (int i = index + 1; i \u0026gt; 0; i = i - Lowbit(i)) { sum += bit_[i]; } return sum; } inline int Lowbit(int x) { return x \u0026amp; (-x); } std::vector\u0026lt;int\u0026gt; arr_, bit_; }; In summary # 若求区间(i, j)的区间和rangeSum(i, j) 使用前缀和时，rangeSum(i, j) = sum(j) - sum(i)，时间复杂度为O(1)。 使用线段树时，需要从根向下搜索，找到所有包含且仅包含(i, j)中元素的区间和，所有的深度最大为树的高度，时间复杂度为O(log n)。 使用树状数组，根据公式sum(i) = sum(i - lowbit(i)) + C[i]，使用树状数组求前缀和的时间复杂度为O(log n)。区间和rangeSum(i, j) = sum(j) - sum(i)，求区间和的操作可以转换为求两次前缀和，因此时间复杂度也是O(log n)。 Chapter 4 # Lintcode 206 Interval Sum # Lintcode 206 Interval Sum n为数组长度，m为查询次数\n暴力枚举求和O(nm) 树状数组/线段树查询区间和O(mlogn) 前缀和数组O(n + m) class BinaryIndexTree { public: BinaryIndexTree(std::vector\u0026lt;int\u0026gt;\u0026amp; A) { size_ = A.size(); a_ = std::vector\u0026lt;long long\u0026gt;(size_ + 1); // a_.assign(size_ + 1, 0); for (int i = 0; i \u0026lt; size_; ++i) { Add(i, A[i]); } } // A[index] += val void Add(int index, int val) { ++index; while (index \u0026lt;= size_) { a_[index] += val; index += Lowbit(index); } } // A[0] + ... + A[index] long long PrefixSum(int index) { ++index; long long ans = 0; while (index \u0026gt; 0) { ans += a_[index]; index -= Lowbit(index); } return ans; } private: inline int Lowbit(int x) { return x \u0026amp; (-x); } std::vector\u0026lt;long long\u0026gt; a_; int size_; }; class Solution { public: std::vector\u0026lt;long long\u0026gt; intervalSum(std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;Interval\u0026gt;\u0026amp; queries) { std::vector\u0026lt;long long\u0026gt; ans; // BinaryIndexTree tree(A); BinaryIndexTree* tree = new BinaryIndexTree(A); // for (Interval\u0026amp; i : queries) { // if (i.start == 0) { // ans.push_back(tree.PrefixSum(i.end)); // } else { // ans.push_back(tree.PrefixSum(i.end) - // tree.PrefixSum(i.start - 1)); // } // } for (Interval\u0026amp; i : queries) { if (i.start == 0) { ans.push_back(tree-\u0026gt;PrefixSum(i.end)); } else { ans.push_back(tree-\u0026gt;PrefixSum(i.end) - tree-\u0026gt;PrefixSum(i.start - 1)); } } return ans; } }; Lintcode 207 Interval Sum II # Lintcode 207 Interval Sum II n为数组长度，m为操作次数\n暴力枚举求和O(nm) 树状数组/线段树查询区间和O(mlogn) class BinaryIndexTree { public: BinaryIndexTree(std::vector\u0026lt;int\u0026gt;\u0026amp; A) { size_ = A.size(); a_ = std::vector\u0026lt;long long\u0026gt;(size_ + 1); // a_.assign(size_ + 1, 0); for (int i = 0; i \u0026lt; size_; ++i) { Add(i, A[i]); } } // A[index] += val void Add(int index, int val) { ++index; while (index \u0026lt;= size_) { a_[index] += val; index += Lowbit(index); } } // A[0] + ... + A[index] long long PrefixSum(int index) { ++index; long long ans = 0; while (index \u0026gt; 0) { ans += a_[index]; index -= Lowbit(index); } return ans; } private: inline int Lowbit(int x) { return x \u0026amp; (-x); } std::vector\u0026lt;long long\u0026gt; a_; int size_; }; class Solution { public: Solution(std::vector\u0026lt;int\u0026gt;\u0026amp; A) { A_ = A; tree_ = new BinaryIndexTree(A); } long long query(int start, int end) { if (start == 0) { return tree_-\u0026gt;PrefixSum(end); } else { return tree_-\u0026gt;PrefixSum(end) - tree_-\u0026gt;PrefixSum(start - 1); } } void modify(int index, int value) { tree_-\u0026gt;Add(index, value - A_[index]); A_[index] = value; } private: std::vector\u0026lt;int\u0026gt; A_; // 为了计算difference BinaryIndexTree* tree_; }; Lintcode 248 Count of Smaller Number # Lintcode 248 Count of Smaller Number n为数组长度，m为查询次数，k为数组最大值\n暴力求解O(nm) 树状数组/线段树O(mlogk) 二分法O(nlogn + mlogn) 前缀和数组O(k + n + m) class BinaryIndexTree { public: BinaryIndexTree(int size) { size_ = size; a_.assign(size_ + 1, 0); } // B[index] += val void Add(int index, int val) { ++index; while (index \u0026lt;= size_) { a_[index] += val; index += Lowbit(index); } } // B[0] + ... + B[index] int PrefixSum(int index) { ++index; int ret = 0; while (index \u0026gt; 0) { ret += a_[index]; index -= Lowbit(index); } return ret; } private: int Lowbit(int x) { return x \u0026amp; (-x); } int size_; std::vector\u0026lt;int\u0026gt; a_; }; class Solution { public: std::vector\u0026lt;int\u0026gt; countOfSmallerNumber(std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;int\u0026gt;\u0026amp; queries) { int max_a = -1; for (int\u0026amp; i : A) { max_a = std::max(max_a, i); } BinaryIndexTree* tree = new BinaryIndexTree(max_a + 1); // B[A[i]]++ for (int\u0026amp; i : A) { tree-\u0026gt;Add(i, 1); } std::vector\u0026lt;int\u0026gt; ans; for (int\u0026amp; i : queries) { if (i \u0026gt; max_a) { ans.push_back(A.size()); } else if (i \u0026lt; 0) { ans.push_back(0); } else { ans.push_back(tree-\u0026gt;PrefixSum(i - 1)); } } return ans; } }; Lintcode 249 Count of Smaller Number before itself # Lintcode 249 Count of Smaller Number before itself n为数组长度，k为数组最大值\n暴力求解O(n^2) 树状数组/线段树O(nlogk) class BinaryIndexTree { public: BinaryIndexTree(int size) { size_ = size; a_.assign(size_ + 1, 0); } // B[index] += val void Add(int index, int val) { ++index; while (index \u0026lt;= size_) { a_[index] += val; index += Lowbit(index); } } // B[0] + ... + B[index] int PrefixSum(int index) { ++index; int ret = 0; while (index \u0026gt; 0) { ret += a_[index]; index -= Lowbit(index); } return ret; } private: int Lowbit(int x) { return x \u0026amp; (-x); } int size_; std::vector\u0026lt;int\u0026gt; a_; }; class Solution { public: std::vector\u0026lt;int\u0026gt; countOfSmallerNumberII(std::vector\u0026lt;int\u0026gt;\u0026amp; A) { std::vector\u0026lt;int\u0026gt; ans; int max_a = -1; for (int\u0026amp; i : A) { max_a = std::max(max_a, i); } BinaryIndexTree* tree = new BinaryIndexTree(max_a + 1); for (int\u0026amp; i : A) { if (i == 0) { ans.push_back(0); } else { ans.push_back(tree-\u0026gt;PrefixSum(i - 1)); } tree-\u0026gt;Add(i, 1); // B[i] += 1 } return ans; } }; Exercise: Lintcode 840 可变范围求和 # Lintcode 840 可变范围求和 Exercise: Lintcode 817 范围矩阵元素和-可变的 # Lintcode 817 范围矩阵元素和-可变的 Exercise: Lintcode 665 平面范围求和 -不可变矩阵 # Lintcode 665 平面范围求和 -不可变矩阵 Exercise: Lintcode 207 区间求和 II # Lintcode 207 区间求和 II Exercise: Lintcode 206 区间求和 I # Lintcode 206 区间求和 I ","date":"1 March 2023","externalUrl":null,"permalink":"/blog/segment_tree/","section":"Blog","summary":"Covered topics of Segment Tree and Binary Index Tree\n","title":"Segment Tree \u0026 Binary Index Tree","type":"blog"},{"content":"Covered all topics of Dynamic Programming\nChapter 1: DP 入门 DP题目特点 最值型动态规划 \u0026amp;\u0026amp; DP组成部分一：确定状态 递归写法的不可行性 递归写法 递归写法的问题 DP组成部分二：转移方程（到此对了一半，比确定状态简单一些） DP组成部分三：初始条件和边界情况 DP组成部分四：计算顺序 Time Complexity Coding 669 计数型动态规划 DP组成部分一：确定状态 DP组成部分二：转移方程 DP组成部分三：初始条件和边界情况 DP组成部分四：计算顺序 Coding 存在型动态规划 DP组成部分一：确定状态 DP组成部分二：转移方程 DP组成部分三：初始条件和边界情况 DP组成部分四：计算顺序 Coding In summary Exercise1: 金字塔 Exercise2: 乘积最大子序列 Chapter 2: 动态规划初探 + 坐标型动态规划 + 位操作型动态规划 初探 坐标型动态规划 题目分析 初始条件和边界情况 Coding 初探 序列型动态规划 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Coding In summary: seq-type 初探 划分型动态规划 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Coding 坐标型动态规划：最小路径和 坐标型动态规划：最小路径和\u0026ndash;路径打印 坐标型动态规划：最小路径和\u0026ndash;空间优化 坐标型动态规划：炸弹袭击 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 四个方向 Coding 坐标型动态规划 总结 位操作型动态规划：Counting Bits 题目分析 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Coding Exercise: 最长上升子序列 Chapter 3: 打劫房屋: 坐标型，前缀型 坐标型 动态规划 代码思路 前缀型 Chapter 4: 最大矩形 \u0026amp;\u0026amp; 最大直方图：坐标型 最大矩形 直方图最大矩形覆盖 Chapter 5: 最短假期：坐标型 Exercise: 相关题目 Chapter 6: 最小调整代价：背包型 状态 转移方程 思路 1.临界值： 2.状态转移方程： Coding Chapter 7: 香槟塔：坐标型 Step 1 : 如何定义状态？ Step 2 : 临界值是什么？ Step 3 : 状态转移方程怎么写？ Step 4 : DP结果是什么？ 空间优化： Soluton 1: row % 2 Soluton 2: 一维数组 Chapter 8: 飞行棋I Step 1 : 如何定义状态？ Step 2 : 临界值是什么？ Step 3 : 状态转移方程怎么写？ Step 4 : DP结果是什么？ DP Solution Chapter 9: 序列型动态规划 序列型动态规划\u0026ndash;简介 序列型动态规划\u0026ndash;数字翻转 动态规划组成部分一：确定状态 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Coding Mine Correct Solution Official Solution 序列型动态规划的时间优化\u0026ndash;房屋染色II Mine Correct Answer O(nk^2) 时间优化 Mine correct time optimized solution Official time optimized solution 序列型动态规划\u0026ndash;买卖股票1 动态规划解法 Mine correct solution Official solution: Better 序列型动态规划\u0026ndash;买卖股票2 题目分析 Official solution 序列型动态规划\u0026ndash;买卖股票3: 序列型 题目分析 动态规划组成部分一：确定状态 记录阶段 动态规划组成部分一：确定状态 continued 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Official Solution 序列型动态规划\u0026ndash;买卖股票4 题目分析 记录阶段 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Official Solution Official Solution : rolling array optimization 序列型动态规划\u0026ndash;小结 初探 最长上升子序列(LIS) 最长序列型动态规划 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 思考：如何做到时间复杂度O(nlogn) Official Solution: O(n^2) phone interview Exercise 602 俄罗斯套娃信封 Mine first solution: Time Limit Exceeded: dp Mine second solution: Time Limit Exceeded: dfs Correct forum official solution 课后习题 Chapter 10: 骰子求和：背包型 背包型 Chapter 11: 最长有效括号：后缀型(与前缀型只区别与计算顺序) Chapter 12: 最大子数组差 Mine solution Official Solution: with some Greedy idea 相关题目 Chapter 13: 工作安排：坐标型 Chapter 14: 染色问题：坐标型 Chapter 15: 最小的窗口子序列：匹配型 The first solution with O(n^2 * (n + m)) approximate to O(n^3) The second solution with O(n * (n + m)) approximate to O(n^2), we should let time less than 10^8, if n is 20000, then it becomes 4 * 10^8 \u0026gt; 10^8, which is not good The thrid solution: Dynamic Programming: Time O(n * m) Space O(n * m) Relative Problems Chapter 16: 划分型、博弈型 和 背包型 动态规划 划分型动态规划 Example: Lintcode 513 Perfect Square：划分型 动态规划组成部分一：确定状态 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Coding Solution Follow up Example: Lintcode 108 Palindrome Partitioning II 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 回文串判断 回文串种类 生成回文串 在字符串中找到所有回文串 记录回文串 回到原题 Example: Lintcode 437 Copy Books 题目分析 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Coding: The First Solution with DP Coding: The Second Solution with Binary Search Summary 博弈型动态规划 Example: Lintcode 394 Coins in a Line 动态规划组成部分一：确定状态 博弈型动态规划：必胜 vs 必败 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Official Solution The Second Solution with Time O(1) Space O(1) 背包型动态规划 直觉 Example: Lintcode 92 Backpack 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 DP Official Solution Backpack Official Solution Summary Example: Lintcode 563 Backpack V 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Official Solution 进一步空间优化 My Correct Solution Exercise: Lintcode 564 BackPack IV (组合总和 IV) 题目分析 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Offical Solution Summary Exercise: Single Choice Chapter 17: 背包型 和 区间型 动态规划 01 backpack 打印路径 complete backpack multiple backpack 区间型动态规划 Example: Lintcode 667 Longest Palindrome Subsequence 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Official Solution 记忆化搜索方法 与递推方法比较 Coding with Template (important) Example: Lintcode 396 Coins in A Line III （区间型动态规划—博弈问题） 博弈 动态规划组成部分一：确定状态 博弈子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Example: Lintcode 430 Scramble String 动态规划组成部分一：确定状态 子问题 动态规划组成部分一：确定状态 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 记忆化搜索 Example: Lintcode 168 吹气球 (消去型 \u0026ndash;\u0026gt; 区间型) 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Summary Chapter 18: 石头碰撞：背包型 Chapter 19: 合并金币：区间型 Chapter 20: 外卖满减：01背包 Exercise Lintcode 92 backpack Exercise Lintcode 125 backpack II Exercise Lintcode 563 backpack V Chapter 21: 考试策略：0/0.5/1背包 Exercise Lintcode 1538 卡牌游戏 II Exercise Lintcode 700 杆子分割 Chapter 22: 双序列动态规划 Example: Lintcode 77 最长公共子序列 题目分析 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 打印最长公共子序列 Example: Lintcode 29 交叉字符串 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 滚动数组优化 Example: Lintcode 119 编辑距离 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 滚动数组优化 编辑距离的实际用途 Example: Lintcode 154 Regular Expression Matching 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Example: Lintcode 192 Wildcard Matching 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Example: Lintcode 668 Ones and Zeroes：双背包 动态规划组成部分一：确定状态 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Example: Lintcode 118 Distinct Subsequences Chapter 23: 毕业旅行 Solution 1: 排列型DFS 分析 AC Optimize: Pruning Solution 2: 状态压缩型动态规划??? Chapter 24: 双色塔 Chapter 25: 编辑距离 Example: Lintcode 119 编辑距离 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 滚动数组优化 编辑距离的实际用途 Example: Lintcode 154 Regular Expression Matching 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Example: Lintcode 192 Wildcard Matching 动态规划组成部分一：确定状态 子问题 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Example: Lintcode 668 Ones and Zeroes：双背包 动态规划组成部分一：确定状态 动态规划组成部分二：转移方程 动态规划组成部分三：初始条件和边界情况 动态规划组成部分四：计算顺序 Example: Lintcode 118 Distinct Subsequences Chapter 23: 毕业旅行 Solution 1: 排列型DFS 分析 AC Optimize: Pruning Solution 2: 状态压缩型动态规划??? Chapter 24: 双色塔 Chapter 25: 编辑距离 (Really Important) The most relevant problem Relevant Problems Chapter 26: 动态规划难题专场 Lintcode 752 Rogue Knight Sven Others Note 动态规划的题型 简历: 最好一页 How to use heap in c++ Others Note 动态规划的题型 简历: 最好一页 How to use heap in c++ Chapter 1: DP 入门 # How to realize a DP problem # First, if the question is asking for the How many ways Maximum or Minimum Yes/No Second, we have to make decisions that may depend on previously made decisions 常见DP类型： # 坐标型（20%）\n序列型（20%）\n划分型（20%）\n区间型（15%）\n背包型（10%）\n最长序列型（5%）\n博弈型（5%）\n综合型（5%）\nDP时间空间优化\nFollowUp 常考：滚动数组 或者 降维 DP打印路径\nDP题目特点 # 计数(求方案数) 有多少种方式走到右下角 有多少种方法选出k个数使得和是sum How many ways (can you / does it)? 求最大最小值(求最值) 从左上角走到右下角路径的最大数字和 最长上升子序列长度 Maximum/ Minimum/ Longest/ Shortest/ Minimum Cost \u0026hellip; 存在性(判断可行性) 取石子游戏，先手是否必胜 能不能选出k个数使得和是sum Yes/No, True/False, 0/1 DP都有方向性：数组顺序固定或不可变(方向性: 从头到尾或者从尾到头)\n状态：把你觉得会影响结果的信息，全部放到数组的下标中去: 觉得有两个信息影响结果，就弄成二维数组；觉得有三个信息影响结果，就弄成三位数组\n状态转移：永远考虑最后一次干了什么事情，最后一步从哪儿来，最后一次做了什么操作\n初始条件：其实就是 用转移方程算不出来，但又需要它的定义，此时需要手工定义 初始条件和边界情况 (人话)：最小的值搞定一下和不要数组越界\n计算顺序：的确定只有一个原则，当你要算f[X]等式左边的时候，右边用到的状态都已经算过了\n最值型动态规划 \u0026amp;\u0026amp; DP组成部分一：确定状态 # Lintcode 669: Coin Change DP组成部分一：确定状态 状态在DP中的作用属于定海神针 简单的说，解DP的时候需要开一个数组，数组的每个元素f[i]或者f[i][j]代表什么 类似于解数学题中 X, Y, Z 代表什么 确定状态需要两个意识： 最后一步：最优策略中的最后一个决策 当前问题： 虽然我们不知道最优策略是什么，但是最优策略肯定是K枚硬币a1, a2, ..., ak面值加起来是27 所以一定有一枚最后的硬币：ak 除掉这枚硬币，前面硬币的面值加起来是27 - ak Key 1: 我们不关心前面的K - 1枚硬币是怎么拼出27 - ak的（可能有1种拼法，可能有100种拼法），而且我们现在甚至还不知道ak和K，但是我们确定前面的硬币拼出了27 - ak Key 2: 因为是最优策略，所以拼出27 - ak的硬币数一定要最少，否则这就不是最优策略了 子问题 所以我们就要求：最少用多少枚硬币拼出27 - ak 愿问题是 最少用多少枚硬币拼出27 我们将原问题转化成了一个子问题，而且规模更小：27 - ak 为了简化定义，我们设状态f(X) = 最少用多少枚硬币拼出X 递归写法的不可行性 # 递归写法 # int f(int X) { // f(X)=最少用多少枚硬币拼出X if (X == 0) { // 0元钱只要0枚硬币 return 0; } int result = 0x3f3f3f3f; // INT_MAX; // 初始化用无穷大 if (X \u0026gt;= 2) { // 最后一枚硬币是2元 result = std::max(f(X - 2) + 1, result); } if (X \u0026gt;= 5) { // 最后一枚硬币是5元 result = std::max(f(X - 5) + 1, result); } if (X \u0026gt;= 7) { // 最后一枚硬币是7元 result = std::max(f(X - 7) + 1, result); } return result; } 递归写法的问题 # 做了很多重复计算，效率低下 如何避免？ DP：将计算结果保存下来，并改变计算顺序 DP组成部分二：转移方程（到此对了一半，比确定状态简单一些） # DP组成部分二：转移方程 设状态f[X]=最少用多少枚硬币拼出X 对于任意X, $f[X] = min{f[X - 2] + 1, f[X - 5] + 1, f[X - 7] + 1}$ f[X] f[X - 2] + 1 f[X - 5] + 1 f[X - 7] + 1 拼出X所需最少的硬币数 拼出X-2所需最少的硬币数，加上最后一枚硬币2 拼出X-5所需最少的硬币数，加上最后一枚硬币5 拼出X-7所需最少的硬币数，加上最后一枚硬币7 DP组成部分三：初始条件和边界情况 # DP组成部分三：初始条件和边界情况 $f[X] = min{f[X - 2] + 1, f[X - 5] + 1, f[X - 7] + 1}$ 两个问题： X - 2, X - 5 或者 X - 7小于0怎么办？ 什么时候停下来？ 如果不能拼出Y，就定义f[Y]=正无穷 例如f[-1] = f[-2] = ... =正无穷 所以f[1] = min{f[-1] + 1, f[-4] + 1, f[-6] + 1} = 正无穷，表示拼不出来1 这道题里初始条件为：f[0] = 0 初始条件其实就是 用转移方程算不出来，但又需要它的定义，此时需要手工定义 初始条件和边界情况 (人话)：最小的值搞定一下和不要数组越界 DP组成部分四：计算顺序 # DP组成部分四：计算顺序 拼出X所需的最少硬币数: f[X] = min{f[X - 2] + 1, f[X - 5] + 1, f[X - 7] + 1} 初始条件：f[0] = 0 然后计算f[1], f[2], ..., f[27] 当我们计算到f[x]时，f[X - 2], f[X - 5], f[X - 7]都已经得到结果了 Key: 计算顺序的确定只有一个原则，当你要算f[X]等式左边的时候，右边用到的状态都已经算过了 Time Complexity # 每一步尝试三种硬币，一共27步 与递归算法相比，没有任何重复计算 算法时间复杂度（即需要进行的步数）：时间复杂度 = 27 * 3 如果这道题是：拼出n块钱，有m枚硬币：时间复杂度 = n * m 递归时间复杂读为指数级别 Coding 669 # Lintcode 669: Coin Change class Solution { public: // coins // amount // {2, 5, 7} // 27 int coinChange(std::vector\u0026lt;int\u0026gt;\u0026amp; A, int M) { // 0....n: [n+1] // 0...n-1: [n] std::vector\u0026lt;int\u0026gt; f(M + 1); int n = A.size(); // number of kinds of coins // initialization f[0] = 0; int i, j; // f[1], f[2], ..., f[27] for (i = 1; i \u0026lt;= M; ++i) { f[i] = INT_MAX; // last coin A[j] // f[i] = min{f[i - A[0]] + 1, ..., f[i - A[n - 1]] + 1} for (j = 0; j \u0026lt; n; ++j) { if (i \u0026gt;= A[j] \u0026amp;\u0026amp; f[i - A[j]] != INT_MAX) { f[i] = std::min(f[i - A[j]] + 1, f[i]); } } } if (f[M] == INT_MAX) { f[M] = -1; } return f[M]; } }; 计数型动态规划 # Lintcode 114 Unique Paths DP组成部分一：确定状态 # 最后一步：无论机器人用何种方式到达右下角，总有最后挪动的一步：\n向右 或者 向下 右下角坐标设为(m - 1, n - 1)\n那么前一步（倒数第二步）一定是在(m - 2, n - 1)或者(m - 1, n - 2)\n子问题：那么，如果机器人有X种方式从左上角走到(m - 2, n - 1)，有Y种方式从左上角走到(m - 1, n - 2)，则机器人有X + Y种方式走到(m - 1, n - 1)\n求总方式数的计数型动态规划经常用到加法原理: 无重复\u0026amp;无遗漏 问题转化为，机器人有多少种方式从左上角走到(m - 2, n - 1)和(m - 1, n - 2)\n原题要求有多少种方式从左上角走到(m - 1, n - 1)\n子问题\n状态：设f[i][j]为机器人有多少种方式从左上角走到(i, j)\nDP组成部分二：转移方程 # 对于任意一个格子(i, j), $f[i][j] = f[i - 1][j] + f[i][j - 1]$ f[i][j] f[i - 1][j] f[i][j - 1] 机器人有多少种方式走到(i, j) 机器人有多少种方式走到(i - 1, j) 机器人有多少种方式走到(i, j - 1) DP组成部分三：初始条件和边界情况 # 初始条件：f[0][0] = 1，因为机器人只有一种方式到左上角 边界情况：i = 0或j = 0，则前一步只能有一个方向过来 \u0026ndash;\u0026gt; f[i][j] = 1 即第一行和第一列都是1 DP组成部分四：计算顺序 # f[0][0] = 1 计算第0行：f[0][0], f[0][1], ..., f[0][n - 1] 计算第1行：f[1][0], f[1][1], ..., f[1][n - 1] \u0026hellip; 计算第m-1行：f[m - 1][0], f[m - 1][1], ..., f[m - 1][n - 1] 顺序的定义，不是为写for循环，而是为了转移方程，f[i][j]要用到f[i - 1][j]和f[i][j - 1] 答案是f[m - 1][n - 1] 时间复杂度（计算步数）：O(MN), 空间复杂度（数组大小）：O(MN) Coding # class Solution { public: int uniquePaths(int m, int n) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(m, std::vector\u0026lt;int\u0026gt;(n)); int i, j; for (i = 0; i \u0026lt; m; ++i) { // row: top to bottom for (j = 0; j \u0026lt; n; ++j) { // column: left to right if (i == 0 || j == 0) { f[i][j] = 1; } else { f[i][j] = f[i - 1][j] + f[i][j - 1]; } } } return f[m - 1][n - 1]; } }; 存在型动态规划 # Lintcode 116 Jump Game DP组成部分一：确定状态 # 最后一步：如果🐸能跳到最后一块石头n - 1，我们考虑它跳的最后一步\n这一步是从(n - 1之前的)石头i跳过来，i \u0026lt; n - 1\n这需要两个条件同时满足：\n🐸可以跳到石头i(青蛙可以跳到i) 最后一步不超过跳跃的最大距离(i和n - 1的距离不能超过a_i)(即青蛙可以从i跳过来)：n - 1 - i \u0026lt;= a_i 子问题：那么我们需要知道青蛙能不能跳到石头i (i \u0026lt; n - 1)\n而我们原来要求青蛙能不能跳到石头n - 1\n子问题\n状态：设f[j]表示青蛙能不能跳到石头j\nDP组成部分二：转移方程 # 设f[j]表示青蛙能不能跳到石头j, $f[j] = OR_{0 \u0026lt;= i \u0026lt; j}(f[i]\\ AND\\ i + a[i] \u0026gt;= j)$ f[j] 青蛙能不能跳到石头j OR_{0\u0026lt;=i\u0026lt;j} 枚举上一个跳到的石头(编号)i f[i] 青蛙能不能跳到石头i i + a[i] \u0026gt;= j 最后一步的距离不能超过$a_i$ DP组成部分三：初始条件和边界情况 # 设f[j]表示青蛙能不能跳到石头j 初始条件：f[0] = true，因为青蛙一开始就在石头0 这道题没有边界情况，因为枚举的i不会越界 DP组成部分四：计算顺序 # 设f[j]表示青蛙能不能跳到石头j $f[j] = OR_{0 \u0026lt;= i \u0026lt; j}(f[i]\\ AND\\ i + a[i] \u0026gt;= j)$ 初始化f[0] = true 计算f[1], f[2], ..., f[n - 1] 答案是f[n - 1] 时间复杂度：O(N^2)，空间复杂度（数组大小）：O(N) Coding # class Solution { public: bool canJump(std::vector\u0026lt;int\u0026gt;\u0026amp; A) { int n = A.size(); std::vector\u0026lt;bool\u0026gt; f(n); f[0] = true; // initialization for (int j = 1; j \u0026lt; n; ++j) { f[j] = false; // previous stone i // last jump is from i to j for (int i = 0; i \u0026lt; j; ++i) { if (f[i] \u0026amp;\u0026amp; i + A[i] \u0026gt;= j) { f[j] = true; break; } } } return f[n - 1]; } }; In summary # 四个组成部分： 确定状态：确定要开的数组的意义定下来：\n研究最优策略的最后一步 化为子问题（把公共的汉字抽出来，有几个变量就是几维数组） 转移方程\n根据子问题定义直接得到 初始条件和边界情况\n细心，考虑周全（验证初值对不对，f[3]或f[4]的正确性）（边界情况数组不能越界） 计算顺序\n根本原理：利用之前的计算结果（大部分都是从小到达，二维的话就是从上到下然后从左到右） Exercise1: 金字塔 # $dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + array[i][j]$\nExercise2: 乘积最大子序列 # Lintcode 191 Maximum Product Subarray\nMaximum Product Subarray\n因为负数乘法的原因，需要记录到每个位置为止最大和最小的乘积\n状态：设f[j] =以a[j]结尾的连续子序列的最大乘积，设g[j] = 以a[j]结尾的连续子序列的最小乘积\nf[j] = max{a[j], max{a[j] * f[j - 1], a[j] * g[j - 1]} | j \u0026gt; 0}\ng[j] = min{a[j], min{a[j] * f[j - 1], a[j] * g[j - 1]} | j \u0026gt; 0}\nChapter 2: 动态规划初探 + 坐标型动态规划 + 位操作型动态规划 # 初探 坐标型动态规划 # Lintcode 115 Unique Paths II 题目分析 # 最后一步一定是从左边(i, j - 1)或上边(i - 1, j)过来 状态f[i][j]表示从左上角有多少种方式走到格子(i, j) 坐标型动态规划：数组下标[i][j]即坐标(i, j) 开的数组不需要加1 f[i][j] = f[i - 1][j] + f[i][j - 1] 初始条件和边界情况 # f[i][j] = 机器人有多少种方式从左上角走到(i, j) 如果左上角(0, 0)格或者右下角(m - 1, n - 1)格有障碍，直接输出0 如果(i, j)格有障碍，f[i][j] = 0，表示机器人不能到达此格(0种方式) 初始条件：f[0][0] = 1，f[i][j] = ： 0, 如果(i, j)格有障碍 1, i == 0 \u0026amp;\u0026amp; j == 0 f[i - 1][j], 如果j == 0，即第一列 f[i][j - 1], 如果i == 0，即第一行 f[i - 1][j] + f[i][j - 1]，其他 Coding # class Solution { public: int uniquePathsWithObstacles(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; A) { if (A.size() == 0 || A[0].size() == 0) { return 0; } int m = A.size(); int n = A[0].size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(m, std::vector\u0026lt;int\u0026gt;(n)); int i, j; for (i = 0; i \u0026lt; m; ++i) { for (j = 0; j \u0026lt; n; ++j) { if (A[i][j] == 1) { //obstacle f[i][j] = 0; continue; } if (i == 0 \u0026amp;\u0026amp; j == 0) { f[i][j] = 1; continue; } f[i][j] = 0; // if it is not on 0-th row if (i \u0026gt; 0) { f[i][j] += f[i - 1][j]; } // if it is not on 0-th column if (j \u0026gt; 0) { f[i][j] += f[i][j - 1]; } } } return f[m - 1][n - 1]; } }; 初探 序列型动态规划 # Lintcode 515 Paint House\n动态规划里，如果你需要知道一个信息，而状态无法体现这个信息，就把这个信息记录下来\n序列型特点：状态里出现了前这个字\n序列型f[i]代表前i个：0, 1, 2, ..., i - 1\n相较于坐标型：在开初始状态与转移方程的时候序列型有很好的作用 坐标型f[i]代表到i为止：0, 1, 2, ..., i\n动态规划组成部分一：确定状态 # 最优策略是花费最小的策略 最后一步：最优策略中房子N - 1一定染成了 红、蓝、绿 中的一种 但是相邻两栋房子不能漆成一种颜色 所成如果最优策略中房子N - 1是红色，房子N - 2只能是蓝色或绿色 所成如果最优策略中房子N - 1是蓝色，房子N - 2只能是红色或绿色 所成如果最优策略中房子N - 1是绿色，房子N - 2只能是红色或蓝色 !!!太复杂，如何优化：\n如果直接套用以前的思路，记录油漆前N栋房子的最小花费 根据套路，也需要记录油漆前N - 1栋房子的最小花费 但是，前N - 1栋房子的最小花费的最优策略中，不知道房子N - 2是什么颜色，所以有可能和房子N - 1撞色 !!!错误，正确做法：\n不知道房子N - 2是什么颜色，就把它记录下来 方法：放到状态里 分别记录油漆前N - 1栋房子并且房子N - 2是红色、蓝色、绿色的最小花费 子问题 # 求油漆前N栋房子并且房子N - 1是红色、蓝色、绿色的最小花费 需要知道油漆前N - 1栋房子并且房子N - 2是红色、蓝色、绿色的最小花费 子问题 状态：设油漆前i栋房子并且房子i - 1是红色、蓝色、绿色的最小花费分别为f[i][0], f[i][1], f[i][2] f = new int[n + 1][3] 动态规划组成部分二：转移方程 # 设油漆前i栋房子并且房子i - 1是红色、蓝色、绿色的最小花费分别为f[i][0], f[i][1], f[i][2] f[i][0] = min{f[i - 1][1] + cost[i - 1][0], f[i - 1][2] + cost[i - 1][0]} f[i][1] = min{f[i - 1][0] + cost[i - 1][1], f[i - 1][2] + cost[i - 1][1]} f[i][2] = min{f[i - 1][0] + cost[i - 1][2], f[i - 1][1] + cost[i - 1][2]} 动态规划组成部分三：初始条件和边界情况 # 设油漆前i栋房子并且房子i - 1是红色、蓝色、绿色的最小花费分别为f[i][0], f[i][1], f[i][2] 初始条件：f[0][0] = f[0][1] = f[0][2] = 0 即不油漆任何房子的花费 无边界情况 动态规划组成部分四：计算顺序 # 设油漆前i栋房子并且房子i - 1是红色、蓝色、绿色的最小花费分别为f[i][0], f[i][1], f[i][2] 初始化f[0][0], f[0][1], f[0][2] 计算f[1][0], f[1][1], f[1][2] \u0026hellip; 计算f[N][0], f[N][1], f[N][2] 答案是min{f[N][0], f[N][1], f[N][2]}时间复杂度O(N)，空间复杂度O(N) Coding # // Version 1 class Solution { public: int minCost(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; costs) { int n = costs.size(); if (n == 0) { return 0; } std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(n + 1, std::vector\u0026lt;int\u0026gt;(3)); // seq-type f[0][0] = f[0][1] = f[0][2] = 0; // initialization // first i houses 前i栋 for (int i = 1; i \u0026lt;= n; ++i) { // house i - 1\u0026#39;s color is j for (int j = 0; j \u0026lt; 3; ++j) { f[i][j] = 0x3f3f3f3f; // house i - 2\u0026#39;s color is k for (int k = 0; k \u0026lt; 3; ++k) { if (j == k) { continue; } f[i][j] = std::min(f[i][j], f[i - 1][k] + costs[i - 1][j]); } } } return std::min(f[n][0], std::min(f[n][1], f[n][2])); } }; // Version 2 class Solution { public: int minCost(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; costs) { int n = costs.size(); if (n == 0) { return 0; } std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(n + 1, std::vector\u0026lt;int\u0026gt;(3)); // seq-type f[0][0] = f[0][1] = f[0][2] = 0; // initialization // first i houses for (int i = 1; i \u0026lt;= n; ++i) { f[i][0] = std::min(f[i - 1][1] + costs[i - 1][0], f[i - 1][2] + costs[i - 1][0]); f[i][1] = std::min(f[i - 1][0] + costs[i - 1][1], f[i - 1][2] + costs[i - 1][1]); f[i][2] = std::min(f[i - 1][0] + costs[i - 1][2], f[i - 1][1] + costs[i - 1][2]); } return std::min(f[n][0], std::min(f[n][1], f[n][2])); } }; In summary: seq-type # 序列型动态规划：\u0026hellip;前i个\u0026hellip;最小/方式数/可行性 f[i] 代表前i个：f[0], f[1], ..., f[i - 1] 在设计动态规划的过程中，发现需要知道油漆前N - 1栋房子的最优策略中，房子N - 2的颜色 如果只用f[N - 1]，将无法区分 解决方法：记录下房子N - 2的颜色 在房子N - 2是 红/蓝/绿 色的情况下，油漆前N - 1栋房子的最小花费 问题迎刃而解 序列+状态 初探 划分型动态规划 # Leetcode 91 Decode Ways 动态规划组成部分一：确定状态 # 解密数字串即划分成若干段数字，每段数字对应一个字母 最后一步（最后一段）：对应一个字母 A, B, \u0026hellip;, Z 这个字母加密时变成1, 2, \u0026hellip;, 26 子问题 # 设数字串长度为N 要求数字串前N个字符的解密方式数 需要知道数字串前N - 1和N - 2个字符的解密方式数 子问题 状态：设数字串S前i个数字解密成字母串有f[i]种方式 动态规划组成部分二：转移方程 # 设数字串S前i个数字解密成字母串有f[i]种方式 f[i] = f[i - 1] | S[i - 1]对应一个字母 + f[i - 2] | S[i - 2]S[i - 1]对应一个字母 f[i]: 数字串S前i个数字解密成字母串的方式数 f[i - 1] | S[i - 1]对应一个字母: 数字串S前i - 1个数字解密成字母串的方式数 f[i - 2] | S[i - 2]S[i - 1]对应一个字母: 数字串S前i - 2个数字解密成字母串的方式数 动态规划组成部分三：初始条件和边界情况 # 设数字串S前i个数字解密成字母串有f[i]种方式\n初始条件：f[0] = 1，即空串有1种方式解密\n解密成空串 边界情况：如果i = 1，只看最后一个数字\n动态规划组成部分四：计算顺序 # f[0], f[1], ..., f[N] 答案是f[N] 时间复杂度O(N)，空间复杂度O(N) Coding # class Solution { public: int numDecodings(std::string\u0026amp; s) { int n = s.size(); if (n == 0) { return 0; } std::vector\u0026lt;int\u0026gt; f(n + 1, 0); int i; f[0] = 1; // initialization; 当物理意义不明确的时候，推断当前的初始化是否能得到正确的结果 // first i digits: s[0], ..., s[i - 1] for (i = 1; i \u0026lt;= n; ++i) { f[i] = 0; // last one digit --\u0026gt; letter // s[i - 1] == [1, 9] if (s[i - 1] != \u0026#39;0\u0026#39;) { f[i] += f[i - 1]; } // last two digits --\u0026gt; letter // s[i - 2]s[i - 1] == [10, 26] if (i \u0026gt;= 2 \u0026amp;\u0026amp; (s[i - 2] == \u0026#39;1\u0026#39; || (s[i - 2] == \u0026#39;2\u0026#39; \u0026amp;\u0026amp; s[i - 1] \u0026lt;= \u0026#39;6\u0026#39;))) { f[i] += f[i - 2]; } } return f[n]; } }; 坐标型动态规划：最小路径和 # Leetcode 64 Minimum Path Sum // Mine correct version class Solution { public: int minPathSum(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; grid) { int n = grid.size(); int m = grid[0].size(); if (n == 0 || m == 0) { return 0; } std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(n, std::vector\u0026lt;int\u0026gt;(m)); f[0][0] = grid[0][0]; for (int i = 1; i \u0026lt; m; ++i) { f[0][i] = f[0][i - 1] + grid[0][i]; } for (int i = 1; i \u0026lt; n; ++i) { f[i][0] = f[i - 1][0] + grid[i][0]; } for (int i = 1; i \u0026lt; n; ++i) { for (int j = 1; j \u0026lt; m; ++j) { f[i][j] = std::min(f[i - 1][j], f[i][j - 1]) + grid[i][j]; } } return f[n - 1][m - 1]; } }; // Official correct version class Solution { public: int minPathSum(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; grid) { int n = grid.size(); int m = grid[0].size(); if (n == 0 || m == 0) { return 0; } std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(n, std::vector\u0026lt;int\u0026gt;(m)); for (int i = 0; i \u0026lt; n; ++i) { for (int j = 0; j \u0026lt; m; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { f[i][j] = grid[i][j]; continue; } f[i][j] = INT_MAX; // if it has a grid above if (i \u0026gt; 0) { f[i][j] = std::min(f[i][j], f[i - 1][j] + grid[i][j]); } // if it has a grid to the left if (j \u0026gt; 0) { f[i][j] = std::min(f[i][j], f[i][j - 1] + grid[i][j]); } } } return f[n - 1][m - 1]; } }; 坐标型动态规划：最小路径和\u0026ndash;路径打印 # 最值和可行性都可以打印方案，但存在数不行 class Solution { public: int minPathSum(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; grid) { int n = grid.size(); int m = grid[0].size(); if (n == 0 || m == 0) { return 0; } std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(n, std::vector\u0026lt;int\u0026gt;(m)); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; pi(n, std::vector\u0026lt;int\u0026gt;(m)); // if f[i][j] depends on f[i - 1][j], pi[i][j] = 0 // if f[i][j] depends on f[i][j - 1], pi[i][j] = 1 for (int i = 0; i \u0026lt; n; ++i) { for (int j = 0; j \u0026lt; m; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { f[i][j] = grid[i][j]; continue; } f[i][j] = INT_MAX; // if it has a grid above if (i \u0026gt; 0) { f[i][j] = std::min(f[i][j], f[i - 1][j] + grid[i][j]); if (f[i][j] == f[i - 1][j] + grid[i][j]) { pi[i][j] = 0; } } // if it has a grid to the left if (j \u0026gt; 0) { f[i][j] = std::min(f[i][j], f[i][j - 1] + grid[i][j]); if (f[i][j] == f[i][j - 1] + grid[i][j]) { pi[i][j] = 1; } } } } // (n - 1, m - 1) std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; path(n + m - 1, std::vector\u0026lt;int\u0026gt;(2)); int p; int i = n - 1; int j = m - 1; // infer the path backward from (n - 1, m - 1) for (p = n + m - 2; p \u0026gt;= 0; --p) { path[p][0] = i; path[p][1] = j; if (p == 0) { break; } if (pi[i][j] == 0) { --i; } else { --j; } } for (p = 0; p \u0026lt; n + m - 1; ++p) { std::cout \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; path[p][0] \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; path[p][1] \u0026lt;\u0026lt; \u0026#34;): \u0026#34; \u0026lt;\u0026lt; grid[path[p][0]][path[p][1]] \u0026lt;\u0026lt; std::endl; } return f[n - 1][m - 1]; } }; # expected result from the test case: [[1,5,7,6,8],[4,7,4,4,9],[10,3,2,3,2]] (0, 0): 1 (1, 0): 4 (1, 1): 7 (2, 1): 3 (2, 2): 2 (2, 3): 3 (2, 4): 2 坐标型动态规划：最小路径和\u0026ndash;空间优化 # f[i][j] = std::min{f[i - 1][j], f[i][j - 1]} + A[i][j] 计算第i行时，只需要第i行和第i - 1行的f 所以，只需要保存两行的f值：f[i][0 ... n - 1]和f[i - 1][0 ... n - 1] 用滚动数组实现 开数组时，只开f[0][0 ... n - 1]和f[1][0 ... n - 1] 计算f[0][0], ..., f[0][n - 1]，计算f[1][0], ..., f[1][n - 1] 计算f[2][0 ... n - 1]时，开f[2][0 ... n - 1]，删掉f[0][0 ... n - 1]，因为已经不需要f[0][0 ... n - 1]的值了 计算f[3][0 ... n - 1]时，开f[3][0 ... n - 1]，删掉f[1][0 ... n - 1]，因为已经不需要f[1][0 ... n - 1]的值了 实际操作时，可以不用每次开数组，而是用滚动法 计算f[0][0], ..., f[0][n - 1]，计算f[1][0], ..., f[1][n - 1] 计算f[2][0 ... n - 1]时，把值写在f[0][0 ... n - 1]的数组里 同理，f[3][0 ... n - 1]写在f[1][0 ... n - 1]的数组里 最后f[m - 1][n - 1]存储在f[0][n - 1]（或者f[1][n - 1]）里，直接输出 对于网格上的动态规划，如果f[i][j]只依赖于本行的f[i][x]与前一行的f[i - 1][y]，那么就可以采用滚动数组的方法压缩空间。空间复杂度O(n)\n如果网格行数少列数多（大胖子网格），那么就可以逐列计算，滚动数组的长度为行数，空间复杂度O(M)\nclass Solution { public: int minPathSum(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; grid) { int n = grid.size(); int m = grid[0].size(); if (n == 0 || m == 0) { return 0; } std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(2, std::vector\u0026lt;int\u0026gt;(m)); int old, now = 0; // old: f[old][...] is holding f[i - 1][...] // now: f[now][...] is holding f[i][...] for (int i = 0; i \u0026lt; n; ++i) { // swap old and now old = now; now = 1 - now; // 0--\u0026gt;1, 1--\u0026gt;0 // 将所有的 f[i] 变成 f[now]； f[i - 1] 变成 f[old] for (int j = 0; j \u0026lt; m; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { f[now][j] = grid[i][j]; continue; } f[now][j] = INT_MAX; // if it has a grid above if (i \u0026gt; 0) { f[now][j] = std::min(f[now][j], f[old][j] + grid[i][j]); } // if it has a grid to the left if (j \u0026gt; 0) { f[now][j] = std::min(f[now][j], f[now][j - 1] + grid[i][j]); } } } return f[now][m - 1]; } }; 取模运算(%) 会比这个稍微慢一些\n坐标型动态规划：炸弹袭击 # Leetcode 361 Bomb Enemy 动态规划组成部分一：确定状态 # 我们假设有敌人或有墙的格子也能放炸弹 有敌人的格子：格子里的敌人被炸死，并继续向上爆炸 有墙的格子：炸弹不能炸死任何敌人 在(i, j)格放一个炸弹，它向上能炸死的敌人数是： (i, j)格为空地：(i - 1, j)格向上能炸死的敌人数 (i, j)格为敌人：(i - 1, j)格向上能炸死的敌人数 + 1 (i, j)格为墙：0 子问题 # 需要知道(i - 1, j)格放一个炸弹向上能炸死的敌人数 原来要求(i, j)格放一个炸弹向上能炸死的敌人数 子问题 状态： Up[i][j]表示(i, j)格放一个炸弹向上能炸死的敌人数 动态规划组成部分二：转移方程 # 设Up[i][j]表示(i, j)格放一个炸弹向上能炸死的敌人数 Up[i][j]: Up[i - 1][j]，如果(i, j)格是空地 Up[i - 1][j] + 1，如果(i, j)格是敌人 0，如果(i, j)格是墙 动态规划组成部分三：初始条件和边界情况 # 设Up[i][j]表示(i, j)格放一个炸弹向上能炸死的敌人数\n初始条件： 第0行的Up值和格子内容相关\nUp[0][j] = 0，如果(0, j)格不是敌人 Up[0][j] = 1，如果(0, j)格是敌人 动态规划组成部分四：计算顺序 # 逐行计算 Up[0][0], Up[0][1], ..., Up[0][n - 1] Up[1][0], Up[1][1], ..., Up[1][n - 1] \u0026hellip; Up[m - 1][0], Up[m - 1][1], ..., Up[m - 1][n - 1] 时间复杂度O(MN)，空间复杂度O(MN) 四个方向 # Up[i][j]表示如果(i, j)放一个炸弹向上可以最多炸死多少敌人 一共四个方向 可以类似地计算Down[i][j], Left[i][j], Right[i][j]，注意计算顺序会有改变 (i, j)如果是空地，放一个炸弹最多炸死的敌人数是： Up[i][j] + Down[i][j] + Left[i][j] + Right[i][j] 取最大值即可 时间复杂度和空间复杂度依然为O(MN) Coding # class Solution { public: int maxKilledEnemies(std::vector\u0026lt;std::vector\u0026lt;char\u0026gt;\u0026gt;\u0026amp; grid) { if (grid.size() == 0 || grid[0].size() == 0) { return 0; } int n = grid.size(); int m = grid[0].size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; up(n, std::vector\u0026lt;int\u0026gt;(m)); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; down(n, std::vector\u0026lt;int\u0026gt;(m)); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; left(n, std::vector\u0026lt;int\u0026gt;(m)); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; right(n, std::vector\u0026lt;int\u0026gt;(m)); // up // count E like prefix_sum // encounter W start from 0 for (int i = 0; i \u0026lt; n; ++i) { for (int j = 0; j \u0026lt; m; ++j) { up[i][j] = 0; if (grid[i][j] != \u0026#39;W\u0026#39;) { if (grid[i][j] == \u0026#39;E\u0026#39;) { ++up[i][j]; } if (i \u0026gt; 0) { up[i][j] += up[i - 1][j]; } } } } // down for (int i = n - 1; i \u0026gt;= 0; --i) { for (int j = 0; j \u0026lt; m; ++j) { down[i][j] = 0; if (grid[i][j] != \u0026#39;W\u0026#39;) { if (grid[i][j] == \u0026#39;E\u0026#39;) { ++down[i][j]; } if (i \u0026lt; n - 1) { down[i][j] += down[i + 1][j]; } } } } // left for (int i = 0; i \u0026lt; n; ++i) { for (int j = 0; j \u0026lt; m; ++j) { left[i][j] = 0; if (grid[i][j] != \u0026#39;W\u0026#39;) { if (grid[i][j] == \u0026#39;E\u0026#39;) { ++left[i][j]; } if (j \u0026gt; 0) { left[i][j] += left[i][j - 1]; } } } } // right for (int i = 0; i \u0026lt; n; ++i) { for (int j = m - 1; j \u0026gt;= 0; --j) { right[i][j] = 0; if (grid[i][j] != \u0026#39;W\u0026#39;) { if (grid[i][j] == \u0026#39;E\u0026#39;) { ++right[i][j]; } if (j \u0026lt; m - 1) { right[i][j] += right[i][j + 1]; } } } } int result = 0; for (int i = 0; i \u0026lt; n; ++i) { for (int j = 0; j \u0026lt; m; ++j) { if (grid[i][j] == \u0026#39;0\u0026#39;) { // empty result = std::max(result, up[i][j] + down[i][j] + left[i][j] + right[i][j]); } } } return result; } }; 坐标型动态规划 总结 # 给定输入为序列或者网格/矩阵 动态规划状态下标为序列下标i或者网格坐标(i, j) f[i]：以第i个元素结尾的某种性质 f[i][j]：到格子(i, j)的路径的性质 初始化设置f[0]的值 / f[0][0 ... n - 1]的值 二维空间优化：如果f[i][j]的值只依赖于当前行和前一行，则可以用滚动数组节省空间 位操作型动态规划：Counting Bits # 位操作（二进制） \u0026amp;与，|或，^异或，!非 逐位操作 Leetcode 338 Counting Bits 题目分析 # 对于每个数0 \u0026lt;= i \u0026lt;= N，直接求i的二进制表示里有多少个1 二进制表示算法：（十进制转二进制算法） 第一步：i mod 2是最低位的bit 第二步：i \u0026lt;- floor(i / 2)，如果i = 0，结束，否则回到第一步 时间复杂度：O(NlogN) 2个数有1位二进制(0 and 1) 2个数有2位二进制(2 and 3) 4个数有3位二进制(3, 4, 5 and 6) 8个数有4位二进制(7, 8, 9, 10, 11, 12, 13 and 14) \u0026hellip; 大约N / 2个数有log{2}N位二进制 用动态规划的话会比上面快一些 动态规划组成部分一：确定状态 # 观察一个数的二进制位\n$(170)_{10} = (10101010)_2$ 最后一步：观察这个数最后一个二进制位（最低位），去掉它，看剩下多少个1\n$(170)_{10} = (10101010)_2$ $(85)_{10} = (1010101)_2$ 85 的二进制表示里有4个1 170 的二进制表示里有4个1 子问题 # 要求N的二进制表示中有多少1 在N的二进制去掉最后一位N mod 2（有两种方法：\u0026raquo; and floor(/)），设新的数是Y = (N \u0026gt;\u0026gt; 1)（右移一位） 要知道Y的二进制表示中有多少1 子问题 状态：设f[i]表示i的二进制表示中有多少个1 知识点：和位操作相关的动态规划一般用值作状态\n动态规划组成部分二：转移方程 # 设f[i]表示i的二进制表示中有多少个1\nf[i] = f[i \u0026gt;\u0026gt; 1] + (i mod 2)\n动态规划组成部分三：初始条件和边界情况 # 设f[i]表示i的二进制表示中有多少个1\nf[i] = f[i \u0026gt;\u0026gt; 1] + (i mod 2)\n初始条件：f[0] = 0\n动态规划组成部分四：计算顺序 # f[0], f[1], f[2], ..., f[N] 时间复杂度O(N) 空间复杂度O(N) Coding # class Solution { public: std::vector\u0026lt;int\u0026gt; countBits(int num) { std::vector\u0026lt;int\u0026gt; f(num + 1); f[0] = 0; for (int i = 1; i \u0026lt;= num; ++i) { f[i] = f[i \u0026gt;\u0026gt; 1] + (i % 2); } return f; } }; Exercise: 最长上升子序列 # LIS: Leetcode 300 Longest Increasing Subsequence class Solution { public: int lengthOfLIS(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { if (nums.size() == 0) { return 0; } std::vector\u0026lt;int\u0026gt; f(nums.size(), 1); for (int i = 1; i \u0026lt; nums.size(); ++i) { // compare current number with all previous numbers // if satisfies the increasing condition then compete // [0, i) for (int j = 0; j \u0026lt; i; ++j) { if (nums[i] \u0026gt; nums[j]) { f[i] = std::max(f[i], f[j] + 1); } } } return *max_element(f.begin(), f.end()); } }; Chapter 3: 打劫房屋: 坐标型，前缀型 # Leetcode 198 打劫房屋 坐标型 # dp[坐标] = 行走到这个坐标的最优值\n转移：上一个坐标从哪里来，比如：上一次打劫了哪个房屋，或上一次行走了那个坐标\n动态规划 # 由抢房屋的性质可以看出，抢前i个房屋能得到的最大值，与后面如何抢的方案无关，只与前i - 1个房屋的最优方案有关。这满足了动态规划的无后效性和最优子结构 同时，由于题目不能抢相邻房屋，那么如果抢了第i个房屋，就不能抢第i - 1个房屋，可以得出前i个的最优方案也与前i - 2个的最优方案有关 代码思路 # 将要不要打劫的这个决策，记录到状态当中去(每走到一个坐标(房子)，都有两种情况:打劫/不打劫):\n可以设dp[i][0]为如果不抢第i个房屋，前i个房屋的最优方案为多少 设dp[i][1]为如果抢第i个房屋，前i个房屋的最优方案为多少 可以得出一下的状态转移方程式： $dp[i][0] = max(dp[i - 1][0], dp[i - 1][1])$ 因为如果不打劫当前的房子，从前一个位置选择一个最大值 $dp[i][1] = A[i] + dp[i - 1][0]$ 因为如果打劫当前的房子，之前的房子只能选择不打劫 class Solution { public: int rob(std::vector\u0026lt;int\u0026gt;\u0026amp; A) { int n = A.size(); if (n == 0) { return 0; } std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n, std::vector\u0026lt;int\u0026gt;(2, 0)); dp[0][0] = 0; dp[0][1] = A[0]; for (int i = 1; i \u0026lt; n; ++i) { // 如果不抢第i个，取前i - 1个位置dp较大值 dp[i][0] = std::max(dp[i - 1][0], dp[i - 1][1]); // 如果抢第i个，前一个不抢，考虑从前i - 2个位置的dp值转移，即i - 1选择不打劫 dp[i][1] = A[i] + dp[i - 1][0]; } int result = std::max(dp[n - 1][0], dp[n - 1][1]); return result; } }; 前缀型 # 坐标型关心走到哪儿，前缀型不关心\n前缀型永远是用前缀来表示子状态: 看前i个数怎样怎样，和前j个数或前i - 1个数的怎样怎样，之间的关系\n$$\\begin{align} dp[i] \u0026amp;= 前`i`个数取出的最大和(不关心第`i`个取或者不取) \\\\ \u0026amp;= max(dp[i - 2] + a[i], dp[i - 1]) 以此来避免取相邻的两个房子 \\\\ 优化：dp[i \\% 3] \u0026amp;= 前`i`个数取出的最大和(不关心第`i`个取或者不取) \\\\ \u0026amp;= max(dp[(i - 2) \\% 3] + a[i], dp[(i - 1) \\% 3]) 以此来避免取相邻的两个房子 \\end{align}$$ Chapter 4: 最大矩形 \u0026amp;\u0026amp; 最大直方图：坐标型 # 单调栈：求一个位置往左看或者往右看，第一个小于等于它的数的时候，用单调栈\n最大矩形 # Leetcode 85 最大矩形\n这题和Lintcode 122 直方图最大矩形覆盖很相似，只需要求出以每一行作为底最大的矩形是多少，每一行都有一个height数组，利用单调栈，每次更新height数组，height数组代表的是这一列上面有多少个连续的1，即矩形的高度，以每一行作为底（直方图最下面）时最大矩形面积，然后记录最大值即可。\n初始化dp数组，用dp数组记录当前位置上方有多少个连续1。对于每一行作为底，利用单调栈求高度，寻找最大的底乘高。\n注意这个栈是 从栈底到栈顶依次是从小到大的。如果栈中的数比当前的数大（或着等于）就要处理栈顶的（记录左右两边的比它小的第一个数）。\n然后如果遍历完之后，单独处理栈，此时所有元素右边都不存在比它小的height[j]表示目前的底上（第1行），j位置往上（包括j位置）有多少连续的1。\n不断更新最大面积。\n时间复杂度O(mn): dp的O(nm)和单调栈的O(n) 空间复杂度O(mn): dp的大小（下面的代码的空间复杂度可以优化成O(n)） class Solution { public: int maximalRectangle(std::vector\u0026lt;std::vector\u0026lt;char\u0026gt;\u0026gt;\u0026amp; matrix) { if (matrix.size() == 0 || matrix[0].size() == 0) { return 0; } int ans = 0; int n = matrix.size(); int m = matrix[0].size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n, std::vector\u0026lt;int\u0026gt;(m + 1)); for (int i = 0; i \u0026lt; n; ++i) { // 每个位置上方有多少连续的1 for (int j = 0; j \u0026lt; m; ++j) { if (i == 0 \u0026amp;\u0026amp; matrix[i][j] == \u0026#39;1\u0026#39;) { dp[i][j] = 1; continue; } if (matrix[i][j] == \u0026#39;1\u0026#39;) { dp[i][j] = dp[i - 1][j] + 1; } } } for (int i = 0; i \u0026lt; n; ++i) { // 把每一行作为底找最大矩形 ans = std::max(ans, largestRectangleArea(dp[i])); } return ans; } private: int largestRectangleArea(std::vector\u0026lt;int\u0026gt;\u0026amp; heights) { std::deque\u0026lt;int\u0026gt; S; heights[heights.size() - 1] = 0; int sum = 0; for (int i = 0; i \u0026lt; heights.size(); ++i) { if (S.empty() || heights[i] \u0026gt; heights[S.back()]) { S.push_back(i); } else { int temp = S.back(); S.pop_back(); sum = std::max(sum, heights[temp] * (S.empty() ? i : i - S.back() - 1)); --i; // 拿着右边界，寻找左边界 } } return sum; } }; 直方图最大矩形覆盖 # Leetcode 84 直方图最大矩形覆盖 // LHC version: Monotonic-stack answer class Solution { public: int largestRectangleArea(std::vector\u0026lt;int\u0026gt;\u0026amp; height) { std::deque\u0026lt;int\u0026gt; S; std::vector\u0026lt;int\u0026gt; heights = height; heights.push_back(-1); int sum = 0; for (int i = 0; i \u0026lt; heights.size(); ++i) { if (S.empty() || heights[i] \u0026gt; heights[S.back()]) { S.push_back(i); } else { int temp = S.back(); S.pop_back(); sum = std::max(sum, heights[temp] * (S.empty() ? i : i - S.back() - 1)); --i; // 拿着右边界，寻找左边界 } } return sum; } }; // Correct Monotonic-stack answer from other students class Solution { public: int largestRectangleArea(std::vector\u0026lt;int\u0026gt;\u0026amp; heights) { if (heights.size() == 0) { return 0; } std::deque\u0026lt;int\u0026gt; stack; int sum = 0; for (int i = 0; i \u0026lt;= heights.size(); ++i) { int curt = (i == heights.size()) ? -1 : heights[i]; while (!stack.empty() \u0026amp;\u0026amp; curt \u0026lt;= heights[stack.back()]) { int h = heights[stack.back()]; stack.pop_back(); int w = stack.empty() ? i : i - stack.back() - 1; sum = std::max(sum, h * w); } stack.push_back(i); } return sum; } }; Chapter 5: 最短假期：坐标型 # Lintcode 267 最短假期\n转移方程：\n如果今天工作（运动），那么只能由昨天运动（工作）或休息转移而来 如果今天休息，那么可以由昨天三种状态（工作，运动，休息）转移 状态：dp[i][j]第i天干了j这个事情(j: 工作，健身，休息) 的最小休息天数\ndp[i][0], dp[i][1], dp[i][2]分别表示去公司工作、去健身房锻炼、去休息 状态转移方程： dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) dp[i][2] = min(dp[i - 1][0], min(dp[i - 1][1], dp[i - 1][2])) + 1 设假期天数为n 时间复杂度：从左至右扫描数组，每次由三个DP方程进行转移。时间复杂度为O(n) 空间复杂度：DP数组规模为3*n。空间复杂度O(n) class Solution { public: int minimumRestDays(std::vector\u0026lt;int\u0026gt;\u0026amp; company, std::vector\u0026lt;int\u0026gt;\u0026amp; gym) { int n = company.size(); // dp[i][0]表示第i天工作的最小休息天数，dp[i][1]表示锻炼，dp[i][2]表示休息 std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n, std::vector\u0026lt;int\u0026gt;(3)); for (int i = 0; i \u0026lt; n; ++i) { for (int j = 0; j \u0026lt; 3; ++j) { dp[i][j] = 0x3f3f3f3f; } } // 临界值第一天 dp[0][0] = dp[0][1] = 0; if (company[0] == 0) { dp[0][0] = 1; } if (gym[0] == 0) { dp[0][1] = 1; } dp[0][2] = 1; for (int i = 1; i \u0026lt; n; ++i) { if (company[i] == 1) { dp[i][0] = std::min(dp[i - 1][1], dp[i - 1][2]); } if (gym[i] == 1) { dp[i][1] = std::min(dp[i - 1][0], dp[i - 1][2]); } dp[i][2] = std::min(dp[i - 1][0], std::min(dp[i - 1][1], dp[i - 1][2])) + 1; } return std::min(dp[n - 1][0], std::min(dp[n - 1][1], dp[n - 1][2])); } }; Exercise: 相关题目 # Lintcode 151 买卖股票的最佳时机III\nLintcode 515 房屋染色\nChapter 6: 最小调整代价：背包型 # Lintcode 91 最小调整代价 Solution 状态 # 令dp[i][j]表示从左到右调整到前i个数时，将第i个数的数值调整为j所需要付出的最小代价。 整个调整的过程中要满足相邻两数之差不超过target\n转移方程 # dp[i][j] = min(dp[i][j], dp[i - 1][k] + abs(j - A[i])) k是把第i - 1个数调整为k k和j差值不超过target j - target \u0026lt;= k \u0026lt;= j + target 思路 # 已知每个整数范围[1,100]，那么对于每个元素，为了调整到该元素和与之相邻的元素的差不大于target，该元素调整的范围就在[1,100]。所以对于数组A[]的每一位元素，我们都需要进行[1,100]范围内的可能状态的转移。\n令dp[i][j]表示元素A[i]=j时，A[i]与A[i-1]差值不大于target所需要付出的最小代价。\n当A[i]=j时，可行的A[i-1]的范围为[max(1, j - target)，min(100, j + target)]。而dp[i][j]为所有可行的A[i-1]中，花费代价最小的一种可能，再加上A[i]调整到 j 所需花费abs(j - A[i])。\n当A[i]=j时，k在[max(1, j - target)，min(100, j + target)]范围内时，我们可以写出以下式子：\n1.临界值： # dp[0][j] = abs(j - A[0])\n2.状态转移方程： # dp[i][j] = min(dp[i][j], dp[i - 1][k] + abs(j - A[i])) 最后在所有最后一位的可能解dp[n-1][i]中的最小值，就是我们所求的最小代价。 假设数组长度为n 空间复杂度O(10000*n) 时间复杂度O(n^2) Coding # class Solution { public: int minAdjustmentCost(std::vector\u0026lt;int\u0026gt;\u0026amp; A, int target) { int n = A.size(); // dp[i][j]表示元素A[i]=j时，A[i]与A[i-1]差值不大于target所需要付出的最小代价 int dp[n][101]; for (int i = 0; i \u0026lt; n; ++i) { for (int j = 1; j \u0026lt;= 100; ++j) { // 初始化为极大值 dp[i][j] = 0x3f3f3f3f; } } for (int i = 0; i \u0026lt; n; ++i) { for (int j = 1; j \u0026lt;= 100; ++j) { if (i == 0) { // 临界值：第一个元素A[0]调整为j的代价 dp[0][j] = abs(j - A[0]); } else { // left为A[i]=j时，A[i-1]与A[i]差值不大于target的A[i-1]最小值 // right为A[i]=j时，A[i-1]与A[i]差值不大于target的A[i-1]最大值 int left = max(1, j - target); int right = min(100, j + target); for (int k = left; k \u0026lt;= right; ++k) { // 当A[i-1]=k时，答案为A[i-1]=k的代价dp[i-1][k]，加上A[i]=j的调整代价abs(j-A[i]) dp[i][j] = std::min(dp[i][j], dp[i - 1][k] + abs(j - A[i])); } } } } int mincost = 0x3f3f3f3f; for (int i = 1; i \u0026lt;= 100; ++i) { mincost = min(mincost, dp[n - 1][i]); } return mincost; } }; Chapter 7: 香槟塔：坐标型 # Lintcode 1018 香槟塔 状态：dp[i][j]流入多少水，至于剩下多少水是根据流入的量算出来的\nStep 1 : 如何定义状态？ # 令dp[i][j]为(i, j)位置的杯子的流入香槟总体积占比\nStep 2 : 临界值是什么？ # dp[0][0] = poured 将所有的香槟都倒在顶端 Step 3 : 状态转移方程怎么写？ # (i, j)的杯子流入的香槟总体积 = ((i - 1, j - 1)香槟体积 - 1 + (i - 1, j)香槟体积 - 1) / 2.0 状态转移方程为：dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j] - 2) / 2.0 Step 4 : DP结果是什么？ # min(dp[query_row][query_glass], 1) 这个杯子能装的香槟永远不会超过一杯 class Solution { public: double champagneTower(int poured, int query_row, int query_glass) { double dp[101][101]; dp[0][0] = poured; for (int row = 1; row \u0026lt;= query_row; ++row) { for (int i = 0; i \u0026lt;= row; ++i) { if (i == 0) { dp[row][i] = std::max(0.0, (dp[row - 1][i] - 1) / 2.0); } else if (i == row) { dp[row][i] = std::max(0.0, (dp[row - 1][i - 1] - 1) / 2.0); } else { dp[row][i] = std::max(0.0, (dp[row - 1][i - 1] + dp[row - 1][i] - 2) / 2.0); // here is wrong, we should compare both left and right with the 0 } } } return std::min(dp[query_row][query_glass], 1.0); } }; don\u0026rsquo;t forget to minus one\nthe reason we use std::max there is that to keep volume of each cup to be positive\n空间优化： # Soluton 1: row % 2 # class Solution { public: double champagneTower(int poured, int query_row, int query_glass) { double dp[2][101]; dp[0][0] = poured; for (int row = 1; row \u0026lt;= query_row; ++row) { for (int i = 0; i \u0026lt;= row; ++i) { if (i == 0) { dp[row % 2][i] = std::max(0.0, (dp[(row - 1) % 2][i] - 1) / 2.0); } else if (i == row) { dp[row % 2][i] = std::max(0.0, (dp[(row - 1) % 2][i - 1] - 1) / 2.0); } else { dp[row % 2][i] = std::max(0.0, (dp[(row - 1) % 2][i - 1] + dp[(row - 1) % 2][i] - 2) / 2.0); // here is wrong, we should compare both left and right with the 0 } } } return std::min(dp[query_row % 2][query_glass], 1.0); } }; Soluton 2: 一维数组 # // Solution 2 class Solution { public: double champagneTower(int poured, int query_row, int query_glass) { double dp[101]; dp[0] = poured; for (int row = 1; row \u0026lt;= query_row; ++row) { for (int i = row; i \u0026gt;= 0; --i) { if (i == 0) { dp[i] = std::max(0.0, (dp[i] - 1) / 2.0); } else if (i == row) { dp[i] = std::max(0.0, (dp[i - 1] - 1) / 2.0); } else { dp[i] = std::max(0.0, (dp[i - 1] + dp[i] - 2) / 2.0); // here is wrong, we should compare both left and right with the 0 } } } return std::min(dp[query_glass], 1.0); } }; Chapter 8: 飞行棋I # Lintcode 1565 飞行棋I\n只能向右走，有方向性所以可以用动态规划\nOther notes: 拓扑排序的一个功能就是检测图里是否有循环依赖 拓扑排序与动态规划的关系： 一个题目能够被动态规划，那么把状态看作点，把状态的依赖关系看作边的话，那所构成的图当中一定可以被拓扑排序 有循环依赖就不能产生拓扑排序，有循环依赖就不能使用动态规划 Step 1 : 如何定义状态？ # 令dp[i]表示从位置1到位置i的最小投掷骰子次数 Step 2 : 临界值是什么？ # 当位置i 属于 [2, 7]的时候，可以通过投掷一次骰子抵达，即dp[i] = 1。 并且dp[1] = 0 Step 3 : 状态转移方程怎么写？ # 如果投掷骰子：dp[i] = min(dp[i], dp[i - j] + 1), j 属于 [1, 6] 当前位置投掷一次骰子所能走到的位置的dp值是当前dp值加一。 如果有另外相连的位置：dp[相连的位置] = min(dp[相连的位置], dp[i])，这里实际上是更新后面的结果 可以不需要投掷骰子直接向前走 Step 4 : DP结果是什么？ # 棋盘的长度 length 的 dp 值即为答案: dp[length] DP Solution # Time Complexity O(n)\nSpace Complexity O(n)\nclass Solution { public: int modernLudo(int length, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; connections) { if (length == 1) { return 0; } if (length \u0026lt;= 7) { return 1; } // connected[i] = j 表示i与j相连 std::vector\u0026lt;int\u0026gt; connected(length + 1); std::vector\u0026lt;int\u0026gt; dp(length + 1); // initialization for (int i = 1; i \u0026lt;= length; ++i) { connected[i] = i; dp[i] = 0x3f3f3f3f; } dp[1] = 0; for (int i = 0; i \u0026lt; connections.size(); ++i) { connected[connections[i][0]] = connections[i][1]; } for (int i = 2; i \u0026lt;= length; ++i) { if (i - 6 \u0026lt; 1) { dp[i] = 1; } else { for (int j = 1; j \u0026lt;= 6; ++j) { dp[i] = std::min(dp[i], dp[i - j] + 1); } } dp[connected[i]] = std::min(dp[connected[i]], dp[i]); } return dp[length]; } }; Chapter 9: 序列型动态规划 # 序列型动态规划\u0026ndash;简介 # 给定一个序列 动态规划方程f[i]中的下标i表示前i个元素a[0], a[1], ..., a[i - 1]的某种性质 坐标型的f[i]表示以a[i]为结尾的某种性质 初始化中，f[0]表示空序列的性质 坐标型动态规划的初始条件f[0]就是指以a_0为结尾的子序列的性质 序列型动态规划\u0026ndash;数字翻转 # Lintcode 843 数字翻转 动态规划组成部分一：确定状态 # 最后一步：最优策略中，最后一位数是否翻转 但需要知道前一位数已经变成0还是1 并且前N - 1位数最少翻转多少次，满足要求（无01子串） 不知道的信息加入状态里 状态 用f[i][0]表示A[i - 1]变成0的情况下，前i位最少翻转多少个能满足要求 用f[i][1]表示A[i - 1]变成1的情况下，前i位最少翻转多少个能满足要求 动态规划组成部分二：转移方程 # 用f[i][0]表示A[i - 1]变成0的情况下，前i位最少翻转多少个能满足要求 用f[i][1]表示A[i - 1]变成1的情况下，前i位最少翻转多少个能满足要求 $f[i][j] = min_{(k, j) ≠ (0, 1)}(f[i - 1][k] + 1_{A[i - 1] ≠ j})$ 动态规划组成部分三：初始条件和边界情况 # 用f[i][0]表示A[i - 1]变成0的情况下，前i位最少翻转多少个能满足要求 用f[i][1]表示A[i - 1]变成1的情况下，前i位最少翻转多少个能满足要求 $f[i][j] = min_{(k, j) ≠ (0, 1)}(f[i - 1][k] + 1_{A[i - 1] ≠ j})$ 初始条件： f[0][0] = f[0][1] = 0 动态规划组成部分四：计算顺序 # 用f[i][0]表示A[i - 1]变成0的情况下，前i位最少翻转多少个能满足要求 用f[i][1]表示A[i - 1]变成1的情况下，前i位最少翻转多少个能满足要求 $f[i][j] = min_{(k, j) ≠ (0, 1)}(f[i - 1][k] + 1_{A[i - 1] ≠ j})$ 答案是min(f[N][0], f[N][1]) 算法时间复杂度O(N)，空间复杂度O(N)，可以用滚动数组优化至O(1) Coding # Mine Correct Solution # class Solution { public: int flipDigit(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { int n = nums.size(); if (n \u0026lt;= 1) { return 0; } int f[n + 1][2]; f[0][0] = 0; f[0][1] = 0; for (int i = 1; i \u0026lt;= n; ++i) { if (nums[i - 1] == 1) { f[i][0] = std::min(f[i - 1][1] + 1, f[i - 1][0] + 1); f[i][1] = f[i - 1][1]; } else { f[i][0] = std::min(f[i - 1][0], f[i - 1][1]); f[i][1] = f[i - 1][1] + 1; } } return std::min(f[n][0], f[n][1]); } }; index in nums should minus 1\nI think my solution is better than offical\nOfficial Solution # class Solution { public: int flipDigit(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { int n = nums.size(); if (n \u0026lt;= 1) { return 0; } int f[n + 1][2]; f[0][0] = 0; f[0][1] = 0; // first i digits: nums[0, ..., i - 1] for (int i = 1; i \u0026lt;= n; ++i) { for (int j = 0; j \u0026lt; 2; ++j) { f[i][j] = 0x3f3f3f3f; // nums[i - 1]--\u0026gt;j, should I flip? int t = 0; if (nums[i - 1] != j) { t = 1; } // nums[i - 2]--\u0026gt;k for (int k = 0; k \u0026lt; 2; ++k) { if (k == 0 \u0026amp;\u0026amp; j == 1) { continue; } f[i][j] = std::min(f[i][j], f[i - 1][k] + t); } } } return std::min(f[n][0], f[n][1]); } }; 序列型动态规划的时间优化\u0026ndash;房屋染色II # Lintcode 516 房屋染色II\n时间优化有三个可以做的事情：\n看式子，并展开(也许会发现里面有重复) 画图 小例子 Mine Correct Answer O(nk^2) # class Solution { public: int minCostII(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; costs) { if (costs.size() == 0) { return 0; } int n = costs.size(); int m = costs[0].size(); if (n == 1 \u0026amp;\u0026amp; m == 1) { return costs[0][0]; } int dp[n + 1][m]; for (int i = 0; i \u0026lt; m; ++i) { dp[0][i] = 0; } for (int i = 1; i \u0026lt;= n; ++i) { for (int j = 0; j \u0026lt; m; ++j) { dp[i][j] = costs[i - 1][j]; int min_cost = 0x3f3f3f3f; for (int k = 0; k \u0026lt; m; ++k) { if (k == j) { continue; } min_cost = std::min(min_cost, dp[i][j] + dp[i - 1][k]); } dp[i][j] = min_cost; } } int ans = 0x3f3f3f3f; for (int i = 0; i \u0026lt; m; ++i) { ans = std::min(ans, dp[n][i]); } return ans; } }; follow up: can you make it faster?\nMine solution is O(nk^2), how to make it faster\n时间优化 # 优化方法 记录下最小值f[i - 1][a]和次小值f[i - 1][b] 如果去掉的是最小值，则f[i][a] = f[i - 1][b] + cost[i - 1][a] 如果去掉的不是最小值，则f[i][j] = f[i - 1][a] + cost[i - 1][j] 时间复杂度降为O(nk) Mine correct time optimized solution # class Solution { public: int minCostII(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; costs) { if (costs.size() == 0) { return 0; } int n = costs.size(); int m = costs[0].size(); if (n == 1 \u0026amp;\u0026amp; m == 1) { return costs[0][0]; } int dp[n + 1][m]; for (int i = 0; i \u0026lt; m; ++i) { dp[0][i] = 0; } // record min and second_minimum number std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; min_secondmin(2, std::vector\u0026lt;int\u0026gt;(2, 0x3f3f3f3f)); for (int i = 1; i \u0026lt;= n; ++i) { int index_min = min_secondmin[0][0]; int first_min = min_secondmin[0][1]; int second_min = min_secondmin[1][1]; min_secondmin[0][1] = min_secondmin[1][1] = 0x3f3f3f3f; for (int j = 0; j \u0026lt; m; ++j) { dp[i][j] = costs[i - 1][j]; if (i == 1 \u0026amp;\u0026amp; dp[i][j] \u0026lt; min_secondmin[0][1]) { min_secondmin[1][1] = min_secondmin[0][1]; min_secondmin[1][0] = min_secondmin[0][0]; min_secondmin[0][1] = dp[i][j]; min_secondmin[0][0] = j; continue; } if (i == 1 \u0026amp;\u0026amp; dp[i][j] \u0026lt; min_secondmin[1][1]) { min_secondmin[1][1] = dp[i][j]; min_secondmin[1][0] = j; } if (i == 1) { continue; } if (j == index_min) { // j is the smallest and choose the second smallest dp[i][j] += second_min; } else { // j is not the smallest one and choose the smallest dp[i][j] += first_min; } if (dp[i][j] \u0026lt; min_secondmin[0][1]) { min_secondmin[1][1] = min_secondmin[0][1]; min_secondmin[1][0] = min_secondmin[0][0]; min_secondmin[0][1] = dp[i][j]; min_secondmin[0][0] = j; continue; } if (dp[i][j] \u0026lt; min_secondmin[1][1]) { min_secondmin[1][1] = dp[i][j]; min_secondmin[1][0] = j; } } } int ans = 0x3f3f3f3f; for (int i = 0; i \u0026lt; m; ++i) { ans = std::min(ans, dp[n][i]); } return ans; } }; Official time optimized solution # class Solution { public: int minCostII(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; costs) { int n = costs.size(); if (n == 0) { return 0; } int m = costs[0].size(); if (m == 0) { return 0; } if (n == 1 \u0026amp;\u0026amp; m == 1) { return costs[0][0]; } int dp[n + 1][m]; int f[n + 1][m]; int i, j, k, a, b; for (int i = 0; i \u0026lt; m; ++i) { f[0][i] = 0; } // first i houses: 0 ... i - 1 for (i = 1; i \u0026lt;= n; ++i) { // find minimum and 2nd minimum among // f[i - 1][0], ..., f[i - 1][m - 1] a = b = -1; // this is index for (k = 0; k \u0026lt; m; ++k) { if (a == -1 || f[i - 1][k] \u0026lt; f[i - 1][a]) { // new minimum is f[i - 1][k] b = a; // old minimum becomes now the 2nd minimum a = k; // new minimum is f[i - 1][k] } else { if (b == -1 || f[i - 1][k] \u0026lt; f[i - 1][b]) { b = k; } } } for (j = 0; j \u0026lt; m; ++j) { if (j != a) { // remove an element which is NOT the minimum f[i][j] = f[i - 1][a] + costs[i - 1][j]; } else { // remove an element which IS the minimum f[i][j] = f[i - 1][b] + costs[i - 1][j]; } } } int ans = 0x3f3f3f3f; for (int i = 0; i \u0026lt; m; ++i) { ans = std::min(ans, f[n][i]); } return ans; } }; remember the strategy to calculate minimum and the 2nd minimum with one time iteration\na == -1 || f[a] ....\n序列型动态规划\u0026ndash;买卖股票1 # Lintcode 149 买卖股票1 动态规划解法 # 从 0 到 N - 1 枚举 j, 即第几天卖 时刻保存当前为止（即 0 ~ j - 1 天）的最低加个P_i 最大的P_j - P_i 即为答案 Mine correct solution # class Solution { public: int maxProfit(std::vector\u0026lt;int\u0026gt;\u0026amp; prices) { int n = prices.size(); if (n \u0026lt;= 1) { return 0; } int min_index = 0; int max_profit = 0; for (int j = 1; j \u0026lt; n; ++j) { if (prices[j] \u0026lt; prices[min_index]) { min_index = j; continue; } if (max_profit \u0026lt; prices[j] - prices[min_index]) { max_profit = prices[j] - prices[min_index]; } } return max_profit; } }; Official solution: Better # class Solution { public: int maxProfit(std::vector\u0026lt;int\u0026gt;\u0026amp; prices) { int n = prices.size(); if (n \u0026lt;= 1) { return 0; } int min = prices[0]; int res = 0; for (int j = 1; j \u0026lt; n; ++j) { // The minimum among prices[0] ... prices[j - 1] is stored in min res = std::max(res, prices[j] - min); min = std::min(min, prices[j]); } return res; } }; 序列型动态规划\u0026ndash;买卖股票2 # Lintcode 150 买卖股票2 题目分析 # 买卖任意多次 最优策略是如果今天的价格比明天的价格低，就今天买，明天卖（贪心） 凡事我们自己想出来的贪心算法都需要证明一下： 所有的贪心的证明都是：假设最优策略不是这样，可以改成这样，且不会更差 正确性证明可以从这里下手： 如果最优策略第10天买，第15天卖，我们可以把它分解成5天(即改成这样)，结果不会变差 Official solution # // 贪心 class Solution { public: int maxProfit(std::vector\u0026lt;int\u0026gt;\u0026amp; prices) { if (prices.size() == 0) { return 0; } int res = 0; for (int i = 0; i \u0026lt; prices.size() - 1; ++i) { if (prices[i + 1] \u0026gt; prices[i]) { res += prices[i + 1] - prices[i]; } // res += std::max(prices[i + 1] - prices[i], 0) } return res; } }; 序列型动态规划\u0026ndash;买卖股票3: 序列型 # Lintcode 151 买卖股票3 e.g. 输入：[4,4,6,1,1,4,2,5] 输出：6（4买入，6卖出，1买入，5卖出，收益为(6 - 4) + (5 - 1) = 6)\n题目分析 # 题目大意和 I, II 基本相似 只能最多两次买卖 所以需要记录已经买卖了多少次 动态规划组成部分一：确定状态 # 最后一步：最优策略中，最后一次卖发生在第 j 天 枚举最后一次买发生在第几天 但是不知道之前有没有买卖过 记录阶段 # 不知道有没有买过，就记录下来 阶段可以保持：即不进行买卖操作 在阶段2，继续持有，获利为当天价格减昨天价格（当天获利，当天结算） 阶段可以变化：买或卖 在阶段2，卖了一股后，进入阶段3 动态规划组成部分一：确定状态 continued # 最优策略一定是前 N 天（第 N - 1 天）结束后，处于\n阶段1: 没买卖过；阶段3: 买卖过一次；阶段5: 买卖过两次 状态：f[i][j]表示前i天（第 i - 1天）结束后，在阶段j的最大获利\n例如，如果要求前 N 天（第 N - 1 天）结束后，在阶段5的最大获利，设为f[N][5]\n情况1: 第 N - 2 天就在阶段5 \u0026mdash; f[N - 1][5] 情况2: 第 N - 2 天还在阶段4（第二次持有股票），第 N - 1 天卖掉 f[N - 1][4] + (P_{N - 1} - P_{N - 2}) 例如，如果要求前 N 天（第 N - 1 天）结束后，在阶段4的最大获利，设为f[N][4]\n情况1: 第 N - 2 天就在阶段4 \u0026mdash; f[N - 1][4] + (P_{N - 1} - P_{N - 2}) 情况2: 第 N - 2 天还在阶段3 \u0026mdash; f[N - 1][3] 子问题 # 要求f[N][1], ..., f[N][5] 需要知道f[N - 1][1], ..., f[N - 1][5] 子问题 动态规划组成部分二：转移方程 # f[i][j]: 前i天（第 i - 1天）结束后，处在阶段j，最大获利 动态规划组成部分三：初始条件和边界情况 # 刚开始（前 0 天）处于阶段1 f[0][1] = 0 f[0][2] = f[0][3] = f[0][4] = f[0][5] = -inf 阶段 1, 3, 5: f[i][j] = max(f[i - 1][j], f[i - 1][j - 1] + P_{i - 1} - P_{i - 2}) 阶段 2, 4: f[i][j] = max(f[i - 1][j] + P_{i - 1} - P_{i - 2}, f[i - 1][j - 1]) 如果 j - 1 \u0026lt; 1 或 i - 2 \u0026lt; 0，对应项不计入 max 因为最多买卖两次，所以答案是max(f[N][1], f[N][3], f[N][5])，即清仓状态下最后一天最大获利 动态规划组成部分四：计算顺序 # 初始化f[0][1], ..., f[0][5] f[1][1], ..., f[1][5] \u0026hellip; f[N][1], ..., f[N][5] 时间复杂度：O(N), 空间复杂度：O(N), 优化后可以O(1), 因为f[i][1..5]只依赖于f[i - 1][1..5] Official Solution # class Solution { public: int maxProfit(std::vector\u0026lt;int\u0026gt;\u0026amp; prices) { int n = prices.size(); if (n == 0) { return 0; } int f[n + 1][5 + 1]; int i, j, k; for (k = 1; k \u0026lt;= 5; ++k) { f[0][k] = 0xcfcfcfcf; // impossible } f[0][1] = 0; for (i = 1; i \u0026lt;= n; ++i) { // 阶段 1, 3, 5：f[i][j] = max(f[i - 1][j], f[i - 1][j - 1] + prices[i - 1] - prices[i - 2]) for (j = 1; j \u0026lt;= 5; j += 2) { // keep state f[i][j] = f[i - 1][j]; // sell today if (j \u0026gt; 1 \u0026amp;\u0026amp; i \u0026gt; 1 \u0026amp;\u0026amp; f[i - 1][j - 1] != 0xcfcfcfcf) { f[i][j] = std::max(f[i][j], f[i - 1][j - 1] + prices[i - 1] - prices[i - 2]); } } // 阶段 2, 4：f[i][j] = max(f[i - 1][j] + prices[i - 1] - prices[i - 2], f[i - 1][j - 1]) for (j = 2; j \u0026lt;= 5; j += 2) { // buy today f[i][j] = f[i - 1][j - 1]; // keep state, calculate profit today if (i \u0026gt; 1 \u0026amp;\u0026amp; f[i - 1][j] != 0xcfcfcfcf) { f[i][j] = std::max(f[i][j], f[i - 1][j] + prices[i - 1] - prices[i - 2]); } } } int res = 0; for (j = 1; j \u0026lt;= 5; j += 2) { res = std::max(res, f[n][j]); } return res; } }; 序列型动态规划\u0026ndash;买卖股票4 # Lintcode 393 买卖股票4 题目分析 # 首先，如果 K 很大，K \u0026gt; N / 2，则题目可以化简成为Best Time to Buy and Sell Stock II, 每天买入当且仅当价格比下一天低 Best Time to Buy and Sell Stock III 相当于这题中K = 2 所以我们可以借鉴之前的解法 记录阶段 # 阶段1: 没买卖过 阶段3: 买卖过一次，现在空仓 阶段5: 买卖过两次，现在空仓 \u0026hellip; 阶段2K + 1: 买卖过K次，现在空仓 阶段2: 第一次持有，还没有卖 阶段4: 第二次持有，还没有卖 阶段6: 第三次持有，还没有卖 \u0026hellip; 阶段2K: 第K次持有，还没有卖 动态规划组成部分二：转移方程 # f[i][j]: 前i天（第i - 1天）结束后，处在阶段j，最大获利 动态规划组成部分三：初始条件和边界情况 # 刚开始（前 0 天）处于阶段1 f[0][1] = 0 f[0][2] = f[0][3] = f[0][4] = f[0][**2K + 1**] = -inf 阶段 1, 3, 5, \u0026hellip;, 2K + 1: f[i][j] = max(f[i - 1][j], f[i - 1][j - 1] + P_{i - 1} - P_{i - 2}) 阶段 2, 4, \u0026hellip;, 2K: f[i][j] = max(f[i - 1][j] + P_{i - 1} - P_{i - 2}, f[i - 1][j - 1]) 如果 j - 1 \u0026lt; 1 或 i - 2 \u0026lt; 0，对应项不计入 max 因为最多买卖K次，所以答案是max(f[N][1], f[N][3], ..., f[N][**2K + 1**])，即清仓状态下最后一天最大获利 动态规划组成部分四：计算顺序 # 初始化f[0][1], ..., f[0][**2K + 1**] f[1][1], ..., f[1][**2K + 1**] \u0026hellip; f[N][1], ..., f[N][**2K + 1**] 时间复杂度：O(NK), 空间复杂度：O(NK), 优化后可以O(1), 因为f[i][1..**2K + 1**]只依赖于f[i - 1][1..**2K + 1**] Official Solution # class Solution { public: int maxProfit(int K, std::vector\u0026lt;int\u0026gt;\u0026amp; prices) { int n = prices.size(); if (n == 0) { return 0; } int i, j, k; if (K \u0026gt; n / 2) { int ans = 0; for (i = 0; i \u0026lt; n - 1; ++i) { if (prices[i + 1] - prices[i] \u0026gt; 0) { ans += prices[i + 1] - prices[i]; } } return ans; } int f[n + 1][2 * K + 1 + 1]; for (k = 1; k \u0026lt;= 2 * K + 1; ++k) { f[0][k] = 0xcfcfcfcf; // impossible } f[0][1] = 0; for (i = 1; i \u0026lt;= n; ++i) { // 阶段 1, 3, 2 * K + 1：f[i][j] = max(f[i - 1][j], f[i - 1][j - 1] + prices[i - 1] - prices[i - 2]) for (j = 1; j \u0026lt;= 2 * K + 1; j += 2) { // keep state f[i][j] = f[i - 1][j]; // sell today if (j \u0026gt; 1 \u0026amp;\u0026amp; i \u0026gt; 1 \u0026amp;\u0026amp; f[i - 1][j - 1] != 0xcfcfcfcf) { f[i][j] = std::max(f[i][j], f[i - 1][j - 1] + prices[i - 1] - prices[i - 2]); } } // 阶段 2, 2 * K：f[i][j] = max(f[i - 1][j] + prices[i - 1] - prices[i - 2], f[i - 1][j - 1]) for (j = 2; j \u0026lt;= 2 * K; j += 2) { // buy today f[i][j] = f[i - 1][j - 1]; // keep state, calculate profit today if (i \u0026gt; 1 \u0026amp;\u0026amp; f[i - 1][j] != 0xcfcfcfcf) { f[i][j] = std::max(f[i][j], f[i - 1][j] + prices[i - 1] - prices[i - 2]); } } } int res = 0; for (j = 1; j \u0026lt;= 2 * K + 1; j += 2) { res = std::max(res, f[n][j]); } return res; } }; Official Solution : rolling array optimization # class Solution { public: int maxProfit(int K, std::vector\u0026lt;int\u0026gt;\u0026amp; prices) { int n = prices.size(); if (n == 0) { return 0; } int i, j, k; if (K \u0026gt; n / 2) { int ans = 0; for (i = 0; i \u0026lt; n - 1; ++i) { if (prices[i + 1] - prices[i] \u0026gt; 0) { ans += prices[i + 1] - prices[i]; } } return ans; } // rolling array optimization int f[2][2 * K + 1 + 1]; int old, now = 0; for (k = 1; k \u0026lt;= 2 * K + 1; ++k) { f[now][k] = 0xcfcfcfcf; // impossible } f[now][1] = 0; for (i = 1; i \u0026lt;= n; ++i) { // 先交换old, now old = now; now = 1 - now; // 阶段 1, 3, 2 * K + 1：f[i][j] = max(f[i - 1][j], f[i - 1][j - 1] + prices[i - 1] - prices[i - 2]) for (j = 1; j \u0026lt;= 2 * K + 1; j += 2) { // keep state f[now][j] = f[old][j]; // sell today if (j \u0026gt; 1 \u0026amp;\u0026amp; i \u0026gt; 1 \u0026amp;\u0026amp; f[old][j - 1] != 0xcfcfcfcf) { f[now][j] = std::max(f[now][j], f[old][j - 1] + prices[i - 1] - prices[i - 2]); } } // 阶段 2, 2 * K：f[i][j] = max(f[i - 1][j] + prices[i - 1] - prices[i - 2], f[i - 1][j - 1]) for (j = 2; j \u0026lt;= 2 * K; j += 2) { // buy today f[now][j] = f[old][j - 1]; // keep state, calculate profit today if (i \u0026gt; 1 \u0026amp;\u0026amp; f[old][j] != 0xcfcfcfcf) { f[now][j] = std::max(f[now][j], f[old][j] + prices[i - 1] - prices[i - 2]); } } } int res = 0; for (j = 1; j \u0026lt;= 2 * K + 1; j += 2) { res = std::max(res, f[now][j]); } return res; } }; 序列型动态规划\u0026ndash;小结 # 当思考序列型动态规划最后一步时，这一步的选择依赖于前一步的某种状态 题目 最后一步需要知道的信息 序列 + 状态 Paint House 房子N - 1 油漆成红色，则房子N - 2不能油漆成红色 记录油漆前N - 1栋房子并且房子N - 2是红、蓝、绿色的最小花费 Digital Flip 翻转A[i]时，A[i - 1]A[i]不能是01 记录翻转前N - 1位并且第N - 2位是0/1的最小翻转次数 Best Time to Buy and Sell Stock III 第j天卖股票，第i天买股票(i \u0026lt; j)时，需要知道第i天之前是不是已经买了股票 记录前N天买卖股票最大获利，并且第N - 1天：1.未买卖股票；2.买了第一次股票还没卖；\u0026hellip;；5.已经第二次卖了股票 初始化时，f[0]代表前0个元素/前0天当情况 与坐标型动态规划区别 计算时，f[i]代表前i个元素（即元素0~i-1）的某种性质 初探 最长上升子序列(LIS) # 最长序列型动态规划 # 题目给定一个序列\n要求找出符合条件的最长子序列\n方法\n记录以每个元素i结尾的最长子序列的长度 计算时，在i之前枚举子序列上一个元素是哪个 为坐标型动态规划\nLintcode 76 Longest Increasing Subsequence\n动态规划组成部分一：确定状态 # 最后一步：对于最优的策略，一定有最后一个元素a[j] 第一种情况：最优策略种最长上升子序列就是{a[j]}，答案是1 第二种情况：子序列长度大于1，那么最优策略中a[j]前一个元素是a[i]，并且a[i] \u0026lt; a[j]（不一定是连续的） 因为是最优策略，那么它选中的以a[i]结尾的上升子序列一定是最长的 子问题 # 因为不确定最优策略中a[j]前一个元素a[i]是哪个，需要枚举每个i 求以a[i]结尾的最长上升子序列 本来是求以a[j]结尾的最长上升子序列 化为子问题：i\u0026lt;j 状态：设f[j] = 以a[j]结尾的最长上升子序列的长度 动态规划组成部分二：转移方程 # f[j] = 以a[j]结尾的最长上升子序列的长度 f[j] = max(1, f[i] + 1 | i \u0026lt; j and a[i] \u0026lt; a[j]) 动态规划组成部分三：初始条件和边界情况 # 情况2必须满足： i \u0026gt;= 0 a[j] \u0026gt; a[i]，满足单调性 初始条件：空 动态规划组成部分四：计算顺序 # f[j] = 以a[j]结尾的最长上升子序列的长度 计算f[0], f[1], f[2], ..., f[n - 1] 答案是max(f[0], f[1], f[2], \u0026hellip;, f[n - 1]) 算法时间复杂度O(n^2), 空间复杂度O(n) 思考：如何做到时间复杂度O(nlogn) # Official Solution: O(n^2) # class Solution { public: int longestIncreasingSubsequence(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { int n = nums.size(); int f[n]; int max = 0; for (int j = 0; j \u0026lt; n; ++j) { f[j] = 1; // previous element `nums[i]` for (int i = 0; i \u0026lt; j; ++i) { if (nums[i] \u0026lt; nums[j]) { f[j] = std::max(f[j], f[i] + 1); } } max = std::max(f[j], max); } return max; } }; phone interview # 组里做什么 下一步什么时候 Exercise 602 俄罗斯套娃信封 # Lintcode 602 俄罗斯套娃信封\n信封按照长度从小到大排序后（相同长度按照宽度从大到小），找宽度的 Longest Increasing Subsequence\nnormal: O(n^2)\nchallenge: O(nlogn) （后面再介绍）\nMine first solution: Time Limit Exceeded: dp # class Solution { public: int maxEnvelopes(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; envelopes) { int n = envelopes.size(); if (n \u0026lt;= 1) { return n; } int f[n]; std::sort(envelopes.begin(), envelopes.end(), [](const auto\u0026amp; l, const auto\u0026amp; r) { return (l[0] == r[0]) ? l[1] \u0026gt; r[1] : l[0] \u0026lt; r[0]; } ); int max = 0; for (int j = 0; j \u0026lt; n; ++j) { f[j] = 1; for (int i = 0; i \u0026lt; j; ++i) { if (envelopes[i][1] \u0026lt; envelopes[j][1]) { f[j] = std::max(f[j], f[i] + 1); } } max = std::max(f[j], max); } return max; } }; Mine second solution: Time Limit Exceeded: dfs # class Solution { public: int maxEnvelopes(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; envelopes) { int n = envelopes.size(); if (n \u0026lt;= 1) { return n; } std::sort(envelopes.begin(), envelopes.end()); int ans = 0; dfs(envelopes, 0, 0, ans); return ans; } private: void dfs(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; envelopes, int current, int num, int\u0026amp; ans) { ans = std::max(ans, num); for (int i = current; i \u0026lt; envelopes.size(); ++i) { if (current == 0 || envelopes[i][0] \u0026gt; envelopes[current - 1][0] \u0026amp;\u0026amp; envelopes[i][1] \u0026gt; envelopes[current - 1][1]) { dfs(envelopes, i + 1, num + 1, ans); } } } }; Correct forum official solution # 此处使用二分优化最长上升子序列，在dp数组中二分查找第一个大于等于当前数的位置，然后dp[i]=k，即第i处的最长上升子序列长度为k。 class Solution { public: int maxEnvelopes(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; envelopes) { int n = envelopes.size(); if (n == 0) { return 0; } auto cmp = [](const auto\u0026amp; x, const auto\u0026amp; y) { return x[0] == y[0] ? x[1] \u0026gt; y[1] : x[0] \u0026lt; y[0]; }; std::sort(envelopes.begin(), envelopes.end(), cmp); std::vector\u0026lt;int\u0026gt; dp(n), height(n+1, INT_MAX); for (int i = 0; i \u0026lt; n; ++i) { int k = std::lower_bound(height.begin(), height.end(), envelopes[i][1]) - height.begin(); dp[i] = k; height[k] = envelopes[i][1]; } int ans = 0; for (int i = 0; i \u0026lt; n; ++i) { ans = std::max(ans, dp[i]); } return ans + 1; } }; 课后习题 # Chapter 10: 骰子求和：背包型 # Lintcode 20 骰子求和 本质上这道题是求方案数\n核心的是看状态是什么：\n状态：用来表示一个子问题的一些参数凑在一起 这道题的状态： 背包型 # f[i][j] 表示前i次骰子（掷i次骰子）我能够凑出和为j的概率是多少 能想出这个的原因： 影响到状态，影响到每个计算结果的是掷多少次 我们要求的和，求的和是1，求的和是2，的概率也不一样 将以上两个信息全部放到状态当中去 class Solution { public: std::vector\u0026lt;std::pair\u0026lt;int, double\u0026gt;\u0026gt; dicesSum(int n) { std::vector\u0026lt;std::pair\u0026lt;int, double\u0026gt;\u0026gt; result; std::vector\u0026lt;std::vector\u0026lt;double\u0026gt;\u0026gt; f(n + 1, std::vector\u0026lt;double\u0026gt;(6 * n + 1, 0)); // has to be double for (int i = 1; i \u0026lt;= 6; ++i) { f[1][i] = 1.0 / 6; } for (int i = 2; i \u0026lt;= n; ++i) { for (int j = i; j \u0026lt;= 6 * n; ++j) { // n or i, both work for (int k = 1; k \u0026lt;= 6; ++k) { if (j \u0026gt; k) { f[i][j] += f[i - 1][j - k]; } } f[i][j] /= 6.0; } } for (int j = n; j \u0026lt;= 6 * n; ++j) { result.push_back(std::make_pair(j, f[n][j])); } return result; } }; Chapter 11: 最长有效括号：后缀型(与前缀型只区别与计算顺序) # Lintcode 193 最长有效括号\n字符串的题目特别多的题目都是前缀型 或 后缀型\n设状态dp[i]为从i到len - 1中，以i开头的最长合法子串长度 初始化：dp[len - 1] = 0 如果s[i] = ')'，则跳过，因为不可能有由'('开头的串 如果s[i] = '(', 则需要找到右括号和它匹配。可以跳过以i + 1开头的合法子串，看j = i + dp[i + 1] + 1的位置是否为右括号。 如果位置i没越界且为右括号，那么有dp[i] = dp[i + 1] + 2，此外在这个基础上还要将j + 1开头的子串加进来（只要不越界）。 class Solution { public: int longestValidParentheses(std::string\u0026amp; s) { int n = s.size(); if (n \u0026lt; 2) { return 0; } int result = 0; // int dp[n] should have an additional step to initialize all elements to zero std::vector\u0026lt;int\u0026gt; dp(n, 0); for (int i = n - 2; i \u0026gt;= 0; --i) { if (s[i] == \u0026#39;(\u0026#39;) { int j = i + dp[i + 1] + 1; // 如果没越界且为右括号 if (j \u0026lt; n \u0026amp;\u0026amp; s[j] == \u0026#39;)\u0026#39;) { dp[i] = dp[i + 1] + 2; // 还要将j + 1开头的子串加进来 if (j + 1 \u0026lt; n) { dp[i] += dp[j + 1]; } } result = std::max(result, dp[i]); } } return result; } }; Chapter 12: 最大子数组差 # Lintcode 45 最大子数组差\n关键字不重叠，应该想到隔板法\n因为区间不重合，那么他们肯定会被一个隔板隔开，我们枚举隔板，再去算左右两边的 最大区间和 和 最小区间和\nMine solution # class Solution { public: int maxDiffSubArrays(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { int n = nums.size(); if (n \u0026lt; 2) { return 0; } std::vector\u0026lt;int\u0026gt; prefix_sum(n + 1, 0); for (int i = 1; i \u0026lt; n + 1; ++i) { prefix_sum[i] = prefix_sum[i - 1] + nums[i - 1]; } std::vector\u0026lt;int\u0026gt; left_max(n, nums[0]); std::vector\u0026lt;int\u0026gt; right_max(n, nums[n - 1]); std::vector\u0026lt;int\u0026gt; left_min(n, nums[0]); std::vector\u0026lt;int\u0026gt; right_min(n, nums[n - 1]); int temp_min = nums[0]; int temp_max = nums[0]; for (int i = 1; i \u0026lt; n; ++i) { } } }; Official Solution: with some Greedy idea # class Solution { public: int maxDiffSubArrays(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { int n = nums.size(); if (n \u0026lt; 2) { return 0; } // max_sum_of_left[i], min_sum_of_left[i]分别表示从左到i的范围内的子数组最大/最小和 int max_sum_of_left[n]; int min_sum_of_left[n]; // max_sum_of_right[i], min_sum_of_right[i]分别表示从右到i的范围内的子数组最大/最小和 int max_sum_of_right[n]; int min_sum_of_right[n]; // 求从左到i的范围内的子数组最大和 max_sum_of_left[0] = nums[0]; for (int i = 1, now = nums[0]; i \u0026lt; n; ++i) { now = std::max(nums[i], now + nums[i]); max_sum_of_left[i] = std::max(max_sum_of_left[i - 1], now); } // 求从右到i的范围内的子数组最大和 max_sum_of_right[n - 1] = nums[n - 1]; for (int i = n - 2, now = nums[n - 1]; i \u0026gt;= 0; --i) { now = std::max(nums[i], now + nums[i]); max_sum_of_right[i] = std::max(max_sum_of_right[i + 1], now); } // 求从左到i的范围内的子数组最小和 min_sum_of_left[0] = nums[0]; for (int i = 1, now = nums[0]; i \u0026lt; n; ++i) { now = std::min(nums[i], now + nums[i]); min_sum_of_left[i] = std::min(min_sum_of_left[i - 1], now); } // 求从右到i的范围内的子数组最小和 min_sum_of_right[n - 1] = nums[n - 1]; for (int i = n - 2, now = nums[n - 1]; i \u0026gt;= 0; --i) { now = std::min(nums[i], now + nums[i]); min_sum_of_right[i] = std::min(min_sum_of_right[i + 1], now); } int ans = 0xcfcfcfcf; for (int i = 0; i \u0026lt; n - 1; ++i) { // max(左大右小的差值，左小右大的差值) ans = std::max(ans, std::max(std::abs(max_sum_of_left[i] - min_sum_of_right[i + 1]), std::abs(min_sum_of_left[i] - max_sum_of_right[i + 1]))); } return ans; } }; 相关题目 # Lintcode 1833 钢笔盒\nLintcode 1850 捡苹果\nChapter 13: 工作安排：坐标型 # Lintcode 1147 工作安排 Compared to the question of house robbers, not only do we have to decide whether to pick the task(whether to rob the current house) but also we have to determine what task we will choose(simple or complex)\n状态：令dp[i]表示前i周可完成的最大价值 状态转移：dp[i] = max(dp[i - 1] + low[i], dp[i - 2] + high[i]) 临界值：第一周只能选择简单任务 class Solution { public: int workPlan(std::vector\u0026lt;int\u0026gt;\u0026amp; low, std::vector\u0026lt;int\u0026gt;\u0026amp; high) { int n = low.size(); int dp[n]; if (n == 0) { return 0; } dp[0] = low[0]; for (int i = 1; i \u0026lt; n; ++i) { if (i \u0026lt; 2) { dp[i] = std::max(dp[i - 1] + low[i], high[i]); continue; } dp[i] = std::max(dp[i - 1] + low[i], dp[i - 2] + high[i]); } return dp[n - 1]; } }; 思考：如果有负数怎么办？\nChapter 14: 染色问题：坐标型 # Lintcode 1444 染色问题\nState: let dp[i] represents the total number of plans when circle is divided into i sectors\nInitialization:\nwhen i == 1, we can use at least 1 color, A(m, 1) : dp[1] = m when i % 2 == 0, we can use at least 2 color, A(m, 2) : dp[2] = m * (m - 1) when i % 2 == 1 \u0026amp;\u0026amp; i \u0026gt;= 3, we can use at least 3 color, A(m, 3) : dp[3] = m * (m - 1) * (m - 2) Function:\nthe n sectors problem can be derived from n - 1 sectors sub-problem and n - 2 sectors sub-problem Regarding to n - 1 sectors sub-problem, it has dp[n - 1] color plans. Due to the colors of adjacent sectors cannot be the same, when we insert a new sector between two sectors, the number of plans is m - 2, and then the total number of color plans is dp[n - 1] * (m - 2) function 1: dp[i] += dp[i - 1] * (m - 2) Regarding to n - 2 sectors sub-problem, it has dp[n - 2] color plans We just choose one sector and then split it into two sectors with the same colors, and then insert a new sector between these two same color sectors. Then we have dp[n - 2] * (m - 1) the number of color plans function 2: dp[i] += dp[i - 2] * (m - 1) Answer:\nWhen calculating the result, we should add module operation within calculation to avoid overflowing dp[n] is the final answer Time Complexity: O(n) Space Complexity: O(n)\nclass Solution { public: int getCount(int n, int m) { long long MOD = 1000000007; // long long dp[n + 3]; // ERROR: each value has to be initialized to zero // dp[i] represent number of color plans when we have i sectors and m colors std::vector\u0026lt;long long\u0026gt; dp(n + 3, 0); // one sector has m color plans dp[1] = m % MOD; // two sectors have m * (m - 1) color plans dp[2] = (long long)m * (m - 1) % MOD; // three sectors have m * (m - 1) * (m - 2) color plans dp[3] = (long long)m * (m - 1) * (m - 2) % MOD; for (int i = 4; i \u0026lt;= n; ++i) { dp[i] += dp[i - 1] * (m - 2) % MOD; dp[i] += dp[i - 2] * (m - 1) % MOD; dp[i] %= MOD; // dp[i] = (dp[i - 1] * (m - 2) + dp[i - 2] * (m - 1)) % MOD; // alternative } return (int)dp[n]; } }; Note: long long dp[n + 5]; // each value has to be initialized to zero we can also optimize space with rolling array\nChapter 15: 最小的窗口子序列：匹配型 # Lintcode 857 MinWindow This problem is very similar to The Problem of LCS and The Problem of Edit Distance, given two strings which means 匹配型动态规划\n匹配型动态规划开(n + 1)(m + 1)的数组， 类似于背包型动态规划 这个题的题目名看起来和很多题目有相似之处，如“滑动窗口的最小值”等，那里我们使用单调队列进行求解，不要弄混\nThe first solution with O(n^2 * (n + m)) approximate to O(n^3) # The first solution we should come up with is brute force: use O(n^2) to find all sub-string of a string, and then check each sub-string whether they include the sub-sequence of string T. The total time cost O(n^2 * (n + m)) The second solution with O(n * (n + m)) approximate to O(n^2), we should let time less than 10^8, if n is 20000, then it becomes 4 * 10^8 \u0026gt; 10^8, which is not good # Then to optimize time complexity, we should consider which part can be optimized. The first part which is to find the sub-string. We just enumerate left of sub-string and ignore right of sub-string. Then the total time becomes O(n * (n + m)) The thrid solution: Dynamic Programming: Time O(n * m) Space O(n * m) # State: let dp[i][j] represent the left start pointer which successfully have previous j characters of string T is the sub-sequence of previous i characters of string S\n状态：令dp[i][j]表示成功匹配T串的前j个字符为S中前i个字符的子序列时的匹配起点（即第几个字符）\nFunction:\nS[i] == T[j] \u0026amp;\u0026amp; j == 1 : dp[i][j] = i S[i] == T[j] \u0026amp;\u0026amp; j != 1 : dp[i][j] = dp[i - 1][j - 1] S[i] != T[j] : dp[i][j] = dp[i - 1][j] Initialization: dp[i][0] = 0 represents the left start pointer of pairation of an empty string and previous i characters of string S.\nExplanation: it equals to 0 means 0th character, not the index here Alternative: To initialize here is let dp[i][0] = -1, because index 0 represent a real character Answer:\nEnumerate dp[][T.size()]: if dp[i][T.size()] != 0(here 0 is 0th character, -1 is index), then here exist left start pointer of the window dp[i][T.size()], the length of window is i - dp[i][T.size()] + 1 At this time, we maintain our minimum length of window len and the most left start pointer start Answer: S.substr(start, start + len) class Solution { public: std::string minWindow(std::string\u0026amp; S, std::string\u0026amp; T) { int n = S.size(); int m = T.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); for (int i = 1; i \u0026lt;= n; ++i) { for (int j = 1; j \u0026lt;= m; ++j) { if (S[i - 1] == T[j - 1]) { if (j == 1) { dp[i][j] = i; } else { dp[i][j] = dp[i - 1][j - 1]; } } else { dp[i][j] = dp[i - 1][j]; } } } int start = 0; int len = n + 1; for (int i = 1; i \u0026lt;= n; ++i) { if (dp[i][m] != 0) { if (i - dp[i][m] + 1 \u0026lt; len) { start = dp[i][m] - 1; len = i - dp[i][m] + 1; } } } if (len == n + 1) { return \u0026#34;\u0026#34;; } return S.substr(start, len); } }; Relative Problems # Lintcode 32 最小子串覆盖 Lintcode 397 最长上升连续子序列 Lintcode 77 最长公共子序列 Chapter 16: 划分型、博弈型 和 背包型 动态规划 # 划分型动态规划 # 定义： 给定长度为N的序列或字符串，要求划分成若干段，每一段要满足一定的性质\n段数不限，或指定K段 每一段满足一定的性质 做法：\n类似于序列型动态规划，但是通常要加上段数信息 一般用f[i][j]记录前i个元素（元素0 ~ i-1）分成j段的性质，如最小代价 Example: Lintcode 513 Perfect Square：划分型 # Lintcode 513 完美平方 动态规划组成部分一：确定状态 # 确定状态：关注最优策略中最后一个完全平方数j^2 最优策略中n - j^2也一定被划分成最少的完全平方数之和 需要知道n - j^2最少被分成几个完全平方数之和 原来是求n最少被分成几个完全平方数之和 子问题 状态：设f[i]表示i最少被分成几个完全平方数之和（copy下来，变量变成下标，就能得到状态） 这道题只开一维数组，没有段数，原因是段数不是我们需要限定的。比如说如果这道题问，能不能分成10段，就需要改一下。 动态规划组成部分二：转移方程 # 设f[i]表示i最少被分成几个完全平方数之和 f[i] = min_{1 \u0026lt;= j * j \u0026lt;= i}(f[i - j^2] + 1) 此处{1 \u0026lt;= j * j \u0026lt;= i}是限定条件 动态规划组成部分三：初始条件和边界情况 # 设f[i]表示i最少被分成几个完全平方数之和\nf[i] = min_{1 \u0026lt;= j * j \u0026lt;= i}(f[i - j^2] + 1)\n初始条件：0被分成0个完全平方数之和\nf[0] = 0 动态规划组成部分四：计算顺序 # 初始化f[0] 计算f[1], ..., f[N] 答案是f[N] 空间复杂度是O(n)并且不能用滚动数组 时间复杂度是sqrt(1) + sqrt(2) + ... + sqrt(n) = O(n * sqrt(n)) Coding Solution # // DP solution // Running time error class Solution { public: int numSquares(int n) { std::vector\u0026lt;int\u0026gt; f(n + 1); f[0] = 0; for (int i = 1; i \u0026lt;= n; ++i) { f[i] = INT_MAX; // last perfect square is j * j for (int j = 1; j * j \u0026lt;= i; ++j) { f[i] = std::min(f[i], f[i - j * j] + 1); } } return f[n]; } }; A Math solution costs O(n) time, and O(1) space\n// Math solution // Accepted class Solution { public: int numSquares(int n) { while (n % 4 == 0) { n /= 4; } if (n % 8 == 7) { return 4; } for (int i = 0; i * i \u0026lt;= n; ++i) { int j = (int)std::sqrt(n * 1.0 - i * i); if (i * i + j * j == n) { int res = 0; if (i \u0026gt; 0) { res += 1; } if (j \u0026gt; 0) { res += 1; } return res; } } return 3; } }; Follow up # 有多少种方式把N表示成完全平方数之和（1^2 + 2^2和2^2 + 1^2属于不同的方式）————方案数 Ans: replace min with sum 能不能把N表示成恰好K个完全平方数之和————可行性 状态：f[i][k]能不能将i表示成恰好k个完全平方数之和 状态转移：f[i][k] = OR_{1 \u0026lt;= j * j \u0026lt;= i}(f[i - j^2][k - 1]) Example: Lintcode 108 Palindrome Partitioning II # Lintcode Palindrome Partitioning II 动态规划组成部分一：确定状态 # 最后一步：关注最优策略中最后一段回文串，设为S[j .. N-1] 需要知道S前j个字符[0 .. j-1]最少可以划分成几个回文串 子问题 # 求S前N个字符S[0 .. N-1]最少划分为几个回文串 需要知道S前j个字符[0 .. j-1]最少可以划分成几个回文串 子问题 状态：设S前i个字符S[0 .. i-1]最少可以划分成f[i]个回文串 动态规划组成部分二：转移方程 # 设f[i]为S前i个字符S[0 .. i-1]最少可以划分成几个回文串 f[i] = min_{j = 0, ..., i-1}(f[j] + 1 | S[j .. i-1]是回文串) 动态规划组成部分三：初始条件和边界情况 # 设f[i]为S前i个字符S[0 .. i-1]最少可以划分成几个回文串 f[i] = min_{j = 0, ..., i-1}(f[j] + 1 | S[j .. i-1]是回文串) 初始条件：空串可以被分成0个回文串 f[0] = 0 动态规划组成部分四：计算顺序 # 计算f[0], f[1], ..., f[N] 回文串判断 # 方法一：从左到右 和 从右到左 各读一遍，完全一样 方法二：可以用两个指针从两头向中间移动，每一步两个指针指向的字符都必须相等 但是动态规划转移方程是f[i] = min_{j = 0, ..., i-1}(f[j] + 1 | S[j .. i-1]是回文串) 每次都判断S[j .. i-1]是不是回文串很慢( 转移方程为O(n^3)太慢) 如何优化？ 回文串种类 # 回文串分两种： 长度为奇数 长度为偶数 生成回文串 # 假设我们现在不是寻找回文串，而是生成回文串 从中间开始，向两边扩展，每次左右两端加上同样的字符 在字符串中找到所有回文串 # 以字符串的每一个字符为中点，向两边扩展，找到所有回文串 记录回文串 # 从S每一个字符开始向两边扩展 考虑奇数长度回文串 和 偶数长度回文串 用isPlain[i][j]表示S[i .. j]是否是回文串 时间复杂度O(n^2) 回到原题 # S最少划分成多少个回文串 f[i] = min_{j = 0, ..., i-1}(f[j] + 1 | S[j .. i-1]是回文串) f[i] = min_{j = 0, ..., i-1}(f[j] + 1 | isPlain[j][i - 1] = True) 答案是f[N] - 1(因为原题是求最少划分几次) 时间复杂度O(n^2) 空间复杂度O(n^2) class Solution { public: int minCut(std::string\u0026amp; S) { int n = S.size(); if (n == 0) { return 0; } std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt; palin = CalcPalin(S); std::vector\u0026lt;int\u0026gt; f(n + 1); f[0] = 0; int i, j; // S[0 .. i-1] for (i = 1; i \u0026lt;= n; ++i) { f[i] = 0x3f3f3f3f; // S[j .. i-1] for (j = 0; j \u0026lt; i; ++j) { if (palin[j][i - 1]) { f[i] = std::min(f[i], f[j] + 1); } } } return f[n] - 1; } std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt; CalcPalin(std::string\u0026amp; S) { int n = S.size(); std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt; palin(n, std::vector\u0026lt;bool\u0026gt;(n, false)); int i, j, mid; for (mid = 0; mid \u0026lt; n; ++mid) { // odd-length palindrome i = j = mid; while (i \u0026gt;= 0 \u0026amp;\u0026amp; j \u0026lt; n \u0026amp;\u0026amp; S[i] == S[j]) { palin[i][j] = true; --i; ++j; } // even-length palindrome i = mid - 1; j = mid; while (i \u0026gt;= 0 \u0026amp;\u0026amp; j \u0026lt; n \u0026amp;\u0026amp; S[i] == S[j]) { palin[i][j] = true; --i; ++j; } } return palin; } }; Example: Lintcode 437 Copy Books # Lintcode 437 Copy Books\n通过关键词连续可以判断是否为划分型动态规划\n这道题有段数限制\n题目分析 # 如果一个抄写员抄写第i本到第j本书，则需要时间A[i] + A[i + 1] + ... + A[j] 最后完成时间取决于耗时最长的那个抄写员 需要找到一种分段方式，分成不超过K段，使得所有段的数字之和的最大值最小 动态规划组成部分一：确定状态 # 最后一步：最优策略中最后一个抄写员Bob（设他是第K个）抄写的部分 一段连续的书，包含最后一本 如果Bob抄写第j本到第N - 1本书 则Bob需要时间A[j] + ... + A[N - 1] 需要知道前面K - 1个人最少需要多少时间抄完前j本书（第0 ~ j-1本书）(这里也是指 时间的最大值最小) 子问题 # 求K个人最短需要多少时间抄完前N本书 需要知道K - 1个人最少需要多少时间抄完前j本书 子问题 状态：设f[k][i]为前k个抄写员最少需要多少时间抄完前i本书 动态规划组成部分二：转移方程 # 设f[k][i]为k个抄写员最少需要多少时间抄完前i本书 f[k][i] = min_{i = 0, ..., i}(max(f[k - 1][j], A[j] + ... + A[i - 1]))(Note: min的下标可以到i，代表这个人根本就不抄书，有些人可能分到0本书，所以是0 到 i) 动态规划组成部分三：初始条件和边界情况 # 设f[k][i]为k个抄写员最少需要多少时间抄完前i本书\nf[k][i] = min_{j = 0, ..., i}(max(f[k - 1][j], A[j] + ... A[i - 1]))\n初始条件：\n0 个抄写员只能抄0本书 f[0][0] = 0, f[0][1] = f[0][2] = ... = f[0][N] = +inf k 个抄写员（k \u0026gt; 0）需要 0 时间抄 0 本书 f[k][0] = 0(k \u0026gt; 0) 动态规划组成部分四：计算顺序 # 计算f[0][0], f[0][1], ..., f[0][N] 计算f[1][0], f[1][1], ..., f[1][N] \u0026hellip; 计算f[K][0], f[K][1], ..., f[K][N] 答案是f[K][N] 时间复杂度O(N^2 * K) 空间复杂度O(NK)，优化后可以达到O(N) 如果K \u0026gt; N, 可以赋值K \u0026lt;- N Coding: The First Solution with DP # class Solution { public: int copyBooks(std::vector\u0026lt;int\u0026gt;\u0026amp; A, int K) { int n = A.size(); if (n == 0) { return 0; } if (K \u0026gt; n) { K = n; } int f[K + 1][n + 1]; int i, j, k, s; for (i = 1; i \u0026lt;= n; ++i) { f[0][i] = 0x3f3f3f3f; } f[0][0] = 0; // first k copier for (k = 1; k \u0026lt;= K; ++k) { f[k][0] = 0; // copy first i books for (i = 1; i \u0026lt;= n; ++i) { f[k][i] = 0x3f3f3f3f; s = 0; for (j = i; j \u0026gt;= 0; --j) { // s = A[j] + ... + A[i - 1] if (f[k - 1][j] != 0x3f3f3f3f) { f[k][i] = std::min(f[k][i], std::max(f[k - 1][j], s)); } // update s // s += A[j - 1] if (j \u0026gt; 0) { s += A[j - 1]; } } } } return f[K][n]; } }; Coding: The Second Solution with Binary Search # #include \u0026lt;numeric\u0026gt; // accumulate #include \u0026lt;algorithm\u0026gt; // max_element class Solution { public: int copyBooks(std::vector\u0026lt;int\u0026gt;\u0026amp; pages, int K) { if (pages.size() == 0) { return 0; } // int start = 0xcfcfcfcf; // maximum element of pages // int end = 0; // sum of pages // for (int i = 0; i \u0026lt; pages.size(); ++i) { // start = std::max(start, pages[end]); // i += pages[i]; // } int start = *std::max_element(pages.begin(), pages.end()); int end = std::accumulate(pages.begin(), pages.end(), 0); while (start + 1 \u0026lt; end) { int mid = start + (end - start) / 2; if (GetLeastPeople(pages, mid) \u0026lt;= K) { end = mid; } else { start = mid; } } if (GetLeastPeople(pages, start) \u0026lt;= K) { return start; } return end; } int GetLeastPeople(std::vector\u0026lt;int\u0026gt;\u0026amp; pages, int time_limit) { int count = 0; int time_cost = 0; for (auto\u0026amp; page : pages) { if (time_cost + page \u0026gt; time_limit) { ++count; time_cost = 0; } time_cost += page; } return count + 1; } }; Summary # 划分型动态规划 要求将一个序列或字符串划分成若干满足要求的片段 解决方法：最后一步-\u0026gt;最后一段 枚举最后一段的起点，然后把最后一段拿出来判断满不满足性质，然后再看前面最少的段数啊最少的时间等等 如果题目不指定段数，用f[i]表示前i个元素分段后的最值，可行性，方式数：Perfect Squares, Palindrome Partition II 如果题目指定段数，用f[i][j]表示前i个元素分成j段后的最值，可行性，方案数：Copy Books 博弈型动态规划 # 博弈为两方游戏\n一方先下，在一定规则下依次出招\n如果满足一定条件，则一方胜\n目标：取胜\n先手：先出招的一方\n出招后，先手换人，新的先手面对一个新的局面\nNote: 只记先手(为了简化状态)，当前要下棋的那个人\n只有博弈型动态规划不是从最后一步分析，而是从第一步分析 反例：如果往一个空棋盘上加石子，先到n的人先赢，那这个时候应该按最后一步来想，因为这个时候子问题从后往前会更小 博弈型动态规划基本上都是: 取数字，取石子 Example: Lintcode 394 Coins in a Line # Lintcode 394 Coins in a Line 动态规划组成部分一：确定状态 # 博弈动态规划通常从第一步分析，而不是最后一步 因为局面越来越简单，石子数越来越少 面对N个石子，先手Alice第一步可以拿1个或2个石子 这样后手Bob就面对N - 1个石子或N - 2个石子 先手Alice一定会选择能让自己赢的一步 因为双方都是采取最优策略 假如后手Bob面对N - 1个石子 其实这和一开始Bob是先手，有N - 1个石子的情况是一样的 那么Bob也会选择让自己赢的一步：取走1个或2个石子 之后Alice面对新的局面，自己成为新的先手，选择让自己赢的一步 \u0026hellip; 博弈型动态规划：必胜 vs 必败 # 怎么选择让自己赢的一步 就是走了这一步之后，对手面对剩下的石子，他必输(这里不是循环定义) 知识点：如果取1个或2个石子后，能让剩下的局面先手必败，则当前先手必胜 知识点：如果不管怎么走，剩下的局面都是先手必胜，则当前先手必败 宗旨： 必胜：在当下的局面走出一步，让对手无路可逃（即必败） 必败：自己无路可逃（即必败） 子问题 # 要求面对N个石子，是否先手必胜 需要知道面对N - 1个石子和N - 2个石子，是否先手必胜 子问题 状态：设f[i]表示面对i个石子，是否先手必胜（f[i] = TRUE / FALSE） 动态规划组成部分二：转移方程 # 设f[i]表示面对i个石子，是否先手必胜（f[i] = TRUE / FALSE）\nf[i] = true, f[i - 1] == False \u0026amp;\u0026amp; f[i - 2] == false 拿1或2个石子都必胜 true, f[i - 1] == false \u0026amp;\u0026amp; f[i - 2] == true 拿1个石子必胜 true, f[i - 1] == true \u0026amp;\u0026amp; f[i - 2] == false 拿2个石子必胜 false, f[i - 1] == true \u0026amp;\u0026amp; f[i - 2] == true 必败 Simplify: f[i] = f[i - 1] == false || f[i - 2] == false\n动态规划组成部分三：初始条件和边界情况 # 设f[i]表示面对i个石子，是否先手必胜（f[i] = TRUE / FALSE）\nf[i] = f[i - 1] == false || f[i - 2] == false\nf[0] = false 面对0个石子，先手必败\nf[1] = f[2] = true 面对1个或2个石子，先手必胜\n动态规划组成部分四：计算顺序 # f[0], f[1], f[2], ..., f[N] 如果f[N] = true则先手必胜，否则先手必败 时间复杂度O(N) 空间复杂度O(N)，可以滚动数组优化至O(1) Official Solution # class Solution { public: bool firstWillWin(int n) { if (n == 0) { return false; } std::vector\u0026lt;bool\u0026gt; f(n + 1); f[0] = false; f[1] = true; int i, j, k; for (i = 2; i \u0026lt;= n; ++i) { f[i] = f[i - 1] == false || f[i - 2] == false; } return f[n]; } }; The Second Solution with Time O(1) Space O(1) # class Solution { public: bool firstWillWin(int n) { return n % 3 != 0; } }; 背包型动态规划 # 你有一个背包，背包有最大承重 商店里有若干物品，都是免费拿 每个物品有重量和价值 目标：不撑爆背包的前提下 装下最多重量物品 装下最大总价值的物品 有多少种方式正好带走满满一书包物品 直觉 # 逐个放物品，看是否还能放入 两个关键点: 还有几个物品 还剩多少承重 Example: Lintcode 92 Backpack # Lintcode 92 Backpack\n黄金定律：背包问题中，数组大小和总承重有关\n动态规划组成部分一：确定状态 # 需要知道N个物品是否能拼出重量W（W = 0, 1, \u0026hellip;, M）\n最后一步：最后一个物品（重量A_{N - 1}是否进入背包）\n情况一：如果前N - 1个物品能拼出W，当然前N个物品也能拼出W\n情况二：如果前N - 1个物品能拼出W - A_{N - 1}，再加上最后的物品A_{N - 1}，拼出W\n例子:\n4个物品，重量为 2, 3, 5, 7\n前3个物品可以拼出重量8(即3 + 5)，自然4个物品可以拼出重量8\n前3个物品可以拼出重量2（即2），加上最后一个物品，可以拼出重量9\n子问题 # 要求前N个物品能不能拼出重量 0, 1, \u0026hellip;, M 需要知道前N - 1个物品能不能拼出重量 0, 1, \u0026hellip;, M 子问题 状态：设f[i][w] = 能否用前i个物品拼出重量w (TRUE / FALSE) 常见误区：错误 设f[i]表示前i个物品能拼出的最大重量（不超过M） 反例：A = [3 9 5 2], M = 10 错误原因：最优策略中，前N - 1个物品拼出的不一定是不超过M的最大重量 或者用黄金定律，即一定要有背包承重的维度 动态规划组成部分二：转移方程 # 设f[i][w] = 能否用前i个物品拼出重量w (TRUE / FALSE) f[i][w] = f[i - 1][w] || f[i - 1][w - A_{i - 1}] 动态规划组成部分三：初始条件和边界情况 # f[i][w] = f[i - 1][w] || f[i - 1][w - A_{i - 1}] 初始条件： f[0][0] = true : 0个物品可以拼出重量0 f[0][1 .. M] = false : 0个物品不能拼出大于0的重量 边界情况： f[i - 1][w - A_{i - 1}] 只能在w \u0026gt;= A_{i - 1}时使用\n动态规划组成部分四：计算顺序 # 初始化f[0][0], f[0][1], ..., f[0][M] 计算前 1 个物品能拼出哪些重量：f[1][0], f[1][1], ..., f[1][M] 计算前 2 个物品能拼出哪些重量：f[2][0], f[2][1], ..., f[2][M] \u0026hellip; 计算前 N 个物品能拼出哪些重量：f[N][0], f[N][1], ..., f[N][M] 时间复杂度（计算步数）：O(MN) 空间复杂度（数组大小）：优化后可以达到O(M) DP Official Solution # class Solution { public: int backPack(int m, std::vector\u0026lt;int\u0026gt;\u0026amp; A) { int n = A.size(); if (n == 0) { return 0; } bool f[n + 1][m + 1]; int i, w; // initialization for (i = 1; i \u0026lt;= m; ++i) { f[0][i] = false; } f[0][0] = true; // first i items for (i = 1; i \u0026lt;= n; ++i) { for (w = 0; w \u0026lt;= m; ++w) { // case 1: not using item_{i - 1} f[i][w] = f[i - 1][w]; // case 2: using item_{i - 1} if (w \u0026gt;= A[i - 1]) { f[i][w] = f[i][w] || f[i - 1][w - A[i - 1]]; } } } for (i = m; i \u0026gt;= 0; --i) { if (f[n][i]) { return i; } } return 0; } }; Backpack Official Solution # class Solution { public: int backPack(int m, std::vector\u0026lt;int\u0026gt;\u0026amp; A) { int n = A.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1)); for (int i = 0; i \u0026lt;= n; ++i) { for (int j = 0; j \u0026lt;= m; ++j) { if (i == 0 || j == 0) { dp[i][j] = 0; } else if (j - A[i - 1] \u0026gt;= 0) { dp[i][j] = std::max(dp[i - 1][j - A[i - 1]] + A[i - 1], dp[i - 1][j]); } else { dp[i][j] = dp[i - 1][j]; } } } return dp[n][m]; } }; Summary # 方法二： 要求不超过M时能拼出的最大重量 记录前i个物品能拼出哪些重量 前i个物品能拼出的重量： 前i - 1个物品能拼出的重量 前i - 1个物品能拼出的重量 + 第i个物品重量A_{i - 1} 如果我们的重量不是正数，而是保留的两位小数，那么我们应该怎么处理? 把重量扩大100倍进行背包，但一般仅限于小数位数比较少的时候，不然我们需要大量的空间来存储背包。 Example: Lintcode 563 Backpack V # Lintcode 563 Backpack V 动态规划组成部分一：确定状态 # 需要知道N个物品有多少种方式拼出重量W (W = 0, 1, ..., Target) 最后一步：第N个物品（重量A_{N - 1}）是否进入背包 情况一：用前N - 1个物品拼出W 情况二：用前N - 1个物品能拼出W - A_{N - 1}，再加上最后的物品A_{N - 1}，拼出W 情况一的个数 + 情况二的个数 = 用前N个物品拼出W的方式 子问题 # 要求前N个物品有多少种方式拼出重量 0, 1, \u0026hellip;, Target 需要知道前N - 1个物品有多少种方式拼出重量 0, 1, \u0026hellip;, Target 子问题 状态：设f[i][w] = 用前 i 个物品有多少种方式拼出重量w 动态规划组成部分二：转移方程 # 设f[i][w] = 用前 i 个物品有多少种方式拼出重量w f[i][w] = f[i - 1][w] + f[i - 1][w - A_{i - 1}] 动态规划组成部分三：初始条件和边界情况 # f[i][w] = f[i - 1][w] + f[i - 1][w - A_{i - 1}]\n初始条件：\nf[0][0] = 1 : 0 个物品可以有一种方式拼出重量 0 f[0][1 .. M] = 0 : 0 个物品不能拼出大于 0 的重量 边界情况：\nf[i - 1][w - A_{i - 1}]只能在w \u0026gt;= A_{i - 1}时使用 动态规划组成部分四：计算顺序 # 初始化f[0][0], f[0][1], ..., f[0][Target] 计算前 1 个物品有多少种方式拼出重量：f[1][0], f[1][1], ..., f[1][Target] \u0026hellip; 计算前 N 个物品有多少种方式拼出重量：f[N][0], f[N][1], ..., f[N][Target] 答案是f[N][Target] 时间复杂度（计算步数）：O(N * Target) 空间复杂度（数组大小）：滚动数组优化后可以达到O(Target) Official Solution # class Solution { public: int backPackV(std::vector\u0026lt;int\u0026gt;\u0026amp; A, int m) { int n = A.size(); if (n == 0) { return 0; } int f[n + 1][m + 1]; int i, w; // initialization for (i = 1; i \u0026lt;= m; ++i) { f[0][i] = 0; } f[0][0] = 1; // first i items for (i = 1; i \u0026lt;= n; ++i) { for (w = 0; w \u0026lt;= m; ++w) { // case 1: not using item_{i - 1} f[i][w] = f[i - 1][w]; // case 2: using item_{i - 1} if (w \u0026gt;= A[i - 1]) { f[i][w] = f[i][w] + f[i - 1][w - A[i - 1]]; } } } return f[n][m]; } }; 进一步空间优化 # f[i][w] = f[i - 1][w] + f[i - 1][w - A_{i - 1}] 可以只开一个数组 按照f[i][Target], ..., f[i][0]的顺序更新 class Solution { public: int backPackV(std::vector\u0026lt;int\u0026gt;\u0026amp; A, int m) { int n = A.size(); if (n == 0) { return 0; } int f[m + 1]; int i, w; // initialization for (i = 1; i \u0026lt;= m; ++i) { f[i] = 0; } f[0] = 1; // first i items for (i = 1; i \u0026lt;= n; ++i) { for (w = m; w \u0026gt;= A[i - 1]; --w) { // w doesn\u0026#39;t have to be 0 here f[w] += f[w - A[i - 1]]; } } return f[m]; } }; My Correct Solution # class Solution { public: int backPackV(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { int n = nums.size(); int dp[n + 1][target + 1]; for (int i = 0; i \u0026lt;= n; ++i) { for (int j = 0; j \u0026lt;= target; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { dp[i][j] = 1; } else if (i == 0) { dp[i][j] = 0; } else if (j - nums[i - 1] \u0026gt;= 0) { dp[i][j] = dp[i - 1][j - nums[i - 1]] + dp[i - 1][j]; } else { dp[i][j]= dp[i - 1][j]; } } } return dp[n][target]; } }; Exercise: Lintcode 564 BackPack IV (组合总和 IV) # Lintcode 564 Backpack IV (组合总和 IV)\n类似于最前面的Coin Change\n区别于BackpackV:\nHere: [5, 1, 1], [1, 1, 5] 为两种不同的方案 BackpackV: [5, 1, 1], [1, 1, 5] 只能存在一种 最后一步：背包里最后一个物品的重量是多少\nf[i] = 有多少种组合能拼出重量i\nf[i] = f[i - A_{0}] + f[i - A_{1}] + ... + f[i - A_{N - 1}]\n题目分析 # 和BackpackV唯一区别：组合中数字可以按不同的顺序，比如1 + 1 + 2与1 + 2 + 1算两种组合 不能先处理第一个物品，再处理第二个物品 似乎是更难的背包问题 其实更简单 动态规划组成部分一：确定状态 # 关注最后一步：最后一个物品的重量是多少 关键点1: 任何一个正确的组合中，所有物品总重量是Target 关键点2: 如果最后一个物品重量是K，则前面的物品重量是Target - K 如果最后一个物品重量是A_{0}，则要求有多少种组合能拼成Target - A_{0} 如果最后一个物品重量是A_{1}，则要求有多少种组合能拼成Target - A_{1} \u0026hellip; 如果最后一个物品重量是A_{N - 1}，则要求有多少种组合能拼成Target - A_{N - 1} 子问题 # 原问题要求 有多少种组合能拼成Target 子问题 设f[i] = 有多少种组合能拼出重量i 动态规划组成部分二：转移方程 # 设f[i] = 有多少种组合能拼出重量i f[i] = f[i - A_{0}] + f[i - A_{1}] + ... + f[i - A_{N - 1}] 动态规划组成部分三：初始条件和边界情况 # f[i] = f[i - A_{0}] + f[i - A_{1}] + ... + f[i - A_{N - 1}]\n出事条件：\nf[0] = 1\n有1种组合能拼出重量0（什么都不选） 边界情况：\n如果i \u0026lt; A_{j}，则对应的f[i - A_{j}]不加入f[i]\nA_{0} = 1, A_{1} = 2, A_{2} = 4 f[3] = f[3 - A_{0}] + f[3 - A_{1}] 动态规划组成部分四：计算顺序 # 设f[i] = 有多少种组合能拼出重量i f[i] = f[i - A_{0}] + f[i - A_{1}] + ... + f[i - A_{N - 1}] f[0] = 1 计算f[1], f[2], ..., f[Target] 结果为f[Target] 时间复杂度（计算步数）：O(N * Target) 空间复杂度：O(Target) Offical Solution # class Solution { public: int backPackVI(std::vector\u0026lt;int\u0026gt;\u0026amp; A, int m) { int n = A.size(); int f[m + 1]; f[0] = 1; int i, j; for (i = 1; i \u0026lt;= m; ++i) { // how many ways can we make weight // last item A[j] f[i] = 0; for (j = 0; j \u0026lt; n; ++j) { if (A[j] \u0026lt;= i) { f[i] += f[i - A[j]]; } } } return f[m]; } }; Summary # 划分型动态规划\n如果不需要段数，f[i]: 前i个元素分段的最优值，方案数，可行性 如果需要段数，f[i][k] 博弈型动态规划\n必胜 vs 必败 只考虑先手 当前状态必胜，说明当前状态至少有一种状态 可以走到 必败 当前状态必败，说明不管怎么走都到 必胜的局面 背包型动态规划\n黄金定律：背包的总承重一定要放在状态里 Exercise: Single Choice # Chapter 17: 背包型 和 区间型 动态规划 # 01 backpack # Lintcode 125 Backpack II class Solution { public: int backPackII(int m, std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;int\u0026gt;\u0026amp; V) { int n = A.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); for (int i = 0; i \u0026lt;= n; ++i) { for (int j = 0; j \u0026lt;= m; ++j) { if (i == 0 || j == 0) { dp[i][j] = 0; } else if (j - A[i - 1] \u0026gt;= 0) { dp[i][j] = std::max(dp[i - 1][j - A[i - 1]] + V[i - 1], dp[i - 1][j]); } else { dp[i][j] = dp[i - 1][j]; } } } return dp[n][m]; } }; 打印路径 # class Solution { public: int backPackII(int m, std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;int\u0026gt;\u0026amp; V) { int n = A.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); // pi has the same size with dp std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; pi(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); for (int i = 0; i \u0026lt;= n; ++i) { for (int j = 0; j \u0026lt;= m; ++j) { if (i == 0 || j == 0) { dp[i][j] = 0; } else if (j - A[i - 1] \u0026gt;= 0) { dp[i][j] = std::max(dp[i - 1][j - A[i - 1]] + V[i - 1], dp[i - 1][j]); // keep tracking whether we choose the item if (dp[i][j] == dp[i - 1][j - A[i - 1]] + V[i - 1]) { pi[i][j] = 1; } } else { dp[i][j] = dp[i - 1][j]; } } } // print path std::vector\u0026lt;bool\u0026gt; selected(n); int weight = m; for (int i = n; i \u0026gt;= 1; --i) { if (pi[i][weight] == 1) { selected[i - 1] = true; weight -= A[i - 1]; } else { selected[i - 1] = false; } } for (int i = 0; i \u0026lt; n; ++i) { if (selected[i]) { std::cout \u0026lt;\u0026lt; \u0026#34;i: \u0026#34; \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#34;A[i]: \u0026#34; \u0026lt;\u0026lt; A[i] \u0026lt;\u0026lt; \u0026#34;V[i]: \u0026#34; \u0026lt;\u0026lt; V[i] \u0026lt;\u0026lt; std::endl; } } return dp[n][m]; } }; complete backpack # Lintcode 440 backpack III class Solution { public: int backPackIII(std::vector\u0026lt;int\u0026gt;\u0026amp; A, std::vector\u0026lt;int\u0026gt;\u0026amp; V, int m) { std::vector\u0026lt;int\u0026gt; AA; std::vector\u0026lt;int\u0026gt; VV; for (int ) int n = AA.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); } }; Convert to 01backpack Two dimentional matrix One dimentinoal array multiple backpack # Convert the problem to 01-backpack 区间型动态规划 # 给定一个序列/字符串，进行一些操作 最后一步会将序列/字符串 去头/去尾 剩下的会是一个区间[i, j] 状态自然定义为f[i][j]，表示面对子序列[i, ..., j]时的最优性质 Example: Lintcode 667 Longest Palindrome Subsequence # Lintcode 667 Longest Palindrome Subsequence 动态规划组成部分一：确定状态 # 最优策略产生最长的回文子序列T，长度是M\n情况1: 回文串长度是1，即一个字母\n情况2: 回文串长度大于1，那么必定有T[0] = T[M - 1]\n设T[0]是S[i]，T[M - 1]是S[j]\nT剩下的部分T[1 ... M - 2]仍然是一个回文串，而且是S[i + 1 ... j - 1]的最长回文子序列\n子问题 # 要求S[i ... j]的最长回文子序列 如果S[i] = S[j]，需要知道S[i + 1 ... j - 1]的最长回文子序列 否则答案是S[i + 1 .. j]的最长回文子序列 或 S[i ... j - 1]的最长回文子序列 子问题 状态：设f[i][j]为S[i ... j]的最长回文子序列的长度 动态规划组成部分二：转移方程 # 设f[i][j]为S[i ... j]的最长回文子序列的长度 f[i][j] = max(f[i + 1][j], f[i][j - 1], f[i + 1][j - 1] + 2 | S[i] == S[j]) 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为S[i ... j]的最长回文子序列的长度\nf[i][j] = max(f[i + 1][j], f[i][j - 1], f[i + 1][j - 1] + 2 | S[i] == S[j]) 初始条件：\nf[0][0] = f[1][1] = ... = f[N - 1][N - 1] = 1 一个字母也是一个长度为1的回文串 如果S[i] == S[i + 1], f[i][i + 1] = 2(即相邻) 如果S[i] != S[i + 1], f[i][i + 1] = 1(即相邻) 动态规划组成部分四：计算顺序 # 设f[i][j]为S[i ... j]的最长回文子序列的长度 f[i][j] = max(f[i + 1][j], f[i][j - 1], f[i + 1][j - 1] + 2 | S[i] == S[j]) 不能按照i的顺序去算 区间动态规划：按照长度j - i从小到大的顺序去算 即for循环那个长度，不能循环i或j 长度 1 ：f[0][0], f[1][1], f[2][2], ..., f[N - 1][N - 1] 长度 2 ：f[0][1], ..., f[N - 2][N - 1] \u0026hellip; 长度 N ：f[0][N - 1] 答案是f[0][N - 1] 时间复杂度O(N^2) 空间复杂度O(N^2) Official Solution # class Solution { public: int longestPalindromeSubseq(std::string\u0026amp; S) { int n = S.size(); if (n \u0026lt;= 1) { return n; } int f[n][n]; int i, j, len; // case 1: len == 1 for (i = 0; i \u0026lt; n; ++i) { f[i][i] = 1; } // case 2: len == 2 for (i = 0; i \u0026lt; n - 1; ++i) { f[i][i + 1] = (S[i] == S[i + 1]) ? 2 : 1; } for (len = 3; len \u0026lt;= n; ++len) { // [i .. i + len - 1] // i + len - 1 \u0026lt; n ==\u0026gt; i \u0026lt; n - len + 1 ==\u0026gt; i \u0026lt;= n - len for (i = 0; i \u0026lt;= n - len; ++i) { // i 是起点 j = i + len - 1; // j 是终点 // S[i .. j], length is len // 三种情况： f[i][j] = std::max(f[i + 1][j], f[i][j - 1]); if (S[i] == S[j]) { f[i][j] = std::max(f[i][j], f[i + 1][j - 1] + 2); } } } return f[0][n - 1]; } }; 区间动态规划的初始化和计算顺序都是基于区间长度\n记忆化搜索方法 # 一种写程序的方法，不是新的算法 动态规划编程的另一个选择 f[i][j] = max(f[i + 1][j], f[i][j - 1], f[i + 1][j - 1] + 2 | S[i] == S[j]) 计算f[0][N - 1] 递归计算f[1][N - 1], f[0, N - 2], f[1][N - 2] 记忆化：计算f[i][j]结束后，将结果保存在数组f[i][j]里，下次如果需要再次计算f[i][j]，直接返回f[i][j] 两种解决动态规划的思路: top-down 记忆化搜索 bottom-up 递推 recurrence 任何动态规划的题 既可以用 记忆化搜索 又可以用 递推\n与递推方法比较 # 递推方法 自下而上（从简单到复杂）：f[0], f[1], ..., f[N] 记忆化方法 自上而下（从复杂到简单）：f[N], f[N - 1], ... 记忆化搜索编写一般比较简单 递推方法在某些条件下可以做空间优化，记忆化搜索则必须存储所有f值 Coding with Template (important) # class Solution { public: int longestPalindromeSubseq(std::string\u0026amp; ss) { s = ss; n = s.size(); if (n \u0026lt;= 1) { return n; } f.resize(n, std::vector\u0026lt;int\u0026gt;(n, 0)); // important1: clear memory int i, j; for (i = 0; i \u0026lt; n; ++i) { for (j = i; j \u0026lt; n; ++j) { f[i][j] = -1; // f[i][j] has not been computed yet } } calc(0, n - 1); return f[0][n - 1]; } private: void calc(int i, int j) { // compute f[i][j] if (f[i][j] != -1) { // important2: 递归里的第一句话就要写上: if f[i][j] has been computed, return directly return; } // simple case: caculate directly: length is 1 if (i == j) { f[i][i] = 1; return; } // simple case: caculate directly: length is 2 if (i + 1 == j) { f[i][i + 1] = (s[i] == s[i + 1]) ? 2 : 1; return; } // important3: 先在这里递归 calc(i + 1, j); // f[i + 1][j] is computed calc(i, j - 1); calc(i + 1, j - 1); f[i][j] = std::max(f[i + 1][j], f[i][j - 1]); if (s[i] == s[j]) { f[i][j] = std::max(f[i][j], f[i + 1][j - 1] + 2); } } // we need to do recursion, so we need some global variable std::string s; int n; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f; }; Example: Lintcode 396 Coins in A Line III （区间型动态规划—博弈问题） # Lintcode 396 Coins in A Line III 博弈 # 这道题是一道博弈题，目标是让自己拿到的数字之和不比对手小 设己方数字和是A，对手数字和是B，即目标是A \u0026gt;= B 等价于A - B \u0026gt;= 0 也就是说，如果Alice和Bob都存着自己的数字和与对手的数字和之差，分别记为S_A = A - B，S_B = B - A 则Alice的目标是最大化S_A，Bob的目标是最大化S_B 当一方X面对剩下的数字，可以认为X就是当前的先手，他的目标就是最大化S_X = X - Y 当他取走一个数字m后，对手Y变成先手，同理他也要最大化S_Y = Y - X important: 对于X来说，S_X = - S_Y + m 其中，m是当前这步的数字，-S_Y是对手看来的数字差取相反数（因为先手是X） 现在X有两种选择，取第一个数字m_1或最后一个数字m_2，为了最大化S_X，应该选择较大的那个S_X 动态规划组成部分一：确定状态 # 如果Alice第一步取走a[0]，Bob面对a[1 .. N - 1]\nBob的最大数字差是S_Y\nAlice的数字差是a[0] - S_Y\n如果Alice第一步取走a[N - 1]，Bob面对a[0 .. N - 2]\nBob的最大数字差是S_Y_prime\nAlice的数字差是a[N - 1] - S_Y_prime\nAlice 选择较大的数字差\n博弈子问题 # 当Bob面对a[1 .. N - 1]，他这时是先手 他的目标同样是最大化先手（自己）和后手（Alice）的数字差 但是此时的数字少了一个：a[1 .. N - 1] 子问题 状态：设f[i][j]为一方先手在面对a[i .. j]这些数字时，能得到的最大的与对手的数字差 动态规划组成部分二：转移方程 # 设f[i][j]为一方先手在面对a[i .. j]这些数字时，能得到的最大的与对手的数字差 f[i][j] = std::max(a[i] - f[i + 1][j], a[j] - f[i][j - 1]) 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为一方先手在面对a[i .. j]这些数字时，能得到的最大的与对手的数字差 f[i][j] = std::max(a[i] - f[i + 1][j], a[j] - f[i][j - 1]) 只有一个数字a[i]时，己方得a[i]分，对手0分，数字差为a[i] f[i][i] = a[i] (i = 0, 1, ..., N - 1) 动态规划组成部分四：计算顺序 # 长度1: f[0][0], f[1][1], f[2][2], ..., f[N - 1][N - 1] 长度2: f[0][1], ..., f[N - 2][N - 1] \u0026hellip; 长度N: f[0][N - 1] 如果f[0][N - 1] \u0026gt;= 0，先手Alice必赢，否则必输 时间复杂度O(N^2) 空间复杂度O(N^2) // 博弈型与区间型的结合类问题 class Solution { public: bool firstWillWin(std::vector\u0026lt;int\u0026gt;\u0026amp; values) { int n = values.size(); if (n == 0) { return true; } int f[n][n]; int i, j, len; // len == 1 for (i = 0; i \u0026lt; n; ++i) { f[i][i] = values[i]; } for (len = 2; len \u0026lt;= n; ++len) { for (i = 0; i \u0026lt;= n - len; ++i) { j = i + len - 1; // A[i ... j] f[i][j] = std::max(values[i] - f[i + 1][j], values[j] - f[i][j - 1]); } } return f[0][n - 1] \u0026gt;= 0; } }; Example: Lintcode 430 Scramble String # Lintcode 430 Scramble String\n区间有两种获得方式：\n去头去尾 二分，中间劈一刀，像这道题 动态规划组成部分一：确定状态 # 显然，T如果长度和S不一样，那么肯定不能由S变换而来 如果T是S变换而来的，并且我们知道S最上层二分被分成S = S_1 S_2，那么一定有： T也有两部分T = T_1 T_2，T_1是S_1变换而来的，T_2是S_2变换而来的 T也有两部分T = T_1 T_2，T_1是S_2变换而来的，T_2是S_1变换而来的 子问题 # 要求T是否由S变换而来 需要知道T_1是否由S_1变换而来的，T_2是否由S_2变换而来 需要知道T_1是否由S_2变换而来的，T_2是否由S_1变换而来 S_1, S_2, T_1, T_2长度更短 子问题 状态：f[i][j][k][h]表示T[k .. h]是否由S[i .. j]变换而来 动态规划组成部分一：确定状态 # 这里所有串都是S和T的子串，且长度一样 所以每个串都可以用**（起始位置， 长度）**表示 例如： S_1长度是5，在S中位置7开始 T_1长度是5，在T中位置0开始 可以用f[7][0][5] = True/False表示S_1能否通过变换成为T_1 状态：设f[i][j][k]表示S_1能否通过变换成为T_1 S_1为S从字符i开始的长度为k的子串 T_1为T从字符j开始的长度为k的子串 动态规划组成部分二：转移方程 # 状态：设f[i][j][k]表示S_1能否通过变换成为T_1 S_1为S从字符i开始的长度为k的子串 T_1为T从字符j开始的长度为k的子串 动态规划组成部分三：初始条件和边界情况 # 状态：设f[i][j][k]表示S_1能否通过变换成为T_1\nS_1为S从字符i开始的长度为k的子串 T_1为T从字符j开始的长度为k的子串 如果S[i] = T[j], f[i][j][1] = True否则f[i][j][1] = False\n动态规划组成部分四：计算顺序 # 状态：设f[i][j][k]表示S_1能否通过变换成为T_1\nS_1为S从字符i开始的长度为k的子串 T_1为T从字符j开始的长度为k的子串 按照k从小到大的顺序去计算\nf[i][j][1], 0 \u0026lt;= i \u0026lt; N, 0 \u0026lt;= j \u0026lt; N f[i][j][2], 0 \u0026lt;= i \u0026lt; N - 1, 0 \u0026lt;= j \u0026lt; N - 1 \u0026hellip; f[0][0][N] 答案是f[0][0][N]\n时间复杂度O(N^4)\n空间复杂度O(N^3)\nclass Solution { public: bool isScramble(std::string\u0026amp; S, std::string\u0026amp; T) { int m = S.size(); int n = T.size(); if (m != n) { return false; } bool f[n][n][n + 1]; int i, j, w, len; // len = 1 for (i = 0; i \u0026lt; n; ++i) { for (j = 0; j \u0026lt; n; ++j) { f[i][j][1] = (S[i] == T[j]); } } for (len = 2; len \u0026lt;= n; ++len) { for (i = 0; i \u0026lt;= n - len; ++i) { // S[i ... i+len-1] for (j = 0; j \u0026lt;= n - len; ++j) { // T[j ... j+len-1] f[i][j][len] = false; // break into S1 and S2 // S1 has length w, S2 has length len - w for (w = 1; w \u0026lt; len; ++w) { //no swap // S1--\u0026gt;T1, S2--\u0026gt;T2 if (f[i][j][w] \u0026amp;\u0026amp; f[i + w][j + w][len - w]) { f[i][j][len] = true; break; } // swap // S1--\u0026gt;T2, S2--\u0026gt;T1 if (f[i][j + len - w][w] \u0026amp;\u0026amp; f[i + w][j][len - w]) { f[i][j][len] = true; break; } } } } } return f[0][0][n]; } }; 记忆化搜索 # class Solution { public: bool isScramble(std::string\u0026amp; SS, std::string\u0026amp; TT) { S = SS; T = TT; int m = S.size(); n = T.size(); if (m != n) { return false; } int i, j, len; f.resize(n, std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt;(n, std::vector\u0026lt;bool\u0026gt;(n + 1))); done.resize(n, std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt;(n, std::vector\u0026lt;bool\u0026gt;(n + 1))); for (len = 1; len \u0026lt;= n; ++len) { for (i = 0; i \u0026lt;= n - len; ++i) { // S[i ... i+len-1] for (j = 0; j \u0026lt;= n - len; ++j) { // T[j ... j+len-1] done[i][j][len] = false; // f[i][j][len] not computed yet } } } calc(0, 0, n); return f[0][0][n]; } private: void calc(int i, int j, int len) { if (done[i][j][len]) { return; } int w; if (len == 1) { f[i][j][1] = (S[i] == T[j]); return; } // break into S1 and S2 // S1 has length w, S2 has length len - w for (w = 1; w \u0026lt; len; ++w) { //no swap // S1--\u0026gt;T1, S2--\u0026gt;T2 calc(i, j, w); calc(i + w, j + w, len - w); if (f[i][j][w] \u0026amp;\u0026amp; f[i + w][j + w][len - w]) { f[i][j][len] = true; break; } // swap // S1--\u0026gt;T2, S2--\u0026gt;T1 calc(i, j + len - w, w); calc(i + w, j, len - w); if (f[i][j + len - w][w] \u0026amp;\u0026amp; f[i + w][j][len - w]) { f[i][j][len] = true; break; } } done[i][j][len] = true; // has been computed } std::vector\u0026lt;std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt;\u0026gt; f; std::vector\u0026lt;std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt;\u0026gt; done; int n; std::string S; std::string T; }; Example: Lintcode 168 吹气球 (消去型 \u0026ndash;\u0026gt; 区间型) # Lintcode 168 吹气球\n消去型：一定要倒着想，不然状态过多\n观察最后被扎破的气球，分为左右两个区间\n设f[i][j]为扎破i+1 ~ j-1号气球，最多获得的金币数\nf[i][j] = max_{i \u0026lt; k \u0026lt; j}(f[i][k] + f[k][j] + a[i] * a[k] * a[j])\n时间复杂度O(N^3)\n空间复杂度O(N^2)\n类似题目： Lintcode 1694 Monster Hunter 动态规划组成部分一：确定状态 # 所有N个气球都被扎破 最后一步：一定有最后一个被扎破的气球，编号是i 扎破i时，左边是气球0，右边是气球N + 1，获得金币1 * a_i * 1 = a_i 此时气球1 ~ i-1以及i+1 ~ N都已经被扎破，并且已经获得对应金币 子问题 # 要求扎破1 ~ N号气球，最多获得的金币数 需要知道扎破1 ~ i - 1号气球，最多获得的金币数和扎破i+1 ~ N号气球，最多获得的金币数 子问题 状态：设f[i][j]为扎破i+1 ~ j-1号气球，最多获得的金币数 动态规划组成部分二：转移方程 # 设f[i][j]为扎破i+1 ~ j-1号气球，最多获得的金币数 i和j不能扎破 f[i][j] = max_{i \u0026lt; k \u0026lt; j}(f[i][k] + f[k][j] + a[i] * a[k] * a[j]) 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为扎破i+1 ~ j-1号气球，最多获得的金币数\ni和j不能扎破 f[i][j] = max_{i \u0026lt; k \u0026lt; j}(f[i][k] + f[k][j] + a[i] * a[k] * a[j])\n初始条件：f[0][1] = f[1][2] = ... = f[N][N + 1] = 0\n当没有气球要扎破时，最多获得0枚金币 动态规划组成部分四：计算顺序 # 设f[i][j]为扎破i+1 ~ j-1号气球，最多获得的金币数\ni和j不能扎破 f[i][j] = max_{i \u0026lt; k \u0026lt; j}(f[i][k] + f[k][j] + a[i] * a[k] * a[j])\n区间动态规划：按照长度j - i从小到大的顺序去算\nf[0][1], f[1][2], f[2][3], ..., f[N][N + 1] f[0][2], f[1][3], f[2][4], ..., f[N - 1][N + 1] \u0026hellip; f[0][N + 1] 时间复杂度O(N^3) 空间复杂度O(N^2)\nclass Solution { public: int maxCoins(std::vector\u0026lt;int\u0026gt;\u0026amp; AA) { int n = AA.size(); if (n == 0) { return 0; } int i, j, k, len; int A[n + 2]; A[0] = A[n + 1] = 1; for (i = 1; i \u0026lt;= n; ++i) { A[i] = AA[i - 1]; } n += 2; // AA: 3 2 8 7 9 // A: 1 3 2 8 7 9 1 int f[n][n]; for (i = 0; i \u0026lt; n - 1; ++i) { f[i][i + 1] = 0; } for (len = 3; len \u0026lt;= n; ++len) { for (i = 0; i \u0026lt;= n - len; ++i) { j = i + len - 1; // i ... k ... j f[i][j] = 0; for (k = i + 1; k \u0026lt; j; ++k) { f[i][j] = std::max(f[i][j], f[i][k] + f[k][j] + A[i] * A[k] * A[j]); } } } return f[0][n - 1]; // n here has added 2 before } }; Summary # 区间型动态规划 状态用区间左右端点：f[i][j] 有时需要逆向思考，从最后一个操作开始考虑，分成左右两个独立的空间 如何发现是否是区间型动态规划 去头去尾 二分: Scramble String 消去型，其实也是二分，但是一定要倒着想 Chapter 18: 石头碰撞：背包型 # Lintcode 724 最小划分\n分析也许和coding不一致，只能做参考\n使用i循环从1到石头总和 / 2大小的背包 dp[j]代表将容量为j的01背包装满是否可行，如果可行，|sum - j - j|即为目前的碰撞结果 每装满一个大小为x的01背包，都要维护|sum - 2x|的最小值 class Solution { public: int findMin(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { int n = nums.size(); if (n == 0) return 0; if (n == 1) return nums[0]; int sum = 0; for (int i = 0; i \u0026lt; n ; ++i) sum += nums[i]; std::vector\u0026lt;int\u0026gt; dp(sum / 2 + 1); dp[0] = 0; for (int i = 0; i \u0026lt; n; ++i) for (int j = sum / 2; j \u0026gt;= nums[i]; --j) dp[j] = dp[j - nums[i]] + nums[i] \u0026gt; dp[j] ? dp[j - nums[i]] + nums[i] : dp[j]; return std::abs(sum - 2 * dp[sum / 2]); } }; Chapter 19: 合并金币：区间型 # Lintcode 476 石子合并\n这道题是一道区间dp的入门题，通过理解状态转移的过程，决定循环的要素。在这题里，我们需要先枚举区间长度，再枚举起点。这就是区间dp的精髓。让我们一起来亲手做一下这道题目吧~\n我们令dp[i][j]为从 第i堆金币到第j堆金币所需的最小代价\n区间[i, j]可以从以任意的i \u0026lt;= k \u0026lt; j为分割点所得的两个子区间得来\ndp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[i][j])\nsum[i][j]表示区间[i, j]的数字和，可以使用前缀和来维护\n区间DP通用思路： 目标是求解在一个区间上的最优解，那么我把这个区间分割成一个个小区间，求解每个小区间的最优解，再合并小区间得到大区间即可 本题思路： 枚举区间长度 len 为每次分割成的小区间长度（由短到长不断合并），内层枚举该长度下的可能起点，并按照 len 计算终点。然后在这个起点终点之间枚举分割点 k ，求解这段以 i 为起点。长度为 len 的小区间在某个 k 的最优解。 class Solution { public: int stoneGame(std::vector\u0026lt;int\u0026gt;\u0026amp; A) { int n = A.size(); if (n == 0) { return 0; } std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n, std::vector\u0026lt;int\u0026gt;(n, 0)); std::vector\u0026lt;int\u0026gt; sum(n + 1, 0); // prefix sum for (int i = 1; i \u0026lt; n + 1; ++i) { sum[i] = sum[i - 1] + A[i - 1]; } for (int len = 2; len \u0026lt;= n; ++len) { for (int i = 0; i \u0026lt;= n - len; ++i) { int j = i + len - 1; dp[i][j] = 0x3f3f3f3f; int least_sum = sum[j + 1] - sum[i]; for (int k = i; k \u0026lt; j; ++k) { // 当前区间由子区间得到 dp[i][j] = std::min(dp[i][j], dp[i][k] + dp[k + 1][j] + least_sum); } } } return dp[0][n - 1]; } }; Lintcode 593 Stone Game II Lintcode 168 Burst Ballon Chapter 20: 外卖满减：01背包 # 输入\u0026amp;输出 输入：5 20 [18, 19, 17, 6, 7] 输出：23 class Solution { public: std::vector minimumPrice(int n, int X, std::vector\u0026lt;int\u0026gt; price) { int total_price = 0; for (int i = 0; i \u0026lt; n; ++i) { total_price += price[i]; } // 转化为容量为 total_price 的 01背包 // dp[i] 为 true 表示可以选择总价恰好为 i 的物品 std::vector\u0026lt;bool\u0026gt; dp(total_price + 1, false); for (int i = 0; i \u0026lt; n; ++i) { for (int j = total_price; j \u0026gt;= price[i]; --j) { dp[j] |= dp[j - price[i]]; } } // 找到 \u0026gt;= X 的最小价格 for (int i = X; i \u0026lt;= total_price; ++i) { if (dp[i]) { return i; } } return -1; } }; Exercise Lintcode 92 backpack # Lintcode 92 Backpack Exercise Lintcode 125 backpack II # Lintcode 125 Backpack Exercise Lintcode 563 backpack V # Lintcode 563 Backpack Chapter 21: 考试策略：0/0.5/1背包 # Lintcode 273 考试策略\n做一部分：(满足p[i - 1] \u0026lt;= j): part = dp[i - 1][j - p_time[i - 1]] + p_score[i - 1]\n全做完：(满足f[i - 1] \u0026lt;= j): full = dp[i - 1][j - f_time[i - 1]] + f_score[i - 1]\ndp[i][j]= max(part, full, dp[i - 1][j])\nclass Solution { public: int exam(std::vector\u0026lt;int\u0026gt;\u0026amp; p, std::vector\u0026lt;int\u0026gt;\u0026amp; part, std::vector\u0026lt;int\u0026gt;\u0026amp; f, std::vector\u0026lt;int\u0026gt;\u0026amp; full) { int n = p.size(); int m = 120; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); for (int i = 1; i \u0026lt;= n; ++i) { for (int j = 1; j \u0026lt;= m; ++j) { dp[i][j] = dp[i - 1][j]; if (j - p[i - 1] \u0026gt;= 0) { dp[i][j] = std::max(dp[i][j], dp[i - 1][j - p[i - 1]] + part[i - 1]); } if (j - f[i - 1] \u0026gt;= 0) { dp[i][j] = std::max(dp[i][j], dp[i - 1][j - f[i - 1]] + full[i - 1]); } } } return dp[n][m]; } }; // 滚动数组优化 class Solution { public: int exam(std::vector\u0026lt;int\u0026gt;\u0026amp; p, std::vector\u0026lt;int\u0026gt;\u0026amp; part, std::vector\u0026lt;int\u0026gt;\u0026amp; f, std::vector\u0026lt;int\u0026gt;\u0026amp; full) { int n = p.size(); int m = 120; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; dp(2, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); for (int i = 1; i \u0026lt;= n; ++i) { for (int j = 1; j \u0026lt;= m; ++j) { dp[i % 2][j] = dp[(i - 1) % 2][j]; if (j - p[i - 1] \u0026gt;= 0) { dp[i % 2][j] = std::max(dp[i % 2][j], dp[(i - 1) % 2][j - p[i - 1]] + part[i - 1]); } if (j - f[i - 1] \u0026gt;= 0) { dp[i % 2][j] = std::max(dp[i % 2][j], dp[(i - 1) % 2][j - f[i - 1]] + full[i - 1]); } } } return dp[n % 2][m]; } }; Exercise Lintcode 1538 卡牌游戏 II # Lintcode 1538 卡牌游戏 II Exercise Lintcode 700 杆子分割 # Lintcode 700 杆子分割 Chapter 22: 双序列动态规划 # 顾名思义，有两个序列/字符串，需要进行一些操作 每个序列本身是一维的 可以转化为二维动态规划 Example: Lintcode 77 最长公共子序列 # Lintcode 77 最长公共子序列 题目分析 # 公共子序列一定是对应的字符按顺序都相等 找到最长的对应对子，且对子连线不能相交 动态规划组成部分一：确定状态 # 设A长度是m，B长度是n\n现在我们考虑最优策略产生出的最长公共子序列（虽然还不知道是什么）\n最后一步：观察A[m - 1]和B[n - 1]这两个字符是否作为一个对子在最优策略中\n最长公共子序列也是公共子序列：长度是L -\u0026gt; 选定了L个对应的对子 最长公共子序列 情况一：对子中没有A[m - 1] 推论：A和B的最长公共子序列就是 A 前 m - 1 个字符和 B 前 n 个字符的最长公共子序列 情况二：对子中没有B[n - 1] 推论：A和B的最长公共子序列就是 A 前 m 个字符和 B 前 n - 1 个字符的最长公共子序列 情况三：对子中有 A[m - 1] - B[n - 1] 推论：A和B的最长公共子序列就是 A 前 m - 1 个字符和 B 前 n - 1 个字符的最长公共子序列 + A[m - 1] 子问题 # 要求A[0 .. m - 1]和B[0 .. n - 2]的最长公共子序列，A[0 .. m - 2]和B[0 .. n - 1]的最长公共子序列和A[0 .. m - 2]和B[0 .. n - 2]的最长公共子序列 原来是求A[0 .. m - 1]和B[0 .. n - 1]的最长公共子序列 子问题 状态：设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符[0 .. j - 1]的最长公共子序列的长度 动态规划组成部分二：转移方程 # 状态：设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符[0 .. j - 1]的最长公共子序列的长度 要求f[m][n] f[i][j] = max(f[i - 1][j], f[i][j - 1], f[i - 1][j - 1] + 1 | A[i - 1] == B[j - 1]) 动态规划组成部分三：初始条件和边界情况 # f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符[0 .. j - 1]的最长公共子序列的长度 转移方程：f[i][j] = max(f[i - 1][j], f[i][j - 1], f[i - 1][j - 1] + 1 | A[i - 1] == B[j - 1]) 初始条件：空串和任何串的最长公共子序列长度是0 f[0][j] = 0, j = 0 .. n f[i][0] = 0, i = 0 .. m 动态规划组成部分四：计算顺序 # f[0][0], f[0][1], ..., f[0][n] f[1][0], f[1][1], ..., f[1][n] \u0026hellip; f[m][0], f[m][1], ..., f[m][n] 答案是f[m][n] 时间复杂度（计算步数）O(MN) 空间复杂度（数组大小）O(MN)，可以用滚动数组优化空间至O(N) class Solution { public: int longestCommonSubsequence(std::string\u0026amp; A, std::string\u0026amp; B) { int n = A.size(); int m = B.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); for (int i = 0; i \u0026lt;= n; ++i) { for (int j = 0; j \u0026lt;= m; ++j) { if (i == 0 || j == 0) { f[i][j] = 0; continue; } f[i][j] = std::max(f[i][j - 1], f[i - 1][j]); if (A[i - 1] == B[j - 1]) { f[i][j] = std::max(f[i][j], f[i - 1][j - 1] + 1); } } } return f[n][m]; } }; 打印最长公共子序列 # class Solution { public: int longestCommonSubsequence(std::string\u0026amp; A, std::string\u0026amp; B) { int n = A.size(); int m = B.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; pi(n + 1, std::vector\u0026lt;int\u0026gt;(m + 1, 0)); for (int i = 0; i \u0026lt;= n; ++i) { for (int j = 0; j \u0026lt;= m; ++j) { if (i == 0 || j == 0) { f[i][j] = 0; continue; } f[i][j] = std::max(f[i][j - 1], f[i - 1][j]); if (f[i][j] == f[i - 1][j]) { pi[i][j] = 1; } else { pi[i][j] = 2; } if (A[i - 1] == B[j - 1]) { f[i][j] = std::max(f[i][j], f[i - 1][j - 1] + 1); if (f[i][j] == f[i - 1][j - 1] + 1) { pi[i][j] = 3; } } } } std::vector\u0026lt;char\u0026gt; routine(f[n][m]); int p = f[n][m] - 1; int i = n; int j = m; while (i \u0026gt; 0 \u0026amp;\u0026amp; j \u0026gt; 0) { if (pi[i][j] == 1) { --i; // not using A\u0026#39;s tail } else { if (pi[i][j] == 2) { --j; // not using B\u0026#39;s tail } else { routine[p] = A[i - 1]; --p; --i; --j; } } } for (p = 0; p \u0026lt; f[n][m]; ++p) { std::cout \u0026lt;\u0026lt; routine[p]; } return f[n][m]; } }; Example: Lintcode 29 交叉字符串 # Lintcode 29 交叉字符串 动态规划组成部分一：确定状态 # 首先，如果 X的长度 不等于 A的长度 + B的长度，直接输出 False 设A长度是m，B长度是n，X的长度是m + n 最后一步：假设X是由A和B交错形成的，那么X的最后一个字符X[m + n - 1] 要么是A[m - 1] 那么X[0 .. m + n - 2]是由A[0 .. m - 2]和B[0 .. n - 1]交错形成的 要么是B[n - 1] 那么X[0 .. m + n - 2]是由A[0 .. m - 1]和B[0 .. n - 2]交错形成的 子问题 # 要求X[0 .. m + n - 1]是否由A[0 .. m - 1]和B[0 .. n - 1]交错形成 需要知道X[0 .. m + n - 2]是否由A[0 .. m - 2]和B[0 .. n - 1]交错形成，以及X[0 .. m + n - 2]是否由A[0 .. m - 1]和B[0 .. n - 2]交错形成 子问题 状态：设f[s][i][j]为X前s个字符是否由A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]交错形成 但是s = i + j，所以可以简化为：设f[i][j]为X前i + j个字符是否由A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]交错形成 动态规划组成部分二：转移方程 # 设f[i][j]为X前i + j个字符是否由A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]交错形成 f[i][j] = (f[i - 1][j] \u0026amp;\u0026amp; X[i + j - 1] == A[i - 1]) || (f[i][j - 1] \u0026amp;\u0026amp; X[i + j - 1] == B[j - 1]) 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为X前i + j个字符是否由A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]交错形成 f[i][j] = (f[i - 1][j] \u0026amp;\u0026amp; X[i + j - 1] == A[i - 1]) || (f[i][j - 1] \u0026amp;\u0026amp; X[i + j - 1] == B[j - 1]) 初始条件：空串由A的空串和B的空串交错形成 -\u0026gt; f[0][0] = true 边界情况：如果i = 0，不考虑情况一；如果j = 0，不考虑情况二 动态规划组成部分四：计算顺序 # f[0][0], f[0][1], ..., f[0][m] f[1][0], f[1][1], ..., f[1][m] \u0026hellip; f[n][0], f[n][1], ..., f[n][m] 答案是f[n][m] 时间复杂度（计算步数）O(MN) 空间复杂度（数组大小）O(MN)，可以用滚动数组优化空间至O(N) class Solution { public: bool isInterleave(std::string\u0026amp; A, std::string\u0026amp; B, std::string\u0026amp; X) { int n = A.size(); int m = B.size(); if (X.size() != n + m) { return false; } std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt; f(n + 1, std::vector\u0026lt;bool\u0026gt;(m + 1, false)); int i, j; for (i = 0; i \u0026lt;= n; ++i) { for (j = 0; j \u0026lt;= m; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { f[i][j] = true; continue; } f[i][j] = false; if (i \u0026gt; 0 \u0026amp;\u0026amp; X[i + j - 1] == A[i - 1] \u0026amp;\u0026amp; f[i - 1][j]) { f[i][j] = true; } if (j \u0026gt; 0 \u0026amp;\u0026amp; X[i + j - 1] == B[j - 1] \u0026amp;\u0026amp; f[i][j - 1]) { f[i][j] = true; } } } return f[n][m]; } }; 滚动数组优化 # class Solution { public: bool isInterleave(std::string\u0026amp; A, std::string\u0026amp; B, std::string\u0026amp; X) { int n = A.size(); int m = B.size(); if (X.size() != n + m) { return false; } // first std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt; f(2, std::vector\u0026lt;bool\u0026gt;(m + 1, false)); int i, j; // second int old, now = 0; for (i = 0; i \u0026lt;= n; ++i) { // third old = now; now = 1 - now; // then change all f[i] to f[now] // change all f[i - 1] to f[old] for (j = 0; j \u0026lt;= m; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { f[now][j] = true; continue; } f[now][j] = false; if (i \u0026gt; 0 \u0026amp;\u0026amp; X[i + j - 1] == A[i - 1] \u0026amp;\u0026amp; f[old][j]) { f[now][j] = true; } if (j \u0026gt; 0 \u0026amp;\u0026amp; X[i + j - 1] == B[j - 1] \u0026amp;\u0026amp; f[now][j - 1]) { f[now][j] = true; } } } return f[now][m]; } }; Example: Lintcode 119 编辑距离 # Lintcode 119 编辑距离 最小操作次数 == 最小编辑距离 动态规划组成部分一：确定状态 # 设A长度是m，B长度是n\n全部操作完成后A的长度也是n，并且A[n - 1] = B[n - 1]\n于是最优策略（以及所有合法策略）最终都是让A的最后一个字符变成B的最后一个字符\n情况一：A在最后插入B[n - 1]\n要将A[0 .. m - 1]变成B[0 .. n - 2] 情况二：A最后一个字符替换成B[n - 1]\n要将A[0 .. m - 2]变成B[0 .. n - 2] 情况三：A删掉最后一个字符\n要将A[0 .. m - 2]变成B[0 .. n - 1] 情况四：A和B最后一个字符相等\n要将A[0 .. m - 2]变成B[0 .. n - 2] 子问题 # 要求A[0 .. m - 1]和B[0 .. n - 2]的最小编辑距离，A[0 .. m - 2]和B[0 .. n - 1]的最小编辑距离和A[0 .. m - 2]和B[0 .. n - 2]的最小编辑距离 原来是求A[0 .. m - 1]和B[0 .. n - 1]的最小编辑距离 子问题 状态：设f[i][j]为A前i个字符A[0 .. i - 1]和B的前j个字符B[0 .. j - 1]的最小编辑距离 动态规划组成部分二：转移方程 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]的最小编辑距离 要求f[m][n] f[i][j] = std::min(f[i][j - 1] + 1, f[i - 1][j - 1] + 1, f[i - 1][j] + 1, f[i - 1][j - 1] | A[i - 1] == B[j - 1]) 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]的最小编辑距离\n要求f[m][n]\nf[i][j] = std::min(f[i][j - 1] + 1, f[i - 1][j - 1] + 1, f[i - 1][j] + 1, f[i - 1][j - 1] | A[i - 1] == B[j - 1]) 初始条件：一个空串和一个长度为L的串的最小编辑距离是L\nf[0][j] = j (j = 0, 1, 2, ..., n) f[i][0] = i (j = 0, 1, 2, ..., m) 动态规划组成部分四：计算顺序 # f[0][0], f[0][1], ..., f[0][n] f[1][0], f[1][1], ..., f[1][n] \u0026hellip; f[m][0], f[m][1], ..., f[m][n] 答案是f[m][n] 时间复杂度（计算步数）O(MN) 空间复杂度（数组大小）O(MN)，可以用滚动数组优化空间至O(N) class Solution { public: int minDistance(std::string\u0026amp; A, std::string\u0026amp; B) { int m = A.size(); int n = B.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(m + 1, std::vector\u0026lt;int\u0026gt;(n + 1, 0)); int i, j; for (i = 0; i \u0026lt;= m; ++i) { for (j = 0; j \u0026lt;= n; ++j) { if (i == 0) { // insert, insert, ... f[i][j] = j; continue; } if (j == 0) { // delete, delete, ... f[i][j] = i; continue; } // insert, delete, replace f[i][j] = std::min(std::min(f[i - 1][j], f[i][j - 1]), f[i - 1][j - 1]) + 1; if (A[i - 1] == B[j - 1]) { // 情况四 f[i][j] = std::min(f[i][j], f[i - 1][j - 1]); } } } return f[m][n]; } }; 滚动数组优化 # class Solution { public: int minDistance(std::string\u0026amp; A, std::string\u0026amp; B) { int m = A.size(); int n = B.size(); // first std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(2, std::vector\u0026lt;int\u0026gt;(n + 1, 0)); int i, j; // second int old, now = 0; for (i = 0; i \u0026lt;= m; ++i) { // third old = now; now = 1 - now; for (j = 0; j \u0026lt;= n; ++j) { if (i == 0) { f[now][j] = j; continue; } if (j == 0) { f[now][j] = i; continue; } f[now][j] = std::min(std::min(f[old][j], f[now][j - 1]), f[old][j - 1]) + 1; if (A[i - 1] == B[j - 1]) { f[now][j] = std::min(f[now][j], f[old][j - 1]); } } } return f[now][n]; } }; 编辑距离的实际用途 # 比较两个字符串 显示较错：input \u0026ldquo;Chia\u0026rdquo; 然后根据最小编辑距离1 显示结果\u0026quot;China\u0026quot;，然后根据最小编辑距离2 显示结果\u0026quot;Chinaa\u0026quot;, \u0026hellip; Example: Lintcode 154 Regular Expression Matching # Lintcode 154 Regular Expression Matching 动态规划组成部分一：确定状态 # 双序列型动态规划\n设A长度是m，B长度是n\n现在我们考虑A和B如何匹配\n最后一步：关注最后的字符\n主要取决于正则表达式B中最后的字符B[n - 1]是什么\n如果B[n - 1]是一个正常字符（非.非*），则必须A[m - 1] = B[n - 1]，能否匹配取决于A[0 .. m - 2]和B[0 .. n - 2]是否匹配；否则不能匹配\n如果B[n - 1]是.，则A[m - 1]一定是和.匹配，之后能否匹配取决于A[0 .. m - 2]和B[0 .. n - 2]是否匹配\n如果B[n - 1]是*，它代表B[n - 2] = c可以重复0次或多次，它们是一个整体c*，需要考虑A[m - 1]是0个c，还是多个c中的最后一个\nA[m - 1]是0个c，能否匹配取决于A[0 .. m - 1]和B[0 .. n - 3]是否匹配 A[m - 1]是多个c中的最后一个，能否匹配取决于A[0 .. m - 2]和B[0 .. n - 1]是否匹配 这种情况必须A[m - 1] = c或者c = . 子问题 # 要求A前m个字符和B前n个字符能否匹配，需要知道A前m个字符和B前n - 1个字符，A前m - 1个字符和B前n个字符以及A前m个字符和B前n - 2个字符能否匹配 子问题 状态：设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 动态规划组成部分二：转移方程 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 f[i][j] = f[i - 1][j - 1]，如果i \u0026gt; 0，并且B[j - 1] = .或者A[i - 1] = B[j - 1] f[i][j - 2] || (f[i - 1][j] \u0026amp;\u0026amp; (B[j - 2] == . || B[j - 2] == A[i - 1]))，如果B[j - 1] = * 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 空串和空正则表达式匹配：f[0][0] = true 空正则表达式不能匹配长度\u0026gt; 0的串 f[1][0] = ... = f[m][0] = false 注意：f[0][1 .. n]也用动态规划计算，但是因为没有A[-1]，所以只能用第二种情况中的f[i][j - 2] 动态规划组成部分四：计算顺序 # f[0][0], f[0][1], ..., f[0][n] f[1][0], f[1][1], ..., f[1][n] \u0026hellip; f[m][0], f[m][1], ..., f[m][n] 答案是f[m][n] 时间复杂度（计算步数）O(MN) 空间复杂度（数组大小）O(MN)，可以用滚动数组优化空间至O(N) class Solution { public: bool isMatch(std::string\u0026amp; s, std::string\u0026amp; p) { int m = s.size(); int n = p.size(); std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt; f(m + 1, std::vector\u0026lt;bool\u0026gt;(n + 1, false)); int i, j; for (i = 0; i \u0026lt;= m; ++i) { for (j = 0; j \u0026lt;= n; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { f[i][j] = true; continue; } if (j == 0) { // i \u0026gt; 0 f[i][j] = false; continue; } f[i][j] = false; if (p[j - 1] != \u0026#39;*\u0026#39;) { if (i \u0026gt; 0 \u0026amp;\u0026amp; (p[j - 1] == \u0026#39;.\u0026#39; || p[j - 1] == s[i - 1])) { f[i][j] = f[i - 1][j - 1]; } } else { // c* // 0 c\u0026#39;s if (j \u0026gt; 1) { f[i][j] = f[i][j - 2]; } // \u0026gt;= 1 c\u0026#39;s, c: p[j - 2] if (i \u0026gt; 0 \u0026amp;\u0026amp; j \u0026gt; 1 \u0026amp;\u0026amp; (p[j - 2] == \u0026#39;.\u0026#39; || p[j - 2] == s[i - 1])) { f[i][j] = f[i][j] || f[i - 1][j]; } } } } return f[m][n]; } }; Example: Lintcode 192 Wildcard Matching # Lintcode 192 Wildcard Matching 动态规划组成部分一：确定状态 # 双序列型动态规划\n和Regular Expression Matching很类似，因为.和?作用相同，但是这题中*可以匹配0个或多个任意字符\n设A长度是m，B长度是n\n现在我们考虑A和B如何匹配\n最后一步：关注最后的字符\n主要取决于Wildcard B中最后的字符B[n - 1]是什么\n如果B[n - 1]是一个正常字符（非?非*），则必须A[m - 1] = B[n - 1]，能否匹配取决于A[0 .. m - 2]和B[0 .. n - 2]是否匹配；否则不能匹配\n如果B[n - 1]是?，则A[m - 1]一定是和?匹配，之后能否匹配取决于A[0 .. m - 2]和B[0 .. n - 2]是否匹配\n如果B[n - 1]是*，它可以匹配0个或任意多个字符，需要考虑A[m - 1]有没有被这个*匹配\nA[m - 1]不被*匹配，能否匹配取决于A[0 .. m - 1]和B[0 .. n - 2]是否匹配 A[m - 1]被*匹配，能否匹配取决于A[0 .. m - 2]和B[0 .. n - 1]是否匹配 子问题 # 要求A前m个字符和B前n个字符能否匹配，需要知道A前m - 1个字符和B前n - 1个字符，A前m个字符和B前n - 1个字符以及A前m - 1个字符和B前n个字符能否匹配 子问题 状态：设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 动态规划组成部分二：转移方程 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 f[i][j] = f[i - 1][j - 1]，如果i \u0026gt; 0，并且B[j - 1] = ?或者A[i - 1] = B[j - 1] f[i - 1][j] || f[i][j - 1]，如果B[j - 1] = * 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 空串和空Wildcard匹配：f[0][0] = true 空Wildcard不能匹配长度\u0026gt; 0的串 f[1][0] = ... = f[m][0] = false 注意：f[0][1 .. n]也用动态规划计算，但是因为没有A[-1]，所以只能用第二种情况中的f[i][j - 1] 动态规划组成部分四：计算顺序 # f[0][0], f[0][1], ..., f[0][n] f[1][0], f[1][1], ..., f[1][n] \u0026hellip; f[m][0], f[m][1], ..., f[m][n] 答案是f[m][n] 时间复杂度（计算步数）O(MN) 空间复杂度（数组大小）O(MN)，可以用滚动数组优化空间至O(N) class Solution { public: bool isMatch(std::string\u0026amp; A, std::string\u0026amp; B) { int m = A.size(); int n = B.size(); std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt; f(m + 1, std::vector\u0026lt;bool\u0026gt;(n + 1, false)); int i, j; for (i = 0; i \u0026lt;= m; ++i) { for (j = 0; j \u0026lt;= n; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { f[i][j] = true; continue; } if (j == 0) { f[i][j] = false; continue; } // j \u0026gt; 0 f[i][j] = false; if (B[j - 1] != \u0026#39;*\u0026#39;) { if (i \u0026gt; 0 \u0026amp;\u0026amp; (B[j - 1] == \u0026#39;?\u0026#39; || B[j - 1] == A[i - 1])) { f[i][j] = f[i - 1][j - 1]; } } else { // * represents 0 character f[i][j] = f[i][j - 1]; if (i \u0026gt; 0) { f[i][j] = (f[i][j] || f[i - 1][j]); } } } } return f[m][n]; } }; Example: Lintcode 668 Ones and Zeroes：双背包 # Lintcode 668 Ones and Zeroes 动态规划组成部分一：确定状态 # 最后一步：最优策略组成了最多的01串，其中有没有最后一个字符串S_{T - 1} 情况一：没有S_{T - 1} 需要知道前T - 1个01串中，用m个0和n个1最多能组成多少个01串 情况二：有S_{T - 1} 设第T - 1个01串中有a_{T - 1}个0，b_{T - 1}个1 需要知道前T - 1个01串中，用m - a_{T - 1}个0和n - b_{T - 1}个1最多能组成多少个01串 子问题 0和1的个数在变化，如何记录？ 直接放入状态 状态：设f[i][j][k]为前i个01串最多能有多少个被j个0和k个1组成 动态规划组成部分二：转移方程 # 设f[i][j][k]为前i个01串最多能有多少个被j个0和k个1组成 设S_i中有a_i个0，b_i个1 f[i][j][k] = max(f[i - 1][j][k], f[i - 1][j - a_{i - 1}][k - b_{i - 1}] + 1 | j \u0026gt;= a_{i - 1} \u0026amp;\u0026amp; k \u0026gt;= b_{i - 1}) 动态规划组成部分三：初始条件和边界情况 # 设f[i][j][k]为前i个01串最多能有多少个被j个0和k个1组成 设S_i中有a_i个0，b_i个1 f[i][j][k] = max(f[i - 1][j][k], f[i - 1][j - a_{i - 1}][k - b_{i - 1}] + 1 | j \u0026gt;= a_{i - 1} \u0026amp;\u0026amp; k \u0026gt;= b_{i - 1}) 初始条件：f[0][0 ~ m][0 ~ n] = 0 无论有多少0和1，前0个01串最多能组成0个 边界情况：f[i - 1][j - a_{i - 1}][k - b_{i - 1}] + 1必须j \u0026gt;= a_{i - 1} \u0026amp;\u0026amp; k \u0026gt;= b_{i - 1} 动态规划组成部分四：计算顺序 # f[0][0][0], f[0][0][1], ..., f[0][0][n], f[0][1][0], ..., f[0][1][n], ..., f[0][m][n] f[1][0][0], f[1][0][1], ..., f[1][0][n], f[1][1][0], ..., f[1][1][n], ..., f[1][m][n] \u0026hellip; f[T][0][0], f[T][0][1], ..., f[T][0][n], f[T][1][0], ..., f[T][1][n], ..., f[T][m][n] 答案是max(f[T][0][0], f[T][0][1], ..., f[T][m][n]) 时间复杂度：O(Tmn) 空间复杂度：O(Tmn)，可以用滚动数组优化空间至O(mn) class Solution { public: int findMaxForm(std::vector\u0026lt;std::string\u0026gt;\u0026amp; A, int m, int n) { if (A.size() == 0) { return 0; } int T = A.size(); std::vector\u0026lt;int\u0026gt; cnt0(T, 0); std::vector\u0026lt;int\u0026gt; cnt1(T, 0); int i, j, k; for (i = 0; i \u0026lt; T; ++i) { cnt0[i] = cnt1[i] = 0; std::string s = A[i]; for (j = 0; j \u0026lt; s.size(); ++j) { if (s[j] == \u0026#39;0\u0026#39;) { ++cnt0[i]; } else { ++cnt1[i]; } } } std::vector\u0026lt;std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026gt; f(T + 1, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;(m + 1, std::vector\u0026lt;int\u0026gt;(n + 1, 0))); for (i = 0; i \u0026lt;= m; ++i) { for (j = 0; j \u0026lt;= n; ++j) { f[0][i][j] = 0; } } for (i = 1; i \u0026lt;= T; ++i) { for (j = 0; j \u0026lt;= m; ++j) { for (k = 0; k \u0026lt;= n; ++k) { // j 0\u0026#39;s, k 1\u0026#39;s // do not take A[i - 1] f[i][j][k] = f[i - 1][j][k]; // take A[i - 1] if (j \u0026gt;= cnt0[i - 1] \u0026amp;\u0026amp; k \u0026gt;= cnt1[i - 1]) { f[i][j][k] = std::max(f[i][j][k], f[i - 1][j - cnt0[i - 1]][k - cnt1[i - 1]] + 1); } } } } int ans = 0; for (j = 0; j \u0026lt;= m; ++j) { for (k = 0; k \u0026lt;= n; ++k) { ans = std::max(ans, f[T][j][k]); } } return ans; } }; Example: Lintcode 118 Distinct Subsequences # Lintcode 118 Distinct Subsequences\n类似于最长公共子序列\nB在A中出现多少次 -\u0026gt; B的每个字符都要在A中出现\nB的“尾巴”是否和A的“尾巴”结成对子\n设f[i][j]为B前j个字符B[0 .. j - 1]在A前i个字符A[0 .. i - 1]中出现多少次\nf[i][j] = f[i - 1][j - 1] | A[i - 1] == B[j - 1] + f[i - 1][j]\nclass Solution { public: int numDistinct(std::string\u0026amp; s, std::string\u0026amp; t) { } }; Chapter 23: 毕业旅行 # Lintcode 816 TSP\nTSP问题（旅行商问题）是 NP 问题非常典型的代表\nTraveling salesman problem\nSolution 1: 排列型DFS # 需要开数组存储哪些位置/元素已经被访问 递归中用循环选择下一个符合条件的位置/元素 循环内： 标记访问 递归 标记未访问 分析 # DFS 相当于所有城市全排列，但是第一个城市固定是1，需要找到代价最小的路径（全排列） 需要记录当前的路径（包括上一个城市） 搜索过程中可以剪枝：当前路径长度已经 \u0026gt;= 当前最优解即退出 AC # class Solution { public: int minCost(int nn, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; costs) { // costs: [[i, j, d]] i----j cost is d n = nn; result = 0x3f3f3f3f; int i, j, x, y; g.resize(n, std::vector\u0026lt;int\u0026gt;(n, 0x3f3f3f3f)); for (i = 0; i \u0026lt; costs.size(); ++i) { x = costs[i][0] - 1; y = costs[i][1] - 1; g[x][y] = std::min(g[x][y], costs[i][2]); g[y][x] = std::min(g[y][x], costs[i][2]); } done.resize(n, false); done[0] = true; // 第0个城市搞过了 dfs(1, 0, 0); // 第1个城市; 前一个城市是0; 当前花费时间是0 return result; } private: // level is the level-th city // previous city p // current cost c void dfs(int level, int p, int c) { if (level == n) { if (c \u0026lt; result) { result = c; } return; } int i; // next city i, from p // p--\u0026gt;i must have a road for (i = 0; i \u0026lt; n; ++i) { if (!done[i] \u0026amp;\u0026amp; g[p][i] != 0x3f3f3f3f) { done[i] = true; dfs(level + 1, i, c + g[p][i]); done[i] = false; } } } int n; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; g; // g[i][j] is the cost to go from city i to j (\u0026lt;---\u0026gt; 双向) std::vector\u0026lt;bool\u0026gt; done; int result; }; Optimize: Pruning # class Solution { public: int minCost(int nn, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; costs) { n = nn; result = 0x3f3f3f3f; int i, j, x, y; g.resize(n, std::vector\u0026lt;int\u0026gt;(n, 0x3f3f3f3f)); for (i = 0; i \u0026lt; costs.size(); ++i) { x = costs[i][0] - 1; y = costs[i][1] - 1; g[x][y] = std::min(g[x][y], costs[i][2]); g[y][x] = std::min(g[y][x], costs[i][2]); } done.resize(n, false); done[0] = true; dfs(1, 0, 0); return result; } private: void dfs(int level, int p, int c) { // 1. pruning!!! if (c \u0026gt;= result) { return; } if (level == n) { // 2. pruning, remove this branch // c \u0026lt; result result = c; return; } int i; for (i = 0; i \u0026lt; n; ++i) { if (!done[i] \u0026amp;\u0026amp; g[p][i] != 0x3f3f3f3f) { done[i] = true; dfs(level + 1, i, c + g[p][i]); done[i] = false; } } } int n; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; g; // g[i][j] is the cost to go from city i to j (\u0026lt;---\u0026gt; 双向) std::vector\u0026lt;bool\u0026gt; done; int result; }; Solution 2: 状态压缩型动态规划??? # 设城市数为n，则有2^n个子集合 时间复杂度：枚举全部集合2^n，起点n，子问题n。时间复杂度为O(n^2 * 2^n) 空间复杂度：DP数组规模为n * (2^n)。空间复杂度为O(n * 2^n) class Solution { public: int minCost(int n, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; roads) { int inf = 1000000000; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; graph(n + 1, std::vector\u0026lt;int\u0026gt;(n + 1, 0)); ConstructGraph(graph, roads, n); // state_size represent the number of cities int state_size = 1 \u0026lt;\u0026lt; n; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(state_size, std::vector\u0026lt;int\u0026gt;(n + 1, 0)); for (int i = 0; i \u0026lt; state_size; i++) { for (int j = 0; j \u0026lt; n + 1; j++) { f[i][j] = inf; } } f[1][1] = 0; for (int state = 0; state \u0026lt; state_size; state++) { for (int i = 2; i \u0026lt; n + 1; i++) { if ((state \u0026amp; (1 \u0026lt;\u0026lt; (i - 1))) == 0) { continue; } int prev_state = state ^ (1 \u0026lt;\u0026lt; (i - 1)); for (int j = 1; j \u0026lt; n + 1; j++) { if ((prev_state \u0026amp; (1 \u0026lt;\u0026lt; (j - 1))) == 0) { continue; } f[state][i] = std::min(f[state][i], f[prev_state][j] + graph[j][i]); } } } int minimal_cost = inf; for (int i = 0; i \u0026lt; n + 1; i++) { minimal_cost = std::min(minimal_cost, f[state_size - 1][i]); } return minimal_cost; } private: void ConstructGraph(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; graph, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; roads, int n) { int inf = 1000000000; for (int i = 0; i \u0026lt; n + 1; i++) { for (int j = 0; j \u0026lt; n + 1; j++) { graph[i][j] = inf; } } for (int i = 0; i \u0026lt; roads.size(); i++) { int a = roads[i][0], b = roads[i][1], c = roads[i][2]; graph[a][b] = std::min(graph[a][b], c); graph[b][a] = std::min(graph[b][a], c); } } }; Chapter 24: 双色塔 # Lintcode 269 双色塔 class Solution { public: int twoColorsTower(int red, int green) { } }; Chapter 25: 编辑距离 # Example: Lintcode 119 编辑距离 # Lintcode 119 编辑距离 最小操作次数 == 最小编辑距离 动态规划组成部分一：确定状态 # 设A长度是m，B长度是n\n全部操作完成后A的长度也是n，并且A[n - 1] = B[n - 1]\n于是最优策略（以及所有合法策略）最终都是让A的最后一个字符变成B的最后一个字符\n情况一：A在最后插入B[n - 1]\n要将A[0 .. m - 1]变成B[0 .. n - 2] 情况二：A最后一个字符替换成B[n - 1]\n要将A[0 .. m - 2]变成B[0 .. n - 2] 情况三：A删掉最后一个字符\n要将A[0 .. m - 2]变成B[0 .. n - 1] 情况四：A和B最后一个字符相等\n要将A[0 .. m - 2]变成B[0 .. n - 2] 子问题 # 要求A[0 .. m - 1]和B[0 .. n - 2]的最小编辑距离，A[0 .. m - 2]和B[0 .. n - 1]的最小编辑距离和A[0 .. m - 2]和B[0 .. n - 2]的最小编辑距离 原来是求A[0 .. m - 1]和B[0 .. n - 1]的最小编辑距离 子问题 状态：设f[i][j]为A前i个字符A[0 .. i - 1]和B的前j个字符B[0 .. j - 1]的最小编辑距离 动态规划组成部分二：转移方程 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]的最小编辑距离 要求f[m][n] f[i][j] = std::min(f[i][j - 1] + 1, f[i - 1][j - 1] + 1, f[i - 1][j] + 1, f[i - 1][j - 1] | A[i - 1] == B[j - 1]) 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]的最小编辑距离\n要求f[m][n]\nf[i][j] = std::min(f[i][j - 1] + 1, f[i - 1][j - 1] + 1, f[i - 1][j] + 1, f[i - 1][j - 1] | A[i - 1] == B[j - 1]) 初始条件：一个空串和一个长度为L的串的最小编辑距离是L\nf[0][j] = j (j = 0, 1, 2, ..., n) f[i][0] = i (j = 0, 1, 2, ..., m) 动态规划组成部分四：计算顺序 # f[0][0], f[0][1], ..., f[0][n] f[1][0], f[1][1], ..., f[1][n] \u0026hellip; f[m][0], f[m][1], ..., f[m][n] 答案是f[m][n] 时间复杂度（计算步数）O(MN) 空间复杂度（数组大小）O(MN)，可以用滚动数组优化空间至O(N) class Solution { public: int minDistance(std::string\u0026amp; A, std::string\u0026amp; B) { int m = A.size(); int n = B.size(); std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(m + 1, std::vector\u0026lt;int\u0026gt;(n + 1, 0)); int i, j; for (i = 0; i \u0026lt;= m; ++i) { for (j = 0; j \u0026lt;= n; ++j) { if (i == 0) { // insert, insert, ... f[i][j] = j; continue; } if (j == 0) { // delete, delete, ... f[i][j] = i; continue; } // insert, delete, replace f[i][j] = std::min(std::min(f[i - 1][j], f[i][j - 1]), f[i - 1][j - 1]) + 1; if (A[i - 1] == B[j - 1]) { // 情况四 f[i][j] = std::min(f[i][j], f[i - 1][j - 1]); } } } return f[m][n]; } }; 滚动数组优化 # class Solution { public: int minDistance(std::string\u0026amp; A, std::string\u0026amp; B) { int m = A.size(); int n = B.size(); // first std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(2, std::vector\u0026lt;int\u0026gt;(n + 1, 0)); int i, j; // second int old, now = 0; for (i = 0; i \u0026lt;= m; ++i) { // third old = now; now = 1 - now; for (j = 0; j \u0026lt;= n; ++j) { if (i == 0) { f[now][j] = j; continue; } if (j == 0) { f[now][j] = i; continue; } f[now][j] = std::min(std::min(f[old][j], f[now][j - 1]), f[old][j - 1]) + 1; if (A[i - 1] == B[j - 1]) { f[now][j] = std::min(f[now][j], f[old][j - 1]); } } } return f[now][n]; } }; 编辑距离的实际用途 # 比较两个字符串 显示较错：input \u0026ldquo;Chia\u0026rdquo; 然后根据最小编辑距离1 显示结果\u0026quot;China\u0026quot;，然后根据最小编辑距离2 显示结果\u0026quot;Chinaa\u0026quot;, \u0026hellip; Example: Lintcode 154 Regular Expression Matching # Lintcode 154 Regular Expression Matching 动态规划组成部分一：确定状态 # 双序列型动态规划\n设A长度是m，B长度是n\n现在我们考虑A和B如何匹配\n最后一步：关注最后的字符\n主要取决于正则表达式B中最后的字符B[n - 1]是什么\n如果B[n - 1]是一个正常字符（非.非*），则必须A[m - 1] = B[n - 1]，能否匹配取决于A[0 .. m - 2]和B[0 .. n - 2]是否匹配；否则不能匹配\n如果B[n - 1]是.，则A[m - 1]一定是和.匹配，之后能否匹配取决于A[0 .. m - 2]和B[0 .. n - 2]是否匹配\n如果B[n - 1]是*，它代表B[n - 2] = c可以重复0次或多次，它们是一个整体c*，需要考虑A[m - 1]是0个c，还是多个c中的最后一个\nA[m - 1]是0个c，能否匹配取决于A[0 .. m - 1]和B[0 .. n - 3]是否匹配 A[m - 1]是多个c中的最后一个，能否匹配取决于A[0 .. m - 2]和B[0 .. n - 1]是否匹配 这种情况必须A[m - 1] = c或者c = . 子问题 # 要求A前m个字符和B前n个字符能否匹配，需要知道A前m个字符和B前n - 1个字符，A前m - 1个字符和B前n个字符以及A前m个字符和B前n - 2个字符能否匹配 子问题 状态：设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 动态规划组成部分二：转移方程 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 f[i][j] = f[i - 1][j - 1]，如果i \u0026gt; 0，并且B[j - 1] = .或者A[i - 1] = B[j - 1] f[i][j - 2] || (f[i - 1][j] \u0026amp;\u0026amp; (B[j - 2] == . || B[j - 2] == A[i - 1]))，如果B[j - 1] = * 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 空串和空正则表达式匹配：f[0][0] = true 空正则表达式不能匹配长度\u0026gt; 0的串 f[1][0] = ... = f[m][0] = false 注意：f[0][1 .. n]也用动态规划计算，但是因为没有A[-1]，所以只能用第二种情况中的f[i][j - 2] 动态规划组成部分四：计算顺序 # f[0][0], f[0][1], ..., f[0][n] f[1][0], f[1][1], ..., f[1][n] \u0026hellip; f[m][0], f[m][1], ..., f[m][n] 答案是f[m][n] 时间复杂度（计算步数）O(MN) 空间复杂度（数组大小）O(MN)，可以用滚动数组优化空间至O(N) class Solution { public: bool isMatch(std::string\u0026amp; s, std::string\u0026amp; p) { int m = s.size(); int n = p.size(); std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt; f(m + 1, std::vector\u0026lt;bool\u0026gt;(n + 1, false)); int i, j; for (i = 0; i \u0026lt;= m; ++i) { for (j = 0; j \u0026lt;= n; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { f[i][j] = true; continue; } if (j == 0) { // i \u0026gt; 0 f[i][j] = false; continue; } f[i][j] = false; if (p[j - 1] != \u0026#39;*\u0026#39;) { if (i \u0026gt; 0 \u0026amp;\u0026amp; (p[j - 1] == \u0026#39;.\u0026#39; || p[j - 1] == s[i - 1])) { f[i][j] = f[i - 1][j - 1]; } } else { // c* // 0 c\u0026#39;s if (j \u0026gt; 1) { f[i][j] = f[i][j - 2]; } // \u0026gt;= 1 c\u0026#39;s, c: p[j - 2] if (i \u0026gt; 0 \u0026amp;\u0026amp; j \u0026gt; 1 \u0026amp;\u0026amp; (p[j - 2] == \u0026#39;.\u0026#39; || p[j - 2] == s[i - 1])) { f[i][j] = f[i][j] || f[i - 1][j]; } } } } return f[m][n]; } }; Example: Lintcode 192 Wildcard Matching # Lintcode 192 Wildcard Matching 动态规划组成部分一：确定状态 # 双序列型动态规划\n和Regular Expression Matching很类似，因为.和?作用相同，但是这题中*可以匹配0个或多个任意字符\n设A长度是m，B长度是n\n现在我们考虑A和B如何匹配\n最后一步：关注最后的字符\n主要取决于Wildcard B中最后的字符B[n - 1]是什么\n如果B[n - 1]是一个正常字符（非?非*），则必须A[m - 1] = B[n - 1]，能否匹配取决于A[0 .. m - 2]和B[0 .. n - 2]是否匹配；否则不能匹配\n如果B[n - 1]是?，则A[m - 1]一定是和?匹配，之后能否匹配取决于A[0 .. m - 2]和B[0 .. n - 2]是否匹配\n如果B[n - 1]是*，它可以匹配0个或任意多个字符，需要考虑A[m - 1]有没有被这个*匹配\nA[m - 1]不被*匹配，能否匹配取决于A[0 .. m - 1]和B[0 .. n - 2]是否匹配 A[m - 1]被*匹配，能否匹配取决于A[0 .. m - 2]和B[0 .. n - 1]是否匹配 子问题 # 要求A前m个字符和B前n个字符能否匹配，需要知道A前m - 1个字符和B前n - 1个字符，A前m个字符和B前n - 1个字符以及A前m - 1个字符和B前n个字符能否匹配 子问题 状态：设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 动态规划组成部分二：转移方程 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 f[i][j] = f[i - 1][j - 1]，如果i \u0026gt; 0，并且B[j - 1] = ?或者A[i - 1] = B[j - 1] f[i - 1][j] || f[i][j - 1]，如果B[j - 1] = * 动态规划组成部分三：初始条件和边界情况 # 设f[i][j]为A前i个字符A[0 .. i - 1]和B前j个字符B[0 .. j - 1]能否匹配 空串和空Wildcard匹配：f[0][0] = true 空Wildcard不能匹配长度\u0026gt; 0的串 f[1][0] = ... = f[m][0] = false 注意：f[0][1 .. n]也用动态规划计算，但是因为没有A[-1]，所以只能用第二种情况中的f[i][j - 1] 动态规划组成部分四：计算顺序 # f[0][0], f[0][1], ..., f[0][n] f[1][0], f[1][1], ..., f[1][n] \u0026hellip; f[m][0], f[m][1], ..., f[m][n] 答案是f[m][n] 时间复杂度（计算步数）O(MN) 空间复杂度（数组大小）O(MN)，可以用滚动数组优化空间至O(N) class Solution { public: bool isMatch(std::string\u0026amp; A, std::string\u0026amp; B) { int m = A.size(); int n = B.size(); std::vector\u0026lt;std::vector\u0026lt;bool\u0026gt;\u0026gt; f(m + 1, std::vector\u0026lt;bool\u0026gt;(n + 1, false)); int i, j; for (i = 0; i \u0026lt;= m; ++i) { for (j = 0; j \u0026lt;= n; ++j) { if (i == 0 \u0026amp;\u0026amp; j == 0) { f[i][j] = true; continue; } if (j == 0) { f[i][j] = false; continue; } // j \u0026gt; 0 f[i][j] = false; if (B[j - 1] != \u0026#39;*\u0026#39;) { if (i \u0026gt; 0 \u0026amp;\u0026amp; (B[j - 1] == \u0026#39;?\u0026#39; || B[j - 1] == A[i - 1])) { f[i][j] = f[i - 1][j - 1]; } } else { // * represents 0 character f[i][j] = f[i][j - 1]; if (i \u0026gt; 0) { f[i][j] = (f[i][j] || f[i - 1][j]); } } } } return f[m][n]; } }; Example: Lintcode 668 Ones and Zeroes：双背包 # Lintcode 668 Ones and Zeroes 动态规划组成部分一：确定状态 # 最后一步：最优策略组成了最多的01串，其中有没有最后一个字符串S_{T - 1} 情况一：没有S_{T - 1} 需要知道前T - 1个01串中，用m个0和n个1最多能组成多少个01串 情况二：有S_{T - 1} 设第T - 1个01串中有a_{T - 1}个0，b_{T - 1}个1 需要知道前T - 1个01串中，用m - a_{T - 1}个0和n - b_{T - 1}个1最多能组成多少个01串 子问题 0和1的个数在变化，如何记录？ 直接放入状态 状态：设f[i][j][k]为前i个01串最多能有多少个被j个0和k个1组成 动态规划组成部分二：转移方程 # 设f[i][j][k]为前i个01串最多能有多少个被j个0和k个1组成 设S_i中有a_i个0，b_i个1 f[i][j][k] = max(f[i - 1][j][k], f[i - 1][j - a_{i - 1}][k - b_{i - 1}] + 1 | j \u0026gt;= a_{i - 1} \u0026amp;\u0026amp; k \u0026gt;= b_{i - 1}) 动态规划组成部分三：初始条件和边界情况 # 设f[i][j][k]为前i个01串最多能有多少个被j个0和k个1组成 设S_i中有a_i个0，b_i个1 f[i][j][k] = max(f[i - 1][j][k], f[i - 1][j - a_{i - 1}][k - b_{i - 1}] + 1 | j \u0026gt;= a_{i - 1} \u0026amp;\u0026amp; k \u0026gt;= b_{i - 1}) 初始条件：f[0][0 ~ m][0 ~ n] = 0 无论有多少0和1，前0个01串最多能组成0个 边界情况：f[i - 1][j - a_{i - 1}][k - b_{i - 1}] + 1必须j \u0026gt;= a_{i - 1} \u0026amp;\u0026amp; k \u0026gt;= b_{i - 1} 动态规划组成部分四：计算顺序 # f[0][0][0], f[0][0][1], ..., f[0][0][n], f[0][1][0], ..., f[0][1][n], ..., f[0][m][n] f[1][0][0], f[1][0][1], ..., f[1][0][n], f[1][1][0], ..., f[1][1][n], ..., f[1][m][n] \u0026hellip; f[T][0][0], f[T][0][1], ..., f[T][0][n], f[T][1][0], ..., f[T][1][n], ..., f[T][m][n] 答案是max(f[T][0][0], f[T][0][1], ..., f[T][m][n]) 时间复杂度：O(Tmn) 空间复杂度：O(Tmn)，可以用滚动数组优化空间至O(mn) class Solution { public: int findMaxForm(std::vector\u0026lt;std::string\u0026gt;\u0026amp; A, int m, int n) { if (A.size() == 0) { return 0; } int T = A.size(); std::vector\u0026lt;int\u0026gt; cnt0(T, 0); std::vector\u0026lt;int\u0026gt; cnt1(T, 0); int i, j, k; for (i = 0; i \u0026lt; T; ++i) { cnt0[i] = cnt1[i] = 0; std::string s = A[i]; for (j = 0; j \u0026lt; s.size(); ++j) { if (s[j] == \u0026#39;0\u0026#39;) { ++cnt0[i]; } else { ++cnt1[i]; } } } std::vector\u0026lt;std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026gt; f(T + 1, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;(m + 1, std::vector\u0026lt;int\u0026gt;(n + 1, 0))); for (i = 0; i \u0026lt;= m; ++i) { for (j = 0; j \u0026lt;= n; ++j) { f[0][i][j] = 0; } } for (i = 1; i \u0026lt;= T; ++i) { for (j = 0; j \u0026lt;= m; ++j) { for (k = 0; k \u0026lt;= n; ++k) { // j 0\u0026#39;s, k 1\u0026#39;s // do not take A[i - 1] f[i][j][k] = f[i - 1][j][k]; // take A[i - 1] if (j \u0026gt;= cnt0[i - 1] \u0026amp;\u0026amp; k \u0026gt;= cnt1[i - 1]) { f[i][j][k] = std::max(f[i][j][k], f[i - 1][j - cnt0[i - 1]][k - cnt1[i - 1]] + 1); } } } } int ans = 0; for (j = 0; j \u0026lt;= m; ++j) { for (k = 0; k \u0026lt;= n; ++k) { ans = std::max(ans, f[T][j][k]); } } return ans; } }; Example: Lintcode 118 Distinct Subsequences # Lintcode 118 Distinct Subsequences\n类似于最长公共子序列\nB在A中出现多少次 -\u0026gt; B的每个字符都要在A中出现\nB的“尾巴”是否和A的“尾巴”结成对子\n设f[i][j]为B前j个字符B[0 .. j - 1]在A前i个字符A[0 .. i - 1]中出现多少次\nf[i][j] = f[i - 1][j - 1] | A[i - 1] == B[j - 1] + f[i - 1][j]\nclass Solution { public: int numDistinct(std::string\u0026amp; s, std::string\u0026amp; t) { } }; Chapter 23: 毕业旅行 # Lintcode 816 TSP\nTSP问题（旅行商问题）是 NP 问题非常典型的代表\nTraveling salesman problem\nSolution 1: 排列型DFS # 需要开数组存储哪些位置/元素已经被访问 递归中用循环选择下一个符合条件的位置/元素 循环内： 标记访问 递归 标记未访问 分析 # DFS 相当于所有城市全排列，但是第一个城市固定是1，需要找到代价最小的路径（全排列） 需要记录当前的路径（包括上一个城市） 搜索过程中可以剪枝：当前路径长度已经 \u0026gt;= 当前最优解即退出 AC # class Solution { public: int minCost(int nn, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; costs) { // costs: [[i, j, d]] i----j cost is d n = nn; result = 0x3f3f3f3f; int i, j, x, y; g.resize(n, std::vector\u0026lt;int\u0026gt;(n, 0x3f3f3f3f)); for (i = 0; i \u0026lt; costs.size(); ++i) { x = costs[i][0] - 1; y = costs[i][1] - 1; g[x][y] = std::min(g[x][y], costs[i][2]); g[y][x] = std::min(g[y][x], costs[i][2]); } done.resize(n, false); done[0] = true; // 第0个城市搞过了 dfs(1, 0, 0); // 第1个城市; 前一个城市是0; 当前花费时间是0 return result; } private: // level is the level-th city // previous city p // current cost c void dfs(int level, int p, int c) { if (level == n) { if (c \u0026lt; result) { result = c; } return; } int i; // next city i, from p // p--\u0026gt;i must have a road for (i = 0; i \u0026lt; n; ++i) { if (!done[i] \u0026amp;\u0026amp; g[p][i] != 0x3f3f3f3f) { done[i] = true; dfs(level + 1, i, c + g[p][i]); done[i] = false; } } } int n; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; g; // g[i][j] is the cost to go from city i to j (\u0026lt;---\u0026gt; 双向) std::vector\u0026lt;bool\u0026gt; done; int result; }; Optimize: Pruning # class Solution { public: int minCost(int nn, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; costs) { n = nn; result = 0x3f3f3f3f; int i, j, x, y; g.resize(n, std::vector\u0026lt;int\u0026gt;(n, 0x3f3f3f3f)); for (i = 0; i \u0026lt; costs.size(); ++i) { x = costs[i][0] - 1; y = costs[i][1] - 1; g[x][y] = std::min(g[x][y], costs[i][2]); g[y][x] = std::min(g[y][x], costs[i][2]); } done.resize(n, false); done[0] = true; dfs(1, 0, 0); return result; } private: void dfs(int level, int p, int c) { // 1. pruning!!! if (c \u0026gt;= result) { return; } if (level == n) { // 2. pruning, remove this branch // c \u0026lt; result result = c; return; } int i; for (i = 0; i \u0026lt; n; ++i) { if (!done[i] \u0026amp;\u0026amp; g[p][i] != 0x3f3f3f3f) { done[i] = true; dfs(level + 1, i, c + g[p][i]); done[i] = false; } } } int n; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; g; // g[i][j] is the cost to go from city i to j (\u0026lt;---\u0026gt; 双向) std::vector\u0026lt;bool\u0026gt; done; int result; }; Solution 2: 状态压缩型动态规划??? # 设城市数为n，则有2^n个子集合 时间复杂度：枚举全部集合2^n，起点n，子问题n。时间复杂度为O(n^2 * 2^n) 空间复杂度：DP数组规模为n * (2^n)。空间复杂度为O(n * 2^n) class Solution { public: int minCost(int n, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; roads) { int inf = 1000000000; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; graph(n + 1, std::vector\u0026lt;int\u0026gt;(n + 1, 0)); ConstructGraph(graph, roads, n); // state_size represent the number of cities int state_size = 1 \u0026lt;\u0026lt; n; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; f(state_size, std::vector\u0026lt;int\u0026gt;(n + 1, 0)); for (int i = 0; i \u0026lt; state_size; i++) { for (int j = 0; j \u0026lt; n + 1; j++) { f[i][j] = inf; } } f[1][1] = 0; for (int state = 0; state \u0026lt; state_size; state++) { for (int i = 2; i \u0026lt; n + 1; i++) { if ((state \u0026amp; (1 \u0026lt;\u0026lt; (i - 1))) == 0) { continue; } int prev_state = state ^ (1 \u0026lt;\u0026lt; (i - 1)); for (int j = 1; j \u0026lt; n + 1; j++) { if ((prev_state \u0026amp; (1 \u0026lt;\u0026lt; (j - 1))) == 0) { continue; } f[state][i] = std::min(f[state][i], f[prev_state][j] + graph[j][i]); } } } int minimal_cost = inf; for (int i = 0; i \u0026lt; n + 1; i++) { minimal_cost = std::min(minimal_cost, f[state_size - 1][i]); } return minimal_cost; } private: void ConstructGraph(std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; graph, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; roads, int n) { int inf = 1000000000; for (int i = 0; i \u0026lt; n + 1; i++) { for (int j = 0; j \u0026lt; n + 1; j++) { graph[i][j] = inf; } } for (int i = 0; i \u0026lt; roads.size(); i++) { int a = roads[i][0], b = roads[i][1], c = roads[i][2]; graph[a][b] = std::min(graph[a][b], c); graph[b][a] = std::min(graph[b][a], c); } } }; Chapter 24: 双色塔 # Lintcode 269 双色塔\n求方案总数和可行性的问题 很有 可能是动态规划，求最大最小值其次\n求方案总数的问题 99% 都是动态规划\nclass Solution { public: int twoColorsTower(int red, int green) { } }; Chapter 25: 编辑距离 # Click on this link (Really Important) The most relevant problem # LCS: Longest common subsequences LIS: Longest increasing subsequences Relevant Problems # Lintcode 640 一次编辑距离 Lintcode 623 K步编辑 Chapter 26: 动态规划难题专场 # Lintcode 752 Rogue Knight Sven # Others Note # 动态规划的题型 # 坐标型：Triangle, Unique Paths, Jump Game\n前缀型：\n匹配型：Longest Increasing Subsequence, Wildcard Matching 划分型：Word Break 区间型：Stone Game\n背包型：Backpack series\n博弈型：Coins in a Line\n状态压缩型：Traveling Salesman Problem\n树型：Binary Tree Maximum Path Sum\n图型：（面试基本没考过）\n常见DP类型：\n坐标型（20%）\n序列型（20%）\n划分型（20%）\n区间型（15%）\n背包型（10%）\n博弈型（5%）\n最长序列型（5%）\n综合型（5%）\nleft: 清华动态规划专题课\n美团：最长公共子序列 微软：最长上升子序列 阿里巴巴、腾讯：最长回文子串 猿辅导：零钱兑换II 第一章 动态规划了入门 第二章 动态规划初探+坐标型动态规划+位操作型动态规划 第九章 序列型动态规划 第十六章 划分型，博弈型和背包型动态规划 第十七章 背包型动态规划和区间型动态规划 第二十二章 双序列动态规划 right: 国内大厂高频动规题详解\n简历: 最好一页 # 清楚简洁，简明扼要，但不要太花里胡哨。不要有错字，错词或者语法错误 Project \u0026amp; Experience: 一定要和申请的职位相关 写清楚Contribution。避免写成产品介绍，主要写我做了什么，必要的时候写用了什么工具。e.g. I drove \u0026hellip;, drove \u0026hellip;, design \u0026hellip; 可以放一些数字: reduce pipline time by 51.7% 选最喜欢，最擅长，贡献最大的三个projects 倒背如流细节 How to use heap in c++ # #include \u0026lt;iostream\u0026gt; #include \u0026lt;set\u0026gt; #define assertm(exp, msg) assert(((void)msg, exp)) #define print(input) for (auto\u0026amp; elem : input) std::cout \u0026lt;\u0026lt; elem \u0026lt;\u0026lt; std::endl int main() { auto cmp = [](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a, const std::pair\u0026lt;int, int\u0026gt;\u0026amp; b) {return a.second \u0026lt; b.second;}; std::set\u0026lt;std::pair\u0026lt;int, int\u0026gt;, decltype(cmp)\u0026gt; heap; heap.insert(std::make_pair(1, 3)); heap.insert(std::make_pair(31, 1)); heap.insert(std::make_pair(4, 4)); heap.insert(std::make_pair(2, 2)); heap.insert(std::make_pair(5, 5)); auto it = heap.begin(); it = std::next(it, 2); it = std::prev(it, 1); std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;size: \u0026#34; \u0026lt;\u0026lt; heap.size() \u0026lt;\u0026lt; std::endl; int index = 31; auto it2 = std::find_if(heap.begin(), heap.end(), [\u0026amp;index](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a) {return a.first == index;}); heap.erase(it2); it = heap.begin(); std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;size: \u0026#34; \u0026lt;\u0026lt; heap.size() \u0026lt;\u0026lt; std::endl; return 0; } Others Note # 动态规划的题型 # 坐标型：Triangle, Unique Paths, Jump Game\n前缀型：\n匹配型：Longest Increasing Subsequence, Wildcard Matching 划分型：Word Break 区间型：Stone Game\n背包型：Backpack series\n博弈型：Coins in a Line\n状态压缩型：Traveling Salesman Problem\n树型：Binary Tree Maximum Path Sum\n图型：（面试基本没考过）\n常见DP类型：\n坐标型（20%） 序列型（20%） 划分型（20%） 区间型（15%） 背包型（10%） 最长序列型（5%） 博弈型（5%） 综合型（5%） left: 清华动态规划专题课\n美团：最长公共子序列 微软：最长上升子序列 阿里巴巴、腾讯：最长回文子串 猿辅导：零钱兑换II 第一章 动态规划了入门 第二章 动态规划初探+坐标型动态规划+位操作型动态规划 第九章 序列型动态规划 第十六章 划分型，博弈型和背包型动态规划 第十七章 背包型动态规划和区间型动态规划 第二十二章 双序列动态规划 right: 国内大厂高频动规题详解\n简历: 最好一页 # 清楚简洁，简明扼要，但不要太花里胡哨。不要有错字，错词或者语法错误 Project \u0026amp; Experience: 一定要和申请的职位相关 写清楚Contribution。避免写成产品介绍，主要写我做了什么，必要的时候写用了什么工具。e.g. I drove \u0026hellip;, drove \u0026hellip;, design \u0026hellip; 可以放一些数字: reduce pipline time by 51.7% 选最喜欢，最擅长，贡献最大的三个projects 倒背如流细节 How to use heap in c++ # #include \u0026lt;iostream\u0026gt; #include \u0026lt;set\u0026gt; #define assertm(exp, msg) assert(((void)msg, exp)) #define print(input) for (auto\u0026amp; elem : input) std::cout \u0026lt;\u0026lt; elem \u0026lt;\u0026lt; std::endl int main() { auto cmp = [](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a, const std::pair\u0026lt;int, int\u0026gt;\u0026amp; b) {return a.second \u0026lt; b.second;}; std::set\u0026lt;std::pair\u0026lt;int, int\u0026gt;, decltype(cmp)\u0026gt; heap; heap.insert(std::make_pair(1, 3)); heap.insert(std::make_pair(31, 1)); heap.insert(std::make_pair(4, 4)); heap.insert(std::make_pair(2, 2)); heap.insert(std::make_pair(5, 5)); auto it = heap.begin(); it = std::next(it, 2); it = std::prev(it, 1); std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;size: \u0026#34; \u0026lt;\u0026lt; heap.size() \u0026lt;\u0026lt; std::endl; int index = 31; auto it2 = std::find_if(heap.begin(), heap.end(), [\u0026amp;index](const std::pair\u0026lt;int, int\u0026gt;\u0026amp; a) {return a.first == index;}); heap.erase(it2); it = heap.begin(); std::cout \u0026lt;\u0026lt; it-\u0026gt;first \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; it-\u0026gt;second \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;size: \u0026#34; \u0026lt;\u0026lt; heap.size() \u0026lt;\u0026lt; std::endl; return 0; }","date":"15 December 2022","externalUrl":null,"permalink":"/blog/dynamic_programming/","section":"Blog","summary":"Covered all topics of Dynamic Programming\n","title":"Dynamic Programming","type":"blog"},{"content":"Covered all topics of Recursion\nChapter 1: 参数传递和递归 Chapter 2: 单向递归\u0026ndash;递归vs循环 二阶阶乘 普通递归 普通递归 \u0026ndash;\u0026gt; 尾递归 尾递归 \u0026ndash;\u0026gt; 迭代 颠倒二进制位 普通递归 普通递归 \u0026ndash;\u0026gt; 尾递归 尾递归 \u0026ndash;\u0026gt; 迭代 Exercise: 寻找最大值 普通递归 普通递归 \u0026ndash;\u0026gt; 尾递归 尾递归 \u0026ndash;\u0026gt; 迭代 Chapter 3: 单向递归\u0026ndash;递归的妙用 两两交换链表中的节点 递归的方式 迭代的方式 经典二分查找问题 普通写法的递归方式: Time Limit Exceeded 二分查找的递归方式 快速幂 普通写法的递归方式: 递归求解a^n 递归求解(a^n) % b: Fail 快速幂的递归方式: Pass 快速幂的迭代形式: Pass Exercise: 14. 二分查找 classic method recursion method recursion method version 2 Exercise: 458. 目标最后位置 classic method recursion method recursion method version 2 In summary: Chapter 4: 双向递归\u0026ndash;二叉树的遍历与递归树 二叉树的深度优先遍历 递归，二叉树的遍历： 斐波那契数列 memorization of fibonacci 汉诺塔 Exercise: 1300. 巴什博弈 memorization optimization 递归的核心思想：由大化小：Best Solution In summary Chapter 5: 双向递归\u0026ndash;二叉树的分治 分治法 vs 递归 适合分治法的数据结构 二叉树上分治模版(template of Divide and Conquer for Binary Tree) 二叉树的最大深度 最大二叉树 通过遍历序确定二叉树(important) 前序遍历和中序遍历树构造二叉树 中序遍历和后序遍历树构造二叉树 前序遍历和后序遍历树构造二叉树 Chapter 6: 多向递归\u0026ndash;组合类问题 子集 1. 二叉树遍历解法 2. 组合数解法 子集II: ???? 1. 二叉树遍历解法 2. 组合数解法 数字组合 1. 二叉树遍历解法 2. 组合数解法 Exercise: 1208. 目标和 1. Solution Wrong answer: counter will always be 0 Approach 1.1 for python: Time Limit Exceeded Approach 1.2 for python: Time Limit Exceeded Approach 1.3 for python: Time Limit Exceeded 2. 组合数解法 Chapter 7: 多向递归\u0026ndash;排列类问题 全排列 排列问题递归树 带重复元素的排列 如何从全排列问题转化过来 第k个排列 求解第k个排列 求解一个排列是第几个排列 下一个排列（非递归） Exercise: 990. 美丽排列 Chapter 8: 非递归\u0026ndash;二叉树类 递归改非递归 用栈实现二叉树非递归遍历 前序遍历 中序遍历 后序遍历 用Morris算法实现二叉树非递归遍历 前序遍历 中序遍历 后序遍历 先右子树再左子树的前序遍历 VS 后序遍历 如何通过Morris解决后序遍历：先将问题转化成异样的前序遍历，然后再翻转Morris的结果 两种解法对比 Exercise: 169. 汉诺塔 Chapter 9: 非递归\u0026ndash;排列组合类 组合类问题非递归（三种解法） 二叉树遍历解法 组合数思路解法 二进制枚举解法 排列类问题非递归 用手写栈模拟递归解法 下一个排列解法 Chapter 1: 参数传递和递归 # 栈空间一般用于存放对象的引用，值类型变量和函数调用信息，堆空间才是用于存放对象本身的\n. and [] 修改的是对象本身，不是引用\n递归的三要素：Recursion\n递归的定义（代表什么含义，接受什么参数，返回什么值） 递归的拆解（把大问题拆成小问题) 递归的出口（到什么时候结束） # 1. 递归的定义 def print_n(n): # 3. 递归的出口 if n \u0026lt; 1: return # 2. 递归的拆解 print_n(n - 1) print(n) 时间复杂度： 迭代O(n), 递归O(n)\n空间复杂度： 迭代O(1), 递归O(n)\n内存中的堆和栈：\n堆空间： - 存放new得到的对象\n- 无限制（剩余内存的大小） 栈空间： - 存放对象的引用\n- 值类型变量\n- C++函数中的数组\n- 有限制，一般很小，MB量级\n- 函数调用栈 递归需谨慎：\n递归调用容易爆栈 人为调用栈不会爆栈 除非C/C++的函数中定义大数组——危险行为 import sys limit = sys.getrecursionlimit() sys.setrecursionlimit(...) Chapter 2: 单向递归\u0026ndash;递归vs循环 # 二阶阶乘 # Lintcode 771 普通递归 # 递归的定义 doubleFactorial(n) 接收一个正整数n 返回n的二阶阶乘 递归的拆解 求解doubleFactorial(n - 2) 在doubleFactorial(n - 2)的基础上乘n 递归的出口 doubleFactorial(1) = 1 doubleFactorial(2) = 2 class Solution { public: long long doubleFactorial(int n) { if (n \u0026lt;= 2) { return n; } long long temp = doubleFactorial(n - 2); return n * temp; } }; class Solution: def double_factorial(self, n: int) -\u0026gt; int: if n \u0026lt;= 2: return n temp = self.double_factorial(n - 2) return n * temp class Solution { public: long long doubleFactorial(int n) { if (n \u0026lt;= 2) { return n; } return n * doubleFactorial(n - 2); } }; class Solution: def double_factorial(self, n: int) -\u0026gt; int: if n \u0026lt;= 2: return n return n * self.double_factorial(n - 2) 普通递归 \u0026ndash;\u0026gt; 尾递归 # 尾递归：\n尾递归的特点： 函数中所有递归形式的调用都出现在函数的末尾 递归调用不属于表达式的一部分（在回归过程中不用做任何操作） 尾递归的作用： 尾递归的调用不会在栈中去创建一个新的 而是覆盖当前的活动记录 为什么可以尾递归： 在回归过程中不用做任何操作 // 在回归过程中不能做任何操作 // 事先处理掉乘n的操作 // 把结果作为参数传递给递归函数 class Solution { public: long long doubleFactorial(int n) { return doubleFactorial(n , 1); } private: long long doubleFactorial(int n, long long result) { if (n \u0026lt;= 2) { return n * result; } return doubleFactorial(n - 2, n * result); } }; class Solution: def double_factorial(self, n, result=1): if n \u0026lt;= 2: return n * result return self.double_factorial(n - 2, n * result) 不是所有语言都支持尾递归优化：\n不支持：python, java, C++ 支持：kotlin(tailrec) 以上四种语言都支持尾递归写法，但是支持尾递归优化都只有kotlin 尾递归 \u0026ndash;\u0026gt; 迭代 # 尾递归优化：就是把递归改成迭代形式\n不支持尾递归优化的语言，解决stackoverflow： 把递归改成迭代形式 Note：所谓的尾递归优化，就是把递归改成迭代形式。所以如果语言不支持尾递归优化，需要手动将尾递归改成迭代形式。 支持尾递归优化的语言，是由编译器自动将尾递归的代码翻译成迭代形式的代码。\n如何改成迭代： 模拟递归中调用下一层的参数传递过程: 1.先做完本层递归的事儿 2.再计算出下一层递归的各个参数 3.然后把值赋给当前层的各个参数 template:(C++) public: ReturnType FunctionName(parameters) { while (true) { do something ... get new parameters parameters = new parameters } } template:(python) def functionName(parameters): while True: do something ... get new parameters parameters = new parameters class Solution { public: long long doubleFactorial(int n) { return doubleFactorial(n , 1); } private: long long doubleFactorial(int n, long long result) { while (true) { // do something if (n \u0026lt;= 2) { return n * result; } // get new parameters int next_n = n - 2; long next_result = n * result; // parameters = new parameters n = next_n; result = next_result; } } }; class Solution: def doubleFactorial(self, n, result=1): while True: # do something if n \u0026lt;= 2: return n * result # get new parameters next_n, next_result = n - 2, n * result # parameters = new parameters n, result = next_n, next_result 颠倒二进制位 # Lintcode 1333 普通递归 # 递归的定义 reverseBits(n, pos) 翻转一个pos位无符号整数n的二进制位并返回 Hints: 把n看成长度为pos的一个数组 递归的拆解 求出n的最后一位二进制位last 翻转n前面的pos-1位 把last放到最前面 递归的出口 n只有1位 pos == 1 class Solution { public: long long reverseBits(long long n) { return reverseBits(n, 32); } private: long long reverseBits(long long n, int pos) { if (pos == 1) { return n; } long long last = n \u0026amp; 1; long long ret = reverseBits(n \u0026gt;\u0026gt; 1, pos - 1); long long result = (last \u0026lt;\u0026lt; (pos - 1)) + ret; return result; } }; class Solution: def reverse_bits(self, n: int, pos=32) -\u0026gt; int: if pos == 1: return n last = n \u0026amp; 1 ret = self.reverse_bits(n \u0026gt;\u0026gt; 1, pos - 1) result = (last \u0026lt;\u0026lt; (pos - 1)) + ret return result 普通递归 \u0026ndash;\u0026gt; 尾递归 # 在回归过程中不能做任何操作 事先处理掉 (last \u0026laquo; (pos - 1)) + ret 的操作 事先把(last \u0026laquo; (post - 1)) 加到result里 再把result作为参数传给下一层递归 class Solution { public: long long reverseBits(long long n) { return reverseBits(n, 32, 0); } private: long long reverseBits(long long n, int pos, long long result) { if (pos == 1) { return n + result; } long long last = n \u0026amp; 1; result += last \u0026lt;\u0026lt; (pos - 1); return reverseBits(n \u0026gt;\u0026gt; 1, pos - 1, result); } }; class Solution: def reverse_bits(self, n, pos=32, result=0) -\u0026gt; int: if pos == 1: return n + result last = n \u0026amp; 1 result += (last \u0026lt;\u0026lt; (pos - 1)) return self.reverse_bits(n \u0026gt;\u0026gt; 1, pos - 1, result) 尾递归 \u0026ndash;\u0026gt; 迭代 # do something?\npos == 1 last = n \u0026amp; 1 get new parameters\nnew_n = n \u0026raquo; 1 new_pos = pos - 1 new_result = result + last \u0026laquo; (pos - 1) parameters = new parameters\nn = new_n pos = new_pos result = new_result class Solution { public: long long reverseBits(long long n) { return reverseBits(n, 32, 0); } private: long long reverseBits(long long n, int pos, long long result) { while (true) { // do something if (pos == 1) { return n + result; } long long last = n \u0026amp; 1; // get new parameters long long new_n = n \u0026gt;\u0026gt; 1; int new_pos = pos - 1; long long new_result = result + (last \u0026lt;\u0026lt; (pos - 1)); // parameters = new parameters n = new_n; pos = new_pos; result = new_result; } } }; class Solution: def reverse_bits(self, n, pos=32, result=0) -\u0026gt; int: while True: # do something if pos == 1: return n + result last = n \u0026amp; 1 # get new parameters new_n, new_pos, new_result = n \u0026gt;\u0026gt; 1, pos - 1, result + (last \u0026lt;\u0026lt; (pos - 1)) # parameters = new parameters n, pos, result = new_n, new_pos, new_result Exercise: 寻找最大值 # Lintcode 297 普通递归 # 递归的定义 maxNum(nums, index) 递归的拆解 nums[index] vs maxNum(\u0026hellip;) 递归的出口 index = nums.size() - 1 class Solution { public: int maxNum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { return maxNum(nums, 0); } private: int maxNum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int index) { if (index == nums.size() - 1) { return nums[index]; } int ret = maxNum(nums, index + 1); return nums[index] \u0026gt; ret ? nums[index] : ret; } }; from typing import ( List, ) class Solution: def max_num(self, nums: List[int], index=0) -\u0026gt; int: if index == len(nums) - 1: return nums[index] ret = self.max_num(nums, index + 1) return nums[index] if nums[index] \u0026gt; ret else ret 普通递归 \u0026ndash;\u0026gt; 尾递归 # class Solution { public: int maxNum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { return maxNum(nums, 0, nums[0]); } private: int maxNum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int index, int result) { if (index == nums.size() - 1) { return nums[index] \u0026gt; result ? nums[index] : result; } result = result \u0026gt; nums[index] ? result : nums[index]; return maxNum(nums, index + 1, result); } }; from typing import ( List, ) class Solution: def max_num(self, nums: List[int], index=0, result=float(\u0026#34;-inf\u0026#34;)) -\u0026gt; int: if index == len(nums) - 1: return nums[index] if nums[index] \u0026gt; result else result result = result if result \u0026gt; nums[index] else nums[index] return self.max_num(nums, index + 1, result) 尾递归 \u0026ndash;\u0026gt; 迭代 # class Solution { public: int maxNum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { return maxNum(nums, 0, nums[0]); } private: int maxNum(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int index, int result) { while (true) { if (index == nums.size() - 1) { return nums[index] \u0026gt; result ? nums[index] : result; } int new_index = index + 1; int new_result = result \u0026gt; nums[index] ? result : nums[index]; index = new_index; result = new_result; } } }; from typing import ( List, ) class Solution: def max_num(self, nums: List[int], index=0, result=float(\u0026#34;-inf\u0026#34;)) -\u0026gt; int: while True: if index == len(nums) - 1: return nums[index] if nums[index] \u0026gt; result else result new_index, new_result = index + 1, result if result \u0026gt; nums[index] else nums[index] index, result = new_index, new_result Chapter 3: 单向递归\u0026ndash;递归的妙用 # 两两交换链表中的节点 # Lintcode 451 递归的方式 # 递归的定义 swapPairs(head) 两两交换链表head中的节点 返回交换后的链表表头 递归的拆解 取下head中的前两个节点并交换 swapPairs(head.next.next) 连接前两个节点和后面部分链表 递归的出口 链表中节点数量不足两个 不用交换，直接返回当前表头 class Solution { public: ListNode* swapPairs(ListNode* head) { if (head == nullptr || head-\u0026gt;next == nullptr) { return head; } ListNode* first = head; ListNode* second = head-\u0026gt;next; ListNode* suffix = swapPairs(second-\u0026gt;next); second-\u0026gt;next = first; first-\u0026gt;next = suffix; return second; } }; from lintcode import ( ListNode, ) class Solution: def swap_pairs(self, head: ListNode) -\u0026gt; ListNode: if head is None or head.next is None: return head first, second = head, head.next suffix = self.swap_pairs(second.next) second.next = first first.next = suffix return second 迭代的方式 # class Solution { public: ListNode* swapPairs(ListNode* head) { ListNode* dummy = new ListNode(0); dummy-\u0026gt;next = head; head = dummy; while (head-\u0026gt;next != nullptr \u0026amp;\u0026amp; head-\u0026gt;next-\u0026gt;next != nullptr) { ListNode* first = head-\u0026gt;next; ListNode* second = first-\u0026gt;next; // head-\u0026gt;first-\u0026gt;second-\u0026gt;... // =\u0026gt; head-\u0026gt;second-\u0026gt;first-\u0026gt;... head-\u0026gt;next = second; first-\u0026gt;next = second-\u0026gt;next; second-\u0026gt;next = first; head = first; } return dummy-\u0026gt;next; } }; from lintcode import ( ListNode, ) class Solution: def swap_pairs(self, head: ListNode) -\u0026gt; ListNode: dummy = ListNode(0) dummy.next = head head = dummy while head.next is not None and head.next.next is not None: first = head.next second = head.next.next # head-\u0026gt;first-\u0026gt;second-\u0026gt;... # =\u0026gt; head-\u0026gt;second-\u0026gt;first-\u0026gt;... head.next = second first.next = second.next second.next = first head = first return dummy.next 经典二分查找问题 # Lintcode 457 普通写法的递归方式: Time Limit Exceeded # 递归的定义 findPosition(nums, start, end, target) 在nums数组[start, end]区间上查找target 递归的拆解 findPosition(nums, start + 1, end, target) 递归的出口 start \u0026gt; end: return -1 nums[start] == target: return start class Solution { public: int findPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { return findPosition(nums, 0, nums.size() - 1, target); } private: int findPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, int end, int\u0026amp; target) { if (start \u0026gt; end) { return -1; } if (nums[start] == target) { return start; } return findPosition(nums, start + 1, end, target); } }; class Solution: def findPosition(self, nums, target, start=0, end=None): if end is None: end = len(nums) - 1 if start \u0026gt; end: return -1 if nums[start] == target: return start return self.findPosition(nums, target, start + 1, end) 二分查找的递归方式 # 递归的定义 findPosition(nums, start, end, target) 在nums数组[start, end]区间上查找target 递归的拆解 找到start到end这个范围的中间值middle 如果middle这个位置小了 findPosition(nums, middle + 1, end, target) 如果middle这个位置大了 findPosition(nums, start, middle - 1, target) 递归的出口 start \u0026gt; end; return -1 nums[middle] == target: return middle class Solution { public: int findPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { return findPosition(nums, 0, nums.size() - 1, target); } private: int findPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, int end, int\u0026amp; target) { if (start \u0026gt; end) { return -1; } int middle = start + (end - start) / 2; if (nums[middle] \u0026lt; target) { findPosition(nums, middle + 1, end, target); } else if (nums[middle] \u0026gt; target) { findPosition(nums, start, middle - 1, target); } else { return middle; } } }; class Solution: def findPosition(self, nums, target): return self.findPosition_helper(nums, 0, len(nums) - 1, target) def findPosition_helper(self, nums, start, end, target): if start \u0026gt; end: return -1 middle = (start + end) // 2 if nums[middle] \u0026lt; target: # keyword return is requried???? return self.findPosition_helper(nums, middle + 1, end, target) elif nums[middle] \u0026gt; target: # keyword return is requried???? return self.findPosition_helper(nums, start, middle - 1, target) else: return middle python doesn\u0026rsquo;t support function overloading\n快速幂 # Lintcode 140 普通写法的递归方式: # 递归求解a^n # 递归的定义 fastPower(a, n) 计算a^n 递归的拆解 a * fastPower(a, n - 1) 递归的出口 n == 0 return 1 同余定理： x * y % z = (x % z) * (y % z) % z\na % b = (a + b) % b = (a + 2 * b) % b \u0026hellip; = (a + k * b) % b, k 是任意整数 x 和 (x % z) 取余相差了整数个z y 和 (y % z) 取余相差了整数个z 递归求解(a^n) % b: Fail # 递归的定义 fastPower(a, b, n) 计算(a^n) % b 递归的拆解 同余定理 (x * y) % z \u0026ndash;\u0026gt; ((x % z) * (y % z)) % z x \u0026ndash;\u0026gt; a y \u0026ndash;\u0026gt; a^(n - 1) z \u0026ndash;\u0026gt; b 递归的出口 n == 0 return 1 % b 递归时间复杂度 = 函数参数的可能组合 * 每层递归的处理时间\n函数参数的可能组合 O(n) 每层递归的时间 O(1) class Solution { public: int fastPower(int a, int b, int n) { if (n == 0) { return 1 % b; } int x = a % b; int y = fastPower(a, b, n - 1); return x * y % b; } }; class Solution: def fast_power(self, a: int, b: int, n: int) -\u0026gt; int: if n == 0: return 1 % b x = a % b y = self.fast_power(a, b, n - 1) return x * y % b 快速幂的递归方式: Pass # 递归的定义 fastPower(a, b, n) 计算(a^n) % b 递归的拆解 n % 2 == 0 x \u0026ndash;\u0026gt; a^(n/2) y \u0026ndash;\u0026gt; a^(n/2) n % 2 == 1 x \u0026ndash;\u0026gt; a^(n/2) y \u0026ndash;\u0026gt; a^(n/2) * a z \u0026ndash;\u0026gt; b 递归的出口 n == 0 return 1 % b class Solution { public: int fastPower(int a, int b, int n) { if (n == 0) { return 1 % b; } // here must be long long, cannot be int long long x = fastPower(a, b, n / 2); long long y = n % 2 != 0 ? (x * a % b) : x; return x * y % b; } }; class Solution: def fast_power(self, a: int, b: int, n: int) -\u0026gt; int: if n == 0: return 1 % b x = self.fast_power(a, b, n // 2) y = x * a % b if n % 2 != 0 else x return x * y % b 快速幂的迭代形式: Pass # class Solution { public: int fastPower(int a, int b, int n) { int result = 1 % b; while (n != 0) { if (n % 2 == 1) { result = (long long)result * a % b; } a = (long long)a * a % b; n /= 2; } return result; } }; class Solution: def fast_power(self, a: int, b: int, n: int) -\u0026gt; int: result = 1 % b while n != 0: if n % 2 == 1: result = result * a % b a = a * a % b n //= 2 return result Exercise: 14. 二分查找 # Lintcode 14 classic method # class Solution { public: int binarySearch(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { if (nums.size() == 0) { return -1; } int left = 0; int right = nums.size() - 1; while (left + 1 \u0026lt; right) { int mid = left + (right - left) / 2; if (nums[mid] \u0026gt; target) { right = mid; } else if (nums[mid] \u0026lt; target) { left = mid; } else { right = mid; } } if (nums[left] == target) { return left; } if (nums[right] == target) { return right; } return -1; } }; recursion method # class Solution { public: int binarySearch(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { return binarySearch(nums, target, 0, nums.size() - 1); } private: int binarySearch(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target, int start, int end) { if (start \u0026gt; end) { return -1; } // optional base case if (nums[start] == target) { return start; } int mid = start + (end - start) / 2; if (nums[mid] \u0026gt; target) { return binarySearch(nums, target, start, mid - 1); } else if (nums[mid] \u0026lt; target) { return binarySearch(nums, target, mid + 1, end); } else { if (mid - 1 \u0026gt;= 0 \u0026amp;\u0026amp; nums[mid - 1] == nums[mid]) { return binarySearch(nums, target, start, mid - 1); } else { return mid; } } } }; class Solution: def binarySearch(self, nums, target): return self._binary_search_helper(nums, target, 0, len(nums) - 1) def _binary_search_helper(self, nums, target, start, end): if start \u0026gt; end: return -1 mid = (start + end) // 2 if nums[mid] \u0026lt; target: # return keyword is required return self._binary_search_helper(nums, target, mid + 1, end) elif nums[mid] \u0026gt; target: # return keyword is required return self._binary_search_helper(nums, target, start, mid - 1) else: if mid - 1 \u0026gt;= 0 and nums[mid - 1] == nums[mid]: return self._binary_search_helper(nums, target, start, mid - 1) else: return mid recursion method version 2 # class Solution { public: int binarySearch(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { return binarySearch(nums, target, 0, nums.size() - 1); } int binarySearch(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target, int left, int right) { if (left \u0026gt; right) { return -1; } int mid = left + (right - left) / 2; if (nums[mid] \u0026lt; target) { return binarySearch(nums, target, mid + 1, right); } if (nums[mid] \u0026gt; target) { return binarySearch(nums, target, left, mid - 1); } int temp = binarySearch(nums, target, left, mid - 1); if (temp != -1) { return temp; } return mid; } }; Exercise: 458. 目标最后位置 # Lintcode 458 classic method # class Solution { public: int lastPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { if (nums.size() == 0) { return -1; } int left = 0; int right = nums.size() - 1; while (left + 1 \u0026lt; right) { int mid = left + (right - left) / 2; if (nums[mid] \u0026gt; target) { right = mid; } else if (nums[mid] \u0026lt; target) { left = mid; } else { left = mid; } } if (nums[right] == target) { return right; } else if (nums[left] == target) { return left; } return -1; } }; recursion method # class Solution { public: int lastPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { return lastPosition(nums, target, 0, nums.size() - 1); } int lastPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target, int left, int right) { if (left \u0026gt; right) { return -1; } if (nums[right] == target) { return right; } int mid = left + (right - left) / 2; if (nums[mid] \u0026lt; target) { return lastPosition(nums, target, mid + 1, right); } else if (nums[mid] \u0026gt; target) { return lastPosition(nums, target, left, mid - 1); } else { if (mid + 1 \u0026lt;= nums.size() - 1 \u0026amp;\u0026amp; nums[mid + 1] == nums[mid]) { return lastPosition(nums, target, mid + 1, right); } else { return mid; } } } }; from typing import ( List, ) class Solution: def last_position(self, nums: List[int], target: int) -\u0026gt; int: return self._last_position_helper(nums, target, 0, len(nums) - 1) def _last_position_helper(self, nums, target, left, right): if left \u0026gt; right: return -1 if nums[right] == target: return right mid = (left + right) // 2 if nums[mid] \u0026lt; target: return self._last_position_helper(nums, target, mid + 1, right) elif nums[mid] \u0026gt; target: return self._last_position_helper(nums, target, left, mid - 1) else: if mid + 1 \u0026lt; len(nums) - 1 and nums[mid + 1] == nums[mid]: return self._last_position_helper(nums, target, mid + 1, right) else: return mid recursion method version 2 # class Solution { public: int lastPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { return lastPosition(nums, target, 0, nums.size() - 1); } int lastPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target, int left, int right) { if (left \u0026gt; right) { return -1; } int mid = left + (right - left) / 2; if (nums[mid] \u0026lt; target) { return lastPosition(nums, target, mid + 1, right); } if (nums[mid] \u0026gt; target) { return lastPosition(nums, target, left, mid - 1); } int temp = lastPosition(nums, target, mid + 1, right); if (temp != -1) { return temp; } return mid; } }; In summary: # 两两交换链表中的节点\n递归比迭代更加好想，好写，不易出bug 但是递归有可能发生爆栈 经典二分查找问题 和 快速幂问题\n和递归的核心思想由大化小完美贴合的两个算法 换种递归拆分的方法会让时间复杂度和栈深度降低很多 由于每次砍掉一半，递归深度不会太深，没有爆栈风险 Chapter 4: 双向递归\u0026ndash;二叉树的遍历与递归树 # 二叉树的深度优先遍历 # 前序遍历(Preorder Traversal)\n根节点 -\u0026gt; 左子树 -\u0026gt; 右子树 Root -\u0026gt; Left -\u0026gt; Right 中序遍历\n左子树 -\u0026gt; 根节点 -\u0026gt; 右子树 Left -\u0026gt; Root -\u0026gt; Right 后序遍历\n左子树 -\u0026gt; 右子树 -\u0026gt; 根节点 Left -\u0026gt; Right -\u0026gt; Root 递归，二叉树的遍历： # Lintcode 66 preorder Lintcode 67 inorder Lintcode 68 postorder 递归的定义 preorderTraversal(root) \u0026mdash;- 前序遍历 inorderTraversal(root) \u0026mdash;- 中序遍历 postorderTraversal(root) \u0026mdash;- 后序遍历 递归的拆解 先处理自己 再处理左子树 然后处理右子树 \u0026mdash;- 前序遍历 先处理左子树 再处理自己 然后处理右子树 \u0026mdash;- 中序遍历 先处理左子树 再处理右子树 然后处理自己 \u0026mdash;- 后序遍历 递归的出口 root是一颗空树 \u0026mdash;- 前序遍历、中序遍历、后序遍历 class Solution { public: std::vector\u0026lt;int\u0026gt; preorderTraversal(TreeNode* root) { std::vector\u0026lt;int\u0026gt; nodes; preorder_traversal(root, nodes); return nodes; } private: void preorder_traversal(TreeNode* root, std::vector\u0026lt;int\u0026gt;\u0026amp; nodes) { if (root == nullptr) { return; } nodes.push_back(root-\u0026gt;val); preorder_traversal(root-\u0026gt;left, nodes); preorder_traversal(root-\u0026gt;right, nodes); } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def preorder_traversal(self, root: TreeNode) -\u0026gt; List[int]: nodes = list() self.preorder_traversal_helper(root, nodes) return nodes def preorder_traversal_helper(self, root, nodes): if root is None: return nodes.append(root.val) self.preorder_traversal_helper(root.left, nodes) self.preorder_traversal_helper(root.right, nodes) 斐波那契数列 # 递归树(后序遍历) Lintcode 366 fibonacci 递归的定义 fibonacci(n) 求解斐波那契数列第n项 递归的拆解 fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2) 递归的出口 fibonacci(1) = 0 fibonacci(2) = 1 // Time Limit Exceeded class Solution { public: int fibonacci(int n) { if (n \u0026lt;= 2) { return n - 1; } return fibonacci(n - 1) + fibonacci(n - 2); } }; memorization of fibonacci # class Solution { public: int fibonacci(int n) { std::unordered_map\u0026lt;int, int\u0026gt; memo; return fibonacci(n, memo); } private: int fibonacci(int n, std::unordered_map\u0026lt;int, int\u0026gt;\u0026amp; memo) { if (memo.find(n) != memo.end()) { return memo[n]; } if (n \u0026lt;= 2) { return n - 1; } int result = fibonacci(n - 1, memo) + fibonacci(n - 2, memo); memo[n] = result; return result; } }; class Solution: def fibonacci(self, n: int, memo = None) -\u0026gt; int: if memo is None: memo = dict() if n in memo: return memo[n] if n \u0026lt;= 2: return n - 1 result = self.fibonacci(n - 1, memo) + self.fibonacci(n - 2, memo) memo[n] = result return result 汉诺塔 # 递归树(中序遍历) 拆解的时候只考虑当前层 Lintcode 169 tower of Hanoi 递归的定义 helper(n, start, end, temp, moves) 把n个盘子从start移到end 可以借助temp进行移动 移动的方案存到moves里 递归的拆解 把前n - 1个盘子从start移到temp helper(n - 1, start, temp, end, moves) 把第n个盘子从start移到end 把前n-1个盘子从temp移到end helper(n - 1, temp, end, start, moves) 递归的出口 n == 1 直接把盘子从start移到end class Solution { public: std::vector\u0026lt;std::string\u0026gt; towerOfHanoi(int n) { std::vector\u0026lt;std::string\u0026gt; moves; towerOfHanoi(n, \u0026#39;A\u0026#39;, \u0026#39;C\u0026#39;, \u0026#39;B\u0026#39;, moves); return moves; } private: void towerOfHanoi(int n, char start, char end, char temp, std::vector\u0026lt;std::string\u0026gt;\u0026amp; moves) { if (n == 1) { moves.push_back(move(start, end)); return; // don\u0026#39;t forgot this return } towerOfHanoi(n - 1, start, temp, end, moves); moves.push_back(move(start, end)); towerOfHanoi(n - 1, temp, end, start, moves); } std::string move(char start, char end) { return std::string() + \u0026#34;from \u0026#34; + start + \u0026#34; to \u0026#34; + end; } }; from typing import ( List, ) class Solution: def tower_of_hanoi(self, n: int) -\u0026gt; List[str]: moves = list() self.helper(n, \u0026#39;A\u0026#39;, \u0026#39;C\u0026#39;, \u0026#39;B\u0026#39;, moves) return moves def helper(self, n, start, end, temp, moves): if n == 1: moves.append(self.move(start, end)) return # don\u0026#39;t forgot this return self.helper(n - 1, start, temp, end, moves) moves.append(self.move(start, end)) self.helper(n - 1, temp, end, start, moves) def move(self, start, end): return \u0026#34;from \u0026#34; + start + \u0026#34; to \u0026#34; + end Exercise: 1300. 巴什博弈 # Lintcode 1300 递归的定义 canWinBash(n) 有n个石子，先手拿能否获胜 递归的拆解 canWinBash(n - 1) ==\u0026gt; 拿走1个 canWinBash(n - 2) ==\u0026gt; 拿走2个 canWinBash(n - 3) ==\u0026gt; 拿走3个 递归的出口 n \u0026lt;= 3 // Time Limit Exceeded class Solution { public: bool canWinBash(int n) { if (n \u0026lt;= 3) { return true; } return !canWinBash(n - 1) || !canWinBash(n - 2) || !canWinBash(n - 3); } }; # Time Limit Exceeded class Solution: def can_win_bash(self, n: int) -\u0026gt; bool: if n \u0026lt;= 3: return True; return not self.can_win_bash(n - 1) \\ or not self.can_win_bash(n - 2) \\ or not self.can_win_bash(n - 3) memorization optimization # // Time Limit Exceeded class Solution { public: bool canWinBash(int n) { std::unordered_map\u0026lt;int, bool\u0026gt; memo; bool result = canWinBash(n, memo); return result; } private: bool canWinBash(int n, std::unordered_map\u0026lt;int, bool\u0026gt;\u0026amp; memo) { if (memo.find(n) != memo.end()) { return memo[n]; } if (n \u0026lt;= 3) { return true; } bool temp = !canWinBash(n - 1) || !canWinBash(n - 2) || !canWinBash(n - 3); memo[n] = temp; return temp; } }; 递归的核心思想：由大化小：Best Solution # class Solution { public: bool canWinBash(int n) { return n % 4 != 0; } }; class Solution: def can_win_bash(self, n: int) -\u0026gt; bool: return n % 4 != 0 In summary # 斐波那契数列\n有递归式的数列可以直接根据递归式写递归 递归树(后序遍历) 汉诺塔\n递归的时候只考虑当前层，否则参数多转移多的递归会很乱 递归树(中序遍历) Chapter 5: 双向递归\u0026ndash;二叉树的分治 # 分治法 vs 递归 # 分治法：分治法 是一种 算法 递归：递归 是一种 程序设计方式\n适合分治法的数据结构 # 数组：一个大数组可以拆分为若干个不相交的子数组 二叉树：整棵二叉树的左子树和右子树都是二叉树 二叉树上分治模版(template of Divide and Conquer for Binary Tree) # class Solution { public: 返回结果类型 DivideConquer(TreeNode* root) { if (root == nullptr) { 处理空树应该返回的结果 } // if (root-\u0026gt;left == nullptr \u0026amp;\u0026amp; root-\u0026gt;right == nullptr) { // 处理叶子应该返回的结果 // 如果叶子的返回结果可以通过两个空节点的返回结果得到 // 就可以省略这一段代码 // 一般可省略 // } 左子树的返回结果 = DivideConquer(root-\u0026gt;left) 右子树的返回结果 = DivideConquer(root-\u0026gt;right) 整棵树的结果 = 按照一定方法合并左右子树的结果 } }; def devide_conquer(root): if root is None: 处理空树应该返回的结果 # if root.left is None and root.right is None: # 处理叶子应该返回的结果 # 如果叶子的返回结果可以通过两个空节点的返回结果得到 # 就可以省略这一段代码 # 一般可省略 左子树的返回结果 = self.divide_conquer(root.left) 右子树的返回结果 = self.divide_conquer(root.right) 整棵树的结果 = 按照一定方法合并左右子树的结果 二叉树的最大深度 # Lintcode 97 递归的定义 maxDepth(root) 以root为根的二叉树的最大深度是多少 递归的拆解 maxDepth(root.left) maxDepth(root.right) 递归的出口 root是一棵空树的根 class Solution { public: int maxDepth(TreeNode* root) { if (root == nullptr) { return 0; } return std::max(maxDepth(root-\u0026gt;left), maxDepth(root-\u0026gt;right)) + 1; } }; from lintcode import ( TreeNode, ) class Solution: def max_depth(self, root: TreeNode) -\u0026gt; int: if root is None: return 0 return max(self.max_depth(root.left), self.max_depth(root.right)) + 1 最大二叉树 # Lintcode 1106 递归的定义 buildTree(nums, start, end) 以nums数组的start～end区间构建最大二叉树 递归的拆解 找到nums数组start～end区间上的最大元素位置记做position root = nums[position] root.left = buildTree(nums, start, position - 1); root.right = buildTree(nums, position + 1, end); 递归的出口 nums数组或start～end区间为空的时候 class Solution { public: TreeNode* constructMaximumBinaryTree(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { return buildTree(nums, 0, nums.size() - 1); } private: TreeNode* buildTree(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, int end) { if (start \u0026gt; end) { return nullptr; } int position = start; for (int i = start + 1; i \u0026lt;= end; ++i) { if (nums[i] \u0026gt; nums[position]) { position = i; } } TreeNode* root = new TreeNode(nums[position]); root-\u0026gt;left = buildTree(nums, start, position - 1); root-\u0026gt;right = buildTree(nums, position + 1, end); return root; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def construct_maximum_binary_tree(self, nums: List[int]) -\u0026gt; TreeNode: return self.buildTree(nums, 0, len(nums) - 1) def buildTree(self, nums, start, end): if start \u0026gt; end: return None position = start for i in range(start + 1, end + 1): if nums[i] \u0026gt; nums[position]: position = i root = TreeNode(nums[position]) root.left = self.buildTree(nums, start, position - 1) root.right = self.buildTree(nums, position + 1, end) return root 通过遍历序确定二叉树(important) # 前序遍历和中序遍历树构造二叉树 # 唯一\nLintcode 73 递归的定义 buildTree(preorder, pre_start, pre_end, inorder, in_start, in_end) 以preorder数组的pre_start~pre_end区间为前序遍历 以inorder数组的in_start~in_end区间为中序遍历 构建二叉树 递归的拆解 preorder[pre_start]: 前序遍历第一个访问到的节点必是根 inorder中preorder[pre_start]左边的部分是左子树，右边的部分是右子树 root.left = buildTree(preorder, pre_start + 1, pre_start + leftLen, inorder, in_start, position - 1) root.right = buildTree(preorder, pre_end - rightLen + 1, pre_end, inorder, position + 1, in_end) 递归的出口 preorder数组或inorder数组区间为空的时候 class Solution { public: TreeNode* buildTree(std::vector\u0026lt;int\u0026gt;\u0026amp; preorder, std::vector\u0026lt;int\u0026gt;\u0026amp; inorder) { return buildTree(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1); } private: TreeNode* buildTree(std::vector\u0026lt;int\u0026gt;\u0026amp; preorder, int pre_start, int pre_end, std::vector\u0026lt;int\u0026gt;\u0026amp; inorder, int in_start, int in_end) { if (pre_start \u0026gt; pre_end) { return nullptr; } if (in_start \u0026gt; in_end) { return nullptr; } TreeNode* root = new TreeNode(preorder[pre_start]); int position = FindPosition(inorder, preorder[pre_start]); int left_len = position - in_start; int right_len = in_end - position; root-\u0026gt;left = buildTree(preorder, pre_start + 1, pre_start + left_len, inorder, in_start, position - 1); root-\u0026gt;right = buildTree(preorder, pre_end - right_len + 1, pre_end, inorder, position + 1, in_end); return root; } int FindPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { for (int i = 0; i \u0026lt; nums.size(); ++i) { if (nums[i] == target) { return i; } } return -1; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def build_tree(self, preorder: List[int], inorder: List[int]) -\u0026gt; TreeNode: return self.build_tree_helper(preorder, 0, len(preorder) - 1, inorder, 0, len(inorder) - 1) def build_tree_helper(self, preorder, pre_start, pre_end, inorder, in_start, in_end): if pre_start \u0026gt; pre_end: return None if in_start \u0026gt; in_end: return None root = TreeNode(preorder[pre_start]) position = inorder.index(preorder[pre_start]) left_len = position - in_start right_len = in_end - position root.left = self.build_tree_helper(preorder, pre_start + 1, pre_start + left_len, inorder, in_start, position - 1) root.right = self.build_tree_helper(preorder, pre_end - right_len + 1, pre_end, inorder, position + 1, in_end) return root 中序遍历和后序遍历树构造二叉树 # 唯一\nLintcode 72 class Solution { public: TreeNode* buildTree(std::vector\u0026lt;int\u0026gt;\u0026amp; inorder, std::vector\u0026lt;int\u0026gt;\u0026amp; postorder) { return buildTree(postorder, 0, postorder.size() - 1, inorder, 0, inorder.size() - 1); } private: TreeNode* buildTree(std::vector\u0026lt;int\u0026gt;\u0026amp; postorder, int post_start, int post_end, std::vector\u0026lt;int\u0026gt;\u0026amp; inorder, int in_start, int in_end) { if (post_start \u0026gt; post_end) { return nullptr; } if (in_start \u0026gt; in_end) { return nullptr; } int position = FindPosition(inorder, postorder[post_end]); int left_len = position - in_start; int right_len = in_end - position; TreeNode* root = new TreeNode(postorder[post_end]); root-\u0026gt;left = buildTree(postorder, post_start, post_start + left_len - 1, inorder, in_start, position - 1); root-\u0026gt;right = buildTree(postorder, post_end - right_len, post_end - 1, inorder, position + 1, in_end); return root; } int FindPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { for (int i = 0; i \u0026lt; nums.size(); ++i) { if (nums[i] == target) { return i; } } return -1; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def build_tree(self, inorder: List[int], postorder: List[int]) -\u0026gt; TreeNode: return self.build_tree_helper(postorder, 0, len(postorder) - 1, inorder, 0, len(inorder) - 1) def build_tree_helper(self, postorder, post_start, post_end, inorder, in_start, in_end): if post_start \u0026gt; post_end: return None if in_start \u0026gt; in_end: return None root = TreeNode(postorder[post_end]) position = inorder.index(postorder[post_end]) left_len = position - in_start right_len = in_end - position root.left = self.build_tree_helper(postorder, post_start, post_start + left_len - 1, inorder, in_start, position - 1) root.right = self.build_tree_helper(postorder, post_end - right_len, post_end - 1, inorder, position + 1, in_end) return root 前序遍历和后序遍历树构造二叉树 # 不唯一\nLintcode 1593 递归的定义 buildTree(pre, preStart, preEnd, post, postStart, postEnd) 以pre数组的preStart ~ preEnd区间为前序遍历 以post数组的postStart ~ postEnd区间为后序遍历 构建二叉树 递归的拆解 pre[preStart]: 前序遍历第一个访问到的节点必是根 post中pre[preStart + 1]以及左边的部分是左子树，右边的部分是右子树 root.left = buildTree(pre, preStart + 1, preStart + leftLen, post, postStart, postStart + leftLen - 1) root.right = buildTree(pre, preEnd - rightLen + 1, preEnd, post, postEnd - rightLen, postEnd - 1) 递归的出口 pre数组或post数组区间为空的时候 class Solution { public: TreeNode* constructFromPrePost(std::vector\u0026lt;int\u0026gt;\u0026amp; pre, std::vector\u0026lt;int\u0026gt;\u0026amp; post) { return BuildTree(pre, 0, pre.size() - 1, post, 0, post.size() - 1); } private: TreeNode* BuildTree(std::vector\u0026lt;int\u0026gt;\u0026amp; pre, int pre_start, int pre_end, std::vector\u0026lt;int\u0026gt;\u0026amp; post, int post_start, int post_end) { if (pre_start \u0026gt; pre_end) { return nullptr; } if (post_start \u0026gt; post_end) { return nullptr; } // here is optional if (pre[pre_start] != post[post_end]) { return nullptr; } TreeNode* root = new TreeNode(pre[pre_start]); // here is required. 是递归出口。这里属于模版里面需要特殊处理的叶子节点。如果不处理叶子节点，后面pre_start + 1, 就会数组越界。如果不是叶子节点，就再处理后面的左子树和右子树。 if (pre_start == pre_end || post_start == post_end) { return root; } int position = FindPosition(post, pre[pre_start + 1]); int left_len = position - post_start + 1; int right_len = post_end - position - 1; // 将争议节点并入左子树 root-\u0026gt;left = BuildTree(pre, pre_start + 1, pre_start + left_len, post, post_start, post_start + left_len - 1); root-\u0026gt;right = BuildTree(pre, pre_end - right_len + 1, pre_end, post, post_end - right_len, post_end - 1); return root; } int FindPosition(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { for (int i = 0; i \u0026lt; nums.size(); ++i) { if (nums[i] == target) { return i; } } return -1; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def construct_from_pre_post(self, pre: List[int], post: List[int]) -\u0026gt; TreeNode: return self.build_tree(pre, 0, len(pre) - 1, post, 0, len(post) - 1) def build_tree(self, pre, pre_start, pre_end, post, post_start, post_end): if pre_start \u0026gt; pre_end: return None if post_start \u0026gt; post_end: return None if pre[pre_start] != post[post_end]: return None root = TreeNode(pre[pre_start]) # leaf node if pre_start == pre_end or post_start == post_end: return root position = post.index(pre[pre_start + 1]) left_len = position - post_start + 1 right_len = post_end - position - 1 root.left = self.build_tree(pre, pre_start + 1, pre_start + left_len, post, post_start, post_start + left_len - 1) root.right = self.build_tree(pre, pre_end - right_len + 1, pre_end, post, post_end - right_len, post_end - 1) return root Chapter 6: 多向递归\u0026ndash;组合类问题 # 子集 # Lintcode 17 1. 二叉树遍历解法 # 类似于单向递归\n递归的定义 helper(nums, start, end, combinations, combination) ==\u0026gt; 由于end在递归调用中没有发生变化，所以可以抹掉end helper(nums, start, combinations, combination) 递归的拆解 选取第start个数 不选第start个数 递归的出口 start~end区间为空的时候 =\u0026gt; start越出nums范围的时候 class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; subsets(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::vector\u0026lt;int\u0026gt; combination; std::sort(nums.begin(), nums.end()); Helper(nums, 0, combinations, combination); return combinations; } private: void Helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; combinations, std::vector\u0026lt;int\u0026gt;\u0026amp; combination) { if (start == nums.size()) { combinations.push_back(combination); // push_back make a copy of the argument and stores it in the vector return; // let return void } combination.push_back(nums[start]); Helper(nums, start + 1, combinations, combination); combination.pop_back(); Helper(nums, start + 1, combinations, combination); } }; from typing import ( List, ) class Solution: def subsets(self, nums: List[int]) -\u0026gt; List[List[int]]: combinations = list() nums.sort() # required in this question self.helper(nums, 0, combinations, None) return combinations def helper(self, nums, start, combinations, combination): if combination == None: combination = list() if start == len(nums): # ==, not \u0026gt; combinations.append(list(combination)) # deep copy by list(...) return combination.append(nums[start]) self.helper(nums, start + 1, combinations, combination) combination.pop() self.helper(nums, start + 1, combinations, combination) 2. 组合数解法 # 优化: 铲除多余节点 和之前写的递归套路不太一样\n状态的转移放在了for循环里 递归的隐式出口 class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; subsets(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::vector\u0026lt;int\u0026gt; combination; std::sort(nums.begin(), nums.end()); Helper(nums, 0, combinations, combination); return combinations; } private: void Helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; combinations, std::vector\u0026lt;int\u0026gt;\u0026amp; combination) { // 树上没有重复的节点，每一个节点都是需要的子集 combinations.push_back(combination); // 从start开始往后找 for (int i = start; i \u0026lt; nums.size(); ++i) { combination.push_back(nums[i]); Helper(nums, i + 1, combinations, combination); combination.pop_back(); } } }; from typing import ( List, ) class Solution: def subsets(self, nums: List[int]) -\u0026gt; List[List[int]]: combinations = list() nums.sort() self.helper(nums, 0, combinations, None) return combinations def helper(self, nums, start, combinations, combination): if combination == None: combination = list() # 树上没有重复的节点，每一个节点都是需要的子集 combinations.append(list(combination)) # 从start开始往后找 for i in range(start, len(nums)): combination.append(nums[i]) self.helper(nums, i + 1, combinations, combination) combination.pop() 子集II: ???? # Lintcode 18 1. 二叉树遍历解法 # 递归的定义 helper(nums, start, combinations, combination, refuse) refuse 表示前面相同的数字是否都拿走了，如果有一个没拿走，我就不能拿当前这个数 递归的拆解 选取第start个数 不选第start个数 递归的出口 start越出nums范围的时候 class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; subsetsWithDup(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::vector\u0026lt;int\u0026gt; combination; std::sort(nums.begin(), nums.end()); Helper(nums, 0, combinations, combination, false); return combinations; } private: void Helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; combinations, std::vector\u0026lt;int\u0026gt;\u0026amp; combination, bool refuse) { if (start == nums.size()) { combinations.push_back(combination); return; } Helper(nums, start + 1, combinations, combination, true); if (refuse \u0026amp;\u0026amp; nums[start] == nums[start - 1]) { return; } combination.push_back(nums[start]); Helper(nums, start + 1, combinations, combination, false); combination.pop_back(); } }; from typing import ( List, ) class Solution: def subsets_with_dup(self, nums: List[int]) -\u0026gt; List[List[int]]: combinations = list() nums.sort() self.helper(nums, 0, combinations, None, False) return combinations def helper(self, nums, start, combinations, combination, refuse): if combination == None: combination = list() if start == len(nums): combinations.append(list(combination)) return self.helper(nums, start + 1, combinations, combination, True) if refuse and nums[start] == nums[start - 1]: return combination.append(nums[start]) self.helper(nums, start + 1, combinations, combination, False) combination.pop() 2. 组合数解法 # // Mine version class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; subsetsWithDup(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::vector\u0026lt;int\u0026gt; combination; std::sort(nums.begin(), nums.end()); Helper(nums, 0, combinations, combination); return combinations; } private: void Helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; combinations, std::vector\u0026lt;int\u0026gt;\u0026amp; combination) { // 去重 if (std::find(combinations.begin(), combinations.end(), combination) == combinations.end()) { combinations.push_back(combination); } for (int i = start; i \u0026lt; nums.size(); ++i) { combination.push_back(nums[i]); Helper(nums, i + 1, combinations, combination); combination.pop_back(); } } }; // Official version class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; subsetsWithDup(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::vector\u0026lt;int\u0026gt; combination; std::sort(nums.begin(), nums.end()); Helper(nums, 0, combinations, combination); return combinations; } private: void Helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; combinations, std::vector\u0026lt;int\u0026gt;\u0026amp; combination) { combinations.push_back(combination); for (int i = start; i \u0026lt; nums.size(); ++i) { if (i != start \u0026amp;\u0026amp; nums[i] == nums[i - 1]) { continue; } combination.push_back(nums[i]); Helper(nums, i + 1, combinations, combination); combination.pop_back(); } } }; # Mine version from typing import ( List, ) class Solution: def subsetsWithDup(self, nums: List[int]) -\u0026gt; List[List[int]]: combinations = list() nums.sort() self.helper(nums, 0, combinations, None) return combinations def helper(self, nums, start, combinations, combination): if combination == None: combination = list() # 去重 if combination not in combinations: combinations.append(list(combination)) for i in range(start, len(nums)): combination.append(nums[i]) self.helper(nums, i + 1, combinations, combination) combination.pop() # Official version from typing import ( List, ) class Solution: def subsetsWithDup(self, nums: List[int]) -\u0026gt; List[List[int]]: combinations = list() nums.sort() self.helper(nums, 0, combinations, None) return combinations def helper(self, nums, start, combinations, combination): if combination == None: combination = list() combinations.append(list(combination)) for i in range(start, len(nums)): if i != start and nums[i] == nums[i - 1]: continue combination.append(nums[i]) self.helper(nums, i + 1, combinations, combination) combination.pop() 数字组合 # Lintcode 135 1. 二叉树遍历解法 # 递归的定义 helper(nums, now, combinations, combination) 递归的拆解 选取第now个数 选取某个数之后，还能再选这个数 不选第now个数 不选某个数之后，就不能再选这个数 递归的出口 选取的数的和 到达target的时候 now越过nums范围的时候 class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinationSum(std::vector\u0026lt;int\u0026gt;\u0026amp; candidates, int target) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::vector\u0026lt;int\u0026gt; combination; // remove duplicates and sort std::set\u0026lt;int\u0026gt; candidates_set{candidates.begin(), candidates.end()}; candidates = {candidates_set.begin(), candidates_set.end()}; Helper(candidates, 0, combinations, combination, target); return combinations; } private: void Helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int now, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; combinations, std::vector\u0026lt;int\u0026gt;\u0026amp; combination, int target) { if (target \u0026lt;= 0) { if (target == 0) { combinations.push_back(combination); } return; } if (now \u0026gt;= nums.size()) { return; } combination.push_back(nums[now]); Helper(nums, now, combinations, combination, target - nums[now]); combination.pop_back(); Helper(nums, now + 1, combinations, combination, target); } }; from typing import ( List, ) class Solution: def combination_sum(self, candidates: List[int], target: int) -\u0026gt; List[List[int]]: combinations = list() candidates = sorted(set(candidates)) self._helper(candidates, 0, combinations, None, target) return combinations def _helper(self, nums, now, combinations, combination, target): if combination is None: combination = list() if target \u0026lt;= 0: if target == 0: combinations.append(list(combination)) # copy value return if now \u0026gt;= len(nums): return combination.append(nums[now]) self._helper(nums, now, combinations, combination, target - nums[now]) combination.pop() self._helper(nums, now + 1, combinations, combination, target) 2. 组合数解法 # 递归的定义 helper(nums, start, combinations, combination) 递归的拆解 选取第start个数 选取某个数之后，还能再选这个数 不选第start个数 不选某个数之后，就不能再选这个数 递归的出口 选取的数的和 到达target的时候 start越过nums范围的时候 class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinationSum(std::vector\u0026lt;int\u0026gt;\u0026amp; candidates, int target) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::vector\u0026lt;int\u0026gt; combination; std::set\u0026lt;int\u0026gt; candidates_set{candidates.begin(), candidates.end()}; candidates = {candidates_set.begin(), candidates_set.end()}; Helper(candidates, 0, combinations, combination, target); return combinations; } private: void Helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int now, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; combinations, std::vector\u0026lt;int\u0026gt;\u0026amp; combination, int target) { if (target == 0) { // target == 0 instead of target - nums[start] == 0 combinations.push_back(combination); return; } for (int i = now; i \u0026lt; nums.size(); ++i) { if (target - nums[i] \u0026gt;= 0) { // needed combination.push_back(nums[i]); Helper(nums, i, combinations, combination, target - nums[i]); // i instead of i + 1 combination.pop_back(); } } } }; from typing import ( List, ) class Solution: def combination_sum(self, candidates: List[int], target: int) -\u0026gt; List[List[int]]: combinations = list() candidates = sorted(set(candidates)) self._helper(candidates, 0, combinations, None, target) return combinations def _helper(self, nums, start, combinations, combination, target): if combination is None: combination = list() if target == 0: combinations.append(list(combination)) return for i in range(start, len(nums)): if (target - nums[i] \u0026gt;= 0): combination.append(nums[i]) self._helper(nums, i, combinations, combination, target - nums[i]) combination.pop() Exercise: 1208. 目标和 # Lintcode 1208 1. Solution # 递归的定义 // 全子集的二叉树解法 helper(nums, start, combinations, combination) start \u0026ndash;\u0026gt; now_index combinations \u0026ndash;\u0026gt; counter // counter 储存最终找到的方案数量 combination \u0026ndash;\u0026gt; now_sum // combination 记录当前所选取的这些方案。 对于当前问题我们不关心+1-2\u0026hellip;， 我们只关心他们最终的和, 不关心是怎么得出这个和的 // s 用于比较 now_sum // 当前问题\nhelper(nums, now_index, now_sum, s, counter) 递归的拆解 // 全子集的二叉树解法 选取第 start 个数 不选第 start 个数 // 当前问题\n第 now_index 个数前面放 \u0026lsquo;+\u0026rsquo; 第 now_index 个数前面放 \u0026lsquo;-\u0026rsquo; 递归的出口 // 全子集的二叉树解法 start 越出 nums 范围的时候 // 当前问题\nnow_index 越出 nums 范围的时候 Wrong answer: counter will always be 0 # class Solution { public: int findTargetSumWays(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int s) { int counter = 0; helper(nums, 0, 0, s, counter); return counter; } private: void helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int now_index, int now_sum, int s, int counter) { // void helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int now_index, int now_sum, int s, int\u0026amp; counter) { // corrected if (now_index == nums.size()) { counter += now_sum == s ? 1 : 0; return; } now_sum += nums[now_index]; helper(nums, now_index + 1, now_sum, s, counter); now_sum -= nums[now_index]; now_sum -= nums[now_index]; helper(nums, now_index + 1, now_sum, s, counter); now_sum += nums[now_index]; } }; from typing import ( List, ) class Solution: def find_target_sum_ways(self, nums: List[int], s: int) -\u0026gt; int: counter = 0 self.helper(nums, 0, 0, s, counter) return counter def helper(self, nums, now_index, now_sum, s, counter): if now_index == len(nums): counter += 1 if now_sum == s else 0 return now_sum += nums[now_index] self.helper(nums, now_index + 1, now_sum, s, counter) now_sum -= nums[now_index] now_sum -= nums[now_index] self.helper(nums, now_index + 1, now_sum, s, counter) now_sum += nums[now_index] Approach 1.1 for python: Time Limit Exceeded # from typing import ( List, ) class Result: def __init__(self, val): self.val = val class Solution: def find_target_sum_ways(self, nums: List[int], s: int) -\u0026gt; int: # counter = 0 counter = Result(0) self.helper(nums, 0, 0, s, counter) # return counter return counter.val def helper(self, nums, now_index, now_sum, s, counter): if now_index == len(nums): # counter += 1 if now_sum == s else 0 counter.val += 1 if now_sum == s else 0 return now_sum += nums[now_index] self.helper(nums, now_index + 1, now_sum, s, counter) now_sum -= nums[now_index] now_sum -= nums[now_index] self.helper(nums, now_index + 1, now_sum, s, counter) now_sum += nums[now_index] Approach 1.2 for python: Time Limit Exceeded # from typing import ( List, ) class Solution: def find_target_sum_ways(self, nums: List[int], s: int) -\u0026gt; int: # counter = 0 counter = [0] self.helper(nums, 0, 0, s, counter) # return counter return counter[0] def helper(self, nums, now_index, now_sum, s, counter): if now_index == len(nums): # counter += 1 if now_sum == s else 0 counter[0] += 1 if now_sum == s else 0 return now_sum += nums[now_index] self.helper(nums, now_index + 1, now_sum, s, counter) now_sum -= nums[now_index] now_sum -= nums[now_index] self.helper(nums, now_index + 1, now_sum, s, counter) now_sum += nums[now_index] Approach 1.3 for python: Time Limit Exceeded # from typing import ( List, ) class Solution: def find_target_sum_ways(self, nums: List[int], s: int) -\u0026gt; int: # counter = 0 # self.helper(nums, 0, 0, s, counter) # return counter return self.helper(nums, 0, 0, s) # def helper(self, nums, now_index, now_sum, s, counter): def helper(self, nums, now_index, now_sum, s): if now_index == len(nums): # counter += 1 if now_sum == s else 0 return 1 if now_sum == s else 0 counter = 0 now_sum += nums[now_index] # self.helper(nums, now_index + 1, now_sum, s, counter) counter += self.helper(nums, now_index + 1, now_sum, s) now_sum -= nums[now_index] now_sum -= nums[now_index] # self.helper(nums, now_index + 1, now_sum, s, counter) counter += self.helper(nums, now_index + 1, now_sum, s) now_sum += nums[now_index] return counter 2. 组合数解法 # 递归的定义 helper(nums, start_index, now_sum, s) 递归的拆解 在 start_index 后面的位置中选择一个 将其符号修改成 \u0026lsquo;+\u0026rsquo; 并进入递归下一层 递归的出口 start_index 越出 nums 范围的时候 // Accepted #include \u0026lt;numeric\u0026gt; // include numeric for std::accumulate class Solution { public: int findTargetSumWays(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int s) { int now_sum = -std::accumulate(nums.begin(), nums.end(), 0); return helper(nums, 0, now_sum, s); } private: int helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start_index, int now_sum, int s) { int counter = now_sum == s ? 1 : 0; for (int i = start_index; i \u0026lt; nums.size(); ++i) { now_sum += 2 * nums[i]; counter += helper(nums, i + 1, now_sum, s); now_sum -= 2 * nums[i]; } return counter; } }; # Time limit exceeded from typing import ( List, ) class Solution: def find_target_sum_ways(self, nums: List[int], s: int) -\u0026gt; int: now_sum = -sum(nums) return self.helper(nums, 0, now_sum, s) def helper(self, nums, start_index, now_sum, s): counter = 1 if now_sum == s else 0 for i in range(start_index, len(nums)): now_sum += 2 * nums[i] counter += self.helper(nums, i + 1, now_sum, s) now_sum -= 2 * nums[i] return counter Chapter 7: 多向递归\u0026ndash;排列类问题 # 全排列 # Lintcode 15 排列问题递归树 # 递归的定义 helper(nums, permutations, permutation, visited) 不需要 start 和 end 两个指针 递归的拆解 在 nums 里选择一个还未选择的数 递归的出口 所有数都被选中的时候 visited 的大小达到了 length 或者 permutation 的大小达到了 length class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; permute(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; permutations; std::vector\u0026lt;int\u0026gt; permutation; std::unordered_set\u0026lt;int\u0026gt; visited; helper(nums, permutations, permutation, visited); return permutations; } private: void helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; permutations, std::vector\u0026lt;int\u0026gt;\u0026amp; permutation, std::unordered_set\u0026lt;int\u0026gt;\u0026amp; visited) { if (visited.size() == nums.size()) { permutations.push_back(permutation); return; // can be ignored } for (int i = 0; i \u0026lt; nums.size(); ++i) { if (visited.find(nums[i]) != visited.end()) { continue; } permutation.push_back(nums[i]); visited.insert(nums[i]); helper(nums, permutations, permutation, visited); permutation.pop_back(); visited.erase(nums[i]); } } }; return?\nfrom typing import ( List, ) class Solution: def permute(self, nums: List[int]) -\u0026gt; List[List[int]]: permutations = list() self.helper(nums, permutations, None, None) return permutations def helper(self, nums, permutations, permutation, visited): if permutation is None: permutation = list() if visited is None: visited = set() if len(visited) == len(nums): permutations.append(list(permutation)) return # can be ignored for num in nums: if num in visited: continue permutation.append(num) visited.add(num) self.helper(nums, permutations, permutation, visited) permutation.pop() visited.remove(num) return?\n带重复元素的排列 # Lintcode 16 递归的定义\nhelper(nums, permutations, permutation, visited) 递归的拆解\n在 nums 里选择一个还未选择的数 且这个数前面相同的数都被选过了 递归的出口\n所有数都被选中的时候 如何从全排列问题转化过来 # class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; permuteUnique(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; permutations; std::sort(nums.begin(), nums.end()); // important std::vector\u0026lt;int\u0026gt; permutation; std::vector\u0026lt;bool\u0026gt; visited(nums.size(), false); helper(nums, permutations, permutation, visited); return permutations; } private: void helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; permutations, std::vector\u0026lt;int\u0026gt;\u0026amp; permutation, std::vector\u0026lt;bool\u0026gt;\u0026amp; visited) { if (permutation.size() == nums.size()) { permutations.push_back(permutation); return; } for (int i = 0; i \u0026lt; nums.size(); ++i) { if (visited[i]) { continue; } if (i - 1 \u0026gt;= 0 \u0026amp;\u0026amp; nums[i] == nums[i - 1] \u0026amp;\u0026amp; !visited[i - 1]) { continue; } permutation.push_back(nums[i]); visited[i] = true; helper(nums, permutations, permutation, visited); permutation.pop_back(); visited[i] = false; } } }; from typing import ( List, ) class Solution: def permute_unique(self, nums: List[int]) -\u0026gt; List[List[int]]: permutations = list() nums = sorted(nums) # sort self.helper(nums, permutations, None, None) return permutations def helper(self, nums, permutations, permutation, visited): if permutation is None: permutation = list() if visited is None: visited = [False] * len(nums) if len(permutation) == len(nums): permutations.append(list(permutation)) return # can be ignored for i in range(len(nums)): if visited[i]: continue if i - 1 \u0026gt;= 0 and nums[i] == nums[i - 1] and not visited[i - 1]: continue permutation.append(nums[i]) visited[i] = True self.helper(nums, permutations, permutation, visited) permutation.pop() visited[i] = False 第k个排列 # Lintcode 388 递归的定义\nhelper(nums, k, result) 由于每次修改的位置在中间 无法为 nums 添加 start 和 end 两个指针 递归的拆解\n找到第 k 个排列的第一个元素 再用剩下的元素到下一层构造后面的部分 递归的出口\nnums 数组为空的时候 class Solution { public: std::string getPermutation(int n, int k) { std::vector\u0026lt;int\u0026gt; nums(n); for (int i = 0; i \u0026lt; n; ++i) { nums[i] = i + 1; } std::string result; helper(nums, k, result); return result; } private: void helper(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int k, std::string\u0026amp; result) { if (nums.size() == 0) { return; } // e.g. numbers of result after fixing the first element int factorial = 1; for (int i = 1; i \u0026lt; nums.size(); ++i) { factorial *= i; } int first = (k - 1) / factorial; result += std::to_string(nums[first]); nums.erase(nums.begin() + first); helper(nums, (k - 1) % factorial + 1, result); // ??? } }; class Solution: def get_permutation(self, n: int, k: int) -\u0026gt; str: nums = list(range(1, n + 1)) result = list() self.helper(nums, k, result) return \u0026#34;\u0026#34;.join(result) def helper(self, nums, k, result): if not nums: return # e.g. numbers of result after fixing the first element factorial = 1 for i in range(1, len(nums)): factorial *= i first = (k - 1) // factorial result.append(str(nums[first])) nums.pop(first) print(nums) self.helper(nums, (k - 1) % factorial + 1, result) # ??? 求解第k个排列 # 求解一个排列是第几个排列 # 下一个排列（非递归） # Lintcode 52 class Solution { public: std::vector\u0026lt;int\u0026gt; nextPermutation(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { int index = -1; // e.g. nums={3,2,5,4,1}, find index of 2 for (int i = nums.size() - 2; i \u0026gt;= 0; --i) { if (nums[i] \u0026lt; nums[i + 1]) { index = i; break; } } if (index == -1) { reverse(nums, 0, nums.size() - 1); return nums; } // set initial last_bigger is index of 5, and then find the smallest bigger number than 2 int last_bigger = index + 1; for (int i = nums.size() - 1; i \u0026gt; index; --i) { if (nums[i] \u0026gt; nums[index]) { last_bigger = i; break; } } // e.g. {3,2,5,4,1} --\u0026gt; {3,4,5,2,1} int temp = nums[index]; nums[index] = nums[last_bigger]; nums[last_bigger] = temp; // e.g. {3,4,5,2,1} --\u0026gt; {3,4,1,2,5} reverse(nums, index + 1, nums.size() - 1); return nums; } private: void reverse(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, int end) { while (start \u0026lt; end) { int temp = nums[start]; nums[start] = nums[end]; nums[end] = temp; ++start; --end; } } }; from typing import ( List, ) class Solution: def next_permutation(self, nums: List[int]) -\u0026gt; List[int]: index = -1 for i in range(len(nums) - 2, -1, -1): if nums[i] \u0026lt; nums[i + 1]: index = i break else: return nums[::-1] last_bigger = index + 1 for i in range(len(nums) - 1, index, -1): if nums[i] \u0026gt; nums[index]: last_bigger = i break nums[index], nums[last_bigger] = nums[last_bigger], nums[index] return nums[: index + 1] + nums[len(nums) - 1 : index : -1] Exercise: 990. 美丽排列 # Lintcode 990 递归的定义\nhelper(n, visited, counter) 不需要知道具体排列 把返回值用上可以省略 counter 递归的拆解\n选一个还未选择的且能放到第i位的数 递归的出口\n所有数都被选中的时候 class Solution { public: int countArrangement(int N) { std::unordered_set\u0026lt;int\u0026gt; visited; return helper(N, visited); } private: int helper(int n, std::unordered_set\u0026lt;int\u0026gt;\u0026amp; visited) { if (visited.size() == n) { return 1; } int counter = 0; for (int num = 1; num \u0026lt;= n; ++num) { if (visited.find(num) != visited.end()) { continue; } // (visited.size() + 1) 代表`i` if (num % (visited.size() + 1) != 0 \u0026amp;\u0026amp; (visited.size() + 1) % num != 0) { continue; } visited.insert(num); counter += helper(n, visited); visited.erase(num); } return counter; } }; class Solution: def count_arrangement(self, n: int) -\u0026gt; int: return self.helper(n, None) def helper(self, n, visited): if visited is None: visited = set() if len(visited) == n: return 1 counter = 0 for num in range(1, n + 1): if num in visited: continue # (visited.size() + 1) 代表`i` if num % (len(visited) + 1) and (len(visited) + 1) % num: continue visited.add(num) counter += self.helper(n, visited) visited.remove(num) return counter Chapter 8: 非递归\u0026ndash;二叉树类 # 递归改非递归 # 尾递归 改成迭代形式：只要是线性递归，都能改成迭代形式 非尾递归 模拟系统调用栈：当遇到非线性递归（二叉递归，多叉递归） 用特殊思路来完成递归要做的事儿 Morris算法 不算是递归改成非递归 用栈实现二叉树非递归遍历 # 前序遍历 # Lintcode 66 #include \u0026lt;deque\u0026gt; struct State { State(TreeNode* node, int count) : node(node), count(count) {} TreeNode* node; int count; }; class Solution { public: std::vector\u0026lt;int\u0026gt; preorderTraversal(TreeNode* root) { std::deque\u0026lt;State*\u0026gt; stack; stack.push_back(new State(root, 0)); std::vector\u0026lt;int\u0026gt; values; while (!stack.empty()) { State* now = stack.back(); stack.pop_back(); TreeNode* node = now-\u0026gt;node; int count = now-\u0026gt;count; if (node == nullptr) { continue; } if (count == 0) { stack.push_back(new State(node, 3)); // can be ignore stack.push_back(new State(node-\u0026gt;right, 0)); stack.push_back(new State(node, 2)); // can be ignore stack.push_back(new State(node-\u0026gt;left, 0)); stack.push_back(new State(node, 1)); } if (count == 1) { values.push_back(node-\u0026gt;val); } } return values; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def preorder_traversal(self, root: TreeNode) -\u0026gt; List[int]: stack = [(root, 0)] values = [] while stack: node, count = stack.pop() if node is None: continue if count == 0: stack.append((node, 3)) # can be ignore stack.append((node.right, 0)) stack.append((node, 2)) # can be ignore stack.append((node.left, 0)) stack.append((node, 1)) if count == 1: values.append(node.val) return values 中序遍历 # Lintcode 67 #include \u0026lt;deque\u0026gt; struct State { State(TreeNode* node, int count) : node(node), count(count) {} TreeNode* node; int count; }; class Solution { public: std::vector\u0026lt;int\u0026gt; inorderTraversal(TreeNode* root) { std::deque\u0026lt;State*\u0026gt; stack; stack.push_back(new State(root, 0)); std::vector\u0026lt;int\u0026gt; values; while (!stack.empty()) { State* now = stack.back(); stack.pop_back(); TreeNode* node = now-\u0026gt;node; int count = now-\u0026gt;count; if (node == nullptr) { continue; } if (count == 0) { stack.push_back(new State(node, 3)); // can be ignore stack.push_back(new State(node-\u0026gt;right, 0)); stack.push_back(new State(node, 2)); stack.push_back(new State(node-\u0026gt;left, 0)); stack.push_back(new State(node, 1)); // can be ignore } if (count == 2) { values.push_back(node-\u0026gt;val); } } return values; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def inorder_traversal(self, root: TreeNode) -\u0026gt; List[int]: stack = [(root, 0)] values = [] while stack: node, count = stack.pop() if node is None: continue if count == 0: stack.append((node, 3)) # can be ignore stack.append((node.right, 0)) stack.append((node, 2)) stack.append((node.left, 0)) stack.append((node, 1)) # can be ignore if count == 2: values.append(node.val) return values 后序遍历 # Lintcode 68 #include \u0026lt;deque\u0026gt; struct State { State(TreeNode* node, int count) : node(node), count(count) {} TreeNode* node; int count; }; class Solution { public: std::vector\u0026lt;int\u0026gt; postorderTraversal(TreeNode* root) { std::deque\u0026lt;State*\u0026gt; stack; stack.push_back(new State(root, 0)); std::vector\u0026lt;int\u0026gt; values; while (!stack.empty()) { State* now = stack.back(); stack.pop_back(); TreeNode* node = now-\u0026gt;node; int count = now-\u0026gt;count; if (node == nullptr) { continue; } if (count == 0) { stack.push_back(new State(node, 3)); stack.push_back(new State(node-\u0026gt;right, 0)); stack.push_back(new State(node, 2)); // can be ignore stack.push_back(new State(node-\u0026gt;left, 0)); stack.push_back(new State(node, 1)); // can be ignore } if (count == 3) { values.push_back(node-\u0026gt;val); } } return values; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def postorder_traversal(self, root: TreeNode) -\u0026gt; List[int]: stack = [(root, 0)] values = [] while stack: node, count = stack.pop() if node is None: continue if count == 0: stack.append((node, 3)) stack.append((node.right, 0)) stack.append((node, 2)) # can be ignore stack.append((node.left, 0)) stack.append((node, 1)) # can be ignore if count == 3: values.append(node.val) return values 用Morris算法实现二叉树非递归遍历 # 前序遍历 # Lintcode 66 树上的节点最多只会被访问两次，而对于没有左孩子的节点只会访问一次（把两次访问合二为一）\ne.g. 2在中序遍历下的前驱节点是7; 1在中序遍历下的前驱节点是4\nclass Solution { public: std::vector\u0026lt;int\u0026gt; preorderTraversal(TreeNode* root) { std::vector\u0026lt;int\u0026gt; values; TreeNode* now = root; while (now != nullptr) { if (now-\u0026gt;left != nullptr) { TreeNode* temp = now-\u0026gt;left; while (temp-\u0026gt;right != nullptr \u0026amp;\u0026amp; temp-\u0026gt;right != now) { temp = temp-\u0026gt;right; // 一直找到temp 为中序遍历的前驱节点 } if (temp-\u0026gt;right == now) { temp-\u0026gt;right = nullptr; now = now-\u0026gt;right; } else { values.push_back(now-\u0026gt;val); temp-\u0026gt;right = now; now = now-\u0026gt;left; } } else { values.push_back(now-\u0026gt;val); now = now-\u0026gt;right; } } return values; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def preorder_traversal(self, root: TreeNode) -\u0026gt; List[int]: values = [] now = root while now: if now.left: temp = now.left while temp.right and temp.right != now: temp = temp.right if temp.right == now: temp.right = None now = now.right else: values.append(now.val) temp.right = now now = now.left else: values.append(now.val) now = now.right return values 中序遍历 # Lintcode 67 class Solution { public: std::vector\u0026lt;int\u0026gt; inorderTraversal(TreeNode* root) { std::vector\u0026lt;int\u0026gt; values; TreeNode* now = root; while (now != nullptr) { if (now-\u0026gt;left != nullptr) { TreeNode* temp = now-\u0026gt;left; while (temp-\u0026gt;right != nullptr \u0026amp;\u0026amp; temp-\u0026gt;right != now) { temp = temp-\u0026gt;right; // 一直找到temp 为中序遍历的前驱节点 } if (temp-\u0026gt;right == now) { values.push_back(now-\u0026gt;val); // the only difference against the preorder_traversal temp-\u0026gt;right = nullptr; now = now-\u0026gt;right; } else { temp-\u0026gt;right = now; now = now-\u0026gt;left; } } else { values.push_back(now-\u0026gt;val); now = now-\u0026gt;right; } } return values; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def inorder_traversal(self, root: TreeNode) -\u0026gt; List[int]: values = [] now = root while now: if now.left: temp = now.left while temp.right and temp.right != now: temp = temp.right if temp.right == now: values.append(now.val) # the only difference against the preorder_traversal temp.right = None now = now.right else: temp.right = now now = now.left else: values.append(now.val) now = now.right return values 后序遍历 # Lintcode 68 先右子树再左子树的前序遍历 VS 后序遍历 # 刚好是反着的关系\ndef helper(self, root, nodes): if root is None: return nodes.append(root.val) self.helper(root.right, nodes) self.helper(root.left, nodes) e.g. result = {2,1,5,4,3,7,6}\ndef helper(self, root, nodes): if root is None: return self.helper(root.left, nodes) self.helper(root.right, nodes) nodes.append(root.val) e.g. result = {6,7,3,4,5,1,2}\n如何通过Morris解决后序遍历：先将问题转化成异样的前序遍历，然后再翻转Morris的结果 # class Solution { public: std::vector\u0026lt;int\u0026gt; postorderTraversal(TreeNode* root) { std::vector\u0026lt;int\u0026gt; values; TreeNode* now = root; while (now != nullptr) { if (now-\u0026gt;right != nullptr) { TreeNode* temp = now-\u0026gt;right; while (temp-\u0026gt;left != nullptr \u0026amp;\u0026amp; temp-\u0026gt;left != now) { temp = temp-\u0026gt;left; } if (temp-\u0026gt;left == now) { temp-\u0026gt;left = nullptr; now = now-\u0026gt;left; } else { values.push_back(now-\u0026gt;val); temp-\u0026gt;left = now; now = now-\u0026gt;right; } } else { values.push_back(now-\u0026gt;val); now = now-\u0026gt;left; } } std::reverse(values.begin(), values.end()); return values; } }; from typing import ( List, ) from lintcode import ( TreeNode, ) class Solution: def postorder_traversal(self, root: TreeNode) -\u0026gt; List[int]: values = list() now = root while now: if now.right: temp = now.right while temp.left and temp.left != now: temp = temp.left if temp.left == now: temp.left = None now = now.left else: values.append(now.val) temp.left = now now = now.right else: values.append(now.val) now = now.left values.reverse() return values 两种解法对比 # 用Morris实现的解法 时间复杂度：O(n) 空间复杂度：O(1) 对树结构的修改：是（之后有重新修改回去了） 用栈来实现的解法（递归与非递归都一样） 时间复杂度：O(n) 空间复杂度：O(n) 对树结构的修改：否 Exercise: 169. 汉诺塔 # Lintcode 169 #include \u0026lt;deque\u0026gt; struct Node { Node(int n, char start, char end, char temp) : n(n), start(start), end(end), temp(temp) {} Node* get_left() { // int n = this-\u0026gt;n - 1; // char start = this-\u0026gt;start; // char end = this-\u0026gt;temp; // char temp = this-\u0026gt;end; // return new Node(n, start, end, temp); return new Node(n - 1, start, temp, end); } Node* get_right() { // int n = this-\u0026gt;n - 1; // char start = this-\u0026gt;temp; // char end = this-\u0026gt;end; // char temp = this-\u0026gt;start; // return new Node(n, start, end, temp); return new Node(n - 1, temp, end, start); } std::string move() { // return std::string(\u0026#34;from \u0026#34;) + this-\u0026gt;start + \u0026#34; to \u0026#34; + this-\u0026gt;end; return std::string(\u0026#34;from \u0026#34;) + start + \u0026#34; to \u0026#34; + end; } int n; char start, end, temp; }; struct State { State(Node* node, int count) : node(node), count(count) {} Node* node; int count; }; class Solution { public: std::vector\u0026lt;std::string\u0026gt; towerOfHanoi(int n) { std::deque\u0026lt;State*\u0026gt; stack; stack.push_back(new State(new Node(n, \u0026#39;A\u0026#39;, \u0026#39;C\u0026#39;, \u0026#39;B\u0026#39;), 0)); std::vector\u0026lt;std::string\u0026gt; moves; while (!stack.empty()) { State* now = stack.back(); stack.pop_back(); Node* node = now-\u0026gt;node; int count = now-\u0026gt;count; if (node-\u0026gt;n == 0) { continue; } if (count == 0) { stack.push_back(new State(node, 3)); // can be ignore stack.push_back(new State(node-\u0026gt;get_right(), 0)); stack.push_back(new State(node, 2)); stack.push_back(new State(node-\u0026gt;get_left(), 0)); stack.push_back(new State(node, 1)); // can be ignore } if (count == 2) { moves.push_back(node-\u0026gt;move()); } } return moves; } }; from typing import ( List, ) class Node: def __init__(self, n, start, end, temp): self.n = n self.start = start self.end = end self.temp = temp def get_left(self): n = self.n - 1 start = self.start end = self.temp temp = self.end return Node(n, start, end, temp) def get_right(self): n = self.n - 1 start = self.temp end = self.end temp = self.start return Node(n, start, end, temp) def move(self): return \u0026#34;from \u0026#34; + self.start + \u0026#34; to \u0026#34; + self.end class Solution: def tower_of_hanoi(self, n: int) -\u0026gt; List[str]: stack = [(Node(n, \u0026#39;A\u0026#39;, \u0026#39;C\u0026#39;, \u0026#39;B\u0026#39;), 0)] moves = [] while stack: node, count = stack.pop() if node.n == 0: continue if count == 0: stack.append((node, 3)) # can be ignore stack.append((node.get_right(), 0)) stack.append((node, 2)) stack.append((node.get_left(), 0)) stack.append((node, 1)) # can be ignore if count == 2: moves.append(node.move()) return moves Chapter 9: 非递归\u0026ndash;排列组合类 # 组合类问题非递归（三种解法） # Lintcode 17 二叉树遍历解法 # 1. 参考：二叉树遍历解法(递归) struct State { State(int node, int count) : node(node), count(count) {} int node, count; }; class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; subsets(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::vector\u0026lt;int\u0026gt; combination; std::sort(nums.begin(), nums.end()); std::deque\u0026lt;State*\u0026gt; stack; stack.push_back(new State(0, 0)); while (!stack.empty()) { State* now = stack.back(); stack.pop_back(); int node = now-\u0026gt;node; int count = now-\u0026gt;count; if (node == nums.size()) { combinations.push_back(combination); continue; } if (count == 0) { stack.push_back(new State(node, 3)); stack.push_back(new State(node + 1, 0)); stack.push_back(new State(node, 2)); stack.push_back(new State(node + 1, 0)); stack.push_back(new State(node, 1)); } if (count == 1) { combination.push_back(nums[node]); } if (count == 2) { combination.erase(combination.begin() + combination.size() - 1); } } return combinations; } }; from typing import ( List, ) class Solution: def subsets(self, nums: List[int]) -\u0026gt; List[List[int]]: combination = [] combinations = [] nums = sorted(nums) stack = [(0, 0)] while stack: node, count = stack.pop() if node == len(nums): combinations.append(list(combination)) continue if count == 0: stack.append((node, 3)) stack.append((node + 1, 0)) stack.append((node, 2)) stack.append((node + 1, 0)) stack.append((node, 1)) if count == 1: combination.append(nums[node]) if count == 2: combination.pop() return combinations 组合数思路解法 # 2. 参考：组合数解法(递归) struct State { State(int node, int count) : node(node), count(count) {} int node, count; }; class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; subsets(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::vector\u0026lt;int\u0026gt; combination; std::sort(nums.begin(), nums.end()); std::deque\u0026lt;State*\u0026gt; stack; stack.push_back(new State(0, 0)); while (!stack.empty()) { State* now = stack.back(); stack.pop_back(); int node = now-\u0026gt;node; int count = now-\u0026gt;count; if (count == 0) { combinations.push_back(combination); for (int i = node; i \u0026lt; nums.size(); ++i) { stack.push_back(new State(i, 2)); stack.push_back(new State(i + 1, 0)); stack.push_back(new State(i, 1)); } } if (count == 1) { combination.push_back(nums[node]); } if (count == 2) { combination.erase(combination.begin() + combination.size() - 1); } } return combinations; } }; from typing import ( List, ) class Solution: def subsets(self, nums: List[int]) -\u0026gt; List[List[int]]: combination = [] combinations = [] nums = sorted(nums) stack = [(0, 0)] while stack: node, count = stack.pop() if count == 0: combinations.append(list(combination)) for i in range(node, len(nums)): stack.append((i, 2)) stack.append((i + 1, 0)) stack.append((i, 1)) if count == 1: combination.append(nums[node]) if count == 2: combination.pop() return combinations 二进制枚举解法 # Time complexity O(n * 2^n)\nclass Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; subsets(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; combinations; std::sort(nums.begin(), nums.end()); int n = nums.size(); for (int i = 0; i \u0026lt; (1 \u0026lt;\u0026lt; n); ++i) { std::vector\u0026lt;int\u0026gt; combination; for (int j = 0; j \u0026lt; n; ++j) { if ((i \u0026amp; (1 \u0026lt;\u0026lt; j)) != 0) { combination.push_back(nums[j]); } } combinations.push_back(combination); } return combinations; } }; from typing import ( List, ) class Solution: def subsets(self, nums: List[int]) -\u0026gt; List[List[int]]: combinations = [] nums = sorted(nums) n = len(nums) for i in range(0, (1 \u0026lt;\u0026lt; n)): combination = [] for j in range(n): if i \u0026amp; (1 \u0026lt;\u0026lt; j): combination.append(nums[j]) combinations.append(combination) return combinations 排列类问题非递归 # Lintcode 15 用手写栈模拟递归解法 # 参考：全排列 struct State { State(int node, int count) : node(node), count(count) {} int node, count; }; class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; permute(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;int\u0026gt; permutation; std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; permutations; std::unordered_set\u0026lt;int\u0026gt; visited; std::deque\u0026lt;State*\u0026gt; stack; stack.push_back(new State(0, 0)); while (!stack.empty()) { State* now = stack.back(); stack.pop_back(); int node = now-\u0026gt;node; int count = now-\u0026gt;count; if (count == 0) { // didn\u0026#39;t use node here when count is 0 if (visited.size() == nums.size()) { permutations.push_back(permutation); continue; } for (int i = 0; i \u0026lt; nums.size(); ++i) { // begin from 0 instead of node if (visited.find(nums[i]) != visited.end()) { continue; } stack.push_back(new State(i, 2)); stack.push_back(new State(i + 1, 0)); // here i + 1 can be anything stack.push_back(new State(i, 1)); } } if (count == 1) { permutation.push_back(nums[node]); visited.insert(nums[node]); } if (count == 2) { permutation.pop_back(); visited.erase(nums[node]); } } return permutations; } }; from typing import ( List, ) class Solution: def permute(self, nums: List[int]) -\u0026gt; List[List[int]]: permutation = list() permutations = list() visited = set() stack = [(0, 0)] while stack: node, count = stack.pop() if count == 0: # didn\u0026#39;t use node here when count is 0 if len(visited) == len(nums): permutations.append(list(permutation)) continue for i in range(len(nums)): # begin from 0 instead of node if nums[i] in visited: continue stack.append((i, 2)) stack.append((i + 1, 0)) # here i + 1 can be anything stack.append((i, 1)) if count == 1: permutation.append(nums[node]) visited.add(nums[node]) if count == 2: permutation.pop() visited.remove(nums[node]) return permutations 下一个排列解法 # 参考：下一个排列（非递归） class Solution { public: std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; permute(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; permutations; std::sort(nums.begin(), nums.end()); // only for corner case where nums is [] if (nums.empty()) { std::vector\u0026lt;int\u0026gt; permutation; permutations.push_back(permutation); } while (!nums.empty()) { std::vector\u0026lt;int\u0026gt; permutation; for (int i = 0; i \u0026lt; nums.size(); ++i) { permutation.push_back(nums[i]); } permutations.push_back(permutation); nums = NextPermutation(nums); } return permutations; } private: std::vector\u0026lt;int\u0026gt; NextPermutation(std::vector\u0026lt;int\u0026gt;\u0026amp; nums) { int index = -1; for (int i = nums.size() - 2; i \u0026gt;= 0; --i) { if (nums[i] \u0026lt; nums[i + 1]) { index = i; break; } } if (index == -1) { // Reverse(nums, 0, nums.size() - 1); // return nums; return {}; } int last_bigger = index + 1; for (int i = nums.size() - 1; i \u0026gt; index; --i) { if (nums[i] \u0026gt; nums[index]) { last_bigger = i; break; } } int temp = nums[index]; nums[index] = nums[last_bigger]; nums[last_bigger] = temp; Reverse(nums, index + 1, nums.size() - 1); return nums; } void Reverse(std::vector\u0026lt;int\u0026gt;\u0026amp; nums, int start, int end) { while (start \u0026lt; end) { int temp = nums[start]; nums[start] = nums[end]; nums[end] = temp; ++start; --end; } } }; from typing import ( List, ) class Solution: def permute(self, nums: List[int]) -\u0026gt; List[List[int]]: permutations = list() nums = sorted(nums) while nums is not None: permutations.append(list(nums)) nums = self.next_permutation(nums) return permutations def next_permutation(self, nums: List[int]) -\u0026gt; List[int]: index = -1 for i in range(len(nums) - 2, -1, -1): if nums[i] \u0026lt; nums[i + 1]: index = i break else: # return nums[::-1] return None last_bigger = index + 1 for i in range(len(nums) - 1, index, -1): if nums[i] \u0026gt; nums[index]: last_bigger = i break nums[index], nums[last_bigger] = nums[last_bigger], nums[index] return nums[: index + 1] + nums[len(nums) - 1 : index : -1] We can convert kth permutation problem as well.\n第k个排列是尾递归，可以转化成迭代的形式\nfor 1 .. n!: get kth permutation\n","date":"17 June 2022","externalUrl":null,"permalink":"/blog/note_recursion/","section":"Blog","summary":"Covered all topics of Recursion\n","title":"Recursion","type":"blog"},{"content":" Implemented astrophysical simulation solved N-body problem using Barnes-Hut algorithm with MPICH by C++ Programmed OpenGL to visualize the movement of the bodies in the domain by C++ Analyzed performance with the number of bodies, processors, timesteps, iterations Hardware Details \u0026amp; OS Version # Processor: 3.6GHz 10-core Intel Core i9 Memory: 32GB 2667 MHz DDR4 OS version: macOS Big Sur Version 11.6.1 Data # Data1: nb-10.txt Data1: nb-100.txt Data1: nb-100000.txt Analyze # My approach can be represented by two main parts: the first is constructing a sequential version of the Barnes-Hut Algorithm and implementing OpenGL to visualize the result, the second is implementing API of MPI to take advantage of parallel from different processes.\nIn my second part, I tried to implement the method introduced by Grama in the reference paper which is “Scalable Parallel Formulations of the Barnes-Hut Method for n-Body Simulations”, however, in my implementation, after each process computes the trees locally, I cannot get the ideal performance on merging trees part. The paper said the only information that needs to be communicated for merging trees is the number of particles and the center of mass. From my understanding I need to construct a parent node above previous processes, however, I found out that I need to construct the whole new tree in the parent process and other processes need to send the nodes messages of the local tree to parent process because different processes do not share memories and after “Allgather” method, then other processes can traverse the tree during force computing part. These messages passing in tree merging impact performance dramatically in my application and thus in my final submission I didn’t utilize the tree merging method introduced by Grama in the paper.\nHowever, I implement force computation with MPI and get an obvious performance boost when running my program in multiple processes scenarios. I split input nodes which used to construct the tree into #(number of inputs/number of processes) parts. I set the last rank to be the root process that implements “Gather” and “Scatter” method because, in this setting, it can handle the situation that processes cannot split input nodes equally. And the root process which implements the “Gather” and “Scatter” method does not need to pay attention to the size of messages, because the unevenly be separated input nodes will within the last rank which is the root process.\nFig. 1. Performance measurement by fixing the number of steps(-s parameter to 1000), the number of threshold for MAC(-t parameter to 1.0) and the timestep(-d parameter to 0.04).\nIn Fig. 1., I measured the performance of my program from different input sizes by fixing other parameters. In a small input size case(“nb-10.txt”), my program cannot take advantage of the parallel and it can get the best performance when the number of processes is one that is sequentially implemented as shown in the first graph in Fig 1. The main reason is that in a modern computer, the CPU is very powerful it can handle instructions very fast in a sequential style in small input size case. In addition, the small input size will have more overhead on message passing if the number of processes is greater than one which is parallelly implemented. Thus when I increase the number of processes, it will have more overhead on message passing and it will impact the performance of the program. In the (“nb-100.txt”) input scenario, our program can get benefit from parallel. It can get the best performance when the number of processes is around 10 shown as in the second graph in Fig 1, the main reason is that my processor has 10 cores and thus different cores in the processor can handle different processes separately. When I increase the number of processes from 1 to 10, the performance is improved significantly, this indicates that we can get benefit from the computing force in a parallel style. However, when the number of processes is greater than 10, the performance is decreased, some processes are idle during the run time because all 10 cores in processor work on 10 processes separately and the scheduler will let other processes wait for these 10 processes. In a large input size case(“nb-100000.txt”) case, my program has similar improvement when increasing the number of processes. It also can get benefit from the parallel and it can get best performance when the number of processes is around five. However, the performance decreased earlier than the (“nb-100.txt”) case, the main reason is that a large input size has more overhead on the communication between processes during computing force. It has to gather all data computed in local processes in each iteration step and thus the running time in large input size case increasing earlier than previous case. Fig. 2. Average running time by fixing the number of processes(-np parameter to 4), the number of steps(-s parameter to 1000) and the timestep(-d parameter to 0.04). NAÏVE is the quadratic relationship.\nThe running time results in Fig 2 indicates that the Barnes-Hut approximation can significantly speed up computation about 2.5X when theta is setting to 1.0 or 1.5 comparing to the NAÏVE case. As expected, the naïve approach exhibits a quadratic relationship, whereas increasing the theta parameter leads to faster calculations. It does not fare better than the naïve approach until processing the largest input file. Until that point, the overhead of quadtree construction and center of mass calculation outstrips any gains in force estimation. For theta=1 and theta=1.5, however, we see a significant improvement in running time, with similar performance for each. ","date":"24 December 2021","externalUrl":null,"permalink":"/projects/mpi_barnes_hut/","section":"Projects","summary":" Implemented astrophysical simulation solved N-body problem using Barnes-Hut algorithm with MPICH by C++ Programmed OpenGL to visualize the movement of the bodies in the domain by C++ Analyzed performance with the number of bodies, processors, timesteps, iterations ","title":"Exploring Parallel Processes Programming with MPICH Simulating Barnes Hut Algorithm","type":"projects"},{"content":" Implemented concurrency programming model to compute BST(binary search tree) equivalence with Go Implemented channels, go-routines, and signaling with Go Programmed threads to parallelize hash operations with Go Assembled a concurrent buffer to secure communication among threads Analyzed performance among all the implementations Hardware Details \u0026amp; OS Version # CPU: 2 GHz Quad-Core Intel Core i5 OS: macOS Big Sur version 11.6 Data # Data1: simple Data2: coarse Data3: fine Analyze HashTime # Fig. 1. Hash value computing time compared to Goroutines. Different input files has different hash-workers experiments. Computing time are all measured in seconds. The x-axis represents the number of hash-workers.\nThe above graph shows the computing time for each BST hashes. The red circle indicates the number of hash-workers equal to the input size for a specific input file, for example, “simple.txt” has an input size of 12. When the input size is small, the sequential version is the fastest. The main reason is that our CPU can solve the problem very fast in sequential when the input size is small and the overhead of the data communication in parallel also influence the performance, and thus in small input size case, the sequential version is the fastest. In a small input size case, the sequential version is about 5X times faster than my second implementation that iterates over the available BSTs. However, when the input size increases and the input dimension increases, we can see from the above graph that the program can get benefit from Goroutine implementations compared to the sequential version.\nMy second implementation has better performance than my first implementation in “coarse.txt” and “fine.txt” cases. The main reason is that my laptop has 4 core CPUs, when the number of goroutines reaches the number of cores of my laptop, it can map different jobs to different CPU processors. As the second graph shows, the benefits of parallelism without high amounts of overhead have significant improvement when the number of hash-workers is 4. The third graph above has the same trend when the number of hash-worker is 4. My second implementation performs about 4X times better than my first implementation in both these cases.\nGo can manage goroutines very well. The main reason is that goroutines have growable segmented stacks and they grows as needed, and the Go runtime does the scheduling, not the OS.\nHowever, I still think we have to worry about or pay attention to the number of goroutines because when we know the hardware, we can get the best performance for our program.\nIf the number of goroutines is less than the number of CPU cores, the program cannot get the best performance on specific hardware even if the Go runtime can schedule the goroutine very well. If the number of goroutines is much more than the number of CPU cores, it will also have some overhead when creating the segment stack even if only 3 registers need when goroutine switch context, and thus I think we also can’t get ideal performance on this setup. In both “coarse.txt” and “fine.txt” cases, I can get the best performance when my second implementation has 4 hash-workers which is the number of the CPU cores and it can also get ideal performance when the number of hash-workers is in a reasonable range. So I think this is kind of proof.\nAnalyze Averaged HashGroupTime # Fig. 2. Averaged hashGroupTime for different input file. There are four different implementations tested on each input file: Sequential version, Channel version, Lock version and Extra Implementation for extra credit. All computing time are measured in seconds.\nIn the “simple.txt” scenario, the sequential version implementation is the fastest. In both the “coarse.txt” and “fine.txt” scenarios, the Channel version implementation is the fastest. The main reason is that when the input size is small, the concurrency mechanism has the drawback that it has to communicate between other goroutines when modifying shared data and in this scenario, this has a significant influence. However, when the input size is large like in the “coarse.txt” case which almost doesn’t have a high amount of overhead, we can see both the Channel version and Lock version can have better performance than the Sequential version and Channel version is the fastest.\nIn addition, when it comes to the “fine.txt” case, the program also has better performance on the Channel version and the Lock version implementation than the Sequential version, this indicates that our program can scale great in both cases and Channel also is the fastest.\nThe third implementation has more overhead because other threads must wait for the lock to unlock, and in this trying to unlock process, increases the complexity and overhead. We have to handle the lock and unlock procedure by ourselves.\nIn the “coarse.txt” case, the Channel version is 2.5X faster than a single thread, and the Lock version is 2.4X faster than a single thread. In addition, the channel implementation is much simpler, the main reason is that I don’t have to worry about the lock, the channel is threaded safe and only one channel changes the data at a time. Furthermore, this result in the channel version has a much simpler implementation, because the channel implicitly handles the lock mechanism, and thus the channel in Go is threaded safe. We don’t have to bother with lock explicitly and this decreases lots of complexity.\nFor extra credits, I also implement fine-grain synchronization to allow up to data-workers threads to access the data structure at once. It was not access to the shared data structure a bottleneck before. The main reason is that even if it has more parallelism it also has more overhead and it also has to wait for other threads to unlock the shared data structure. These extra implementations also are not simpler than the previous three implementations, because we have to handle the different threads to access data and avoid concurrency errors.\nAnalyze Averaged CompareTreeTime # Fig. 3. Averaged compareTreeTime for different input file. There are three different implementation for each input file: Sequential, Goroutine per BST and Concurrent Buffer. All computing time measured in seconds.\nThe above graph shows the compareTreeTime for each input file on different implementations. When comparing the performance of different implementations, the Go per BST has better performance than the Concurrent Buffer, and the Go per BST implementation also has less complexity than the Concurrent Buffer implementation. The main reason is that when we explore concurrent buffer implementation we have to contain mutex to prevent concurrency errors and it also has additional conditional variables to deal with the buffer size. And thus the Go per BST has better performance and less complexity and less overhead.\nEven if the Concurrent buffer is slower than the Go per BST, it still has better performance than a single thread. The Concurrent buffer is 2X faster than single thread implementation and the Go per BST is 2.3X faster than single thread implementation in the “coarse.txt” case. In the “fine.txt” case, the Concurrent buffer is 1.3X faster than single thread implementation and the Go per BST has 2.8X faster than single thread implementation. It is not worth managing the thread pool, because the go can handle the goroutines correctly most of the time if we give a reasonable number of goroutines in our program. In addition, managing the thread pool also increases the complexity and overhead of our program.\nAt last, I spent about 11 days in this lab. I don’t have Golang experience before, and thus I spent 3 days on Golang syntax and concurrent knowledge and the other 8 days to work out the result and the report. Although I spent tons of time on this lab, I like the experience in this lab, because I learned a lot from it, like how to debugging and how to manage goroutines and channel correctly, etc. Thanks!\n","date":"24 October 2021","externalUrl":null,"permalink":"/projects/go_tree_comparison/","section":"Projects","summary":" Implemented concurrency programming model to compute BST(binary search tree) equivalence with Go Implemented channels, go-routines, and signaling with Go Programmed threads to parallelize hash operations with Go Assembled a concurrent buffer to secure communication among threads Analyzed performance among all the implementations ","title":"Exploring Concurrency Programming with Go","type":"projects"},{"content":" Implemented base sequential version of K-Means algorithm on CPU by C++ Implemented first parallel version of K-Means with Thrust primitives and a GroupBy-Aggregate by C++ Implemented second parallel version of K-Means with CUDA by C++ Implemented third parallel version of K-Means with CUDA on Shared Memory by C++ Analyzed speedup among all the implementations Hardware Details \u0026amp; OS Version \u0026amp; Settings # GPU: the Codio environment(Tesla T4, Driver Version: 460.91.03 , CUDA Version: 11.2) CPU: the Codio environment(Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz, 4 cores) OS: the Codio environment(Ubuntu 18.04.2 LTS (GNU/Linux 5.4.0-1054-aws x86_64)) the data in this report was collected when I set “threshold argument” to 1e-6.\nData # Data1: size = 2048, dim = 16, c = 16 Data2: size = 16384, dim = 24, c = 16 Data3: size = 65536, dim = 32, c = 16 The Fastest Implementation # For averaged elapsed time per iteration, CUDA Shared Memory is always the fastest implementation on different input sizes. (Fig. 1, left) Because the shared memory in each block is very fast, which means the processing unit for each thread can access data in shared memory very fast after transferring data from global memory to the shared memory.\nFor total elapsed time (E2E runtime), sequential version is the fastest when the input size is very small and CUDA Shared Memory is the fastest when the input size is very large. (Fig. 1, right) The main reason is that the CPU is very powerful in computing a single thread and the CPU doesn’t need to transfer data between host and device. When the input size is very small, it is the fastest. However, when the input size is large, the power and advantage of parallel are obvious, and the overhead over transferring data can be ignored, and in this case, the CUDA Shared Memory is my fastest implementation.\nCompare and Analyze Performance Among Four Implementations # The CUDA Shared Memory has the smallest elapsed time per iteration (Fig. 1, left) but it also has the largest overhead on transferring data between host and device and between the device and shared memory. It is the fastest implementation when the input size is very large. As the amount of input data increases, it can get more benefit from parallelism and the influence of the overhead can be ignored. (Fig. 1, right)\nThe CUDA Basic is slightly slower than CUDA Shared Memory on converging speed (Fig. 1, left) and it also has a slightly smaller overhead on transferring data than CUDA Shared Memory, because it doesn’t have to transfer data between the device and shared memory. (Fig. 1, right)\nThe Thrust is always converging slower than CUDA Basic and has a similar overhead as CUDA Basic. However, it can always converge faster than Sequential implementation. (Fig. 1, left)\nThe sequential has the slowest converge speed but it doesn’t have any overhead on transferring data. The CPU is very powerful the Sequential version is the fastest implementation for the total elapsed time when the input size is small. (Fig. 1, right)\nFig. 1. Averaged elapsed time per iteration and Total elapsed time(E2E runtime) measured on different input size.\nEstimate the best-case performance of CUDA implementation should have based on the hardware # Based on the number of threads in my program and the number of processing contexts actually supported by my hardware\nBecause a higher occupancy reduces processor idle time(SM may stall due to unavailability of data or busy functional units) and improves overall performance. The best case of performance of CUDA implementation should have the highest occupancy in theory.\nAt first, I should list my device info in detail and then use this info to deduce the theoretical best performance of CUDA implementation speedup compared to sequential implementation. I use Codio environment for my lab2. In the Codio environment, the device is Tesla T4, the warp size is 32, the maximum number of threads per block is 1024, the maximum number of blocks is 2147483647. I also found out the bandwidth between Host and Device is 6.3GB/s and the bandwidth between Device to Device is 239.4 GB/s.\nSecondly, I ignore the time cost on data transfer and only focus on elapsed time per iteration when estimating the best-case performance, because, from the device info above, we can see that the bandwidth are too big to measure its performance and the “cudaMemCpy” instruction is not an asynchronous instruction(it will cause different performance each time when I test it because it has “barrier” within this kind of function). In addition, the fraction of the end-to-end runtime in CUDA versions is spent in data transfer are the most time-consuming part(Fig. 3.) and the fraction also decreasing when the input size increasing, this is irrelevant with the number of thread or other settings in the program. For these three reasons, it’s fair to ignore the data transfer part when estimating the best performance case. I also assume that the CPU clock cycle per processor is the same as the GPU.\nIn addition, in my program, I set my number of threads per block in my CUDA implementation to 32, because the warp size of Tesla T4 is 32, and thus the warp schedulers can map each thread to their own position without more or less. Another reason I set the block size to 32 is that after I experimented with the different thread numbers from 16 to 1024 for each given input size and I always got the smallest elapsed time per iteration when the number of threads per block is 32. (I also got the best performance when thread number is 86, the only reason I can come up with is the randomness in bank conflict, because I use double data type throughout in my program, this will produce 2-way bank conflict in 32 warp size situation, every second bank is being asked for 2 different values. The warp scheduler will handle the 2-way bank conflict in random in a sequential style.)\nAt last, from all above assumption, the theoretical speedup of my CUDA implementations should have when I set my number of threads in each block to 32, and the hardware in Codio support this setup when the input size smaller than 2147483647 * 32, which is maximum block size times warp size of Tesla T4. In this setup, the warp scheduler won’t let any memory wait for others and thus it has the maximum occupancy.\nFig. 2. Cuda Shmem, Cuda basic, and Thrust implementations speed up X times compared to sequential kmeans. “2k, 16k and 65k” means “2048, 16384 and 65536” input size. Red color is Cuda Shmem, orange color is Cuda basic and yellow color is Thrust implementation.\nAnalyze Why Thrust Is Compromising # The Thrust is the slowest parallel implementation, and it does match my expectations. (Fig. 1, left and Fig. 2.)\nThe first reason is that the advantages of Thrust are abstraction and portability, but under the hood, it has more temporary memory allocations required by the Thrust algorithm during computing than CUDA implementations. For example, in lab2, the Thrust algorithm has additional temporary memory to store the data structure to perform the “reduce_by_key” function. However, we can visually manage every memory in CUDA implementation. Thus, these additional temporary memory allocations can impact Thrust performance.\nThe second reason is that Thrust cannot make use of shared memory or constant memory in GPU, both two kinds of memories have almost register speed. The Thrust only can make use of global GPU memory and transfer data to computing units through L1 memory to register.\nFig. 3. The fraction of the end-to-end runtime in CUDA versions(Shared Memory and Basic) is spent in data transfer.\nAnalyze the Fraction of the End-to-End Runtime of CUDA Implementations Spent in Data Transfer # For both CUDA versions (Shared Memory and Basic), the fraction is decreased with the increasing input data size. This indicates that the impact of the overhead of transferring data is decreasing with the increase of input size. (Fig. 3.)\nFor CUDA Shared Memory has slightly more overhead on transferring data than CUDA basic, because to use shared memory in GPU, it must transfer data from device to shared memory whereas the CUDA Basic only has overhead on transferring data between host and device, it doesn’t need shared memory.\n","date":"24 October 2021","externalUrl":null,"permalink":"/projects/cuda_kmeans/","section":"Projects","summary":" Implemented base sequential version of K-Means algorithm on CPU by C++ Implemented first parallel version of K-Means with Thrust primitives and a GroupBy-Aggregate by C++ Implemented second parallel version of K-Means with CUDA by C++ Implemented third parallel version of K-Means with CUDA on Shared Memory by C++ Analyzed speedup among all the implementations ","title":"Exploring GPU Programming with CUDA/CUDA Shared Memory/Thrust Solving K-Means Algorithm","type":"projects"},{"content":" Implemented base sequential version of work-efficient parallel prefix sum algorithm by C++ Implemented parallel and barrier versions of work-efficient parallel prefix sum with POSIX thread (pthread) by C++ Analyzed speedup among the all implantations with respect to the number of threads and data size Data # Data1: 1k Data2: 8k Data3: 16k Data4: seq_64_test Abstract # I use Work Efficient Algorithm with building blocks style to compute prefix sum of large array. My work-efficient algorithm has O(log N) time and O(N) work. Implementation Details for Work-Efficiency Algorithm # I separate input data into same size blocks. (num_blocks = ceil(log2(N)))\nI compute local prefix sums for these blocks in parallel. It has O(num_blocks) = O(log N) time, and O(num_blocks) * ceil(N / num_blocks) ~ O(N) work.\nI store the last element of each blocks into an 2D array, and then I compute prefix sum of this 2D array in parallel, because this 2D array has very small size than input size, so I choose Hillis’s prefix sum algorithm to compute this 2D array in parallel. The main reason is in this step I do care about the time efficiency not the work efficiency in parallel, and this 2D array has very small size which is equal to the number of blocks, so here we should use work-inefficient algorithm which is Hillis’s prefix sum algorithm. It has O(log(N / num_blocks)) = O(log N) time, and O(N/num_blocks * log(N/num_blocks)) = O(n) work.\nAt last, add 2D array to each blocks in proper places in parallel. It also has O(num_blocks) = O(log N) time here, and also has O(num_blocks) * ceil(N / num_blocks) ~ O(N) work.\nAnalysis:\nTime: O(log N) Work: O(N) Analyze Each Step # Step 1 # The above graph can show my program performance when setting LOOP to 100000 on different thread numbers. From the graph, we can see there are two inflection points on each subgraph. The first inflection point is on 2 threads because my work-efficient algorithm after averaging on each thread has additional parallel computing operations on my part 2 implementation (see page 1) than the sequential algorithm. The second inflection point is on 4 threads because my laptop CPU has 4 cores, and thus my program can perform best when setting to 4 threads and each core doesn’t need to switch to another thread, and this can save overhead on context switching. And with more than 4 threads, the performance of my program decreased, I think the main reason is that there is more overhead when switching threads and that might cause the performance doesn’t improve even decrease. If I setting 4 threads, each core of my 4 core CPU can get only one thread without switching to other threads. Step 2 # Here, I set my LOOP to 10 and all other arguments keep the same as step1. We decrease the time of each addition operation, and thus our sequential algorithm can get the fastest result. The main reason is that our CPU can solve the problem very fast in sequential as the professor mentioned in the lecture. The decreased amount of time on each addition operation also indicates that the amount of time cost on each thread decreased, and thus more threads will result in more context switch frequency, and thus this will have more overhead than the sequential algorithm. This is the main reason when the addition operation becoming very fast, our parallel algorithm performance can’t beat the sequential algorithm. Another reason is that our parallel algorithm has more addition operation than sequential algorithm after averaging for each thread as I mentioned on page 1 of my implementation details. This also leads to a performance decrease. The above two reasons can explain why the sequential algorithm is the fastest and why the trend is like this. The above graph shows that when setting THREAD number argument to 0 and 4 (0 means sequential algorithm, 4 is because my laptop can get the best performance when threads number is 4), after setting the THREAD argument and changing the LOOP argument to test our program performance. We can see there is an inflection point on each subplot in the red circle because increasing the LOOP argument causes the amount of work on each thread to increase and thus the parallel performance can take advantage of the different workers work at the same time and that meet the performance with the sequential algorithm at the red circle on the graph. We also notice that by increasing the input size from 64 to 8k, our inflection point moves to the left as indicated in the green box on the graph. The reason is that increasing the numbers of input values, increases the addition operation to both algorithms and this has the same effect as increasing the LOOP. Thus with these two main factors, increasing the LOOP and increasing the input size, can explain why inflection points in these subgraphs of the second graph can meet earlier and earlier, and cause the trend like above. Step 3 # Here, I used my own re-entrant barrier to plot the same graph as before in Step 2. My barrier is implemented with conditional variable and mutex lock. Each thread will wait and try to unlock the barrier in the queue, and once all threads get the signal, the program will go further to the next step.\nSpinlock is different from the mutex lock. As the professor said, spinlock causes the thread to keep rolling the lock to try to find out whether the stuff is unlocked or opened all the time. This will constantly cost CPU resources, and if the thread is locked for a long time, spinlock will keep trying to get the control and cause a lot more CPU resources waste.\nIn the scenarios of the small amount of work in each thread, the spinlock can be better, because spinlock will not waste much CPU time on waiting for the other threads to unlock. It might also boost some performance by trying to get control from those short lifetime threads. And thus in the contrast, in the scenario of a large amount of work in each thread, which means the mutex is better, because in this long waiting time scenarios if some thread wants to unlock and it will wait in a queue instead of waste CPU resources, and won’t constantly cost CPU time.\nIn addition, a multi-core scenario also can get benefit from the spinlock. When the critical section is small and multi-core environment can reduce the context switch time and that can take advantage of the spinlock.\nThe main drawback of the spinlock is that it will cost a lot amount of CPU time when the critical section was held for a long time in some thread, and the spinlock will keep trying to unlock the thread, which will waste the CPU resources.\nThe main drawback of the mutex is that if our program only has a small workload to deal with for each thread, then mutex is very inefficient. Because each thread will go sleep allow another thread to run, however, if the thread has a short lifespan, then the context switching will let the mutex become very inefficient.\nFrom the above two graphs compared to Step 2, we can see that my own barrier can’t get better performance than the Pthread barrier. From the first graph, we can see the inflection point meets later than the traditional Pthread barrier in Step 2. And from the second graph, we can compare to the average cost time of the program, the green box, indicating that our barrier is getting slower than the Pthread barrier but not that much, it only got 1000 ms average worse. But basically, the results are in line with my expectation, because the logic behind the Phtread is similar to mine. The main reason that my barrier is slightly inefficient is that I let my barrier spin a little bit to try to unlock the thread, which would cost some CPU time, and that will cause worse performance.\nI suggest using my own spinlock barrier to some tasks or some scenarios that have some small subtasks that only have a short lifespan, and the rest of the tasks has a large workload. In this scenario, my barrier will work better. Otherwise, if the tasks are all with a large workload, then I suggest using the Pthread barrier instead because it doesn’t waste any time on trying to unlock, it will go to sleep and wait in a queue and let other threads use the resources.\n","date":"24 September 2021","externalUrl":null,"permalink":"/projects/pthreads_prefix_sum/","section":"Projects","summary":" Implemented base sequential version of work-efficient parallel prefix sum algorithm by C++ Implemented parallel and barrier versions of work-efficient parallel prefix sum with POSIX thread (pthread) by C++ Analyzed speedup among the all implantations with respect to the number of threads and data size ","title":"Exploring Multithreaded Programming with Pthreads Solving Parallel Prefix Scan Algorithm","type":"projects"},{"content":" Designed deep networks for a racing simulator, SuperTuxKart with Pytorch Trained linear model and multi-layer perceptron model to classify images from SuperTuxKart Trained a convolutional network to classify images from SuperTuxKart Built classification network fully convolutional and solved a semantic labeling task (labeling every pixel in the image) Implemented an object detector Trained a CNN to do vision-based self-driving in SuperTuxKart Programmed a SuperTuxKart ice-hockey player (AI that plays ice-hockey) ","date":"25 June 2021","externalUrl":null,"permalink":"/projects/supertuxkart/","section":"Projects","summary":" Designed deep networks for a racing simulator, SuperTuxKart with Pytorch Trained linear model and multi-layer perceptron model to classify images from SuperTuxKart Trained a convolutional network to classify images from SuperTuxKart Built classification network fully convolutional and solved a semantic labeling task (labeling every pixel in the image) Implemented an object detector Trained a CNN to do vision-based self-driving in SuperTuxKart Programmed a SuperTuxKart ice-hockey player (AI that plays ice-hockey) ","title":"Draft: Self-driving SuperTuxKart with Pytorch","type":"projects"},{"content":" Implemented an Encoder-Decoder model for semantic parsing with Pytorch Implemented a decoder by using LTSM whose output is passed to a feedforward layer and a softmax over the vocabulary Added attention mechanisms to the model to make it more powerful and faster to train ","date":"25 April 2021","externalUrl":null,"permalink":"/projects/nlp_semantic_parsing_encoder_decoder/","section":"Projects","summary":" Implemented an Encoder-Decoder model for semantic parsing with Pytorch Implemented a decoder by using LTSM whose output is passed to a feedforward layer and a softmax over the vocabulary Added attention mechanisms to the model to make it more powerful and faster to train ","title":"Semantic Parsing with Encoder-Decoder Models","type":"projects"},{"content":"Here’s a comprehensive list of essential C++ syntax and language knowledge you should master for solving coding problems efficiently on LeetCode — especially for interviews at top tech companies.\nI’ve grouped them into Beginner → Intermediate → Advanced levels so you can build your mastery progressively.\n🧱 1. Basic C++ Syntax \u0026amp; Structure (Fundamentals) # ✅ Basic program structure\n#include \u0026lt;iostream\u0026gt; using namespace std; int main() { ... } ✅ Input \u0026amp; Output\ncin \u0026gt;\u0026gt; x; / cout \u0026lt;\u0026lt; x \u0026lt;\u0026lt; endl; printf / scanf (for faster IO) ✅ Variable declarations\nint, long, double, char, bool, string Type modifiers: long long, unsigned ✅ Operators\nArithmetic: + - * / % Relational: == != \u0026lt; \u0026lt;= \u0026gt; \u0026gt;= Logical: \u0026amp;\u0026amp; || ! Bitwise: \u0026amp; | ^ ~ \u0026lt;\u0026lt; \u0026gt;\u0026gt; Assignment: = += -= *= ... ✅ Control flow\nif, else if, else switch / case for, while, do-while break, continue, return 🧮 2. Data Structures Syntax # ✅ Arrays\nStatic arrays: int arr[100]; Dynamic arrays via vector ✅ Strings\nstring s; — s.size(), s.substr(), s.find(), s.push_back() s[i] indexing ✅ Vectors (std::vector)\nvector\u0026lt;int\u0026gt; v; push_back, pop_back, size, clear, empty v.begin(), v.end() Initialization: vector\u0026lt;int\u0026gt; v(n, 0); ✅ Stacks / Queues / Deques\nstack\u0026lt;int\u0026gt; s; → push, pop, top, empty queue\u0026lt;int\u0026gt; q; → push, pop, front, back deque\u0026lt;int\u0026gt; dq; → double-ended operations ✅ Priority Queue / Heap\npriority_queue\u0026lt;int\u0026gt; (max heap) priority_queue\u0026lt;int, vector\u0026lt;int\u0026gt;, greater\u0026lt;int\u0026gt;\u0026gt; (min heap) ✅ Sets \u0026amp; Maps\nset\u0026lt;int\u0026gt; / unordered_set\u0026lt;int\u0026gt; map\u0026lt;int, int\u0026gt; / unordered_map\u0026lt;int, int\u0026gt; insert, erase, find, count, [] ✅ Pairs \u0026amp; Tuples\npair\u0026lt;int,int\u0026gt; p = {1,2}; → p.first, p.second tuple\u0026lt;int,int,int\u0026gt; t; / tie(a,b,c) = t; 🧠 3. Functions \u0026amp; Scope # ✅ Function definition \u0026amp; declaration\nint add(int a, int b) { return a + b; } ✅ Pass by value / reference (\u0026amp;)\nvoid f(int\u0026amp; x) { x++; } ✅ Recursion syntax\nBase case \u0026amp; recursive calls ✅ Function overloading\n✅ Default parameters\n✅ Inline functions (inline)\n🧰 4. Memory \u0026amp; Pointers # ✅ Pointer basics\nint* p = \u0026amp;x;, *p, \u0026amp;x ✅ nullptr vs NULL\n✅ Dynamic memory\nnew and delete int* arr = new int[n]; ✅ Reference (\u0026amp;) vs pointer (*)\n✅ Passing pointers to functions\n✅ Smart pointers (basic familiarity): unique_ptr, shared_ptr (rare in LeetCode but useful)\n🧭 5. Classes, Structs \u0026amp; OOP # ✅ struct and class definition\nMembers, methods, constructors ✅ Access modifiers: public, private, protected\n✅ this pointer\n✅ Constructors \u0026amp; Destructors\n✅ const methods (int get() const { ... })\n✅ Operator overloading (optional)\n✅ friend functions (rare)\n✅ Inheritance \u0026amp; polymorphism (only needed for some design problems)\n🧮 6. STL Algorithms (Very Important) # ✅ #include \u0026lt;algorithm\u0026gt;\nsort(v.begin(), v.end()); reverse(v.begin(), v.end()); max_element, min_element binary_search, lower_bound, upper_bound accumulate (with #include \u0026lt;numeric\u0026gt;) ✅ #include \u0026lt;functional\u0026gt; (e.g., greater\u0026lt;int\u0026gt;())\n✅ next_permutation, prev_permutation\n✅ count, find, unique\n👉 Mastering STL will save you a lot of time in LeetCode.\n🧭 7. Common Patterns \u0026amp; Syntax # ✅ Range-based for loops\nfor (int x : v) { ... } ✅ Lambda functions\nauto cmp = [](int a, int b){ return a \u0026gt; b; }; sort(v.begin(), v.end(), cmp); ✅ auto keyword\n✅ typedef / using aliases\nusing ll = long long; ✅ Initializer list: {1, 2, 3}\n✅ emplace_back vs push_back\n✅ const correctness\n🧮 8. Advanced C++ (LeetCode-Useful) # ✅ std::function for passing functions around\n✅ Custom comparator for priority queue, sort, set/map\nstruct cmp { bool operator()(...) const { ... } }; ✅ Lambda with capture\n✅ Recursion with lambda (std::function\u0026lt;void(...)\u0026gt; dfs = [\u0026amp;](...) { ... };)\n✅ vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; and multi-dimensional containers\n✅ memset for initializing arrays\n✅ fill and assign for containers\nExample: int dist[1000]; memset(dist, 0x3f, sizeof(dist)); // sets all ints to 0x3f3f3f3f\n⚡ 9. Time \u0026amp; Space Optimization Tricks # ios::sync_with_stdio(false); cin.tie(nullptr); Avoid unnecessary copies (use references \u0026amp;) Use reserve() for vectors to prevent reallocations Use bit operations for performance in some problems 🧠 10. C++17/20 Features That Can Help # (These aren’t strictly required, but can make code cleaner.)\nstructured bindings:\nauto [a, b] = p; if with initializer\nif (auto it = mp.find(x); it != mp.end()) { ... } constexpr, inline variables\nstd::optional (occasionally useful)\nstd::variant (rare on LeetCode)\n📌 BONUS: C++ Syntax Patterns You’ll Use Repeatedly # Pattern Usage DFS/BFS recursion function\u0026lt;void(int)\u0026gt; dfs = [\u0026amp;](int u){ ... }; Graph adjacency list vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; g(n); Sliding window int l=0, r=0; while(r\u0026lt;n){...} Binary search int l=0, r=n-1; while(l\u0026lt;=r){...} Two pointers while (l\u0026lt;r) {...} Prefix sum vector\u0026lt;int\u0026gt; pre(n+1,0); Heap with custom comparator priority_queue\u0026lt;pair\u0026lt;int,int\u0026gt;, vector\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt;, greater\u0026lt;\u0026gt;\u0026gt; pq; ✅ Recommendation for mastering LeetCode with C++:\nBe fully comfortable with vector, string, and STL algorithms. Learn how to use custom comparators with sort and priority queue. Get used to writing fast I/O setup (ios::sync_with_stdio(false) + cin.tie(0)). Practice recursion and lambda patterns (great for backtracking \u0026amp; DFS problems). Know your containers — which one to use for O(1) or O(log n). // C++ LeetCode Cheat Sheet // Single-file templates \u0026amp; snippets: paste into LeetCode editor and modify per problem. #include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; // --- Common type aliases --- using ll = long long; using pii = pair\u0026lt;int,int\u0026gt;; using vi = vector\u0026lt;int\u0026gt;; using vvi = vector\u0026lt;vi\u0026gt;; // --- Fast I/O (useable in local contests; LeetCode ignores sync flags) --- static auto __fast_io = [](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }(); // --- Utility helpers --- template\u0026lt;class T\u0026gt; void chmin(T \u0026amp;a, const T \u0026amp;b){ if(b \u0026lt; a) a = b; } template\u0026lt;class T\u0026gt; void chmax(T \u0026amp;a, const T \u0026amp;b){ if(b \u0026gt; a) a = b; } // --- Common constants --- const int INF = 1e9; const ll LINF = (ll)4e18; // --------------------------- // 1) Basic templates // --------------------------- // Function template int add(int a, int b){ return a + b; } // Lambda example auto cmp = [](int a, int b){ return a \u0026gt; b; }; // --------------------------- // 2) Binary Search (classic) -- on index 0..n-1 where predicate is monotonic // --------------------------- int binary_search_index(int n, function\u0026lt;bool(int)\u0026gt; good){ int l = 0, r = n - 1, ans = -1; while(l \u0026lt;= r){ int m = l + (r - l) / 2; if(good(m)){ ans = m; r = m - 1; } else l = m + 1; } return ans; } // --------------------------- // 3) Two pointers / Sliding window // --------------------------- int count_subarrays_with_sum_at_most_k(const vector\u0026lt;int\u0026gt;\u0026amp; a, int k){ int n = a.size(); long long sum = 0; int l = 0; int cnt = 0; for(int r=0;r\u0026lt;n;++r){ sum += a[r]; while(l \u0026lt;= r \u0026amp;\u0026amp; sum \u0026gt; k){ sum -= a[l++]; } cnt += (r - l + 1); } return cnt; } // --------------------------- // 4) DFS (recursive) \u0026amp; backtracking template // --------------------------- void dfs_recursive(int u, const vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; g, vector\u0026lt;int\u0026gt;\u0026amp; vis){ vis[u] = 1; for(int v: g[u]) if(!vis[v]) dfs_recursive(v,g,vis); } // Backtracking example: generate permutations void backtrack_perms(vector\u0026lt;int\u0026gt;\u0026amp; a, vector\u0026lt;int\u0026gt;\u0026amp; cur, vector\u0026lt;bool\u0026gt;\u0026amp; used, vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; out){ if((int)cur.size() == (int)a.size()){ out.push_back(cur); return; } for(int i=0;i\u0026lt;(int)a.size();++i){ if(used[i]) continue; used[i] = true; cur.push_back(a[i]); backtrack_perms(a,cur,used,out); cur.pop_back(); used[i] = false; } } // --------------------------- // 5) BFS template (shortest path in unweighted graph) // --------------------------- vector\u0026lt;int\u0026gt; bfs_dist(int s, const vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; g){ int n = g.size(); vector\u0026lt;int\u0026gt; dist(n, -1); queue\u0026lt;int\u0026gt;q; q.push(s); dist[s]=0; while(!q.empty()){ int u=q.front(); q.pop(); for(int v: g[u]) if(dist[v]==-1){ dist[v]=dist[u]+1; q.push(v); } } return dist; } // --------------------------- // 6) Priority queue (heap) - min and max // --------------------------- // Max-heap: priority_queue\u0026lt;int\u0026gt; pq; // Min-heap: priority_queue\u0026lt;int, vector\u0026lt;int\u0026gt;, greater\u0026lt;int\u0026gt;\u0026gt; pq; // Example: k smallest elements vector\u0026lt;int\u0026gt; k_smallest(const vector\u0026lt;int\u0026gt;\u0026amp; a, int k){ priority_queue\u0026lt;int\u0026gt; pq; for(int x: a){ pq.push(x); if((int)pq.size() \u0026gt; k) pq.pop(); } vector\u0026lt;int\u0026gt; res; while(!pq.empty()){ res.push_back(pq.top()); pq.pop(); } reverse(res.begin(), res.end()); return res; } // --------------------------- // 7) Union-Find (Disjoint Set Union) // --------------------------- struct DSU{ int n; vector\u0026lt;int\u0026gt; p, r; DSU(int n=0): n(n), p(n), r(n,0){ iota(p.begin(), p.end(), 0); } int find(int x){ return p[x]==x?x:p[x]=find(p[x]); } bool unite(int a,int b){ a=find(a); b=find(b); if(a==b) return false; if(r[a]\u0026lt;r[b]) swap(a,b); p[b]=a; if(r[a]==r[b]) r[a]++; return true; } }; // --------------------------- // 8) Common STL usages \u0026amp; patterns // --------------------------- // sort vector sort(v.begin(), v.end()); // sort by custom comparator sort(v.begin(), v.end(), [](auto \u0026amp;x, auto \u0026amp;y){ return x.second \u0026lt; y.second; }); // lower_bound / upper_bound (on sorted container) auto it = lower_bound(v.begin(), v.end(), value); int idx = it - v.begin(); // unique to remove consecutive duplicates v.erase(unique(v.begin(), v.end()), v.end()); // prefix sum vector\u0026lt;ll\u0026gt; pref(n+1,0); for(int i=0;i\u0026lt;n;++i) pref[i+1]=pref[i]+a[i]; // --------------------------- // 9) DP templates (top-down \u0026amp; bottom-up) // --------------------------- // Top-down memoization example (Fibonacci) vector\u0026lt;ll\u0026gt; memo_fib; ll fib_td(int n){ if(n\u0026lt;=1) return n; if(memo_fib[n] != -1) return memo_fib[n]; return memo_fib[n] = fib_td(n-1) + fib_td(n-2); } // Bottom-up DP example ll fib_bu(int n){ if(n\u0026lt;=1) return n; ll a=0,b=1; for(int i=2;i\u0026lt;=n;i++){ ll c=a+b; a=b; b=c; } return b; } // --------------------------- // 10) Graph algorithms (Dijkstra, TopoSort) // --------------------------- vector\u0026lt;ll\u0026gt; dijkstra(int src, const vector\u0026lt;vector\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt;\u0026gt;\u0026amp; g){ int n = g.size(); vector\u0026lt;ll\u0026gt; dist(n, LINF); priority_queue\u0026lt;pair\u0026lt;ll,int\u0026gt;, vector\u0026lt;pair\u0026lt;ll,int\u0026gt;\u0026gt;, greater\u0026lt;pair\u0026lt;ll,int\u0026gt;\u0026gt;\u0026gt; pq; dist[src]=0; pq.push({0,src}); while(!pq.empty()){ auto [d,u] = pq.top(); pq.pop(); if(d!=dist[u]) continue; for(auto [v,w]: g[u]){ if(dist[v] \u0026gt; dist[u] + w){ dist[v] = dist[u] + w; pq.push({dist[v], v}); } } } return dist; } vector\u0026lt;int\u0026gt; topo_sort(int n, const vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; g){ vector\u0026lt;int\u0026gt; indeg(n,0); for(int u=0;u\u0026lt;n;++u) for(int v: g[u]) indeg[v]++; queue\u0026lt;int\u0026gt; q; for(int i=0;i\u0026lt;n;++i) if(indeg[i]==0) q.push(i); vector\u0026lt;int\u0026gt; order; while(!q.empty()){ int u=q.front(); q.pop(); order.push_back(u); for(int v: g[u]) if(--indeg[v]==0) q.push(v); } if((int)order.size()!=n) return {}; // cycle return order; } // --------------------------- // 11) Trie (prefix tree) -- common string problem template // --------------------------- struct TrieNode{ array\u0026lt;int,26\u0026gt; nxt; bool end=false; TrieNode(){ nxt.fill(-1); } }; struct Trie{ vector\u0026lt;TrieNode\u0026gt; t; Trie(){ t.emplace_back(); } void insert(const string \u0026amp;s){ int cur=0; for(char ch: s){ int c=ch-\u0026#39;a\u0026#39;; if(t[cur].nxt[c]==-1){ t[cur].nxt[c]=t.size(); t.emplace_back(); } cur=t[cur].nxt[c]; } t[cur].end=true; } bool search(const string \u0026amp;s){ int cur=0; for(char ch: s){ int c=ch-\u0026#39;a\u0026#39;; if(t[cur].nxt[c]==-1) return false; cur=t[cur].nxt[c]; } return t[cur].end; } }; // --------------------------- // 12) Segment Tree (range sum) -- iterative (bottom-up) // --------------------------- struct SegTree{ int n; vector\u0026lt;ll\u0026gt; t; SegTree(int _n=0){ init(_n); } void init(int _n){ n=1; while(n\u0026lt;_n) n\u0026lt;\u0026lt;=1; t.assign(2*n,0); } void build(const vector\u0026lt;ll\u0026gt;\u0026amp; a){ int m = a.size(); init(m); for(int i=0;i\u0026lt;m;i++) t[n+i]=a[i]; for(int i=n-1;i\u0026gt;0;i--) t[i]=t[i\u0026lt;\u0026lt;1]+t[i\u0026lt;\u0026lt;1|1]; } void update(int p, ll val){ p += n; t[p] = val; while(p\u0026gt;1){ p\u0026gt;\u0026gt;=1; t[p] = t[p\u0026lt;\u0026lt;1] + t[p\u0026lt;\u0026lt;1|1]; } } ll query(int l, int r){ // inclusive l,r ll res=0; l+=n; r+=n; while(l\u0026lt;=r){ if(l\u0026amp;1) res += t[l++]; if(!(r\u0026amp;1)) res += t[r--]; l \u0026gt;\u0026gt;= 1; r \u0026gt;\u0026gt;= 1; } return res; } }; // --------------------------- // 13) String utilities // --------------------------- vector\u0026lt;int\u0026gt; prefix_function(const string\u0026amp; s){ int n=s.size(); vector\u0026lt;int\u0026gt; pi(n); for(int i=1;i\u0026lt;n;i++){ int j = pi[i-1]; while(j\u0026gt;0 \u0026amp;\u0026amp; s[i]!=s[j]) j = pi[j-1]; if(s[i]==s[j]) ++j; pi[i]=j; } return pi; } // --------------------------- // 14) Common snippets \u0026amp; tips // --------------------------- // Reserve vector capacity to avoid reallocations v.reserve(1000); // Use references to avoid copies when iterating large objects for(const auto \u0026amp;x : bigVec) { ... } // Use move semantics when returning big containers (RVO helps) // Use stable sort if order matters: stable_sort(begin,end,comp); // --------------------------- // 15) Problem-specific templates (examples) // --------------------------- // Example: Two-sum (hashmap) vector\u0026lt;int\u0026gt; twoSum(const vector\u0026lt;int\u0026gt;\u0026amp; nums, int target){ unordered_map\u0026lt;int,int\u0026gt; mp; for(int i=0;i\u0026lt;(int)nums.size();++i){ int need = target - nums[i]; if(mp.count(need)) return {mp[need], i}; mp[nums[i]] = i; } return {}; } // Example: Merge intervals (sort + sweep) vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; mergeIntervals(vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; intervals){ if(intervals.empty()) return {}; sort(intervals.begin(), intervals.end()); vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; res; res.push_back(intervals[0]); for(auto \u0026amp;it: intervals){ auto \u0026amp;last = res.back(); if(it[0] \u0026lt;= last[1]) last[1] = max(last[1], it[1]); else res.push_back(it); } return res; } // Example: Reverse linked list (iterative) - LeetCode singly-linked-list structure assumed ListNode* reverseList(ListNode* head){ ListNode *prev=nullptr, *cur=head; while(cur){ ListNode* nxt=cur-\u0026gt;next; cur-\u0026gt;next=prev; prev=cur; cur=nxt; } return prev; } // --------------------------- // End of cheat sheet // --------------------------- int main(){ // This file is a template repository. Main left empty. return 0; } ","date":"13 October 2020","externalUrl":null,"permalink":"/notes/cpp-syntax-leetcode/","section":"Notes","summary":"Here’s a comprehensive list of essential C++ syntax and language knowledge you should master for solving coding problems efficiently on LeetCode — especially for interviews at top tech companies.\nI’ve grouped them into Beginner → Intermediate → Advanced levels so you can build your mastery progressively.\n🧱 1. Basic C++ Syntax \u0026 Structure (Fundamentals) # ✅ Basic program structure\n","title":"Cpp Syntax Leetcode","type":"notes"},{"content":" Projects # PTS UI \u0026amp;\u0026amp; SERVICE # DCTR SERVICE CI # HMBS BRE SERVICE # RECERTIFICATION SERVICE # HUD11708 SERVICE # BATCH JOB # Challenges # Virtual thread block the other thread # jps -l # to list java process, find springboot Application.class process 2.0 jcmd 12345 help jcmd 12345 Thread.print jcmd 12345 VM.flags # inspect JVM flags jcmd 12345 GC.heap_info jcmd PID VM.command_line # inspect JVM arguments Use JFR # jcmd 12345 JFR.start name=debug duration=60s filename=C:\\temp\\debug.jfr 1.1 jcmd JFR.check 1.2 jcmd JFR.stop name=xxx jmc BRE performance: stream dao layer for large query, separate query to avoid large CTE, virtual thread # Production defect: WAF rule # DB Unique index constraint bug for efficient code # Drool syntax: \u0026amp;\u0026amp; cannot skip second part, comma can skip second part # Not code related: not coding error, need to push back to not coding error # jdk25 bugs: # jackson 3, cannot use primitive boolean in payload sb4 bugs: # queryForObject always throw EmptyResultDataAccessException if set fetchSize globally, we should use for each preparedStatement for each query improve query performance # reduce the size of CTE (common table expression) landing on index (most of time) improve table join Experience # TA # 2022 Spring: Data structure \u0026amp; algorithms by Calvin Lin 2025 Spring: Advances in Deep Learning by Philipp Krahenbuhl 2025 Summer: Advances in Deep Learning by Philipp Krahenbuhl 2025 Fall: Natural Language Processing by Greg Durrett \u0026amp; Jessy Li 2026 Spring: Advances in Generative Modeling by Qiang Liu Staff SDE # leading cross-team initiatives, writing design docs others reference, and mentoring might make a lateral Senior move into a more ML-adjacent role at your current level, then grow into ML-infra-specialized Staff over 2-3 years.\n","externalUrl":null,"permalink":"/cv/memo/","section":"Cvs","summary":"Projects # PTS UI \u0026\u0026 SERVICE # DCTR SERVICE CI # HMBS BRE SERVICE # RECERTIFICATION SERVICE # HUD11708 SERVICE # BATCH JOB # Challenges # Virtual thread block the other thread # jps -l # to list java process, find springboot Application.class process 2.0 jcmd 12345 help jcmd 12345 Thread.print jcmd 12345 VM.flags # inspect JVM flags jcmd 12345 GC.heap_info jcmd PID VM.command_line # inspect JVM arguments Use JFR # jcmd 12345 JFR.start name=debug duration=60s filename=C:\\temp\\debug.jfr 1.1 jcmd JFR.check 1.2 jcmd JFR.stop name=xxx jmc BRE performance: stream dao layer for large query, separate query to avoid large CTE, virtual thread # Production defect: WAF rule # DB Unique index constraint bug for efficient code # Drool syntax: \u0026\u0026 cannot skip second part, comma can skip second part # Not code related: not coding error, need to push back to not coding error # jdk25 bugs: # jackson 3, cannot use primitive boolean in payload sb4 bugs: # queryForObject always throw EmptyResultDataAccessException if set fetchSize globally, we should use for each preparedStatement for each query improve query performance # reduce the size of CTE (common table expression) landing on index (most of time) improve table join Experience # TA # 2022 Spring: Data structure \u0026 algorithms by Calvin Lin 2025 Spring: Advances in Deep Learning by Philipp Krahenbuhl 2025 Summer: Advances in Deep Learning by Philipp Krahenbuhl 2025 Fall: Natural Language Processing by Greg Durrett \u0026 Jessy Li 2026 Spring: Advances in Generative Modeling by Qiang Liu Staff SDE # leading cross-team initiatives, writing design docs others reference, and mentoring might make a lateral Senior move into a more ML-adjacent role at your current level, then grow into ML-infra-specialized Staff over 2-3 years.\n","title":"","type":"cv"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/cv/","section":"Cvs","summary":"","title":"Cvs","type":"cv"},{"content":"","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"}]