文章

449. 序列化和反序列化二叉搜索树

问题449 序列化和反序列化二叉搜索树 ★★☆☆☆

https://leetcode.cn/problems/serialize-and-deserialize-bst/

题目要求很好理解了,简单来说,给定一颗二叉查找树,写两个方法,一个方法可以把二叉查找树序列化为字符串,另一个方法可以把序列化的字符串反序列化为同样结构的二叉查找树。

我是从反序列化方法入手的,先搞清楚如何从一个字符串能构建出一颗二叉查找树,这里面隐含着我们需要怎样的一个字符串,这个字符串需要让我们比较方便的重建二叉查找树。那么我们需要怎样的一个字符串呢?这就要从二叉查找树这个特性入手了,因为单纯的一个字符串直觉上很难包含树结构信息。这个地方稍作思考,可以发现,如果我们有二叉查找树先序遍历的结果,那么就可以比较方便的构造,逻辑如下:第一个元素显然是根节点,剩下的元素可以一分为二,左边的是左子树,右边的是右子树,而继续构造左子树或右子树的过程也是如此,利用递归很容易实现这个逻辑。OK,通过先序遍历的结果可以重构二叉查找树了,那么如果通过字符串得到先序遍历的结果呢?或者反过来说,如果通过先序遍历的结果得到一个字符串呢?这种转换显然是可以相互的。这个地方再稍作思考,比较简单直接的方法就是把先序遍历的结果以数组的形式存储,然后把这个数组转换为字符串。一个数组就是连续的字节序列,转换为字符串的简单方法就是每四位比特为一组解释为十六进制字符串。

代码提交以后是没有问题的,但是感觉有点复杂。看了一下官方题解,思路是差不多的,不过这个直接把遍历结果toString的处理还是有点,额,序列化的结果未免看着太不专业了吧……

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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
	public String serialize(TreeNode root) {
		if (root == null) {
			return null;
		}

		List<Integer> inOrderList = new ArrayList<>();
		inOrderTraverse(root, inOrderList);

		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < inOrderList.size(); i++) {
			short cur = inOrderList.get(i).shortValue();

			int val = (cur & 0b1111000000000000) >>> 12;
			char ch = map(val);
			builder.append(ch);

			val = (cur & 0b0000111100000000) >>> 8;
			ch = map(val);
			builder.append(ch);

			val = (cur & 0b0000000011110000) >>> 4;
			ch = map(val);
			builder.append(ch);

			val = cur & 0b0000000000001111;
			ch = map(val);
			builder.append(ch);
		}

		return builder.toString();
	}

	private void inOrderTraverse(TreeNode node, List<Integer> inOrderList) {
		if (node == null) {
			return;
		}

		inOrderList.add(node.val);
		inOrderTraverse(node.left, inOrderList);
		inOrderTraverse(node.right, inOrderList);
	}

	private char map(int val) {
		char ch = 0;
		switch (val) {
			case 0:
				ch = '0';
				break;
			case 1:
				ch = '1';
				break;
			case 2:
				ch = '2';
				break;
			case 3:
				ch = '3';
				break;
			case 4:
				ch = '4';
				break;
			case 5:
				ch = '5';
				break;
			case 6:
				ch = '6';
				break;
			case 7:
				ch = '7';
				break;
			case 8:
				ch = '8';
				break;
			case 9:
				ch = '9';
				break;
			case 10:
				ch = 'A';
				break;
			case 11:
				ch = 'B';
				break;
			case 12:
				ch = 'C';
				break;
			case 13:
				ch = 'D';
				break;
			case 14:
				ch = 'E';
				break;
			case 15:
				ch = 'F';
				break;
			default:
				break;
		}

		return ch;
	}

	public TreeNode deserialize(String data) {
		if (data == null
			|| data.isEmpty()) {
			return null;
		}

		char[] chars = data.toCharArray();
		int length = chars.length;

		short[] inOrderArray = new short[length / 4];

		for (int i = 0; i < inOrderArray.length; i++) {
			char ch1 = chars[i*4];
			char ch2 = chars[i*4 + 1];
			char ch3 = chars[i*4 + 2];
			char ch4 = chars[i*4 + 3];

			int cur = 0;

			int val = map(ch1);
			cur = cur | (val << 12);

			val = map(ch2);
			cur = cur | (val << 8);

			val = map(ch3);
			cur = cur | (val << 4);

			val = map(ch4);
			cur = cur | val;

			inOrderArray[i] = (short) cur;
		}

		return rebuild(inOrderArray, 0, inOrderArray.length - 1);
	}

	private int map(char ch) {
		int val = 0;
		switch (ch) {
			case '0':
				val = 0;
				break;
			case '1':
				val = 1;
				break;
			case '2':
				val = 2;
				break;
			case '3':
				val = 3;
				break;
			case '4':
				val = 4;
				break;
			case '5':
				val = 5;
				break;
			case '6':
				val = 6;
				break;
			case '7':
				val = 7;
				break;
			case '8':
				val = 8;
				break;
			case '9':
				val = 9;
				break;
			case 'A':
				val = 10;
				break;
			case 'B':
				val = 11;
				break;
			case 'C':
				val = 12;
				break;
			case 'D':
				val = 13;
				break;
			case 'E':
				val = 14;
				break;
			case 'F':
				val = 15;
				break;
			default:
				break;
		}

		return val;
	}

	private TreeNode rebuild(short[] inOrderArray, int start, int end) {
		if (start > end) {
			return null;
		}

		short val = inOrderArray[start];
		TreeNode node = new TreeNode(val);

		int lessThan = -1;
		int greaterThan = -1;
		for (int i = start + 1; i <= end; i++) {
			if (inOrderArray[i] < val) {
				lessThan = i;
			} else if (inOrderArray[i] > val) {
				greaterThan = i;
				break;
			}
		}

		if (lessThan != -1) {
			node.left = rebuild(inOrderArray, start + 1, lessThan);
		}
		if (greaterThan != -1) {
			node.right = rebuild(inOrderArray, greaterThan, end);
		}

		return node;
	}

在 LeetCode 查看

本文由作者按照 CC BY 4.0 进行授权