Update tech_docs/python/Python_programming.md
This commit is contained in:
@@ -1,3 +1,281 @@
|
|||||||
|
Sorting is a fundamental operation in computer science and programming, used to arrange the elements of a list or array in a particular order (ascending or descending). There are numerous sorting algorithms, each with its own characteristics and use cases. Below are some common sorting algorithms along with their time complexities and a brief description.
|
||||||
|
|
||||||
|
### Common Sorting Algorithms
|
||||||
|
|
||||||
|
1. **Bubble Sort**
|
||||||
|
- **Description**: Repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. The process is repeated until the list is sorted.
|
||||||
|
- **Time Complexity**:
|
||||||
|
- Worst-case: \(O(n^2)\)
|
||||||
|
- Best-case: \(O(n)\) (when the list is already sorted)
|
||||||
|
- **Space Complexity**: \(O(1)\)
|
||||||
|
- **Stability**: Yes
|
||||||
|
|
||||||
|
2. **Selection Sort**
|
||||||
|
- **Description**: Divides the list into a sorted and an unsorted region. Repeatedly selects the smallest (or largest) element from the unsorted region and moves it to the sorted region.
|
||||||
|
- **Time Complexity**:
|
||||||
|
- Worst-case: \(O(n^2)\)
|
||||||
|
- Best-case: \(O(n^2)\)
|
||||||
|
- **Space Complexity**: \(O(1)\)
|
||||||
|
- **Stability**: No
|
||||||
|
|
||||||
|
3. **Insertion Sort**
|
||||||
|
- **Description**: Builds the sorted array one element at a time by repeatedly picking the next element and inserting it into its correct position in the already sorted part.
|
||||||
|
- **Time Complexity**:
|
||||||
|
- Worst-case: \(O(n^2)\)
|
||||||
|
- Best-case: \(O(n)\) (when the list is already sorted)
|
||||||
|
- **Space Complexity**: \(O(1)\)
|
||||||
|
- **Stability**: Yes
|
||||||
|
|
||||||
|
4. **Merge Sort**
|
||||||
|
- **Description**: Divides the list into two halves, recursively sorts each half, and then merges the sorted halves back together.
|
||||||
|
- **Time Complexity**: \(O(n \log n)\)
|
||||||
|
- **Space Complexity**: \(O(n)\)
|
||||||
|
- **Stability**: Yes
|
||||||
|
|
||||||
|
5. **Quick Sort**
|
||||||
|
- **Description**: Selects a 'pivot' element, partitions the array around the pivot (elements less than the pivot to its left, elements greater than the pivot to its right), and recursively sorts the partitions.
|
||||||
|
- **Time Complexity**:
|
||||||
|
- Worst-case: \(O(n^2)\) (when the pivot is the smallest or largest element every time)
|
||||||
|
- Average-case: \(O(n \log n)\)
|
||||||
|
- **Space Complexity**: \(O(\log n)\)
|
||||||
|
- **Stability**: No
|
||||||
|
|
||||||
|
6. **Heap Sort**
|
||||||
|
- **Description**: Builds a binary heap from the list and repeatedly extracts the maximum element from the heap, reconstructing the heap each time.
|
||||||
|
- **Time Complexity**: \(O(n \log n)\)
|
||||||
|
- **Space Complexity**: \(O(1)\)
|
||||||
|
- **Stability**: No
|
||||||
|
|
||||||
|
7. **Counting Sort**
|
||||||
|
- **Description**: Assumes that the input consists of integers in a fixed range. Counts the occurrences of each integer and uses these counts to place the integers in the correct position.
|
||||||
|
- **Time Complexity**: \(O(n + k)\) where \(k\) is the range of the input
|
||||||
|
- **Space Complexity**: \(O(k)\)
|
||||||
|
- **Stability**: Yes
|
||||||
|
|
||||||
|
8. **Radix Sort**
|
||||||
|
- **Description**: Sorts the numbers by processing individual digits. Uses a stable sorting algorithm (like Counting Sort) as a subroutine to sort digits.
|
||||||
|
- **Time Complexity**: \(O(d(n + k))\) where \(d\) is the number of digits and \(k\) is the range of the digits
|
||||||
|
- **Space Complexity**: \(O(n + k)\)
|
||||||
|
- **Stability**: Yes
|
||||||
|
|
||||||
|
### Example: Quick Sort Implementation
|
||||||
|
|
||||||
|
Here’s an example of Quick Sort in JavaScript:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function quickSort(arr) {
|
||||||
|
if (arr.length <= 1) return arr;
|
||||||
|
|
||||||
|
const pivot = arr[Math.floor(arr.length / 2)];
|
||||||
|
const left = [];
|
||||||
|
const right = [];
|
||||||
|
const equal = [];
|
||||||
|
|
||||||
|
for (let element of arr) {
|
||||||
|
if (element < pivot) {
|
||||||
|
left.push(element);
|
||||||
|
} else if (element > pivot) {
|
||||||
|
right.push(element);
|
||||||
|
} else {
|
||||||
|
equal.push(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...quickSort(left), ...equal, ...quickSort(right)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage
|
||||||
|
const unsortedArray = [3, 6, 8, 10, 1, 2, 1];
|
||||||
|
const sortedArray = quickSort(unsortedArray);
|
||||||
|
console.log(sortedArray); // [1, 1, 2, 3, 6, 8, 10]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Choosing the Right Sorting Algorithm
|
||||||
|
|
||||||
|
- **Small Arrays**: For small arrays, simple algorithms like Insertion Sort can be very efficient.
|
||||||
|
- **Large Arrays**: For larger arrays, more efficient algorithms like Quick Sort, Merge Sort, or Heap Sort are preferable.
|
||||||
|
- **Memory Constraints**: If memory usage is a concern, use in-place sorting algorithms like Quick Sort or Heap Sort.
|
||||||
|
- **Stable Sort Requirement**: If you need a stable sort (where equal elements retain their original order), use Merge Sort, Insertion Sort, or a modified version of Quick Sort that ensures stability.
|
||||||
|
|
||||||
|
Sorting is a broad topic, and the best algorithm depends on the specific requirements and constraints of your problem.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Priority queues and hashmaps (or hash tables) are two fundamental data structures in computer science, each serving different purposes and having unique characteristics. Here's an overview of both:
|
||||||
|
|
||||||
|
### Priority Queue
|
||||||
|
|
||||||
|
A priority queue is a type of queue in which each element is associated with a priority. Elements are dequeued in order of their priority, rather than in the order they were enqueued. There are two main types of priority queues: max-priority queues and min-priority queues.
|
||||||
|
|
||||||
|
- **Max-priority queue**: The element with the highest priority is dequeued first.
|
||||||
|
- **Min-priority queue**: The element with the lowest priority is dequeued first.
|
||||||
|
|
||||||
|
#### Implementations
|
||||||
|
|
||||||
|
1. **Binary Heap**:
|
||||||
|
- **Min-Heap**: The root is the minimum element. Used for min-priority queues.
|
||||||
|
- **Max-Heap**: The root is the maximum element. Used for max-priority queues.
|
||||||
|
- Time Complexity:
|
||||||
|
- Insertion: \(O(\log n)\)
|
||||||
|
- Deletion (extract-min or extract-max): \(O(\log n)\)
|
||||||
|
- Peek (get-min or get-max): \(O(1)\)
|
||||||
|
|
||||||
|
2. **Fibonacci Heap**:
|
||||||
|
- More complex but can offer better amortized time complexities for some operations.
|
||||||
|
- Time Complexity (amortized):
|
||||||
|
- Insertion: \(O(1)\)
|
||||||
|
- Deletion: \(O(\log n)\)
|
||||||
|
- Decrease key: \(O(1)\)
|
||||||
|
|
||||||
|
3. **Binary Search Tree (BST)**:
|
||||||
|
- Can also be used but usually less efficient than binary heaps for priority queues.
|
||||||
|
|
||||||
|
#### Example Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class MinHeap {
|
||||||
|
constructor() {
|
||||||
|
this.heap = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(value) {
|
||||||
|
this.heap.push(value);
|
||||||
|
this.bubbleUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
bubbleUp() {
|
||||||
|
let index = this.heap.length - 1;
|
||||||
|
while (index > 0) {
|
||||||
|
let parentIndex = Math.floor((index - 1) / 2);
|
||||||
|
if (this.heap[parentIndex] <= this.heap[index]) break;
|
||||||
|
[this.heap[parentIndex], this.heap[index]] = [this.heap[index], this.heap[parentIndex]];
|
||||||
|
index = parentIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extractMin() {
|
||||||
|
const min = this.heap[0];
|
||||||
|
const end = this.heap.pop();
|
||||||
|
if (this.heap.length > 0) {
|
||||||
|
this.heap[0] = end;
|
||||||
|
this.sinkDown(0);
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
sinkDown(index) {
|
||||||
|
const length = this.heap.length;
|
||||||
|
const element = this.heap[index];
|
||||||
|
while (true) {
|
||||||
|
let leftChildIdx = 2 * index + 1;
|
||||||
|
let rightChildIdx = 2 * index + 2;
|
||||||
|
let swap = null;
|
||||||
|
|
||||||
|
if (leftChildIdx < length) {
|
||||||
|
if (this.heap[leftChildIdx] < element) swap = leftChildIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightChildIdx < length) {
|
||||||
|
if (
|
||||||
|
(swap === null && this.heap[rightChildIdx] < element) ||
|
||||||
|
(swap !== null && this.heap[rightChildIdx] < this.heap[leftChildIdx])
|
||||||
|
) {
|
||||||
|
swap = rightChildIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swap === null) break;
|
||||||
|
[this.heap[index], this.heap[swap]] = [this.heap[swap], this.heap[index]];
|
||||||
|
index = swap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage
|
||||||
|
const pq = new MinHeap();
|
||||||
|
pq.insert(10);
|
||||||
|
pq.insert(5);
|
||||||
|
pq.insert(20);
|
||||||
|
console.log(pq.extractMin()); // 5
|
||||||
|
console.log(pq.extractMin()); // 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### HashMap (Hash Table)
|
||||||
|
|
||||||
|
A hashmap, or hash table, is a data structure that implements an associative array, a structure that can map keys to values. It uses a hash function to compute an index into an array of buckets or slots, from which the desired value can be found.
|
||||||
|
|
||||||
|
#### Characteristics
|
||||||
|
|
||||||
|
- **Average Time Complexity**:
|
||||||
|
- Insertion: \(O(1)\)
|
||||||
|
- Deletion: \(O(1)\)
|
||||||
|
- Lookup: \(O(1)\)
|
||||||
|
- **Worst-case Time Complexity**:
|
||||||
|
- \(O(n)\) (if many keys hash to the same index)
|
||||||
|
|
||||||
|
#### Handling Collisions
|
||||||
|
|
||||||
|
1. **Chaining**: Store multiple elements at the same index using a linked list or another data structure.
|
||||||
|
2. **Open Addressing**: Find another open slot within the array itself using probing (linear probing, quadratic probing, double hashing).
|
||||||
|
|
||||||
|
#### Example Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class HashMap {
|
||||||
|
constructor(size = 53) {
|
||||||
|
this.keyMap = new Array(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
_hash(key) {
|
||||||
|
let total = 0;
|
||||||
|
let WEIRD_PRIME = 31;
|
||||||
|
for (let i = 0; i < Math.min(key.length, 100); i++) {
|
||||||
|
let char = key[i];
|
||||||
|
let value = char.charCodeAt(0) - 96;
|
||||||
|
total = (total * WEIRD_PRIME + value) % this.keyMap.length;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
let index = this._hash(key);
|
||||||
|
if (!this.keyMap[index]) {
|
||||||
|
this.keyMap[index] = [];
|
||||||
|
}
|
||||||
|
this.keyMap[index].push([key, value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
let index = this._hash(key);
|
||||||
|
if (this.keyMap[index]) {
|
||||||
|
for (let i = 0; i < this.keyMap[index].length; i++) {
|
||||||
|
if (this.keyMap[index][i][0] === key) {
|
||||||
|
return this.keyMap[index][i][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage
|
||||||
|
const hm = new HashMap();
|
||||||
|
hm.set("hello", "world");
|
||||||
|
hm.set("foo", "bar");
|
||||||
|
console.log(hm.get("hello")); // "world"
|
||||||
|
console.log(hm.get("foo")); // "bar"
|
||||||
|
console.log(hm.get("baz")); // undefined
|
||||||
|
```
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
- **Priority Queue**: Used when you need to process elements based on their priority. Implementations often use binary heaps for efficient operations.
|
||||||
|
- **HashMap (Hash Table)**: Used for fast key-value lookups. They provide average \(O(1)\) time complexity for insertion, deletion, and lookup operations, with different strategies to handle collisions.
|
||||||
|
|
||||||
|
Each data structure has its specific use cases and choosing the right one depends on the problem you are trying to solve.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Python Functions: A Comprehensive Guide
|
# Python Functions: A Comprehensive Guide
|
||||||
|
|
||||||
Python functions are the building blocks of Python programming, enabling code reusability, organization, and modularity. This guide explores Python functions, their syntax, and how to use them effectively.
|
Python functions are the building blocks of Python programming, enabling code reusability, organization, and modularity. This guide explores Python functions, their syntax, and how to use them effectively.
|
||||||
|
|||||||
Reference in New Issue
Block a user