手搓cpp数据结构库·记录1

一、栈

#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;
	} 
};


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值