0%

数据结构学习笔记 - 08 映射

介绍

映射在有的语言也叫 字典,代表一种一一对应的关系。

主要的应用如:

  • 字典:单词–>释意

  • 名册:身份证号–>人

  • 车辆管理:车牌号–>车

  • 数据库:id–>信息

  • 单词统计:单词–>频率

    映射也分有序映射和无序映射。有序映射基于搜索树实现,无序映射通常会使用哈希表实现。

    集合和映射是很相似的,使用映射完全可以包装出一个集合的结构,只需不管 value 即可。

实现

定义映射的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Map<K, V> {

void add(K key, V value);

V remove(K key);

boolean contains(K key);

V get(K key);

void set(K key, V newValue);

int getSize();

boolean isEmpty();
}

使用二分搜索树实现的映射:

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
public class BSTMap<K extends Comparable<K>, V> implements Map<K, V> {

// 在类的内部定义一个二分搜索树的节点类
private class Node {
public K key;
public V value;
public Node left, right;

public Node(K key, V value) {
this.key = key;
this.value = value;
left = null;
right = null;
}
}

private Node root;
private int size;

public BSTMap() {
root = null;
size = 0;
}

@Override
public int getSize() {
return size;
}

@Override
public boolean isEmpty() {
return size == 0;
}

@Override
public void add(K key, V value) {
root = add(root, key, value);
}

private Node add(Node node, K key, V value) {
if (node == null) {
size++;
return new Node(key, value);
}

if (key.compareTo(node.key) < 0) {
node.left = add(node, key, value);
} else if (key.compareTo(node.key) > 0) {
node.right = add(node, key, value);
} else {
node.value = value;
}

return node;
}

private Node getNode(Node node, K key) {
if (node == null) {
return null;
}

if (key.compareTo(node.key) == 0) {
return node;
} else if (key.compareTo(node.key) < 0) {
return getNode(node.left, key);
} else {
return getNode(node.right, key);
}
}

@Override
public boolean contains(K key) {
return getNode(root, key) != null;
}

@Override
public V get(K key) {
Node node = getNode(root, key);
return node == null ? null : node.value;
}

@Override
public void set(K key, V newValue) {
Node node = getNode(root, key);

if (node == null) {
throw new IllegalArgumentException(key + "dosen't exist!");
}

node.value = newValue;
}

private Node minimum(Node node) {
if (node.left == null) {
return node;
}

return minimum(node.left);
}

private Node removeMin(Node node) {
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}

node.left = removeMin(node.left);
return node;
}

@Override
public V remove(K key) {
Node node = getNode(root, key);
if (node != null) {
root = remove(root, key);
return node.value;
}

return null;
}

private Node remove(Node node, K key) {
if (node == null) {
return null;
}

if (key.compareTo(node.key) < 0) {
node.left = remove(node.left, key);
return node;
} else if (key.compareTo(node.key) > 0) {
node.left = remove(node.right, key);
return node;
} else {
// 待删除节点右子树为空
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}

// 待删除节点右子树为空
if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}

// 待删除节点左右子树均不为空
// 找到比待删除节点大的最小节点,即待删除节点右子树的最小节点
// 用这个节点顶替待删除节点的位置。
Node successor = minimum(node.right);
successor.right = removeMin(node.right);
successor.left = node.left;

node.left = node.right = null;

return successor;
}
}

}

使用链表实现的映射:

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
public class LinkedListMap<K, V> implements Map<K, V> {

private class Node {
public K key;
public V value;
public Node next;

public Node(K key, V value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}

public Node(K key) {
this(key, null, null);
}

public Node() {
this(null);
}

@Override
public String toString() {
return key.toString() + ": " + value.toString();
}
}

private Node dummyHead;
private int size;

public LinkedListMap() {
dummyHead = new Node();
size = 0;
}

@Override
public int getSize() {
return size;
}

@Override
public boolean isEmpty() {
return size == 0;
}

/**
* 私有的辅助函数,传入一个 key,返回对应的节点的引用。
*/
private Node getNode(K key) {
Node cur = dummyHead.next;

while (cur != null) {
if (cur.key.equals(key)) {
return cur;
}
cur = cur.next;
}

return null;
}

@Override
public boolean contains(K key) {
return getNode(key) != null;
}

@Override
public V get(K key) {
Node node = getNode(key);
return node == null ? null : node.value;
}

@Override
public void add(K key, V value) {
Node node = getNode(key);

if (node == null) {
dummyHead.next = new Node(key, value, dummyHead.next);
size++;
} else {
node.value = value;
}
}

@Override
public void set(K key, V newValue) {
Node node = getNode(key);

if (node == null) {
throw new IllegalArgumentException(key + "dosen't exist!");
}

node.value = newValue;
}

@Override
public V remove(K key) {
Node prev = dummyHead;

while (prev.next != null) {
if (prev.next.key.equals(key)) {
break;
}
prev = prev.next;
}

if (prev.next != null) {
Node delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
size--;
return delNode.value;
}

return null;
}
}

分析

链表映射 二分搜索树映射(平均) 二分搜索树映射(最差)
增 add O(n) O(logn) O(n)
删 remove O(n) O(logn) O(n)
改 set O(n) O(logn) O(n)
查 get O(n) O(logn) O(n)
查 contains O(n) O(logn) O(n)

对映射的分析基本和对集合的分析是一致的。