Spaces:
Sleeping
Sleeping
Aditya commited on
Commit ·
4a3f117
0
Parent(s):
Add LLM vs RAG vs Fine-Tuning comparison project
Browse files- .gitattributes +5 -0
- .gitignore +24 -0
- README.md +207 -0
- data/docs/backtracking.txt +204 -0
- data/docs/binary_search.txt +130 -0
- data/docs/css_flexbox.txt +17 -0
- data/docs/dynamic_programming.txt +173 -0
- data/docs/graph_algorithms.txt +217 -0
- data/docs/greedy_algorithms.txt +169 -0
- data/docs/hashing.txt +171 -0
- data/docs/javascript_promises.txt +34 -0
- data/docs/linked_list.txt +206 -0
- data/docs/react_hooks.txt +20 -0
- data/docs/recursion.txt +148 -0
- data/docs/rest_api.txt +21 -0
- data/docs/sorting_algorithms.txt +165 -0
- data/docs/stack_queue.txt +218 -0
- data/docs/string_algorithms.txt +194 -0
- data/docs/trees.txt +224 -0
- data/docs/two_pointers.txt +204 -0
- data/faiss_index/index.faiss +0 -0
- data/faiss_index/index.pkl +0 -0
- data/finetune_data.jsonl +20 -0
- data/prepare_docs.py +222 -0
- data/prepare_finetune.py +91 -0
- data/prepare_questions.py +66 -0
- data/questions.csv +51 -0
- demo.py +409 -0
- evaluate.py +135 -0
- requirements.txt +13 -0
- system1_baseline.py +50 -0
- system2_rag.py +101 -0
- system3_finetune_colab.ipynb +176 -0
- system3_inference.py +75 -0
.gitattributes
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
checkpoint-25/tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Environment variables
|
| 2 |
+
.env
|
| 3 |
+
|
| 4 |
+
# Python
|
| 5 |
+
__pycache__/
|
| 6 |
+
*.py[cod]
|
| 7 |
+
*.egg-info/
|
| 8 |
+
dist/
|
| 9 |
+
build/
|
| 10 |
+
*.egg
|
| 11 |
+
|
| 12 |
+
# Model artifacts
|
| 13 |
+
finetuned_model/
|
| 14 |
+
|
| 15 |
+
# Results
|
| 16 |
+
results/
|
| 17 |
+
|
| 18 |
+
# IDE
|
| 19 |
+
.vscode/
|
| 20 |
+
.idea/
|
| 21 |
+
|
| 22 |
+
# OS
|
| 23 |
+
.DS_Store
|
| 24 |
+
Thumbs.db
|
README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
base_model: TinyLlama/TinyLlama-1.1B-Chat-v1.0
|
| 3 |
+
library_name: peft
|
| 4 |
+
pipeline_tag: text-generation
|
| 5 |
+
tags:
|
| 6 |
+
- base_model:adapter:TinyLlama/TinyLlama-1.1B-Chat-v1.0
|
| 7 |
+
- lora
|
| 8 |
+
- transformers
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# Model Card for Model ID
|
| 12 |
+
|
| 13 |
+
<!-- Provide a quick summary of what the model is/does. -->
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
## Model Details
|
| 18 |
+
|
| 19 |
+
### Model Description
|
| 20 |
+
|
| 21 |
+
<!-- Provide a longer summary of what this model is. -->
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
- **Developed by:** [More Information Needed]
|
| 26 |
+
- **Funded by [optional]:** [More Information Needed]
|
| 27 |
+
- **Shared by [optional]:** [More Information Needed]
|
| 28 |
+
- **Model type:** [More Information Needed]
|
| 29 |
+
- **Language(s) (NLP):** [More Information Needed]
|
| 30 |
+
- **License:** [More Information Needed]
|
| 31 |
+
- **Finetuned from model [optional]:** [More Information Needed]
|
| 32 |
+
|
| 33 |
+
### Model Sources [optional]
|
| 34 |
+
|
| 35 |
+
<!-- Provide the basic links for the model. -->
|
| 36 |
+
|
| 37 |
+
- **Repository:** [More Information Needed]
|
| 38 |
+
- **Paper [optional]:** [More Information Needed]
|
| 39 |
+
- **Demo [optional]:** [More Information Needed]
|
| 40 |
+
|
| 41 |
+
## Uses
|
| 42 |
+
|
| 43 |
+
<!-- Address questions around how the model is intended to be used, including the foreseeable users of the model and those affected by the model. -->
|
| 44 |
+
|
| 45 |
+
### Direct Use
|
| 46 |
+
|
| 47 |
+
<!-- This section is for the model use without fine-tuning or plugging into a larger ecosystem/app. -->
|
| 48 |
+
|
| 49 |
+
[More Information Needed]
|
| 50 |
+
|
| 51 |
+
### Downstream Use [optional]
|
| 52 |
+
|
| 53 |
+
<!-- This section is for the model use when fine-tuned for a task, or when plugged into a larger ecosystem/app -->
|
| 54 |
+
|
| 55 |
+
[More Information Needed]
|
| 56 |
+
|
| 57 |
+
### Out-of-Scope Use
|
| 58 |
+
|
| 59 |
+
<!-- This section addresses misuse, malicious use, and uses that the model will not work well for. -->
|
| 60 |
+
|
| 61 |
+
[More Information Needed]
|
| 62 |
+
|
| 63 |
+
## Bias, Risks, and Limitations
|
| 64 |
+
|
| 65 |
+
<!-- This section is meant to convey both technical and sociotechnical limitations. -->
|
| 66 |
+
|
| 67 |
+
[More Information Needed]
|
| 68 |
+
|
| 69 |
+
### Recommendations
|
| 70 |
+
|
| 71 |
+
<!-- This section is meant to convey recommendations with respect to the bias, risk, and technical limitations. -->
|
| 72 |
+
|
| 73 |
+
Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.
|
| 74 |
+
|
| 75 |
+
## How to Get Started with the Model
|
| 76 |
+
|
| 77 |
+
Use the code below to get started with the model.
|
| 78 |
+
|
| 79 |
+
[More Information Needed]
|
| 80 |
+
|
| 81 |
+
## Training Details
|
| 82 |
+
|
| 83 |
+
### Training Data
|
| 84 |
+
|
| 85 |
+
<!-- This should link to a Dataset Card, perhaps with a short stub of information on what the training data is all about as well as documentation related to data pre-processing or additional filtering. -->
|
| 86 |
+
|
| 87 |
+
[More Information Needed]
|
| 88 |
+
|
| 89 |
+
### Training Procedure
|
| 90 |
+
|
| 91 |
+
<!-- This relates heavily to the Technical Specifications. Content here should link to that section when it is relevant to the training procedure. -->
|
| 92 |
+
|
| 93 |
+
#### Preprocessing [optional]
|
| 94 |
+
|
| 95 |
+
[More Information Needed]
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
#### Training Hyperparameters
|
| 99 |
+
|
| 100 |
+
- **Training regime:** [More Information Needed] <!--fp32, fp16 mixed precision, bf16 mixed precision, bf16 non-mixed precision, fp16 non-mixed precision, fp8 mixed precision -->
|
| 101 |
+
|
| 102 |
+
#### Speeds, Sizes, Times [optional]
|
| 103 |
+
|
| 104 |
+
<!-- This section provides information about throughput, start/end time, checkpoint size if relevant, etc. -->
|
| 105 |
+
|
| 106 |
+
[More Information Needed]
|
| 107 |
+
|
| 108 |
+
## Evaluation
|
| 109 |
+
|
| 110 |
+
<!-- This section describes the evaluation protocols and provides the results. -->
|
| 111 |
+
|
| 112 |
+
### Testing Data, Factors & Metrics
|
| 113 |
+
|
| 114 |
+
#### Testing Data
|
| 115 |
+
|
| 116 |
+
<!-- This should link to a Dataset Card if possible. -->
|
| 117 |
+
|
| 118 |
+
[More Information Needed]
|
| 119 |
+
|
| 120 |
+
#### Factors
|
| 121 |
+
|
| 122 |
+
<!-- These are the things the evaluation is disaggregating by, e.g., subpopulations or domains. -->
|
| 123 |
+
|
| 124 |
+
[More Information Needed]
|
| 125 |
+
|
| 126 |
+
#### Metrics
|
| 127 |
+
|
| 128 |
+
<!-- These are the evaluation metrics being used, ideally with a description of why. -->
|
| 129 |
+
|
| 130 |
+
[More Information Needed]
|
| 131 |
+
|
| 132 |
+
### Results
|
| 133 |
+
|
| 134 |
+
[More Information Needed]
|
| 135 |
+
|
| 136 |
+
#### Summary
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
## Model Examination [optional]
|
| 141 |
+
|
| 142 |
+
<!-- Relevant interpretability work for the model goes here -->
|
| 143 |
+
|
| 144 |
+
[More Information Needed]
|
| 145 |
+
|
| 146 |
+
## Environmental Impact
|
| 147 |
+
|
| 148 |
+
<!-- Total emissions (in grams of CO2eq) and additional considerations, such as electricity usage, go here. Edit the suggested text below accordingly -->
|
| 149 |
+
|
| 150 |
+
Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700).
|
| 151 |
+
|
| 152 |
+
- **Hardware Type:** [More Information Needed]
|
| 153 |
+
- **Hours used:** [More Information Needed]
|
| 154 |
+
- **Cloud Provider:** [More Information Needed]
|
| 155 |
+
- **Compute Region:** [More Information Needed]
|
| 156 |
+
- **Carbon Emitted:** [More Information Needed]
|
| 157 |
+
|
| 158 |
+
## Technical Specifications [optional]
|
| 159 |
+
|
| 160 |
+
### Model Architecture and Objective
|
| 161 |
+
|
| 162 |
+
[More Information Needed]
|
| 163 |
+
|
| 164 |
+
### Compute Infrastructure
|
| 165 |
+
|
| 166 |
+
[More Information Needed]
|
| 167 |
+
|
| 168 |
+
#### Hardware
|
| 169 |
+
|
| 170 |
+
[More Information Needed]
|
| 171 |
+
|
| 172 |
+
#### Software
|
| 173 |
+
|
| 174 |
+
[More Information Needed]
|
| 175 |
+
|
| 176 |
+
## Citation [optional]
|
| 177 |
+
|
| 178 |
+
<!-- If there is a paper or blog post introducing the model, the APA and Bibtex information for that should go in this section. -->
|
| 179 |
+
|
| 180 |
+
**BibTeX:**
|
| 181 |
+
|
| 182 |
+
[More Information Needed]
|
| 183 |
+
|
| 184 |
+
**APA:**
|
| 185 |
+
|
| 186 |
+
[More Information Needed]
|
| 187 |
+
|
| 188 |
+
## Glossary [optional]
|
| 189 |
+
|
| 190 |
+
<!-- If relevant, include terms and calculations in this section that can help readers understand the model or model card. -->
|
| 191 |
+
|
| 192 |
+
[More Information Needed]
|
| 193 |
+
|
| 194 |
+
## More Information [optional]
|
| 195 |
+
|
| 196 |
+
[More Information Needed]
|
| 197 |
+
|
| 198 |
+
## Model Card Authors [optional]
|
| 199 |
+
|
| 200 |
+
[More Information Needed]
|
| 201 |
+
|
| 202 |
+
## Model Card Contact
|
| 203 |
+
|
| 204 |
+
[More Information Needed]
|
| 205 |
+
### Framework versions
|
| 206 |
+
|
| 207 |
+
- PEFT 0.18.1
|
data/docs/backtracking.txt
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
BACKTRACKING
|
| 2 |
+
============
|
| 3 |
+
Backtracking explores all possible solutions by building candidates incrementally.
|
| 4 |
+
If a candidate fails, it backtracks and tries the next option.
|
| 5 |
+
|
| 6 |
+
Template:
|
| 7 |
+
def backtrack(state, choices):
|
| 8 |
+
if is_solution(state):
|
| 9 |
+
record(state)
|
| 10 |
+
return
|
| 11 |
+
for choice in choices:
|
| 12 |
+
if is_valid(state, choice):
|
| 13 |
+
make_choice(state, choice)
|
| 14 |
+
backtrack(state, remaining_choices)
|
| 15 |
+
undo_choice(state, choice) # BACKTRACK
|
| 16 |
+
|
| 17 |
+
--- Generate All Subsets ---
|
| 18 |
+
def subsets(nums):
|
| 19 |
+
result = []
|
| 20 |
+
def backtrack(start, current):
|
| 21 |
+
result.append(current[:]) # record every state
|
| 22 |
+
for i in range(start, len(nums)):
|
| 23 |
+
current.append(nums[i])
|
| 24 |
+
backtrack(i + 1, current)
|
| 25 |
+
current.pop() # backtrack
|
| 26 |
+
backtrack(0, [])
|
| 27 |
+
return result
|
| 28 |
+
|
| 29 |
+
print(subsets([1, 2, 3]))
|
| 30 |
+
# [[], [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3]]
|
| 31 |
+
|
| 32 |
+
--- Generate All Permutations ---
|
| 33 |
+
def permute(nums):
|
| 34 |
+
result = []
|
| 35 |
+
def backtrack(current):
|
| 36 |
+
if len(current) == len(nums):
|
| 37 |
+
result.append(current[:])
|
| 38 |
+
return
|
| 39 |
+
for num in nums:
|
| 40 |
+
if num not in current:
|
| 41 |
+
current.append(num)
|
| 42 |
+
backtrack(current)
|
| 43 |
+
current.pop() # backtrack
|
| 44 |
+
backtrack([])
|
| 45 |
+
return result
|
| 46 |
+
|
| 47 |
+
print(permute([1, 2, 3]))
|
| 48 |
+
# [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
|
| 49 |
+
|
| 50 |
+
--- Combination Sum ---
|
| 51 |
+
Find all combinations that sum to target. Numbers can be reused.
|
| 52 |
+
|
| 53 |
+
def combination_sum(candidates, target):
|
| 54 |
+
result = []
|
| 55 |
+
candidates.sort()
|
| 56 |
+
def backtrack(start, current, remaining):
|
| 57 |
+
if remaining == 0:
|
| 58 |
+
result.append(current[:])
|
| 59 |
+
return
|
| 60 |
+
for i in range(start, len(candidates)):
|
| 61 |
+
if candidates[i] > remaining:
|
| 62 |
+
break
|
| 63 |
+
current.append(candidates[i])
|
| 64 |
+
backtrack(i, current, remaining - candidates[i]) # i (not i+1) allows reuse
|
| 65 |
+
current.pop()
|
| 66 |
+
backtrack(0, [], target)
|
| 67 |
+
return result
|
| 68 |
+
|
| 69 |
+
print(combination_sum([2, 3, 6, 7], 7))
|
| 70 |
+
# [[2, 2, 3], [7]]
|
| 71 |
+
|
| 72 |
+
--- N-Queens Problem ---
|
| 73 |
+
Place N queens on NxN board so no two queens attack each other.
|
| 74 |
+
|
| 75 |
+
def solve_n_queens(n):
|
| 76 |
+
result = []
|
| 77 |
+
cols = set()
|
| 78 |
+
diag1 = set() # row - col
|
| 79 |
+
diag2 = set() # row + col
|
| 80 |
+
|
| 81 |
+
board = [['.' for _ in range(n)] for _ in range(n)]
|
| 82 |
+
|
| 83 |
+
def backtrack(row):
|
| 84 |
+
if row == n:
|
| 85 |
+
result.append([''.join(r) for r in board])
|
| 86 |
+
return
|
| 87 |
+
for col in range(n):
|
| 88 |
+
if col in cols or (row - col) in diag1 or (row + col) in diag2:
|
| 89 |
+
continue
|
| 90 |
+
cols.add(col)
|
| 91 |
+
diag1.add(row - col)
|
| 92 |
+
diag2.add(row + col)
|
| 93 |
+
board[row][col] = 'Q'
|
| 94 |
+
backtrack(row + 1)
|
| 95 |
+
cols.remove(col) # backtrack
|
| 96 |
+
diag1.remove(row - col)
|
| 97 |
+
diag2.remove(row + col)
|
| 98 |
+
board[row][col] = '.'
|
| 99 |
+
|
| 100 |
+
backtrack(0)
|
| 101 |
+
return result
|
| 102 |
+
|
| 103 |
+
solutions = solve_n_queens(4)
|
| 104 |
+
print(f"Number of solutions for 4-Queens: {len(solutions)}") # 2
|
| 105 |
+
for sol in solutions:
|
| 106 |
+
for row in sol:
|
| 107 |
+
print(row)
|
| 108 |
+
print()
|
| 109 |
+
|
| 110 |
+
--- Sudoku Solver ---
|
| 111 |
+
Fill 9x9 grid so each row, column, and 3x3 box has digits 1-9.
|
| 112 |
+
|
| 113 |
+
def solve_sudoku(board):
|
| 114 |
+
def is_valid(board, row, col, num):
|
| 115 |
+
num = str(num)
|
| 116 |
+
if num in board[row]:
|
| 117 |
+
return False
|
| 118 |
+
if num in [board[r][col] for r in range(9)]:
|
| 119 |
+
return False
|
| 120 |
+
box_r, box_c = 3 * (row // 3), 3 * (col // 3)
|
| 121 |
+
for r in range(box_r, box_r + 3):
|
| 122 |
+
for c in range(box_c, box_c + 3):
|
| 123 |
+
if board[r][c] == num:
|
| 124 |
+
return False
|
| 125 |
+
return True
|
| 126 |
+
|
| 127 |
+
def backtrack():
|
| 128 |
+
for r in range(9):
|
| 129 |
+
for c in range(9):
|
| 130 |
+
if board[r][c] == '.':
|
| 131 |
+
for num in range(1, 10):
|
| 132 |
+
if is_valid(board, r, c, num):
|
| 133 |
+
board[r][c] = str(num)
|
| 134 |
+
if backtrack():
|
| 135 |
+
return True
|
| 136 |
+
board[r][c] = '.' # backtrack
|
| 137 |
+
return False
|
| 138 |
+
return True
|
| 139 |
+
|
| 140 |
+
backtrack()
|
| 141 |
+
|
| 142 |
+
--- Word Search in Grid ---
|
| 143 |
+
Find if word exists in 2D grid by moving up/down/left/right.
|
| 144 |
+
|
| 145 |
+
def word_search(board, word):
|
| 146 |
+
rows, cols = len(board), len(board[0])
|
| 147 |
+
|
| 148 |
+
def backtrack(r, c, index):
|
| 149 |
+
if index == len(word):
|
| 150 |
+
return True
|
| 151 |
+
if r < 0 or r >= rows or c < 0 or c >= cols:
|
| 152 |
+
return False
|
| 153 |
+
if board[r][c] != word[index]:
|
| 154 |
+
return False
|
| 155 |
+
temp = board[r][c]
|
| 156 |
+
board[r][c] = '#' # mark visited
|
| 157 |
+
found = (backtrack(r+1, c, index+1) or
|
| 158 |
+
backtrack(r-1, c, index+1) or
|
| 159 |
+
backtrack(r, c+1, index+1) or
|
| 160 |
+
backtrack(r, c-1, index+1))
|
| 161 |
+
board[r][c] = temp # backtrack (restore)
|
| 162 |
+
return found
|
| 163 |
+
|
| 164 |
+
for r in range(rows):
|
| 165 |
+
for c in range(cols):
|
| 166 |
+
if backtrack(r, c, 0):
|
| 167 |
+
return True
|
| 168 |
+
return False
|
| 169 |
+
|
| 170 |
+
board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
|
| 171 |
+
print(word_search(board, "ABCCED")) # True
|
| 172 |
+
print(word_search(board, "SEE")) # True
|
| 173 |
+
print(word_search(board, "ABCB")) # False
|
| 174 |
+
|
| 175 |
+
--- Rat in a Maze ---
|
| 176 |
+
Find path from (0,0) to (n-1,n-1) in a grid (1=open, 0=blocked).
|
| 177 |
+
|
| 178 |
+
def rat_in_maze(maze):
|
| 179 |
+
n = len(maze)
|
| 180 |
+
solution = [[0]*n for _ in range(n)]
|
| 181 |
+
|
| 182 |
+
def is_safe(r, c):
|
| 183 |
+
return 0 <= r < n and 0 <= c < n and maze[r][c] == 1
|
| 184 |
+
|
| 185 |
+
def solve(r, c):
|
| 186 |
+
if r == n-1 and c == n-1:
|
| 187 |
+
solution[r][c] = 1
|
| 188 |
+
return True
|
| 189 |
+
if is_safe(r, c):
|
| 190 |
+
solution[r][c] = 1
|
| 191 |
+
if solve(r+1, c): return True
|
| 192 |
+
if solve(r, c+1): return True
|
| 193 |
+
solution[r][c] = 0 # backtrack
|
| 194 |
+
return False
|
| 195 |
+
return False
|
| 196 |
+
|
| 197 |
+
if solve(0, 0):
|
| 198 |
+
return solution
|
| 199 |
+
return []
|
| 200 |
+
|
| 201 |
+
maze = [[1,0,0,0],[1,1,0,1],[0,1,0,0],[1,1,1,1]]
|
| 202 |
+
path = rat_in_maze(maze)
|
| 203 |
+
for row in path:
|
| 204 |
+
print(row)
|
data/docs/binary_search.txt
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SEARCHING ALGORITHMS
|
| 2 |
+
====================
|
| 3 |
+
|
| 4 |
+
--- Linear Search ---
|
| 5 |
+
Check every element one by one until the target is found.
|
| 6 |
+
Time: O(n). Space: O(1). Works on unsorted arrays.
|
| 7 |
+
|
| 8 |
+
def linear_search(arr, target):
|
| 9 |
+
for i, val in enumerate(arr):
|
| 10 |
+
if val == target:
|
| 11 |
+
return i
|
| 12 |
+
return -1
|
| 13 |
+
|
| 14 |
+
print(linear_search([5, 3, 8, 1, 9], 8)) # Output: 2
|
| 15 |
+
print(linear_search([5, 3, 8, 1, 9], 7)) # Output: -1
|
| 16 |
+
|
| 17 |
+
--- Binary Search ---
|
| 18 |
+
Works on SORTED arrays. Compare target with middle element.
|
| 19 |
+
If smaller, search left half. If larger, search right half.
|
| 20 |
+
Time: O(log n). Space: O(1) iterative, O(log n) recursive.
|
| 21 |
+
|
| 22 |
+
# Iterative
|
| 23 |
+
def binary_search(arr, target):
|
| 24 |
+
low, high = 0, len(arr) - 1
|
| 25 |
+
while low <= high:
|
| 26 |
+
mid = (low + high) // 2
|
| 27 |
+
if arr[mid] == target:
|
| 28 |
+
return mid
|
| 29 |
+
elif arr[mid] < target:
|
| 30 |
+
low = mid + 1
|
| 31 |
+
else:
|
| 32 |
+
high = mid - 1
|
| 33 |
+
return -1
|
| 34 |
+
|
| 35 |
+
arr = [1, 3, 5, 7, 9, 11, 13]
|
| 36 |
+
print(binary_search(arr, 7)) # Output: 3
|
| 37 |
+
print(binary_search(arr, 10)) # Output: -1
|
| 38 |
+
|
| 39 |
+
# Recursive
|
| 40 |
+
def binary_search_recursive(arr, target, low, high):
|
| 41 |
+
if low > high:
|
| 42 |
+
return -1
|
| 43 |
+
mid = (low + high) // 2
|
| 44 |
+
if arr[mid] == target:
|
| 45 |
+
return mid
|
| 46 |
+
elif arr[mid] < target:
|
| 47 |
+
return binary_search_recursive(arr, target, mid + 1, high)
|
| 48 |
+
else:
|
| 49 |
+
return binary_search_recursive(arr, target, low, mid - 1)
|
| 50 |
+
|
| 51 |
+
arr = [2, 4, 6, 8, 10, 12]
|
| 52 |
+
print(binary_search_recursive(arr, 10, 0, len(arr) - 1)) # Output: 4
|
| 53 |
+
|
| 54 |
+
--- Find First and Last Position ---
|
| 55 |
+
Find first and last occurrence of target in sorted array.
|
| 56 |
+
|
| 57 |
+
def find_first(arr, target):
|
| 58 |
+
low, high, result = 0, len(arr) - 1, -1
|
| 59 |
+
while low <= high:
|
| 60 |
+
mid = (low + high) // 2
|
| 61 |
+
if arr[mid] == target:
|
| 62 |
+
result = mid
|
| 63 |
+
high = mid - 1 # keep searching left
|
| 64 |
+
elif arr[mid] < target:
|
| 65 |
+
low = mid + 1
|
| 66 |
+
else:
|
| 67 |
+
high = mid - 1
|
| 68 |
+
return result
|
| 69 |
+
|
| 70 |
+
def find_last(arr, target):
|
| 71 |
+
low, high, result = 0, len(arr) - 1, -1
|
| 72 |
+
while low <= high:
|
| 73 |
+
mid = (low + high) // 2
|
| 74 |
+
if arr[mid] == target:
|
| 75 |
+
result = mid
|
| 76 |
+
low = mid + 1 # keep searching right
|
| 77 |
+
elif arr[mid] < target:
|
| 78 |
+
low = mid + 1
|
| 79 |
+
else:
|
| 80 |
+
high = mid - 1
|
| 81 |
+
return result
|
| 82 |
+
|
| 83 |
+
arr = [1, 2, 2, 2, 3, 4]
|
| 84 |
+
print(find_first(arr, 2)) # Output: 1
|
| 85 |
+
print(find_last(arr, 2)) # Output: 3
|
| 86 |
+
|
| 87 |
+
--- Search in Rotated Sorted Array ---
|
| 88 |
+
Array was sorted then rotated (e.g., [4,5,6,7,0,1,2]).
|
| 89 |
+
|
| 90 |
+
def search_rotated(arr, target):
|
| 91 |
+
low, high = 0, len(arr) - 1
|
| 92 |
+
while low <= high:
|
| 93 |
+
mid = (low + high) // 2
|
| 94 |
+
if arr[mid] == target:
|
| 95 |
+
return mid
|
| 96 |
+
if arr[low] <= arr[mid]: # left half sorted
|
| 97 |
+
if arr[low] <= target < arr[mid]:
|
| 98 |
+
high = mid - 1
|
| 99 |
+
else:
|
| 100 |
+
low = mid + 1
|
| 101 |
+
else: # right half sorted
|
| 102 |
+
if arr[mid] < target <= arr[high]:
|
| 103 |
+
low = mid + 1
|
| 104 |
+
else:
|
| 105 |
+
high = mid - 1
|
| 106 |
+
return -1
|
| 107 |
+
|
| 108 |
+
print(search_rotated([4, 5, 6, 7, 0, 1, 2], 0)) # Output: 4
|
| 109 |
+
print(search_rotated([4, 5, 6, 7, 0, 1, 2], 3)) # Output: -1
|
| 110 |
+
|
| 111 |
+
--- Square Root using Binary Search ---
|
| 112 |
+
Find integer square root of a number without using sqrt().
|
| 113 |
+
|
| 114 |
+
def sqrt_binary(n):
|
| 115 |
+
if n < 2:
|
| 116 |
+
return n
|
| 117 |
+
low, high, result = 1, n // 2, 0
|
| 118 |
+
while low <= high:
|
| 119 |
+
mid = (low + high) // 2
|
| 120 |
+
if mid * mid == n:
|
| 121 |
+
return mid
|
| 122 |
+
elif mid * mid < n:
|
| 123 |
+
result = mid
|
| 124 |
+
low = mid + 1
|
| 125 |
+
else:
|
| 126 |
+
high = mid - 1
|
| 127 |
+
return result
|
| 128 |
+
|
| 129 |
+
print(sqrt_binary(16)) # Output: 4
|
| 130 |
+
print(sqrt_binary(20)) # Output: 4 (floor)
|
data/docs/css_flexbox.txt
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
CSS Flexbox (Flexible Box Layout) is a layout model for arranging items in a container.
|
| 2 |
+
Activated by setting display: flex on a container element.
|
| 3 |
+
|
| 4 |
+
Container properties:
|
| 5 |
+
- flex-direction: row (default, horizontal) or column (vertical).
|
| 6 |
+
- justify-content: aligns items on main axis. Values: flex-start, flex-end, center, space-between, space-around.
|
| 7 |
+
- align-items: aligns items on cross axis. Values: stretch (default), flex-start, flex-end, center.
|
| 8 |
+
- flex-wrap: nowrap (default) or wrap to allow items to move to next line.
|
| 9 |
+
- gap: space between flex items.
|
| 10 |
+
|
| 11 |
+
Item properties:
|
| 12 |
+
- flex-grow: how much item grows relative to siblings. 0 means no grow.
|
| 13 |
+
- flex-shrink: how much item shrinks when space is limited.
|
| 14 |
+
- flex-basis: initial size of item before flex calculations.
|
| 15 |
+
- align-self: overrides align-items for a single item.
|
| 16 |
+
|
| 17 |
+
Shorthand: flex: grow shrink basis (e.g., flex: 1 1 auto)
|
data/docs/dynamic_programming.txt
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
DYNAMIC PROGRAMMING (DP)
|
| 2 |
+
========================
|
| 3 |
+
DP solves problems by breaking them into overlapping subproblems and storing results to avoid recomputation.
|
| 4 |
+
|
| 5 |
+
Two approaches:
|
| 6 |
+
- Top-Down (Memoization): recursion + cache
|
| 7 |
+
- Bottom-Up (Tabulation): fill a table iteratively
|
| 8 |
+
|
| 9 |
+
Key properties needed:
|
| 10 |
+
1. Optimal substructure: optimal solution built from optimal subproblems.
|
| 11 |
+
2. Overlapping subproblems: same subproblem solved multiple times.
|
| 12 |
+
|
| 13 |
+
--- Fibonacci Number ---
|
| 14 |
+
Classic DP example. fib(n) = fib(n-1) + fib(n-2)
|
| 15 |
+
|
| 16 |
+
# Naive recursion - O(2^n)
|
| 17 |
+
def fib_naive(n):
|
| 18 |
+
if n <= 1:
|
| 19 |
+
return n
|
| 20 |
+
return fib_naive(n-1) + fib_naive(n-2)
|
| 21 |
+
|
| 22 |
+
# Memoization - O(n)
|
| 23 |
+
def fib_memo(n, memo={}):
|
| 24 |
+
if n in memo:
|
| 25 |
+
return memo[n]
|
| 26 |
+
if n <= 1:
|
| 27 |
+
return n
|
| 28 |
+
memo[n] = fib_memo(n-1, memo) + fib_memo(n-2, memo)
|
| 29 |
+
return memo[n]
|
| 30 |
+
|
| 31 |
+
# Tabulation - O(n)
|
| 32 |
+
def fib_dp(n):
|
| 33 |
+
if n <= 1:
|
| 34 |
+
return n
|
| 35 |
+
dp = [0] * (n + 1)
|
| 36 |
+
dp[1] = 1
|
| 37 |
+
for i in range(2, n + 1):
|
| 38 |
+
dp[i] = dp[i-1] + dp[i-2]
|
| 39 |
+
return dp[n]
|
| 40 |
+
|
| 41 |
+
print(fib_dp(10)) # Output: 55
|
| 42 |
+
print(fib_dp(20)) # Output: 6765
|
| 43 |
+
|
| 44 |
+
--- 0/1 Knapsack Problem ---
|
| 45 |
+
Given items with weights and values, maximize value in a bag of capacity W.
|
| 46 |
+
Each item can be taken once (0/1). Time: O(n*W). Space: O(n*W).
|
| 47 |
+
|
| 48 |
+
def knapsack(weights, values, capacity):
|
| 49 |
+
n = len(weights)
|
| 50 |
+
dp = [[0] * (capacity + 1) for _ in range(n + 1)]
|
| 51 |
+
for i in range(1, n + 1):
|
| 52 |
+
for w in range(capacity + 1):
|
| 53 |
+
# Don't take item i
|
| 54 |
+
dp[i][w] = dp[i-1][w]
|
| 55 |
+
# Take item i if it fits
|
| 56 |
+
if weights[i-1] <= w:
|
| 57 |
+
dp[i][w] = max(dp[i][w], dp[i-1][w - weights[i-1]] + values[i-1])
|
| 58 |
+
return dp[n][capacity]
|
| 59 |
+
|
| 60 |
+
weights = [1, 2, 3, 5]
|
| 61 |
+
values = [1, 6, 10, 16]
|
| 62 |
+
capacity = 7
|
| 63 |
+
print(knapsack(weights, values, capacity)) # Output: 22
|
| 64 |
+
|
| 65 |
+
--- Longest Common Subsequence (LCS) ---
|
| 66 |
+
Find length of longest subsequence present in both strings.
|
| 67 |
+
Time: O(m*n). Space: O(m*n).
|
| 68 |
+
|
| 69 |
+
def lcs(s1, s2):
|
| 70 |
+
m, n = len(s1), len(s2)
|
| 71 |
+
dp = [[0] * (n + 1) for _ in range(m + 1)]
|
| 72 |
+
for i in range(1, m + 1):
|
| 73 |
+
for j in range(1, n + 1):
|
| 74 |
+
if s1[i-1] == s2[j-1]:
|
| 75 |
+
dp[i][j] = dp[i-1][j-1] + 1
|
| 76 |
+
else:
|
| 77 |
+
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
|
| 78 |
+
return dp[m][n]
|
| 79 |
+
|
| 80 |
+
print(lcs("ABCBDAB", "BDCABA")) # Output: 4 (BCBA)
|
| 81 |
+
|
| 82 |
+
--- Longest Increasing Subsequence (LIS) ---
|
| 83 |
+
Find length of longest strictly increasing subsequence.
|
| 84 |
+
Time: O(n^2). Space: O(n).
|
| 85 |
+
|
| 86 |
+
def lis(arr):
|
| 87 |
+
n = len(arr)
|
| 88 |
+
dp = [1] * n
|
| 89 |
+
for i in range(1, n):
|
| 90 |
+
for j in range(i):
|
| 91 |
+
if arr[j] < arr[i]:
|
| 92 |
+
dp[i] = max(dp[i], dp[j] + 1)
|
| 93 |
+
return max(dp)
|
| 94 |
+
|
| 95 |
+
print(lis([10, 9, 2, 5, 3, 7, 101, 18])) # Output: 4 (2,3,7,101)
|
| 96 |
+
|
| 97 |
+
--- Coin Change Problem ---
|
| 98 |
+
Find minimum number of coins to make amount. Coins can be reused.
|
| 99 |
+
Time: O(amount * len(coins)). Space: O(amount).
|
| 100 |
+
|
| 101 |
+
def coin_change(coins, amount):
|
| 102 |
+
dp = [float('inf')] * (amount + 1)
|
| 103 |
+
dp[0] = 0
|
| 104 |
+
for i in range(1, amount + 1):
|
| 105 |
+
for coin in coins:
|
| 106 |
+
if coin <= i:
|
| 107 |
+
dp[i] = min(dp[i], dp[i - coin] + 1)
|
| 108 |
+
return dp[amount] if dp[amount] != float('inf') else -1
|
| 109 |
+
|
| 110 |
+
print(coin_change([1, 5, 6, 9], 11)) # Output: 2 (coins 5+6)
|
| 111 |
+
print(coin_change([2], 3)) # Output: -1 (impossible)
|
| 112 |
+
|
| 113 |
+
--- Subset Sum Problem ---
|
| 114 |
+
Check if any subset of array sums to target.
|
| 115 |
+
Time: O(n * target). Space: O(n * target).
|
| 116 |
+
|
| 117 |
+
def subset_sum(arr, target):
|
| 118 |
+
n = len(arr)
|
| 119 |
+
dp = [[False] * (target + 1) for _ in range(n + 1)]
|
| 120 |
+
for i in range(n + 1):
|
| 121 |
+
dp[i][0] = True # sum 0 always possible
|
| 122 |
+
for i in range(1, n + 1):
|
| 123 |
+
for j in range(1, target + 1):
|
| 124 |
+
dp[i][j] = dp[i-1][j]
|
| 125 |
+
if arr[i-1] <= j:
|
| 126 |
+
dp[i][j] = dp[i][j] or dp[i-1][j - arr[i-1]]
|
| 127 |
+
return dp[n][target]
|
| 128 |
+
|
| 129 |
+
print(subset_sum([3, 34, 4, 12, 5, 2], 9)) # Output: True (4+5)
|
| 130 |
+
print(subset_sum([3, 34, 4, 12, 5, 2], 30)) # Output: False
|
| 131 |
+
|
| 132 |
+
--- Matrix Chain Multiplication ---
|
| 133 |
+
Find minimum operations to multiply a chain of matrices.
|
| 134 |
+
Time: O(n^3). Space: O(n^2).
|
| 135 |
+
|
| 136 |
+
def matrix_chain(dims):
|
| 137 |
+
n = len(dims) - 1
|
| 138 |
+
dp = [[0] * n for _ in range(n)]
|
| 139 |
+
for length in range(2, n + 1):
|
| 140 |
+
for i in range(n - length + 1):
|
| 141 |
+
j = i + length - 1
|
| 142 |
+
dp[i][j] = float('inf')
|
| 143 |
+
for k in range(i, j):
|
| 144 |
+
cost = dp[i][k] + dp[k+1][j] + dims[i]*dims[k+1]*dims[j+1]
|
| 145 |
+
dp[i][j] = min(dp[i][j], cost)
|
| 146 |
+
return dp[0][n-1]
|
| 147 |
+
|
| 148 |
+
dims = [40, 20, 30, 10, 30]
|
| 149 |
+
print(matrix_chain(dims)) # Output: 26000
|
| 150 |
+
|
| 151 |
+
--- Edit Distance (Levenshtein) ---
|
| 152 |
+
Minimum operations (insert, delete, replace) to convert one string to another.
|
| 153 |
+
Time: O(m*n). Space: O(m*n).
|
| 154 |
+
|
| 155 |
+
def edit_distance(s1, s2):
|
| 156 |
+
m, n = len(s1), len(s2)
|
| 157 |
+
dp = [[0] * (n + 1) for _ in range(m + 1)]
|
| 158 |
+
for i in range(m + 1):
|
| 159 |
+
dp[i][0] = i
|
| 160 |
+
for j in range(n + 1):
|
| 161 |
+
dp[0][j] = j
|
| 162 |
+
for i in range(1, m + 1):
|
| 163 |
+
for j in range(1, n + 1):
|
| 164 |
+
if s1[i-1] == s2[j-1]:
|
| 165 |
+
dp[i][j] = dp[i-1][j-1]
|
| 166 |
+
else:
|
| 167 |
+
dp[i][j] = 1 + min(dp[i-1][j], # delete
|
| 168 |
+
dp[i][j-1], # insert
|
| 169 |
+
dp[i-1][j-1]) # replace
|
| 170 |
+
return dp[m][n]
|
| 171 |
+
|
| 172 |
+
print(edit_distance("horse", "ros")) # Output: 3
|
| 173 |
+
print(edit_distance("intention", "execution")) # Output: 5
|
data/docs/graph_algorithms.txt
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
GRAPH ALGORITHMS
|
| 2 |
+
================
|
| 3 |
+
A graph has vertices (nodes) connected by edges. Used to model networks, maps, dependencies.
|
| 4 |
+
|
| 5 |
+
Types:
|
| 6 |
+
- Directed / Undirected
|
| 7 |
+
- Weighted / Unweighted
|
| 8 |
+
- Cyclic / Acyclic (DAG = Directed Acyclic Graph)
|
| 9 |
+
|
| 10 |
+
Representations:
|
| 11 |
+
- Adjacency Matrix: 2D array. O(1) edge lookup, O(V^2) space.
|
| 12 |
+
- Adjacency List: dict/list of lists. O(V+E) space. Most common.
|
| 13 |
+
|
| 14 |
+
# Adjacency List representation
|
| 15 |
+
graph = {
|
| 16 |
+
0: [1, 2],
|
| 17 |
+
1: [0, 3, 4],
|
| 18 |
+
2: [0],
|
| 19 |
+
3: [1],
|
| 20 |
+
4: [1]
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
--- BFS (Breadth-First Search) ---
|
| 24 |
+
Explore level by level using a queue. Finds shortest path in unweighted graphs.
|
| 25 |
+
Time: O(V+E). Space: O(V).
|
| 26 |
+
|
| 27 |
+
from collections import deque
|
| 28 |
+
|
| 29 |
+
def bfs(graph, start):
|
| 30 |
+
visited = set([start])
|
| 31 |
+
queue = deque([start])
|
| 32 |
+
order = []
|
| 33 |
+
while queue:
|
| 34 |
+
node = queue.popleft()
|
| 35 |
+
order.append(node)
|
| 36 |
+
for neighbor in graph[node]:
|
| 37 |
+
if neighbor not in visited:
|
| 38 |
+
visited.add(neighbor)
|
| 39 |
+
queue.append(neighbor)
|
| 40 |
+
return order
|
| 41 |
+
|
| 42 |
+
graph = {0:[1,2], 1:[0,3,4], 2:[0], 3:[1], 4:[1]}
|
| 43 |
+
print(bfs(graph, 0)) # Output: [0, 1, 2, 3, 4]
|
| 44 |
+
|
| 45 |
+
--- DFS (Depth-First Search) ---
|
| 46 |
+
Explore as deep as possible before backtracking. Uses stack (or recursion).
|
| 47 |
+
Time: O(V+E). Space: O(V).
|
| 48 |
+
|
| 49 |
+
# Iterative DFS
|
| 50 |
+
def dfs_iterative(graph, start):
|
| 51 |
+
visited = set()
|
| 52 |
+
stack = [start]
|
| 53 |
+
order = []
|
| 54 |
+
while stack:
|
| 55 |
+
node = stack.pop()
|
| 56 |
+
if node not in visited:
|
| 57 |
+
visited.add(node)
|
| 58 |
+
order.append(node)
|
| 59 |
+
for neighbor in graph[node]:
|
| 60 |
+
if neighbor not in visited:
|
| 61 |
+
stack.append(neighbor)
|
| 62 |
+
return order
|
| 63 |
+
|
| 64 |
+
# Recursive DFS
|
| 65 |
+
def dfs_recursive(graph, node, visited=None):
|
| 66 |
+
if visited is None:
|
| 67 |
+
visited = set()
|
| 68 |
+
visited.add(node)
|
| 69 |
+
result = [node]
|
| 70 |
+
for neighbor in graph[node]:
|
| 71 |
+
if neighbor not in visited:
|
| 72 |
+
result += dfs_recursive(graph, neighbor, visited)
|
| 73 |
+
return result
|
| 74 |
+
|
| 75 |
+
print(dfs_iterative(graph, 0)) # Output: [0, 2, 1, 4, 3]
|
| 76 |
+
print(dfs_recursive(graph, 0)) # Output: [0, 1, 3, 4, 2]
|
| 77 |
+
|
| 78 |
+
--- Shortest Path: Dijkstra's Algorithm ---
|
| 79 |
+
Find shortest path from source to all nodes in a weighted graph (non-negative weights).
|
| 80 |
+
Time: O((V+E) log V) with min-heap. Space: O(V).
|
| 81 |
+
|
| 82 |
+
import heapq
|
| 83 |
+
|
| 84 |
+
def dijkstra(graph, start):
|
| 85 |
+
# graph: {node: [(neighbor, weight), ...]}
|
| 86 |
+
dist = {node: float('inf') for node in graph}
|
| 87 |
+
dist[start] = 0
|
| 88 |
+
heap = [(0, start)] # (distance, node)
|
| 89 |
+
while heap:
|
| 90 |
+
d, u = heapq.heappop(heap)
|
| 91 |
+
if d > dist[u]:
|
| 92 |
+
continue
|
| 93 |
+
for v, w in graph[u]:
|
| 94 |
+
if dist[u] + w < dist[v]:
|
| 95 |
+
dist[v] = dist[u] + w
|
| 96 |
+
heapq.heappush(heap, (dist[v], v))
|
| 97 |
+
return dist
|
| 98 |
+
|
| 99 |
+
wgraph = {
|
| 100 |
+
'A': [('B', 1), ('C', 4)],
|
| 101 |
+
'B': [('C', 2), ('D', 5)],
|
| 102 |
+
'C': [('D', 1)],
|
| 103 |
+
'D': []
|
| 104 |
+
}
|
| 105 |
+
print(dijkstra(wgraph, 'A'))
|
| 106 |
+
# Output: {'A': 0, 'B': 1, 'C': 3, 'D': 4}
|
| 107 |
+
|
| 108 |
+
--- Cycle Detection in Directed Graph ---
|
| 109 |
+
Use DFS with a recursion stack to detect back edges.
|
| 110 |
+
|
| 111 |
+
def has_cycle_directed(graph):
|
| 112 |
+
visited = set()
|
| 113 |
+
rec_stack = set()
|
| 114 |
+
|
| 115 |
+
def dfs(node):
|
| 116 |
+
visited.add(node)
|
| 117 |
+
rec_stack.add(node)
|
| 118 |
+
for neighbor in graph.get(node, []):
|
| 119 |
+
if neighbor not in visited:
|
| 120 |
+
if dfs(neighbor):
|
| 121 |
+
return True
|
| 122 |
+
elif neighbor in rec_stack:
|
| 123 |
+
return True
|
| 124 |
+
rec_stack.remove(node)
|
| 125 |
+
return False
|
| 126 |
+
|
| 127 |
+
for node in graph:
|
| 128 |
+
if node not in visited:
|
| 129 |
+
if dfs(node):
|
| 130 |
+
return True
|
| 131 |
+
return False
|
| 132 |
+
|
| 133 |
+
g1 = {0: [1], 1: [2], 2: [0]} # cycle
|
| 134 |
+
g2 = {0: [1], 1: [2], 2: []} # no cycle
|
| 135 |
+
print(has_cycle_directed(g1)) # True
|
| 136 |
+
print(has_cycle_directed(g2)) # False
|
| 137 |
+
|
| 138 |
+
--- Topological Sort (Kahn's Algorithm) ---
|
| 139 |
+
Linear ordering of vertices in a DAG such that for every edge u->v, u comes before v.
|
| 140 |
+
Time: O(V+E). Space: O(V).
|
| 141 |
+
|
| 142 |
+
from collections import deque
|
| 143 |
+
|
| 144 |
+
def topological_sort(graph, num_nodes):
|
| 145 |
+
in_degree = [0] * num_nodes
|
| 146 |
+
for u in graph:
|
| 147 |
+
for v in graph[u]:
|
| 148 |
+
in_degree[v] += 1
|
| 149 |
+
queue = deque([i for i in range(num_nodes) if in_degree[i] == 0])
|
| 150 |
+
order = []
|
| 151 |
+
while queue:
|
| 152 |
+
node = queue.popleft()
|
| 153 |
+
order.append(node)
|
| 154 |
+
for neighbor in graph.get(node, []):
|
| 155 |
+
in_degree[neighbor] -= 1
|
| 156 |
+
if in_degree[neighbor] == 0:
|
| 157 |
+
queue.append(neighbor)
|
| 158 |
+
return order if len(order) == num_nodes else [] # empty = cycle
|
| 159 |
+
|
| 160 |
+
dag = {0: [1, 2], 1: [3], 2: [3], 3: []}
|
| 161 |
+
print(topological_sort(dag, 4)) # Output: [0, 1, 2, 3] or [0, 2, 1, 3]
|
| 162 |
+
|
| 163 |
+
--- Minimum Spanning Tree: Kruskal's Algorithm ---
|
| 164 |
+
Connect all nodes with minimum total edge weight. Uses Union-Find.
|
| 165 |
+
Time: O(E log E). Space: O(V).
|
| 166 |
+
|
| 167 |
+
def kruskal(num_nodes, edges):
|
| 168 |
+
# edges: [(weight, u, v), ...]
|
| 169 |
+
edges.sort()
|
| 170 |
+
parent = list(range(num_nodes))
|
| 171 |
+
|
| 172 |
+
def find(x):
|
| 173 |
+
while parent[x] != x:
|
| 174 |
+
parent[x] = parent[parent[x]]
|
| 175 |
+
x = parent[x]
|
| 176 |
+
return x
|
| 177 |
+
|
| 178 |
+
def union(x, y):
|
| 179 |
+
px, py = find(x), find(y)
|
| 180 |
+
if px == py:
|
| 181 |
+
return False
|
| 182 |
+
parent[px] = py
|
| 183 |
+
return True
|
| 184 |
+
|
| 185 |
+
mst_cost = 0
|
| 186 |
+
mst_edges = []
|
| 187 |
+
for w, u, v in edges:
|
| 188 |
+
if union(u, v):
|
| 189 |
+
mst_cost += w
|
| 190 |
+
mst_edges.append((u, v, w))
|
| 191 |
+
return mst_cost, mst_edges
|
| 192 |
+
|
| 193 |
+
edges = [(4,0,1),(2,0,2),(3,1,2),(1,1,3),(5,2,3)]
|
| 194 |
+
print(kruskal(4, edges))
|
| 195 |
+
# Output: (6, [(1, 3, 1), (0, 2, 2), (1, 2, 3)])
|
| 196 |
+
|
| 197 |
+
--- Bellman-Ford Algorithm ---
|
| 198 |
+
Shortest path with negative weights. Detects negative cycles.
|
| 199 |
+
Time: O(V*E). Space: O(V).
|
| 200 |
+
|
| 201 |
+
def bellman_ford(graph, num_nodes, start):
|
| 202 |
+
# graph: [(u, v, weight), ...]
|
| 203 |
+
dist = [float('inf')] * num_nodes
|
| 204 |
+
dist[start] = 0
|
| 205 |
+
for _ in range(num_nodes - 1):
|
| 206 |
+
for u, v, w in graph:
|
| 207 |
+
if dist[u] + w < dist[v]:
|
| 208 |
+
dist[v] = dist[u] + w
|
| 209 |
+
# Check for negative cycles
|
| 210 |
+
for u, v, w in graph:
|
| 211 |
+
if dist[u] + w < dist[v]:
|
| 212 |
+
return None # negative cycle
|
| 213 |
+
return dist
|
| 214 |
+
|
| 215 |
+
edges = [(0,1,4),(0,2,5),(1,2,-3),(2,3,4)]
|
| 216 |
+
print(bellman_ford(edges, 4, 0))
|
| 217 |
+
# Output: [0, 4, 1, 5]
|
data/docs/greedy_algorithms.txt
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
GREEDY ALGORITHMS
|
| 2 |
+
=================
|
| 3 |
+
Greedy algorithms make the locally optimal choice at each step hoping to find a global optimum.
|
| 4 |
+
They do NOT reconsider past choices (unlike DP).
|
| 5 |
+
|
| 6 |
+
When to use greedy: when local optimum leads to global optimum (greedy choice property + optimal substructure).
|
| 7 |
+
|
| 8 |
+
--- Activity Selection Problem ---
|
| 9 |
+
Select maximum number of non-overlapping activities.
|
| 10 |
+
Sort by end time, always pick activity that ends earliest.
|
| 11 |
+
Time: O(n log n).
|
| 12 |
+
|
| 13 |
+
def activity_selection(activities):
|
| 14 |
+
# activities: [(start, end), ...]
|
| 15 |
+
activities.sort(key=lambda x: x[1]) # sort by end time
|
| 16 |
+
selected = [activities[0]]
|
| 17 |
+
last_end = activities[0][1]
|
| 18 |
+
for start, end in activities[1:]:
|
| 19 |
+
if start >= last_end:
|
| 20 |
+
selected.append((start, end))
|
| 21 |
+
last_end = end
|
| 22 |
+
return selected
|
| 23 |
+
|
| 24 |
+
acts = [(1,4),(3,5),(0,6),(5,7),(3,9),(5,9),(6,10),(8,11),(8,12),(2,14),(12,16)]
|
| 25 |
+
print(activity_selection(acts))
|
| 26 |
+
# [(1,4),(5,7),(8,11),(12,16)] — 4 activities
|
| 27 |
+
|
| 28 |
+
--- Fractional Knapsack ---
|
| 29 |
+
Take fractions of items to maximize value. Unlike 0/1 knapsack, items can be split.
|
| 30 |
+
Sort by value/weight ratio. Greedy works here (not for 0/1).
|
| 31 |
+
Time: O(n log n).
|
| 32 |
+
|
| 33 |
+
def fractional_knapsack(weights, values, capacity):
|
| 34 |
+
items = sorted(zip(values, weights), key=lambda x: x[0]/x[1], reverse=True)
|
| 35 |
+
total_value = 0
|
| 36 |
+
for value, weight in items:
|
| 37 |
+
if capacity >= weight:
|
| 38 |
+
total_value += value
|
| 39 |
+
capacity -= weight
|
| 40 |
+
else:
|
| 41 |
+
total_value += value * (capacity / weight)
|
| 42 |
+
break
|
| 43 |
+
return round(total_value, 2)
|
| 44 |
+
|
| 45 |
+
weights = [10, 20, 30]
|
| 46 |
+
values = [60, 100, 120]
|
| 47 |
+
print(fractional_knapsack(weights, values, 50)) # Output: 240.0
|
| 48 |
+
|
| 49 |
+
--- Coin Change (Greedy — only works for canonical coin systems) ---
|
| 50 |
+
For standard denominations (1,5,10,25 cents), greedy gives minimum coins.
|
| 51 |
+
Note: greedy FAILS for arbitrary denominations — use DP instead.
|
| 52 |
+
|
| 53 |
+
def coin_change_greedy(coins, amount):
|
| 54 |
+
coins.sort(reverse=True)
|
| 55 |
+
count = 0
|
| 56 |
+
result = []
|
| 57 |
+
for coin in coins:
|
| 58 |
+
while amount >= coin:
|
| 59 |
+
amount -= coin
|
| 60 |
+
count += 1
|
| 61 |
+
result.append(coin)
|
| 62 |
+
return count, result
|
| 63 |
+
|
| 64 |
+
print(coin_change_greedy([1, 5, 10, 25], 41))
|
| 65 |
+
# Output: (4, [25, 10, 5, 1])
|
| 66 |
+
|
| 67 |
+
--- Job Scheduling to Maximize Profit ---
|
| 68 |
+
Schedule jobs with deadlines to maximize total profit. Each job takes 1 unit of time.
|
| 69 |
+
Time: O(n^2).
|
| 70 |
+
|
| 71 |
+
def job_scheduling(jobs):
|
| 72 |
+
# jobs: [(id, deadline, profit), ...]
|
| 73 |
+
jobs.sort(key=lambda x: x[2], reverse=True) # sort by profit
|
| 74 |
+
max_deadline = max(j[1] for j in jobs)
|
| 75 |
+
slots = [None] * (max_deadline + 1)
|
| 76 |
+
total_profit = 0
|
| 77 |
+
for job_id, deadline, profit in jobs:
|
| 78 |
+
for t in range(min(deadline, max_deadline), 0, -1):
|
| 79 |
+
if slots[t] is None:
|
| 80 |
+
slots[t] = job_id
|
| 81 |
+
total_profit += profit
|
| 82 |
+
break
|
| 83 |
+
scheduled = [s for s in slots if s]
|
| 84 |
+
return scheduled, total_profit
|
| 85 |
+
|
| 86 |
+
jobs = [('a',2,100),('b',1,19),('c',2,27),('d',1,25),('e',3,15)]
|
| 87 |
+
print(job_scheduling(jobs)) # (['c', 'a', 'e'], 142)
|
| 88 |
+
|
| 89 |
+
--- Huffman Encoding ---
|
| 90 |
+
Build optimal prefix-free codes for data compression.
|
| 91 |
+
Characters with higher frequency get shorter codes.
|
| 92 |
+
Time: O(n log n).
|
| 93 |
+
|
| 94 |
+
import heapq
|
| 95 |
+
|
| 96 |
+
def huffman_encoding(freq):
|
| 97 |
+
heap = [[f, [ch, ""]] for ch, f in freq.items()]
|
| 98 |
+
heapq.heapify(heap)
|
| 99 |
+
while len(heap) > 1:
|
| 100 |
+
lo = heapq.heappop(heap)
|
| 101 |
+
hi = heapq.heappop(heap)
|
| 102 |
+
for pair in lo[1:]:
|
| 103 |
+
pair[1] = '0' + pair[1]
|
| 104 |
+
for pair in hi[1:]:
|
| 105 |
+
pair[1] = '1' + pair[1]
|
| 106 |
+
heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])
|
| 107 |
+
return sorted(heap[0][1:], key=lambda x: len(x[1]))
|
| 108 |
+
|
| 109 |
+
freq = {'a': 5, 'b': 9, 'c': 12, 'd': 13, 'e': 16, 'f': 45}
|
| 110 |
+
codes = huffman_encoding(freq)
|
| 111 |
+
for char, code in codes:
|
| 112 |
+
print(f"{char}: {code}")
|
| 113 |
+
# f: 0
|
| 114 |
+
# c: 100
|
| 115 |
+
# d: 101
|
| 116 |
+
# a: 1100
|
| 117 |
+
# b: 1101
|
| 118 |
+
# e: 111
|
| 119 |
+
|
| 120 |
+
--- Minimum Platforms (Greedy) ---
|
| 121 |
+
Find minimum number of platforms needed at a railway station.
|
| 122 |
+
Time: O(n log n).
|
| 123 |
+
|
| 124 |
+
def min_platforms(arrivals, departures):
|
| 125 |
+
arrivals.sort()
|
| 126 |
+
departures.sort()
|
| 127 |
+
platforms = 1
|
| 128 |
+
max_platforms = 1
|
| 129 |
+
i = 1
|
| 130 |
+
j = 0
|
| 131 |
+
while i < len(arrivals) and j < len(departures):
|
| 132 |
+
if arrivals[i] <= departures[j]:
|
| 133 |
+
platforms += 1
|
| 134 |
+
i += 1
|
| 135 |
+
else:
|
| 136 |
+
platforms -= 1
|
| 137 |
+
j += 1
|
| 138 |
+
max_platforms = max(max_platforms, platforms)
|
| 139 |
+
return max_platforms
|
| 140 |
+
|
| 141 |
+
arr = [900, 940, 950, 1100, 1500, 1800]
|
| 142 |
+
dep = [910, 1200, 1120, 1130, 1900, 2000]
|
| 143 |
+
print(min_platforms(arr, dep)) # Output: 3
|
| 144 |
+
|
| 145 |
+
--- Gas Station (Circular Route) ---
|
| 146 |
+
Find starting gas station to complete a circular route. Greedy: if total gas >= total cost, solution exists.
|
| 147 |
+
Time: O(n). Space: O(1).
|
| 148 |
+
|
| 149 |
+
def can_complete_circuit(gas, cost):
|
| 150 |
+
total_tank = 0
|
| 151 |
+
curr_tank = 0
|
| 152 |
+
start = 0
|
| 153 |
+
for i in range(len(gas)):
|
| 154 |
+
diff = gas[i] - cost[i]
|
| 155 |
+
total_tank += diff
|
| 156 |
+
curr_tank += diff
|
| 157 |
+
if curr_tank < 0:
|
| 158 |
+
start = i + 1
|
| 159 |
+
curr_tank = 0
|
| 160 |
+
return start if total_tank >= 0 else -1
|
| 161 |
+
|
| 162 |
+
gas = [1, 2, 3, 4, 5]
|
| 163 |
+
cost = [3, 4, 5, 1, 2]
|
| 164 |
+
print(can_complete_circuit(gas, cost)) # Output: 3
|
| 165 |
+
|
| 166 |
+
--- Greedy vs Dynamic Programming ---
|
| 167 |
+
Greedy: fast O(n log n), works when greedy choice is provably optimal.
|
| 168 |
+
DP: slower O(n^2) or O(n*W), works for all optimization problems.
|
| 169 |
+
Example: Fractional Knapsack → Greedy. 0/1 Knapsack → DP.
|
data/docs/hashing.txt
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
HASHING AND HASH TABLES
|
| 2 |
+
=======================
|
| 3 |
+
A hash table stores key-value pairs. Uses a hash function to map keys to indices.
|
| 4 |
+
Average time: O(1) for insert, delete, search. Worst case: O(n) with collisions.
|
| 5 |
+
|
| 6 |
+
Python's dict and set are hash tables.
|
| 7 |
+
|
| 8 |
+
--- Hash Table Basics ---
|
| 9 |
+
# Python dict = hash map
|
| 10 |
+
phone_book = {}
|
| 11 |
+
phone_book["Alice"] = "555-1234"
|
| 12 |
+
phone_book["Bob"] = "555-5678"
|
| 13 |
+
|
| 14 |
+
print(phone_book["Alice"]) # "555-1234"
|
| 15 |
+
print("Alice" in phone_book) # True
|
| 16 |
+
del phone_book["Bob"]
|
| 17 |
+
print(phone_book.get("Bob", "N/A")) # "N/A"
|
| 18 |
+
|
| 19 |
+
# Iterate
|
| 20 |
+
for name, number in phone_book.items():
|
| 21 |
+
print(name, number)
|
| 22 |
+
|
| 23 |
+
--- Build a Hash Map from Scratch ---
|
| 24 |
+
class HashMap:
|
| 25 |
+
def __init__(self, size=1000):
|
| 26 |
+
self.size = size
|
| 27 |
+
self.buckets = [[] for _ in range(size)]
|
| 28 |
+
|
| 29 |
+
def _hash(self, key):
|
| 30 |
+
return hash(key) % self.size
|
| 31 |
+
|
| 32 |
+
def put(self, key, value):
|
| 33 |
+
idx = self._hash(key)
|
| 34 |
+
for i, (k, v) in enumerate(self.buckets[idx]):
|
| 35 |
+
if k == key:
|
| 36 |
+
self.buckets[idx][i] = (key, value)
|
| 37 |
+
return
|
| 38 |
+
self.buckets[idx].append((key, value))
|
| 39 |
+
|
| 40 |
+
def get(self, key):
|
| 41 |
+
idx = self._hash(key)
|
| 42 |
+
for k, v in self.buckets[idx]:
|
| 43 |
+
if k == key:
|
| 44 |
+
return v
|
| 45 |
+
return None
|
| 46 |
+
|
| 47 |
+
def remove(self, key):
|
| 48 |
+
idx = self._hash(key)
|
| 49 |
+
self.buckets[idx] = [(k, v) for k, v in self.buckets[idx] if k != key]
|
| 50 |
+
|
| 51 |
+
hm = HashMap()
|
| 52 |
+
hm.put("name", "Alice")
|
| 53 |
+
hm.put("age", 25)
|
| 54 |
+
print(hm.get("name")) # Alice
|
| 55 |
+
hm.remove("age")
|
| 56 |
+
print(hm.get("age")) # None
|
| 57 |
+
|
| 58 |
+
--- Two Sum Problem ---
|
| 59 |
+
Find two numbers that add up to target. Classic hash map problem.
|
| 60 |
+
Time: O(n). Space: O(n).
|
| 61 |
+
|
| 62 |
+
def two_sum(nums, target):
|
| 63 |
+
seen = {} # value → index
|
| 64 |
+
for i, num in enumerate(nums):
|
| 65 |
+
complement = target - num
|
| 66 |
+
if complement in seen:
|
| 67 |
+
return [seen[complement], i]
|
| 68 |
+
seen[num] = i
|
| 69 |
+
return []
|
| 70 |
+
|
| 71 |
+
print(two_sum([2, 7, 11, 15], 9)) # [0, 1]
|
| 72 |
+
print(two_sum([3, 2, 4], 6)) # [1, 2]
|
| 73 |
+
|
| 74 |
+
--- Group Anagrams ---
|
| 75 |
+
Group words that are anagrams of each other.
|
| 76 |
+
Time: O(n * k log k) where k = max word length.
|
| 77 |
+
|
| 78 |
+
from collections import defaultdict
|
| 79 |
+
|
| 80 |
+
def group_anagrams(words):
|
| 81 |
+
groups = defaultdict(list)
|
| 82 |
+
for word in words:
|
| 83 |
+
key = ''.join(sorted(word)) # sorted chars as key
|
| 84 |
+
groups[key].append(word)
|
| 85 |
+
return list(groups.values())
|
| 86 |
+
|
| 87 |
+
print(group_anagrams(["eat","tea","tan","ate","nat","bat"]))
|
| 88 |
+
# [["eat","tea","ate"], ["tan","nat"], ["bat"]]
|
| 89 |
+
|
| 90 |
+
--- Frequency Counter ---
|
| 91 |
+
Count frequency of each element.
|
| 92 |
+
|
| 93 |
+
from collections import Counter
|
| 94 |
+
|
| 95 |
+
def top_k_frequent(nums, k):
|
| 96 |
+
count = Counter(nums)
|
| 97 |
+
return [x for x, _ in count.most_common(k)]
|
| 98 |
+
|
| 99 |
+
print(top_k_frequent([1,1,1,2,2,3], 2)) # [1, 2]
|
| 100 |
+
|
| 101 |
+
--- Longest Consecutive Sequence ---
|
| 102 |
+
Find length of longest consecutive elements sequence.
|
| 103 |
+
Time: O(n). Space: O(n).
|
| 104 |
+
|
| 105 |
+
def longest_consecutive(nums):
|
| 106 |
+
num_set = set(nums)
|
| 107 |
+
best = 0
|
| 108 |
+
for num in num_set:
|
| 109 |
+
if num - 1 not in num_set: # start of sequence
|
| 110 |
+
length = 1
|
| 111 |
+
while num + length in num_set:
|
| 112 |
+
length += 1
|
| 113 |
+
best = max(best, length)
|
| 114 |
+
return best
|
| 115 |
+
|
| 116 |
+
print(longest_consecutive([100,4,200,1,3,2])) # 4 (1,2,3,4)
|
| 117 |
+
|
| 118 |
+
--- Subarray Sum Equals K ---
|
| 119 |
+
Count subarrays with sum equal to k. Uses prefix sum + hash map.
|
| 120 |
+
Time: O(n). Space: O(n).
|
| 121 |
+
|
| 122 |
+
def subarray_sum(nums, k):
|
| 123 |
+
count = 0
|
| 124 |
+
prefix = 0
|
| 125 |
+
freq = {0: 1} # prefix_sum → frequency
|
| 126 |
+
for num in nums:
|
| 127 |
+
prefix += num
|
| 128 |
+
if prefix - k in freq:
|
| 129 |
+
count += freq[prefix - k]
|
| 130 |
+
freq[prefix] = freq.get(prefix, 0) + 1
|
| 131 |
+
return count
|
| 132 |
+
|
| 133 |
+
print(subarray_sum([1, 1, 1], 2)) # 2
|
| 134 |
+
print(subarray_sum([1, 2, 3], 3)) # 2
|
| 135 |
+
|
| 136 |
+
--- Check if Two Strings are Anagram ---
|
| 137 |
+
def is_anagram(s, t):
|
| 138 |
+
return Counter(s) == Counter(t)
|
| 139 |
+
|
| 140 |
+
print(is_anagram("anagram", "nagaram")) # True
|
| 141 |
+
print(is_anagram("rat", "car")) # False
|
| 142 |
+
|
| 143 |
+
--- First Non-Repeating Character ---
|
| 144 |
+
def first_unique(s):
|
| 145 |
+
count = Counter(s)
|
| 146 |
+
for i, ch in enumerate(s):
|
| 147 |
+
if count[ch] == 1:
|
| 148 |
+
return i
|
| 149 |
+
return -1
|
| 150 |
+
|
| 151 |
+
print(first_unique("leetcode")) # 0 (l)
|
| 152 |
+
print(first_unique("aabb")) # -1
|
| 153 |
+
|
| 154 |
+
--- Hash Set Uses ---
|
| 155 |
+
# O(1) membership test
|
| 156 |
+
visited = set()
|
| 157 |
+
visited.add(5)
|
| 158 |
+
print(5 in visited) # True
|
| 159 |
+
print(10 in visited) # False
|
| 160 |
+
|
| 161 |
+
# Remove duplicates
|
| 162 |
+
arr = [1, 2, 2, 3, 3, 4]
|
| 163 |
+
unique = list(set(arr))
|
| 164 |
+
print(unique) # [1, 2, 3, 4]
|
| 165 |
+
|
| 166 |
+
# Intersection, Union
|
| 167 |
+
a = {1, 2, 3, 4}
|
| 168 |
+
b = {3, 4, 5, 6}
|
| 169 |
+
print(a & b) # {3, 4} intersection
|
| 170 |
+
print(a | b) # {1,2,3,4,5,6} union
|
| 171 |
+
print(a - b) # {1, 2} difference
|
data/docs/javascript_promises.txt
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
JavaScript Promises handle asynchronous operations in a cleaner way than callbacks.
|
| 2 |
+
|
| 3 |
+
Promise States:
|
| 4 |
+
- Pending: initial state, operation not complete.
|
| 5 |
+
- Fulfilled: operation completed successfully.
|
| 6 |
+
- Rejected: operation failed.
|
| 7 |
+
|
| 8 |
+
Creating a Promise:
|
| 9 |
+
const myPromise = new Promise((resolve, reject) => {
|
| 10 |
+
if (success) resolve(result);
|
| 11 |
+
else reject(error);
|
| 12 |
+
});
|
| 13 |
+
|
| 14 |
+
Consuming a Promise:
|
| 15 |
+
myPromise
|
| 16 |
+
.then(result => console.log(result))
|
| 17 |
+
.catch(error => console.error(error))
|
| 18 |
+
.finally(() => console.log('always runs'));
|
| 19 |
+
|
| 20 |
+
async/await syntax (ES2017) is syntactic sugar over promises:
|
| 21 |
+
async function fetchData() {
|
| 22 |
+
try {
|
| 23 |
+
const data = await fetch(url);
|
| 24 |
+
const json = await data.json();
|
| 25 |
+
return json;
|
| 26 |
+
} catch (error) {
|
| 27 |
+
console.error(error);
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
Promise utility methods:
|
| 32 |
+
- Promise.all([p1, p2]): runs in parallel, resolves when all resolve.
|
| 33 |
+
- Promise.race([p1, p2]): resolves/rejects with first settled promise.
|
| 34 |
+
- Promise.allSettled(): waits for all, returns all results regardless of success/failure.
|
data/docs/linked_list.txt
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
LINKED LISTS
|
| 2 |
+
============
|
| 3 |
+
A linked list stores elements in nodes. Each node holds data and a pointer to the next node.
|
| 4 |
+
Unlike arrays, nodes are not stored in contiguous memory.
|
| 5 |
+
|
| 6 |
+
Advantages: O(1) insert/delete at head, dynamic size.
|
| 7 |
+
Disadvantages: O(n) access by index, extra memory for pointers.
|
| 8 |
+
|
| 9 |
+
--- Node Definition ---
|
| 10 |
+
class Node:
|
| 11 |
+
def __init__(self, data):
|
| 12 |
+
self.data = data
|
| 13 |
+
self.next = None
|
| 14 |
+
|
| 15 |
+
--- Singly Linked List ---
|
| 16 |
+
Each node points to the next. Last node points to None.
|
| 17 |
+
|
| 18 |
+
class LinkedList:
|
| 19 |
+
def __init__(self):
|
| 20 |
+
self.head = None
|
| 21 |
+
|
| 22 |
+
def insert_at_head(self, data):
|
| 23 |
+
node = Node(data)
|
| 24 |
+
node.next = self.head
|
| 25 |
+
self.head = node
|
| 26 |
+
|
| 27 |
+
def insert_at_tail(self, data):
|
| 28 |
+
node = Node(data)
|
| 29 |
+
if not self.head:
|
| 30 |
+
self.head = node
|
| 31 |
+
return
|
| 32 |
+
curr = self.head
|
| 33 |
+
while curr.next:
|
| 34 |
+
curr = curr.next
|
| 35 |
+
curr.next = node
|
| 36 |
+
|
| 37 |
+
def delete(self, data):
|
| 38 |
+
if not self.head:
|
| 39 |
+
return
|
| 40 |
+
if self.head.data == data:
|
| 41 |
+
self.head = self.head.next
|
| 42 |
+
return
|
| 43 |
+
curr = self.head
|
| 44 |
+
while curr.next and curr.next.data != data:
|
| 45 |
+
curr = curr.next
|
| 46 |
+
if curr.next:
|
| 47 |
+
curr.next = curr.next.next
|
| 48 |
+
|
| 49 |
+
def search(self, data):
|
| 50 |
+
curr = self.head
|
| 51 |
+
while curr:
|
| 52 |
+
if curr.data == data:
|
| 53 |
+
return True
|
| 54 |
+
curr = curr.next
|
| 55 |
+
return False
|
| 56 |
+
|
| 57 |
+
def print_list(self):
|
| 58 |
+
curr = self.head
|
| 59 |
+
result = []
|
| 60 |
+
while curr:
|
| 61 |
+
result.append(curr.data)
|
| 62 |
+
curr = curr.next
|
| 63 |
+
print(result)
|
| 64 |
+
|
| 65 |
+
ll = LinkedList()
|
| 66 |
+
ll.insert_at_tail(1)
|
| 67 |
+
ll.insert_at_tail(2)
|
| 68 |
+
ll.insert_at_tail(3)
|
| 69 |
+
ll.insert_at_head(0)
|
| 70 |
+
ll.print_list() # [0, 1, 2, 3]
|
| 71 |
+
ll.delete(2)
|
| 72 |
+
ll.print_list() # [0, 1, 3]
|
| 73 |
+
|
| 74 |
+
--- Reverse a Linked List ---
|
| 75 |
+
Time: O(n). Space: O(1).
|
| 76 |
+
|
| 77 |
+
def reverse(head):
|
| 78 |
+
prev = None
|
| 79 |
+
curr = head
|
| 80 |
+
while curr:
|
| 81 |
+
next_node = curr.next
|
| 82 |
+
curr.next = prev
|
| 83 |
+
prev = curr
|
| 84 |
+
curr = next_node
|
| 85 |
+
return prev # new head
|
| 86 |
+
|
| 87 |
+
--- Detect Cycle (Floyd's Algorithm) ---
|
| 88 |
+
Use slow and fast pointers. If they meet, there is a cycle.
|
| 89 |
+
Time: O(n). Space: O(1).
|
| 90 |
+
|
| 91 |
+
def has_cycle(head):
|
| 92 |
+
slow = fast = head
|
| 93 |
+
while fast and fast.next:
|
| 94 |
+
slow = slow.next
|
| 95 |
+
fast = fast.next.next
|
| 96 |
+
if slow == fast:
|
| 97 |
+
return True
|
| 98 |
+
return False
|
| 99 |
+
|
| 100 |
+
--- Find Middle of Linked List ---
|
| 101 |
+
Time: O(n). Space: O(1).
|
| 102 |
+
|
| 103 |
+
def find_middle(head):
|
| 104 |
+
slow = fast = head
|
| 105 |
+
while fast and fast.next:
|
| 106 |
+
slow = slow.next
|
| 107 |
+
fast = fast.next.next
|
| 108 |
+
return slow # slow is at middle
|
| 109 |
+
|
| 110 |
+
--- Merge Two Sorted Linked Lists ---
|
| 111 |
+
Time: O(n + m). Space: O(1).
|
| 112 |
+
|
| 113 |
+
def merge_sorted(l1, l2):
|
| 114 |
+
dummy = Node(0)
|
| 115 |
+
curr = dummy
|
| 116 |
+
while l1 and l2:
|
| 117 |
+
if l1.data <= l2.data:
|
| 118 |
+
curr.next = l1
|
| 119 |
+
l1 = l1.next
|
| 120 |
+
else:
|
| 121 |
+
curr.next = l2
|
| 122 |
+
l2 = l2.next
|
| 123 |
+
curr = curr.next
|
| 124 |
+
curr.next = l1 or l2
|
| 125 |
+
return dummy.next
|
| 126 |
+
|
| 127 |
+
--- Remove Nth Node From End ---
|
| 128 |
+
Use two pointers separated by n steps.
|
| 129 |
+
Time: O(n). Space: O(1).
|
| 130 |
+
|
| 131 |
+
def remove_nth_from_end(head, n):
|
| 132 |
+
dummy = Node(0)
|
| 133 |
+
dummy.next = head
|
| 134 |
+
fast = slow = dummy
|
| 135 |
+
for _ in range(n + 1):
|
| 136 |
+
fast = fast.next
|
| 137 |
+
while fast:
|
| 138 |
+
slow = slow.next
|
| 139 |
+
fast = fast.next
|
| 140 |
+
slow.next = slow.next.next
|
| 141 |
+
return dummy.next
|
| 142 |
+
|
| 143 |
+
--- Check if Palindrome ---
|
| 144 |
+
Time: O(n). Space: O(1).
|
| 145 |
+
|
| 146 |
+
def is_palindrome(head):
|
| 147 |
+
# Find middle
|
| 148 |
+
slow = fast = head
|
| 149 |
+
while fast and fast.next:
|
| 150 |
+
slow = slow.next
|
| 151 |
+
fast = fast.next.next
|
| 152 |
+
# Reverse second half
|
| 153 |
+
prev = None
|
| 154 |
+
curr = slow
|
| 155 |
+
while curr:
|
| 156 |
+
nxt = curr.next
|
| 157 |
+
curr.next = prev
|
| 158 |
+
prev = curr
|
| 159 |
+
curr = nxt
|
| 160 |
+
# Compare halves
|
| 161 |
+
left, right = head, prev
|
| 162 |
+
while right:
|
| 163 |
+
if left.data != right.data:
|
| 164 |
+
return False
|
| 165 |
+
left = left.next
|
| 166 |
+
right = right.next
|
| 167 |
+
return True
|
| 168 |
+
|
| 169 |
+
--- Doubly Linked List ---
|
| 170 |
+
Each node has a pointer to both next and previous nodes.
|
| 171 |
+
|
| 172 |
+
class DNode:
|
| 173 |
+
def __init__(self, data):
|
| 174 |
+
self.data = data
|
| 175 |
+
self.next = None
|
| 176 |
+
self.prev = None
|
| 177 |
+
|
| 178 |
+
class DoublyLinkedList:
|
| 179 |
+
def __init__(self):
|
| 180 |
+
self.head = None
|
| 181 |
+
|
| 182 |
+
def insert_at_head(self, data):
|
| 183 |
+
node = DNode(data)
|
| 184 |
+
if self.head:
|
| 185 |
+
self.head.prev = node
|
| 186 |
+
node.next = self.head
|
| 187 |
+
self.head = node
|
| 188 |
+
|
| 189 |
+
def delete(self, node):
|
| 190 |
+
if node.prev:
|
| 191 |
+
node.prev.next = node.next
|
| 192 |
+
else:
|
| 193 |
+
self.head = node.next
|
| 194 |
+
if node.next:
|
| 195 |
+
node.next.prev = node.prev
|
| 196 |
+
|
| 197 |
+
--- Circular Linked List ---
|
| 198 |
+
Last node points back to head forming a circle.
|
| 199 |
+
|
| 200 |
+
def is_circular(head):
|
| 201 |
+
if not head:
|
| 202 |
+
return False
|
| 203 |
+
curr = head.next
|
| 204 |
+
while curr and curr != head:
|
| 205 |
+
curr = curr.next
|
| 206 |
+
return curr == head
|
data/docs/react_hooks.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
React Hooks are functions that let you use state and lifecycle features in functional components.
|
| 2 |
+
Introduced in React 16.8 to replace class components.
|
| 3 |
+
|
| 4 |
+
useState: manages local component state.
|
| 5 |
+
Syntax: const [state, setState] = useState(initialValue)
|
| 6 |
+
Example: const [count, setCount] = useState(0)
|
| 7 |
+
|
| 8 |
+
useEffect: handles side effects like API calls, subscriptions, DOM updates.
|
| 9 |
+
Runs after every render by default.
|
| 10 |
+
Syntax: useEffect(() => { /* effect */ }, [dependencies])
|
| 11 |
+
Empty dependency array [] means run only once on mount.
|
| 12 |
+
|
| 13 |
+
useContext: consume React context without wrapping in Consumer component.
|
| 14 |
+
Syntax: const value = useContext(MyContext)
|
| 15 |
+
|
| 16 |
+
useRef: holds mutable value that does not trigger re-render. Also used to access DOM elements.
|
| 17 |
+
|
| 18 |
+
Rules of Hooks:
|
| 19 |
+
1. Only call hooks at the top level (not inside loops or conditions).
|
| 20 |
+
2. Only call hooks from React function components or custom hooks.
|
data/docs/recursion.txt
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
RECURSION
|
| 2 |
+
=========
|
| 3 |
+
Recursion is when a function calls itself to solve a smaller version of the same problem.
|
| 4 |
+
Every recursive function needs:
|
| 5 |
+
1. Base case — condition to stop recursion.
|
| 6 |
+
2. Recursive case — function calls itself with smaller input.
|
| 7 |
+
|
| 8 |
+
--- Factorial ---
|
| 9 |
+
n! = n * (n-1) * ... * 1
|
| 10 |
+
|
| 11 |
+
def factorial(n):
|
| 12 |
+
if n == 0 or n == 1: # base case
|
| 13 |
+
return 1
|
| 14 |
+
return n * factorial(n - 1) # recursive case
|
| 15 |
+
|
| 16 |
+
print(factorial(5)) # 120
|
| 17 |
+
print(factorial(0)) # 1
|
| 18 |
+
|
| 19 |
+
# Iterative equivalent
|
| 20 |
+
def factorial_iter(n):
|
| 21 |
+
result = 1
|
| 22 |
+
for i in range(2, n + 1):
|
| 23 |
+
result *= i
|
| 24 |
+
return result
|
| 25 |
+
|
| 26 |
+
--- Fibonacci ---
|
| 27 |
+
fib(n) = fib(n-1) + fib(n-2), fib(0)=0, fib(1)=1
|
| 28 |
+
|
| 29 |
+
def fib(n):
|
| 30 |
+
if n <= 1:
|
| 31 |
+
return n
|
| 32 |
+
return fib(n-1) + fib(n-2)
|
| 33 |
+
|
| 34 |
+
print([fib(i) for i in range(8)]) # [0, 1, 1, 2, 3, 5, 8, 13]
|
| 35 |
+
|
| 36 |
+
--- Sum of Array ---
|
| 37 |
+
def array_sum(arr):
|
| 38 |
+
if not arr:
|
| 39 |
+
return 0
|
| 40 |
+
return arr[0] + array_sum(arr[1:])
|
| 41 |
+
|
| 42 |
+
print(array_sum([1, 2, 3, 4, 5])) # 15
|
| 43 |
+
|
| 44 |
+
--- Power Function ---
|
| 45 |
+
def power(base, exp):
|
| 46 |
+
if exp == 0:
|
| 47 |
+
return 1
|
| 48 |
+
if exp % 2 == 0:
|
| 49 |
+
half = power(base, exp // 2)
|
| 50 |
+
return half * half # O(log n) — fast power
|
| 51 |
+
return base * power(base, exp - 1)
|
| 52 |
+
|
| 53 |
+
print(power(2, 10)) # 1024
|
| 54 |
+
print(power(3, 5)) # 243
|
| 55 |
+
|
| 56 |
+
--- Reverse a String ---
|
| 57 |
+
def reverse_string(s):
|
| 58 |
+
if len(s) <= 1:
|
| 59 |
+
return s
|
| 60 |
+
return reverse_string(s[1:]) + s[0]
|
| 61 |
+
|
| 62 |
+
print(reverse_string("hello")) # "olleh"
|
| 63 |
+
|
| 64 |
+
--- Check Palindrome ---
|
| 65 |
+
def is_palindrome(s):
|
| 66 |
+
if len(s) <= 1:
|
| 67 |
+
return True
|
| 68 |
+
if s[0] != s[-1]:
|
| 69 |
+
return False
|
| 70 |
+
return is_palindrome(s[1:-1])
|
| 71 |
+
|
| 72 |
+
print(is_palindrome("racecar")) # True
|
| 73 |
+
print(is_palindrome("hello")) # False
|
| 74 |
+
|
| 75 |
+
--- Tower of Hanoi ---
|
| 76 |
+
Move n disks from source to destination using auxiliary peg.
|
| 77 |
+
Minimum moves = 2^n - 1.
|
| 78 |
+
|
| 79 |
+
def hanoi(n, source, destination, auxiliary):
|
| 80 |
+
if n == 1:
|
| 81 |
+
print(f"Move disk 1 from {source} to {destination}")
|
| 82 |
+
return
|
| 83 |
+
hanoi(n-1, source, auxiliary, destination)
|
| 84 |
+
print(f"Move disk {n} from {source} to {destination}")
|
| 85 |
+
hanoi(n-1, auxiliary, destination, source)
|
| 86 |
+
|
| 87 |
+
hanoi(3, 'A', 'C', 'B')
|
| 88 |
+
# Moves disk 1 from A to C
|
| 89 |
+
# Moves disk 2 from A to B
|
| 90 |
+
# Moves disk 1 from C to B
|
| 91 |
+
# ... (7 total moves)
|
| 92 |
+
|
| 93 |
+
--- Binary Search (Recursive) ---
|
| 94 |
+
def binary_search(arr, target, low, high):
|
| 95 |
+
if low > high:
|
| 96 |
+
return -1
|
| 97 |
+
mid = (low + high) // 2
|
| 98 |
+
if arr[mid] == target:
|
| 99 |
+
return mid
|
| 100 |
+
elif arr[mid] < target:
|
| 101 |
+
return binary_search(arr, target, mid + 1, high)
|
| 102 |
+
else:
|
| 103 |
+
return binary_search(arr, target, low, mid - 1)
|
| 104 |
+
|
| 105 |
+
arr = [1, 3, 5, 7, 9]
|
| 106 |
+
print(binary_search(arr, 7, 0, len(arr)-1)) # 3
|
| 107 |
+
|
| 108 |
+
--- Flatten Nested List ---
|
| 109 |
+
def flatten(lst):
|
| 110 |
+
result = []
|
| 111 |
+
for item in lst:
|
| 112 |
+
if isinstance(item, list):
|
| 113 |
+
result.extend(flatten(item))
|
| 114 |
+
else:
|
| 115 |
+
result.append(item)
|
| 116 |
+
return result
|
| 117 |
+
|
| 118 |
+
print(flatten([1, [2, [3, 4], 5], 6])) # [1, 2, 3, 4, 5, 6]
|
| 119 |
+
|
| 120 |
+
--- Generate All Subsets (Power Set) ---
|
| 121 |
+
def subsets(nums):
|
| 122 |
+
if not nums:
|
| 123 |
+
return [[]]
|
| 124 |
+
rest = subsets(nums[1:])
|
| 125 |
+
return rest + [[nums[0]] + s for s in rest]
|
| 126 |
+
|
| 127 |
+
print(subsets([1, 2, 3]))
|
| 128 |
+
# [[], [3], [2], [2,3], [1], [1,3], [1,2], [1,2,3]]
|
| 129 |
+
|
| 130 |
+
--- Generate Permutations ---
|
| 131 |
+
def permutations(nums):
|
| 132 |
+
if len(nums) <= 1:
|
| 133 |
+
return [nums]
|
| 134 |
+
result = []
|
| 135 |
+
for i, num in enumerate(nums):
|
| 136 |
+
rest = nums[:i] + nums[i+1:]
|
| 137 |
+
for perm in permutations(rest):
|
| 138 |
+
result.append([num] + perm)
|
| 139 |
+
return result
|
| 140 |
+
|
| 141 |
+
print(permutations([1, 2, 3]))
|
| 142 |
+
# [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
|
| 143 |
+
|
| 144 |
+
--- Recursion vs Iteration ---
|
| 145 |
+
Recursion: cleaner code, natural for tree/graph problems, risk of stack overflow.
|
| 146 |
+
Iteration: better performance, no stack overflow, sometimes harder to read.
|
| 147 |
+
Use recursion when the problem is naturally recursive (trees, divide-and-conquer).
|
| 148 |
+
Convert to iteration for very deep recursion using an explicit stack.
|
data/docs/rest_api.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
REST API (Representational State Transfer) is an architectural style for web services.
|
| 2 |
+
REST uses standard HTTP methods to perform operations on resources.
|
| 3 |
+
|
| 4 |
+
HTTP Methods:
|
| 5 |
+
- GET: retrieve/read resource. No request body.
|
| 6 |
+
- POST: create new resource. Data sent in request body.
|
| 7 |
+
- PUT: update existing resource completely.
|
| 8 |
+
- PATCH: partially update a resource.
|
| 9 |
+
- DELETE: remove a resource.
|
| 10 |
+
|
| 11 |
+
HTTP Status Codes:
|
| 12 |
+
- 200 OK: request succeeded.
|
| 13 |
+
- 201 Created: new resource created successfully.
|
| 14 |
+
- 400 Bad Request: invalid request from client.
|
| 15 |
+
- 401 Unauthorized: authentication required.
|
| 16 |
+
- 403 Forbidden: authenticated but not authorized.
|
| 17 |
+
- 404 Not Found: resource does not exist.
|
| 18 |
+
- 500 Internal Server Error: server-side error.
|
| 19 |
+
|
| 20 |
+
REST principles: Stateless, Client-Server, Cacheable, Uniform Interface.
|
| 21 |
+
JSON is the most common data format for REST API responses.
|
data/docs/sorting_algorithms.txt
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SORTING ALGORITHMS
|
| 2 |
+
==================
|
| 3 |
+
|
| 4 |
+
--- Bubble Sort ---
|
| 5 |
+
Repeatedly compare adjacent elements and swap if out of order. Largest element bubbles to end each pass.
|
| 6 |
+
Time: O(n^2) average/worst, O(n) best (already sorted). Space: O(1). Stable.
|
| 7 |
+
|
| 8 |
+
def bubble_sort(arr):
|
| 9 |
+
n = len(arr)
|
| 10 |
+
for i in range(n):
|
| 11 |
+
swapped = False
|
| 12 |
+
for j in range(0, n - i - 1):
|
| 13 |
+
if arr[j] > arr[j + 1]:
|
| 14 |
+
arr[j], arr[j + 1] = arr[j + 1], arr[j]
|
| 15 |
+
swapped = True
|
| 16 |
+
if not swapped:
|
| 17 |
+
break # already sorted
|
| 18 |
+
return arr
|
| 19 |
+
|
| 20 |
+
print(bubble_sort([64, 34, 25, 12, 22, 11, 90]))
|
| 21 |
+
# Output: [11, 12, 22, 25, 34, 64, 90]
|
| 22 |
+
|
| 23 |
+
--- Selection Sort ---
|
| 24 |
+
Find the minimum element and place it at the beginning. Repeat for remaining array.
|
| 25 |
+
Time: O(n^2) all cases. Space: O(1). Not stable.
|
| 26 |
+
|
| 27 |
+
def selection_sort(arr):
|
| 28 |
+
n = len(arr)
|
| 29 |
+
for i in range(n):
|
| 30 |
+
min_idx = i
|
| 31 |
+
for j in range(i + 1, n):
|
| 32 |
+
if arr[j] < arr[min_idx]:
|
| 33 |
+
min_idx = j
|
| 34 |
+
arr[i], arr[min_idx] = arr[min_idx], arr[i]
|
| 35 |
+
return arr
|
| 36 |
+
|
| 37 |
+
print(selection_sort([64, 25, 12, 22, 11]))
|
| 38 |
+
# Output: [11, 12, 22, 25, 64]
|
| 39 |
+
|
| 40 |
+
--- Insertion Sort ---
|
| 41 |
+
Build sorted array one element at a time. Take each element and insert it into the correct position.
|
| 42 |
+
Time: O(n^2) worst, O(n) best. Space: O(1). Stable. Best for small or nearly sorted data.
|
| 43 |
+
|
| 44 |
+
def insertion_sort(arr):
|
| 45 |
+
for i in range(1, len(arr)):
|
| 46 |
+
key = arr[i]
|
| 47 |
+
j = i - 1
|
| 48 |
+
while j >= 0 and arr[j] > key:
|
| 49 |
+
arr[j + 1] = arr[j]
|
| 50 |
+
j -= 1
|
| 51 |
+
arr[j + 1] = key
|
| 52 |
+
return arr
|
| 53 |
+
|
| 54 |
+
print(insertion_sort([12, 11, 13, 5, 6]))
|
| 55 |
+
# Output: [5, 6, 11, 12, 13]
|
| 56 |
+
|
| 57 |
+
--- Merge Sort ---
|
| 58 |
+
Divide array in half, recursively sort both halves, then merge the sorted halves.
|
| 59 |
+
Time: O(n log n) all cases. Space: O(n). Stable. Best for linked lists and large datasets.
|
| 60 |
+
|
| 61 |
+
def merge_sort(arr):
|
| 62 |
+
if len(arr) <= 1:
|
| 63 |
+
return arr
|
| 64 |
+
mid = len(arr) // 2
|
| 65 |
+
left = merge_sort(arr[:mid])
|
| 66 |
+
right = merge_sort(arr[mid:])
|
| 67 |
+
return merge(left, right)
|
| 68 |
+
|
| 69 |
+
def merge(left, right):
|
| 70 |
+
result = []
|
| 71 |
+
i = j = 0
|
| 72 |
+
while i < len(left) and j < len(right):
|
| 73 |
+
if left[i] <= right[j]:
|
| 74 |
+
result.append(left[i]); i += 1
|
| 75 |
+
else:
|
| 76 |
+
result.append(right[j]); j += 1
|
| 77 |
+
result.extend(left[i:])
|
| 78 |
+
result.extend(right[j:])
|
| 79 |
+
return result
|
| 80 |
+
|
| 81 |
+
print(merge_sort([38, 27, 43, 3, 9, 82, 10]))
|
| 82 |
+
# Output: [3, 9, 10, 27, 38, 43, 82]
|
| 83 |
+
|
| 84 |
+
--- Quick Sort ---
|
| 85 |
+
Pick a pivot, partition array so all elements less than pivot are on left, greater on right. Recurse.
|
| 86 |
+
Time: O(n log n) average, O(n^2) worst. Space: O(log n). Not stable. Fastest in practice.
|
| 87 |
+
|
| 88 |
+
def quick_sort(arr, low=0, high=None):
|
| 89 |
+
if high is None:
|
| 90 |
+
high = len(arr) - 1
|
| 91 |
+
if low < high:
|
| 92 |
+
pi = partition(arr, low, high)
|
| 93 |
+
quick_sort(arr, low, pi - 1)
|
| 94 |
+
quick_sort(arr, pi + 1, high)
|
| 95 |
+
return arr
|
| 96 |
+
|
| 97 |
+
def partition(arr, low, high):
|
| 98 |
+
pivot = arr[high]
|
| 99 |
+
i = low - 1
|
| 100 |
+
for j in range(low, high):
|
| 101 |
+
if arr[j] <= pivot:
|
| 102 |
+
i += 1
|
| 103 |
+
arr[i], arr[j] = arr[j], arr[i]
|
| 104 |
+
arr[i + 1], arr[high] = arr[high], arr[i + 1]
|
| 105 |
+
return i + 1
|
| 106 |
+
|
| 107 |
+
print(quick_sort([10, 7, 8, 9, 1, 5]))
|
| 108 |
+
# Output: [1, 5, 7, 8, 9, 10]
|
| 109 |
+
|
| 110 |
+
--- Heap Sort ---
|
| 111 |
+
Build a max-heap from the array. Repeatedly extract the maximum and place at end.
|
| 112 |
+
Time: O(n log n) all cases. Space: O(1). Not stable.
|
| 113 |
+
|
| 114 |
+
def heap_sort(arr):
|
| 115 |
+
n = len(arr)
|
| 116 |
+
for i in range(n // 2 - 1, -1, -1):
|
| 117 |
+
heapify(arr, n, i)
|
| 118 |
+
for i in range(n - 1, 0, -1):
|
| 119 |
+
arr[0], arr[i] = arr[i], arr[0]
|
| 120 |
+
heapify(arr, i, 0)
|
| 121 |
+
return arr
|
| 122 |
+
|
| 123 |
+
def heapify(arr, n, i):
|
| 124 |
+
largest = i
|
| 125 |
+
left = 2 * i + 1
|
| 126 |
+
right = 2 * i + 2
|
| 127 |
+
if left < n and arr[left] > arr[largest]:
|
| 128 |
+
largest = left
|
| 129 |
+
if right < n and arr[right] > arr[largest]:
|
| 130 |
+
largest = right
|
| 131 |
+
if largest != i:
|
| 132 |
+
arr[i], arr[largest] = arr[largest], arr[i]
|
| 133 |
+
heapify(arr, n, largest)
|
| 134 |
+
|
| 135 |
+
print(heap_sort([12, 11, 13, 5, 6, 7]))
|
| 136 |
+
# Output: [5, 6, 7, 11, 12, 13]
|
| 137 |
+
|
| 138 |
+
--- Counting Sort ---
|
| 139 |
+
Count occurrences of each element. Works only on non-negative integers within a known range.
|
| 140 |
+
Time: O(n + k) where k is the range. Space: O(k). Stable.
|
| 141 |
+
|
| 142 |
+
def counting_sort(arr):
|
| 143 |
+
if not arr:
|
| 144 |
+
return arr
|
| 145 |
+
max_val = max(arr)
|
| 146 |
+
count = [0] * (max_val + 1)
|
| 147 |
+
for num in arr:
|
| 148 |
+
count[num] += 1
|
| 149 |
+
result = []
|
| 150 |
+
for i, c in enumerate(count):
|
| 151 |
+
result.extend([i] * c)
|
| 152 |
+
return result
|
| 153 |
+
|
| 154 |
+
print(counting_sort([4, 2, 2, 8, 3, 3, 1]))
|
| 155 |
+
# Output: [1, 2, 2, 3, 3, 4, 8]
|
| 156 |
+
|
| 157 |
+
--- Comparison Summary ---
|
| 158 |
+
Algorithm | Best | Average | Worst | Space | Stable
|
| 159 |
+
Bubble Sort | O(n) | O(n^2) | O(n^2) | O(1) | Yes
|
| 160 |
+
Selection Sort| O(n^2) | O(n^2) | O(n^2) | O(1) | No
|
| 161 |
+
Insertion Sort| O(n) | O(n^2) | O(n^2) | O(1) | Yes
|
| 162 |
+
Merge Sort | O(n log n)| O(n log n)| O(n log n)| O(n) | Yes
|
| 163 |
+
Quick Sort | O(n log n)| O(n log n)| O(n^2) | O(log n)| No
|
| 164 |
+
Heap Sort | O(n log n)| O(n log n)| O(n log n)| O(1) | No
|
| 165 |
+
Counting Sort | O(n+k) | O(n+k) | O(n+k) | O(k) | Yes
|
data/docs/stack_queue.txt
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
STACK AND QUEUE
|
| 2 |
+
===============
|
| 3 |
+
|
| 4 |
+
--- STACK ---
|
| 5 |
+
LIFO — Last In First Out. Think of a stack of plates.
|
| 6 |
+
Operations: push (add), pop (remove top), peek (view top). All O(1).
|
| 7 |
+
|
| 8 |
+
Uses: undo/redo, function call stack, expression evaluation, DFS, balanced brackets.
|
| 9 |
+
|
| 10 |
+
# Stack using Python list
|
| 11 |
+
class Stack:
|
| 12 |
+
def __init__(self):
|
| 13 |
+
self.items = []
|
| 14 |
+
|
| 15 |
+
def push(self, item):
|
| 16 |
+
self.items.append(item)
|
| 17 |
+
|
| 18 |
+
def pop(self):
|
| 19 |
+
if self.is_empty():
|
| 20 |
+
raise IndexError("Stack is empty")
|
| 21 |
+
return self.items.pop()
|
| 22 |
+
|
| 23 |
+
def peek(self):
|
| 24 |
+
if self.is_empty():
|
| 25 |
+
raise IndexError("Stack is empty")
|
| 26 |
+
return self.items[-1]
|
| 27 |
+
|
| 28 |
+
def is_empty(self):
|
| 29 |
+
return len(self.items) == 0
|
| 30 |
+
|
| 31 |
+
def size(self):
|
| 32 |
+
return len(self.items)
|
| 33 |
+
|
| 34 |
+
s = Stack()
|
| 35 |
+
s.push(10)
|
| 36 |
+
s.push(20)
|
| 37 |
+
s.push(30)
|
| 38 |
+
print(s.peek()) # 30
|
| 39 |
+
print(s.pop()) # 30
|
| 40 |
+
print(s.size()) # 2
|
| 41 |
+
|
| 42 |
+
--- Balanced Brackets using Stack ---
|
| 43 |
+
Check if brackets (, ), {, }, [, ] are balanced.
|
| 44 |
+
|
| 45 |
+
def is_balanced(expr):
|
| 46 |
+
stack = []
|
| 47 |
+
pairs = {')': '(', '}': '{', ']': '['}
|
| 48 |
+
for ch in expr:
|
| 49 |
+
if ch in '({[':
|
| 50 |
+
stack.append(ch)
|
| 51 |
+
elif ch in ')}]':
|
| 52 |
+
if not stack or stack[-1] != pairs[ch]:
|
| 53 |
+
return False
|
| 54 |
+
stack.pop()
|
| 55 |
+
return len(stack) == 0
|
| 56 |
+
|
| 57 |
+
print(is_balanced("({[]})")) # True
|
| 58 |
+
print(is_balanced("({[})")) # False
|
| 59 |
+
print(is_balanced("((()))")) # True
|
| 60 |
+
|
| 61 |
+
--- Next Greater Element using Stack ---
|
| 62 |
+
For each element, find the next element greater than it.
|
| 63 |
+
Time: O(n). Space: O(n).
|
| 64 |
+
|
| 65 |
+
def next_greater(arr):
|
| 66 |
+
n = len(arr)
|
| 67 |
+
result = [-1] * n
|
| 68 |
+
stack = [] # stores indices
|
| 69 |
+
for i in range(n):
|
| 70 |
+
while stack and arr[stack[-1]] < arr[i]:
|
| 71 |
+
result[stack.pop()] = arr[i]
|
| 72 |
+
stack.append(i)
|
| 73 |
+
return result
|
| 74 |
+
|
| 75 |
+
print(next_greater([4, 5, 2, 25])) # [5, 25, 25, -1]
|
| 76 |
+
|
| 77 |
+
--- Evaluate Postfix Expression ---
|
| 78 |
+
Postfix: operands come before operator (e.g. "2 3 +" = 5).
|
| 79 |
+
|
| 80 |
+
def evaluate_postfix(expression):
|
| 81 |
+
stack = []
|
| 82 |
+
for token in expression.split():
|
| 83 |
+
if token.lstrip('-').isdigit():
|
| 84 |
+
stack.append(int(token))
|
| 85 |
+
else:
|
| 86 |
+
b = stack.pop()
|
| 87 |
+
a = stack.pop()
|
| 88 |
+
if token == '+': stack.append(a + b)
|
| 89 |
+
elif token == '-': stack.append(a - b)
|
| 90 |
+
elif token == '*': stack.append(a * b)
|
| 91 |
+
elif token == '/': stack.append(int(a / b))
|
| 92 |
+
return stack[0]
|
| 93 |
+
|
| 94 |
+
print(evaluate_postfix("2 3 1 * + 9 -")) # Output: -4
|
| 95 |
+
|
| 96 |
+
--- Min Stack ---
|
| 97 |
+
Stack that supports getMin() in O(1).
|
| 98 |
+
|
| 99 |
+
class MinStack:
|
| 100 |
+
def __init__(self):
|
| 101 |
+
self.stack = []
|
| 102 |
+
self.min_stack = []
|
| 103 |
+
|
| 104 |
+
def push(self, val):
|
| 105 |
+
self.stack.append(val)
|
| 106 |
+
if not self.min_stack or val <= self.min_stack[-1]:
|
| 107 |
+
self.min_stack.append(val)
|
| 108 |
+
|
| 109 |
+
def pop(self):
|
| 110 |
+
val = self.stack.pop()
|
| 111 |
+
if val == self.min_stack[-1]:
|
| 112 |
+
self.min_stack.pop()
|
| 113 |
+
|
| 114 |
+
def get_min(self):
|
| 115 |
+
return self.min_stack[-1]
|
| 116 |
+
|
| 117 |
+
ms = MinStack()
|
| 118 |
+
ms.push(5); ms.push(3); ms.push(7); ms.push(2)
|
| 119 |
+
print(ms.get_min()) # 2
|
| 120 |
+
ms.pop()
|
| 121 |
+
print(ms.get_min()) # 3
|
| 122 |
+
|
| 123 |
+
--- QUEUE ---
|
| 124 |
+
FIFO — First In First Out. Think of a line at a ticket counter.
|
| 125 |
+
Operations: enqueue (add rear), dequeue (remove front), peek. All O(1).
|
| 126 |
+
|
| 127 |
+
Uses: BFS, scheduling, print queues, sliding window.
|
| 128 |
+
|
| 129 |
+
from collections import deque
|
| 130 |
+
|
| 131 |
+
class Queue:
|
| 132 |
+
def __init__(self):
|
| 133 |
+
self.items = deque()
|
| 134 |
+
|
| 135 |
+
def enqueue(self, item):
|
| 136 |
+
self.items.append(item)
|
| 137 |
+
|
| 138 |
+
def dequeue(self):
|
| 139 |
+
if self.is_empty():
|
| 140 |
+
raise IndexError("Queue is empty")
|
| 141 |
+
return self.items.popleft()
|
| 142 |
+
|
| 143 |
+
def peek(self):
|
| 144 |
+
return self.items[0]
|
| 145 |
+
|
| 146 |
+
def is_empty(self):
|
| 147 |
+
return len(self.items) == 0
|
| 148 |
+
|
| 149 |
+
def size(self):
|
| 150 |
+
return len(self.items)
|
| 151 |
+
|
| 152 |
+
q = Queue()
|
| 153 |
+
q.enqueue(10)
|
| 154 |
+
q.enqueue(20)
|
| 155 |
+
q.enqueue(30)
|
| 156 |
+
print(q.dequeue()) # 10
|
| 157 |
+
print(q.peek()) # 20
|
| 158 |
+
|
| 159 |
+
--- Circular Queue ---
|
| 160 |
+
Fixed-size queue where rear wraps around to front.
|
| 161 |
+
|
| 162 |
+
class CircularQueue:
|
| 163 |
+
def __init__(self, capacity):
|
| 164 |
+
self.queue = [None] * capacity
|
| 165 |
+
self.front = self.rear = -1
|
| 166 |
+
self.capacity = capacity
|
| 167 |
+
self.size = 0
|
| 168 |
+
|
| 169 |
+
def enqueue(self, item):
|
| 170 |
+
if self.size == self.capacity:
|
| 171 |
+
raise OverflowError("Queue is full")
|
| 172 |
+
self.rear = (self.rear + 1) % self.capacity
|
| 173 |
+
self.queue[self.rear] = item
|
| 174 |
+
if self.front == -1:
|
| 175 |
+
self.front = 0
|
| 176 |
+
self.size += 1
|
| 177 |
+
|
| 178 |
+
def dequeue(self):
|
| 179 |
+
if self.size == 0:
|
| 180 |
+
raise IndexError("Queue is empty")
|
| 181 |
+
item = self.queue[self.front]
|
| 182 |
+
self.front = (self.front + 1) % self.capacity
|
| 183 |
+
self.size -= 1
|
| 184 |
+
return item
|
| 185 |
+
|
| 186 |
+
--- Deque (Double-Ended Queue) ---
|
| 187 |
+
Insert and delete from both ends in O(1).
|
| 188 |
+
|
| 189 |
+
from collections import deque
|
| 190 |
+
d = deque()
|
| 191 |
+
d.appendleft(1) # add to front
|
| 192 |
+
d.append(2) # add to rear
|
| 193 |
+
d.appendleft(0)
|
| 194 |
+
print(d) # deque([0, 1, 2])
|
| 195 |
+
d.popleft() # remove from front → 0
|
| 196 |
+
d.pop() # remove from rear → 2
|
| 197 |
+
print(d) # deque([1])
|
| 198 |
+
|
| 199 |
+
--- Priority Queue ---
|
| 200 |
+
Elements are dequeued based on priority, not insertion order.
|
| 201 |
+
Python's heapq is a min-heap by default.
|
| 202 |
+
|
| 203 |
+
import heapq
|
| 204 |
+
|
| 205 |
+
pq = []
|
| 206 |
+
heapq.heappush(pq, (3, 'low'))
|
| 207 |
+
heapq.heappush(pq, (1, 'high'))
|
| 208 |
+
heapq.heappush(pq, (2, 'medium'))
|
| 209 |
+
print(heapq.heappop(pq)) # (1, 'high')
|
| 210 |
+
print(heapq.heappop(pq)) # (2, 'medium')
|
| 211 |
+
|
| 212 |
+
# Max-heap: negate priorities
|
| 213 |
+
heapq.heappush(pq, (-5, 'top priority'))
|
| 214 |
+
print(heapq.heappop(pq)) # (-5, 'top priority')
|
| 215 |
+
|
| 216 |
+
--- Difference: Stack vs Queue ---
|
| 217 |
+
Stack: LIFO — last added is first removed. Uses: DFS, backtracking, undo.
|
| 218 |
+
Queue: FIFO — first added is first removed. Uses: BFS, scheduling, buffering.
|
data/docs/string_algorithms.txt
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
STRING ALGORITHMS
|
| 2 |
+
=================
|
| 3 |
+
Strings are sequences of characters. Most string operations in Python run in O(n).
|
| 4 |
+
|
| 5 |
+
--- Basic String Operations ---
|
| 6 |
+
s = "hello world"
|
| 7 |
+
print(len(s)) # 11
|
| 8 |
+
print(s.upper()) # HELLO WORLD
|
| 9 |
+
print(s.lower()) # hello world
|
| 10 |
+
print(s.split()) # ['hello', 'world']
|
| 11 |
+
print(s.replace("world", "Python")) # hello Python
|
| 12 |
+
print(s.startswith("hello")) # True
|
| 13 |
+
print(s.endswith("world")) # True
|
| 14 |
+
print(s.find("world")) # 6 (index)
|
| 15 |
+
print(s.count("l")) # 3
|
| 16 |
+
print(s[::-1]) # dlrow olleh (reverse)
|
| 17 |
+
print("".join(reversed(s))) # same as above
|
| 18 |
+
|
| 19 |
+
--- Reverse Words in a Sentence ---
|
| 20 |
+
def reverse_words(sentence):
|
| 21 |
+
return ' '.join(sentence.split()[::-1])
|
| 22 |
+
|
| 23 |
+
print(reverse_words("Hello World Python")) # Python World Hello
|
| 24 |
+
|
| 25 |
+
--- Check Palindrome ---
|
| 26 |
+
def is_palindrome(s):
|
| 27 |
+
s = ''.join(c.lower() for c in s if c.isalnum())
|
| 28 |
+
return s == s[::-1]
|
| 29 |
+
|
| 30 |
+
print(is_palindrome("A man a plan a canal Panama")) # True
|
| 31 |
+
print(is_palindrome("race a car")) # False
|
| 32 |
+
|
| 33 |
+
--- Anagram Check ---
|
| 34 |
+
Two strings are anagrams if they have same characters with same frequency.
|
| 35 |
+
|
| 36 |
+
from collections import Counter
|
| 37 |
+
|
| 38 |
+
def is_anagram(s, t):
|
| 39 |
+
return Counter(s) == Counter(t)
|
| 40 |
+
|
| 41 |
+
print(is_anagram("listen", "silent")) # True
|
| 42 |
+
print(is_anagram("hello", "world")) # False
|
| 43 |
+
|
| 44 |
+
--- Count Character Frequency ---
|
| 45 |
+
def char_frequency(s):
|
| 46 |
+
freq = {}
|
| 47 |
+
for ch in s:
|
| 48 |
+
freq[ch] = freq.get(ch, 0) + 1
|
| 49 |
+
return freq
|
| 50 |
+
|
| 51 |
+
print(char_frequency("abracadabra"))
|
| 52 |
+
# {'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
|
| 53 |
+
|
| 54 |
+
--- KMP String Matching ---
|
| 55 |
+
Find all occurrences of pattern in text.
|
| 56 |
+
Time: O(n + m). Space: O(m). Efficient, no unnecessary comparisons.
|
| 57 |
+
|
| 58 |
+
def kmp_search(text, pattern):
|
| 59 |
+
n, m = len(text), len(pattern)
|
| 60 |
+
if m == 0:
|
| 61 |
+
return [0]
|
| 62 |
+
|
| 63 |
+
# Build failure function
|
| 64 |
+
fail = [0] * m
|
| 65 |
+
j = 0
|
| 66 |
+
for i in range(1, m):
|
| 67 |
+
while j > 0 and pattern[i] != pattern[j]:
|
| 68 |
+
j = fail[j - 1]
|
| 69 |
+
if pattern[i] == pattern[j]:
|
| 70 |
+
j += 1
|
| 71 |
+
fail[i] = j
|
| 72 |
+
|
| 73 |
+
# Search
|
| 74 |
+
matches = []
|
| 75 |
+
j = 0
|
| 76 |
+
for i in range(n):
|
| 77 |
+
while j > 0 and text[i] != pattern[j]:
|
| 78 |
+
j = fail[j - 1]
|
| 79 |
+
if text[i] == pattern[j]:
|
| 80 |
+
j += 1
|
| 81 |
+
if j == m:
|
| 82 |
+
matches.append(i - m + 1)
|
| 83 |
+
j = fail[j - 1]
|
| 84 |
+
return matches
|
| 85 |
+
|
| 86 |
+
print(kmp_search("aabaacaadaabaaba", "aaba")) # [0, 9, 12]
|
| 87 |
+
|
| 88 |
+
--- Rabin-Karp Rolling Hash ---
|
| 89 |
+
Find pattern in text using hashing. Avg O(n+m), worst O(nm).
|
| 90 |
+
|
| 91 |
+
def rabin_karp(text, pattern):
|
| 92 |
+
n, m = len(text), len(pattern)
|
| 93 |
+
BASE, MOD = 256, 101
|
| 94 |
+
pat_hash = text_hash = 0
|
| 95 |
+
h = pow(BASE, m - 1, MOD)
|
| 96 |
+
matches = []
|
| 97 |
+
|
| 98 |
+
for i in range(m):
|
| 99 |
+
pat_hash = (BASE * pat_hash + ord(pattern[i])) % MOD
|
| 100 |
+
text_hash = (BASE * text_hash + ord(text[i])) % MOD
|
| 101 |
+
|
| 102 |
+
for i in range(n - m + 1):
|
| 103 |
+
if pat_hash == text_hash:
|
| 104 |
+
if text[i:i+m] == pattern: # verify
|
| 105 |
+
matches.append(i)
|
| 106 |
+
if i < n - m:
|
| 107 |
+
text_hash = (BASE*(text_hash - ord(text[i])*h) + ord(text[i+m])) % MOD
|
| 108 |
+
if text_hash < 0:
|
| 109 |
+
text_hash += MOD
|
| 110 |
+
return matches
|
| 111 |
+
|
| 112 |
+
print(rabin_karp("GEEKS FOR GEEKS", "GEEKS")) # [0, 10]
|
| 113 |
+
|
| 114 |
+
--- Longest Common Prefix ---
|
| 115 |
+
def longest_common_prefix(strs):
|
| 116 |
+
if not strs:
|
| 117 |
+
return ""
|
| 118 |
+
prefix = strs[0]
|
| 119 |
+
for s in strs[1:]:
|
| 120 |
+
while not s.startswith(prefix):
|
| 121 |
+
prefix = prefix[:-1]
|
| 122 |
+
if not prefix:
|
| 123 |
+
return ""
|
| 124 |
+
return prefix
|
| 125 |
+
|
| 126 |
+
print(longest_common_prefix(["flower","flow","flight"])) # "fl"
|
| 127 |
+
print(longest_common_prefix(["dog","racecar","car"])) # ""
|
| 128 |
+
|
| 129 |
+
--- Valid Parentheses ---
|
| 130 |
+
def is_valid_parentheses(s):
|
| 131 |
+
stack = []
|
| 132 |
+
pairs = {')':'(', '}':'{', ']':'['}
|
| 133 |
+
for ch in s:
|
| 134 |
+
if ch in '({[':
|
| 135 |
+
stack.append(ch)
|
| 136 |
+
elif ch in ')}]':
|
| 137 |
+
if not stack or stack[-1] != pairs[ch]:
|
| 138 |
+
return False
|
| 139 |
+
stack.pop()
|
| 140 |
+
return not stack
|
| 141 |
+
|
| 142 |
+
print(is_valid_parentheses("()[]{}")) # True
|
| 143 |
+
print(is_valid_parentheses("([)]")) # False
|
| 144 |
+
|
| 145 |
+
--- Roman to Integer ---
|
| 146 |
+
def roman_to_int(s):
|
| 147 |
+
values = {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
|
| 148 |
+
result = 0
|
| 149 |
+
for i in range(len(s)):
|
| 150 |
+
if i + 1 < len(s) and values[s[i]] < values[s[i+1]]:
|
| 151 |
+
result -= values[s[i]]
|
| 152 |
+
else:
|
| 153 |
+
result += values[s[i]]
|
| 154 |
+
return result
|
| 155 |
+
|
| 156 |
+
print(roman_to_int("III")) # 3
|
| 157 |
+
print(roman_to_int("LVIII")) # 58
|
| 158 |
+
print(roman_to_int("MCMXCIV"))# 1994
|
| 159 |
+
|
| 160 |
+
--- String Compression ---
|
| 161 |
+
Compress "aabcccdddd" to "a2b1c3d4".
|
| 162 |
+
|
| 163 |
+
def compress(s):
|
| 164 |
+
result = []
|
| 165 |
+
i = 0
|
| 166 |
+
while i < len(s):
|
| 167 |
+
ch = s[i]
|
| 168 |
+
count = 0
|
| 169 |
+
while i < len(s) and s[i] == ch:
|
| 170 |
+
count += 1
|
| 171 |
+
i += 1
|
| 172 |
+
result.append(ch + str(count))
|
| 173 |
+
compressed = ''.join(result)
|
| 174 |
+
return compressed if len(compressed) < len(s) else s
|
| 175 |
+
|
| 176 |
+
print(compress("aabcccdddd")) # a2b1c3d4
|
| 177 |
+
print(compress("abcd")) # abcd (no gain)
|
| 178 |
+
|
| 179 |
+
--- Zigzag String Conversion ---
|
| 180 |
+
Convert string to zigzag pattern across rows.
|
| 181 |
+
|
| 182 |
+
def convert_zigzag(s, numRows):
|
| 183 |
+
if numRows == 1 or numRows >= len(s):
|
| 184 |
+
return s
|
| 185 |
+
rows = [''] * numRows
|
| 186 |
+
row, step = 0, 1
|
| 187 |
+
for ch in s:
|
| 188 |
+
rows[row] += ch
|
| 189 |
+
if row == 0: step = 1
|
| 190 |
+
if row == numRows - 1: step = -1
|
| 191 |
+
row += step
|
| 192 |
+
return ''.join(rows)
|
| 193 |
+
|
| 194 |
+
print(convert_zigzag("PAYPALISHIRING", 3)) # PAHNAPLSIIGYIR
|
data/docs/trees.txt
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TREES AND BINARY TREES
|
| 2 |
+
======================
|
| 3 |
+
A tree is a hierarchical data structure. Root is the topmost node.
|
| 4 |
+
Each node has at most one parent and zero or more children.
|
| 5 |
+
Leaf nodes have no children.
|
| 6 |
+
|
| 7 |
+
--- Binary Tree Node ---
|
| 8 |
+
class TreeNode:
|
| 9 |
+
def __init__(self, val=0, left=None, right=None):
|
| 10 |
+
self.val = val
|
| 11 |
+
self.left = left
|
| 12 |
+
self.right = right
|
| 13 |
+
|
| 14 |
+
# Build a sample tree:
|
| 15 |
+
# 1
|
| 16 |
+
# / \
|
| 17 |
+
# 2 3
|
| 18 |
+
# / \ \
|
| 19 |
+
# 4 5 6
|
| 20 |
+
root = TreeNode(1)
|
| 21 |
+
root.left = TreeNode(2)
|
| 22 |
+
root.right = TreeNode(3)
|
| 23 |
+
root.left.left = TreeNode(4)
|
| 24 |
+
root.left.right = TreeNode(5)
|
| 25 |
+
root.right.right = TreeNode(6)
|
| 26 |
+
|
| 27 |
+
--- Tree Traversals ---
|
| 28 |
+
# Inorder (Left, Root, Right) — gives sorted output for BST
|
| 29 |
+
def inorder(root):
|
| 30 |
+
if not root:
|
| 31 |
+
return []
|
| 32 |
+
return inorder(root.left) + [root.val] + inorder(root.right)
|
| 33 |
+
|
| 34 |
+
# Preorder (Root, Left, Right) — used to copy the tree
|
| 35 |
+
def preorder(root):
|
| 36 |
+
if not root:
|
| 37 |
+
return []
|
| 38 |
+
return [root.val] + preorder(root.left) + preorder(root.right)
|
| 39 |
+
|
| 40 |
+
# Postorder (Left, Right, Root) — used to delete the tree
|
| 41 |
+
def postorder(root):
|
| 42 |
+
if not root:
|
| 43 |
+
return []
|
| 44 |
+
return postorder(root.left) + postorder(root.right) + [root.val]
|
| 45 |
+
|
| 46 |
+
# Level Order (BFS)
|
| 47 |
+
from collections import deque
|
| 48 |
+
def level_order(root):
|
| 49 |
+
if not root:
|
| 50 |
+
return []
|
| 51 |
+
result, queue = [], deque([root])
|
| 52 |
+
while queue:
|
| 53 |
+
node = queue.popleft()
|
| 54 |
+
result.append(node.val)
|
| 55 |
+
if node.left: queue.append(node.left)
|
| 56 |
+
if node.right: queue.append(node.right)
|
| 57 |
+
return result
|
| 58 |
+
|
| 59 |
+
print(inorder(root)) # [4, 2, 5, 1, 3, 6]
|
| 60 |
+
print(preorder(root)) # [1, 2, 4, 5, 3, 6]
|
| 61 |
+
print(postorder(root)) # [4, 5, 2, 6, 3, 1]
|
| 62 |
+
print(level_order(root)) # [1, 2, 3, 4, 5, 6]
|
| 63 |
+
|
| 64 |
+
--- Tree Height ---
|
| 65 |
+
def height(root):
|
| 66 |
+
if not root:
|
| 67 |
+
return 0
|
| 68 |
+
return 1 + max(height(root.left), height(root.right))
|
| 69 |
+
|
| 70 |
+
print(height(root)) # Output: 3
|
| 71 |
+
|
| 72 |
+
--- Count Nodes ---
|
| 73 |
+
def count_nodes(root):
|
| 74 |
+
if not root:
|
| 75 |
+
return 0
|
| 76 |
+
return 1 + count_nodes(root.left) + count_nodes(root.right)
|
| 77 |
+
|
| 78 |
+
--- Binary Search Tree (BST) ---
|
| 79 |
+
Left child < Parent < Right child.
|
| 80 |
+
Insert, Search, Delete: O(log n) average, O(n) worst (skewed).
|
| 81 |
+
|
| 82 |
+
# Insert into BST
|
| 83 |
+
def bst_insert(root, val):
|
| 84 |
+
if not root:
|
| 85 |
+
return TreeNode(val)
|
| 86 |
+
if val < root.val:
|
| 87 |
+
root.left = bst_insert(root.left, val)
|
| 88 |
+
else:
|
| 89 |
+
root.right = bst_insert(root.right, val)
|
| 90 |
+
return root
|
| 91 |
+
|
| 92 |
+
# Search in BST
|
| 93 |
+
def bst_search(root, val):
|
| 94 |
+
if not root or root.val == val:
|
| 95 |
+
return root
|
| 96 |
+
if val < root.val:
|
| 97 |
+
return bst_search(root.left, val)
|
| 98 |
+
return bst_search(root.right, val)
|
| 99 |
+
|
| 100 |
+
# Delete from BST
|
| 101 |
+
def bst_delete(root, val):
|
| 102 |
+
if not root:
|
| 103 |
+
return None
|
| 104 |
+
if val < root.val:
|
| 105 |
+
root.left = bst_delete(root.left, val)
|
| 106 |
+
elif val > root.val:
|
| 107 |
+
root.right = bst_delete(root.right, val)
|
| 108 |
+
else:
|
| 109 |
+
if not root.left:
|
| 110 |
+
return root.right
|
| 111 |
+
if not root.right:
|
| 112 |
+
return root.left
|
| 113 |
+
# Find inorder successor (smallest in right subtree)
|
| 114 |
+
curr = root.right
|
| 115 |
+
while curr.left:
|
| 116 |
+
curr = curr.left
|
| 117 |
+
root.val = curr.val
|
| 118 |
+
root.right = bst_delete(root.right, curr.val)
|
| 119 |
+
return root
|
| 120 |
+
|
| 121 |
+
bst = None
|
| 122 |
+
for v in [5, 3, 7, 1, 4, 6, 8]:
|
| 123 |
+
bst = bst_insert(bst, v)
|
| 124 |
+
print(inorder(bst)) # [1, 3, 4, 5, 6, 7, 8]
|
| 125 |
+
|
| 126 |
+
--- Check if Balanced ---
|
| 127 |
+
A tree is balanced if height difference of left and right subtrees is at most 1 for every node.
|
| 128 |
+
|
| 129 |
+
def is_balanced(root):
|
| 130 |
+
def check(node):
|
| 131 |
+
if not node:
|
| 132 |
+
return 0
|
| 133 |
+
left = check(node.left)
|
| 134 |
+
right = check(node.right)
|
| 135 |
+
if left == -1 or right == -1 or abs(left - right) > 1:
|
| 136 |
+
return -1
|
| 137 |
+
return 1 + max(left, right)
|
| 138 |
+
return check(root) != -1
|
| 139 |
+
|
| 140 |
+
--- Lowest Common Ancestor (LCA) ---
|
| 141 |
+
Find the lowest node that has both p and q as descendants.
|
| 142 |
+
|
| 143 |
+
def lca(root, p, q):
|
| 144 |
+
if not root or root == p or root == q:
|
| 145 |
+
return root
|
| 146 |
+
left = lca(root.left, p, q)
|
| 147 |
+
right = lca(root.right, p, q)
|
| 148 |
+
if left and right:
|
| 149 |
+
return root # p and q on different sides
|
| 150 |
+
return left or right
|
| 151 |
+
|
| 152 |
+
--- Level Order by Levels ---
|
| 153 |
+
Return nodes grouped by level.
|
| 154 |
+
|
| 155 |
+
def level_order_levels(root):
|
| 156 |
+
if not root:
|
| 157 |
+
return []
|
| 158 |
+
result, queue = [], deque([root])
|
| 159 |
+
while queue:
|
| 160 |
+
level = []
|
| 161 |
+
for _ in range(len(queue)):
|
| 162 |
+
node = queue.popleft()
|
| 163 |
+
level.append(node.val)
|
| 164 |
+
if node.left: queue.append(node.left)
|
| 165 |
+
if node.right: queue.append(node.right)
|
| 166 |
+
result.append(level)
|
| 167 |
+
return result
|
| 168 |
+
|
| 169 |
+
print(level_order_levels(root))
|
| 170 |
+
# Output: [[1], [2, 3], [4, 5, 6]]
|
| 171 |
+
|
| 172 |
+
--- Diameter of Binary Tree ---
|
| 173 |
+
Longest path between any two nodes (path may not pass through root).
|
| 174 |
+
|
| 175 |
+
def diameter(root):
|
| 176 |
+
max_d = [0]
|
| 177 |
+
def depth(node):
|
| 178 |
+
if not node:
|
| 179 |
+
return 0
|
| 180 |
+
left = depth(node.left)
|
| 181 |
+
right = depth(node.right)
|
| 182 |
+
max_d[0] = max(max_d[0], left + right)
|
| 183 |
+
return 1 + max(left, right)
|
| 184 |
+
depth(root)
|
| 185 |
+
return max_d[0]
|
| 186 |
+
|
| 187 |
+
--- Invert Binary Tree ---
|
| 188 |
+
Mirror the tree — swap left and right children of every node.
|
| 189 |
+
|
| 190 |
+
def invert_tree(root):
|
| 191 |
+
if not root:
|
| 192 |
+
return None
|
| 193 |
+
root.left, root.right = invert_tree(root.right), invert_tree(root.left)
|
| 194 |
+
return root
|
| 195 |
+
|
| 196 |
+
--- AVL Tree (Self-Balancing BST) ---
|
| 197 |
+
Keeps height O(log n) by rotating after every insert/delete.
|
| 198 |
+
Balance factor = height(left) - height(right). Must be -1, 0, or 1.
|
| 199 |
+
|
| 200 |
+
def get_height(node):
|
| 201 |
+
return node.height if node else 0
|
| 202 |
+
|
| 203 |
+
def get_balance(node):
|
| 204 |
+
return get_height(node.left) - get_height(node.right) if node else 0
|
| 205 |
+
|
| 206 |
+
# Right rotation
|
| 207 |
+
def rotate_right(y):
|
| 208 |
+
x = y.left
|
| 209 |
+
T2 = x.right
|
| 210 |
+
x.right = y
|
| 211 |
+
y.left = T2
|
| 212 |
+
y.height = 1 + max(get_height(y.left), get_height(y.right))
|
| 213 |
+
x.height = 1 + max(get_height(x.left), get_height(x.right))
|
| 214 |
+
return x
|
| 215 |
+
|
| 216 |
+
# Left rotation
|
| 217 |
+
def rotate_left(x):
|
| 218 |
+
y = x.right
|
| 219 |
+
T2 = y.left
|
| 220 |
+
y.left = x
|
| 221 |
+
x.right = T2
|
| 222 |
+
x.height = 1 + max(get_height(x.left), get_height(x.right))
|
| 223 |
+
y.height = 1 + max(get_height(y.left), get_height(y.right))
|
| 224 |
+
return y
|
data/docs/two_pointers.txt
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TWO POINTERS AND SLIDING WINDOW
|
| 2 |
+
================================
|
| 3 |
+
|
| 4 |
+
--- TWO POINTERS TECHNIQUE ---
|
| 5 |
+
Use two pointers (left and right) to scan an array from both ends or at different speeds.
|
| 6 |
+
Reduces O(n^2) brute force to O(n).
|
| 7 |
+
|
| 8 |
+
--- Two Sum (Sorted Array) ---
|
| 9 |
+
Find two numbers that add up to target in a sorted array.
|
| 10 |
+
Time: O(n). Space: O(1).
|
| 11 |
+
|
| 12 |
+
def two_sum_sorted(arr, target):
|
| 13 |
+
left, right = 0, len(arr) - 1
|
| 14 |
+
while left < right:
|
| 15 |
+
total = arr[left] + arr[right]
|
| 16 |
+
if total == target:
|
| 17 |
+
return [left, right]
|
| 18 |
+
elif total < target:
|
| 19 |
+
left += 1
|
| 20 |
+
else:
|
| 21 |
+
right -= 1
|
| 22 |
+
return []
|
| 23 |
+
|
| 24 |
+
print(two_sum_sorted([1, 2, 3, 4, 6], 6)) # [1, 3] (2+4)
|
| 25 |
+
|
| 26 |
+
--- Reverse an Array In-Place ---
|
| 27 |
+
def reverse_array(arr):
|
| 28 |
+
left, right = 0, len(arr) - 1
|
| 29 |
+
while left < right:
|
| 30 |
+
arr[left], arr[right] = arr[right], arr[left]
|
| 31 |
+
left += 1
|
| 32 |
+
right -= 1
|
| 33 |
+
return arr
|
| 34 |
+
|
| 35 |
+
print(reverse_array([1, 2, 3, 4, 5])) # [5, 4, 3, 2, 1]
|
| 36 |
+
|
| 37 |
+
--- Remove Duplicates from Sorted Array ---
|
| 38 |
+
Time: O(n). Space: O(1).
|
| 39 |
+
|
| 40 |
+
def remove_duplicates(arr):
|
| 41 |
+
if not arr:
|
| 42 |
+
return 0
|
| 43 |
+
slow = 0
|
| 44 |
+
for fast in range(1, len(arr)):
|
| 45 |
+
if arr[fast] != arr[slow]:
|
| 46 |
+
slow += 1
|
| 47 |
+
arr[slow] = arr[fast]
|
| 48 |
+
return slow + 1 # length of unique part
|
| 49 |
+
|
| 50 |
+
arr = [1, 1, 2, 2, 3, 4, 4]
|
| 51 |
+
k = remove_duplicates(arr)
|
| 52 |
+
print(arr[:k]) # [1, 2, 3, 4]
|
| 53 |
+
|
| 54 |
+
--- Container With Most Water ---
|
| 55 |
+
Find two lines that form container holding most water.
|
| 56 |
+
Time: O(n). Space: O(1).
|
| 57 |
+
|
| 58 |
+
def max_water(heights):
|
| 59 |
+
left, right = 0, len(heights) - 1
|
| 60 |
+
max_area = 0
|
| 61 |
+
while left < right:
|
| 62 |
+
area = min(heights[left], heights[right]) * (right - left)
|
| 63 |
+
max_area = max(max_area, area)
|
| 64 |
+
if heights[left] < heights[right]:
|
| 65 |
+
left += 1
|
| 66 |
+
else:
|
| 67 |
+
right -= 1
|
| 68 |
+
return max_area
|
| 69 |
+
|
| 70 |
+
print(max_water([1,8,6,2,5,4,8,3,7])) # Output: 49
|
| 71 |
+
|
| 72 |
+
--- Three Sum ---
|
| 73 |
+
Find all triplets that sum to zero.
|
| 74 |
+
Time: O(n^2). Space: O(n).
|
| 75 |
+
|
| 76 |
+
def three_sum(nums):
|
| 77 |
+
nums.sort()
|
| 78 |
+
result = []
|
| 79 |
+
for i in range(len(nums) - 2):
|
| 80 |
+
if i > 0 and nums[i] == nums[i-1]:
|
| 81 |
+
continue # skip duplicates
|
| 82 |
+
left, right = i + 1, len(nums) - 1
|
| 83 |
+
while left < right:
|
| 84 |
+
total = nums[i] + nums[left] + nums[right]
|
| 85 |
+
if total == 0:
|
| 86 |
+
result.append([nums[i], nums[left], nums[right]])
|
| 87 |
+
while left < right and nums[left] == nums[left+1]: left += 1
|
| 88 |
+
while left < right and nums[right] == nums[right-1]: right -= 1
|
| 89 |
+
left += 1
|
| 90 |
+
right -= 1
|
| 91 |
+
elif total < 0:
|
| 92 |
+
left += 1
|
| 93 |
+
else:
|
| 94 |
+
right -= 1
|
| 95 |
+
return result
|
| 96 |
+
|
| 97 |
+
print(three_sum([-1, 0, 1, 2, -1, -4]))
|
| 98 |
+
# [[-1, -1, 2], [-1, 0, 1]]
|
| 99 |
+
|
| 100 |
+
--- Trapping Rain Water ---
|
| 101 |
+
Calculate water trapped between bars.
|
| 102 |
+
Time: O(n). Space: O(1).
|
| 103 |
+
|
| 104 |
+
def trap_water(height):
|
| 105 |
+
left, right = 0, len(height) - 1
|
| 106 |
+
left_max = right_max = 0
|
| 107 |
+
water = 0
|
| 108 |
+
while left < right:
|
| 109 |
+
if height[left] < height[right]:
|
| 110 |
+
if height[left] >= left_max:
|
| 111 |
+
left_max = height[left]
|
| 112 |
+
else:
|
| 113 |
+
water += left_max - height[left]
|
| 114 |
+
left += 1
|
| 115 |
+
else:
|
| 116 |
+
if height[right] >= right_max:
|
| 117 |
+
right_max = height[right]
|
| 118 |
+
else:
|
| 119 |
+
water += right_max - height[right]
|
| 120 |
+
right -= 1
|
| 121 |
+
return water
|
| 122 |
+
|
| 123 |
+
print(trap_water([0,1,0,2,1,0,1,3,2,1,2,1])) # Output: 6
|
| 124 |
+
|
| 125 |
+
--- Slow/Fast Pointer: Detect Cycle ---
|
| 126 |
+
def has_cycle(head):
|
| 127 |
+
slow = fast = head
|
| 128 |
+
while fast and fast.next:
|
| 129 |
+
slow = slow.next
|
| 130 |
+
fast = fast.next.next
|
| 131 |
+
if slow == fast:
|
| 132 |
+
return True
|
| 133 |
+
return False
|
| 134 |
+
|
| 135 |
+
--- SLIDING WINDOW TECHNIQUE ---
|
| 136 |
+
Maintain a window of fixed or variable size. Slide it across array.
|
| 137 |
+
Avoids recomputing the entire window each step.
|
| 138 |
+
|
| 139 |
+
--- Maximum Sum Subarray of Size K (Fixed Window) ---
|
| 140 |
+
Time: O(n). Space: O(1).
|
| 141 |
+
|
| 142 |
+
def max_sum_subarray(arr, k):
|
| 143 |
+
window_sum = sum(arr[:k])
|
| 144 |
+
max_sum = window_sum
|
| 145 |
+
for i in range(k, len(arr)):
|
| 146 |
+
window_sum += arr[i] - arr[i - k]
|
| 147 |
+
max_sum = max(max_sum, window_sum)
|
| 148 |
+
return max_sum
|
| 149 |
+
|
| 150 |
+
print(max_sum_subarray([2,1,5,1,3,2], 3)) # Output: 9 (5+1+3)
|
| 151 |
+
|
| 152 |
+
--- Longest Substring Without Repeating Characters (Variable Window) ---
|
| 153 |
+
Time: O(n). Space: O(k) where k = charset size.
|
| 154 |
+
|
| 155 |
+
def length_of_longest_substring(s):
|
| 156 |
+
char_index = {}
|
| 157 |
+
left = 0
|
| 158 |
+
max_len = 0
|
| 159 |
+
for right, ch in enumerate(s):
|
| 160 |
+
if ch in char_index and char_index[ch] >= left:
|
| 161 |
+
left = char_index[ch] + 1
|
| 162 |
+
char_index[ch] = right
|
| 163 |
+
max_len = max(max_len, right - left + 1)
|
| 164 |
+
return max_len
|
| 165 |
+
|
| 166 |
+
print(length_of_longest_substring("abcabcbb")) # 3 (abc)
|
| 167 |
+
print(length_of_longest_substring("pwwkew")) # 3 (wke)
|
| 168 |
+
|
| 169 |
+
--- Minimum Size Subarray Sum ---
|
| 170 |
+
Find smallest subarray with sum >= target.
|
| 171 |
+
Time: O(n). Space: O(1).
|
| 172 |
+
|
| 173 |
+
def min_subarray_len(target, nums):
|
| 174 |
+
left = 0
|
| 175 |
+
curr_sum = 0
|
| 176 |
+
min_len = float('inf')
|
| 177 |
+
for right in range(len(nums)):
|
| 178 |
+
curr_sum += nums[right]
|
| 179 |
+
while curr_sum >= target:
|
| 180 |
+
min_len = min(min_len, right - left + 1)
|
| 181 |
+
curr_sum -= nums[left]
|
| 182 |
+
left += 1
|
| 183 |
+
return min_len if min_len != float('inf') else 0
|
| 184 |
+
|
| 185 |
+
print(min_subarray_len(7, [2,3,1,2,4,3])) # Output: 2 (4+3)
|
| 186 |
+
|
| 187 |
+
--- Longest Substring with K Distinct Characters ---
|
| 188 |
+
Time: O(n). Space: O(k).
|
| 189 |
+
|
| 190 |
+
def longest_k_distinct(s, k):
|
| 191 |
+
from collections import defaultdict
|
| 192 |
+
char_count = defaultdict(int)
|
| 193 |
+
left = max_len = 0
|
| 194 |
+
for right, ch in enumerate(s):
|
| 195 |
+
char_count[ch] += 1
|
| 196 |
+
while len(char_count) > k:
|
| 197 |
+
char_count[s[left]] -= 1
|
| 198 |
+
if char_count[s[left]] == 0:
|
| 199 |
+
del char_count[s[left]]
|
| 200 |
+
left += 1
|
| 201 |
+
max_len = max(max_len, right - left + 1)
|
| 202 |
+
return max_len
|
| 203 |
+
|
| 204 |
+
print(longest_k_distinct("araaci", 2)) # Output: 4 (araa)
|
data/faiss_index/index.faiss
ADDED
|
Binary file (44.6 kB). View file
|
|
|
data/faiss_index/index.pkl
ADDED
|
Binary file (11.7 kB). View file
|
|
|
data/finetune_data.jsonl
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"instruction": "What is binary search?", "input": "", "output": "Binary search is an efficient searching algorithm for sorted arrays. It works by repeatedly dividing the search interval in half. Time complexity: O(log n), Space complexity: O(1). Requires the array to be sorted beforehand."}
|
| 2 |
+
{"instruction": "What is the time complexity of binary search?", "input": "", "output": "Binary search has a time complexity of O(log n) because it halves the search space with each step. Best case is O(1) when the middle element is the target. Space complexity is O(1) for iterative implementation."}
|
| 3 |
+
{"instruction": "What is the difference between stack and queue?", "input": "", "output": "Stack uses LIFO (Last In First Out) - the last element added is the first to be removed. Operations: push and pop. Queue uses FIFO (First In First Out) - the first element added is the first to be removed. Operations: enqueue and dequeue. Both have O(1) operations."}
|
| 4 |
+
{"instruction": "Explain merge sort.", "input": "", "output": "Merge sort is a divide-and-conquer sorting algorithm. It splits the array in half, recursively sorts each half, then merges the two sorted halves. Time complexity: O(n log n) in all cases. Space complexity: O(n). It is a stable sorting algorithm, making it good for sorting linked lists."}
|
| 5 |
+
{"instruction": "What is quicksort?", "input": "", "output": "Quicksort is a divide-and-conquer algorithm that picks a pivot element and partitions the array into elements less than and greater than the pivot, then recursively sorts each partition. Average time: O(n log n), Worst case: O(n^2) when pivot is always smallest or largest element. Space: O(log n)."}
|
| 6 |
+
{"instruction": "What is a linked list?", "input": "", "output": "A linked list is a linear data structure where elements are stored in nodes. Each node contains data and a pointer to the next node. Types: Singly (one direction), Doubly (both directions), Circular (last points to first). Advantage: dynamic size, O(1) insertion at head. Disadvantage: no random access, O(n) search."}
|
| 7 |
+
{"instruction": "What is a Binary Search Tree?", "input": "", "output": "A Binary Search Tree (BST) is a binary tree where for every node, all values in the left subtree are less than the node's value, and all values in the right subtree are greater. This property enables O(log n) average search, insert, and delete operations. Worst case is O(n) for unbalanced trees."}
|
| 8 |
+
{"instruction": "What is dynamic programming?", "input": "", "output": "Dynamic programming solves complex problems by breaking them into overlapping subproblems and storing results to avoid recomputation. Two approaches: Memoization (top-down, recursion with cache) and Tabulation (bottom-up, iterative). Requires optimal substructure and overlapping subproblems. Examples: Fibonacci, Knapsack, LCS, Coin Change."}
|
| 9 |
+
{"instruction": "What are React hooks?", "input": "", "output": "React hooks are functions that let you use state and lifecycle features in functional components, introduced in React 16.8. Key hooks: useState for state management, useEffect for side effects like API calls, useContext for consuming context, useRef for DOM references. Rules: only call hooks at top level, only in React functions."}
|
| 10 |
+
{"instruction": "What is useState in React?", "input": "", "output": "useState is a React hook for managing local component state. Syntax: const [state, setState] = useState(initialValue). It returns the current state value and a setter function. Calling setState triggers a re-render with the new value. Example: const [count, setCount] = useState(0) then setCount(count + 1) to increment."}
|
| 11 |
+
{"instruction": "What is useEffect in React?", "input": "", "output": "useEffect is a React hook for handling side effects like API calls, subscriptions, and DOM updates. It runs after every render by default. The dependency array controls when it runs: empty array [] runs only on mount, [value] runs when value changes. Return a cleanup function to run on unmount. Syntax: useEffect(() => { effect }, [deps])."}
|
| 12 |
+
{"instruction": "What is a REST API?", "input": "", "output": "REST API (Representational State Transfer) is an architectural style for web services using standard HTTP methods. GET retrieves resources, POST creates new resources, PUT updates resources completely, PATCH partially updates, DELETE removes resources. REST is stateless meaning each request contains all needed information. JSON is the most common response format."}
|
| 13 |
+
{"instruction": "What HTTP methods does REST use?", "input": "", "output": "REST uses five main HTTP methods: GET for reading/retrieving data, POST for creating new resources, PUT for completely updating an existing resource, PATCH for partially updating a resource, and DELETE for removing a resource. These map to CRUD operations: Create (POST), Read (GET), Update (PUT/PATCH), Delete (DELETE)."}
|
| 14 |
+
{"instruction": "What does HTTP status code 404 mean?", "input": "", "output": "HTTP status code 404 means Not Found. It indicates the server cannot find the requested resource. The URL may be wrong, the resource may have been deleted, or it may never have existed. Other important codes: 200 (OK), 201 (Created), 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 500 (Internal Server Error)."}
|
| 15 |
+
{"instruction": "What is CSS Flexbox?", "input": "", "output": "CSS Flexbox is a layout model that makes it easy to arrange items in a container. Activated by display: flex on the container. Key properties: flex-direction (row or column), justify-content (main axis alignment: flex-start, center, space-between), align-items (cross axis: stretch, center, flex-start), flex-wrap (allow wrapping). Items can have flex-grow to fill available space."}
|
| 16 |
+
{"instruction": "What is a JavaScript Promise?", "input": "", "output": "A JavaScript Promise represents an asynchronous operation with three states: pending (initial), fulfilled (success), rejected (failure). Created with new Promise((resolve, reject) => {}). Consumed with .then() for success and .catch() for errors. async/await is syntactic sugar that makes promises look synchronous. Promise.all() runs multiple promises in parallel."}
|
| 17 |
+
{"instruction": "What is inorder traversal?", "input": "", "output": "Inorder traversal visits tree nodes in Left, Root, Right order. For a Binary Search Tree, inorder traversal produces nodes in ascending sorted order. Algorithm: recursively visit left subtree, then process current node, then recursively visit right subtree. Time complexity: O(n) where n is number of nodes."}
|
| 18 |
+
{"instruction": "What is the difference between BFS and DFS?", "input": "", "output": "BFS (Breadth-First Search) explores level by level using a queue data structure. It finds shortest path in unweighted graphs. DFS (Depth-First Search) explores as deep as possible using a stack or recursion. BFS is better for shortest path problems; DFS is better for cycle detection, topological sort, and finding connected components. Both have O(V+E) time complexity."}
|
| 19 |
+
{"instruction": "What is memoization?", "input": "", "output": "Memoization is a top-down dynamic programming technique that caches results of expensive function calls and returns the cached result when the same inputs occur again. It uses recursion combined with a hash map or array to store computed values. For example, Fibonacci with memoization runs in O(n) instead of O(2^n). It trades space for time."}
|
| 20 |
+
{"instruction": "What is flex-grow in CSS?", "input": "", "output": "flex-grow is a CSS Flexbox property that defines how much a flex item grows relative to other items when extra space is available in the container. A value of 0 means the item will not grow. A value of 1 means the item takes equal share of available space. If one item has flex-grow: 2 and another has flex-grow: 1, the first gets twice as much extra space."}
|
data/prepare_docs.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
docs = {
|
| 4 |
+
"binary_search.txt": """Binary Search is an efficient algorithm for finding an item in a sorted list.
|
| 5 |
+
It works by repeatedly dividing the search interval in half.
|
| 6 |
+
Time Complexity: O(log n). Space Complexity: O(1).
|
| 7 |
+
Steps:
|
| 8 |
+
1) Find the middle element.
|
| 9 |
+
2) If target equals middle, return index.
|
| 10 |
+
3) If target < middle, search left half.
|
| 11 |
+
4) Else search right half.
|
| 12 |
+
Requires the array to be sorted before searching.
|
| 13 |
+
Best case: O(1) when middle element is the target.
|
| 14 |
+
""",
|
| 15 |
+
|
| 16 |
+
"stack_queue.txt": """Stack is a linear data structure following LIFO (Last In First Out) principle.
|
| 17 |
+
Operations: push() adds element, pop() removes top element, peek() views top.
|
| 18 |
+
All stack operations run in O(1) time.
|
| 19 |
+
Use stack for: undo operations, expression evaluation, function call stack, DFS traversal.
|
| 20 |
+
|
| 21 |
+
Queue follows FIFO (First In First Out) principle.
|
| 22 |
+
Operations: enqueue() adds to rear, dequeue() removes from front.
|
| 23 |
+
All queue operations run in O(1) time.
|
| 24 |
+
Use queue for: BFS traversal, scheduling, print spooler, task queues.
|
| 25 |
+
|
| 26 |
+
Key difference: Stack removes most recently added item; Queue removes oldest item.
|
| 27 |
+
""",
|
| 28 |
+
|
| 29 |
+
"sorting_algorithms.txt": """Bubble Sort: repeatedly swaps adjacent elements if out of order.
|
| 30 |
+
Time: O(n^2) average and worst. Space: O(1). Stable sort.
|
| 31 |
+
|
| 32 |
+
Merge Sort: divide array in half, sort each half recursively, merge results.
|
| 33 |
+
Time: O(n log n) all cases. Space: O(n). Stable sort. Best for linked lists.
|
| 34 |
+
|
| 35 |
+
Quick Sort: picks a pivot element, partitions array around it, recursively sorts partitions.
|
| 36 |
+
Time: O(n log n) average, O(n^2) worst case. Space: O(log n). Not stable.
|
| 37 |
+
|
| 38 |
+
Insertion Sort: builds sorted array one element at a time by inserting each into correct position.
|
| 39 |
+
Time: O(n^2) worst, O(n) best. Space: O(1). Good for small or nearly sorted arrays.
|
| 40 |
+
|
| 41 |
+
Heap Sort: builds a max-heap, repeatedly extracts maximum element.
|
| 42 |
+
Time: O(n log n). Space: O(1). Not stable.
|
| 43 |
+
""",
|
| 44 |
+
|
| 45 |
+
"linked_list.txt": """Linked List is a linear data structure where elements are stored in nodes.
|
| 46 |
+
Each node contains: data field + pointer to next node.
|
| 47 |
+
|
| 48 |
+
Types:
|
| 49 |
+
- Singly Linked List: each node points to next node only.
|
| 50 |
+
- Doubly Linked List: each node has pointers to both next and previous nodes.
|
| 51 |
+
- Circular Linked List: last node points back to first node.
|
| 52 |
+
|
| 53 |
+
Advantages over arrays: dynamic size, O(1) insertion/deletion at head.
|
| 54 |
+
Disadvantages: no random access (must traverse), extra memory for pointers.
|
| 55 |
+
|
| 56 |
+
Operations: Insert at head O(1), Search O(n), Delete O(n), Access by index O(n).
|
| 57 |
+
""",
|
| 58 |
+
|
| 59 |
+
"trees.txt": """Binary Tree: each node has at most 2 children called left and right child.
|
| 60 |
+
Root is the topmost node. Leaf nodes have no children.
|
| 61 |
+
|
| 62 |
+
Binary Search Tree (BST): left child < parent node < right child.
|
| 63 |
+
Search, Insert, Delete all take O(log n) average, O(n) worst case.
|
| 64 |
+
|
| 65 |
+
Tree Traversals:
|
| 66 |
+
- Inorder (Left, Root, Right): gives sorted order for BST.
|
| 67 |
+
- Preorder (Root, Left, Right): used to copy tree.
|
| 68 |
+
- Postorder (Left, Right, Root): used to delete tree.
|
| 69 |
+
- Level Order: BFS traversal level by level.
|
| 70 |
+
|
| 71 |
+
Height of tree: number of edges on longest path from root to leaf.
|
| 72 |
+
Balanced BST examples: AVL Tree, Red-Black Tree — guarantee O(log n) operations.
|
| 73 |
+
""",
|
| 74 |
+
|
| 75 |
+
"dynamic_programming.txt": """Dynamic Programming (DP) solves problems by breaking into overlapping subproblems.
|
| 76 |
+
Stores results of subproblems to avoid recomputation (memoization).
|
| 77 |
+
|
| 78 |
+
Key properties required:
|
| 79 |
+
1. Optimal substructure: optimal solution built from optimal subproblem solutions.
|
| 80 |
+
2. Overlapping subproblems: same subproblems solved multiple times.
|
| 81 |
+
|
| 82 |
+
Approaches:
|
| 83 |
+
- Top-down (Memoization): recursion + cache results in a table.
|
| 84 |
+
- Bottom-up (Tabulation): fill table iteratively from smallest subproblems.
|
| 85 |
+
|
| 86 |
+
Classic DP problems:
|
| 87 |
+
- Fibonacci: O(2^n) naive → O(n) with DP.
|
| 88 |
+
- 0/1 Knapsack: maximize value within weight limit.
|
| 89 |
+
- Longest Common Subsequence (LCS).
|
| 90 |
+
- Coin Change: minimum coins to make a sum.
|
| 91 |
+
- Longest Increasing Subsequence.
|
| 92 |
+
""",
|
| 93 |
+
|
| 94 |
+
"react_hooks.txt": """React Hooks are functions that let you use state and lifecycle features in functional components.
|
| 95 |
+
Introduced in React 16.8 to replace class components.
|
| 96 |
+
|
| 97 |
+
useState: manages local component state.
|
| 98 |
+
Syntax: const [state, setState] = useState(initialValue)
|
| 99 |
+
Example: const [count, setCount] = useState(0)
|
| 100 |
+
|
| 101 |
+
useEffect: handles side effects like API calls, subscriptions, DOM updates.
|
| 102 |
+
Runs after every render by default.
|
| 103 |
+
Syntax: useEffect(() => { /* effect */ }, [dependencies])
|
| 104 |
+
Empty dependency array [] means run only once on mount.
|
| 105 |
+
|
| 106 |
+
useContext: consume React context without wrapping in Consumer component.
|
| 107 |
+
Syntax: const value = useContext(MyContext)
|
| 108 |
+
|
| 109 |
+
useRef: holds mutable value that does not trigger re-render. Also used to access DOM elements.
|
| 110 |
+
|
| 111 |
+
Rules of Hooks:
|
| 112 |
+
1. Only call hooks at the top level (not inside loops or conditions).
|
| 113 |
+
2. Only call hooks from React function components or custom hooks.
|
| 114 |
+
""",
|
| 115 |
+
|
| 116 |
+
"rest_api.txt": """REST API (Representational State Transfer) is an architectural style for web services.
|
| 117 |
+
REST uses standard HTTP methods to perform operations on resources.
|
| 118 |
+
|
| 119 |
+
HTTP Methods:
|
| 120 |
+
- GET: retrieve/read resource. No request body.
|
| 121 |
+
- POST: create new resource. Data sent in request body.
|
| 122 |
+
- PUT: update existing resource completely.
|
| 123 |
+
- PATCH: partially update a resource.
|
| 124 |
+
- DELETE: remove a resource.
|
| 125 |
+
|
| 126 |
+
HTTP Status Codes:
|
| 127 |
+
- 200 OK: request succeeded.
|
| 128 |
+
- 201 Created: new resource created successfully.
|
| 129 |
+
- 400 Bad Request: invalid request from client.
|
| 130 |
+
- 401 Unauthorized: authentication required.
|
| 131 |
+
- 403 Forbidden: authenticated but not authorized.
|
| 132 |
+
- 404 Not Found: resource does not exist.
|
| 133 |
+
- 500 Internal Server Error: server-side error.
|
| 134 |
+
|
| 135 |
+
REST principles: Stateless, Client-Server, Cacheable, Uniform Interface.
|
| 136 |
+
JSON is the most common data format for REST API responses.
|
| 137 |
+
""",
|
| 138 |
+
|
| 139 |
+
"css_flexbox.txt": """CSS Flexbox (Flexible Box Layout) is a layout model for arranging items in a container.
|
| 140 |
+
Activated by setting display: flex on a container element.
|
| 141 |
+
|
| 142 |
+
Container properties:
|
| 143 |
+
- flex-direction: row (default, horizontal) or column (vertical).
|
| 144 |
+
- justify-content: aligns items on main axis. Values: flex-start, flex-end, center, space-between, space-around.
|
| 145 |
+
- align-items: aligns items on cross axis. Values: stretch (default), flex-start, flex-end, center.
|
| 146 |
+
- flex-wrap: nowrap (default) or wrap to allow items to move to next line.
|
| 147 |
+
- gap: space between flex items.
|
| 148 |
+
|
| 149 |
+
Item properties:
|
| 150 |
+
- flex-grow: how much item grows relative to siblings. 0 means no grow.
|
| 151 |
+
- flex-shrink: how much item shrinks when space is limited.
|
| 152 |
+
- flex-basis: initial size of item before flex calculations.
|
| 153 |
+
- align-self: overrides align-items for a single item.
|
| 154 |
+
|
| 155 |
+
Shorthand: flex: grow shrink basis (e.g., flex: 1 1 auto)
|
| 156 |
+
""",
|
| 157 |
+
|
| 158 |
+
"javascript_promises.txt": """JavaScript Promises handle asynchronous operations in a cleaner way than callbacks.
|
| 159 |
+
|
| 160 |
+
Promise States:
|
| 161 |
+
- Pending: initial state, operation not complete.
|
| 162 |
+
- Fulfilled: operation completed successfully.
|
| 163 |
+
- Rejected: operation failed.
|
| 164 |
+
|
| 165 |
+
Creating a Promise:
|
| 166 |
+
const myPromise = new Promise((resolve, reject) => {
|
| 167 |
+
if (success) resolve(result);
|
| 168 |
+
else reject(error);
|
| 169 |
+
});
|
| 170 |
+
|
| 171 |
+
Consuming a Promise:
|
| 172 |
+
myPromise
|
| 173 |
+
.then(result => console.log(result))
|
| 174 |
+
.catch(error => console.error(error))
|
| 175 |
+
.finally(() => console.log('always runs'));
|
| 176 |
+
|
| 177 |
+
async/await syntax (ES2017) is syntactic sugar over promises:
|
| 178 |
+
async function fetchData() {
|
| 179 |
+
try {
|
| 180 |
+
const data = await fetch(url);
|
| 181 |
+
const json = await data.json();
|
| 182 |
+
return json;
|
| 183 |
+
} catch (error) {
|
| 184 |
+
console.error(error);
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
Promise utility methods:
|
| 189 |
+
- Promise.all([p1, p2]): runs in parallel, resolves when all resolve.
|
| 190 |
+
- Promise.race([p1, p2]): resolves/rejects with first settled promise.
|
| 191 |
+
- Promise.allSettled(): waits for all, returns all results regardless of success/failure.
|
| 192 |
+
""",
|
| 193 |
+
|
| 194 |
+
"graph_algorithms.txt": """Graph is a data structure with nodes (vertices) connected by edges.
|
| 195 |
+
Types: Directed/Undirected, Weighted/Unweighted, Cyclic/Acyclic.
|
| 196 |
+
|
| 197 |
+
Representations:
|
| 198 |
+
- Adjacency Matrix: 2D array. O(1) edge lookup, O(V^2) space.
|
| 199 |
+
- Adjacency List: array of lists. O(V+E) space. Better for sparse graphs.
|
| 200 |
+
|
| 201 |
+
BFS (Breadth-First Search):
|
| 202 |
+
Explores level by level using a queue. Time: O(V+E).
|
| 203 |
+
Used for: shortest path in unweighted graph, level-order traversal.
|
| 204 |
+
|
| 205 |
+
DFS (Depth-First Search):
|
| 206 |
+
Explores as far as possible using stack/recursion. Time: O(V+E).
|
| 207 |
+
Used for: cycle detection, topological sort, connected components.
|
| 208 |
+
|
| 209 |
+
Dijkstra's Algorithm: shortest path in weighted graph with non-negative edges.
|
| 210 |
+
Time: O((V+E) log V) with priority queue.
|
| 211 |
+
|
| 212 |
+
Topological Sort: linear ordering of vertices where each directed edge u→v means u comes before v.
|
| 213 |
+
Only valid for Directed Acyclic Graphs (DAGs).
|
| 214 |
+
"""
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
os.makedirs("docs", exist_ok=True)
|
| 218 |
+
for filename, content in docs.items():
|
| 219 |
+
with open(f"docs/{filename}", "w", encoding="utf-8") as f:
|
| 220 |
+
f.write(content.strip())
|
| 221 |
+
|
| 222 |
+
print(f"Created {len(docs)} documents in docs/")
|
data/prepare_finetune.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
qa_pairs = [
|
| 5 |
+
{"instruction": "What is binary search?",
|
| 6 |
+
"input": "",
|
| 7 |
+
"output": "Binary search is an efficient searching algorithm for sorted arrays. It works by repeatedly dividing the search interval in half. Time complexity: O(log n), Space complexity: O(1). Requires the array to be sorted beforehand."},
|
| 8 |
+
|
| 9 |
+
{"instruction": "What is the time complexity of binary search?",
|
| 10 |
+
"input": "",
|
| 11 |
+
"output": "Binary search has a time complexity of O(log n) because it halves the search space with each step. Best case is O(1) when the middle element is the target. Space complexity is O(1) for iterative implementation."},
|
| 12 |
+
|
| 13 |
+
{"instruction": "What is the difference between stack and queue?",
|
| 14 |
+
"input": "",
|
| 15 |
+
"output": "Stack uses LIFO (Last In First Out) - the last element added is the first to be removed. Operations: push and pop. Queue uses FIFO (First In First Out) - the first element added is the first to be removed. Operations: enqueue and dequeue. Both have O(1) operations."},
|
| 16 |
+
|
| 17 |
+
{"instruction": "Explain merge sort.",
|
| 18 |
+
"input": "",
|
| 19 |
+
"output": "Merge sort is a divide-and-conquer sorting algorithm. It splits the array in half, recursively sorts each half, then merges the two sorted halves. Time complexity: O(n log n) in all cases. Space complexity: O(n). It is a stable sorting algorithm, making it good for sorting linked lists."},
|
| 20 |
+
|
| 21 |
+
{"instruction": "What is quicksort?",
|
| 22 |
+
"input": "",
|
| 23 |
+
"output": "Quicksort is a divide-and-conquer algorithm that picks a pivot element and partitions the array into elements less than and greater than the pivot, then recursively sorts each partition. Average time: O(n log n), Worst case: O(n^2) when pivot is always smallest or largest element. Space: O(log n)."},
|
| 24 |
+
|
| 25 |
+
{"instruction": "What is a linked list?",
|
| 26 |
+
"input": "",
|
| 27 |
+
"output": "A linked list is a linear data structure where elements are stored in nodes. Each node contains data and a pointer to the next node. Types: Singly (one direction), Doubly (both directions), Circular (last points to first). Advantage: dynamic size, O(1) insertion at head. Disadvantage: no random access, O(n) search."},
|
| 28 |
+
|
| 29 |
+
{"instruction": "What is a Binary Search Tree?",
|
| 30 |
+
"input": "",
|
| 31 |
+
"output": "A Binary Search Tree (BST) is a binary tree where for every node, all values in the left subtree are less than the node's value, and all values in the right subtree are greater. This property enables O(log n) average search, insert, and delete operations. Worst case is O(n) for unbalanced trees."},
|
| 32 |
+
|
| 33 |
+
{"instruction": "What is dynamic programming?",
|
| 34 |
+
"input": "",
|
| 35 |
+
"output": "Dynamic programming solves complex problems by breaking them into overlapping subproblems and storing results to avoid recomputation. Two approaches: Memoization (top-down, recursion with cache) and Tabulation (bottom-up, iterative). Requires optimal substructure and overlapping subproblems. Examples: Fibonacci, Knapsack, LCS, Coin Change."},
|
| 36 |
+
|
| 37 |
+
{"instruction": "What are React hooks?",
|
| 38 |
+
"input": "",
|
| 39 |
+
"output": "React hooks are functions that let you use state and lifecycle features in functional components, introduced in React 16.8. Key hooks: useState for state management, useEffect for side effects like API calls, useContext for consuming context, useRef for DOM references. Rules: only call hooks at top level, only in React functions."},
|
| 40 |
+
|
| 41 |
+
{"instruction": "What is useState in React?",
|
| 42 |
+
"input": "",
|
| 43 |
+
"output": "useState is a React hook for managing local component state. Syntax: const [state, setState] = useState(initialValue). It returns the current state value and a setter function. Calling setState triggers a re-render with the new value. Example: const [count, setCount] = useState(0) then setCount(count + 1) to increment."},
|
| 44 |
+
|
| 45 |
+
{"instruction": "What is useEffect in React?",
|
| 46 |
+
"input": "",
|
| 47 |
+
"output": "useEffect is a React hook for handling side effects like API calls, subscriptions, and DOM updates. It runs after every render by default. The dependency array controls when it runs: empty array [] runs only on mount, [value] runs when value changes. Return a cleanup function to run on unmount. Syntax: useEffect(() => { effect }, [deps])."},
|
| 48 |
+
|
| 49 |
+
{"instruction": "What is a REST API?",
|
| 50 |
+
"input": "",
|
| 51 |
+
"output": "REST API (Representational State Transfer) is an architectural style for web services using standard HTTP methods. GET retrieves resources, POST creates new resources, PUT updates resources completely, PATCH partially updates, DELETE removes resources. REST is stateless meaning each request contains all needed information. JSON is the most common response format."},
|
| 52 |
+
|
| 53 |
+
{"instruction": "What HTTP methods does REST use?",
|
| 54 |
+
"input": "",
|
| 55 |
+
"output": "REST uses five main HTTP methods: GET for reading/retrieving data, POST for creating new resources, PUT for completely updating an existing resource, PATCH for partially updating a resource, and DELETE for removing a resource. These map to CRUD operations: Create (POST), Read (GET), Update (PUT/PATCH), Delete (DELETE)."},
|
| 56 |
+
|
| 57 |
+
{"instruction": "What does HTTP status code 404 mean?",
|
| 58 |
+
"input": "",
|
| 59 |
+
"output": "HTTP status code 404 means Not Found. It indicates the server cannot find the requested resource. The URL may be wrong, the resource may have been deleted, or it may never have existed. Other important codes: 200 (OK), 201 (Created), 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 500 (Internal Server Error)."},
|
| 60 |
+
|
| 61 |
+
{"instruction": "What is CSS Flexbox?",
|
| 62 |
+
"input": "",
|
| 63 |
+
"output": "CSS Flexbox is a layout model that makes it easy to arrange items in a container. Activated by display: flex on the container. Key properties: flex-direction (row or column), justify-content (main axis alignment: flex-start, center, space-between), align-items (cross axis: stretch, center, flex-start), flex-wrap (allow wrapping). Items can have flex-grow to fill available space."},
|
| 64 |
+
|
| 65 |
+
{"instruction": "What is a JavaScript Promise?",
|
| 66 |
+
"input": "",
|
| 67 |
+
"output": "A JavaScript Promise represents an asynchronous operation with three states: pending (initial), fulfilled (success), rejected (failure). Created with new Promise((resolve, reject) => {}). Consumed with .then() for success and .catch() for errors. async/await is syntactic sugar that makes promises look synchronous. Promise.all() runs multiple promises in parallel."},
|
| 68 |
+
|
| 69 |
+
{"instruction": "What is inorder traversal?",
|
| 70 |
+
"input": "",
|
| 71 |
+
"output": "Inorder traversal visits tree nodes in Left, Root, Right order. For a Binary Search Tree, inorder traversal produces nodes in ascending sorted order. Algorithm: recursively visit left subtree, then process current node, then recursively visit right subtree. Time complexity: O(n) where n is number of nodes."},
|
| 72 |
+
|
| 73 |
+
{"instruction": "What is the difference between BFS and DFS?",
|
| 74 |
+
"input": "",
|
| 75 |
+
"output": "BFS (Breadth-First Search) explores level by level using a queue data structure. It finds shortest path in unweighted graphs. DFS (Depth-First Search) explores as deep as possible using a stack or recursion. BFS is better for shortest path problems; DFS is better for cycle detection, topological sort, and finding connected components. Both have O(V+E) time complexity."},
|
| 76 |
+
|
| 77 |
+
{"instruction": "What is memoization?",
|
| 78 |
+
"input": "",
|
| 79 |
+
"output": "Memoization is a top-down dynamic programming technique that caches results of expensive function calls and returns the cached result when the same inputs occur again. It uses recursion combined with a hash map or array to store computed values. For example, Fibonacci with memoization runs in O(n) instead of O(2^n). It trades space for time."},
|
| 80 |
+
|
| 81 |
+
{"instruction": "What is flex-grow in CSS?",
|
| 82 |
+
"input": "",
|
| 83 |
+
"output": "flex-grow is a CSS Flexbox property that defines how much a flex item grows relative to other items when extra space is available in the container. A value of 0 means the item will not grow. A value of 1 means the item takes equal share of available space. If one item has flex-grow: 2 and another has flex-grow: 1, the first gets twice as much extra space."},
|
| 84 |
+
]
|
| 85 |
+
|
| 86 |
+
output_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "finetune_data.jsonl")
|
| 87 |
+
with open(output_path, "w", encoding="utf-8") as f:
|
| 88 |
+
for pair in qa_pairs:
|
| 89 |
+
f.write(json.dumps(pair) + "\n")
|
| 90 |
+
|
| 91 |
+
print(f"Saved {len(qa_pairs)} training examples to data/finetune_data.jsonl")
|
data/prepare_questions.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import csv
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
questions = [
|
| 5 |
+
"What is binary search?",
|
| 6 |
+
"What is the time complexity of binary search?",
|
| 7 |
+
"When can you use binary search?",
|
| 8 |
+
"Explain bubble sort.",
|
| 9 |
+
"What is the difference between stack and queue?",
|
| 10 |
+
"What is LIFO?",
|
| 11 |
+
"What is FIFO?",
|
| 12 |
+
"Explain merge sort.",
|
| 13 |
+
"What is quicksort?",
|
| 14 |
+
"What is the worst case time complexity of quicksort?",
|
| 15 |
+
"What is a linked list?",
|
| 16 |
+
"What is the difference between array and linked list?",
|
| 17 |
+
"What is a doubly linked list?",
|
| 18 |
+
"What is a binary tree?",
|
| 19 |
+
"What is a Binary Search Tree?",
|
| 20 |
+
"What is inorder traversal?",
|
| 21 |
+
"What is the difference between BFS and DFS?",
|
| 22 |
+
"What is dynamic programming?",
|
| 23 |
+
"What is memoization?",
|
| 24 |
+
"What is the difference between memoization and tabulation?",
|
| 25 |
+
"Explain the coin change problem.",
|
| 26 |
+
"What is React?",
|
| 27 |
+
"What are React hooks?",
|
| 28 |
+
"What is useState in React?",
|
| 29 |
+
"What is useEffect in React?",
|
| 30 |
+
"What is a REST API?",
|
| 31 |
+
"What HTTP methods does REST use?",
|
| 32 |
+
"What does HTTP status code 404 mean?",
|
| 33 |
+
"What does HTTP status code 200 mean?",
|
| 34 |
+
"What is CSS Flexbox?",
|
| 35 |
+
"What does justify-content do in Flexbox?",
|
| 36 |
+
"What is a JavaScript Promise?",
|
| 37 |
+
"What are the three states of a Promise?",
|
| 38 |
+
"What is async/await in JavaScript?",
|
| 39 |
+
"What is Promise.all()?",
|
| 40 |
+
"What is insertion sort?",
|
| 41 |
+
"What is heap sort?",
|
| 42 |
+
"What is a graph in data structures?",
|
| 43 |
+
"What is Dijkstra algorithm?",
|
| 44 |
+
"What is topological sort?",
|
| 45 |
+
"What is the difference between stack and heap memory?",
|
| 46 |
+
"What is a circular linked list?",
|
| 47 |
+
"What is an AVL tree?",
|
| 48 |
+
"What is the difference between preorder and postorder traversal?",
|
| 49 |
+
"What is the knapsack problem?",
|
| 50 |
+
"What is the longest common subsequence?",
|
| 51 |
+
"What is flex-grow in CSS?",
|
| 52 |
+
"What is useContext in React?",
|
| 53 |
+
"What is useRef in React?",
|
| 54 |
+
"What is the difference between PUT and PATCH in REST?",
|
| 55 |
+
]
|
| 56 |
+
|
| 57 |
+
os.makedirs(os.path.dirname(os.path.abspath(__file__)), exist_ok=True)
|
| 58 |
+
|
| 59 |
+
output_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "questions.csv")
|
| 60 |
+
with open(output_path, "w", newline="", encoding="utf-8") as f:
|
| 61 |
+
writer = csv.writer(f)
|
| 62 |
+
writer.writerow(["id", "question"])
|
| 63 |
+
for i, q in enumerate(questions, 1):
|
| 64 |
+
writer.writerow([i, q])
|
| 65 |
+
|
| 66 |
+
print(f"Created {len(questions)} questions in data/questions.csv")
|
data/questions.csv
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
id,question
|
| 2 |
+
1,What is binary search?
|
| 3 |
+
2,What is the time complexity of binary search?
|
| 4 |
+
3,When can you use binary search?
|
| 5 |
+
4,Explain bubble sort.
|
| 6 |
+
5,What is the difference between stack and queue?
|
| 7 |
+
6,What is LIFO?
|
| 8 |
+
7,What is FIFO?
|
| 9 |
+
8,Explain merge sort.
|
| 10 |
+
9,What is quicksort?
|
| 11 |
+
10,What is the worst case time complexity of quicksort?
|
| 12 |
+
11,What is a linked list?
|
| 13 |
+
12,What is the difference between array and linked list?
|
| 14 |
+
13,What is a doubly linked list?
|
| 15 |
+
14,What is a binary tree?
|
| 16 |
+
15,What is a Binary Search Tree?
|
| 17 |
+
16,What is inorder traversal?
|
| 18 |
+
17,What is the difference between BFS and DFS?
|
| 19 |
+
18,What is dynamic programming?
|
| 20 |
+
19,What is memoization?
|
| 21 |
+
20,What is the difference between memoization and tabulation?
|
| 22 |
+
21,Explain the coin change problem.
|
| 23 |
+
22,What is React?
|
| 24 |
+
23,What are React hooks?
|
| 25 |
+
24,What is useState in React?
|
| 26 |
+
25,What is useEffect in React?
|
| 27 |
+
26,What is a REST API?
|
| 28 |
+
27,What HTTP methods does REST use?
|
| 29 |
+
28,What does HTTP status code 404 mean?
|
| 30 |
+
29,What does HTTP status code 200 mean?
|
| 31 |
+
30,What is CSS Flexbox?
|
| 32 |
+
31,What does justify-content do in Flexbox?
|
| 33 |
+
32,What is a JavaScript Promise?
|
| 34 |
+
33,What are the three states of a Promise?
|
| 35 |
+
34,What is async/await in JavaScript?
|
| 36 |
+
35,What is Promise.all()?
|
| 37 |
+
36,What is insertion sort?
|
| 38 |
+
37,What is heap sort?
|
| 39 |
+
38,What is a graph in data structures?
|
| 40 |
+
39,What is Dijkstra algorithm?
|
| 41 |
+
40,What is topological sort?
|
| 42 |
+
41,What is the difference between stack and heap memory?
|
| 43 |
+
42,What is a circular linked list?
|
| 44 |
+
43,What is an AVL tree?
|
| 45 |
+
44,What is the difference between preorder and postorder traversal?
|
| 46 |
+
45,What is the knapsack problem?
|
| 47 |
+
46,What is the longest common subsequence?
|
| 48 |
+
47,What is flex-grow in CSS?
|
| 49 |
+
48,What is useContext in React?
|
| 50 |
+
49,What is useRef in React?
|
| 51 |
+
50,What is the difference between PUT and PATCH in REST?
|
demo.py
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import streamlit as st
|
| 3 |
+
from system1_baseline import ask_baseline
|
| 4 |
+
from system2_rag import ask_rag, load_vectorstore, build_vectorstore
|
| 5 |
+
|
| 6 |
+
st.set_page_config(
|
| 7 |
+
page_title="CodeSage",
|
| 8 |
+
page_icon="🧙",
|
| 9 |
+
layout="wide"
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
# ── Session state init ────────────────────────────────────────────────────────
|
| 13 |
+
if "ratings" not in st.session_state:
|
| 14 |
+
st.session_state.ratings = [] # list of rating dicts
|
| 15 |
+
if "last_results" not in st.session_state:
|
| 16 |
+
st.session_state.last_results = None # most recent answers
|
| 17 |
+
if "rating_submitted" not in st.session_state:
|
| 18 |
+
st.session_state.rating_submitted = False
|
| 19 |
+
|
| 20 |
+
# ── Custom CSS ────────────────────────────────────────────────────────────────
|
| 21 |
+
st.markdown("""
|
| 22 |
+
<style>
|
| 23 |
+
/* System 1 — Blue */
|
| 24 |
+
.card-blue {
|
| 25 |
+
background: linear-gradient(135deg, #0d1f3c, #102a4c);
|
| 26 |
+
border: 1px solid #2563eb;
|
| 27 |
+
border-top: 4px solid #3b82f6;
|
| 28 |
+
border-radius: 12px;
|
| 29 |
+
padding: 20px;
|
| 30 |
+
margin-top: 10px;
|
| 31 |
+
min-height: 180px;
|
| 32 |
+
}
|
| 33 |
+
.card-blue .answer { color: #93c5fd; font-size: 15px; line-height: 1.7; }
|
| 34 |
+
.card-blue .time { color: #60a5fa; font-size: 13px; margin-top: 12px; }
|
| 35 |
+
|
| 36 |
+
/* System 2 — Teal/Green */
|
| 37 |
+
.card-teal {
|
| 38 |
+
background: linear-gradient(135deg, #052e1c, #063d24);
|
| 39 |
+
border: 1px solid #059669;
|
| 40 |
+
border-top: 4px solid #10b981;
|
| 41 |
+
border-radius: 12px;
|
| 42 |
+
padding: 20px;
|
| 43 |
+
margin-top: 10px;
|
| 44 |
+
min-height: 180px;
|
| 45 |
+
}
|
| 46 |
+
.card-teal .answer { color: #6ee7b7; font-size: 15px; line-height: 1.7; }
|
| 47 |
+
.card-teal .time { color: #34d399; font-size: 13px; margin-top: 12px; }
|
| 48 |
+
.card-teal .context { background: #063d24; border: 1px solid #065f46; border-radius: 8px;
|
| 49 |
+
padding: 12px; margin-top: 14px; color: #a7f3d0;
|
| 50 |
+
font-size: 13px; line-height: 1.6; }
|
| 51 |
+
.card-teal .ctx-label { color: #6ee7b7; font-size: 12px; font-weight: 600;
|
| 52 |
+
margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.5px; }
|
| 53 |
+
|
| 54 |
+
/* System 3 — Purple */
|
| 55 |
+
.card-purple {
|
| 56 |
+
background: linear-gradient(135deg, #1a0a2e, #220d3b);
|
| 57 |
+
border: 1px solid #7c3aed;
|
| 58 |
+
border-top: 4px solid #8b5cf6;
|
| 59 |
+
border-radius: 12px;
|
| 60 |
+
padding: 20px;
|
| 61 |
+
margin-top: 10px;
|
| 62 |
+
min-height: 180px;
|
| 63 |
+
}
|
| 64 |
+
.card-purple .answer { color: #c4b5fd; font-size: 15px; line-height: 1.7; }
|
| 65 |
+
.card-purple .time { color: #a78bfa; font-size: 13px; margin-top: 12px; }
|
| 66 |
+
|
| 67 |
+
/* Section headers */
|
| 68 |
+
.sys-header { font-size: 18px; font-weight: 700; margin-bottom: 2px; }
|
| 69 |
+
.sys-blue { color: #3b82f6; }
|
| 70 |
+
.sys-teal { color: #10b981; }
|
| 71 |
+
.sys-purple { color: #8b5cf6; }
|
| 72 |
+
|
| 73 |
+
/* Rating box */
|
| 74 |
+
.rating-box {
|
| 75 |
+
background: #111827;
|
| 76 |
+
border: 1px solid #374151;
|
| 77 |
+
border-radius: 12px;
|
| 78 |
+
padding: 18px 20px;
|
| 79 |
+
margin-top: 8px;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/* Eval table */
|
| 83 |
+
.eval-table {
|
| 84 |
+
width: 100%;
|
| 85 |
+
border-collapse: collapse;
|
| 86 |
+
font-size: 14px;
|
| 87 |
+
margin-top: 10px;
|
| 88 |
+
}
|
| 89 |
+
.eval-table th {
|
| 90 |
+
padding: 12px 16px;
|
| 91 |
+
text-align: center;
|
| 92 |
+
font-weight: 700;
|
| 93 |
+
font-size: 15px;
|
| 94 |
+
border-bottom: 2px solid #374151;
|
| 95 |
+
}
|
| 96 |
+
.eval-table th:first-child { text-align: left; color: #9ca3af; }
|
| 97 |
+
.eval-table th.col-blue { color: #3b82f6; }
|
| 98 |
+
.eval-table th.col-teal { color: #10b981; }
|
| 99 |
+
.eval-table th.col-purple { color: #8b5cf6; }
|
| 100 |
+
.eval-table td {
|
| 101 |
+
padding: 11px 16px;
|
| 102 |
+
text-align: center;
|
| 103 |
+
border-bottom: 1px solid #1f2937;
|
| 104 |
+
color: #d1d5db;
|
| 105 |
+
}
|
| 106 |
+
.eval-table td:first-child { text-align: left; color: #9ca3af; font-weight: 600; }
|
| 107 |
+
.eval-table tr:hover td { background: #111827; }
|
| 108 |
+
.badge {
|
| 109 |
+
display: inline-block;
|
| 110 |
+
padding: 3px 10px;
|
| 111 |
+
border-radius: 12px;
|
| 112 |
+
font-weight: 700;
|
| 113 |
+
font-size: 13px;
|
| 114 |
+
}
|
| 115 |
+
.badge-green { background: #052e1c; color: #10b981; border: 1px solid #059669; }
|
| 116 |
+
.badge-yellow { background: #1c1a05; color: #f59e0b; border: 1px solid #d97706; }
|
| 117 |
+
.badge-red { background: #2e0505; color: #ef4444; border: 1px solid #dc2626; }
|
| 118 |
+
.badge-grey { background: #1f2937; color: #6b7280; border: 1px solid #374151; }
|
| 119 |
+
.metric-note { color: #6b7280; font-size: 12px; font-weight: 400; }
|
| 120 |
+
</style>
|
| 121 |
+
""", unsafe_allow_html=True)
|
| 122 |
+
|
| 123 |
+
# ── Title ─────────────────────────────────────────────────────────────────────
|
| 124 |
+
st.title("🧙 CODESAGE")
|
| 125 |
+
st.markdown(
|
| 126 |
+
"**Comparative Study — RAG vs Fine-Tuning for Domain-Specific QA** \n"
|
| 127 |
+
"Ask the same question to all 3 systems and compare their answers."
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
# ── Load vector store once ────────────────────────────────────────────────────
|
| 131 |
+
@st.cache_resource(show_spinner="Building knowledge base...")
|
| 132 |
+
def get_vectorstore():
|
| 133 |
+
if not os.path.exists("data/faiss_index"):
|
| 134 |
+
return build_vectorstore()
|
| 135 |
+
return load_vectorstore()
|
| 136 |
+
|
| 137 |
+
vs = get_vectorstore()
|
| 138 |
+
|
| 139 |
+
# ── Input ─────────────────────────────────────────────────────────────────────
|
| 140 |
+
st.divider()
|
| 141 |
+
question = st.text_input(
|
| 142 |
+
"Enter your question:",
|
| 143 |
+
placeholder="e.g. What is binary search? / What are React hooks? / Explain merge sort."
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
sample_questions = [
|
| 147 |
+
"What is binary search?",
|
| 148 |
+
"What is the difference between stack and queue?",
|
| 149 |
+
"Explain merge sort.",
|
| 150 |
+
"What are React hooks?",
|
| 151 |
+
"What is a REST API?",
|
| 152 |
+
"What is dynamic programming?",
|
| 153 |
+
]
|
| 154 |
+
|
| 155 |
+
st.markdown("**Quick questions:**")
|
| 156 |
+
cols = st.columns(len(sample_questions))
|
| 157 |
+
for i, sq in enumerate(sample_questions):
|
| 158 |
+
if cols[i].button(sq, key=f"sq_{i}"):
|
| 159 |
+
question = sq
|
| 160 |
+
|
| 161 |
+
# ── Run systems ───────────────────────────────────────────────────────────────
|
| 162 |
+
if st.button("Ask All 3 Systems", type="primary") and question:
|
| 163 |
+
st.session_state.rating_submitted = False # reset for new question
|
| 164 |
+
col1, col2, col3 = st.columns(3)
|
| 165 |
+
|
| 166 |
+
with col1:
|
| 167 |
+
st.markdown('<p class="sys-header sys-blue">System 1 — Baseline LLM</p>', unsafe_allow_html=True)
|
| 168 |
+
st.caption("Prompt only. No extra knowledge.")
|
| 169 |
+
with st.spinner("Generating..."):
|
| 170 |
+
r1 = ask_baseline(question)
|
| 171 |
+
|
| 172 |
+
with col2:
|
| 173 |
+
st.markdown('<p class="sys-header sys-teal">System 2 — RAG Chatbot</p>', unsafe_allow_html=True)
|
| 174 |
+
st.caption("Retrieves from knowledge base, then generates.")
|
| 175 |
+
with st.spinner("Retrieving + generating..."):
|
| 176 |
+
r2 = ask_rag(question, vs)
|
| 177 |
+
|
| 178 |
+
with col3:
|
| 179 |
+
st.markdown('<p class="sys-header sys-purple">System 3 — Fine-Tuned Model</p>', unsafe_allow_html=True)
|
| 180 |
+
st.caption("TinyLlama fine-tuned on programming Q&A via LoRA.")
|
| 181 |
+
if os.path.exists("./finetuned_model"):
|
| 182 |
+
with st.spinner("Generating..."):
|
| 183 |
+
from system3_inference import ask_finetuned
|
| 184 |
+
r3 = ask_finetuned(question)
|
| 185 |
+
else:
|
| 186 |
+
r3 = {"answer": "Model not available.", "response_time": 0}
|
| 187 |
+
|
| 188 |
+
# persist results so they survive reruns
|
| 189 |
+
st.session_state.last_results = {
|
| 190 |
+
"question": question,
|
| 191 |
+
"r1": r1, "r2": r2, "r3": r3,
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
# ── Always render answers if they exist ───────────────────────────────────────
|
| 195 |
+
if st.session_state.last_results:
|
| 196 |
+
res = st.session_state.last_results
|
| 197 |
+
r1, r2, r3 = res["r1"], res["r2"], res["r3"]
|
| 198 |
+
|
| 199 |
+
st.divider()
|
| 200 |
+
col1, col2, col3 = st.columns(3)
|
| 201 |
+
|
| 202 |
+
with col1:
|
| 203 |
+
st.markdown('<p class="sys-header sys-blue">System 1 — Baseline LLM</p>', unsafe_allow_html=True)
|
| 204 |
+
st.caption("Prompt only. No extra knowledge.")
|
| 205 |
+
st.markdown(f"""
|
| 206 |
+
<div class="card-blue">
|
| 207 |
+
<div class="answer">{r1['answer']}</div>
|
| 208 |
+
<div class="time">⏱️ Response Time: {r1['response_time']}s</div>
|
| 209 |
+
</div>
|
| 210 |
+
""", unsafe_allow_html=True)
|
| 211 |
+
|
| 212 |
+
with col2:
|
| 213 |
+
st.markdown('<p class="sys-header sys-teal">System 2 — RAG Chatbot</p>', unsafe_allow_html=True)
|
| 214 |
+
st.caption("Retrieves from knowledge base, then generates.")
|
| 215 |
+
st.markdown(f"""
|
| 216 |
+
<div class="card-teal">
|
| 217 |
+
<div class="answer">{r2['answer']}</div>
|
| 218 |
+
<div class="time">⏱️ Response Time: {r2['response_time']}s</div>
|
| 219 |
+
<div class="context">
|
| 220 |
+
<div class="ctx-label">📄 Context retrieved from knowledge base</div>
|
| 221 |
+
{r2['context_used']}
|
| 222 |
+
</div>
|
| 223 |
+
</div>
|
| 224 |
+
""", unsafe_allow_html=True)
|
| 225 |
+
|
| 226 |
+
with col3:
|
| 227 |
+
st.markdown('<p class="sys-header sys-purple">System 3 — Fine-Tuned Model</p>', unsafe_allow_html=True)
|
| 228 |
+
st.caption("TinyLlama fine-tuned on programming Q&A via LoRA.")
|
| 229 |
+
if r3["answer"] == "Model not available.":
|
| 230 |
+
st.warning(
|
| 231 |
+
"Fine-tuned model not ready yet. \n"
|
| 232 |
+
"Run `system3_finetune_colab.ipynb` on Google Colab, "
|
| 233 |
+
"download `finetuned_model.zip`, and unzip it here."
|
| 234 |
+
)
|
| 235 |
+
else:
|
| 236 |
+
st.markdown(f"""
|
| 237 |
+
<div class="card-purple">
|
| 238 |
+
<div class="answer">{r3['answer']}</div>
|
| 239 |
+
<div class="time">⏱️ Response Time: {r3['response_time']}s</div>
|
| 240 |
+
</div>
|
| 241 |
+
""", unsafe_allow_html=True)
|
| 242 |
+
|
| 243 |
+
# ── Rating section ─────────────────────────────────────────────────────────────
|
| 244 |
+
if st.session_state.last_results and not st.session_state.rating_submitted:
|
| 245 |
+
st.divider()
|
| 246 |
+
st.subheader("Rate the Answers")
|
| 247 |
+
st.caption("Your ratings are used to build the live evaluation table below.")
|
| 248 |
+
|
| 249 |
+
rc1, rc2, rc3 = st.columns(3)
|
| 250 |
+
|
| 251 |
+
with rc1:
|
| 252 |
+
st.markdown("**System 1 — Baseline LLM**")
|
| 253 |
+
c1_correct = st.slider("Correctness (1–5)", 1, 5, 3, key="c1_correct")
|
| 254 |
+
c1_halluc = st.checkbox("Hallucination detected?", key="c1_halluc")
|
| 255 |
+
c1_complete= st.slider("Completeness (1–5)", 1, 5, 3, key="c1_complete")
|
| 256 |
+
|
| 257 |
+
with rc2:
|
| 258 |
+
st.markdown("**System 2 — RAG Chatbot**")
|
| 259 |
+
c2_correct = st.slider("Correctness (1–5)", 1, 5, 3, key="c2_correct")
|
| 260 |
+
c2_halluc = st.checkbox("Hallucination detected?", key="c2_halluc")
|
| 261 |
+
c2_complete= st.slider("Completeness (1–5)", 1, 5, 3, key="c2_complete")
|
| 262 |
+
|
| 263 |
+
with rc3:
|
| 264 |
+
st.markdown("**System 3 — Fine-Tuned**")
|
| 265 |
+
c3_correct = st.slider("Correctness (1–5)", 1, 5, 3, key="c3_correct")
|
| 266 |
+
c3_halluc = st.checkbox("Hallucination detected?", key="c3_halluc")
|
| 267 |
+
c3_complete= st.slider("Completeness (1–5)", 1, 5, 3, key="c3_complete")
|
| 268 |
+
|
| 269 |
+
if st.button("Submit Ratings", type="primary"):
|
| 270 |
+
res = st.session_state.last_results
|
| 271 |
+
st.session_state.ratings.append({
|
| 272 |
+
"question": res["question"],
|
| 273 |
+
# correctness
|
| 274 |
+
"c1_correct": c1_correct,
|
| 275 |
+
"c2_correct": c2_correct,
|
| 276 |
+
"c3_correct": c3_correct,
|
| 277 |
+
# hallucination (1 = yes, 0 = no)
|
| 278 |
+
"c1_halluc": 1 if c1_halluc else 0,
|
| 279 |
+
"c2_halluc": 1 if c2_halluc else 0,
|
| 280 |
+
"c3_halluc": 1 if c3_halluc else 0,
|
| 281 |
+
# completeness
|
| 282 |
+
"c1_complete": c1_complete,
|
| 283 |
+
"c2_complete": c2_complete,
|
| 284 |
+
"c3_complete": c3_complete,
|
| 285 |
+
# response times (auto)
|
| 286 |
+
"c1_time": res["r1"]["response_time"],
|
| 287 |
+
"c2_time": res["r2"]["response_time"],
|
| 288 |
+
"c3_time": res["r3"]["response_time"],
|
| 289 |
+
})
|
| 290 |
+
st.session_state.rating_submitted = True
|
| 291 |
+
st.success(f"Rating saved! Total questions rated: {len(st.session_state.ratings)}")
|
| 292 |
+
st.rerun()
|
| 293 |
+
|
| 294 |
+
# ── Metrics table (live) ───────────────────────────────────────────────────────
|
| 295 |
+
st.divider()
|
| 296 |
+
st.subheader("Evaluation Metrics Comparison")
|
| 297 |
+
|
| 298 |
+
ratings = st.session_state.ratings
|
| 299 |
+
n = len(ratings)
|
| 300 |
+
|
| 301 |
+
if n == 0:
|
| 302 |
+
st.caption("No ratings yet. Ask a question and rate the answers to populate this table.")
|
| 303 |
+
|
| 304 |
+
# show placeholder dashes
|
| 305 |
+
def badge(val, _n):
|
| 306 |
+
return f'<span class="badge badge-grey">—</span>'
|
| 307 |
+
else:
|
| 308 |
+
st.caption(f"Based on **{n} question(s)** rated by you. Response time is measured automatically.")
|
| 309 |
+
|
| 310 |
+
def avg(key):
|
| 311 |
+
return round(sum(r[key] for r in ratings) / n, 2)
|
| 312 |
+
|
| 313 |
+
def pct(key):
|
| 314 |
+
return round(sum(r[key] for r in ratings) / n * 100)
|
| 315 |
+
|
| 316 |
+
def color_correct(v):
|
| 317 |
+
if v >= 4.0: return "badge-green"
|
| 318 |
+
if v >= 3.0: return "badge-yellow"
|
| 319 |
+
return "badge-red"
|
| 320 |
+
|
| 321 |
+
def color_halluc(v): # lower = better
|
| 322 |
+
if v <= 15: return "badge-green"
|
| 323 |
+
if v <= 30: return "badge-yellow"
|
| 324 |
+
return "badge-red"
|
| 325 |
+
|
| 326 |
+
def color_complete(v):
|
| 327 |
+
if v >= 4.0: return "badge-green"
|
| 328 |
+
if v >= 3.0: return "badge-yellow"
|
| 329 |
+
return "badge-red"
|
| 330 |
+
|
| 331 |
+
def color_time(v): # lower = better
|
| 332 |
+
if v <= 1.0: return "badge-green"
|
| 333 |
+
if v <= 2.0: return "badge-yellow"
|
| 334 |
+
return "badge-red"
|
| 335 |
+
|
| 336 |
+
v_c1 = avg("c1_correct"); v_c2 = avg("c2_correct"); v_c3 = avg("c3_correct")
|
| 337 |
+
v_h1 = pct("c1_halluc"); v_h2 = pct("c2_halluc"); v_h3 = pct("c3_halluc")
|
| 338 |
+
v_p1 = avg("c1_complete"); v_p2 = avg("c2_complete"); v_p3 = avg("c3_complete")
|
| 339 |
+
v_t1 = avg("c1_time"); v_t2 = avg("c2_time"); v_t3 = avg("c3_time")
|
| 340 |
+
|
| 341 |
+
def badge(val, cls):
|
| 342 |
+
return f'<span class="badge {cls}">{val}</span>'
|
| 343 |
+
|
| 344 |
+
row_correct = (badge(f"{v_c1}/5", color_correct(v_c1)),
|
| 345 |
+
badge(f"{v_c2}/5", color_correct(v_c2)),
|
| 346 |
+
badge(f"{v_c3}/5", color_correct(v_c3)))
|
| 347 |
+
row_halluc = (badge(f"{v_h1}%", color_halluc(v_h1)),
|
| 348 |
+
badge(f"{v_h2}%", color_halluc(v_h2)),
|
| 349 |
+
badge(f"{v_h3}%", color_halluc(v_h3)))
|
| 350 |
+
row_complete = (badge(f"{v_p1}/5", color_complete(v_p1)),
|
| 351 |
+
badge(f"{v_p2}/5", color_complete(v_p2)),
|
| 352 |
+
badge(f"{v_p3}/5", color_complete(v_p3)))
|
| 353 |
+
row_time = (badge(f"{v_t1}s", color_time(v_t1)),
|
| 354 |
+
badge(f"{v_t2}s", color_time(v_t2)),
|
| 355 |
+
badge(f"{v_t3}s", color_time(v_t3)))
|
| 356 |
+
|
| 357 |
+
if n == 0:
|
| 358 |
+
dash = '<span class="badge badge-grey">—</span>'
|
| 359 |
+
row_correct = row_halluc = row_complete = row_time = (dash, dash, dash)
|
| 360 |
+
|
| 361 |
+
st.markdown(f"""
|
| 362 |
+
<table class="eval-table">
|
| 363 |
+
<thead>
|
| 364 |
+
<tr>
|
| 365 |
+
<th>Metric</th>
|
| 366 |
+
<th class="col-blue">System 1<br><span style="font-weight:400;font-size:12px">Baseline LLM</span></th>
|
| 367 |
+
<th class="col-teal">System 2<br><span style="font-weight:400;font-size:12px">RAG Chatbot</span></th>
|
| 368 |
+
<th class="col-purple">System 3<br><span style="font-weight:400;font-size:12px">Fine-Tuned</span></th>
|
| 369 |
+
</tr>
|
| 370 |
+
</thead>
|
| 371 |
+
<tbody>
|
| 372 |
+
<tr>
|
| 373 |
+
<td>Correctness Score <span class="metric-note">(1–5, user rated)</span></td>
|
| 374 |
+
<td>{row_correct[0]}</td><td>{row_correct[1]}</td><td>{row_correct[2]}</td>
|
| 375 |
+
</tr>
|
| 376 |
+
<tr>
|
| 377 |
+
<td>Hallucination Rate <span class="metric-note">(%, user rated)</span></td>
|
| 378 |
+
<td>{row_halluc[0]}</td><td>{row_halluc[1]}</td><td>{row_halluc[2]}</td>
|
| 379 |
+
</tr>
|
| 380 |
+
<tr>
|
| 381 |
+
<td>Answer Completeness <span class="metric-note">(1–5, user rated)</span></td>
|
| 382 |
+
<td>{row_complete[0]}</td><td>{row_complete[1]}</td><td>{row_complete[2]}</td>
|
| 383 |
+
</tr>
|
| 384 |
+
<tr>
|
| 385 |
+
<td>Avg Response Time <span class="metric-note">(seconds, auto)</span></td>
|
| 386 |
+
<td>{row_time[0]}</td><td>{row_time[1]}</td><td>{row_time[2]}</td>
|
| 387 |
+
</tr>
|
| 388 |
+
</tbody>
|
| 389 |
+
</table>
|
| 390 |
+
""", unsafe_allow_html=True)
|
| 391 |
+
|
| 392 |
+
if n > 0:
|
| 393 |
+
if st.button("Clear All Ratings"):
|
| 394 |
+
st.session_state.ratings = []
|
| 395 |
+
st.rerun()
|
| 396 |
+
|
| 397 |
+
# ── About ──────────────────────────────────────────────────────────────────────
|
| 398 |
+
st.divider()
|
| 399 |
+
with st.expander("About CodeSage"):
|
| 400 |
+
st.markdown("""
|
| 401 |
+
**Project:** CodeSage — Comparative Study of RAG and Fine-Tuning for Domain-Specific Question Answering
|
| 402 |
+
**Domain:** Programming Tutor (DSA + Web Development)
|
| 403 |
+
|
| 404 |
+
| System | Description |
|
| 405 |
+
|--------|-------------|
|
| 406 |
+
| Baseline | Llama 3.1 8B (via Groq) with only a system prompt |
|
| 407 |
+
| RAG | FAISS vector store + HuggingFace embeddings + Llama 3.1 8B (via Groq) |
|
| 408 |
+
| Fine-tuned | TinyLlama 1.1B fine-tuned via LoRA on 20 programming Q&A pairs |
|
| 409 |
+
""")
|
evaluate.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Runs all 3 systems on the test questions and saves results to results/evaluation_results.csv.
|
| 3 |
+
After running, open the CSV and manually fill in:
|
| 4 |
+
- correctness_score (1-5): 1=wrong, 3=partial, 5=perfect
|
| 5 |
+
- hallucination (Yes/No): Yes if the answer contains false information
|
| 6 |
+
"""
|
| 7 |
+
import csv
|
| 8 |
+
import os
|
| 9 |
+
import time
|
| 10 |
+
|
| 11 |
+
from system1_baseline import ask_baseline
|
| 12 |
+
from system2_rag import ask_rag, load_vectorstore, build_vectorstore
|
| 13 |
+
|
| 14 |
+
QUESTIONS_PATH = "data/questions.csv"
|
| 15 |
+
RESULTS_PATH = "results/evaluation_results.csv"
|
| 16 |
+
NUM_QUESTIONS = 30 # increase to 50 when ready
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def load_questions():
|
| 20 |
+
questions = []
|
| 21 |
+
with open(QUESTIONS_PATH, encoding="utf-8") as f:
|
| 22 |
+
reader = csv.DictReader(f)
|
| 23 |
+
for row in reader:
|
| 24 |
+
questions.append(row["question"])
|
| 25 |
+
return questions[:NUM_QUESTIONS]
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def run_evaluation():
|
| 29 |
+
os.makedirs("results", exist_ok=True)
|
| 30 |
+
|
| 31 |
+
# Load RAG vectorstore
|
| 32 |
+
if not os.path.exists("data/faiss_index"):
|
| 33 |
+
print("Building FAISS index first...")
|
| 34 |
+
vs = build_vectorstore()
|
| 35 |
+
else:
|
| 36 |
+
print("Loading existing FAISS index...")
|
| 37 |
+
vs = load_vectorstore()
|
| 38 |
+
|
| 39 |
+
questions = load_questions()
|
| 40 |
+
print(f"\nEvaluating {len(questions)} questions across 3 systems...\n")
|
| 41 |
+
|
| 42 |
+
results = []
|
| 43 |
+
|
| 44 |
+
for i, q in enumerate(questions, 1):
|
| 45 |
+
print(f"[{i}/{len(questions)}] {q}")
|
| 46 |
+
|
| 47 |
+
r1 = ask_baseline(q)
|
| 48 |
+
time.sleep(0.5) # avoid rate limiting
|
| 49 |
+
r2 = ask_rag(q, vs)
|
| 50 |
+
time.sleep(0.5)
|
| 51 |
+
|
| 52 |
+
# System 3 — placeholder until fine-tuned model is ready
|
| 53 |
+
try:
|
| 54 |
+
from system3_inference import ask_finetuned
|
| 55 |
+
r3 = ask_finetuned(q)
|
| 56 |
+
except Exception:
|
| 57 |
+
r3 = {"answer": "Model not available yet.", "response_time": 0}
|
| 58 |
+
|
| 59 |
+
results.append({
|
| 60 |
+
"id": i,
|
| 61 |
+
"question": q,
|
| 62 |
+
# Baseline
|
| 63 |
+
"baseline_answer": r1["answer"],
|
| 64 |
+
"baseline_time": r1["response_time"],
|
| 65 |
+
"baseline_correctness": "", # Fill manually: 1-5
|
| 66 |
+
"baseline_hallucination": "", # Fill manually: Yes/No
|
| 67 |
+
# RAG
|
| 68 |
+
"rag_answer": r2["answer"],
|
| 69 |
+
"rag_time": r2["response_time"],
|
| 70 |
+
"rag_correctness": "",
|
| 71 |
+
"rag_hallucination": "",
|
| 72 |
+
# Fine-tuned
|
| 73 |
+
"finetuned_answer": r3["answer"],
|
| 74 |
+
"finetuned_time": r3["response_time"],
|
| 75 |
+
"finetuned_correctness": "",
|
| 76 |
+
"finetuned_hallucination": "",
|
| 77 |
+
})
|
| 78 |
+
|
| 79 |
+
print(f" Baseline ({r1['response_time']}s): {r1['answer'][:70]}...")
|
| 80 |
+
print(f" RAG ({r2['response_time']}s): {r2['answer'][:70]}...")
|
| 81 |
+
print()
|
| 82 |
+
|
| 83 |
+
# Save CSV
|
| 84 |
+
with open(RESULTS_PATH, "w", newline="", encoding="utf-8") as f:
|
| 85 |
+
writer = csv.DictWriter(f, fieldnames=results[0].keys())
|
| 86 |
+
writer.writeheader()
|
| 87 |
+
writer.writerows(results)
|
| 88 |
+
|
| 89 |
+
print(f"Saved {len(results)} results to {RESULTS_PATH}")
|
| 90 |
+
print("\nNext step: open the CSV and fill in 'correctness' (1-5) and 'hallucination' (Yes/No) columns manually.")
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def print_summary():
|
| 94 |
+
"""Print average scores after you have manually filled in the CSV."""
|
| 95 |
+
if not os.path.exists(RESULTS_PATH):
|
| 96 |
+
print("No results file found. Run evaluate.py first.")
|
| 97 |
+
return
|
| 98 |
+
|
| 99 |
+
baseline_scores, rag_scores, ft_scores = [], [], []
|
| 100 |
+
baseline_hall, rag_hall, ft_hall = [], [], []
|
| 101 |
+
baseline_times, rag_times, ft_times = [], [], []
|
| 102 |
+
|
| 103 |
+
with open(RESULTS_PATH, encoding="utf-8") as f:
|
| 104 |
+
reader = csv.DictReader(f)
|
| 105 |
+
for row in reader:
|
| 106 |
+
if row["baseline_correctness"]:
|
| 107 |
+
baseline_scores.append(float(row["baseline_correctness"]))
|
| 108 |
+
baseline_hall.append(1 if row["baseline_hallucination"].strip().lower() == "yes" else 0)
|
| 109 |
+
baseline_times.append(float(row["baseline_time"]) if row["baseline_time"] else 0)
|
| 110 |
+
if row["rag_correctness"]:
|
| 111 |
+
rag_scores.append(float(row["rag_correctness"]))
|
| 112 |
+
rag_hall.append(1 if row["rag_hallucination"].strip().lower() == "yes" else 0)
|
| 113 |
+
rag_times.append(float(row["rag_time"]) if row["rag_time"] else 0)
|
| 114 |
+
if row["finetuned_correctness"]:
|
| 115 |
+
ft_scores.append(float(row["finetuned_correctness"]))
|
| 116 |
+
ft_hall.append(1 if row["finetuned_hallucination"].strip().lower() == "yes" else 0)
|
| 117 |
+
ft_times.append(float(row["finetuned_time"]) if row["finetuned_time"] else 0)
|
| 118 |
+
|
| 119 |
+
def avg(lst): return round(sum(lst) / len(lst), 2) if lst else "N/A"
|
| 120 |
+
def pct(lst): return f"{round(sum(lst)/len(lst)*100)}%" if lst else "N/A"
|
| 121 |
+
|
| 122 |
+
print("\n===== EVALUATION SUMMARY =====")
|
| 123 |
+
print(f"{'Metric':<30} {'Baseline':>12} {'RAG':>12} {'Fine-tuned':>12}")
|
| 124 |
+
print("-" * 66)
|
| 125 |
+
print(f"{'Avg Correctness (1-5)':<30} {avg(baseline_scores):>12} {avg(rag_scores):>12} {avg(ft_scores):>12}")
|
| 126 |
+
print(f"{'Hallucination Rate':<30} {pct(baseline_hall):>12} {pct(rag_hall):>12} {pct(ft_hall):>12}")
|
| 127 |
+
print(f"{'Avg Response Time (s)':<30} {avg(baseline_times):>12} {avg(rag_times):>12} {avg(ft_times):>12}")
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
if __name__ == "__main__":
|
| 131 |
+
import sys
|
| 132 |
+
if len(sys.argv) > 1 and sys.argv[1] == "summary":
|
| 133 |
+
print_summary()
|
| 134 |
+
else:
|
| 135 |
+
run_evaluation()
|
requirements.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
openai>=1.0.0
|
| 2 |
+
langchain>=0.2.0
|
| 3 |
+
langchain-community>=0.2.0
|
| 4 |
+
faiss-cpu>=1.7.4
|
| 5 |
+
sentence-transformers>=2.7.0
|
| 6 |
+
transformers>=4.40.0
|
| 7 |
+
datasets>=2.19.0
|
| 8 |
+
peft>=0.10.0
|
| 9 |
+
accelerate>=0.29.0
|
| 10 |
+
streamlit>=1.35.0
|
| 11 |
+
pandas>=2.0.0
|
| 12 |
+
python-dotenv>=1.0.0
|
| 13 |
+
tqdm>=4.66.0
|
system1_baseline.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import time
|
| 3 |
+
from openai import OpenAI
|
| 4 |
+
from dotenv import load_dotenv
|
| 5 |
+
|
| 6 |
+
load_dotenv()
|
| 7 |
+
|
| 8 |
+
client = OpenAI(
|
| 9 |
+
api_key=os.getenv("GROQ_API_KEY"),
|
| 10 |
+
base_url="https://api.groq.com/openai/v1",
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
MODEL = "llama-3.1-8b-instant"
|
| 14 |
+
|
| 15 |
+
SYSTEM_PROMPT = (
|
| 16 |
+
"You are a programming tutor specializing in Data Structures, Algorithms, "
|
| 17 |
+
"and Web Development. Answer questions clearly and concisely."
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def ask_baseline(question: str) -> dict:
|
| 22 |
+
start = time.time()
|
| 23 |
+
|
| 24 |
+
response = client.chat.completions.create(
|
| 25 |
+
model=MODEL,
|
| 26 |
+
messages=[
|
| 27 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
| 28 |
+
{"role": "user", "content": question},
|
| 29 |
+
],
|
| 30 |
+
max_tokens=300,
|
| 31 |
+
temperature=0.3,
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
elapsed = round(time.time() - start, 2)
|
| 35 |
+
answer = response.choices[0].message.content.strip()
|
| 36 |
+
|
| 37 |
+
return {
|
| 38 |
+
"system": "Baseline",
|
| 39 |
+
"question": question,
|
| 40 |
+
"answer": answer,
|
| 41 |
+
"response_time": elapsed,
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
if __name__ == "__main__":
|
| 46 |
+
test_q = "What is binary search?"
|
| 47 |
+
print(f"Question: {test_q}\n")
|
| 48 |
+
result = ask_baseline(test_q)
|
| 49 |
+
print(f"Answer:\n{result['answer']}")
|
| 50 |
+
print(f"\nResponse time: {result['response_time']}s")
|
system2_rag.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import time
|
| 3 |
+
from dotenv import load_dotenv
|
| 4 |
+
from langchain_community.document_loaders import DirectoryLoader, TextLoader
|
| 5 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 6 |
+
from langchain_community.vectorstores import FAISS
|
| 7 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
| 8 |
+
from openai import OpenAI
|
| 9 |
+
|
| 10 |
+
load_dotenv()
|
| 11 |
+
|
| 12 |
+
client = OpenAI(
|
| 13 |
+
api_key=os.getenv("GROQ_API_KEY"),
|
| 14 |
+
base_url="https://api.groq.com/openai/v1",
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
MODEL = "llama-3.1-8b-instant"
|
| 18 |
+
|
| 19 |
+
DOCS_PATH = "data/docs"
|
| 20 |
+
INDEX_PATH = "data/faiss_index"
|
| 21 |
+
EMBED_MODEL = "all-MiniLM-L6-v2"
|
| 22 |
+
|
| 23 |
+
SYSTEM_PROMPT = (
|
| 24 |
+
"You are a programming tutor. Use only the provided context to answer. "
|
| 25 |
+
"If the answer is not in the context, say 'I don't have that in my knowledge base.'"
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def build_vectorstore() -> FAISS:
|
| 30 |
+
print("Loading documents...")
|
| 31 |
+
loader = DirectoryLoader(DOCS_PATH, glob="**/*.txt", loader_cls=TextLoader,
|
| 32 |
+
loader_kwargs={"encoding": "utf-8"})
|
| 33 |
+
documents = loader.load()
|
| 34 |
+
print(f"Loaded {len(documents)} documents.")
|
| 35 |
+
|
| 36 |
+
splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=60)
|
| 37 |
+
chunks = splitter.split_documents(documents)
|
| 38 |
+
print(f"Split into {len(chunks)} chunks.")
|
| 39 |
+
|
| 40 |
+
embeddings = HuggingFaceEmbeddings(model_name=EMBED_MODEL)
|
| 41 |
+
vectorstore = FAISS.from_documents(chunks, embeddings)
|
| 42 |
+
vectorstore.save_local(INDEX_PATH)
|
| 43 |
+
print(f"Vector store saved to {INDEX_PATH}/")
|
| 44 |
+
return vectorstore
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def load_vectorstore() -> FAISS:
|
| 48 |
+
embeddings = HuggingFaceEmbeddings(model_name=EMBED_MODEL)
|
| 49 |
+
return FAISS.load_local(INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def ask_rag(question: str, vectorstore: FAISS) -> dict:
|
| 53 |
+
start = time.time()
|
| 54 |
+
|
| 55 |
+
# Retrieve top-3 relevant chunks
|
| 56 |
+
docs = vectorstore.similarity_search(question, k=3)
|
| 57 |
+
context = "\n\n".join([doc.page_content for doc in docs])
|
| 58 |
+
|
| 59 |
+
prompt = f"""Use the following context to answer the question accurately.
|
| 60 |
+
|
| 61 |
+
Context:
|
| 62 |
+
{context}
|
| 63 |
+
|
| 64 |
+
Question: {question}
|
| 65 |
+
Answer:"""
|
| 66 |
+
|
| 67 |
+
response = client.chat.completions.create(
|
| 68 |
+
model=MODEL,
|
| 69 |
+
messages=[
|
| 70 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
| 71 |
+
{"role": "user", "content": prompt},
|
| 72 |
+
],
|
| 73 |
+
max_tokens=300,
|
| 74 |
+
temperature=0.3,
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
elapsed = round(time.time() - start, 2)
|
| 78 |
+
answer = response.choices[0].message.content.strip()
|
| 79 |
+
|
| 80 |
+
return {
|
| 81 |
+
"system": "RAG",
|
| 82 |
+
"question": question,
|
| 83 |
+
"answer": answer,
|
| 84 |
+
"context_used": context,
|
| 85 |
+
"response_time": elapsed,
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
if __name__ == "__main__":
|
| 90 |
+
if not os.path.exists(INDEX_PATH):
|
| 91 |
+
vs = build_vectorstore()
|
| 92 |
+
else:
|
| 93 |
+
print("Loading existing vector store...")
|
| 94 |
+
vs = load_vectorstore()
|
| 95 |
+
|
| 96 |
+
test_q = "What is binary search?"
|
| 97 |
+
print(f"\nQuestion: {test_q}\n")
|
| 98 |
+
result = ask_rag(test_q, vs)
|
| 99 |
+
print(f"Answer:\n{result['answer']}")
|
| 100 |
+
print(f"\nContext used:\n{result['context_used'][:300]}...")
|
| 101 |
+
print(f"\nResponse time: {result['response_time']}s")
|
system3_finetune_colab.ipynb
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# System 3 — Fine-Tuning with LoRA\n",
|
| 8 |
+
"**Run this notebook on Google Colab with a T4 GPU.**\n",
|
| 9 |
+
"Runtime → Change runtime type → T4 GPU"
|
| 10 |
+
]
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"cell_type": "code",
|
| 14 |
+
"execution_count": null,
|
| 15 |
+
"metadata": {},
|
| 16 |
+
"outputs": [],
|
| 17 |
+
"source": [
|
| 18 |
+
"!pip install transformers datasets peft accelerate bitsandbytes -q"
|
| 19 |
+
]
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"cell_type": "code",
|
| 23 |
+
"execution_count": null,
|
| 24 |
+
"metadata": {},
|
| 25 |
+
"outputs": [],
|
| 26 |
+
"source": [
|
| 27 |
+
"# Upload finetune_data.jsonl from your data/ folder before running this cell\n",
|
| 28 |
+
"from google.colab import files\n",
|
| 29 |
+
"uploaded = files.upload() # select data/finetune_data.jsonl"
|
| 30 |
+
]
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"cell_type": "code",
|
| 34 |
+
"execution_count": null,
|
| 35 |
+
"metadata": {},
|
| 36 |
+
"outputs": [],
|
| 37 |
+
"source": [
|
| 38 |
+
"import json\n",
|
| 39 |
+
"import torch\n",
|
| 40 |
+
"from datasets import Dataset\n",
|
| 41 |
+
"from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForLanguageModeling\n",
|
| 42 |
+
"from peft import LoraConfig, get_peft_model, TaskType\n",
|
| 43 |
+
"\n",
|
| 44 |
+
"# Load dataset\n",
|
| 45 |
+
"data = []\n",
|
| 46 |
+
"with open('finetune_data.jsonl') as f:\n",
|
| 47 |
+
" for line in f:\n",
|
| 48 |
+
" data.append(json.loads(line))\n",
|
| 49 |
+
"\n",
|
| 50 |
+
"print(f'Loaded {len(data)} training examples')\n",
|
| 51 |
+
"\n",
|
| 52 |
+
"def format_prompt(example):\n",
|
| 53 |
+
" return f\"### Instruction:\\n{example['instruction']}\\n\\n### Response:\\n{example['output']}\"\n",
|
| 54 |
+
"\n",
|
| 55 |
+
"dataset = Dataset.from_list([{'text': format_prompt(d)} for d in data])\n",
|
| 56 |
+
"print(dataset[0]['text'][:200])"
|
| 57 |
+
]
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"cell_type": "code",
|
| 61 |
+
"execution_count": null,
|
| 62 |
+
"metadata": {},
|
| 63 |
+
"outputs": [],
|
| 64 |
+
"source": [
|
| 65 |
+
"MODEL_NAME = 'TinyLlama/TinyLlama-1.1B-Chat-v1.0'\n",
|
| 66 |
+
"\n",
|
| 67 |
+
"tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\n",
|
| 68 |
+
"tokenizer.pad_token = tokenizer.eos_token\n",
|
| 69 |
+
"\n",
|
| 70 |
+
"model = AutoModelForCausalLM.from_pretrained(\n",
|
| 71 |
+
" MODEL_NAME,\n",
|
| 72 |
+
" torch_dtype=torch.float16,\n",
|
| 73 |
+
" device_map='auto'\n",
|
| 74 |
+
")\n",
|
| 75 |
+
"print('Model loaded.')"
|
| 76 |
+
]
|
| 77 |
+
},
|
| 78 |
+
{
|
| 79 |
+
"cell_type": "code",
|
| 80 |
+
"execution_count": null,
|
| 81 |
+
"metadata": {},
|
| 82 |
+
"outputs": [],
|
| 83 |
+
"source": [
|
| 84 |
+
"lora_config = LoraConfig(\n",
|
| 85 |
+
" task_type=TaskType.CAUSAL_LM,\n",
|
| 86 |
+
" r=8,\n",
|
| 87 |
+
" lora_alpha=32,\n",
|
| 88 |
+
" target_modules=['q_proj', 'v_proj'],\n",
|
| 89 |
+
" lora_dropout=0.05,\n",
|
| 90 |
+
")\n",
|
| 91 |
+
"model = get_peft_model(model, lora_config)\n",
|
| 92 |
+
"model.print_trainable_parameters()"
|
| 93 |
+
]
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"cell_type": "code",
|
| 97 |
+
"execution_count": null,
|
| 98 |
+
"metadata": {},
|
| 99 |
+
"outputs": [],
|
| 100 |
+
"source": [
|
| 101 |
+
"def tokenize(examples):\n",
|
| 102 |
+
" return tokenizer(examples['text'], truncation=True, max_length=512, padding='max_length')\n",
|
| 103 |
+
"\n",
|
| 104 |
+
"tokenized = dataset.map(tokenize, batched=True)\n",
|
| 105 |
+
"\n",
|
| 106 |
+
"training_args = TrainingArguments(\n",
|
| 107 |
+
" output_dir='./finetuned_model',\n",
|
| 108 |
+
" num_train_epochs=5,\n",
|
| 109 |
+
" per_device_train_batch_size=4,\n",
|
| 110 |
+
" save_steps=50,\n",
|
| 111 |
+
" logging_steps=10,\n",
|
| 112 |
+
" learning_rate=2e-4,\n",
|
| 113 |
+
" fp16=True,\n",
|
| 114 |
+
" report_to='none'\n",
|
| 115 |
+
")\n",
|
| 116 |
+
"\n",
|
| 117 |
+
"trainer = Trainer(\n",
|
| 118 |
+
" model=model,\n",
|
| 119 |
+
" args=training_args,\n",
|
| 120 |
+
" train_dataset=tokenized,\n",
|
| 121 |
+
" data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False)\n",
|
| 122 |
+
")\n",
|
| 123 |
+
"\n",
|
| 124 |
+
"trainer.train()\n",
|
| 125 |
+
"model.save_pretrained('./finetuned_model')\n",
|
| 126 |
+
"tokenizer.save_pretrained('./finetuned_model')\n",
|
| 127 |
+
"print('Fine-tuning complete!')"
|
| 128 |
+
]
|
| 129 |
+
},
|
| 130 |
+
{
|
| 131 |
+
"cell_type": "code",
|
| 132 |
+
"execution_count": null,
|
| 133 |
+
"metadata": {},
|
| 134 |
+
"outputs": [],
|
| 135 |
+
"source": [
|
| 136 |
+
"# Test the fine-tuned model\n",
|
| 137 |
+
"def ask_finetuned(question):\n",
|
| 138 |
+
" prompt = f'### Instruction:\\n{question}\\n\\n### Response:\\n'\n",
|
| 139 |
+
" inputs = tokenizer(prompt, return_tensors='pt').to(model.device)\n",
|
| 140 |
+
" with torch.no_grad():\n",
|
| 141 |
+
" outputs = model.generate(**inputs, max_new_tokens=200, temperature=0.7,\n",
|
| 142 |
+
" do_sample=True, pad_token_id=tokenizer.eos_token_id)\n",
|
| 143 |
+
" full = tokenizer.decode(outputs[0], skip_special_tokens=True)\n",
|
| 144 |
+
" return full.split('### Response:\\n')[-1].strip()\n",
|
| 145 |
+
"\n",
|
| 146 |
+
"print(ask_finetuned('What is binary search?'))"
|
| 147 |
+
]
|
| 148 |
+
},
|
| 149 |
+
{
|
| 150 |
+
"cell_type": "code",
|
| 151 |
+
"execution_count": null,
|
| 152 |
+
"metadata": {},
|
| 153 |
+
"outputs": [],
|
| 154 |
+
"source": [
|
| 155 |
+
"# Download the fine-tuned model\n",
|
| 156 |
+
"import shutil\n",
|
| 157 |
+
"shutil.make_archive('finetuned_model', 'zip', './finetuned_model')\n",
|
| 158 |
+
"from google.colab import files\n",
|
| 159 |
+
"files.download('finetuned_model.zip')"
|
| 160 |
+
]
|
| 161 |
+
}
|
| 162 |
+
],
|
| 163 |
+
"metadata": {
|
| 164 |
+
"kernelspec": {
|
| 165 |
+
"display_name": "Python 3",
|
| 166 |
+
"language": "python",
|
| 167 |
+
"name": "python3"
|
| 168 |
+
},
|
| 169 |
+
"language_info": {
|
| 170 |
+
"name": "python",
|
| 171 |
+
"version": "3.10.0"
|
| 172 |
+
}
|
| 173 |
+
},
|
| 174 |
+
"nbformat": 4,
|
| 175 |
+
"nbformat_minor": 5
|
| 176 |
+
}
|
system3_inference.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Run inference with the fine-tuned model.
|
| 3 |
+
After running system3_finetune_colab.ipynb on Google Colab:
|
| 4 |
+
1. Download finetuned_model.zip from Colab
|
| 5 |
+
2. Unzip into this project root as finetuned_model/
|
| 6 |
+
3. Then run this file
|
| 7 |
+
"""
|
| 8 |
+
import os
|
| 9 |
+
import time
|
| 10 |
+
import torch
|
| 11 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM
|
| 12 |
+
from peft import PeftModel
|
| 13 |
+
|
| 14 |
+
BASE_MODEL = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
|
| 15 |
+
FINETUNED_PATH = "./finetuned_model"
|
| 16 |
+
|
| 17 |
+
_model = None
|
| 18 |
+
_tokenizer = None
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def load_model():
|
| 22 |
+
global _model, _tokenizer
|
| 23 |
+
if _model is not None:
|
| 24 |
+
return _model, _tokenizer
|
| 25 |
+
|
| 26 |
+
print("Loading fine-tuned model (this takes ~30s first time)...")
|
| 27 |
+
_tokenizer = AutoTokenizer.from_pretrained(FINETUNED_PATH)
|
| 28 |
+
base = AutoModelForCausalLM.from_pretrained(BASE_MODEL, torch_dtype=torch.float32)
|
| 29 |
+
_model = PeftModel.from_pretrained(base, FINETUNED_PATH)
|
| 30 |
+
_model.eval()
|
| 31 |
+
print("Fine-tuned model ready.")
|
| 32 |
+
return _model, _tokenizer
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def ask_finetuned(question: str) -> dict:
|
| 36 |
+
if not os.path.exists(FINETUNED_PATH):
|
| 37 |
+
return {
|
| 38 |
+
"system": "Fine-tuned",
|
| 39 |
+
"question": question,
|
| 40 |
+
"answer": "Fine-tuned model not found. Run system3_finetune_colab.ipynb on Google Colab first, then unzip finetuned_model.zip here.",
|
| 41 |
+
"response_time": 0,
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
model, tokenizer = load_model()
|
| 45 |
+
start = time.time()
|
| 46 |
+
|
| 47 |
+
prompt = f"### Instruction:\n{question}\n\n### Response:\n"
|
| 48 |
+
inputs = tokenizer(prompt, return_tensors="pt")
|
| 49 |
+
|
| 50 |
+
with torch.no_grad():
|
| 51 |
+
outputs = model.generate(
|
| 52 |
+
**inputs,
|
| 53 |
+
max_new_tokens=100,
|
| 54 |
+
do_sample=False,
|
| 55 |
+
pad_token_id=tokenizer.eos_token_id,
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
full_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 59 |
+
answer = full_output.split("### Response:\n")[-1].strip()
|
| 60 |
+
elapsed = round(time.time() - start, 2)
|
| 61 |
+
|
| 62 |
+
return {
|
| 63 |
+
"system": "Fine-tuned",
|
| 64 |
+
"question": question,
|
| 65 |
+
"answer": answer,
|
| 66 |
+
"response_time": elapsed,
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
if __name__ == "__main__":
|
| 71 |
+
test_q = "What is binary search?"
|
| 72 |
+
print(f"Question: {test_q}\n")
|
| 73 |
+
result = ask_finetuned(test_q)
|
| 74 |
+
print(f"Answer:\n{result['answer']}")
|
| 75 |
+
print(f"\nResponse time: {result['response_time']}s")
|