Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -327,6 +327,7 @@
* [Minimum Coin Change](dynamic_programming/minimum_coin_change.py)
* [Minimum Cost Path](dynamic_programming/minimum_cost_path.py)
* [Minimum Partition](dynamic_programming/minimum_partition.py)
* [Minimum Size Subarray Sum](dynamic_programming/minimum_size_subarray_sum.py)
* [Minimum Squares To Represent A Number](dynamic_programming/minimum_squares_to_represent_a_number.py)
* [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py)
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
Expand All@@ -339,6 +340,7 @@
* [Word Break](dynamic_programming/word_break.py)

## Electronics
* [Apparent Power](electronics/apparent_power.py)
* [Builtin Voltage](electronics/builtin_voltage.py)
* [Carrier Concentration](electronics/carrier_concentration.py)
* [Circular Convolution](electronics/circular_convolution.py)
Expand All@@ -348,6 +350,7 @@
* [Electrical Impedance](electronics/electrical_impedance.py)
* [Ind Reactance](electronics/ind_reactance.py)
* [Ohms Law](electronics/ohms_law.py)
* [Real And Reactive Power](electronics/real_and_reactive_power.py)
* [Resistor Equivalence](electronics/resistor_equivalence.py)
* [Resonant Frequency](electronics/resonant_frequency.py)

Expand DownExpand Up@@ -483,6 +486,7 @@
* [Astar](machine_learning/astar.py)
* [Data Transformations](machine_learning/data_transformations.py)
* [Decision Tree](machine_learning/decision_tree.py)
* [Dimensionality Reduction](machine_learning/dimensionality_reduction.py)
* Forecasting
* [Run](machine_learning/forecasting/run.py)
* [Gradient Descent](machine_learning/gradient_descent.py)
Expand DownExpand Up@@ -604,6 +608,7 @@
* [Perfect Number](maths/perfect_number.py)
* [Perfect Square](maths/perfect_square.py)
* [Persistence](maths/persistence.py)
* [Pi Generator](maths/pi_generator.py)
* [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py)
* [Points Are Collinear 3D](maths/points_are_collinear_3d.py)
* [Pollard Rho](maths/pollard_rho.py)
Expand DownExpand Up@@ -1162,6 +1167,7 @@
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
* [Split](strings/split.py)
* [Text Justification](strings/text_justification.py)
* [Top K Frequent Words](strings/top_k_frequent_words.py)
* [Upper](strings/upper.py)
* [Wave](strings/wave.py)
* [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py)
Expand Down
31 changes: 25 additions & 6 deletions data_structures/heap/heap.py
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
from __future__ import annotations

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar


class Heap:
class Comparable(Protocol):
@abstractmethod
def __lt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __gt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __eq__(self: T, other: object) -> bool:
pass


T = TypeVar("T", bound=Comparable)


class Heap(Generic[T]):
"""A Max Heap Implementation

>>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]
Expand All@@ -27,7 +46,7 @@ class Heap:
"""

def __init__(self) -> None:
self.h: list[float] = []
self.h: list[T] = []
self.heap_size: int = 0

def __repr__(self) -> str:
Expand DownExpand Up@@ -79,7 +98,7 @@ def max_heapify(self, index: int) -> None:
# fix the subsequent violation recursively if any
self.max_heapify(violation)

def build_max_heap(self, collection: Iterable[float]) -> None:
def build_max_heap(self, collection: Iterable[T]) -> None:
"""build max heap from an unsorted array"""
self.h = list(collection)
self.heap_size = len(self.h)
Expand All@@ -88,7 +107,7 @@ def build_max_heap(self, collection: Iterable[float]) -> None:
for i in range(self.heap_size // 2 - 1, -1, -1):
self.max_heapify(i)

def extract_max(self) -> float:
def extract_max(self) -> T:
"""get and remove max from heap"""
if self.heap_size >= 2:
me = self.h[0]
Expand All@@ -102,7 +121,7 @@ def extract_max(self) -> float:
else:
raise Exception("Empty heap")

def insert(self, value: float) -> None:
def insert(self, value: T) -> None:
"""insert a new value into the max heap"""
self.h.append(value)
idx = (self.heap_size - 1) // 2
Expand DownExpand Up@@ -144,7 +163,7 @@ def heap_sort(self) -> None:
]:
print(f"unsorted array: {unsorted}")

heap = Heap()
heap: Heap[int] = Heap()
heap.build_max_heap(unsorted)
print(f"after build heap: {heap}")

Expand Down
2 changes: 1 addition & 1 deletion machine_learning/linear_discriminant_analysis.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -399,7 +399,7 @@ def main():
if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q":
print("\n" + "GoodBye!".center(100, "-") + "\n")
break
system("clear" if name == "posix" else "cls") # noqa: S605
system("cls" if name == "nt" else "clear") # noqa: S605


if __name__ == "__main__":
Expand Down
101 changes: 101 additions & 0 deletions strings/top_k_frequent_words.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
"""
Finds the top K most frequent words from the provided word list.

This implementation aims to show how to solve the problem using the Heap class
already present in this repository.
Computing order statistics is, in fact, a typical usage of heaps.

This is mostly shown for educational purposes, since the problem can be solved
in a few lines using collections.Counter from the Python standard library:

from collections import Counter
def top_k_frequent_words(words, k_value):
return [x[0] for x in Counter(words).most_common(k_value)]
"""


from collections import Counter
from functools import total_ordering

from data_structures.heap.heap import Heap


@total_ordering
class WordCount:
def __init__(self, word: str, count: int) -> None:
self.word = word
self.count = count

def __eq__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__eq__(WordCount('b', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 2))
False
>>> WordCount('a', 1).__eq__(WordCount('b', 2))
False
>>> WordCount('a', 1).__eq__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count == other.count

def __lt__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 2))
True
>>> WordCount('a', 1).__lt__(WordCount('b', 2))
True
>>> WordCount('a', 2).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 2).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count < other.count


def top_k_frequent_words(words: list[str], k_value: int) -> list[str]:
"""
Returns the `k_value` most frequently occurring words,
in non-increasing order of occurrence.
In this context, a word is defined as an element in the provided list.

In case `k_value` is greater than the number of distinct words, a value of k equal
to the number of distinct words will be considered, instead.

>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 3)
['c', 'a', 'b']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 2)
['c', 'a']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 1)
['c']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 0)
[]
>>> top_k_frequent_words([], 1)
[]
>>> top_k_frequent_words(['a', 'a'], 2)
['a']
"""
heap: Heap[WordCount] = Heap()
count_by_word = Counter(words)
heap.build_max_heap(
[WordCount(word, count) for word, count in count_by_word.items()]
)
return [heap.extract_max().word for _ in range(min(k_value, len(count_by_word)))]


if __name__ == "__main__":
import doctest

doctest.testmod()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Solving the `Top k most frequent words` problem using a max-heap by cclauss · Pull Request #8685 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -327,6 +327,7 @@
* [Minimum Coin Change](dynamic_programming/minimum_coin_change.py)
* [Minimum Cost Path](dynamic_programming/minimum_cost_path.py)
* [Minimum Partition](dynamic_programming/minimum_partition.py)
* [Minimum Size Subarray Sum](dynamic_programming/minimum_size_subarray_sum.py)
* [Minimum Squares To Represent A Number](dynamic_programming/minimum_squares_to_represent_a_number.py)
* [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py)
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
Expand All@@ -339,6 +340,7 @@
* [Word Break](dynamic_programming/word_break.py)

## Electronics
* [Apparent Power](electronics/apparent_power.py)
* [Builtin Voltage](electronics/builtin_voltage.py)
* [Carrier Concentration](electronics/carrier_concentration.py)
* [Circular Convolution](electronics/circular_convolution.py)
Expand All@@ -348,6 +350,7 @@
* [Electrical Impedance](electronics/electrical_impedance.py)
* [Ind Reactance](electronics/ind_reactance.py)
* [Ohms Law](electronics/ohms_law.py)
* [Real And Reactive Power](electronics/real_and_reactive_power.py)
* [Resistor Equivalence](electronics/resistor_equivalence.py)
* [Resonant Frequency](electronics/resonant_frequency.py)

Expand DownExpand Up@@ -483,6 +486,7 @@
* [Astar](machine_learning/astar.py)
* [Data Transformations](machine_learning/data_transformations.py)
* [Decision Tree](machine_learning/decision_tree.py)
* [Dimensionality Reduction](machine_learning/dimensionality_reduction.py)
* Forecasting
* [Run](machine_learning/forecasting/run.py)
* [Gradient Descent](machine_learning/gradient_descent.py)
Expand DownExpand Up@@ -604,6 +608,7 @@
* [Perfect Number](maths/perfect_number.py)
* [Perfect Square](maths/perfect_square.py)
* [Persistence](maths/persistence.py)
* [Pi Generator](maths/pi_generator.py)
* [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py)
* [Points Are Collinear 3D](maths/points_are_collinear_3d.py)
* [Pollard Rho](maths/pollard_rho.py)
Expand DownExpand Up@@ -1162,6 +1167,7 @@
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
* [Split](strings/split.py)
* [Text Justification](strings/text_justification.py)
* [Top K Frequent Words](strings/top_k_frequent_words.py)
* [Upper](strings/upper.py)
* [Wave](strings/wave.py)
* [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py)
Expand Down
31 changes: 25 additions & 6 deletions data_structures/heap/heap.py
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
from __future__ import annotations

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar


class Heap:
class Comparable(Protocol):
@abstractmethod
def __lt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __gt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __eq__(self: T, other: object) -> bool:
pass


T = TypeVar("T", bound=Comparable)


class Heap(Generic[T]):
"""A Max Heap Implementation

>>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]
Expand All@@ -27,7 +46,7 @@ class Heap:
"""

def __init__(self) -> None:
self.h: list[float] = []
self.h: list[T] = []
self.heap_size: int = 0

def __repr__(self) -> str:
Expand DownExpand Up@@ -79,7 +98,7 @@ def max_heapify(self, index: int) -> None:
# fix the subsequent violation recursively if any
self.max_heapify(violation)

def build_max_heap(self, collection: Iterable[float]) -> None:
def build_max_heap(self, collection: Iterable[T]) -> None:
"""build max heap from an unsorted array"""
self.h = list(collection)
self.heap_size = len(self.h)
Expand All@@ -88,7 +107,7 @@ def build_max_heap(self, collection: Iterable[float]) -> None:
for i in range(self.heap_size // 2 - 1, -1, -1):
self.max_heapify(i)

def extract_max(self) -> float:
def extract_max(self) -> T:
"""get and remove max from heap"""
if self.heap_size >= 2:
me = self.h[0]
Expand All@@ -102,7 +121,7 @@ def extract_max(self) -> float:
else:
raise Exception("Empty heap")

def insert(self, value: float) -> None:
def insert(self, value: T) -> None:
"""insert a new value into the max heap"""
self.h.append(value)
idx = (self.heap_size - 1) // 2
Expand DownExpand Up@@ -144,7 +163,7 @@ def heap_sort(self) -> None:
]:
print(f"unsorted array: {unsorted}")

heap = Heap()
heap: Heap[int] = Heap()
heap.build_max_heap(unsorted)
print(f"after build heap: {heap}")

Expand Down
2 changes: 1 addition & 1 deletion machine_learning/linear_discriminant_analysis.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -399,7 +399,7 @@ def main():
if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q":
print("\n" + "GoodBye!".center(100, "-") + "\n")
break
system("clear" if name == "posix" else "cls") # noqa: S605
system("cls" if name == "nt" else "clear") # noqa: S605


if __name__ == "__main__":
Expand Down
101 changes: 101 additions & 0 deletions strings/top_k_frequent_words.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
"""
Finds the top K most frequent words from the provided word list.

This implementation aims to show how to solve the problem using the Heap class
already present in this repository.
Computing order statistics is, in fact, a typical usage of heaps.

This is mostly shown for educational purposes, since the problem can be solved
in a few lines using collections.Counter from the Python standard library:

from collections import Counter
def top_k_frequent_words(words, k_value):
return [x[0] for x in Counter(words).most_common(k_value)]
"""


from collections import Counter
from functools import total_ordering

from data_structures.heap.heap import Heap


@total_ordering
class WordCount:
def __init__(self, word: str, count: int) -> None:
self.word = word
self.count = count

def __eq__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__eq__(WordCount('b', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 2))
False
>>> WordCount('a', 1).__eq__(WordCount('b', 2))
False
>>> WordCount('a', 1).__eq__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count == other.count

def __lt__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 2))
True
>>> WordCount('a', 1).__lt__(WordCount('b', 2))
True
>>> WordCount('a', 2).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 2).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count < other.count


def top_k_frequent_words(words: list[str], k_value: int) -> list[str]:
"""
Returns the `k_value` most frequently occurring words,
in non-increasing order of occurrence.
In this context, a word is defined as an element in the provided list.

In case `k_value` is greater than the number of distinct words, a value of k equal
to the number of distinct words will be considered, instead.

>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 3)
['c', 'a', 'b']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 2)
['c', 'a']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 1)
['c']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 0)
[]
>>> top_k_frequent_words([], 1)
[]
>>> top_k_frequent_words(['a', 'a'], 2)
['a']
"""
heap: Heap[WordCount] = Heap()
count_by_word = Counter(words)
heap.build_max_heap(
[WordCount(word, count) for word, count in count_by_word.items()]
)
return [heap.extract_max().word for _ in range(min(k_value, len(count_by_word)))]


if __name__ == "__main__":
import doctest

doctest.testmod()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Solving the `Top k most frequent words` problem using a max-heap by cclauss · Pull Request #8685 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -327,6 +327,7 @@
* [Minimum Coin Change](dynamic_programming/minimum_coin_change.py)
* [Minimum Cost Path](dynamic_programming/minimum_cost_path.py)
* [Minimum Partition](dynamic_programming/minimum_partition.py)
* [Minimum Size Subarray Sum](dynamic_programming/minimum_size_subarray_sum.py)
* [Minimum Squares To Represent A Number](dynamic_programming/minimum_squares_to_represent_a_number.py)
* [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py)
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
Expand All@@ -339,6 +340,7 @@
* [Word Break](dynamic_programming/word_break.py)

## Electronics
* [Apparent Power](electronics/apparent_power.py)
* [Builtin Voltage](electronics/builtin_voltage.py)
* [Carrier Concentration](electronics/carrier_concentration.py)
* [Circular Convolution](electronics/circular_convolution.py)
Expand All@@ -348,6 +350,7 @@
* [Electrical Impedance](electronics/electrical_impedance.py)
* [Ind Reactance](electronics/ind_reactance.py)
* [Ohms Law](electronics/ohms_law.py)
* [Real And Reactive Power](electronics/real_and_reactive_power.py)
* [Resistor Equivalence](electronics/resistor_equivalence.py)
* [Resonant Frequency](electronics/resonant_frequency.py)

Expand DownExpand Up@@ -483,6 +486,7 @@
* [Astar](machine_learning/astar.py)
* [Data Transformations](machine_learning/data_transformations.py)
* [Decision Tree](machine_learning/decision_tree.py)
* [Dimensionality Reduction](machine_learning/dimensionality_reduction.py)
* Forecasting
* [Run](machine_learning/forecasting/run.py)
* [Gradient Descent](machine_learning/gradient_descent.py)
Expand DownExpand Up@@ -604,6 +608,7 @@
* [Perfect Number](maths/perfect_number.py)
* [Perfect Square](maths/perfect_square.py)
* [Persistence](maths/persistence.py)
* [Pi Generator](maths/pi_generator.py)
* [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py)
* [Points Are Collinear 3D](maths/points_are_collinear_3d.py)
* [Pollard Rho](maths/pollard_rho.py)
Expand DownExpand Up@@ -1162,6 +1167,7 @@
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
* [Split](strings/split.py)
* [Text Justification](strings/text_justification.py)
* [Top K Frequent Words](strings/top_k_frequent_words.py)
* [Upper](strings/upper.py)
* [Wave](strings/wave.py)
* [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py)
Expand Down
31 changes: 25 additions & 6 deletions data_structures/heap/heap.py
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
from __future__ import annotations

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar


class Heap:
class Comparable(Protocol):
@abstractmethod
def __lt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __gt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __eq__(self: T, other: object) -> bool:
pass


T = TypeVar("T", bound=Comparable)


class Heap(Generic[T]):
"""A Max Heap Implementation

>>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]
Expand All@@ -27,7 +46,7 @@ class Heap:
"""

def __init__(self) -> None:
self.h: list[float] = []
self.h: list[T] = []
self.heap_size: int = 0

def __repr__(self) -> str:
Expand DownExpand Up@@ -79,7 +98,7 @@ def max_heapify(self, index: int) -> None:
# fix the subsequent violation recursively if any
self.max_heapify(violation)

def build_max_heap(self, collection: Iterable[float]) -> None:
def build_max_heap(self, collection: Iterable[T]) -> None:
"""build max heap from an unsorted array"""
self.h = list(collection)
self.heap_size = len(self.h)
Expand All@@ -88,7 +107,7 @@ def build_max_heap(self, collection: Iterable[float]) -> None:
for i in range(self.heap_size // 2 - 1, -1, -1):
self.max_heapify(i)

def extract_max(self) -> float:
def extract_max(self) -> T:
"""get and remove max from heap"""
if self.heap_size >= 2:
me = self.h[0]
Expand All@@ -102,7 +121,7 @@ def extract_max(self) -> float:
else:
raise Exception("Empty heap")

def insert(self, value: float) -> None:
def insert(self, value: T) -> None:
"""insert a new value into the max heap"""
self.h.append(value)
idx = (self.heap_size - 1) // 2
Expand DownExpand Up@@ -144,7 +163,7 @@ def heap_sort(self) -> None:
]:
print(f"unsorted array: {unsorted}")

heap = Heap()
heap: Heap[int] = Heap()
heap.build_max_heap(unsorted)
print(f"after build heap: {heap}")

Expand Down
2 changes: 1 addition & 1 deletion machine_learning/linear_discriminant_analysis.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -399,7 +399,7 @@ def main():
if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q":
print("\n" + "GoodBye!".center(100, "-") + "\n")
break
system("clear" if name == "posix" else "cls") # noqa: S605
system("cls" if name == "nt" else "clear") # noqa: S605


if __name__ == "__main__":
Expand Down
101 changes: 101 additions & 0 deletions strings/top_k_frequent_words.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
"""
Finds the top K most frequent words from the provided word list.

This implementation aims to show how to solve the problem using the Heap class
already present in this repository.
Computing order statistics is, in fact, a typical usage of heaps.

This is mostly shown for educational purposes, since the problem can be solved
in a few lines using collections.Counter from the Python standard library:

from collections import Counter
def top_k_frequent_words(words, k_value):
return [x[0] for x in Counter(words).most_common(k_value)]
"""


from collections import Counter
from functools import total_ordering

from data_structures.heap.heap import Heap


@total_ordering
class WordCount:
def __init__(self, word: str, count: int) -> None:
self.word = word
self.count = count

def __eq__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__eq__(WordCount('b', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 2))
False
>>> WordCount('a', 1).__eq__(WordCount('b', 2))
False
>>> WordCount('a', 1).__eq__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count == other.count

def __lt__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 2))
True
>>> WordCount('a', 1).__lt__(WordCount('b', 2))
True
>>> WordCount('a', 2).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 2).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count < other.count


def top_k_frequent_words(words: list[str], k_value: int) -> list[str]:
"""
Returns the `k_value` most frequently occurring words,
in non-increasing order of occurrence.
In this context, a word is defined as an element in the provided list.

In case `k_value` is greater than the number of distinct words, a value of k equal
to the number of distinct words will be considered, instead.

>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 3)
['c', 'a', 'b']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 2)
['c', 'a']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 1)
['c']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 0)
[]
>>> top_k_frequent_words([], 1)
[]
>>> top_k_frequent_words(['a', 'a'], 2)
['a']
"""
heap: Heap[WordCount] = Heap()
count_by_word = Counter(words)
heap.build_max_heap(
[WordCount(word, count) for word, count in count_by_word.items()]
)
return [heap.extract_max().word for _ in range(min(k_value, len(count_by_word)))]


if __name__ == "__main__":
import doctest

doctest.testmod()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Solving the `Top k most frequent words` problem using a max-heap by cclauss · Pull Request #8685 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -327,6 +327,7 @@
* [Minimum Coin Change](dynamic_programming/minimum_coin_change.py)
* [Minimum Cost Path](dynamic_programming/minimum_cost_path.py)
* [Minimum Partition](dynamic_programming/minimum_partition.py)
* [Minimum Size Subarray Sum](dynamic_programming/minimum_size_subarray_sum.py)
* [Minimum Squares To Represent A Number](dynamic_programming/minimum_squares_to_represent_a_number.py)
* [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py)
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
Expand All@@ -339,6 +340,7 @@
* [Word Break](dynamic_programming/word_break.py)

## Electronics
* [Apparent Power](electronics/apparent_power.py)
* [Builtin Voltage](electronics/builtin_voltage.py)
* [Carrier Concentration](electronics/carrier_concentration.py)
* [Circular Convolution](electronics/circular_convolution.py)
Expand All@@ -348,6 +350,7 @@
* [Electrical Impedance](electronics/electrical_impedance.py)
* [Ind Reactance](electronics/ind_reactance.py)
* [Ohms Law](electronics/ohms_law.py)
* [Real And Reactive Power](electronics/real_and_reactive_power.py)
* [Resistor Equivalence](electronics/resistor_equivalence.py)
* [Resonant Frequency](electronics/resonant_frequency.py)

Expand DownExpand Up@@ -483,6 +486,7 @@
* [Astar](machine_learning/astar.py)
* [Data Transformations](machine_learning/data_transformations.py)
* [Decision Tree](machine_learning/decision_tree.py)
* [Dimensionality Reduction](machine_learning/dimensionality_reduction.py)
* Forecasting
* [Run](machine_learning/forecasting/run.py)
* [Gradient Descent](machine_learning/gradient_descent.py)
Expand DownExpand Up@@ -604,6 +608,7 @@
* [Perfect Number](maths/perfect_number.py)
* [Perfect Square](maths/perfect_square.py)
* [Persistence](maths/persistence.py)
* [Pi Generator](maths/pi_generator.py)
* [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py)
* [Points Are Collinear 3D](maths/points_are_collinear_3d.py)
* [Pollard Rho](maths/pollard_rho.py)
Expand DownExpand Up@@ -1162,6 +1167,7 @@
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
* [Split](strings/split.py)
* [Text Justification](strings/text_justification.py)
* [Top K Frequent Words](strings/top_k_frequent_words.py)
* [Upper](strings/upper.py)
* [Wave](strings/wave.py)
* [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py)
Expand Down
31 changes: 25 additions & 6 deletions data_structures/heap/heap.py
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
from __future__ import annotations

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar


class Heap:
class Comparable(Protocol):
@abstractmethod
def __lt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __gt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __eq__(self: T, other: object) -> bool:
pass


T = TypeVar("T", bound=Comparable)


class Heap(Generic[T]):
"""A Max Heap Implementation

>>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]
Expand All@@ -27,7 +46,7 @@ class Heap:
"""

def __init__(self) -> None:
self.h: list[float] = []
self.h: list[T] = []
self.heap_size: int = 0

def __repr__(self) -> str:
Expand DownExpand Up@@ -79,7 +98,7 @@ def max_heapify(self, index: int) -> None:
# fix the subsequent violation recursively if any
self.max_heapify(violation)

def build_max_heap(self, collection: Iterable[float]) -> None:
def build_max_heap(self, collection: Iterable[T]) -> None:
"""build max heap from an unsorted array"""
self.h = list(collection)
self.heap_size = len(self.h)
Expand All@@ -88,7 +107,7 @@ def build_max_heap(self, collection: Iterable[float]) -> None:
for i in range(self.heap_size // 2 - 1, -1, -1):
self.max_heapify(i)

def extract_max(self) -> float:
def extract_max(self) -> T:
"""get and remove max from heap"""
if self.heap_size >= 2:
me = self.h[0]
Expand All@@ -102,7 +121,7 @@ def extract_max(self) -> float:
else:
raise Exception("Empty heap")

def insert(self, value: float) -> None:
def insert(self, value: T) -> None:
"""insert a new value into the max heap"""
self.h.append(value)
idx = (self.heap_size - 1) // 2
Expand DownExpand Up@@ -144,7 +163,7 @@ def heap_sort(self) -> None:
]:
print(f"unsorted array: {unsorted}")

heap = Heap()
heap: Heap[int] = Heap()
heap.build_max_heap(unsorted)
print(f"after build heap: {heap}")

Expand Down
2 changes: 1 addition & 1 deletion machine_learning/linear_discriminant_analysis.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -399,7 +399,7 @@ def main():
if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q":
print("\n" + "GoodBye!".center(100, "-") + "\n")
break
system("clear" if name == "posix" else "cls") # noqa: S605
system("cls" if name == "nt" else "clear") # noqa: S605


if __name__ == "__main__":
Expand Down
101 changes: 101 additions & 0 deletions strings/top_k_frequent_words.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
"""
Finds the top K most frequent words from the provided word list.

This implementation aims to show how to solve the problem using the Heap class
already present in this repository.
Computing order statistics is, in fact, a typical usage of heaps.

This is mostly shown for educational purposes, since the problem can be solved
in a few lines using collections.Counter from the Python standard library:

from collections import Counter
def top_k_frequent_words(words, k_value):
return [x[0] for x in Counter(words).most_common(k_value)]
"""


from collections import Counter
from functools import total_ordering

from data_structures.heap.heap import Heap


@total_ordering
class WordCount:
def __init__(self, word: str, count: int) -> None:
self.word = word
self.count = count

def __eq__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__eq__(WordCount('b', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 2))
False
>>> WordCount('a', 1).__eq__(WordCount('b', 2))
False
>>> WordCount('a', 1).__eq__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count == other.count

def __lt__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 2))
True
>>> WordCount('a', 1).__lt__(WordCount('b', 2))
True
>>> WordCount('a', 2).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 2).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count < other.count


def top_k_frequent_words(words: list[str], k_value: int) -> list[str]:
"""
Returns the `k_value` most frequently occurring words,
in non-increasing order of occurrence.
In this context, a word is defined as an element in the provided list.

In case `k_value` is greater than the number of distinct words, a value of k equal
to the number of distinct words will be considered, instead.

>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 3)
['c', 'a', 'b']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 2)
['c', 'a']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 1)
['c']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 0)
[]
>>> top_k_frequent_words([], 1)
[]
>>> top_k_frequent_words(['a', 'a'], 2)
['a']
"""
heap: Heap[WordCount] = Heap()
count_by_word = Counter(words)
heap.build_max_heap(
[WordCount(word, count) for word, count in count_by_word.items()]
)
return [heap.extract_max().word for _ in range(min(k_value, len(count_by_word)))]


if __name__ == "__main__":
import doctest

doctest.testmod()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Solving the `Top k most frequent words` problem using a max-heap by cclauss · Pull Request #8685 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -327,6 +327,7 @@
* [Minimum Coin Change](dynamic_programming/minimum_coin_change.py)
* [Minimum Cost Path](dynamic_programming/minimum_cost_path.py)
* [Minimum Partition](dynamic_programming/minimum_partition.py)
* [Minimum Size Subarray Sum](dynamic_programming/minimum_size_subarray_sum.py)
* [Minimum Squares To Represent A Number](dynamic_programming/minimum_squares_to_represent_a_number.py)
* [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py)
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
Expand All@@ -339,6 +340,7 @@
* [Word Break](dynamic_programming/word_break.py)

## Electronics
* [Apparent Power](electronics/apparent_power.py)
* [Builtin Voltage](electronics/builtin_voltage.py)
* [Carrier Concentration](electronics/carrier_concentration.py)
* [Circular Convolution](electronics/circular_convolution.py)
Expand All@@ -348,6 +350,7 @@
* [Electrical Impedance](electronics/electrical_impedance.py)
* [Ind Reactance](electronics/ind_reactance.py)
* [Ohms Law](electronics/ohms_law.py)
* [Real And Reactive Power](electronics/real_and_reactive_power.py)
* [Resistor Equivalence](electronics/resistor_equivalence.py)
* [Resonant Frequency](electronics/resonant_frequency.py)

Expand DownExpand Up@@ -483,6 +486,7 @@
* [Astar](machine_learning/astar.py)
* [Data Transformations](machine_learning/data_transformations.py)
* [Decision Tree](machine_learning/decision_tree.py)
* [Dimensionality Reduction](machine_learning/dimensionality_reduction.py)
* Forecasting
* [Run](machine_learning/forecasting/run.py)
* [Gradient Descent](machine_learning/gradient_descent.py)
Expand DownExpand Up@@ -604,6 +608,7 @@
* [Perfect Number](maths/perfect_number.py)
* [Perfect Square](maths/perfect_square.py)
* [Persistence](maths/persistence.py)
* [Pi Generator](maths/pi_generator.py)
* [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py)
* [Points Are Collinear 3D](maths/points_are_collinear_3d.py)
* [Pollard Rho](maths/pollard_rho.py)
Expand DownExpand Up@@ -1162,6 +1167,7 @@
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
* [Split](strings/split.py)
* [Text Justification](strings/text_justification.py)
* [Top K Frequent Words](strings/top_k_frequent_words.py)
* [Upper](strings/upper.py)
* [Wave](strings/wave.py)
* [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py)
Expand Down
31 changes: 25 additions & 6 deletions data_structures/heap/heap.py
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
from __future__ import annotations

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar


class Heap:
class Comparable(Protocol):
@abstractmethod
def __lt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __gt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __eq__(self: T, other: object) -> bool:
pass


T = TypeVar("T", bound=Comparable)


class Heap(Generic[T]):
"""A Max Heap Implementation

>>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]
Expand All@@ -27,7 +46,7 @@ class Heap:
"""

def __init__(self) -> None:
self.h: list[float] = []
self.h: list[T] = []
self.heap_size: int = 0

def __repr__(self) -> str:
Expand DownExpand Up@@ -79,7 +98,7 @@ def max_heapify(self, index: int) -> None:
# fix the subsequent violation recursively if any
self.max_heapify(violation)

def build_max_heap(self, collection: Iterable[float]) -> None:
def build_max_heap(self, collection: Iterable[T]) -> None:
"""build max heap from an unsorted array"""
self.h = list(collection)
self.heap_size = len(self.h)
Expand All@@ -88,7 +107,7 @@ def build_max_heap(self, collection: Iterable[float]) -> None:
for i in range(self.heap_size // 2 - 1, -1, -1):
self.max_heapify(i)

def extract_max(self) -> float:
def extract_max(self) -> T:
"""get and remove max from heap"""
if self.heap_size >= 2:
me = self.h[0]
Expand All@@ -102,7 +121,7 @@ def extract_max(self) -> float:
else:
raise Exception("Empty heap")

def insert(self, value: float) -> None:
def insert(self, value: T) -> None:
"""insert a new value into the max heap"""
self.h.append(value)
idx = (self.heap_size - 1) // 2
Expand DownExpand Up@@ -144,7 +163,7 @@ def heap_sort(self) -> None:
]:
print(f"unsorted array: {unsorted}")

heap = Heap()
heap: Heap[int] = Heap()
heap.build_max_heap(unsorted)
print(f"after build heap: {heap}")

Expand Down
2 changes: 1 addition & 1 deletion machine_learning/linear_discriminant_analysis.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -399,7 +399,7 @@ def main():
if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q":
print("\n" + "GoodBye!".center(100, "-") + "\n")
break
system("clear" if name == "posix" else "cls") # noqa: S605
system("cls" if name == "nt" else "clear") # noqa: S605


if __name__ == "__main__":
Expand Down
101 changes: 101 additions & 0 deletions strings/top_k_frequent_words.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
"""
Finds the top K most frequent words from the provided word list.

This implementation aims to show how to solve the problem using the Heap class
already present in this repository.
Computing order statistics is, in fact, a typical usage of heaps.

This is mostly shown for educational purposes, since the problem can be solved
in a few lines using collections.Counter from the Python standard library:

from collections import Counter
def top_k_frequent_words(words, k_value):
return [x[0] for x in Counter(words).most_common(k_value)]
"""


from collections import Counter
from functools import total_ordering

from data_structures.heap.heap import Heap


@total_ordering
class WordCount:
def __init__(self, word: str, count: int) -> None:
self.word = word
self.count = count

def __eq__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__eq__(WordCount('b', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 2))
False
>>> WordCount('a', 1).__eq__(WordCount('b', 2))
False
>>> WordCount('a', 1).__eq__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count == other.count

def __lt__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 2))
True
>>> WordCount('a', 1).__lt__(WordCount('b', 2))
True
>>> WordCount('a', 2).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 2).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count < other.count


def top_k_frequent_words(words: list[str], k_value: int) -> list[str]:
"""
Returns the `k_value` most frequently occurring words,
in non-increasing order of occurrence.
In this context, a word is defined as an element in the provided list.

In case `k_value` is greater than the number of distinct words, a value of k equal
to the number of distinct words will be considered, instead.

>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 3)
['c', 'a', 'b']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 2)
['c', 'a']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 1)
['c']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 0)
[]
>>> top_k_frequent_words([], 1)
[]
>>> top_k_frequent_words(['a', 'a'], 2)
['a']
"""
heap: Heap[WordCount] = Heap()
count_by_word = Counter(words)
heap.build_max_heap(
[WordCount(word, count) for word, count in count_by_word.items()]
)
return [heap.extract_max().word for _ in range(min(k_value, len(count_by_word)))]


if __name__ == "__main__":
import doctest

doctest.testmod()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Solving the `Top k most frequent words` problem using a max-heap by cclauss · Pull Request #8685 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -327,6 +327,7 @@
* [Minimum Coin Change](dynamic_programming/minimum_coin_change.py)
* [Minimum Cost Path](dynamic_programming/minimum_cost_path.py)
* [Minimum Partition](dynamic_programming/minimum_partition.py)
* [Minimum Size Subarray Sum](dynamic_programming/minimum_size_subarray_sum.py)
* [Minimum Squares To Represent A Number](dynamic_programming/minimum_squares_to_represent_a_number.py)
* [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py)
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
Expand All@@ -339,6 +340,7 @@
* [Word Break](dynamic_programming/word_break.py)

## Electronics
* [Apparent Power](electronics/apparent_power.py)
* [Builtin Voltage](electronics/builtin_voltage.py)
* [Carrier Concentration](electronics/carrier_concentration.py)
* [Circular Convolution](electronics/circular_convolution.py)
Expand All@@ -348,6 +350,7 @@
* [Electrical Impedance](electronics/electrical_impedance.py)
* [Ind Reactance](electronics/ind_reactance.py)
* [Ohms Law](electronics/ohms_law.py)
* [Real And Reactive Power](electronics/real_and_reactive_power.py)
* [Resistor Equivalence](electronics/resistor_equivalence.py)
* [Resonant Frequency](electronics/resonant_frequency.py)

Expand DownExpand Up@@ -483,6 +486,7 @@
* [Astar](machine_learning/astar.py)
* [Data Transformations](machine_learning/data_transformations.py)
* [Decision Tree](machine_learning/decision_tree.py)
* [Dimensionality Reduction](machine_learning/dimensionality_reduction.py)
* Forecasting
* [Run](machine_learning/forecasting/run.py)
* [Gradient Descent](machine_learning/gradient_descent.py)
Expand DownExpand Up@@ -604,6 +608,7 @@
* [Perfect Number](maths/perfect_number.py)
* [Perfect Square](maths/perfect_square.py)
* [Persistence](maths/persistence.py)
* [Pi Generator](maths/pi_generator.py)
* [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py)
* [Points Are Collinear 3D](maths/points_are_collinear_3d.py)
* [Pollard Rho](maths/pollard_rho.py)
Expand DownExpand Up@@ -1162,6 +1167,7 @@
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
* [Split](strings/split.py)
* [Text Justification](strings/text_justification.py)
* [Top K Frequent Words](strings/top_k_frequent_words.py)
* [Upper](strings/upper.py)
* [Wave](strings/wave.py)
* [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py)
Expand Down
31 changes: 25 additions & 6 deletions data_structures/heap/heap.py
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
from __future__ import annotations

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar


class Heap:
class Comparable(Protocol):
@abstractmethod
def __lt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __gt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __eq__(self: T, other: object) -> bool:
pass


T = TypeVar("T", bound=Comparable)


class Heap(Generic[T]):
"""A Max Heap Implementation

>>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]
Expand All@@ -27,7 +46,7 @@ class Heap:
"""

def __init__(self) -> None:
self.h: list[float] = []
self.h: list[T] = []
self.heap_size: int = 0

def __repr__(self) -> str:
Expand DownExpand Up@@ -79,7 +98,7 @@ def max_heapify(self, index: int) -> None:
# fix the subsequent violation recursively if any
self.max_heapify(violation)

def build_max_heap(self, collection: Iterable[float]) -> None:
def build_max_heap(self, collection: Iterable[T]) -> None:
"""build max heap from an unsorted array"""
self.h = list(collection)
self.heap_size = len(self.h)
Expand All@@ -88,7 +107,7 @@ def build_max_heap(self, collection: Iterable[float]) -> None:
for i in range(self.heap_size // 2 - 1, -1, -1):
self.max_heapify(i)

def extract_max(self) -> float:
def extract_max(self) -> T:
"""get and remove max from heap"""
if self.heap_size >= 2:
me = self.h[0]
Expand All@@ -102,7 +121,7 @@ def extract_max(self) -> float:
else:
raise Exception("Empty heap")

def insert(self, value: float) -> None:
def insert(self, value: T) -> None:
"""insert a new value into the max heap"""
self.h.append(value)
idx = (self.heap_size - 1) // 2
Expand DownExpand Up@@ -144,7 +163,7 @@ def heap_sort(self) -> None:
]:
print(f"unsorted array: {unsorted}")

heap = Heap()
heap: Heap[int] = Heap()
heap.build_max_heap(unsorted)
print(f"after build heap: {heap}")

Expand Down
2 changes: 1 addition & 1 deletion machine_learning/linear_discriminant_analysis.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -399,7 +399,7 @@ def main():
if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q":
print("\n" + "GoodBye!".center(100, "-") + "\n")
break
system("clear" if name == "posix" else "cls") # noqa: S605
system("cls" if name == "nt" else "clear") # noqa: S605


if __name__ == "__main__":
Expand Down
101 changes: 101 additions & 0 deletions strings/top_k_frequent_words.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
"""
Finds the top K most frequent words from the provided word list.

This implementation aims to show how to solve the problem using the Heap class
already present in this repository.
Computing order statistics is, in fact, a typical usage of heaps.

This is mostly shown for educational purposes, since the problem can be solved
in a few lines using collections.Counter from the Python standard library:

from collections import Counter
def top_k_frequent_words(words, k_value):
return [x[0] for x in Counter(words).most_common(k_value)]
"""


from collections import Counter
from functools import total_ordering

from data_structures.heap.heap import Heap


@total_ordering
class WordCount:
def __init__(self, word: str, count: int) -> None:
self.word = word
self.count = count

def __eq__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__eq__(WordCount('b', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 2))
False
>>> WordCount('a', 1).__eq__(WordCount('b', 2))
False
>>> WordCount('a', 1).__eq__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count == other.count

def __lt__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 2))
True
>>> WordCount('a', 1).__lt__(WordCount('b', 2))
True
>>> WordCount('a', 2).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 2).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count < other.count


def top_k_frequent_words(words: list[str], k_value: int) -> list[str]:
"""
Returns the `k_value` most frequently occurring words,
in non-increasing order of occurrence.
In this context, a word is defined as an element in the provided list.

In case `k_value` is greater than the number of distinct words, a value of k equal
to the number of distinct words will be considered, instead.

>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 3)
['c', 'a', 'b']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 2)
['c', 'a']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 1)
['c']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 0)
[]
>>> top_k_frequent_words([], 1)
[]
>>> top_k_frequent_words(['a', 'a'], 2)
['a']
"""
heap: Heap[WordCount] = Heap()
count_by_word = Counter(words)
heap.build_max_heap(
[WordCount(word, count) for word, count in count_by_word.items()]
)
return [heap.extract_max().word for _ in range(min(k_value, len(count_by_word)))]


if __name__ == "__main__":
import doctest

doctest.testmod()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Solving the `Top k most frequent words` problem using a max-heap by cclauss · Pull Request #8685 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -327,6 +327,7 @@
* [Minimum Coin Change](dynamic_programming/minimum_coin_change.py)
* [Minimum Cost Path](dynamic_programming/minimum_cost_path.py)
* [Minimum Partition](dynamic_programming/minimum_partition.py)
* [Minimum Size Subarray Sum](dynamic_programming/minimum_size_subarray_sum.py)
* [Minimum Squares To Represent A Number](dynamic_programming/minimum_squares_to_represent_a_number.py)
* [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py)
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
Expand All@@ -339,6 +340,7 @@
* [Word Break](dynamic_programming/word_break.py)

## Electronics
* [Apparent Power](electronics/apparent_power.py)
* [Builtin Voltage](electronics/builtin_voltage.py)
* [Carrier Concentration](electronics/carrier_concentration.py)
* [Circular Convolution](electronics/circular_convolution.py)
Expand All@@ -348,6 +350,7 @@
* [Electrical Impedance](electronics/electrical_impedance.py)
* [Ind Reactance](electronics/ind_reactance.py)
* [Ohms Law](electronics/ohms_law.py)
* [Real And Reactive Power](electronics/real_and_reactive_power.py)
* [Resistor Equivalence](electronics/resistor_equivalence.py)
* [Resonant Frequency](electronics/resonant_frequency.py)

Expand DownExpand Up@@ -483,6 +486,7 @@
* [Astar](machine_learning/astar.py)
* [Data Transformations](machine_learning/data_transformations.py)
* [Decision Tree](machine_learning/decision_tree.py)
* [Dimensionality Reduction](machine_learning/dimensionality_reduction.py)
* Forecasting
* [Run](machine_learning/forecasting/run.py)
* [Gradient Descent](machine_learning/gradient_descent.py)
Expand DownExpand Up@@ -604,6 +608,7 @@
* [Perfect Number](maths/perfect_number.py)
* [Perfect Square](maths/perfect_square.py)
* [Persistence](maths/persistence.py)
* [Pi Generator](maths/pi_generator.py)
* [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py)
* [Points Are Collinear 3D](maths/points_are_collinear_3d.py)
* [Pollard Rho](maths/pollard_rho.py)
Expand DownExpand Up@@ -1162,6 +1167,7 @@
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
* [Split](strings/split.py)
* [Text Justification](strings/text_justification.py)
* [Top K Frequent Words](strings/top_k_frequent_words.py)
* [Upper](strings/upper.py)
* [Wave](strings/wave.py)
* [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py)
Expand Down
31 changes: 25 additions & 6 deletions data_structures/heap/heap.py
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
from __future__ import annotations

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar


class Heap:
class Comparable(Protocol):
@abstractmethod
def __lt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __gt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __eq__(self: T, other: object) -> bool:
pass


T = TypeVar("T", bound=Comparable)


class Heap(Generic[T]):
"""A Max Heap Implementation

>>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]
Expand All@@ -27,7 +46,7 @@ class Heap:
"""

def __init__(self) -> None:
self.h: list[float] = []
self.h: list[T] = []
self.heap_size: int = 0

def __repr__(self) -> str:
Expand DownExpand Up@@ -79,7 +98,7 @@ def max_heapify(self, index: int) -> None:
# fix the subsequent violation recursively if any
self.max_heapify(violation)

def build_max_heap(self, collection: Iterable[float]) -> None:
def build_max_heap(self, collection: Iterable[T]) -> None:
"""build max heap from an unsorted array"""
self.h = list(collection)
self.heap_size = len(self.h)
Expand All@@ -88,7 +107,7 @@ def build_max_heap(self, collection: Iterable[float]) -> None:
for i in range(self.heap_size // 2 - 1, -1, -1):
self.max_heapify(i)

def extract_max(self) -> float:
def extract_max(self) -> T:
"""get and remove max from heap"""
if self.heap_size >= 2:
me = self.h[0]
Expand All@@ -102,7 +121,7 @@ def extract_max(self) -> float:
else:
raise Exception("Empty heap")

def insert(self, value: float) -> None:
def insert(self, value: T) -> None:
"""insert a new value into the max heap"""
self.h.append(value)
idx = (self.heap_size - 1) // 2
Expand DownExpand Up@@ -144,7 +163,7 @@ def heap_sort(self) -> None:
]:
print(f"unsorted array: {unsorted}")

heap = Heap()
heap: Heap[int] = Heap()
heap.build_max_heap(unsorted)
print(f"after build heap: {heap}")

Expand Down
2 changes: 1 addition & 1 deletion machine_learning/linear_discriminant_analysis.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -399,7 +399,7 @@ def main():
if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q":
print("\n" + "GoodBye!".center(100, "-") + "\n")
break
system("clear" if name == "posix" else "cls") # noqa: S605
system("cls" if name == "nt" else "clear") # noqa: S605


if __name__ == "__main__":
Expand Down
101 changes: 101 additions & 0 deletions strings/top_k_frequent_words.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
"""
Finds the top K most frequent words from the provided word list.

This implementation aims to show how to solve the problem using the Heap class
already present in this repository.
Computing order statistics is, in fact, a typical usage of heaps.

This is mostly shown for educational purposes, since the problem can be solved
in a few lines using collections.Counter from the Python standard library:

from collections import Counter
def top_k_frequent_words(words, k_value):
return [x[0] for x in Counter(words).most_common(k_value)]
"""


from collections import Counter
from functools import total_ordering

from data_structures.heap.heap import Heap


@total_ordering
class WordCount:
def __init__(self, word: str, count: int) -> None:
self.word = word
self.count = count

def __eq__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__eq__(WordCount('b', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 2))
False
>>> WordCount('a', 1).__eq__(WordCount('b', 2))
False
>>> WordCount('a', 1).__eq__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count == other.count

def __lt__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 2))
True
>>> WordCount('a', 1).__lt__(WordCount('b', 2))
True
>>> WordCount('a', 2).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 2).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count < other.count


def top_k_frequent_words(words: list[str], k_value: int) -> list[str]:
"""
Returns the `k_value` most frequently occurring words,
in non-increasing order of occurrence.
In this context, a word is defined as an element in the provided list.

In case `k_value` is greater than the number of distinct words, a value of k equal
to the number of distinct words will be considered, instead.

>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 3)
['c', 'a', 'b']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 2)
['c', 'a']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 1)
['c']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 0)
[]
>>> top_k_frequent_words([], 1)
[]
>>> top_k_frequent_words(['a', 'a'], 2)
['a']
"""
heap: Heap[WordCount] = Heap()
count_by_word = Counter(words)
heap.build_max_heap(
[WordCount(word, count) for word, count in count_by_word.items()]
)
return [heap.extract_max().word for _ in range(min(k_value, len(count_by_word)))]


if __name__ == "__main__":
import doctest

doctest.testmod()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Solving the `Top k most frequent words` problem using a max-heap by cclauss · Pull Request #8685 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -327,6 +327,7 @@
* [Minimum Coin Change](dynamic_programming/minimum_coin_change.py)
* [Minimum Cost Path](dynamic_programming/minimum_cost_path.py)
* [Minimum Partition](dynamic_programming/minimum_partition.py)
* [Minimum Size Subarray Sum](dynamic_programming/minimum_size_subarray_sum.py)
* [Minimum Squares To Represent A Number](dynamic_programming/minimum_squares_to_represent_a_number.py)
* [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py)
* [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py)
Expand All@@ -339,6 +340,7 @@
* [Word Break](dynamic_programming/word_break.py)

## Electronics
* [Apparent Power](electronics/apparent_power.py)
* [Builtin Voltage](electronics/builtin_voltage.py)
* [Carrier Concentration](electronics/carrier_concentration.py)
* [Circular Convolution](electronics/circular_convolution.py)
Expand All@@ -348,6 +350,7 @@
* [Electrical Impedance](electronics/electrical_impedance.py)
* [Ind Reactance](electronics/ind_reactance.py)
* [Ohms Law](electronics/ohms_law.py)
* [Real And Reactive Power](electronics/real_and_reactive_power.py)
* [Resistor Equivalence](electronics/resistor_equivalence.py)
* [Resonant Frequency](electronics/resonant_frequency.py)

Expand DownExpand Up@@ -483,6 +486,7 @@
* [Astar](machine_learning/astar.py)
* [Data Transformations](machine_learning/data_transformations.py)
* [Decision Tree](machine_learning/decision_tree.py)
* [Dimensionality Reduction](machine_learning/dimensionality_reduction.py)
* Forecasting
* [Run](machine_learning/forecasting/run.py)
* [Gradient Descent](machine_learning/gradient_descent.py)
Expand DownExpand Up@@ -604,6 +608,7 @@
* [Perfect Number](maths/perfect_number.py)
* [Perfect Square](maths/perfect_square.py)
* [Persistence](maths/persistence.py)
* [Pi Generator](maths/pi_generator.py)
* [Pi Monte Carlo Estimation](maths/pi_monte_carlo_estimation.py)
* [Points Are Collinear 3D](maths/points_are_collinear_3d.py)
* [Pollard Rho](maths/pollard_rho.py)
Expand DownExpand Up@@ -1162,6 +1167,7 @@
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
* [Split](strings/split.py)
* [Text Justification](strings/text_justification.py)
* [Top K Frequent Words](strings/top_k_frequent_words.py)
* [Upper](strings/upper.py)
* [Wave](strings/wave.py)
* [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py)
Expand Down
31 changes: 25 additions & 6 deletions data_structures/heap/heap.py
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
from __future__ import annotations

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar


class Heap:
class Comparable(Protocol):
@abstractmethod
def __lt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __gt__(self: T, other: T) -> bool:
pass

@abstractmethod
def __eq__(self: T, other: object) -> bool:
pass


T = TypeVar("T", bound=Comparable)


class Heap(Generic[T]):
"""A Max Heap Implementation

>>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]
Expand All@@ -27,7 +46,7 @@ class Heap:
"""

def __init__(self) -> None:
self.h: list[float] = []
self.h: list[T] = []
self.heap_size: int = 0

def __repr__(self) -> str:
Expand DownExpand Up@@ -79,7 +98,7 @@ def max_heapify(self, index: int) -> None:
# fix the subsequent violation recursively if any
self.max_heapify(violation)

def build_max_heap(self, collection: Iterable[float]) -> None:
def build_max_heap(self, collection: Iterable[T]) -> None:
"""build max heap from an unsorted array"""
self.h = list(collection)
self.heap_size = len(self.h)
Expand All@@ -88,7 +107,7 @@ def build_max_heap(self, collection: Iterable[float]) -> None:
for i in range(self.heap_size // 2 - 1, -1, -1):
self.max_heapify(i)

def extract_max(self) -> float:
def extract_max(self) -> T:
"""get and remove max from heap"""
if self.heap_size >= 2:
me = self.h[0]
Expand All@@ -102,7 +121,7 @@ def extract_max(self) -> float:
else:
raise Exception("Empty heap")

def insert(self, value: float) -> None:
def insert(self, value: T) -> None:
"""insert a new value into the max heap"""
self.h.append(value)
idx = (self.heap_size - 1) // 2
Expand DownExpand Up@@ -144,7 +163,7 @@ def heap_sort(self) -> None:
]:
print(f"unsorted array: {unsorted}")

heap = Heap()
heap: Heap[int] = Heap()
heap.build_max_heap(unsorted)
print(f"after build heap: {heap}")

Expand Down
2 changes: 1 addition & 1 deletion machine_learning/linear_discriminant_analysis.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -399,7 +399,7 @@ def main():
if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q":
print("\n" + "GoodBye!".center(100, "-") + "\n")
break
system("clear" if name == "posix" else "cls") # noqa: S605
system("cls" if name == "nt" else "clear") # noqa: S605


if __name__ == "__main__":
Expand Down
101 changes: 101 additions & 0 deletions strings/top_k_frequent_words.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
"""
Finds the top K most frequent words from the provided word list.

This implementation aims to show how to solve the problem using the Heap class
already present in this repository.
Computing order statistics is, in fact, a typical usage of heaps.

This is mostly shown for educational purposes, since the problem can be solved
in a few lines using collections.Counter from the Python standard library:

from collections import Counter
def top_k_frequent_words(words, k_value):
return [x[0] for x in Counter(words).most_common(k_value)]
"""


from collections import Counter
from functools import total_ordering

from data_structures.heap.heap import Heap


@total_ordering
class WordCount:
def __init__(self, word: str, count: int) -> None:
self.word = word
self.count = count

def __eq__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__eq__(WordCount('b', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 1))
True
>>> WordCount('a', 1).__eq__(WordCount('a', 2))
False
>>> WordCount('a', 1).__eq__(WordCount('b', 2))
False
>>> WordCount('a', 1).__eq__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count == other.count

def __lt__(self, other: object) -> bool:
"""
>>> WordCount('a', 1).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 1).__lt__(WordCount('a', 2))
True
>>> WordCount('a', 1).__lt__(WordCount('b', 2))
True
>>> WordCount('a', 2).__lt__(WordCount('a', 1))
False
>>> WordCount('a', 2).__lt__(WordCount('b', 1))
False
>>> WordCount('a', 1).__lt__(1)
NotImplemented
"""
if not isinstance(other, WordCount):
return NotImplemented
return self.count < other.count


def top_k_frequent_words(words: list[str], k_value: int) -> list[str]:
"""
Returns the `k_value` most frequently occurring words,
in non-increasing order of occurrence.
In this context, a word is defined as an element in the provided list.

In case `k_value` is greater than the number of distinct words, a value of k equal
to the number of distinct words will be considered, instead.

>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 3)
['c', 'a', 'b']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 2)
['c', 'a']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 1)
['c']
>>> top_k_frequent_words(['a', 'b', 'c', 'a', 'c', 'c'], 0)
[]
>>> top_k_frequent_words([], 1)
[]
>>> top_k_frequent_words(['a', 'a'], 2)
['a']
"""
heap: Heap[WordCount] = Heap()
count_by_word = Counter(words)
heap.build_max_heap(
[WordCount(word, count) for word, count in count_by_word.items()]
)
return [heap.extract_max().word for _ in range(min(k_value, len(count_by_word)))]


if __name__ == "__main__":
import doctest

doctest.testmod()