Java面试13|算法

来源:互联网 发布:灵族男捏脸数据 编辑:程序博客网 时间:2024/05/16 23:48

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、冒泡排序

1
2
3
4
5
6
7
8
9
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、快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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()方法的实现,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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、分层打印二叉树并在第一层输出换行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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算法可以解决容错性和扩展性的问题。

系统中增加更多的虚拟节点,可以更好的解负载均衡问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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);
    

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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());
        }
         
    }
}

  

某次运行后的结果如下: 

1
2
3
192.168.0.2/599
192.168.0.1/698
192.168.0.3/700

 

 

6、LRU最近最少使用算法

 要效率的话使用hash搜索,要实现最近最少的话就用双向链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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、生产者与消费者

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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等手段来实现。