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等手段来实现。
阅读全文
0 0
- Java面试13|算法
- java面试算法
- Java面试算法题目
- java面试算法题
- java面试经典算法
- Java面试-算法篇
- java面试算法笔记
- java面试算法汇总
- JAVA经典算法面试
- 排序算法 面试 JAVA
- JAVA面试算法小记
- 面试-java算法题
- java面试--算法
- 【java面试】算法篇
- Java面试常见算法
- java面试算法题(经典)
- java百度面试查找算法
- java中常用算法(面试)
- 背包问题九讲【转】
- ajax向后台请求数据包含中文乱码问题解决
- 数据结构上机实验之顺序查找
- JavaWeb登录、注销、退出、记住用户名和密码
- 每日一题(42)—— 已知一个数组table,用一个宏定义,求出数据的元素个数
- Java面试13|算法
- 【04】jQuery事件的绑定、触发、及监听方法简单说明
- Java transient简介
- RabbitMQ的CLI命令行管理工具rabbitmqadmin
- java中的一些应该知道的内容
- 关于页面中文正常显示,存到数据库乱码的问题
- JDFZD1总结
- Java面试14|Session与Cookie
- ubuntu16.04主题美化和软件推荐