Java面试13|算法

 

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等手段来实现。  

 

 

 

 

 

 

 

 

 

 

 

 

  

 

转载于:https://www.cnblogs.com/mazhimazhi/p/7499429.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值