题目总体上还是不太难……只是,一个晚上都卡在第四题,做Java实现去了……

up date: 最后Java实现了一个上午,终于把第四题做好了。基本思想和Solution Manual是一样的,只是我允许相同key的情况——因为我认为,即使是带着相同的key,data也有可能不同。

11.2-1:

11.2-2:

11.2-3:

For every node, it's a linked list; therefore, by keeping the list sorted, the running time would change like this:

Operation

Original

New: keep sorted list

Insertion

O(1)

O(n/m)

Deletion

O(1+n/m)

O(n/m)

Successful Search

O(1+n/m)

O(1+n/m)

Unsuccessful Search

O(1)

O(1)

The problem is, when searching in a linked list, the traversing of every elements in this linked is still needed, no matter sorted or not.

11.2-4:

package hashing;

import java.util.LinkedList;
import java.util.Random;

/* Author Songyy
 * Start Time: 	2011年3月8日22:43:34
 * Second Time: 2011年3月9日9:30:00
 * Third Time: 	2011年3月9日11:40:36
 * End Time: 2011年3月9日13:25:03
 * 
 * 
 * Info:
 * 	This is to implement Question 11.2-4 in CLRS, 2nd ed.
 * 
 * Basic Idea:
 * 	1. Basic data structure: 
 * 		a hash table; each node has:
 * 		-- flag: true means it's a start of a hash chain
 * 		-- Double link on every chain
 * 			 (the empty node list can be treated as a stack of free space, but still need double link for update)
 *  	next empty node: a node reference:
 *  	-- pointed to the last occupied element whose's next is an empty node;
 *  	-- null if the table is full
 *  2. Basic Operation:
 *  	-- Insertion x:
 *  		node = table[hash[x.key]];
 *  		if flag==true:
 *  			find a new empty node;
 *				update link;
 *				copy the data into that new node;
 *  		else:
 *  			if it's empty:
 *  				unlink from the stack
 *  				set up new chain
 *  				copy data
 *  			if it's not:
 *  				copy old data to a new empty node
 *  				update chain
 *  				set up new chain
 *  				copy new data into this node
 *  	-- Search key:
 *  		node = table[key]
 *  		if flag==false: return false;
 *  		else: compare this key with others in the chain
 *  	-- Deletion x:
 *  		// assumption: x is a node in the table already. i.e., this node must exist in the table
 *  		free the x from the linked chain
 *  		x.flag = false;
 *  		add x to the list of the free space (to the beginning of the empty node list)
 */

class LinkedListHashTableNode {
	LinkedListHashTableNode former, next;
	HashObject data;
	/**
	 * False means it's used as a linked list, while true means it's used as a
	 * hash table node
	 */
	boolean flag;

	public LinkedListHashTableNode() {
		flag = false;
		former = null;
		next = null;
		data = null;
	}

	public LinkedListHashTableNode(HashObject x) {
		this();
		data = x;
	}

	public void cloneNode(LinkedListHashTableNode toNode) {
		toNode.former = former;
		toNode.next = next;
		toNode.data = data;
		toNode.flag = flag;
	}
}

class HashObject {
	Object data;
	int key;

	public HashObject(int key) {
		this.key = key;
	}
}

public class LinkedListHashTable {
	public static final int MAXSIZE = 100;
	private LinkedListHashTableNode table[] = new LinkedListHashTableNode[MAXSIZE];
	private LinkedListHashTableNode emptyNode;

	public LinkedListHashTable() {
		// first initialize the table
		for (int i = 0; i < MAXSIZE; i++) {
			table[i] = new LinkedListHashTableNode();
		}
		// set up the link for the empty node list
		// the first's former and the last's next node are null
		for (int i = 1; i < MAXSIZE; i++) {
			table[i].former = table[i - 1];
			table[i - 1].next = table[i];
		}

		// the start of the empty node
		emptyNode = table[0];
	}

	private int hash(int key) {
		return key % MAXSIZE;
	}

	/**
	 * @param x
	 * @return true if insert succeed, false if the table is full
	 */
	public boolean insert(HashObject x) {
		// check if the table is full
		if (emptyNode == null)
			return false;

		LinkedListHashTableNode node = table[hash(x.key)];

		// if the node is the beginning of a chain
		if (node.flag == true) {
			// find new empty node
			LinkedListHashTableNode tempEmptyNode = emptyNode;
			emptyNode = emptyNode.next;

			// unlink the supposed empty node
			tempEmptyNode.former = null;
			tempEmptyNode.next = null;

			// update chain
			tempEmptyNode.next = node.next;
			node.next = tempEmptyNode;
			tempEmptyNode.former = node;

			if (tempEmptyNode.next != null)
				tempEmptyNode.next.former = tempEmptyNode;

			// copy data
			tempEmptyNode.data = x;
		} else {
			// if the node is empty
			if (node.data == null) {
				// if it's the emptyNode, update emptyNode
				if (node == emptyNode) {
					emptyNode = emptyNode.next;
				}

				// unlink this node from the empty node list
				if (node.former != null) {
					node.former.next = node.next;
				}
				if (node.next != null) {
					node.next.former = node.former;
				}
				node.next = null;
				node.former = null;

				// set up a new chain
				node.former = null;
				node.next = null;
				node.flag = true;

				// copy data
				node.data = x;
			}
			// if not empty, it's in another chain
			else {
				// copy the old data to a new emptyNode
				// ** find a new empty node
				LinkedListHashTableNode tempEmptyNode = emptyNode;
				emptyNode = emptyNode.next;
				// ** copy from the node to tempEmptyNode
				tempEmptyNode.former = node.former;
				tempEmptyNode.next = node.next;
				tempEmptyNode.data = node.data;

				// update chain
				// -- node is not the beginning of the chain, thus have a former
				node.former.next = tempEmptyNode;
				if (node.next != null)
					node.next.former = tempEmptyNode;

				// set up new chain
				node.former = null;
				node.next = null;
				node.flag = true;

				// copy data
				node.data = x;
			}
		}
		return true;
	}

	/**
	 * @param key
	 * @return - null if unfound
	 */
	public LinkedListHashTableNode search(int key) {
		LinkedListHashTableNode node = table[hash(key)];
		if (node.flag == false) {
			return null;
		} else {
			while (node != null) {
				if (node.data.key == key)
					return node;
				node = node.next;
			}
		}
		return null;
	}

	/**
	 * @param x
	 *            - x is a node from that table, thus has the information about
	 *            former/ next
	 * @return - false if no need to delete
	 */
	public boolean delete(LinkedListHashTableNode x) {
		// x don't have data, thus don't need to delete
		if (x.data == null)
			return false;

		// else, x must be in the chain
		// ** x is at the beginning of a chain
		if (x.flag == true) {
			// ** if x doesn't have a next, then simply delete it
			if(x.next==null){
				x.flag = false;
			}
			// ** if x has a next, then need to copy it's next to x's position,
			// then delete its next
			else{
				// temp is at the beginning of the chain
				LinkedListHashTableNode temp = x;
				// x becomes the second element in the chain then
				x = x.next;
				temp.data = x.data;
			}
		}
		// ** unlink x from the chain first
		if (x.former != null){
			x.former.next = x.next;
		}
		if (x.next != null){
			x.next.former = x.former;
		}
		// ** make x to be empty
		x.data = null;
		x.former = null;x.next = null;
		
		// ** then add x to be the first element in emptyNode list
		x.next = emptyNode;
		if(emptyNode!=null){
			emptyNode.former = x;
		}
		emptyNode = x;
		
		return true;
	}

	public static void main(String[] args) {
		LinkedListHashTable a = new LinkedListHashTable();
		LinkedList<Integer> l = new LinkedList<Integer>();
		Random rand = new Random();
		for (int i = 0; i < 100; i++) {
			int t = rand.nextInt(10000);
			System.out.println(t % 100);
			l.add(t);
			if (a.insert(new HashObject(t)) == false)
				System.out.println("List Full!");
		}
		while (!l.isEmpty()) {
			int t = l.pop();
			if (a.search(t) == null) {
				System.out.println("Error: element " + t + "unfound.");
			}
		}
		// int test = 12, t = test;
		// for(int i=0;i<100;i++){
		// a.insert(new HashObject(t));
		// }
	}

}

11.2-5:

If |U| > m*n, then for the mapping from U to hash table of size m, it's at least a m*n to n mapping, and thus it's possible for one slot to have n elements.