kopia lustrzana https://github.com/animator/learn-python
Merge branch 'main' into binary_tree
commit
0c7260cce7
|
@ -0,0 +1,185 @@
|
||||||
|
# AVL Tree
|
||||||
|
|
||||||
|
In Data Structures and Algorithms, an **AVL Tree** is a self-balancing binary search tree (BST) where the difference between heights of left and right subtrees cannot be more than one for all nodes. It ensures that the tree remains balanced, providing efficient search, insertion, and deletion operations.
|
||||||
|
|
||||||
|
## Points to be Remembered
|
||||||
|
|
||||||
|
- **Balance Factor**: The difference in heights between the left and right subtrees of a node. It should be -1, 0, or +1 for all nodes in an AVL tree.
|
||||||
|
- **Rotations**: Tree rotations (left, right, left-right, right-left) are used to maintain the balance factor within the allowed range.
|
||||||
|
|
||||||
|
## Real Life Examples of AVL Trees
|
||||||
|
|
||||||
|
- **Databases**: AVL trees can be used to maintain large indexes for database tables, ensuring quick data retrieval.
|
||||||
|
- **File Systems**: Some file systems use AVL trees to keep track of free and used memory blocks.
|
||||||
|
|
||||||
|
## Applications of AVL Trees
|
||||||
|
|
||||||
|
AVL trees are used in various applications in Computer Science:
|
||||||
|
|
||||||
|
- **Database Indexing**
|
||||||
|
- **Memory Allocation**
|
||||||
|
- **Network Routing Algorithms**
|
||||||
|
|
||||||
|
Understanding these applications is essential for Software Development.
|
||||||
|
|
||||||
|
## Operations in AVL Tree
|
||||||
|
|
||||||
|
Key operations include:
|
||||||
|
|
||||||
|
- **INSERT**: Insert a new element into the AVL tree.
|
||||||
|
- **SEARCH**: Find the position of an element in the AVL tree.
|
||||||
|
- **DELETE**: Remove an element from the AVL tree.
|
||||||
|
|
||||||
|
## Implementing AVL Tree in Python
|
||||||
|
|
||||||
|
```python
|
||||||
|
class AVLTreeNode:
|
||||||
|
def __init__(self, key):
|
||||||
|
self.key = key
|
||||||
|
self.left = None
|
||||||
|
self.right = None
|
||||||
|
self.height = 1
|
||||||
|
|
||||||
|
class AVLTree:
|
||||||
|
def insert(self, root, key):
|
||||||
|
if not root:
|
||||||
|
return AVLTreeNode(key)
|
||||||
|
|
||||||
|
if key < root.key:
|
||||||
|
root.left = self.insert(root.left, key)
|
||||||
|
else:
|
||||||
|
root.right = self.insert(root.right, key)
|
||||||
|
|
||||||
|
root.height = 1 + max(self.getHeight(root.left), self.getHeight(root.right))
|
||||||
|
balance = self.getBalance(root)
|
||||||
|
|
||||||
|
if balance > 1 and key < root.left.key:
|
||||||
|
return self.rotateRight(root)
|
||||||
|
if balance < -1 and key > root.right.key:
|
||||||
|
return self.rotateLeft(root)
|
||||||
|
if balance > 1 and key > root.left.key:
|
||||||
|
root.left = self.rotateLeft(root.left)
|
||||||
|
return self.rotateRight(root)
|
||||||
|
if balance < -1 and key < root.right.key:
|
||||||
|
root.right = self.rotateRight(root.right)
|
||||||
|
return self.rotateLeft(root)
|
||||||
|
|
||||||
|
return root
|
||||||
|
|
||||||
|
def search(self, root, key):
|
||||||
|
if not root or root.key == key:
|
||||||
|
return root
|
||||||
|
|
||||||
|
if key < root.key:
|
||||||
|
return self.search(root.left, key)
|
||||||
|
|
||||||
|
return self.search(root.right, key)
|
||||||
|
|
||||||
|
def delete(self, root, key):
|
||||||
|
if not root:
|
||||||
|
return root
|
||||||
|
|
||||||
|
if key < root.key:
|
||||||
|
root.left = self.delete(root.left, key)
|
||||||
|
elif key > root.key:
|
||||||
|
root.right = self.delete(root.right, key)
|
||||||
|
else:
|
||||||
|
if root.left is None:
|
||||||
|
temp = root.right
|
||||||
|
root = None
|
||||||
|
return temp
|
||||||
|
elif root.right is None:
|
||||||
|
temp = root.left
|
||||||
|
root = None
|
||||||
|
return temp
|
||||||
|
|
||||||
|
temp = self.getMinValueNode(root.right)
|
||||||
|
root.key = temp.key
|
||||||
|
root.right = self.delete(root.right, temp.key)
|
||||||
|
|
||||||
|
if root is None:
|
||||||
|
return root
|
||||||
|
|
||||||
|
root.height = 1 + max(self.getHeight(root.left), self.getHeight(root.right))
|
||||||
|
balance = self.getBalance(root)
|
||||||
|
|
||||||
|
if balance > 1 and self.getBalance(root.left) >= 0:
|
||||||
|
return self.rotateRight(root)
|
||||||
|
if balance < -1 and self.getBalance(root.right) <= 0:
|
||||||
|
return self.rotateLeft(root)
|
||||||
|
if balance > 1 and self.getBalance(root.left) < 0:
|
||||||
|
root.left = self.rotateLeft(root.left)
|
||||||
|
return self.rotateRight(root)
|
||||||
|
if balance < -1 and self.getBalance(root.right) > 0:
|
||||||
|
root.right = self.rotateRight(root.right)
|
||||||
|
return self.rotateLeft(root)
|
||||||
|
|
||||||
|
return root
|
||||||
|
|
||||||
|
def rotateLeft(self, z):
|
||||||
|
y = z.right
|
||||||
|
T2 = y.left
|
||||||
|
y.left = z
|
||||||
|
z.right = T2
|
||||||
|
z.height = 1 + max(self.getHeight(z.left), self.getHeight(z.right))
|
||||||
|
y.height = 1 + max(self.getHeight(y.left), self.getHeight(y.right))
|
||||||
|
return y
|
||||||
|
|
||||||
|
def rotateRight(self, z):
|
||||||
|
y = z.left
|
||||||
|
T3 = y.right
|
||||||
|
y.right = z
|
||||||
|
z.left = T3
|
||||||
|
z.height = 1 + max(self.getHeight(z.left), self.getHeight(z.right))
|
||||||
|
y.height = 1 + max(self.getHeight(y.left), self.getHeight(y.right))
|
||||||
|
return y
|
||||||
|
|
||||||
|
def getHeight(self, root):
|
||||||
|
if not root:
|
||||||
|
return 0
|
||||||
|
return root.height
|
||||||
|
|
||||||
|
def getBalance(self, root):
|
||||||
|
if not root:
|
||||||
|
return 0
|
||||||
|
return self.getHeight(root.left) - self.getHeight(root.right)
|
||||||
|
|
||||||
|
def getMinValueNode(self, root):
|
||||||
|
if root is None or root.left is None:
|
||||||
|
return root
|
||||||
|
return self.getMinValueNode(root.left)
|
||||||
|
|
||||||
|
def preOrder(self, root):
|
||||||
|
if not root:
|
||||||
|
return
|
||||||
|
print(root.key, end=' ')
|
||||||
|
self.preOrder(root.left)
|
||||||
|
self.preOrder(root.right)
|
||||||
|
|
||||||
|
#Example usage
|
||||||
|
avl_tree = AVLTree()
|
||||||
|
root = None
|
||||||
|
|
||||||
|
root = avl_tree.insert(root, 10)
|
||||||
|
root = avl_tree.insert(root, 20)
|
||||||
|
root = avl_tree.insert(root, 30)
|
||||||
|
root = avl_tree.insert(root, 40)
|
||||||
|
root = avl_tree.insert(root, 50)
|
||||||
|
root = avl_tree.insert(root, 25)
|
||||||
|
|
||||||
|
print("Preorder traversal of the AVL tree is:")
|
||||||
|
avl_tree.preOrder(root)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Preorder traversal of the AVL tree is:
|
||||||
|
30 20 10 25 40 50
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complexity Analysis
|
||||||
|
|
||||||
|
- **Insertion**: O(logn). Inserting a node involves traversing the height of the tree, which is logarithmic due to the balancing property.
|
||||||
|
- **Search**: O(logn). Searching for a node involves traversing the height of the tree.
|
||||||
|
- **Deletion**: O(logn). Deleting a node involves traversing and potentially rebalancing the tree, maintaining the logarithmic height.
|
|
@ -51,10 +51,6 @@ print(f"The {n}th Fibonacci number is: {fibonacci(n)}.")
|
||||||
- **Time Complexity**: O(n) for both approaches
|
- **Time Complexity**: O(n) for both approaches
|
||||||
- **Space Complexity**: O(n) for the top-down approach (due to memoization), O(1) for the bottom-up approach
|
- **Space Complexity**: O(n) for the top-down approach (due to memoization), O(1) for the bottom-up approach
|
||||||
|
|
||||||
</br>
|
|
||||||
<hr>
|
|
||||||
</br>
|
|
||||||
|
|
||||||
# 2. Longest Common Subsequence
|
# 2. Longest Common Subsequence
|
||||||
|
|
||||||
The longest common subsequence (LCS) problem is to find the longest subsequence common to two sequences. A subsequence is a sequence that appears in the same relative order but not necessarily contiguous.
|
The longest common subsequence (LCS) problem is to find the longest subsequence common to two sequences. A subsequence is a sequence that appears in the same relative order but not necessarily contiguous.
|
||||||
|
@ -84,13 +80,33 @@ Y = "GXTXAYB"
|
||||||
print("Length of Longest Common Subsequence:", longest_common_subsequence(X, Y, len(X), len(Y)))
|
print("Length of Longest Common Subsequence:", longest_common_subsequence(X, Y, len(X), len(Y)))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Complexity Analysis
|
## Longest Common Subsequence Code in Python (Bottom-Up Approach)
|
||||||
- **Time Complexity**: O(m * n) for the top-down approach, where m and n are the lengths of the input sequences
|
|
||||||
- **Space Complexity**: O(m * n) for the memoization table
|
|
||||||
|
|
||||||
</br>
|
```python
|
||||||
<hr>
|
|
||||||
</br>
|
def longestCommonSubsequence(X, Y, m, n):
|
||||||
|
L = [[None]*(n+1) for i in range(m+1)]
|
||||||
|
for i in range(m+1):
|
||||||
|
for j in range(n+1):
|
||||||
|
if i == 0 or j == 0:
|
||||||
|
L[i][j] = 0
|
||||||
|
elif X[i-1] == Y[j-1]:
|
||||||
|
L[i][j] = L[i-1][j-1]+1
|
||||||
|
else:
|
||||||
|
L[i][j] = max(L[i-1][j], L[i][j-1])
|
||||||
|
return L[m][n]
|
||||||
|
|
||||||
|
|
||||||
|
S1 = "AGGTAB"
|
||||||
|
S2 = "GXTXAYB"
|
||||||
|
m = len(S1)
|
||||||
|
n = len(S2)
|
||||||
|
print("Length of LCS is", longestCommonSubsequence(S1, S2, m, n))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complexity Analysis
|
||||||
|
- **Time Complexity**: O(m * n) for both approaches, where m and n are the lengths of the input sequences
|
||||||
|
- **Space Complexity**: O(m * n) for the memoization table
|
||||||
|
|
||||||
# 3. 0-1 Knapsack Problem
|
# 3. 0-1 Knapsack Problem
|
||||||
|
|
||||||
|
@ -123,10 +139,98 @@ n = len(weights)
|
||||||
print("Maximum value that can be obtained:", knapsack(weights, values, capacity, n))
|
print("Maximum value that can be obtained:", knapsack(weights, values, capacity, n))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 0-1 Knapsack Problem Code in Python (Bottom-up Approach)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def knapSack(capacity, weights, values, n):
|
||||||
|
K = [[0 for x in range(capacity + 1)] for x in range(n + 1)]
|
||||||
|
for i in range(n + 1):
|
||||||
|
for w in range(capacity + 1):
|
||||||
|
if i == 0 or w == 0:
|
||||||
|
K[i][w] = 0
|
||||||
|
elif weights[i-1] <= w:
|
||||||
|
K[i][w] = max(values[i-1]
|
||||||
|
+ K[i-1][w-weights[i-1]],
|
||||||
|
K[i-1][w])
|
||||||
|
else:
|
||||||
|
K[i][w] = K[i-1][w]
|
||||||
|
|
||||||
|
return K[n][capacity]
|
||||||
|
|
||||||
|
values = [60, 100, 120]
|
||||||
|
weights = [10, 20, 30]
|
||||||
|
capacity = 50
|
||||||
|
n = len(weights)
|
||||||
|
print(knapSack(capacity, weights, values, n))
|
||||||
|
```
|
||||||
|
|
||||||
## Complexity Analysis
|
## Complexity Analysis
|
||||||
- **Time Complexity**: O(n * W) for the top-down approach, where n is the number of items and W is the capacity of the knapsack
|
- **Time Complexity**: O(n * W) for both approaches, where n is the number of items and W is the capacity of the knapsack
|
||||||
- **Space Complexity**: O(n * W) for the memoization table
|
- **Space Complexity**: O(n * W) for the memoization table
|
||||||
|
|
||||||
</br>
|
# 4. Longest Increasing Subsequence
|
||||||
<hr>
|
|
||||||
</br>
|
The Longest Increasing Subsequence (LIS) is a task is to find the longest subsequence that is strictly increasing, meaning each element in the subsequence is greater than the one before it. This subsequence must maintain the order of elements as they appear in the original sequence but does not need to be contiguous. The goal is to identify the subsequence with the maximum possible length.
|
||||||
|
|
||||||
|
**Algorithm Overview:**
|
||||||
|
- **Base cases:** If the sequence is empty, the LIS length is 0.
|
||||||
|
- **Memoization:** Store the results of previously computed subproblems to avoid redundant computations.
|
||||||
|
- **Recurrence relation:** Compute the LIS length by comparing characters of the sequences and making decisions based on their values.
|
||||||
|
|
||||||
|
## Longest Increasing Subsequence Code in Python (Top-Down Approach using Memoization)
|
||||||
|
|
||||||
|
```python
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def f(idx, prev_idx, n, a, dp):
|
||||||
|
if (idx == n):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if (dp[idx][prev_idx + 1] != -1):
|
||||||
|
return dp[idx][prev_idx + 1]
|
||||||
|
|
||||||
|
notTake = 0 + f(idx + 1, prev_idx, n, a, dp)
|
||||||
|
take = -sys.maxsize - 1
|
||||||
|
if (prev_idx == -1 or a[idx] > a[prev_idx]):
|
||||||
|
take = 1 + f(idx + 1, idx, n, a, dp)
|
||||||
|
|
||||||
|
dp[idx][prev_idx + 1] = max(take, notTake)
|
||||||
|
return dp[idx][prev_idx + 1]
|
||||||
|
|
||||||
|
def longestSubsequence(n, a):
|
||||||
|
|
||||||
|
dp = [[-1 for i in range(n + 1)]for j in range(n + 1)]
|
||||||
|
return f(0, -1, n, a, dp)
|
||||||
|
|
||||||
|
a = [3, 10, 2, 1, 20]
|
||||||
|
n = len(a)
|
||||||
|
|
||||||
|
print("Length of lis is", longestSubsequence(n, a))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Longest Increasing Subsequence Code in Python (Bottom-Up Approach)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def lis(arr):
|
||||||
|
n = len(arr)
|
||||||
|
lis = [1]*n
|
||||||
|
|
||||||
|
for i in range(1, n):
|
||||||
|
for j in range(0, i):
|
||||||
|
if arr[i] > arr[j] and lis[i] < lis[j] + 1:
|
||||||
|
lis[i] = lis[j]+1
|
||||||
|
|
||||||
|
maximum = 0
|
||||||
|
for i in range(n):
|
||||||
|
maximum = max(maximum, lis[i])
|
||||||
|
|
||||||
|
return maximum
|
||||||
|
|
||||||
|
arr = [10, 22, 9, 33, 21, 50, 41, 60]
|
||||||
|
print("Length of lis is", lis(arr))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complexity Analysis
|
||||||
|
- **Time Complexity**: O(n * n) for both approaches, where n is the length of the array.
|
||||||
|
- **Space Complexity**: O(n * n) for the memoization table in Top-Down Approach, O(n) in Bottom-Up Approach.
|
||||||
|
|
|
@ -17,3 +17,5 @@
|
||||||
- [Hashing through Linear Probing](hashing-linear-probing.md)
|
- [Hashing through Linear Probing](hashing-linear-probing.md)
|
||||||
- [Hashing through Chaining](hashing-chaining.md)
|
- [Hashing through Chaining](hashing-chaining.md)
|
||||||
- [Binary Tree](binary_tree.md)
|
- [Binary Tree](binary_tree.md)
|
||||||
|
- [AVL Trees](avl-trees.md)
|
||||||
|
- [Splay Trees](splay-trees.md)
|
|
@ -0,0 +1,162 @@
|
||||||
|
# Splay Tree
|
||||||
|
|
||||||
|
In Data Structures and Algorithms, a **Splay Tree** is a self-adjusting binary search tree with the additional property that recently accessed elements are quick to access again. It performs basic operations such as insertion, search, and deletion in O(log n) amortized time. This is achieved by a process called **splaying**, where the accessed node is moved to the root through a series of tree rotations.
|
||||||
|
|
||||||
|
## Points to be Remembered
|
||||||
|
|
||||||
|
- **Splaying**: Moving the accessed node to the root using rotations.
|
||||||
|
- **Rotations**: Tree rotations (left and right) are used to balance the tree during splaying.
|
||||||
|
- **Self-adjusting**: The tree adjusts itself with each access, keeping frequently accessed nodes near the root.
|
||||||
|
|
||||||
|
## Real Life Examples of Splay Trees
|
||||||
|
|
||||||
|
- **Cache Implementation**: Frequently accessed data is kept near the top of the tree, making repeated accesses faster.
|
||||||
|
- **Networking**: Routing tables in network switches can use splay trees to prioritize frequently accessed routes.
|
||||||
|
|
||||||
|
## Applications of Splay Trees
|
||||||
|
|
||||||
|
Splay trees are used in various applications in Computer Science:
|
||||||
|
|
||||||
|
- **Cache Implementations**
|
||||||
|
- **Garbage Collection Algorithms**
|
||||||
|
- **Data Compression Algorithms (e.g., LZ78)**
|
||||||
|
|
||||||
|
Understanding these applications is essential for Software Development.
|
||||||
|
|
||||||
|
## Operations in Splay Tree
|
||||||
|
|
||||||
|
Key operations include:
|
||||||
|
|
||||||
|
- **INSERT**: Insert a new element into the splay tree.
|
||||||
|
- **SEARCH**: Find the position of an element in the splay tree.
|
||||||
|
- **DELETE**: Remove an element from the splay tree.
|
||||||
|
|
||||||
|
## Implementing Splay Tree in Python
|
||||||
|
|
||||||
|
```python
|
||||||
|
class SplayTreeNode:
|
||||||
|
def __init__(self, key):
|
||||||
|
self.key = key
|
||||||
|
self.left = None
|
||||||
|
self.right = None
|
||||||
|
|
||||||
|
class SplayTree:
|
||||||
|
def __init__(self):
|
||||||
|
self.root = None
|
||||||
|
|
||||||
|
def insert(self, key):
|
||||||
|
self.root = self.splay_insert(self.root, key)
|
||||||
|
|
||||||
|
def search(self, key):
|
||||||
|
self.root = self.splay_search(self.root, key)
|
||||||
|
return self.root
|
||||||
|
|
||||||
|
def splay(self, root, key):
|
||||||
|
if not root or root.key == key:
|
||||||
|
return root
|
||||||
|
|
||||||
|
if root.key > key:
|
||||||
|
if not root.left:
|
||||||
|
return root
|
||||||
|
if root.left.key > key:
|
||||||
|
root.left.left = self.splay(root.left.left, key)
|
||||||
|
root = self.rotateRight(root)
|
||||||
|
elif root.left.key < key:
|
||||||
|
root.left.right = self.splay(root.left.right, key)
|
||||||
|
if root.left.right:
|
||||||
|
root.left = self.rotateLeft(root.left)
|
||||||
|
return root if not root.left else self.rotateRight(root)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if not root.right:
|
||||||
|
return root
|
||||||
|
if root.right.key > key:
|
||||||
|
root.right.left = self.splay(root.right.left, key)
|
||||||
|
if root.right.left:
|
||||||
|
root.right = self.rotateRight(root.right)
|
||||||
|
elif root.right.key < key:
|
||||||
|
root.right.right = self.splay(root.right.right, key)
|
||||||
|
root = self.rotateLeft(root)
|
||||||
|
return root if not root.right else self.rotateLeft(root)
|
||||||
|
|
||||||
|
def splay_insert(self, root, key):
|
||||||
|
if not root:
|
||||||
|
return SplayTreeNode(key)
|
||||||
|
|
||||||
|
root = self.splay(root, key)
|
||||||
|
|
||||||
|
if root.key == key:
|
||||||
|
return root
|
||||||
|
|
||||||
|
new_node = SplayTreeNode(key)
|
||||||
|
|
||||||
|
if root.key > key:
|
||||||
|
new_node.right = root
|
||||||
|
new_node.left = root.left
|
||||||
|
root.left = None
|
||||||
|
else:
|
||||||
|
new_node.left = root
|
||||||
|
new_node.right = root.right
|
||||||
|
root.right = None
|
||||||
|
|
||||||
|
return new_node
|
||||||
|
|
||||||
|
def splay_search(self, root, key):
|
||||||
|
return self.splay(root, key)
|
||||||
|
|
||||||
|
def rotateRight(self, node):
|
||||||
|
temp = node.left
|
||||||
|
node.left = temp.right
|
||||||
|
temp.right = node
|
||||||
|
return temp
|
||||||
|
|
||||||
|
def rotateLeft(self, node):
|
||||||
|
temp = node.right
|
||||||
|
node.right = temp.left
|
||||||
|
temp.left = node
|
||||||
|
return temp
|
||||||
|
|
||||||
|
def preOrder(self, root):
|
||||||
|
if root:
|
||||||
|
print(root.key, end=' ')
|
||||||
|
self.preOrder(root.left)
|
||||||
|
self.preOrder(root.right)
|
||||||
|
|
||||||
|
#Example usage:
|
||||||
|
splay_tree = SplayTree()
|
||||||
|
splay_tree.insert(50)
|
||||||
|
splay_tree.insert(30)
|
||||||
|
splay_tree.insert(20)
|
||||||
|
splay_tree.insert(40)
|
||||||
|
splay_tree.insert(70)
|
||||||
|
splay_tree.insert(60)
|
||||||
|
splay_tree.insert(80)
|
||||||
|
|
||||||
|
print("Preorder traversal of the Splay tree is:")
|
||||||
|
splay_tree.preOrder(splay_tree.root)
|
||||||
|
|
||||||
|
splay_tree.search(60)
|
||||||
|
|
||||||
|
print("\nSplay tree after search operation for key 60:")
|
||||||
|
splay_tree.preOrder(splay_tree.root)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Preorder traversal of the Splay tree is:
|
||||||
|
50 30 20 40 70 60 80
|
||||||
|
|
||||||
|
Splay tree after search operation for key 60:
|
||||||
|
60 50 30 20 40 70 80
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complexity Analysis
|
||||||
|
|
||||||
|
The worst-case time complexities of the main operations in a Splay Tree are as follows:
|
||||||
|
|
||||||
|
- **Insertion**: (O(n)). In the worst case, insertion may take linear time if the tree is highly unbalanced.
|
||||||
|
- **Search**: (O(n)). In the worst case, searching for a node may take linear time if the tree is highly unbalanced.
|
||||||
|
- **Deletion**: (O(n)). In the worst case, deleting a node may take linear time if the tree is highly unbalanced.
|
||||||
|
|
||||||
|
While these operations can take linear time in the worst case, the splay operation ensures that the tree remains balanced over a sequence of operations, leading to better average-case performance.
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 541 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 12 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 13 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 6.0 KiB |
|
@ -19,4 +19,5 @@
|
||||||
- [Hierarchical Clustering](hierarchical-clustering.md)
|
- [Hierarchical Clustering](hierarchical-clustering.md)
|
||||||
- [Grid Search](grid-search.md)
|
- [Grid Search](grid-search.md)
|
||||||
- [Transformers](transformers.md)
|
- [Transformers](transformers.md)
|
||||||
|
- [K-Means](kmeans.md)
|
||||||
- [K-nearest neighbor (KNN)](knn.md)
|
- [K-nearest neighbor (KNN)](knn.md)
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
# K-Means Clustering
|
||||||
|
Unsupervised Learning Algorithm for Grouping Similar Data.
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
K-means clustering is a fundamental unsupervised machine learning algorithm that excels at grouping similar data points together. It's a popular choice due to its simplicity and efficiency in uncovering hidden patterns within unlabeled datasets.
|
||||||
|
|
||||||
|
## Unsupervised Learning
|
||||||
|
Unlike supervised learning algorithms that rely on labeled data for training, unsupervised algorithms, like K-means, operate solely on input data (without predefined categories). Their objective is to discover inherent structures or groupings within the data.
|
||||||
|
|
||||||
|
## The K-Means Objective
|
||||||
|
Organize similar data points into clusters to unveil underlying patterns. The main objective is to minimize total intra-cluster variance or the squared function.
|
||||||
|
|
||||||
|

|
||||||
|
## Clusters and Centroids
|
||||||
|
A cluster represents a collection of data points that share similar characteristics. K-means identifies a pre-determined number (k) of clusters within the dataset. Each cluster is represented by a centroid, which acts as its central point (imaginary or real).
|
||||||
|
|
||||||
|
## Minimizing In-Cluster Variation
|
||||||
|
The K-means algorithm strategically assigns each data point to a cluster such that the total variation within each cluster (measured by the sum of squared distances between points and their centroid) is minimized. In simpler terms, K-means strives to create clusters where data points are close to their respective centroids.
|
||||||
|
|
||||||
|
## The Meaning Behind "K-Means"
|
||||||
|
The "means" in K-means refers to the averaging process used to compute the centroid, essentially finding the center of each cluster.
|
||||||
|
|
||||||
|
## K-Means Algorithm in Action
|
||||||
|

|
||||||
|
The K-means algorithm follows an iterative approach to optimize cluster formation:
|
||||||
|
|
||||||
|
1. **Initial Centroid Placement:** The process begins with randomly selecting k centroids to serve as initial reference points for each cluster.
|
||||||
|
2. **Data Point Assignment:** Each data point is assigned to the closest centroid, effectively creating a preliminary clustering.
|
||||||
|
3. **Centroid Repositioning:** Once data points are assigned, the centroids are recalculated by averaging the positions of the points within their respective clusters. These new centroids represent the refined centers of the clusters.
|
||||||
|
4. **Iteration Until Convergence:** Steps 2 and 3 are repeated iteratively until a stopping criterion is met. This criterion can be either:
|
||||||
|
- **Centroid Stability:** No significant change occurs in the centroids' positions, indicating successful clustering.
|
||||||
|
- **Reaching Maximum Iterations:** A predefined number of iterations is completed.
|
||||||
|
|
||||||
|
## Code
|
||||||
|
Following is a simple implementation of K-Means.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Generate and Visualize Sample Data
|
||||||
|
# import the necessary Libraries
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# Create data points for cluster 1 and cluster 2
|
||||||
|
X = -2 * np.random.rand(100, 2)
|
||||||
|
X1 = 1 + 2 * np.random.rand(50, 2)
|
||||||
|
|
||||||
|
# Combine data points from both clusters
|
||||||
|
X[50:100, :] = X1
|
||||||
|
|
||||||
|
# Plot data points and display the plot
|
||||||
|
plt.scatter(X[:, 0], X[:, 1], s=50, c='b')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
# K-Means Model Creation and Training
|
||||||
|
from sklearn.cluster import KMeans
|
||||||
|
|
||||||
|
# Create KMeans object with 2 clusters
|
||||||
|
kmeans = KMeans(n_clusters=2)
|
||||||
|
kmeans.fit(X) # Train the model on the data
|
||||||
|
|
||||||
|
# Visualize Data Points with Centroids
|
||||||
|
centroids = kmeans.cluster_centers_ # Get centroids (cluster centers)
|
||||||
|
|
||||||
|
plt.scatter(X[:, 0], X[:, 1], s=50, c='b') # Plot data points again
|
||||||
|
plt.scatter(centroids[0, 0], centroids[0, 1], s=200, c='g', marker='s') # Plot centroid 1
|
||||||
|
plt.scatter(centroids[1, 0], centroids[1, 1], s=200, c='r', marker='s') # Plot centroid 2
|
||||||
|
plt.show() # Display the plot with centroids
|
||||||
|
|
||||||
|
# Predict Cluster Label for New Data Point
|
||||||
|
new_data = np.array([-3.0, -3.0])
|
||||||
|
new_data_reshaped = new_data.reshape(1, -1)
|
||||||
|
predicted_cluster = kmeans.predict(new_data_reshaped)
|
||||||
|
print("Predicted cluster for new data:", predicted_cluster)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output:
|
||||||
|
Before Implementing K-Means Clustering
|
||||||
|

|
||||||
|
|
||||||
|
After Implementing K-Means Clustering
|
||||||
|

|
||||||
|
|
||||||
|
Predicted cluster for new data: `[0]`
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
**K-Means** can be applied to data that has a smaller number of dimensions, is numeric, and is continuous or can be used to find groups that have not been explicitly labeled in the data. As an example, it can be used for Document Classification, Delivery Store Optimization, or Customer Segmentation.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Survey of Machine Learning and Data Mining Techniques used in Multimedia System](https://www.researchgate.net/publication/333457161_Survey_of_Machine_Learning_and_Data_Mining_Techniques_used_in_Multimedia_System?_tp=eyJjb250ZXh0Ijp7ImZpcnN0UGFnZSI6Il9kaXJlY3QiLCJwYWdlIjoiX2RpcmVjdCJ9fQ)
|
||||||
|
- [A Clustering Approach for Outliers Detection in a Big Point-of-Sales Database](https://www.researchgate.net/publication/339267868_A_Clustering_Approach_for_Outliers_Detection_in_a_Big_Point-of-Sales_Database?_tp=eyJjb250ZXh0Ijp7ImZpcnN0UGFnZSI6Il9kaXJlY3QiLCJwYWdlIjoiX2RpcmVjdCJ9fQ)
|
Ładowanie…
Reference in New Issue