• 双重队列问题


    一 问题描述

    二 分析

    可以使用平衡二叉树 AVL 解决。

    三 算法设计

    1 读入指令 n,判断类型。

    2 如果 n = 1,则读入客户 num 以及优先级 val,将其插入平衡二叉树中。

    3 如果 n = 2,此时平衡二叉树为空,则输出0,否则输出最大值并删除。

    4 如果 n = 3,此时平衡二叉树为空,则输出0,否则输出最小值并删除。

    四 代码

    1. package com.platform.modules.alg.alglib.poj3481;
    2. public class Poj3481 {
    3. public String cal(String input) {
    4. AVLTree avlTree = new AVLTree();
    5. String[] split = input.split("\n");
    6. String output = "";
    7. for (String line : split) {
    8. String[] word = line.split(" ");
    9. switch (word[0]) {
    10. case "0":
    11. break;
    12. case "1":
    13. avlTree.add(new Node(Integer.parseInt(word[2]), Integer.parseInt(word[1])));
    14. break;
    15. case "2":
    16. if (avlTree.root == null) {
    17. output += "0";
    18. output += "\n";
    19. } else {
    20. Node node = avlTree.findMax();
    21. output += node.num;
    22. output += "\n";
    23. avlTree.delNode(node.value);
    24. }
    25. break;
    26. case "3":
    27. if (avlTree.root == null) {
    28. output += "0";
    29. output += "\n";
    30. } else {
    31. Node node = avlTree.findMin();
    32. output += node.num;
    33. output += "\n";
    34. avlTree.delNode(node.value);
    35. }
    36. break;
    37. }
    38. }
    39. return output;
    40. }
    41. class AVLTree {
    42. // 根节点
    43. private Node root;
    44. public Node getRoot() {
    45. return root;
    46. }
    47. /**
    48. * 功能描述:查找要删除的结点
    49. *
    50. * @param value 要删除节点的值
    51. * @return Node 要删除的节点
    52. * @author cakin
    53. * @date 2021/3/25
    54. */
    55. public Node search(int value) {
    56. if (root == null) {
    57. return null;
    58. } else {
    59. return root.search(value);
    60. }
    61. }
    62. public Node findMax() { // 找优先级最大的结点
    63. Node temp = root;
    64. while (temp.right != null) {
    65. temp = root.right;
    66. }
    67. return temp;
    68. }
    69. public Node findMin() { // 找优先级最低的结点
    70. Node temp = root;
    71. while (temp.left != null) {
    72. temp = root.left;
    73. }
    74. return temp;
    75. }
    76. /**
    77. * 功能描述:要删除节点的父节点
    78. *
    79. * @param value 要删除节点的值
    80. * @return Node 要删除节点的父节点
    81. * @author cakin
    82. * @date 2021/3/25
    83. * @description:
    84. */
    85. public Node searchParent(int value) {
    86. if (root == null) {
    87. return null;
    88. } else {
    89. return root.searchParent(value);
    90. }
    91. }
    92. /**
    93. * 功能描述:返回以 node 为根结点的二叉排序树的最小结点的值
    94. *
    95. * @param node 传入的结点(当做二叉排序树的根结点)
    96. * @return 返回的以 node 为根结点的二叉排序树的最小结点的值
    97. */
    98. public int delRightTreeMin(Node node) {
    99. Node target = node;
    100. // 循环的查找左子节点,就会找到最小值
    101. while (target.left != null) {
    102. target = target.left;
    103. }
    104. // target就指向了最小结点
    105. // 删除最小结点
    106. delNode(target.value);
    107. return target.value;
    108. }
    109. /**
    110. * 功能描述:删除结点
    111. *
    112. * @param value 待删除节点的值
    113. * @author cakin
    114. * @date 2021/3/25
    115. */
    116. public void delNode(int value) {
    117. if (root == null) {
    118. return;
    119. } else {
    120. // 1 先去找到要删除的结点 targetNode
    121. Node targetNode = search(value);
    122. // 如果没有找到要删除的结点
    123. if (targetNode == null) {
    124. return;
    125. }
    126. // 如果发现当前这颗二叉排序树只有一个结点
    127. if (root.left == null && root.right == null) {
    128. root = null;
    129. return;
    130. }
    131. // 去找到 targetNode 的父结点
    132. Node parent = searchParent(value);
    133. // 如果要删除的结点是叶子结点
    134. if (targetNode.left == null && targetNode.right == null) {
    135. // 判断 targetNode 是父结点的左子结点,还是右子结点
    136. if (parent.left != null && parent.left.value == value) { // 是左子结点
    137. parent.left = null;
    138. } else if (parent.right != null && parent.right.value == value) { // 是由子结点
    139. parent.right = null;
    140. }
    141. } else if (targetNode.left != null && targetNode.right != null) { // 删除有两颗子树的节点
    142. int minVal = delRightTreeMin(targetNode.right);
    143. targetNode.value = minVal;
    144. } else { // 删除只有一颗子树的结点
    145. // 如果要删除的结点有左子结点
    146. if (targetNode.left != null) {
    147. if (parent != null) {
    148. // 如果 targetNode 是 parent 的左子结点
    149. if (parent.left.value == value) {
    150. parent.left = targetNode.left;
    151. } else { // targetNode 是 parent 的右子结点
    152. parent.right = targetNode.left;
    153. }
    154. } else {
    155. root = targetNode.left;
    156. }
    157. } else { // 如果要删除的结点有右子结点
    158. if (parent != null) {
    159. // 如果 targetNode 是 parent 的左子结点
    160. if (parent.left.value == value) {
    161. parent.left = targetNode.right;
    162. } else { // 如果 targetNode 是 parent 的右子结点
    163. parent.right = targetNode.right;
    164. }
    165. } else {
    166. root = targetNode.right;
    167. }
    168. }
    169. }
    170. }
    171. }
    172. /**
    173. * 功能描述:添加结点
    174. *
    175. * @param node 节点
    176. * @author cakin
    177. * @date 2021/3/22
    178. */
    179. public void add(Node node) {
    180. if (root == null) {
    181. root = node; // 如果root为空则直接让root指向node
    182. } else {
    183. root.add(node);
    184. }
    185. }
    186. /**
    187. * 功能描述:中序遍历
    188. *
    189. * @author cakin
    190. * @date 2021/3/22
    191. */
    192. public void infixOrder() {
    193. if (root != null) {
    194. root.infixOrder();
    195. } else {
    196. System.out.println("二叉排序树为空,不能遍历");
    197. }
    198. }
    199. }
    200. /**
    201. * @className: Node
    202. * @description: 节点
    203. * @date: 2021/3/22
    204. * @author: cakin
    205. */
    206. class Node {
    207. // 客户 num
    208. int num;
    209. // 优先级
    210. int value;
    211. // 左子树根节点
    212. Node left;
    213. // 右子树根节点
    214. Node right;
    215. public Node(int value, int num) {
    216. this.value = value;
    217. this.num = num;
    218. }
    219. /**
    220. * 功能描述:返回左子树的高度
    221. *
    222. * @author cakin
    223. * @date 2021/3/27
    224. */
    225. public int leftHeight() {
    226. if (left == null) {
    227. return 0;
    228. }
    229. return left.height();
    230. }
    231. /**
    232. * 功能描述:返回右子树的高度
    233. *
    234. * @author cakin
    235. * @date 2021/3/27
    236. */
    237. public int rightHeight() {
    238. if (right == null) {
    239. return 0;
    240. }
    241. return right.height();
    242. }
    243. /**
    244. * 功能描述:返回以该结点为根结点的树的高度
    245. *
    246. * @author cakin
    247. * @date 2021/3/27
    248. */
    249. public int height() {
    250. return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
    251. }
    252. /**
    253. * 功能描述:左旋转方法
    254. *
    255. * @author cakin
    256. * @date 2021/3/27
    257. */
    258. private void leftRotate() {
    259. // 创建新的结点,值为当前根结点的值
    260. Node newNode = new Node(value, num);
    261. // 把新的结点的左子树设置成当前结点的左子树
    262. newNode.left = left;
    263. // 把新节点的右子树设置为当前节点右子树的左子树
    264. newNode.right = right.left;
    265. // 把当前节点的值设置为右子节点的值
    266. value = right.value;
    267. // 把当前节点的右子树设置为右子树的右子树
    268. right = right.right;
    269. // 把当前节点的左子树设置为新节点
    270. left = newNode;
    271. }
    272. /**
    273. * 功能描述:右旋转
    274. *
    275. * @author cakin
    276. * @date 2021/3/27
    277. */
    278. private void rightRotate() {
    279. Node newNode = new Node(value, num);
    280. newNode.right = right;
    281. newNode.left = left.right;
    282. value = left.value;
    283. left = left.left;
    284. right = newNode;
    285. }
    286. /**
    287. * 功能描述:查找要删除的结点
    288. *
    289. * @param value 希望删除的结点的值
    290. * @return 如果找到返回该结点,否则返回null
    291. */
    292. public Node search(int value) {
    293. if (value == this.value) { // 找到该结点
    294. return this;
    295. } else if (value < this.value) {// 如果查找的值小于当前结点值,向左子树递归查找
    296. // 如果左子结点为空
    297. if (this.left == null) {
    298. return null;
    299. }
    300. return this.left.search(value);
    301. } else { // 如果查找的值不小于当前结点,向右子树递归查找
    302. if (this.right == null) {
    303. return null;
    304. }
    305. return this.right.search(value);
    306. }
    307. }
    308. /**
    309. * 功能描述:查找要删除结点的父结点
    310. *
    311. * @param value 要删除的结点的值
    312. * @return 返回的是要删除的结点的父结点,如果没有就返回null
    313. */
    314. public Node searchParent(int value) {
    315. // 如果当前结点就是要删除的结点的父结点
    316. if ((this.left != null && this.left.value == value) ||
    317. (this.right != null && this.right.value == value)) {
    318. return this;
    319. } else {
    320. // 如果查找的值小于当前结点的值, 并且当前结点的左子结点不为空
    321. if (value < this.value && this.left != null) {
    322. return this.left.searchParent(value); // 向左子树递归查找
    323. } else if (value >= this.value && this.right != null) {
    324. return this.right.searchParent(value); // 向右子树递归查找
    325. } else {
    326. return null; // 找到父结点
    327. }
    328. }
    329. }
    330. @Override
    331. public String toString() {
    332. return "Node [value=" + value + "]";
    333. }
    334. /**
    335. * 功能描述:添加节点到平衡二叉树
    336. *
    337. * @param node 节点
    338. * @author cakin
    339. * @date 2021/3/22
    340. */
    341. public void add(Node node) {
    342. if (node == null) {
    343. return;
    344. }
    345. // 传入的结点的值小于当前子树的根结点的值
    346. if (node.value < this.value) {
    347. // 当前结点左子树根结点为null
    348. if (this.left == null) {
    349. this.left = node;
    350. } else {
    351. // 递归的向左子树添加
    352. this.left.add(node);
    353. }
    354. } else { // 传入的结点的值大于当前子树的根结点的值
    355. if (this.right == null) {
    356. this.right = node;
    357. } else {
    358. // 递归的向右子树添加
    359. this.right.add(node);
    360. }
    361. }
    362. // 当添加完一个结点后,如果(右子树的高度-左子树的高度) > 1 , 进行左旋转
    363. if (rightHeight() - leftHeight() > 1) {
    364. // 左旋转
    365. // leftRotate();
    366. // 如果它的右子树的左子树的高度大于它的右子树的右子树的高度
    367. if (right != null && right.leftHeight() > right.rightHeight()) {
    368. // 先对右子结点进行右旋转
    369. right.rightRotate();
    370. // 然后再对当前结点进行左旋转
    371. leftRotate();
    372. } else {
    373. // 直接进行左旋转
    374. leftRotate();
    375. }
    376. return;
    377. }
    378. // 当添加完一个结点后,如果(左子树的高度 - 右子树的高度) > 1, 进行左旋转
    379. if (leftHeight() - rightHeight() > 1) {
    380. // rightRotate();
    381. // 如果它的左子树的右子树高度大于它的左子树的高度
    382. if (left != null && left.rightHeight() > left.leftHeight()) {
    383. // 先对当前结点的左子结点进行左旋转
    384. left.leftRotate();
    385. // 再对当前结点进行右旋转
    386. rightRotate();
    387. } else {
    388. // 直接进行右旋转
    389. rightRotate();
    390. }
    391. }
    392. }
    393. /**
    394. * 功能描述:中序遍历
    395. *
    396. * @author cakin
    397. * @date 2021/3/22
    398. */
    399. public void infixOrder() {
    400. if (this.left != null) {
    401. this.left.infixOrder();
    402. }
    403. System.out.println(this);
    404. if (this.right != null) {
    405. this.right.infixOrder();
    406. }
    407. }
    408. }
    409. }

    五 测试效果

  • 相关阅读:
    金九银十已到,大厂面试大全+面试经历都在这了(建议收藏)
    leetcode 12. Integer to Roman(整数转罗马数字)
    动手学深度学习_目标检测算法 R-CNN 系列
    Android深色主题背景的实现及主题背景颜色互换
    ArcGIS Engine:实现Shp/Mxd数据的加载、图层的简单查询
    基于java+SpringBoot+HTML+Mysql线上视频购买学习
    【问题之书】
    m无线通信的信道建模matlab仿真,仿真分析了6种不同的无线通信信道模型
    web前端面试-- IEEE754标准JS精度丢失问题0.1+0.2!=0.3、0.2+0.3==0.5 十进制转二进制讲解
    深入理解RBAC
  • 原文地址:https://blog.csdn.net/chengqiuming/article/details/126207857