Java面試13|算法
阿新 • • 發佈:2017-09-09
poll() tom 效率 setname 調用 產品 boot pty rom
Java寫算法時常用的函數:
Stack
void push(E e):將給定元素”壓入”棧中。存入的元素會在棧首。即:棧的第一個元素
E pop():將棧首元素刪除並返回。
Queue
boolean offer(E e):將元素追加到隊列末尾,若添加成功則返回true。
E poll():從隊首刪除並返回該元素。
E peek():返回隊首元素,但是不刪除
Deque是雙端隊列,有Stack和Queue的所有方法。
隊首操作:
push、peek、pop
隊尾操作:
add、offer、peekLast、popLast
字符串操作:
toCharArray()轉換為char數組
charAt(index)取字符串中索引為index的字符
1、冒泡排序
for(int i=0;i<n;i++){ for(int j=0;j<n-1-i;j++){ if(temp[j]>temp[j+1]){ int t=temp[j]; temp[j]=temp[j+1]; temp[j+1]=t; } } }
2、快速排序
public void quicksort(int[] array,int left,int right){ if(left<right){ int key = array[left]; int low = left; int high = right; while(low<high){ while(low<high && array[high]>=key){ high--; } array[low] = array[high]; while(low<high && array[low]<=key){ low++; } array[high] = array[low]; } array[low] = key; quicksort(array,left,low-1); quicksort(array,low+1,right); } }
3、查找子字符串出現的第一個索引位置
類似於Java的indexof()方法的實現,如下:
static int indexOf(char[] source, char[] target) { char first = target[0]; int max = (source.length - target.length); for (int i = 0; i <= max; i++) { /* Look for first character. */ if (source[i] != first) { while (++i <= max && source[i] != first) ; } /* Found first character, now look at the rest of v2 */ if (i <= max) { int j = i + 1; int end = j + target.length - 1; for (int k = 1; j < end && source[j] == target[k]; j++, k++) ; if (j == end) { /* Found whole string. */ return i; } } } return -1; }
4、分層打印二叉樹並在第一層輸出換行
public void PrintFromTopToBottom(TreeNode root) { TreeNode currentNode = root; int first = 1; int second = 0; while (currentNode != null) { if (currentNode.left != null) { queue.add(currentNode.left); second++; } if (currentNode.right != null) { queue.add(currentNode.right); second++; } first--; System.out.print(currentNode.val + " "); if (first == 0) { System.out.println(" "); first = second; second = 0; } currentNode = queue.poll(); } }
5、一致性hash
一致性hash算法可以解決容錯性和擴展性的問題。
系統中增加更多的虛擬節點,可以更好的解負載均衡問題。
public class Shard<S> { // S類封裝了機器節點的信息 ,如name、password、ip、port等 private TreeMap<Long, S> circle; // 將整個hash值空間組成一個虛擬的環 private List<S> shards; // 真實機器節點 private final int NODE_NUM = 100; // 每個機器節點關聯的虛擬節點個數 private final HashFunction hashFunction; // 選擇一個碰撞率低的hash()函數 public Shard(List<S> shards,HashFunction hashFunction) { super(); this.shards = shards; this.hashFunction = hashFunction; init(); } private void init() { // 初始化一致性hash環 circle = new TreeMap<Long, S>(); for (int i = 0; i<shards.size(); ++i) { // 每個真實機器節點都需要關聯虛擬節點 final S shardInfo = shards.get(i); add(shardInfo); } } public void add(S node) { for (int i = 0; i < NODE_NUM; i++) { // 虛擬節點用一些特定的hash值來替代,這樣形成了hash值到真實節點的映射 circle.put(hashFunction.hash(node.toString() + i), node); } } public void remove(S node) { for (int i = 0; i < NODE_NUM; i++) { // 移除真實節點下對應的所有虛擬節點(特定的一些hash值) circle.remove(hashFunction.hash(node.toString() + i)); } } public S getShardInfo(String key) { // SortedMap<Long, S> tail = circle.tailMap(hash(key)); // 沿環的順時針找到一個虛擬節點 // if (tail.size() == 0) { // return circle.get(circle.firstKey()); // } // return tail.get(tail.firstKey()); // 返回該虛擬節點對應的真實機器節點的信息 if (circle.isEmpty()) { return null; } Long hash = hashFunction.hash(key); // 如果當前hash值沒有定位到虛擬節點 if (!circle.containsKey(hash)) { SortedMap<Long, S> tailMap = circle.tailMap(hash); hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey(); } return circle.get(hash); } }
class Machine { String ip; String name; public Machine(String ip, String name) { this.ip = ip; this.name = name; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Test { public static void main(String[] args) { Machine a = new Machine("192.168.0.1", "a"); Machine b = new Machine("192.168.0.2", "b"); Machine c = new Machine("192.168.0.3", "c"); List<Machine> list = Arrays.asList(a, b, c); Map<String, Integer> map = new HashMap<String, Integer>(); Shard<Machine> mcs = new Shard<Machine>(list, new HashFunction()); // 存儲0到2000個數,看存儲在各個機器上的數的數量是否大致均勻 for (int i = 0; i < 2000; i++) { String key = i + ""; Machine m = mcs.getShardInfo(key); if (map.get(m.getIp()) == null) { map.put(m.getIp(), 0); } else { map.put(m.getIp(), (int) map.get(m.getIp()) + 1); } } Iterator<Entry<String, Integer>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Entry<String, Integer> entry = iterator.next(); System.out.println(entry.getKey() + "/" + entry.getValue()); } } }
某次運行後的結果如下:
192.168.0.2/599 192.168.0.1/698 192.168.0.3/700
6、LRU最近最少使用算法
要效率的話使用hash搜索,要實現最近最少的話就用雙向鏈表
public class LRUCache { private int cacheSize; private HashMap<Object, Entry> nodes; // 緩存容器 ,為了提高查詢速度需要這個結構 private int currentSize; private Entry first; // 鏈表頭 private Entry last; // 鏈表尾 static class Entry { Entry prev; Entry next; Object key; Object value; } public LRUCache(int i) { currentSize = 0; cacheSize = i; nodes = new HashMap<Object, Entry>(i); } /** * 獲取緩存中對象,並把它放在最前面 */ public Entry get(Object key) { Entry node = nodes.get(key); if (node != null) { moveToHead(node); return node; } else { return null; } } /** * 添加 entry到hashtable, 並把entry */ public void put(Object key, Object value) { //先查看hashtable是否存在該entry, 如果存在,則只更新其value Entry node = nodes.get(key); if (node == null) { //緩存容器是否已經超過大小. if (currentSize >= cacheSize) { nodes.remove(last.key); removeLast(); } else { currentSize++; } node = new Entry(); } node.value = value; //將最新使用的節點放到鏈表頭,表示最新使用的. moveToHead(node); nodes.put(key, node); } /** * 將entry刪除, 註意:刪除操作只有在cache滿了才會被執行 */ public void remove(Object key) { Entry node = nodes.get(key); //在鏈表中刪除 if (node != null) { if (node.prev != null) { node.prev.next = node.next; } if (node.next != null) { node.next.prev = node.prev; } if (last == node) last = node.prev; if (first == node) first = node.next; } //在hashtable中刪除 nodes.remove(key); } /** * 刪除鏈表尾部節點,即使用最後 使用的entry */ private void removeLast() { //鏈表尾不為空,則將鏈表尾指向null. 刪除連表尾(刪除最少使用的緩存對象) if (last != null) { if (last.prev != null){ last.prev.next = null; } else{ first = null; } last = last.prev; } } /** * 移動到鏈表頭,表示這個節點是最新使用過的 */ private void moveToHead(Entry node) { if (node == first) return; if (node.prev != null) node.prev.next = node.next; if (node.next != null) node.next.prev = node.prev; if (last == node) last = node.prev; if (first != null) { node.next = first; first.prev = node; } first = node; node.prev = null; if (last == null){ last = first; } } /* * 清空緩存 */ public void clear() { first = null; last = null; currentSize = 0; } }
7、生產者與消費者
public class ConsumerProducerByWaitNotify { public Integer monitor = new Integer(1); public static void main(String[] args) { ConsumerProducerByWaitNotify instance = new ConsumerProducerByWaitNotify(); instance.bootstrap(); } public void bootstrap(){ Godown godown = new Godown(30); // 必須操作同一個庫的實例,否則不存在多線程的問題 Consumer c1 = new Consumer(20, godown); Consumer c2 = new Consumer(20, godown); Consumer c3 = new Consumer(30, godown); Producer p1 = new Producer(10, godown); Producer p2 = new Producer(10, godown); Producer p3 = new Producer(10, godown); Producer p4 = new Producer(10, godown); Producer p5 = new Producer(10, godown); Producer p6 = new Producer(10, godown); Producer p7 = new Producer(10, godown); c1.start(); c2.start(); c3.start(); p1.start(); p2.start(); p3.start(); p4.start(); p5.start(); p6.start(); p7.start(); } // 倉庫 class Godown { public static final int max_size = 100; // 最大庫存量 public int curnum; // 當前庫存量 Godown(int curnum) { this.curnum = curnum; } // 生產指定數量的產品 public void produce(int neednum) { synchronized (monitor) { // 測試是否需要生產 while (neednum + curnum > max_size) { System.out.println("要生產的產品數量" + neednum + "超過剩余庫存量" + (max_size - curnum) + ",暫時不能執行生產任務!"); try { // 當前的生產線程等待,並讓出鎖(註意,只有獲取到鎖,才有讓鎖的一說) // 如果調用某個對象的wait()方法,當前線程必須擁有這個對象的monitor(即鎖), // 因此調用wait()方法必須在同步塊或者同步方法中進行(synchronized塊或者synchronized方法) monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 滿足生產條件,則進行生產,這裏簡單的更改當前庫存量 curnum += neednum; System.out.println("已經生產了" + neednum + "個產品,現倉儲量為" + curnum); // 喚醒在此對象監視器上等待的所有線程 // 調用某個對象的notify()方法,當前線程也必須擁有這個對象的monitor, // 因此調用notify()方法必須在同步塊或者同步方法中進行(synchronized塊或者synchronized方法)。 monitor.notifyAll(); } } // 消費指定數量的產品 public void consume(int neednum) { synchronized (monitor) { // 測試是否可消費 while (curnum < neednum) { try { // 當前的消費線程等待,並讓出鎖 monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 滿足消費條件,則進行消費,這裏簡單的更改當前庫存量 curnum -= neednum; System.out.println("已經消費了" + neednum + "個產品,現倉儲量為" + curnum); // 喚醒在此對象監視器上等待的所有線程 monitor.notifyAll(); } } } // 生產者 class Producer extends Thread { private int neednum; // 生產產品的數量 private Godown godown; // 倉庫 Producer(int neednum, Godown godown) { this.neednum = neednum; this.godown = godown; } @Override public void run() { // 生產指定數量的產品 godown.produce(neednum); } } // 消費者 class Consumer extends Thread { private int neednum; // 生產產品的數量 private Godown godown; // 倉庫 Consumer(int neednum, Godown godown) { this.neednum = neednum; this.godown = godown; } @Override public void run() { // 消費指定數量的產品 godown.consume(neednum); } } }
還可以使用阻塞隊列、Semaphore等手段來實現。
Java面試13|算法