1. 程式人生 > >Java面試13|算法

Java面試13|算法

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|算法