一、栈
#ifndef DSxy_H
#define DSxy_H
#include <iostream>
namespace XYds
{
template <typename Type>
class Stack
{
private:
struct stack_node
{
Type value;
stack_node* left;
stack_node* right;
};
stack_node* head;
//指向第一个节点
stack_node* last;
//指向最后一个节点
public:
Stack()
{
head=NULL;
last=NULL;
}
void push(Type p_value)
{
if ( head==NULL )
{
head=new stack_node;
head->value=p_value;
head->right=NULL;
head->left=NULL;
last=head;
}
else
{
stack_node* creater=new stack_node;
creater->value=p_value;
creater->right=NULL;
creater->left=last;
last->right=creater;
last=creater;
}
}
void show()
{
stack_node* observe=head;
std::cout<<"//bottom"<<std::endl;
while(observe!=NULL)
{
std::cout<<observe->value<<std::endl;
observe=observe->right;
}
std::cout<<"//top"<<std::endl;
}
void pop()
{
if (head==NULL)
{
std::cout<<"this stack is empty!"<<std::endl;
return;
}
if (last==head)
{
delete last;
last=NULL;
head=NULL;
}
else
{
stack_node* temp=last;
last=temp->left;
delete temp;
last->right=NULL;
}
}
~Stack()
{
if (head==NULL)
{
return;
}
while(last!=head)
{
stack_node* temp=last;
last=temp->left;
delete temp;
last->right=NULL;
}
delete last;
last=NULL;
head=NULL;
}
};
}
#endif
1.1说明
1.1.1 采用双向链表作为自定义栈的基本结构。自定义类的私有成员变量为head和last指针,分别指向链栈的第一个和最后一个节点。
1.1.2 包含push方法,用于压栈操作。该方法的参数是进栈元素,按值传递。
1.1.3 包含pop方法,用于弹栈操作。该方法无参数。
1.1.4 包含show方法,用于从栈底到栈顶输出栈中的所有元素。
1.2注意事项
1.2.1 将类定义在名称空间XYds中,使用类时需要如下操作:
using namespace XYds;
或者这样操作:
using XYds::Stack;
1.2.2 将类定义放在头文件中,为防止官方库,如iostream被重复引用,需要把类定义放在如下结构中:
#ifndef XYds_H
#define XYds_H
//包含的官方库或自定义头文件
#include <iostream>
/主要代码
#endif
1.2.3 使用类模板,实现这个类的多态
将类定义在如下的结构中,实现类模板:
template <typename Type>
class Stack
{
//code
};
或者:
template <class Type>
class Stack
{
//code
};
1.2.4 为什么不把类方法定义在类的外面?
通常多文件编程时,会把类的定义放到头文件中,把类方法的定义放到cpp文件中,只在头文件中留下类方法的声明。
然而,由于使用了类模板,不支持将类方法的模板和类的模板放在不同的文件中。所以,必须把类方法的定义直接置于类的定义之中。
这也意味着,所有的类方法都是内联函数。
1.2.5 什么时候可以把类方法定义在类的外面?
单文件编程时,可以把类方法定义在类的外面。此时,需要如下操作:
namespace XYds
{
using std::cout;
using std::endl;
template <typename Type>
void Stack<Type>::push(Type p_value)
{
if ( head==NULL )
{
head=new stack_node;
head->value=p_value;
head->next=NULL;
last=head;
}
else
{
stack_node* creater=new stack_node;
creater->value=p_value;
creater->next=NULL;
last->next=creater;
last=creater;
}
}
}
注意这一句是规范,要为不同的Type生成不同的类方法:
void Stack<Type>::push(Type p_value)
1.2.6 按值传参还是按引用传参?
按值传参在遇到自定义类型,如类和结构体时会导致严重的问题。看push方法的定义:
void push(Type p_value)
如果Type为如下的结构:
class myStackNode
{
public:
int* array;
int length;
}
由于array指针往往被分配了内存空间,而使用值传参调用的默认复制构造函数只会进行浅复制,所以传入参数中的array指针所指空间和原有的array指针所指空间是一致的。而在传参完毕后,这个参数——一个临时构造的myStackNode类对象实例将会按照该类型析构函数释放空间(你肯定规定了析构函数要主动释放array指针所指空间,不然会造成内存泄漏),那么,原有类对象实例的空间会被提前释放。这就导致了严重问题!
解决方法是自定义复制构造函数:
class myStackNode
{
public:
int* array;
int length;
myStackNode(const myStackNode& st)
{
length=st.length;
array=new int[length];
for (int i=0;i<length;i++)
array[i]=st.array[i];
}
}
如果如下形式按引用传参可以规避这个问题:
void push(Type& p_value)
然而,这意味着你不能把一个常值作为参数传递给该函数,因为常值的引用是非法的!
Stack a;
a.push(0);
以上操作是不可以过编译的!
1.2.7 必须主动定义析构函数,把双向链表彻底地从内存空间释放。否则会导致内存泄漏!
二、二叉树
#ifndef XYds_H
#define XYds_H
#define MAXLevel 100
#include <iostream>
namespace XYds
{
template <typename atomType>
class treenode
{
public:
atomType value;
treenode* left;
treenode* right;
treenode* father;
};
template <typename atomType>
class QueueforBinaryTree
{
private:
int length;
atomType* array;
int rear;
int head;
public:
QueueforBinaryTree(int p_length)
{
array=new atomType[p_length];
rear=-1;
head=-1;
}
~QueueforBinaryTree()
{
delete [] array;
}
void inqueue(atomType p_value)
{
array[++rear]=p_value;
}
atomType dequeue()
{
return array[++head];
}
atomType checkhead()
{
return array[head+1];
}
int show_head()
{
return head;
}
int show_rear()
{
return rear;
}
};
template <typename atomType>
class StackforBinaryTree
{
private:
int length;
atomType* array;
int top;
public:
StackforBinaryTree()
{
array=new atomType[100];
top=-1;
}
StackforBinaryTree(int p_length)
{
array=new atomType[p_length];
top=-1;
}
~StackforBinaryTree()
{
delete [] array;
}
void push(atomType p_value)
{
array[++top]=p_value;
}
atomType& pop()
{
return array[top--];
}
int showtop()
{
return top;
}
atomType& search(int num)
{
return array[num];
}
atomType& searchtop()
{
return array[top];
}
void changetop(int num)
{
array[top]=num;
}
};
template <typename Type>
class BinaryTree
{
private:
treenode<Type>* root;
public:
//构造函数,将树根指针初始化为NULL
BinaryTree()
{
root=NULL;
}
//按照层次建树
bool build_level(Type* array,int length)
{
if (root!=NULL)
{
return false;
}
QueueforBinaryTree <treenode<Type>*>queue(length+5);
for (int i=0;i<length;i++)
{
if (root==NULL)
{
root=new treenode<Type>;
root->value=array[i];
root->left=NULL;
root->right=NULL;
root->father=NULL;
queue.inqueue(root);
}
else
{
if (queue.checkhead()->left!=NULL && queue.checkhead()->right!=NULL)
{
queue.dequeue();
}
treenode<Type>* last=queue.checkhead();
if (last->left==NULL)
{
treenode<Type>* creater=new treenode<Type>;
last->left=creater;
creater->value=array[i];
creater->left=NULL;
creater->right=NULL;
creater->father=last;
queue.inqueue(creater);
}
else
{
treenode<Type>* creater=new treenode<Type>;
last->right=creater;
creater->value=array[i];
creater->left=NULL;
creater->right=NULL;
creater->father=last;
queue.inqueue(creater);
}
}
}
return true;
}
//深度优先遍历方法
Type* depth_traversal(int returnlength=100,int maxLevel=100)
{
StackforBinaryTree <int>stack_cnt(maxLevel);
StackforBinaryTree <treenode<Type>*>stack_ptr(maxLevel);
stack_ptr.push(root);
stack_cnt.push(0);
Type* returnlist=new Type[returnlength];
int top=-1;
while(1)
{
int cnt=stack_cnt.searchtop();
treenode<Type>* ptr=stack_ptr.searchtop();
if (cnt==0)
{
stack_cnt.changetop(1);
returnlist[++top]=ptr->value;
if (ptr->left!=NULL)
{
stack_ptr.push(ptr->left);
stack_cnt.push(0);
}
}
else if (cnt==1)
{
stack_cnt.changetop(2);
if (ptr->right!=NULL)
{
stack_ptr.push(ptr->right);
stack_cnt.push(0);
}
}
else if (cnt==2 && ptr!=root)
{
stack_cnt.pop();
stack_ptr.pop();
}
else
{
break;
}
}
return returnlist;
}
//广度优先遍历方法
Type* breadth_traversal(int returnlength=100,int maxLength=100)
{
QueueforBinaryTree <treenode<Type>*>queue(maxLength);
queue.inqueue(root);
Type* returnlist=new Type[returnlength];
int top=-1;
while(1)
{
if (queue.show_head()==queue.show_rear())
{
break;
}
treenode<Type>* ptr=queue.checkhead();
returnlist[++top]=ptr->value;
if (ptr->left!=NULL)
{
queue.inqueue(ptr->left);
}
if (ptr->right!=NULL)
{
queue.inqueue(ptr->right);
}
queue.dequeue();
}
return returnlist;
}
//深度优先搜索方法
treenode<Type>* depth_search(Type target,int maxLevel=100)
{
StackforBinaryTree <int>stack_cnt(maxLevel);
StackforBinaryTree <treenode<Type>*>stack_ptr(maxLevel);
stack_ptr.push(root);
stack_cnt.push(0);
while(1)
{
int cnt=stack_cnt.searchtop();
treenode<Type>* ptr=stack_ptr.searchtop();
if (cnt==0)
{
stack_cnt.changetop(1);
if (ptr->value==target)
{
return ptr;
}
if (ptr->left!=NULL)
{
stack_ptr.push(ptr->left);
stack_cnt.push(0);
}
}
else if (cnt==1)
{
stack_cnt.changetop(2);
if (ptr->right!=NULL)
{
stack_ptr.push(ptr->right);
stack_cnt.push(0);
}
}
else if (cnt==2 && ptr!=root)
{
stack_cnt.pop();
stack_ptr.pop();
}
else
{
return NULL;
}
}
}
//广度优先搜索方法
treenode<Type>* breadth_search(Type target,int maxLength=100)
{
QueueforBinaryTree <treenode<Type>*>queue(maxLength);
queue.inqueue(root);
while(1)
{
if (queue.show_head()==queue.show_rear())
{
break;
}
treenode<Type>* ptr=queue.checkhead();
if (ptr->value==target)
{
return ptr;
}
if (ptr->left!=NULL)
{
queue.inqueue(ptr->left);
}
if (ptr->right!=NULL)
{
queue.inqueue(ptr->right);
}
queue.dequeue();
}
return NULL;
}
//检查某个节点的方法
treenode<Type>& check_node(treenode<Type>* target)
{
return *target;
}
//检查某个节点内容的方法
//返回内容类型的引用!
Type& check(treenode<Type>* target)
{
return target->value;
}
//更改某个节点的方法
void change(treenode<Type>* target,Type p_value)
{
target->value=p_value;
}
//删除某个节点的方法
//如果是叶子节点,就删除该叶节点;
//如果是非叶子节点,就递归删除该节点以及其所有的子节点
bool delete_node(treenode<Type>* &target)
{
if (target==NULL)
{
return false;
}
StackforBinaryTree <int>stack_cnt(MAXLevel);
StackforBinaryTree <treenode<Type>*>stack_ptr(MAXLevel);
stack_ptr.push(target);
stack_cnt.push(0);
while(1)
{
int cnt=stack_cnt.searchtop();
treenode<Type>* ptr=stack_ptr.searchtop();
if (cnt==0)
{
stack_cnt.changetop(1);
if (ptr->left!=NULL)
{
stack_ptr.push(ptr->left);
stack_cnt.push(0);
}
}
else if (cnt==1)
{
stack_cnt.changetop(2);
if (ptr->right!=NULL)
{
stack_ptr.push(ptr->right);
stack_cnt.push(0);
}
}
else if (cnt==2 && ptr!=target)
{
delete ptr;
stack_cnt.pop();
stack_ptr.pop();
}
else
{
delete target;
if (target->father==NULL)
{
target=NULL;
}
else if ((target->father)->left==target)
{
(target->father)->left=NULL;
}
else
{
(target->father)->right=NULL;
}
break;
}
}
return true;
}
//手动加入节点的方法
//如果该节点是空的根节点,在根节点上加入,返回true
//如果该节点不是根节点,在孩子节点上加入,返回true
//否则返回false,这包括两个树枝已经被占用的情况等
bool add_manual(Type data,treenode<Type>* target=NULL,bool add_toleft=true)
{
if (target==NULL && NULL==root)
{
root=new treenode<Type>;
root->value=data;
root->left=NULL;
root->right=NULL;
root->father=NULL;
return true;
}
if (target==NULL)
{
return false;
}
if (add_toleft&&target->left==NULL)
{
treenode<Type>* creater=new treenode<Type>;
target->left=creater;
creater->value=data;
creater->left=NULL;
creater->right=NULL;
creater->father=target;
return true;
}
if (!add_toleft&&target->right==NULL)
{
treenode<Type>* creater=new treenode<Type>;
target->right=creater;
creater->value=data;
creater->left=NULL;
creater->right=NULL;
creater->father=target;
return true;
}
return false;
}
//析构函数,防止内存泄漏
~BinaryTree()
{
if (root==NULL)
{
return;
}
StackforBinaryTree <int>stack_cnt(MAXLevel);
StackforBinaryTree <treenode<Type>*>stack_ptr(MAXLevel);
stack_ptr.push(root);
stack_cnt.push(0);
while(1)
{
int cnt=stack_cnt.searchtop();
treenode<Type>* ptr=stack_ptr.searchtop();
if (cnt==0)
{
stack_cnt.changetop(1);
if (ptr->left!=NULL)
{
stack_ptr.push(ptr->left);
stack_cnt.push(0);
}
}
else if (cnt==1)
{
stack_cnt.changetop(2);
if (ptr->right!=NULL)
{
stack_ptr.push(ptr->right);
stack_cnt.push(0);
}
}
else if (cnt==2 && ptr!=root)
{
delete ptr;
stack_cnt.pop();
stack_ptr.pop();
}
else
{
delete ptr;
break;
}
}
}
};
}
#endif
2.1说明
2.1.1 定义了四个类模板。分别是treenode、QueueforBinaryTree、StackforBinaryTree和BinaryTree。treenode类定义二叉树节点;QueueforBinaryTree以及StackforBinaryTree分别封装了一个栈和一个队,用于辅助二叉树的建立和遍历;BinaryTree是我们所需的二叉树类。
2.1.2 BinaryTree私有成员变量仅仅包含根指针!
2.1.3 包含build_level方法,用于按照层次建立二叉树,仅用于二叉树根指针为空的情况。参数是指向Type的指针(指向数组)array和int类型数组长度length。返回值为bool型变量,若成功建树则返回true,否则返回false。
2.1.4 包含depth_traversal方法,用于深度优先遍历。参数是返回数组长度和遍历时的地址栈长度,默认参数均为100。返回值是一个指向返回数组的指针,由于返回数组returnlist被定义在堆中,注意手动释放内存!否则会导致内存泄漏!
2.1.5 包含breadth_traversal方法,用于广度优先遍历。使用方法与深度优先遍历一致!参数是返回数组长度和遍历时的地址队长度。
2.1.6 包含depth_search方法,用于深度优先搜索。参数是Type类型目标(按值传递)和遍历时的地址栈长度(默认参数为100)。返回值是第一个匹配节点的地址。
2.1.7 包含breadth_search方法,用于广度优先搜索。使用方法和深度优先搜索一致!第二个参数遍历时的地址队长度。
2.1.8 包含check_node方法,参数是某个树节点的地址,返回这个树节点的引用。
2.1.9 包含check方法,参数是某个树节点的地址,返回这个树节点数据的引用
2.1.10 包含change方法,用于更改树节点的内容,参数某个树节点的地址和需要改变的值(按值传参)。
2.1.11 包含delete_node方法,用于删除某个节点及其附属的树。参数是树节点的地址,返回值是bool型变量。成功删除返回true,否则返回false。
2.1.12 包含add_manual方法,用于手动添加节点,包含三个参数,第一个参数为节点将要储存的数据(按值传递);第二个节点为目标地址,默认为空;第三个节点是bool型参数,默认为true,决定存在左孩子节点还是右孩子节点。方法返回值为bool型变量,成功添加节点返回true。注意,如果目标节点是空的根节点,则在根节点上加入;如果该节点不是根节点,则在其孩子节点上加入。
2.2 注意事项
2.2.1 指向类模板的引用和指针
如果我们如下定义了一个类模板:
template <typename atomType>
class Queue
{
//类定义的内容。
}
那么,创建这个类的对象实例,需要如此声明对象
Queue <int>a;
创建这个类的指针,需要如此声明:
Queue<int>* ptr_a;
创建这个类的引用,需要如此声明:
Queue<int>& quote_a;
我们在遍历二叉树的时候建立了指向树节点的地址栈,且树节点是通过函数模板生成的,所以有了下面这样抽象的语句:
StackforBinaryTree <treenode<Type>*>stack_ptr(MAXLevel);
2.2.2 重载==运算符
treenode<Type>* depth_search(Type target,int maxLevel=100)
{
StackforBinaryTree <int>stack_cnt(maxLevel);
StackforBinaryTree <treenode<Type>*>stack_ptr(maxLevel);
stack_ptr.push(root);
stack_cnt.push(0);
while(1)
{
int cnt=stack_cnt.searchtop();
treenode<Type>* ptr=stack_ptr.searchtop();
if (cnt==0)
{
stack_cnt.changetop(1);
if (ptr->value==target)
{
return ptr;
}
if (ptr->left!=NULL)
{
stack_ptr.push(ptr->left);
stack_cnt.push(0);
}
}
else if (cnt==1)
{
stack_cnt.changetop(2);
if (ptr->right!=NULL)
{
stack_ptr.push(ptr->right);
stack_cnt.push(0);
}
}
else if (cnt==2 && ptr!=root)
{
stack_cnt.pop();
stack_ptr.pop();
}
else
{
return NULL;
}
}
}
广度优先搜索和深度优先搜索中有这样一句
ptr->value==target
类对象和类对象之间==运算符是未定义的。如果你的Type是一个类或者结构体,这段代码就会奔溃。所以要对自定义类和结构体重载==运算符!示例如下:
struct mark
{
int cnt;
int value;
mark():cnt(0),value(0)
{
}
mark(int p_cnt,int p_value)
{
cnt=p_cnt;
value=p_value;
}
bool operator== (const mark& other)
{
if (cnt==other.cnt && value==other.value)
return true;
else
return false;
}
};