数据结构与算法之十 提高二叉搜索树的效率

举报
tea_year 发表于 2021/12/29 23:17:57 2021/12/29
【摘要】 视频课堂https://edu.csdn.net/course/play/7621 目标 在本章中,您将学习: 应用树来解决编程问题 实现线索二叉树 索引 磁盘文件中的数据一般是按记录方式组织的。一条...
目标
在本章中,您将学习:
应用树来解决编程问题
实现线索二叉树

索引

磁盘文件中的数据一般是按记录方式组织的。一条记录由许多字段组成,其 中一个就是键字段。
这个键字段被用于唯一地标识文件中的每条记录。
索引是从磁盘文件中访问记录的数据访问方法之一。
索引通过称为索引的表来实现。
索引有以下两个条目:
所有记录的键字段
每条记录的位移位置( Offset position

你可以实现一个二叉搜索树来存储这引起索引值。
此方法可以更快速地搜索一个键值。

在线索二叉树上常用的操作之一是遍历。
在链表表示的二叉搜索树中,通常通过递归完成遍历。
因此,栈在内存中被维护。
如果树非常大,实现递归遍历树需要许多内存空间。
如果内存不够,执行递归可能导致内存泄漏。

在这样的情况下, 有一些 能够遍历树而不需要实现递归 的机制是很好的。
你可以通过执行线索二叉树来解决此问题。
在二叉搜索树中,有许多节点具有空的左子节点或空的右子节点或两个都空。
在这种情况下,你可以利用这些域以便空的左子节点指向其中序前驱,空的 右子节点指向其中序后继。

在这样的情况下,为了其它一些有用目的利用这些 NULL 域将是很好的。


这种类型的二叉树被称为线索二叉树。
节点中保存中序前驱和中序后继地址的域被称为线索。


线索二叉树中节点的结构与标准二叉树的结构有所不同。
与标准二叉树不同的是线索二叉树的每个节点包含两个额外的信息,被称为 左线索和右线索。

节点的左和右线索域可以有两个值:
1 :表示正常链接到子节点。
0 :表示指向中序前驱和中序后继的线索。


线索二叉树中的各种操作以下:
遍历
搜索
插入
删除


运用算法以找到线索二叉树中节点的中序后继。


1. 识别节点,对于它你需要定位中序后继,并且标记它为 currentNode
2.
2. 如果 currentNode 的右子节点是线索:
a. 标记 currentNode 的右子节点为后继。
b. 退出。
c.
3. 使 currentNode 指向它的右子节点。
4.
4. 重复步骤 5 直到 currentNode 的左子节点变成线索。
5.
5. currentNode 指向它的左子节点。
6.
6. 标记 currentNode 为后继。


小结

在本章中,你已经学到:
二叉搜索树可用于实现索引。
线索二叉树是这样一个二叉树,在其中有空左子节点的节点存储它的中序前驱 的地址,并且空右子节点存储它的中序后继的地址。
在线索二叉树中,节点的左和右子节点域,它保存它的中序前驱和中序后继的 地址,被称为线索。
你可以遍历线索二叉树而不实现递归。
线索二叉树被表示为头节点的左子树


与标准二叉树相比,在线索二叉树中每个节点由两个额外的域组成以保持节点 的左和右子节点域是线索或链接。


   
  1. /*写一个程序以实现插入、删除且遍历线索二叉搜索树,这里树中的每个节点包含一个字典程序。*/
  2. using System;
  3. using System.Text;
  4. namespace Threads
  5. {
  6. class Node
  7. {
  8. /*两个线索域;lthread,rthread;1:表示子节点;0:表示线索.*/
  9. public int lthread; /*左线索标志*/
  10. public Node lchild; /*左子节点/
  11. public string info; /*数据域*/
  12. public Node rchild; /*右子节点*/
  13. public int rthread; /*右线索标志*/
  14. public Node(int lt, Node lc, string i, Node rc, int rt)
  15. {
  16. lthread = lt;
  17. lchild = lc;
  18. info = i;
  19. rchild = rc;
  20. rthread = rt;
  21. }
  22. }
  23. class Operations
  24. {
  25. Node head;
  26. public Operations()
  27. {
  28. /*在一个线索二叉树种,我们增加一个节点,即头节点.线索树作为头节点的左子树,即头点指向树的根节点.当树为空的时候,头节点左子节点指向本身*/
  29. head = new Node(0, head, "头节点", head, 0);
  30. head.lchild = head;
  31. head.rchild = head;
  32. }//构造函数初始化的时候,头节点的左,右子节点指向本身.
  33. public void find(string element, ref Node parent, ref Node currentNode)
  34. {
  35. /*搜索方法,查找你要找的节点位置与之父节点的位置.*/
  36. if (head.lchild == head)
  37. {
  38. /*如果没有找到节点为null,且父节点为头节点*/
  39. currentNode = null;
  40. parent = head;
  41. return;
  42. }
  43. currentNode = head.lchild;
  44. parent = head;
  45. while (currentNode.info != element)
  46. {
  47. parent = currentNode;
  48. if (String.Compare(element,currentNode.info)<0) //如果元素小于当前节点
  49. {
  50. if (currentNode.lthread == 1) //判断当前节点的左线索标志,如果为1,则指向当前节点的左子节点.
  51. currentNode = currentNode.lchild;
  52. else //否则,如果左线索标志为0,则设置当前节点为空.
  53. {
  54. currentNode = null;
  55. return;
  56. }
  57. }
  58. else
  59. {
  60. if (currentNode.rthread == 1) //如果当前节点的右线索标志为1,则指向当前节点的右子节点.
  61. currentNode = currentNode.rchild;
  62. else //否则,右线索标志为0,则设置当前节点为空
  63. {
  64. currentNode = null;
  65. return;
  66. }
  67. }
  68. }
  69. }
  70. public void insert(string element) /*在二叉树中插入一个节点.*/
  71. {
  72. Node tmp, parent = null, currentNode = null; //
  73. find(element, ref parent, ref currentNode); //调用查找当前元素节点,当前元素父节点.
  74. if (currentNode != null)
  75. {
  76. /*在二叉搜索树中不允许,重复节点.*/
  77. Console.WriteLine("\n不允许重复单词.");
  78. return;
  79. }
  80. tmp = new Node(0, null, element, null, 0); //为tmp新节点分配内存.
  81. if (parent == head) /*如果父节点为头节点,则插入节点为根节点.*/
  82. {
  83. head.lthread = 1; /*设置头节点的左线索标志为1*/
  84. head.lchild = tmp; /*设置头节点的左子节点为要新节点.*/
  85. tmp.lchild = head; /*新节点的左线索为头节点.*/
  86. tmp.rchild = head; /*新节点的右线索为头节点.*/
  87. }
  88. else
  89. {
  90. if (String.Compare(element,parent.info)<0)
  91. {
  92. /*要插入的新节点比父节点小*/
  93. tmp.lchild = parent.lchild;
  94. tmp.rchild = parent;
  95. parent.lthread = 1;
  96. parent.lchild = tmp;
  97. }
  98. else
  99. {
  100. /*要插入的新节点比父节点要大!*/
  101. tmp.rchild = parent.rchild;
  102. tmp.lchild = parent;
  103. parent.rthread = 1;
  104. parent.rchild = tmp;
  105. }
  106. }
  107. }
  108. public Node Inorder_successor(Node currentNode) //中序编历查找后继节点
  109. {
  110. /*中序:左子树< 根<右子树 */
  111. Node successor;
  112. if (currentNode.rthread == 0)
  113. successor = currentNode.rchild;
  114. else
  115. {
  116. currentNode = currentNode.rchild;
  117. while (currentNode.lthread == 1)
  118. {
  119. currentNode = currentNode.lchild;
  120. }
  121. successor = currentNode;
  122. }
  123. return successor;
  124. }
  125. public Node Inorder_predecessor(Node currentNode) /*利用中序编历查找前驱节点.*/
  126. {
  127. Node predecessor;
  128. if (currentNode.lthread == 0)
  129. predecessor = currentNode.lchild;
  130. else
  131. {
  132. currentNode = currentNode.lchild;
  133. while (currentNode.rthread == 1)
  134. {
  135. currentNode = currentNode.rchild;
  136. }
  137. predecessor = currentNode;
  138. }
  139. return predecessor;
  140. }
  141. public void Inorder_traversal() /*执行树的中序编历*/
  142. {
  143. Node currentNode = null;
  144. if (head.lchild == head)
  145. {
  146. Console.WriteLine("树空!");
  147. return;
  148. }
  149. currentNode = head.lchild;
  150. while (currentNode.lthread == 1)
  151. {
  152. currentNode = currentNode.lchild;
  153. }
  154. Console.Write(currentNode.info + " ");
  155. while (true)
  156. {
  157. currentNode = Inorder_successor(currentNode);
  158. if (currentNode == head)
  159. break;
  160. Console.Write(currentNode.info + " ");
  161. }
  162. Console.WriteLine();
  163. }
  164. public void remove() /*从树种移除节点*/
  165. {
  166. if (head.lchild == head)
  167. {
  168. Console.WriteLine("树空");
  169. return;
  170. }
  171. Node parent = null, currentNode = null;
  172. string element;
  173. Console.Write("请键入要删除单词:");
  174. element = Console.ReadLine();
  175. find(element, ref parent, ref currentNode);
  176. if (currentNode == null)
  177. {
  178. Console.WriteLine("\n在字典中没有发现该单词");
  179. return;
  180. }
  181. /*依据不同的状态,来删除不同的子节点.*/
  182. if (currentNode.lthread == 0 && currentNode.rthread == 0)
  183. case_1(ref parent, ref currentNode);
  184. if (currentNode.lthread == 1 && currentNode.rthread == 0)
  185. case_2(ref parent, ref currentNode);
  186. if (currentNode.lthread == 0 && currentNode.rthread == 1)
  187. case_2(ref parent, ref currentNode);
  188. if (currentNode.lthread == 1 && currentNode.rthread == 1)
  189. case_3(ref parent, ref currentNode);
  190. }
  191. public void case_1(ref Node parent, ref Node currentNode)
  192. {
  193. /* This function is invoked if the node to be removed is the leaf node */
  194. if (parent == head)
  195. {
  196. head.lthread = 0;
  197. head.lchild = head;
  198. }
  199. else
  200. if (currentNode == parent.lchild)
  201. {
  202. parent.lthread = 0;
  203. parent.lchild = currentNode.lchild;
  204. }
  205. else
  206. {
  207. parent.rthread = 0;
  208. parent.rchild = currentNode.rchild;
  209. }
  210. }
  211. public void case_2(ref Node parent, ref Node currentNode)
  212. {
  213. /* This function is invoked if the node to be removed has only one child (left or right) */
  214. Node child, successor, predecessor;
  215. if (currentNode.lthread == 1)
  216. child = currentNode.lchild;
  217. else
  218. child = currentNode.rchild;
  219. if (parent == head)
  220. head.lchild = child;
  221. else
  222. if (currentNode == parent.lchild)
  223. parent.lchild = child;
  224. else
  225. parent.rchild = child;
  226. successor = Inorder_successor(currentNode);
  227. predecessor = Inorder_predecessor(currentNode);
  228. if (currentNode.rthread == 1)
  229. successor.lchild = predecessor;
  230. else
  231. {
  232. if (currentNode.lthread == 1)
  233. predecessor.rchild = successor;
  234. }
  235. }
  236. public void case_3(ref Node parent, ref Node currentNode)
  237. {
  238. /* This function is invoked if the node to be removed has two children */
  239. Node inorder_suc, inorder_parent;
  240. inorder_parent = currentNode;
  241. inorder_suc = currentNode.rchild;
  242. while (inorder_suc.lthread == 1)
  243. {
  244. inorder_parent = inorder_suc;
  245. inorder_suc = inorder_suc.lchild;
  246. }
  247. currentNode.info = inorder_suc.info;
  248. if (inorder_suc.lthread == 0 && inorder_suc.rthread == 0)
  249. case_1(ref inorder_parent, ref inorder_suc);
  250. else
  251. case_2(ref inorder_parent, ref inorder_suc);
  252. }
  253. static void Main(string[] args)
  254. {
  255. Operations t = new Operations();
  256. while (true)
  257. {
  258. try
  259. {
  260. Console.WriteLine("\n菜单");
  261. Console.WriteLine("1. 插入操作");
  262. Console.WriteLine("2.删除操作");
  263. Console.WriteLine("3.中序编历操作");
  264. Console.WriteLine("4. 退出");
  265. Console.Write("\n请输入您的选择(1-4): ");
  266. char ch = Convert.ToChar(Console.ReadLine());
  267. Console.WriteLine();
  268. switch (ch)
  269. {
  270. case '1':
  271. {
  272. Console.Write("请输入单词: ");
  273. string word = Console.ReadLine();
  274. t.insert(word);
  275. }
  276. break;
  277. case '2':
  278. {
  279. t.remove();
  280. }
  281. break;
  282. case '3':
  283. {
  284. t.Inorder_traversal();
  285. }
  286. break;
  287. case '4':
  288. return;
  289. default:
  290. {
  291. Console.WriteLine("无效选择");
  292. }
  293. break;
  294. }
  295. }
  296. catch (Exception e)
  297. {
  298. Console.WriteLine("请检查您的值.");
  299. }
  300. }
  301. }
  302. }
  303. }



   
  1. /*写一个程序以创建和维护文件中的索引,它包含顾客的纪录。文件中每个纪录包含下面的信息。
  2. 顾客ID(整型)
  3. 顾客的姓名(字符串)
  4. 顾客的电话号码(字符串)
  5. */
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Text;
  9. using System.IO;
  10. namespace Indexing
  11. {
  12. class Node
  13. {
  14. public int key; //键
  15. public int offset; //偏移量
  16. public Node lchild; //左子节点
  17. public Node rchild; //右子节点.
  18. };
  19. class Customer
  20. {
  21. public int key;
  22. public string name;
  23. public string phone;
  24. public void accept()
  25. {
  26. Console.Write("\nEnter customer ID: ");
  27. key = Int32.Parse(Console.ReadLine());
  28. Console.Write("\nEnter name: ");
  29. name = Console.ReadLine();
  30. Console.Write("\nEnter phone: ");
  31. phone = Console.ReadLine();
  32. }
  33. public void read_record(int offset)
  34. {
  35. FileStream fs = new FileStream("E:/Customer.txt", FileMode.Open, FileAccess.Read);
  36. StreamReader r = new StreamReader(fs);
  37. fs.Position = offset;
  38. string k = r.ReadLine();
  39. string nm = r.ReadLine();
  40. string ph = r.ReadLine();
  41. Console.WriteLine("Customer ID: " + k);
  42. Console.WriteLine("\nCustomer name: " + nm);
  43. Console.WriteLine("\nPhone number: " + ph);
  44. Console.WriteLine("\n");
  45. r.Close();
  46. fs.Close();
  47. }
  48. }
  49. class Tree_Index
  50. {
  51. Node ROOT;
  52. public Tree_Index()
  53. {
  54. ROOT = null;
  55. load_Index();
  56. }
  57. public void find(int key, ref Node parent, ref Node currentNode)
  58. {
  59. currentNode = ROOT;
  60. parent = null;
  61. while ((currentNode != null) && (currentNode.key != key))
  62. {
  63. parent = currentNode;
  64. if (key < currentNode.key)
  65. currentNode = currentNode.lchild;
  66. else
  67. currentNode = currentNode.rchild;
  68. }
  69. }
  70. void insert(int key, int offset)
  71. {
  72. Node newnode = new Node();
  73. newnode.key = key;
  74. newnode.offset = offset;
  75. newnode.lchild = null;
  76. newnode.rchild = null;
  77. Node parent = null, currentNode = null;
  78. find(key, ref parent, ref currentNode);
  79. {
  80. if (parent == null)
  81. ROOT = newnode;
  82. else
  83. if (key < parent.key)
  84. parent.lchild = newnode;
  85. else
  86. parent.rchild = newnode;
  87. }
  88. }
  89. int search_key(int key)
  90. {
  91. Node parent=null, currentNode=null;
  92. if(ROOT==null)
  93. {
  94. Console.WriteLine("Tree is empty\n");
  95. }
  96. else
  97. find(key, ref parent,ref currentNode);
  98. if(currentNode==null)
  99. {
  100. Console.WriteLine("This item is not in the tree\n");
  101. return -1;
  102. }
  103. else
  104. {
  105. Console.WriteLine("Customer ID found........offset is " + currentNode.offset +"\n");
  106. return currentNode.offset;
  107. }
  108. }
  109. void load_Index()
  110. {
  111. FileStream fs = new FileStream("E:/Index.txt", FileMode.OpenOrCreate, FileAccess.Read);
  112. StreamReader r = new StreamReader(fs);
  113. r.BaseStream.Seek(0, SeekOrigin.Begin);
  114. while (!r.EndOfStream)
  115. {
  116. string k = r.ReadLine();
  117. string o = r.ReadLine();
  118. insert(Convert.ToInt32(k), Convert.ToInt32(o));
  119. }
  120. r.Close();
  121. }
  122. void inorder(Node ptr)
  123. {
  124. if (ROOT == null)
  125. {
  126. Console.WriteLine("Tree is empty\n");
  127. return;
  128. }
  129. if (ptr != null)
  130. {
  131. inorder(ptr.lchild);
  132. Console.WriteLine(ptr.key + " " + ptr.offset + "\n");
  133. inorder(ptr.rchild);
  134. }
  135. }
  136. void traverse()
  137. {
  138. Console.WriteLine("........Inorder traversal sequence.........\n");
  139. inorder(ROOT);
  140. }
  141. static void Main(string[] args)
  142. {
  143. Tree_Index tobj=new Tree_Index();
  144. Node curr, par;
  145. Customer cobj = new Customer();
  146. int key, offset;
  147. char ch;
  148. do
  149. {
  150. Console.WriteLine("Menu\n");
  151. Console.WriteLine("1. Insert a record");
  152. Console.WriteLine("2. Read a record");
  153. Console.WriteLine("3. View index");
  154. Console.WriteLine("4. Exit");
  155. Console.Write("\nEnter your choice (1-3): ");
  156. ch = Char.Parse(Console.ReadLine());
  157. switch (ch)
  158. {
  159. case '1':
  160. {
  161. cobj.accept();
  162. FileStream fs = new FileStream("E:/Customer.txt", FileMode.Append, FileAccess.Write);
  163. StreamWriter w = new StreamWriter(fs);
  164. offset = Convert.ToInt32(fs.Position);
  165. key = cobj.key;
  166. curr = null;
  167. par = null;
  168. tobj.find(key, ref par, ref curr);
  169. if (curr != null)
  170. {
  171. Console.WriteLine("\nDuplicate Customer IDs not allowed\n");
  172. w.Close();
  173. fs.Close();
  174. break;
  175. }
  176. Console.WriteLine("offset= " + fs.Position);
  177. w.WriteLine(Convert.ToInt32(cobj.key));
  178. w.Flush();
  179. w.WriteLine(cobj.name);
  180. w.Flush();
  181. w.WriteLine(cobj.phone);
  182. w.Flush();
  183. w.Close();
  184. fs.Close();
  185. FileStream fs1 = new FileStream("E:/Index.txt", FileMode.Append, FileAccess.Write);
  186. StreamWriter w1 = new StreamWriter(fs1);
  187. w1.WriteLine(Convert.ToInt32(key));
  188. w1.Flush();
  189. w1.WriteLine(Convert.ToInt32(offset));
  190. w1.Flush();
  191. w1.Close();
  192. fs1.Close();
  193. tobj.insert(key, offset);
  194. tobj.traverse();
  195. }
  196. break;
  197. case '2':
  198. {
  199. Console.Write("\nEnter the customer ID of the record to be searched: ");
  200. key = Convert.ToInt32(Console.ReadLine());
  201. Console.WriteLine("\n");
  202. offset = tobj.search_key(key);
  203. if (offset != -1)
  204. cobj.read_record(offset);
  205. }
  206. break;
  207. case '3':
  208. {
  209. tobj.traverse();
  210. }
  211. break;
  212. case '4': break;
  213. default: Console.WriteLine("Invalid choice.");
  214. break;
  215. }
  216. } while (ch != '4');
  217. }
  218. }
  219. }


文章来源: aaaedu.blog.csdn.net,作者:tea_year,版权归原作者所有,如需转载,请联系作者。

原文链接:aaaedu.blog.csdn.net/article/details/51675081

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。