diff --git a/content/3_Silver/Intro_Sorted_Sets.mdx b/content/3_Silver/Intro_Sorted_Sets.mdx
index f7166aa755..11a625732e 100644
--- a/content/3_Silver/Intro_Sorted_Sets.mdx
+++ b/content/3_Silver/Intro_Sorted_Sets.mdx
@@ -2,7 +2,7 @@
id: intro-sorted-sets
title: 'More Operations on Sorted Sets'
author: Darren Yao, Benjamin Qi, Andrew Wang
-contributors: Aadit Ambadkar, Jeffrey Hu
+contributors: Aadit Ambadkar, Jeffrey Hu, Jason Sun
prerequisites:
- intro-sets
description:
@@ -48,17 +48,16 @@ redirects:
In sets and maps where keys (or elements) are stored in sorted order, accessing
or removing the next key higher or lower than some input key `k` is supported.
-Keep in mind that insertion and deletion will take $\mathcal{O}(\log N)$ time for
-sorted sets, which is more than the average $\mathcal{O}(1)$ insertion and
-deletion for unordered sets, but less than the worst case $\mathcal{O}(N)$ insertion
-and deletion for unordered sets.
+Keep in mind that insertion and deletion will take $\mathcal{O}(\log N)$ time
+for sorted sets, which is more than the average $\mathcal{O}(1)$ insertion and
+deletion for unordered sets, but less than the worst case $\mathcal{O}(N)$
+insertion and deletion for unordered sets.
## Using Iterators
In Bronze, we avoided discussion of any set operations involving iterators.
-
@@ -66,7 +65,6 @@ In Bronze, we avoided discussion of any set operations involving iterators.
-
In Java, iterators are helpful for looping through sets.
@@ -125,13 +123,44 @@ which can only be used once before calling the `next()` method.
+
+
+In Python, we can use `iter()` to obtain the iterator object of any iterable.
+Using `next()` on the iterator lets you iterate through the iterable. Below, a
+dictionary is used instead of a set because dictionaries keep order.
+
+Iterating through a dictionary representation of an ordered set:
+
+```py
+nums = {0: None, 1: None, 2: None, 4: None, 7: None}
+iterator = iter(nums)
+
+print(next(iterator)) # 0
+print(next(iterator)) # 1
+print(next(iterator)) # 2
+print(next(iterator)) # 4
+print(next(iterator)) # 7
+```
+
+As of Python 3.6, dictionaries are ordered by **insertion order**. For older
+versions, you can use an `OrderedDict` from `collections`. Keep in mind that
+the above representation is of an ordered set, not a sorted set, because Python
+does not have sorted sets in its standard library, as you will see in the next
+section.
+
+
+Python's iterators are fundamental to iteration and are used in its for loops
+and tuple unpacking. This is useful when you want more control over your
+iteration. It can also be simply used in cases where you just want the first or
+any element.
+
+
## Sorted Sets
-
The sorted `std::set` also supports:
@@ -168,7 +197,6 @@ make sure to avoid it!
-
`TreeSet`s in Java allow for a multitude of additional operations:
@@ -196,7 +224,67 @@ System.out.println(set.higher(23)); // ERROR, no such element exists
```
+
+
+Python does not have a sorted set nor a sorted map, so see the C++ or Java if
+you want an implementation from the standard library. However, if you are still
+curious regarding the Python implementation, you can find an AVL tree
+representation of a sorted set below.
+
+
+
+ definition and implementation of AVL Trees in Python
+
+
+
+
+
+You are not expected to know how to create an AVL tree in USACO Silver, nor is
+it recommended to do so for a representation of a sorted set since other
+languages have sorted sets built-in.
+
+
+
+Since some online judges include additional libraries, an implementation of
+sorted sets from the `sortedcontainers` library (which is not included in most
+online judges like USACO) is shown below. All operations shown below are
+$\mathcal{O}(\log N)$ time, except for its $\mathcal{O}(N \log N)$
+initialization.
+
+```py
+from sortedcontainers import SortedSet
+
+sorted_set = SortedSet([5, 1, 3, 2])
+print(sorted_set) # SortedSet([1, 2, 3, 4, 7])
+# Add elements
+sorted_set.add(4)
+sorted_set.add(6)
+print(sorted_set) # SortedSet([1, 2, 3, 4, 5, 6])
+
+# Remove elements
+sorted_set.discard(3)
+sorted_set.discard(5)
+print(sorted_set) # SortedSet([1, 2, 4, 6])
+
+# Check if an element is in the sorted set
+print(2 in sorted_set) # True
+print(100 in sorted_set) # False
+
+# Access elements by it's index
+print(sorted_set[0]) # 1 (smallest element, first index)
+print(sorted_set[-1]) # 6 (largest element, last index)
+print(sorted_set[2]) # 4
+
+# Get the index of an element
+print(sorted_set.index(4)) # 2
+
+# Find the index to insert the given value
+print(sorted_set.bisect_left(2)) # 1
+print(sorted_set.bisect_right(2)) # 2
+```
+
+
One limitation of sorted sets is that we can't efficiently access the $k^{th}$
@@ -207,7 +295,6 @@ structure called an [order statistic tree](/gold/PURS#order-statistic-tree).
## Sorted Maps
-
The ordered `map` also allows:
@@ -231,7 +318,6 @@ if (m.upper_bound(10) == m.end()) {
```
-
The ordered map additionally supports `firstKey` / `firstEntry` and `lastKey` /
@@ -255,7 +341,58 @@ System.out.println(map.lowerKey(3)); // ERROR
```
+
+
+Sorted maps in Python can be created by adding a dictionary to a sorted set,
+where each element in the sorted set is a key in the dictionary and values can
+be assigned with the dictionary. This is the most straightforward
+implementation, and the ground up implementation of a sorted set can be found in
+the above section.
+
+Additionally, you can implement a `SortedDict` with the `sortedcontainers`
+library. All operations shown below are $\mathcal{O}(\log N)$ time, except for
+an $\mathcal{O}(N \log N)$ initialization and $\mathcal{O}(N)$ time to get all
+items, keys, or values.
+
+```py
+from sortedcontainers import SortedDict
+sorted_map = SortedDict({1: "one", 4: "four", 3: "three"})
+print(sorted_map) # SortedDict({1: 'one', 3: 'three', 4: 'four'})
+
+# Add elements
+sorted_map[2] = "two"
+sorted_map[5] = "five"
+
+# Output SortedDict({1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five'})
+print(sorted_map)
+
+# Remove elements
+del sorted_map[3]
+print(sorted_map) # SortedDict({1: 'one', 2: 'two', 4: 'four', 5: 'five'})
+
+# Check if a key is in the sorted dict
+print(1 in sorted_map) # True
+print(100 in sorted_map) # False
+
+# Get the key's value
+print(sorted_map[2]) # two
+print(sorted_map[4]) # four
+
+# Get all items (key value pairs), keys, or values below
+print(*sorted_map.items()) # (1, 'one') (2, 'two') (4, 'four') (5, 'five')
+print(*sorted_map.keys()) # 1 2 4 5
+print(*sorted_map.values()) # one two four five
+
+# Find the index of an existing key
+print(sorted_map.index(2)) # 1
+
+# Find the index to insert a given key
+print(sorted_map.bisect_left(3)) # 2
+print(sorted_map.bisect_right(6)) # 4
+```
+
+
## Multisets
@@ -263,7 +400,6 @@ System.out.println(map.lowerKey(3)); // ERROR
A **multiset** is a sorted set that allows multiple copies of the same element.
-
In addition to all of the regular set operations,
@@ -271,8 +407,8 @@ In addition to all of the regular set operations,
- the `count()` method returns the number of times an element is present in the
multiset. However, this method takes time **linear** in the number of matches
so you shouldn't use it in a contest.
-- To remove a value __once__, use `ms.erase(ms.find(val))`.
-- To remove __all__ occurrences of a value, use `ms.erase(val)`.
+- To remove a value **once**, use `ms.erase(ms.find(val))`.
+- To remove **all** occurrences of a value, use `ms.erase(val)`.
Using `ms.erase(val)` erases __all__ instances of `val` from the multiset. To remove one instance of `val`, use `ms.erase(ms.find(val))`!
@@ -296,7 +432,6 @@ cout << ms.count(9) << '\n'; // 0
```
-
While there is no multiset in Java, we can implement one using the `TreeMap`
@@ -326,7 +461,6 @@ static void remove(int x) {
```
-
## Priority Queues
@@ -392,11 +526,16 @@ pq.add(6); // [7, 6, 5]
In Python (unlike in C++), we delete and retrieve the **lowest** element.
-Note that Python's priority queue is not encapsulated; `heapq` operates on a provided list directly by turning it into a heap, then doing operations on the heap.
+Note that Python's priority queue is not encapsulated; `heapq` operates on a
+provided list directly by turning it into a heap, then doing operations on the
+heap.
-Because of a heap's structure, printing out `pq` will **not** print out the elements in sorted order in Python; instead, it will print out the list. The comments below are a **representation** of what the heap contains, **not** what the contents of `pq` actually are.
+Because of a heap's structure, printing out `pq` will **not** print out the
+elements in sorted order in Python; instead, it will print out the list. The
+comments below are a **representation** of what the heap contains, **not** what
+the contents of `pq` actually are.