模板在二叉树和队列中的应用(借助队列广度遍历二叉树)
接着这两三天的时间把模板、队列、链表、二叉树等知识又过了一遍。也亲自动手写了以上数据结构,算是把当年学数据结构时没做完的实验重新做了一遍,有点小小的成就感。因此写此博文以记之,以备以后用的时候查阅。
模板和头文件的知识前面两篇博文已经大概交代了一下,以前没学模板的时候看到这东西头就大,现在学习了一下感觉模板对于代码的重用对提高开发效率还是很有帮助的。
一、链表的实现:
1 //Node.h
2 #ifndef NODE_H_H
3 #define NODE_H_H
4 template<class T>class LinkList;
5 template<class T>
6 class Node
7 {
8 friend LinkList<T>;
9 private:
10 T Data;
11 Node *Next;
12 };
13 #endif
14 ///////////////////////////////////////////////////////
15 //LinkList.h
16 #include "Node.h"
17 #ifndef LINKLIST_H_H
18 #define LINKLIST_H_H
19 template<class T>
20 class LinkList
21 {
22 private:
23 Node<T> *head;
24 public:
25 LinkList();
26 ~LinkList();
27 void Insert(T data);
28 void printLinkList();
29 Node<T> *Create(int len);
30 };
31 #endif
32
33 template<class T>
34 LinkList<T>::LinkList()
35 {
36 head=NULL;
37 }
38
39 template<class T>
40 LinkList<T>::~LinkList()
41 {
42 Node<T> *p;
43 while(head)
44 {
45 p=head->Next;
46 delete head;
47 head=p;
48 cout<<"析构!"<<endl;
49 }
50 }
51 template <class T>
52 Node<T> *LinkList<T>::Create(int len)
53 {
54 Node<T> *p;
55 Node<T> *temp;
56 T s;
57 while(len--)
58 {
59 cout<<"输入数据:"<<endl;
60 cin>>s;
61 temp=new Node<T>;
62 temp->Data=s;
63 temp->Next=NULL;
64 if (!head)
65 {
66 head=temp;
67 p=head;
68 }
69 else
70 {
71 p->Next=temp;
72 p=temp;
73 }
74 }
75 return head;
76 }
77 template<class T>
78 void LinkList<T>::Insert(T data)
79 {
80 Node<T> *p=head;
81 Node<T> *s=new Node<T>;
82 s->Data=data;
83 s->Next=NULL;
84 if (head==NULL)
85 {
86 head=s;
87
88 }
89 else
90 {
91 while(p->Next)
92 {
93 p=p->Next;
94 }
95 p->Next=s;
96 }
97 }
98
99 template<class T>
100 void LinkList<T>::printLinkList()
101 {
102 Node<T> *p=head;
103 while(p)
104 {
105 cout<<p->Data<<endl;
106 p=p->Next;
107
108 }
109 }
110 ///////////////////////////////////////////////////////////////////
111 //TemplateLinkList.cpp
112 #include <iostream>
113 #include<string>
114 #include "Node.h"
115 #include "LinkList.h"
116 using namespace std;
117
118 int main()
119 {
120 LinkList<char> L;
121 L.Insert('a');
122 L.Insert('b');
123 L.Insert('c');
124 L.printLinkList();
125
126 LinkList<string> s;
127 s.Insert("abcd");
128 s.Insert("efgh");
129 s.Insert("ijkl");
130 s.printLinkList();
131
132 LinkList<int> t;
133 t.Insert(1);
134 t.Insert(2);
135 t.Insert(3);
136 t.printLinkList();
137
138 LinkList<double> d;
139 Node<double> *b;
140 b=d.Create(4);
141 d.printLinkList();
142
143 //Node<int> n;
144 return 0;
145 }
有以下几点需要注意的地方:
1、在封闭链表时没有用模板时是通过检测指定的特殊字符,比如'*'来封闭链表。但是用了模板之后对不同的数据类型,前者这种方法只对char类型是有效。所以最后改成Node<T> *Create(int len);通过长度来封闭链表。再写链表的时候用到两个类Node和LinkList注意Node成员如果是private的话要在Node中声明为友元。还有就是在链表中因为要插入节点或者对节点操作,往往要先用一个或者是两个指针指到你要操作的节点或是该节点的前继。一般使用while(p){p=p->Next;}或是while(p->Next){p=p->Next;}具体用的时候还需要仔细考虑一下,比如要指到最后一个节点应该采用while(p->Next){p=p->Next;}。
2、写析构函数的时候一开始写的比较麻烦,最后参考别人写了一种比较简洁的。代码如下:
1 template<class T>
2 LinkList<T>::~LinkList()
3 {
4 Node<T> *p;
5 while(head)
6 {
7 p=head->Next;
8 delete head;
9 head=p;
10 cout<<"析构!"<<endl;
11 }
12 }
3、写链表主要还是指针的操作,有的时候指来指去容易搞错需要注意,实现的过程中注意有头结点和没有头结点的区别。
二、队列的实现
1 //Queue.h
2 #ifndef QUQUE_H_H
3 #define QUQUE_H_H
4 template<class T>
5 class Node
6 {
7 public:
8 T Data; //节点数据
9 Node<T> *Next; //指向下一个节点
10 };
11
12 template<class T>
13 class Queue
14 {
15 private:
16 Node<T> *Front; //队头,出队
17 Node<T> *Rear; //队尾,入队
18 public:
19 Queue();
20 ~Queue();
21 void EnQueue(T data);
22 T DeQueue();
23 bool QueueEmpty() const;
24
25 };
26 #endif
27
28 template<class T>
29 Queue<T>::Queue()
30 {
31 Front=Rear=NULL;
32 }
33
34 template<class T>
35 Queue<T>::~Queue()
36 {
37 Node<T>*p=Front;
38 while(Front)
39 {
40 p=p->Next;
41 delete Front;
42 Front=p;
43 }
44 Front=Rear=NULL;
45 cout<<"XiGou!"<<endl;
46 }
47
48 template<class T>
49 void Queue<T>::EnQueue(T data)
50 {
51 Node<T> *p;
52 Node<T> *s=new Node<T>;
53 s->Data=data;
54 s->Next=NULL;
55 //队列为空
56 if (Front==NULL&&Rear==NULL)
57 {
58 Front=s;
59 Rear=s;
60 }
61 else
62 {
63 p=Front;
64 //指到队尾
65 while (p->Next)
66 {
67 p=p->Next;
68 }
69 //入队一个节点
70 p->Next=s;
71 Rear=s; //Rear始终指向队尾
72 }
73 }
74
75 template<class T>
76 T Queue<T>::DeQueue()
77 {
78 Node<T> *p=Front;
79 T data=Front->Data;
80 Front=Front->Next;
81 //队列中只有一个节点,删除后队列置空
82 if(NULL==Front) Rear=NULL;
83 delete p;
84 return data;
85 }
86
87 template<class T>
88 bool Queue<T>::QueueEmpty() const
89 {
90 if (Front==NULL&&Rear==NULL)
91 {
92 return true;
93 }
94 return false;
95 }
需要注意的地方:
1、知道队列的基本原理"先进先出",从队头(Front)出队尾(Rear)进.
2、Front指针永远指向队头,Rear永远指向队尾。当进行了出队和入队后记得更新Front和Rear指针。
3、如果本身队列只有一个节点,出队后队列为空注意此时Front=Rear=NULL,避免成为悬浮指针。
4、注意类模板的语法。
三、二叉树的实现
1 #include "Queue.h"
2 #ifndef BINARYTREE_H_H
3 #define BINARYTREE_H_H
4 template<class T>
5 class BinaryTree
6 {
7 private:
8 BinaryTree<T> *left;
9 BinaryTree<T> *right;
10 T data;
11 public:
12 void Create(BinaryTree<T> * &root);
13 void PreOrder(BinaryTree<T> *p);
14 void InOrder(BinaryTree<T> *p);
15 void PostOrder(BinaryTree<T> *p);
16 void CountLeaf(BinaryTree<T> *p,int &count);
17 void BinatyTreeDepth(BinaryTree<T> *p,int level,int &count);
18 void LayerOrder(BinaryTree<T> *t);
19 };
20 #endif
21
22 template<class T>
23 void BinaryTree<T>::Create(BinaryTree<T> * &root)
24 {
25 T a;
26 cin>>a;
27 if (a!='*')
28 {
29 root=new BinaryTree;
30 root->data=a;
31 Create(root->left);
32 Create(root->right);
33 }
34 else root=NULL;
35 }
36
37 template<class T>
38 void BinaryTree<T>::PreOrder(BinaryTree<T> *p)
39 {
40 if (p!=NULL)
41 {
42 cout<<p->data<<endl;
43 PreOrder(p->left);
44 PreOrder(p->right);
45 }
46 }
47
48 template<class T>
49 void BinaryTree<T>::InOrder(BinaryTree<T> *p)
50 {
51 if (p!=NULL)
52 {
53 InOrder(p->left);
54 cout<<p->data<<endl;
55 InOrder(p->right);
56 }
57 }
58
59 template<class T>
60 void BinaryTree<T>::PostOrder(BinaryTree<T> *p)
61 {
62 if (p!=NULL)
63 {
64 PostOrder(p->left);
65 PostOrder(p->right);
66 cout<<p->data<<endl;
67 }
68 }
69
70 template<class T>
71 void BinaryTree<T>::CountLeaf(BinaryTree<T> *p,int &count)
72 {
73 if (p)
74 {
75 if (!(p->left)&&!(p->right))
76 {
77 count++;
78 }
79 //注意这两句话外层这个if语句大括号里
80 CountLeaf(p->left,count);
81 CountLeaf(p->right,count);
82 }
83 }
84
85 template<class T>
86 void BinaryTree<T>::BinatyTreeDepth(BinaryTree<T> *p,int level,int &count)
87 {
88 if (p)
89 {
90 if (level>count)count=level;
91 BinatyTreeDepth(p->left,level+1,count);
92 BinatyTreeDepth(p->right,level+1,count);
93 }
94 }
95
96
97 template<class T>
98 void BinaryTree<T>::LayerOrder(BinaryTree<T> *t)
99 {
100 Queue<BinaryTree<T>*> q;
101 BinaryTree<T> *p;
102 T da;
103 q.EnQueue(t);
104 while(!q.QueueEmpty())
105 {
106 p=q.DeQueue();
107 da=p->data;
108 cout<<da<<endl;
109 if(p->left)
110 q.EnQueue(p->left);
111 if(p->right)
112 q.EnQueue(p->right);
113 }
114 }
需要注意的几点地方:
1、void Create(BinaryTree<T> * &root);中&不能少。如果不要&递归每次生成左子树和右子树每次不会生成新的节点。
2、统计叶节点的思路“当一个节点的左子树和右子树为空时,该节点就是叶节点”if (!(p->left)&&!(p->right)){count++; }。
3、计算树的深度“每递归往下走一层,对比当前子树深度是否大于当前记录的值,大于就替换”
4、广度遍历二叉树时需要用到队列,队列的元素师指向树的指针,从根节点的指针开始入队,每个while()循环把当前队头节点的两个节点入队。所以循环一共循环非叶节点个数减一次,减一主要是根节点一开始就入队了。最后队列中就按层次的顺序排列了树的部分节点。注意不是全部节点都在队列中,入队和出队是动态的过程,不是一口气全部节点全部入队再出队。