Tutorial Solutions
A Question of Balance
Explain how we could globally balance a binary search tree using the following partition function:
#define size(tree) (tree)->size
btree_node *btree_partition (btree_node *tree, size_t k)
{
if (tree == NULL) return NULL;
size_t lsize = size (tree->left);
if (lsize > k) {
tree->left = btree_partition (tree->left, k);
tree = btree_rotate_right (tree);
}
if (lsize < k) {
tree->right = btree_partition (tree->right, k - 1 - lsize);
tree = btree_rotate_left (tree);
}
return tree;
}
Show the result of globally rebalancing this tree:
1
\
2
\
3
\
4
\
5
\
6
\
7
Insert items with the keys into a ‘normal’ binary search tree, into a binary search tree with root insertion, and into a splay tree. Draw the resulting trees. For each resulting tree, decide whether the tree is size balanced, height balanced, or unbalanced.
Explain the concept of ‘amortisation’ in the context of binary search trees and splay trees.
Insert the same keys into a randomised binary search tree; get someone to flip a coin each time a decision needs to be made as to whether the node is inserted at the leaf or root of the given subtree. Heads can mean root, tails can mean leaf. (Note: in the actual implementation from lectures, the probability that it is inserted at the root is approx , not a half.)
Hashing and Hash Tables
Draw a hash table of size 13. Use the hash function , and insert the keys into your table (in that order) using the following strategies for handling collisions:
- chaining
- linear probing
- double hashing, with a second hash function . (Why would a second hash function like not be very useful?)
For a hash table that uses chaining for collision resolution, with the chains sorted in ascending order on key value, what are the best case and worst case costs for inserting items into the table? Assume that the size of the table , and that the insertion cost is measured in terms of the number of key comparisons, and that the chains are sorted in key order. What will be average search cost after all the insertions are done?
Digest This!
Consider the following scheme for implementing a hash function by treating the characters in a string as bit patterns that form a long integer value.
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TABLESIZE 8191
#define U64SIZE (sizeof (uint64_t))
static uint64_t hash_short_string (char v[], int N);
int main(int argc, char *argv[])
{
if (argc < 2) {
printf ("Usage: %s <string>\n", argv[0]);
printf ("(<string> truncated after %zu characters)\n", U64SIZE);
return EXIT_FAILURE;
}
char key[U64SIZE] = {0};
strncpy (key, argv[1], U64SIZE);
printf ("Hash: %" PRIu64 "\n", hash_short_string (key, TABLESIZE));
return EXIT_SUCCESS;
}
static uint64_t hash_short_string(char v[], int N)
{
union IntStr {
uint64_t ikey;
uint8_t skey[U64SIZE];
} *is = (union IntStr *)v;
return (is->ikey % N);
}
Discuss how the hash_short_string()
function works.
What is the difference between a struct
and a union
?
How is the conversion achieved?
What happens if strings are provided whose length is smaller or larger than U64SIZE?
If there is any remaining time, use it to finish any questions you did not cover in previous weeks.