In Python, lists, sets, and key-value mappings (dictionaries) are provided directly in the language as container classes . They manage objects of other classes (also potentially of different data types).
5.1 Introduction
In the following, I first describe sequential data types and especially their operations. After that, you briefly look at lists, sets, and dictionaries.
5.1.1 Sequential Data Types
in: elem in values checks if the element is in the sequence.
not in: elem not in values checks if the element is not in the sequence.
+/+=: values1 + values2 and values1 += values2 appends the sequence values2 to the other sequence and returns a new sequence.
*: Repeats the sequence n times.
[index]: values[index] leads to an indexed access and returns the ith element from values. Specifically, [-1] can be used to access the last element.
[start:end]: values[start:end] performs slicing and returns the elements from position start to exclusive end from values as a new sequence. There are two interesting variants. On the one hand, [:] (i. e. without range specification) returns a copy of the entire sequence. On the other hand, [start:] and [:end] respectively return the parts starting at start to the end or from the beginning to the index end exclusive.
[start:end:step]: values[start:end:step] results in slicing and returns the elements from position start to exclusive end with a step size of step from values as a new sequence. There is an interesting variant [::-1] without range specification and with negative step size, which creates a new sequence in reverse order of the original sequence.
len(): len(values) returns the size (i. e. the number of elements in the data container).
min()/max(): Calls to min(values) resp. max(values) gets the element with the smallest or largest value from values.
sum(): sum(values) sums the values from values.
Example
5.1.2 Lists
pop(i) returns the ith element of the list and removes it from the list. By default (i. e., without specifying the index), the first element is returned.
list[i] = element replaces the element at position i with the passed element.
append(element) appends the element to the end of the list.
insert(i, element) inserts the element at index i in the list.
extend(other_list) appends the elements of other_list to the end of the list.
count(value) counts how many times the value value occurs in the list.
index(value) returns the index where the value value first occurs in the list.
remove(value) removes the element with the value value from the list. Only the first one will be deleted if multiple elements with this value exist. If there is no such value, a ValueError is raised.
reverse() reverses the order of the elements in the list.
sort() sorts the list in ascending order. An inverse (descending) sort is obtained with sort(reverse=True).
Example
List Comprehension
In addition, you saw the specification of a condition in the initial example. In general, you should avoid the complexity getting too large. This assists in ensuring understandability and maintainability.
Regardless, comprehensions are a compelling and helpful feature of Python that you should undoubtedly master.
Example: Custom Implementation of remove_all( )
Removing all elements from a collection that match a certain value is a functionality that is unfortunately not built in. Different variants to solve this are shown below.
At first these solutions look quite good, but both variants iterate using the outer loop (while true/in) and a hidden inner one (by the implementation of remove()) resulting in a running time of O(n2).
As convenient as it is for smaller datasets to get the data as a list, there are some reasons why this is not feasible for large datasets. First of all, it may require a lot of memory. Secondly, not all values have to be kept for different calculations. Thus, the change of the mode of operation on Lazy Evaluation in Python 3 helps to save memory and increase performance. If you want to process the data directly and need a list, a simple wrapping is appropriate, as shown above. The dataset described by the filter is converted into a list.
Example: Custom Implementation of collect_all( )
Check Implementations
5.1.3 Sets
add(element) adds an element to the set if it does not already exist.
update(elements) inserts the elements elements from a list or tuple into a set if not already present in the original set.
remove(element)/discard(element) deletes the element from the set. If the element does not exist, remove() will raise a KeyError. With discard(), on the other hand, nonexistence will be ignored and nothing will happen.
pop() removes the first element from the set (as an iteration would return them).
clear() deletes all elements from the set.
copy() returns a (shallow) copy of the set.2
Example
5.1.4 Key-Value Mappings (Dictionaries)
Let’s now turn to mappings from keys to values. They are also called dictionaries or lookup tables; other terms are associative arrays and hashes, respectively. Regardless of the name, the underlying idea is to assign a value for a unique key. An intuitive example is a telephone directory where names are mapped to telephone numbers. A search by name (key) usually returns a phone number (value) quite quickly. If there is no mapping back from phone number to name, finding a name to a phone number becomes quite laborious.
dictionary[key] = value adds a mapping (from key to value) as an entry to this dictionary. If a value is already stored for the given key, it gets overwritten with the new value.
update(other_dictionary) inserts all entries from the passed dictionary into this dictionary. This overwrites values of already existing entries analogous to the way simple assignment works.
items() generates a list containing all key-value pairs of the dictionary as tuples.
keys()/values() returns a list containing all keys or values stored in the dictionary.
get(key, default_value) get the associated value for a key key. If no entry exists for the key, the default value default_value is returned. If no default value was specified in the call, None is returned.
pop(key) deletes an entry (key and associated value) from the dictionary. The value associated with the key key is returned. If no entry was stored for this key, a KeyError is the result.
clear() removes all entries of the dictionary.
Unfortunately, there are no functions like contains_key() or contains_value() to check if a special key or value is stored in the dictionary. However, this functionality can be easily recreated by querying with in, as you will see in the following example.
Example
Example: Filtering Elements of a Dictionary in a General Way
You can either use the more general function, which gets a filter for key and value, or the specific function designed directly for value filtering.
5.1.5 The Stack as a LIFO Data Structure
In the following, I describe the data structure stack. It is not directly a part of Python, but it proves to be very practical for various use cases and can be easily implemented.
- 1.
push(element) adds an element on top.
- 2.
pop() picks and removes the top element.
- 3.
peek() takes a peek at the top element.
- 4.
is_empty() checks if the stack is empty.
These four methods are sufficient to use stacks profitably for various tasks in practice and for algorithms, for example, when sequences have to be reversed. This property is described in computer science by the term LIFO for Last In, First Out. Sometimes it is referred as FCFS for First Come, First Serve.
Exercise 2 is about implementing a stack on your own.
Example
5.1.6 The Queue as a FIFO Data Structure
To conclude this introduction to basic data structures, I would like to talk about queues. They are also not a part of Python. Like a stack, a queue is often very useful and can also be easily implemented.
A queue is similar to a line at a cash register. People queue up, and the person who came first is served first, known in computer science as FIFO for First In, First Out.
- 1.
enqueue(element) adds an element to the end of the queue.
- 2.
dequeue() takes a look at the element at the beginning of the queue.
- 3.
peek() takes a look at the element at the beginning of the queue.
- 4.
is_empty() checks if the queue is empty.
These four methods are sufficient to create queues for various tasks in practice and for algorithms, such as if you intend to transform recursive algorithms into iterative ones.
Implementation
Example
Let’s recap: With a stack, you get the element stored last as the first element (that is, in reverse insertion order). This is why it is called Last-In-First-Out (LIFO) . For insertion, as you know, you conceptually use a function called push() and for removal you use one called pop(). On the other hand, a queue represents a queue as we know it from shopping or ticket machines. Here, the person who was there first gets to go first. Accordingly, one speaks of First-In-First-Out (FIFO). Typically, the corresponding operations are called enqueue() and dequeue().
Discussion: What is not optimal about this? First of all, it is pretty obvious that the function names of the list do not optimally match those commonly used for stacks and queues. Worse, if you accidentally specify no index or the wrong index for pop(), this leads to confusion. Even worse, stacks and queues conceptually do not allow indexed access to elements, but lists do. And this is only the beginning of possible problems. Because the above implementations (or to be precise, usages) are lists, you can also call arbitrary functions that have nothing to do with stacks and queues at all, such as insert() or remove(). If this is not clear enough, you could also sort the elements by calling sort() and thus probably mess up the desired order a lot. As you can see, the pitfalls are many.
Conclusion
Based on this reasoning and the need for intuitive handling without risk of misuse, the definition of your own classes for providing the specific data structures stack and queue becomes obvious.
5.2 Exercises
5.2.1 Exercise 1: Common Elements (★★✩✩✩)
Find the common elements of two lists, A and B, and return them as a set. Implement this, both with and without using matching functions from Python’s sets. Write your own function find_common(values1, values2), which works like the Python function intersection().
Examples
Input A | Input B | Result |
---|---|---|
[1, 2, 4, 7, 8] | [2, 3, 7, 9] | {2, 7} |
[1, 2, 7, 4, 7, 8] | [7, 7, 3, 2, 9] | {2, 7} |
[2, 4, 6, 8] | [1, 3, 5, 7, 9] | ∅ = set() |
5.2.2 Exercise 2: Your Own Stack (★★✩✩✩)
Define the basic requirements for a stack and implement class Stack based on these requirements using a list.
5.2.3 Exercise 3: List Reverse (★★✩✩✩)
Exercise 3a: List Reverse (★✩✩✩✩)
Write function reverse(values) that returns the elements of the original list in reverse order—of course without calling the reverse() function of the list.
Examples
Input | Result |
---|---|
[1, 2, 3, 4] | [4, 3, 2, 1] |
[“A”, “BB”, “CCC”, “DDDD”] | [“DDDD”, “CCC”, “BB”, “A”] |
Exercise 3b: List Reverse Inplace (★★✩✩✩)
What is different if you want to implement reversing the order inplace to be memory- optimal for very large datasets? What should be given then?
Exercise 3c: List Reverse Without Performant Index Access (★★✩✩✩)
Now let’s assume that no performant random index access is available. What happens if you want to reverse the order and any position-based access will result in O(n) and therefore O(n2) for the complete reversal process. How do you avoid this?
Use a stack.
5.2.4 Exercise 4: Remove Duplicates (★★✩✩✩)
You are supposed to remove duplicate entries from a list. The constraint is that the original order should be preserved. Write function remove_duplicates(values).
Examples
Input | Result |
---|---|
[1, 1, 2, 3, 4, 1, 2, 3] | [1, 2, 3, 4] |
[7, 5, 3, 5, 1] | [7, 5, 3, 1] |
[1, 1, 1, 1] | [1] |
5.2.5 Exercise 5: Maximum Profit (★★★✩✩)
Imagine that you have a sequence of prices ordered in time and that you want to calculate the maximum profit. The challenge is to determine at which time (or value, in this case) it would be ideal to buy and to sell. Write function max_revenue(prices) for this purpose, where the temporal order is expressed by the index in the list.
Examples
Input | Result |
---|---|
[250, 270, 230, 240, 222, 260, 294, 210] | 72 |
[0, 10, 20, 30, 40, 50, 60, 70] | 70 |
[70, 60, 50, 40, 30, 20, 10, 0] | 0 |
[ ] | 0 |
5.2.6 Exercise 6: Longest Sequence (★★★✩✩)
Suppose you are modeling stock prices or altitudes of a track by a list of numbers. Find the longest sequence of numbers whose values ascend or at least stay the same. Write function find_longest_growing_sequence(values).
Examples
Input | Result |
---|---|
[7, 2, 7, 1, 2, 5, 7, 1] | [1, 2, 5, 7] |
[7, 2, 7, 1, 2, 3, 8, 1, 2, 3, 4, 5] | [1, 2, 3, 4, 5] |
[1, 1, 2, 2, 2, 3, 3, 3, 3] | [1, 1, 2, 2, 2, 3, 3, 3, 3] |
5.2.7 Exercise 7: Well-Formed Braces (★★✩✩✩)
Write function check_parentheses(braces_input) that checks whether a sequence of braces is neatly nested in each case. This should accept any round, square, and curly braces but no other characters.
Examples
Input | Result | Comment |
---|---|---|
“(( ))” | True | |
“({[ ]})” | True | |
“((( ))” | False | Odd number of braces |
“((a)” | False | Wrong character, no braces |
“((])” | False | No matching braces |
5.2.8 Exercise 8: Pascal’s Triangle (★★★✩✩)
Write function pascal(n) that computes Pascal’s triangle in terms of nested lists. As you know, each new line results from the previous one. If there are more than two elements in it, two values are added and the sums build the values of the new line. In each case, a 1 is appended to the front and back.
Example
5.2.9 Exercise 9: Check Magic Triangle (★★★✩✩)
Write function is_magic_triangle(values) that checks whether a sequence of numbers forms a magic triangle. Such a triangle is defined as one where the respective sums of the three sides’ values must all be equal.
Examples
Input | Values 1 | Values 2 |
---|---|---|
side 1 | 1 + 5 + 3 = 9 | 2 + 5 + 9 + 1 = 17 |
side 2 | 3 + 4 + 2 = 9 | 1 + 6 + 7 + 3 = 17 |
side 3 | 2 + 6 + 1 = 9 | 3 + 4 + 8 + 2 = 17 |
Tip Model the individual sides of the triangle as sublists.
5.2.10 Exercise 10: Most Frequent Elements (★★✩✩✩)
Write function value_count(values) that determines a histogram (i. e., the distribution of the frequencies of the numbers in the given list). Also write function sort_dict_by_value(dictionary) to sort the dictionary by its values instead of by keys. Thereby a descending sorting is to be realized so that smaller values are listed at the beginning.
Examples
Input | Result | Most frequent(s) |
---|---|---|
[1, 2, 3, 4, 4, 4, 3, 3, 2, 4] | {1=1, 2=2, 3=3, 4=4} | 4=4 |
[1, 1, 1, 2, 2, 2, 3, 3, 3] | {1=3, 2=3, 3=3} | Depending on query, logically all |
5.2.11 Exercise 11: Addition of Digits (★★★✩✩)
Consider two decimal numbers that are to be added. Sounds simple, but for this assignment, the numbers are interestingly represented as a list of digits. Write function list_add(values1, values2). Also, consider the special case where there is an overflow.
Exercise 11a: Addition (★★★✩✩)
In the first part of the task, the digits are to be stored in the order of their occurrence in the list.
Examples
Input 1 | Input 2 | Result |
---|---|---|
123 = [1, 2, 3] | 456 = [4, 5, 6] | 579 = [5, 7, 9] |
927 = [9, 2, 7] | 135 = [1, 3, 5] | 1062 = [1, 0, 6, 2] |
Exercise 11b: Addition Inverse (★★★✩✩)
What changes if the digits are stored in reverse order in the list?
Examples
Input 1 | Input 2 | Result |
---|---|---|
123 = [3, 2, 1] | 456 = [6, 5, 4] | 579 = [9, 7, 5] |
927 = [7, 2, 9] | 135 = [5, 3, 1] | 1062 = [2, 6, 0, 1] |
5.2.12 Exercise 12: List Merge (★★✩✩✩)
Given two lists of numbers, each sorted in ascending order, merge them into a result list according to their order. Write function merge(values1, values2).
Examples
Input 1 | Input 2 | Result |
---|---|---|
1, 4, 7, 12, 20 | 10, 15, 17, 33 | 1, 4, 7, 10, 12, 15, 17, 20, 33 |
2, 3, 5, 7 | 11, 13, 17 | 2, 3, 5, 7, 11, 13, 17 |
2, 3, 5, 7, 11 | 7, 11, 13, 17 | 2, 3, 5, 7, 7, 11, 11, 13, 17 |
[1, 2, 3] | ∅ = [ ] | [1, 2, 3] |
5.2.13 Exercise 13: Excel Magic Select (★★✩✩✩)
If you have worked a little with Excel, then you have probably used the Magic Selection. It continuously populates a selected area with values based on the previous values. This works for numbers, weekdays, or dates, for example. To achieve something similar on your own, write function generate_following_values(current_value, sequence_length) that implements this for numbers. Create a variation suitable for weekdays and with the following signature: generate_following_values_for_- predefined(predefined_values, current_value, sequence_length).
Examples
Initial value | Count | Result |
---|---|---|
1 | 7 | [1, 2, 3, 4, 5, 6, 7] |
5 | 4 | [5, 6, 7, 8] |
FRIDAY | 8 | [FRIDAY, SATURDAY, SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY] |
5.2.14 Exercise 14: Stack-Based Queue (★★✩✩✩)
You learned about stack and queue data structures in the introduction and implemented a queue based on a list. Then in exercise 2, you implemented a stack itself. Now you are asked to build a queue based on the stack data structure.
Example
5.3 Solutions
5.3.1 Solution 1: Common Elements (★★✩✩✩)
Find the common elements of two lists, A and B, and return them as a set. Implement this, both with and without using matching functions from Python’s sets. Write your own function find_common(values1, values2), which works like the Python function intersection().
Examples
Input A | Input B | Result |
---|---|---|
[1, 2, 4, 7, 8] | [2, 3, 7, 9] | {2, 7} |
[1, 2, 7, 4, 7, 8] | [7, 7, 3, 2, 9] | {2, 7} |
[2, 4, 6, 8] | [1, 3, 5, 7, 9] | ∅ = set( ) |
Despite this improvement, it seems too complicated. How can you make it better?
Verification
5.3.2 Solution 2: Your Own Stack (★★✩✩✩)
Define the basic requirements for a stack and implement class Stack based on these requirements using a list.
- 1.
push(element) adds an element on top.
- 2.
pop() picks and removes the top element.
- 3.
peek() takes a look at the top element.
- 4.
is_empty() checks if the stack is empty.
Perhaps somewhat more comprehensible would be to add elements at the beginning of the list and take them from there. However, this would be unfavorable in terms of performance. Why? This would result in constant recopying of the internal data of the list.
In addition, one could argue that the Python online documentation3 describes how to use lists as stacks. Even if possible, the interface is not restricted to the above methods. In my book Der Weg zum Java-Profi [Ind20] I discuss in detail what can be problematic about this.
If names start with _, then by convention, the one underscore means that this method or attribute is considered private and an implementation detail of the class. However, Python does not enforce this. Especially, access is not prevented. Instead, you must rely on other programmers to observe this fact.
A double underscore (__) marks an internal method. When used for attributes, this attribute is no longer visible to the outside for other classes under its name. This is also true for methods.
Verification
5.3.3 Solution 3: List Reverse (★★✩✩✩)
Solution 3a: List Reverse (★✩✩✩✩)
Write function reverse(values) that returns the elements of the original list in reverse order—of course without calling the reverse() function of the list.
Examples
Input | Result |
---|---|
[1, 2, 3, 4] | [4, 3, 2, 1] |
[“A”, “BB”, “CCC”, “DDDD”] | [“DDDD”, “CCC”, “BB”, “A”] |
Solution 3b: List Reverse Inplace (★★✩✩✩)
What is different if you want to implement reversing the order inplace to be memory- optimal for very large datasets? What should be given then?
Solution 3c: List Reverse Without Performant Index Access (★★✩✩✩)
Now let’s assume that no performant random index access is available. What happens if you want to reverse the order and any position-based access will result in O(n) and therefore O(n2) for the complete reversal process. How do you avoid this?
Use a stack.
Verification
5.3.4 Solution 4: Remove Duplicates (★★✩✩✩)
You are supposed to remove duplicate entries from a list. The constraint is that the original order should be preserved. Write function remove_duplicates(values).
Examples
Input | Result |
---|---|
[1, 1, 2, 3, 4, 1, 2, 3] | [1, 2, 3, 4] |
[7, 5, 3, 5, 1] | [7, 5, 3, 1] |
[1, 1, 1, 1] | [1] |
Verification
5.3.5 Solution 5: Maximum Profit (★★★✩✩)
Imagine that you have a sequence of prices ordered by time and you want to calculate the maximum profit. The challenge is to determine at which time (or value in this case) it would be ideal to buy and to sell. Write function max_revenue(prices) for this purpose, where the temporal order is expressed by the index in the list.
Examples
Input | Result |
---|---|
[250, 270, 230, 240, 222, 260, 294, 210] | 72 |
[0, 10, 20, 30, 40, 50, 60, 70] | 70 |
[70, 60, 50, 40, 30, 20, 10, 0] | 0 |
[ ] | 0 |
Algorithm Initially, you may be tempted to determine the minimum and the maximum and simply return the difference. After a short reflection, it becomes clear that a time dimension has to be considered in this case. First, a purchase and then a sale at a higher price must take place to realize a profit.
Value | 255 | 260 | 250 | 240 | 228 | 270 | 300 | 210 | 245 |
Minimum | 255 | 255 | 250 | 240 | 228 | 228 | 228 | 210 | 210 |
Difference | 0 | 5 | 0 | 0 | 0 | 42 | 72 | 0 | 35 |
Max. Difference | 0 | 5 | 5 | 5 | 5 | 42 | 72 | 72 | 72 |
Optimized algorithm The variation just shown requires two passes. As long as the accesses are made in memory, this hardly plays a crucial role in the performance. The situation is somewhat different if the data is determined each time, for example, via a REST call or from a database.
Verification
5.3.6 Solution 6: Longest Sequence (★★★✩✩)
Suppose you are modeling stock prices or altitudes of a track by a list of numbers. Find the longest sequence of numbers whose values ascend or at least stay the same. Write function find_longest_growing_sequence(values).
Examples
Input | Result |
---|---|
[7, 2, 7, 1, 2, 5, 7, 1] | [1, 2, 5, 7] |
[7, 2, 7, 1, 2, 3, 8, 1, 2, 3, 4, 5] | [1, 2, 3, 4, 5] |
[1, 1, 2, 2, 2, 3, 3, 3, 3] | [1, 1, 2, 2, 2, 3, 3, 3, 3] |
Algorithm Here a so-called greedy algorithm is used. The idea is to collect the subsequent elements starting from one element until the next element is smaller than the current one. A temporary list and a result list are used for this purpose. Both are initially empty and are successively filled: the temporary list at each element read that is greater than or equal to the predecessor and the result list whenever a smaller successor value is found. If a value is smaller, the temporary list is cleared and starts as a one-element list with the current value. If the result list at a flank change is shorter than the temporary list with the previously collected elements, then the temporary list becomes the new result list. This procedure is repeated until you reach the end of the initial list.
Input | Current character | Temporary list | Result list |
---|---|---|---|
1272134572 | 1 | 1 | |
1272134572 | 2 | 12 | |
1272134572 | 7 | 127 | |
1272134572 | 2 | 2 | 127 |
1272134572 | 1 | 1 | 127 |
1272134572 | 3 | 13 | 127 |
1272134572 | 4 | 134 | 127 |
1272134572 | 5 | 1345 | 127 |
1272134577 | 7 | 13457 | 127 |
1272134572 | 2 | 2 | 13457 |
Be sure to note the additional check after the for loop—otherwise, a final sequence would not be correctly returned as a result.
Procedure for sections of equal length When checking for the longest sequence, you can either compare with > or >=. If there are two or more sequences of the same length, in the first case with > the first one is taken as a result, with >= always the last one.
Verification
5.3.7 Solution 7: Well-Formed Braces (★★✩✩✩)
Write function check_parentheses(braces_input) that checks whether a sequence of braces is neatly nested in each case. This should accept any round, square and curly braces but no other characters.
Examples
Input | Result | Comment |
---|---|---|
“(( ))” | True | |
“({[ ]})” | True | |
“((( ))” | False | Odd number of braces |
“((a)” | False | Wrong character, no braces |
“((])” | False | No matching braces |
Input | Current character | Stack | Comment |
---|---|---|---|
(()] | Start | ||
(()] | ( | ( | Store |
(()] | ( | (( | Store |
[()] | ) | ( | Match |
(()] | ] | ( | Mismatch |
Verification
Let’s look again at the implementation of the check and the return values. Several comments exist why True or False is returned. Wouldn’t it be more intuitive to express this with a suitable enumeration as a return? Let’s take a look at that now in the bonus.
Bonus
Verification
5.3.8 Solution 8: Pascal’s Triangle (★★★✩✩)
Write function pascal(n) that computes Pascal’s triangle in terms of nested lists. As you know, each new line results from the previous one. If there are more than two elements in it, two values are added, and the sums build the values of the new line. In each case, a 1 is appended to the front and back.
Example
Computing a row’s values based on the predecessor row is performed for all rows with n ≥ 2 as follows: If there is more than one value stored in the predecessor row list, iterate through it and sum each. To complete the computation, the value 1 is appended at the front and the back.
Somewhat more formally it can be written as follows, where the index of the rows and columns starts from 1 and not as in Python from 0:
Verification
5.3.9 Solution 9: Check Magic Triangle (★★★✩✩)
Write function is_magic_triangle(values) that checks whether a sequence of numbers forms a magic triangle. Such a triangle is defined as one where the respective sums of the three sides’ values must all be equal.
Examples
Input | Values 1 | Values 2 |
---|---|---|
side 1 | 1 + 5 + 3 = 9 | 2 + 5 + 9 + 1 = 17 |
side 2 | 3 + 4 + 2 = 9 | 1 + 6 + 7 + 3 = 17 |
side 3 | 2 + 6 + 1 = 9 | 3 + 4 + 8 + 2 = 17 |
Model the individual sides of the triangle as sublists.
If the problem is initially unclear, it is advisable to reduce the problem to one or two concrete value assignments and to find the appropriate abstractions based on these.
Verification
- 1.
The variable pos models the current position within the list. The new position is determined by adding 1. However, you need to reaccess the list’s first value at the end of the list, so a modulo operation is used here.
- 2.
After adding up the values for one side, you must go back by one position since the end value of one side of the triangle is also the start value of the next side.
Verification
The verification is performed with a unit test analogous to the previous one and therefore not shown again.
5.3.10 Solution 10: Most Frequent Elements (★★✩✩✩)
Write function value_count(values) that determines a histogram (i. e., the distribution of the frequencies of the numbers in the given list). Also, write function sort_dict_by_value(dictionary) to sort the dictionary by its values instead of by keys. Thereby a descending sorting is realized, so that smaller values are listed at the beginning.
Examples
Input | Result | Most frequent(s) |
---|---|---|
[1, 2, 3, 4, 4, 4, 3, 3, 2, 4] | {4=4, 3=3, 2=2, 1=1} | 4=4 |
[1, 1, 1, 2, 2, 2, 3, 3, 3] | {1=3, 2=3, 3=3} | Depending on query, logically all |
Verification
5.3.11 Solution 11: Addition of Digits (★★★✩✩)
Consider two decimal numbers that are to be added. Sounds simple, but for this assignment, the numbers are interestingly represented as a list of digits. Write function list_add(values1, values2). Also, consider the special case where there is an overflow.
Solution 11a: Addition (★★★✩✩)
In the first part of the task, the digits are to be stored in the order of their occurrence in the list.
Examples
Input 1 | Input 2 | Result |
---|---|---|
123 = [1, 2, 3] | 456 = [4, 5, 6] | 579 = [5, 7, 9] |
927 = [9, 2, 7] | 135 = [1, 3, 5] | 1062 = [1, 0, 6, 2] |
You could also come up with the idea of combining the two sequences of values with zip() and traversing them backwards. However, then you still need a wrapping with list(), since zip() is not reversible. However, this variant also fails since zip() restricts the combination to the smallest length of the sequences passed. Thus, again, you cannot add sequences of numbers of different lengths.
In the implementation, my Java origin can be spotted.
Verification
Solution 11b: Addition Inverse (★★★✩✩)
What changes if the digits are stored in reverse order in the list?
Examples
Input 1 | Input 2 | Result |
---|---|---|
123 = [3, 2, 1] | 456 = [6, 5, 4] | 579 = [9, 7, 5] |
927 = [7, 2, 9] | 135 = [5, 3, 1] | 1062 = [2, 6, 0, 1] |
Verification
5.3.12 Solution 12: List Merge (★★✩✩✩)
Given two lists of numbers, each sorted in ascending order, merge them into a result list according to their order. Write function merge(values1, values2).
Examples
Input 1 | Input 2 | Result |
---|---|---|
1, 4, 7, 12, 20 | 10, 15, 17, 33 | 1, 4, 7, 10, 12, 15, 17, 20, 33 |
2, 3, 5, 7 | 11, 13, 17 | 2, 3, 5, 7, 11, 13, 17 |
2, 3, 5, 7, 11 | 7, 11, 13, 17 | 2, 3, 5, 7, 7, 11, 11, 13, 17 |
[1, 2, 3] | ∅ = [] | [1, 2, 3] |
Alternative algorithm One variant is to generate the result data structure in advance. However, this leads to more index variables, and the entire thing becomes confusing. How can you avoid index access?
The built-in function chain() is used here, which links two iterables together (i.e., make one out of two). Here it is used to restore the original dataset of the iterator.
Verification
5.3.13 Solution 13: Excel Magic Select (★★✩✩✩)
If you have worked a little with Excel, then you have probably used the Magic Selection. It continuously populates a selected area with values based on the previous values. This works for numbers, weekdays, or dates, for example. To achieve something similar on your own, write function generate_following_values(current_value, sequence_length) that implements this for numbers. Create a variation suitable for weekdays and with the following signature: generate_following_values_for_- predefined(predefined_values, current_value, sequence_length).
Examples
Initial value | Count | Result |
---|---|---|
1 | 7 | [1, 2, 3, 4, 5, 6, 7] |
5 | 4 | [5, 6, 7, 8] |
FRIDAY | 8 | [FRIDAY, SATURDAY, SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY] |
Modified algorithm It is similarly easy to fill in with days of the week or via a list of predefined values, which, unlike numerical values, always repeat according to the length of the sequence.
Verification
5.3.14 Solution 14: Stack-Based Queue (★★✩✩✩)
You learned about stack and queue data structures in the introduction and implemented a queue based on a list. Then in exercise 2, you implemented a stack itself. Now you are asked to build a queue based on the stack data structure.
Example
Verification
To test your implementation of the stack-based queue, execute the main() function and see if the output is as expected.
5.4 Summary: What You Learned
This chapter deepened your knowledge of basic data structures like lists, sets, and dictionaries. This knowledge is essential in business applications. These structures are useful for solving many tasks, not only individually but also in combination, such as the deletion of duplicates from lists. In addition, the exercise of the magic triangle, for example, trained abstract thinking. A small delicacy was to program the auto-completion of Excel itself. It is quite surprising what an elegant implementation this results in. Finally, you developed some functionality for merging lists. This is an elementary component for Merge Sort.