Update tech_docs/python/Python_programming.md

This commit is contained in:
2024-06-27 06:11:45 +00:00
parent 56b78e4e4d
commit e8be312dd3

View File

@@ -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
Heres 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 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.