XII. 代码库
在实现本书的代码时出现了某些模式。我们通常试图包含解决方案的完整代码,但在某些情况下它会非常冗余。
本附录提供了几个最有用的代码块的代码。
本书的所有代码都可以从 CrackingTheCodinglnterview.com 下载。
HashMapList
HashMapList 类本质上是 HashMap
例如,我们可能需要一个从整数映射到字符串列表的数据结构。通常,我们会这样写:
1 HashMap<Integer, Arraylist<String>> maplist =2 new HashMap<Integer, Arraylist<String>>();3 for (String s : strings) {4 int key = computeValue(s);5 if (!maplist.containsKey(key)) {6 maplist.put(key, new Arraylist<String>());7 }8 maplist.get(key).add(s);9 }
现在,我们可以这样写:
1 HashMaplist<Integer, String> maplist new HashMapList<Integer, String>();2 for (String s : strings) {3 int key = computeValue(s);4 maplist.put(key, s);5 }
这不是一个很大的变化,但是它使我们的代码更简单了。
1 public class HashMapList<T, E> {2 private HashMap<T, Arraylist<E>> map= new HashMap<T, Arraylist<E>>();34 /* Insert item into list at key. */5 public void put(T key, E item) {6 if (!map.containsKey(key)) {7 map.put(key, new Arraylist<E>());8 }9 map.get(key).add(item);10 }1112 /* Insert list of items at key. */13 public void put(T key, Arraylist<E> items) {14 map.put(key, items);15 }1617 /* Get list of items at key. */18 public Arraylist<E> get(T key) {19 return map.get(key);20 }2122 /* Check if hashmaplist contains key. */23 public boolean containsKey(T key) {24 return map.containsKey(key);25 }2627 /* Check if list at key contains value. */28 public boolean containsKeyValue(T key, E value) {29 Arraylist<E> list = get(key);30 if (list == null) return false;31 return list.contains(value);32 }3334 /* Get the list of keys. */35 public Set<T> keySet() {36 return map.keySet();37 }3839 @Override40 public String toString() {41 return map.toString();42 }43 }
TreeNode (Binary Search Tree)
虽然在可能的情况下使用内置的二叉树类非常好,甚至很好,但并不总是可行的。在许多问题中,我们需要访问节点或树类的内部(或需要调整这些内部),因此无法使用内置库。
TreeNode 类支持各种各样的功能,但我们并不一定希望每个问题/解决方案都使用这些功能。例如,TreeNode 类跟踪节点的父节点,尽管我们通常不使用它(或特别禁止使用它)。
为了简单起见,我们将此树实现为存储整型数据。
1 public class TreeNode {2 public int data;3 public TreeNode left, right, parent;4 private int size = 0;56 public TreeNode(int d) {7 data d;8 size = 1;9 }1011 public void insertinOrder(int d) {12 if (d <= data) {13 if (left == null) {14 setLeftChild(new TreeNode(d));15 } else {16 left.insertinOrder(d);17 }18 } else {19 if (right == null) {20 setRightChild(new TreeNode(d));21 } else {22 right.insertlnOrder(d);23 }24 }25 size++;26 }2728 public int size() {29 return size;30 }3132 public TreeNode find(int d) {33 if (d == data) {34 return this;35 } else if (d <= data) {36 return left != null? left.find(d) : null;37 } else if (d > data) {38 return right != null? right.find(d) : null;39 }40 return null;41 }4243 public void setLeftChild(TreeNode left) {44 this.left = left;45 if (left != null) {46 left.parent = this;47 }48 }4950 public void setRightChild(TreeNode right) {51 this.right = right;52 if (right != null) {53 right.parent = this;54 }55 }5657 }
该树被实现为二叉搜索树。但是,你可以将它用于其他目的。你只需要使用 setLeftChild / setRightChild 方法,或者左右子变量。
因此,我们将这些方法和变量保持为 public。我们需要这种方式来解决许多问题。
LinkedListNode (Linked List)
与 TreeNode 类一样,我们经常需要以内置链表类不支持的方式访问链表的内部。出于这个原因,我们实现了自己的类并将其用于许多问题。
1 public class LinkedListNode {2 public LinkedListNode next, prev, last;3 public int data;4 public LinkedlistNode(int d, LinkedlistNode n, LinkedListNode p){5 data = d;6 setNext(n);7 setPrevious(p);8 }910 public LinkedListNode(int d) {11 data = d;12 }1314 public LinkedListNode() {}1516 public void setNext(LinkedListNode n) {17 next = n;18 if (this == last) {19 last = n;20 }21 if (n != null && n.prev != this) {22 n.setPrevious(this);23 }24 }2526 public void setPrevious(LinkedListNode p) {27 prev = p;28 if (p != null && p.next != this) {29 p.setNext(this);30 }31 }3233 public LinkedListNode clone() {34 LinkedListNode next2 = null;35 if (next != null) {36 next2 = next.clone();37 }38 LinkedlistNode head2 = new LinkedListNode(data, next2, null);39 return head2;40 }41 }
同样,我们将方法和变量保持为 public,因为我们经常需要这种访问。这将允许用户“销毁”链表,然而我们实际上需要这种功能来实现我们的目的。
Trie & TrieNode
在一些问题中使用了 trie 数据结构,以便更容易地在字典(或有效单词列表)中查找单词是否是任何其他单词的前缀。当我们递归地构造单词时,经常使用这种方法,这样当单词无效时,我们就可以短路。
1 public class Trie {2 // The root of this trie.3 private TrieNode root;45 /* Takes a list of strings as an argument, and constructs a trie that stores6 * these strings. */7 public Trie(Arraylist<String> list) {8 root = new TrieNode();9 for (String word : list) {10 root.addWord(word);11 }12 }131415 /* Takes a list of strings as an argument, and constructs a trie that stores16 * these strings. */17 public Trie(String[] list) {18 root = new TrieNode();19 for (String word : list) {20 root.addWord(word);21 }22 }2324 /* Checks whether this trie contains a string with the prefix passed in as25 * argument. */26 public boolean contains(String prefix, boolean exact) {27 TrieNode lastNode = root;28 int i= 0;29 for (i = 0; i < prefix.length(); i++) {30 lastNode = lastNode.getChild(prefix.charAt(i));31 if (lastNode == null) {32 return false;33 }34 }35 return !exact || lastNode.terminates();36 }3738 public boolean contains(String prefix) {39 return contains(prefix, false);40 }4142 public TrieNode getRoot() {43 return root;44 }45 }
Trie 类的实现使用了 TrieNode 类,其实现如下。
1 public class TrieNode {2 /* The children of this node in the trie.*/3 private HashMap<Character, TrieNode> children;4 private boolean terminates = false;56 /* The character stored in this node as data.*/7 private char character;89 /* Constructs an empty trie node and initializes the list of its children to an10 * empty hash map. Used only to construct the root node of the trie. */11 public TrieNode() {12 children= new HashMap<Character, TrieNode>();13 }1415 /* Constructs a trie node and stores this character as the node's value.16 * Initializes the list of child nodes of this node to an empty hash map. */17 public TrieNode(char character) {18 this();19 this.character = character;20 }2122 /* Returns the character data stored in this node. */23 public char getChar() {24 return character;25 }2627 /* Add this word to the trie, and recursively create the child28 * nodes. */29 public void addWord(String word) {30 if (word== null || word.isEmpty()) {31 return;32 }3334 char firstChar word.charAt(0);3536 TrieNode child getChild(firstChar);37 if (child== null) {38 child= new TrieNode(firstChar);39 children.put(firstChar, child);40 }4142 if (word.length() > 1) {43 child.addWord(word.substring(l));44 } else {45 child.setTerminates(true);46 }47 }4849 /* Find a child node of this node that has the char argument as its data. Return50 * null if no such child node is present in the trie. */51 public TrieNode getChild(char c) {52 return children.get(c);53 }5455 /* Returns whether this node represents the end of a complete word. */56 public boolean terminates() {57 return terminates;58 }5960 /* Set whether this node is the end of a complete word.*/61 public void setTerminates(boolean t) {62 terminates= t;63 }64 }
