数据结构——用户登陆系统

数据结构课程设计——用户管理系统

涉及到的东西:MFC,file,AVL;
作为一名初学者,记录一下这个学期的数据结构的课程设计;先上任务书要求。
1.【问题描述】:
在登录服务器系统时,都需要验证用户名和密码,如 telnet 远程登录服务器。
用户输入用户名和密码后,服务器程序会首先验证用户信息的合法性。由于用户信息的验证
频率很高,系统有必要有效地组织这些用户信息,从而快速查找和验证用户。另外,系统也
会经常会添加新用户、删除老用户和更新用户密码等操作,因此,系统必须采用动态结构,
在添加、删除或更新后,依然能保证验证过程的快速。请采用相应的数据结构模拟用户登录
系统,其功能要求包括用户登录、用户密码更新、用户添加和用户删除等。
2.【基本要求】:

  1. 要求自己编程实现二叉树结构及其相关功能,以存储用户信息,不允许使用标准模板类
    的二叉树结构和函数。同时要求根据二叉树的变化情况,进行相应的平衡操作,即 AVL
    平衡树操作,四种平衡操作都必须考虑。测试时,各种情况都需要测试,并附上测试截
    图;
  2. 要求采用类的设计思路,不允许出现类以外的函数定义,但允许友元函数。主函数中只
    能出现类的成员函数的调用,不允许出现对其它函数的调用。
  3. 要求采用多文件方式:.h 文件存储类的声明,.cpp 文件存储类的实现,主函数 main 存
    储在另外一个单独的 cpp 文件中。如果采用类模板,则类的声明和实现都放在.h 文件中。
  4. 不强制要求采用类模板,也不要求采用可视化窗口;要求源程序中有相应注释;
  5. 要求测试例子要比较详尽,各种极限情况也要考虑到,测试的输出信息要详细易懂,表
    明各个功能的执行正确;
  6. 建议采用 Visual C++ 6.0 及以上版本进行调试;

简单的说就是三点1.实现平衡二叉树的操作;2.文件读写;3.登陆界面的设计;

1.平衡二叉树的实现

原理不过多赘述,直接写关键函数;关于AVL树的关键函数有更新树的高度,以及四种旋转的操作;
1.LL(左单旋)

template<typename T>
binnode<T>*AVL<T>::LL(binnode<T>*ptr) {

	binnode<T>*tmp = ptr->right;
//将传进来的结点的右结点进行旋转操作;
	ptr->right = tmp->left;//将其tmp的左子树挂在传ptr的右子树上
	tmp->left = ptr;//将ptr做为tmp的左子树挂上去
	ptr->height = getheight(ptr->left) > getheight(ptr->right) ? getheight(ptr->left)+1 : getheight(ptr->right)+1;//更新树高的函数在后面解释
	tmp->height = getheight(tmp->left) > ptr->height ? getheight(tmp->left) + 1 : ptr->height + 1;
	return tmp;//返回旋转之后的根结点;

}

左单旋图示:

2.RR旋转(右单旋)

template<typename T>
binnode<T>* AVL<T>::RR(binnode<T>*ptr) {

	binnode<T>*tmp = ptr->left;//tmp是ptr的左子女
	ptr->left = tmp->right;//将tmp的右子树挂在ptr的左子树上
	tmp->right = ptr;//将ptr作为子树挂在tmp的右子树上;
	ptr->height = getheight(ptr->left) > getheight(ptr->right) ? getheight(ptr->left)+1 : getheight(ptr->right)+1;//更新树高
	tmp->height = getheight(tmp->left) > ptr->height ? getheight(tmp->left) + 1 : ptr->height + 1;
	return tmp;//返回现在的根结点;
}

右单旋图示:
在这里插入图片描述
3.左右双旋(LR):麻烦结点出现在不平衡结点的左子树的右子树上;

template<typename T>
binnode<T>*AVL<T>::LR(binnode<T>*ptr) {

	 ptr->left = LL(ptr->left);//先对ptr的左结点进行左旋

	return RR(ptr);//在对ptr进行右旋并返回;
    //可以将双旋看成是两次单旋;

}

左右双旋图示:
在这里插入图片描述
4.右左双旋(RL):麻烦结点出现在不平衡结点的右子树的左子树上时;

template<typename T>
binnode<T>*AVL<T>::RL(binnode<T>*ptr) {
   //同上可以将其分解成两次双旋的过程;

    ptr->right= RR(ptr->right);//对右子女进行右旋

	return LL(ptr);//对其左旋;
}

右左双旋图示:
在这里插入图片描述
5.插入函数:对于要插入的结点,先对AVL树进行查找,若找到了则不插入,若没找到进行插入,(注意AVL是排序树所以插入的操作与BST树插入的操作相似,只不过多了一个更新树的高度的操作);

template<typename T>
void AVL<T>::insert( binnode<T>* &ptr, const T &item) {

	if (ptr==NULL)
	{
		ptr = new binnode<T>();
           //当ptr为NULL时,证明该中没有要插入的结点,所以要进行插入操作;
		ptr->data = item;
		ptr->height=1;//将树的高度置为1;
		ptr->left = ptr->right = NULL;//左右子树置空;
		
		
	}
	else if (item<ptr->data)
	{
	     insert(ptr->left, item);//递归插入左子树

		if (getheight(ptr->left)-getheight(ptr->right)==2)
		{//发生不平衡时,因为是在左子树上是进行操作,所以只存在左子树高于右子树;

			if (item<ptr->left->data)
			{//当插入的结点构成了RR不平衡,即麻烦结点在不平衡结点的左子树的左子树上时;
				ptr = RR(ptr);
		//右旋	
			}
			else {
				ptr = LR(ptr);
		//若构成了LR不平衡,左旋;
			}
		}
	}
	else if (item > ptr->data) {
           //对于右子树的操作同左子树;
		   insert(ptr->right, item);

		if (getheight(ptr->left)-getheight(ptr->right)==-2)
		{
			if (item > ptr->right->data) {
				
				ptr = LL(ptr);

			}
			else {
				ptr = RL(ptr);
			}
		}
	}
	else
	{
		cerr << "树中已存在该项,插入失败";
		return;
		
	}
	ptr->height=getheight(ptr->left) > getheight(ptr->right) ? getheight(ptr->left) +1: getheight(ptr->right)+1;
//在函数最后需要更新树高;
}

6.删除函数:同BST树的删除,需要分三种情况,且注意平衡情况;代码如下:

template <typename T>
void AVL<T>::remove(binnode<T>*&ptr, const T &item) {
//操作与插入相似,但是删除的时候需要分左右子树都存在,或者都不存在,或只存在一个
//不赘述了;
	if (ptr == NULL) {
		return;
	}
	else {

		if (item < ptr->data) {


			remove(ptr->left, item);

			if (getheight(ptr->right) - getheight(ptr->left) > 1) {

				if (getheight(ptr->right->left) > getheight(ptr->right->left)) {
					ptr = RL(ptr);

				}
				else {
					ptr = LL(ptr);
				}
			}
			else {

				ptr->height = getheight(ptr->left) > getheight(ptr->right) ? getheight(ptr->left) + 1 : getheight(ptr->right) + 1;
			}


		}
		else if (item > ptr->data) {
			remove(ptr->right, item);

			if (getheight(ptr->left) - getheight(ptr->right) > 1) {

				if (getheight(ptr->left->right) > getheight(ptr->left->left)) {

					ptr = LR(ptr);
				}
				else {
					ptr = RR(ptr);
				}

			}
			else
			{
				ptr->height = getheight(ptr->left) > getheight(ptr->right) ? getheight(ptr->left) + 1 : getheight(ptr->right) + 1;
			}
		}
		else {
			if (ptr->left != NULL && ptr->right != NULL) {

				if (getheight(ptr->left) > getheight(ptr->right)) {
					binnode<T>*tmp = ptr->left;
					while (tmp->right != NULL) {
						tmp = tmp->right;
					}
					ptr->data = tmp->data;
					remove(ptr->left, ptr->data);
				}
				else {

					binnode<T>*tmp = ptr->right;

					while (tmp->left != NULL) {

						tmp = tmp->left;

					}
					ptr->data = tmp->data;
					remove(ptr->right, ptr->data);

				}
			}
			else {
				binnode<T>*tmp = ptr;

				if (ptr->left != NULL) {

					ptr = ptr->left;
				}
				else {

					ptr = ptr->right;

				}
				delete  tmp;
			}
        
		}
	}

}

7.树高更新:在node结点中加入数据height用来记录当前结点的树的高度;

template<typename T>
class binnode {

public:

	T data;

	binnode*left;

	binnode*right;

	int  height;//记录树的高度;

};

获取树高的函数get_height

template<typename T>
int AVL<T>::getheight(binnode<T>*p) {

	if (p == NULL) {
		int a = 0;
		return a;
	}
	else
	{
		return p->height;//对于非空结点直接访问其树高即可;
	}

	
}

用户界面设计(MFC库)

在这次作业中使用了MFC(VS中可以下载)做用户的界面;先上成果图在一步步解释;
在这里插入图片描述
界面由密码输入框,用户名输入框,和四个控件组成;
1.新建一个MFC对话框;

在VS的新建中选择MFC应用程序,没有的朋友们可以自己去扩展程序中下载;打开后选择基于对话框然后一直下一步就好了;
在这里插入图片描述
接着就会来到像下面图片一样的界面
在这里插入图片描述在左边的工具栏中添加相关控件;添加完之后我们需要对对话框进行一个添加类的操作,以及对相关控件进行添加变量的操作;(目的是为了是他们能够被正常的调用和程序关联起来);
1.添加类(1)右击对话框,添加类;之后会自动生成一个.h的头文件;(2)添加变量:右击相关控件,添加变量,对变量进行命名;
2.相关控件的编程:以登陆按钮为例子,添加变量之后,双击登陆按钮;会进入刚刚添加的类中,将头文件包括进来;


#include "stdafx.h"
#include "classwork.h"
#include "CDia.h"//刚刚添加的文件,将其include进来;
#include "afxdialogex.h"
#include "AVL.h"//将写好的AVL树头文件include进来,并根据文件内容初始化一颗树

双击登陆之后会得到自动跳到对该控件的编写之中;

void CclassworkDlg::OnBnClickedButton1()
{      //登陆按钮的实现。
		UpdateData(true);//获取最新的用户名和密码输入框中的值
	if (user_name.IsEmpty()) {//User_name是添加的用户名变量;
		MessageBox(_T("登陆失败,用户名不能为空"));
		return;//当用户名为空时,返回;
	}
	string str2 = CW2A(user_name.GetString());
//由于用户输入框的类型时cstring 类型的所以需要将其转化为string类型;
//使用CW2A将CSstring 转化为string类型。
	if (t.search(str2)) {
		//在树中查找给定的用户名,调用树中的查找函数(本文章中并没有写出)
		//查找函数若成功找到返回true,未能找到返回false;
		string str1 = CW2A(user_pwd.GetString());
		//若找到了,将输入框中的值,赋给str1;
		if (f.search(str2)==str1) {
			//调用File类查找功能并且返回密码进行对比
			//flie类中的查找功能根据用户名在文件中进行查找,若找到则返回密码;
			//若密码相符,则弹出登陆成功;
			MessageBox(_T("登陆成功!欢迎你!"));
		}
		else {
			MessageBox(_T("登陆失败,密码错误"));
		}
	}
	else {
		MessageBox(_T("登陆失败,用户名不存在"));
	}
}

其他控件写法类似不过多赘述;

file文件类(读写)

需要实现的目的:根据文件中的内容返回其密码等;

class file{
	public:
	void Insert(string name,string pwd);//向文本中插入新用户
	void remove(string name);//删除文本中的指定用户
	string search(string name);//根据查找的用户名返回密码;
	private:
		fstream f;
};

插入函数:

void file::Insert(string name,string pwd){
	if(search(name)!="-1"){
		return;
	}
	f.open("1.txt",ios::app|ios::out);
	f<<name<<" "<<pwd<<endl;
	f.close(); 
}

删除函数:首先,因为flie没有办法删除指定的内容,所以想法就是将文本中的内容先选择性保存起来,再重新写入;代码如下;

void file::remove(string name){
		fstream tmpf;	
		tmpf.open("2.txt",ios::out|ios::trunc);
		f.open("1.txt",ios::in);	
		while(1){
			string str1,str2;
			f>>str1>>str2;
			if(str1!=name){	
			tmpf<<str1<<" "<<str2<<endl;
		    }
			if(f.eof()){
				break; 
			}
		}  
        f.close();
	    tmpf.close();
	    tmpf.open("2.txt",ios::in);
		f.open("1.txt",ios::out|ios::trunc) ;
		while(1){
			string str1,str2;
			tmpf>>str1>>str2;
			f<<str1<<" "<<str2<<endl;
			if(tmpf.eof()){
				break;
			}	
		} 
	    tmpf.close() ;
	    tmpf.open("2.txt",ios::trunc|ios::out);
	    tmpf.close();
	    f.close();
}

查找函数:对给定的用户名进行查找,并且返回密码;

string file::search(string name){
	f.open("1.txt",ios::in);
	while(1){
		string str,str2;
		f>>str>>str2;
		if(name==str){
			f.close();
			return str2;
		}
		else if(f.eof()){
			f.close();
			break;
			
		}
	} 
	f.close();
	return "-1";//用-1作为特殊值;
	
}

以上就是大概的这次课程设计的内容了,经过老师的指正后发现了一些错误,比如登陆控件的查找中,应该是对AVL树的查找和删除,文件流只提供读写的操作等;以上只是一些关键函数的实现;并非完整的代码,能力有限,希望大佬指点,这学期刚转的计算机专业,还是个菜鸟(记录自己的成长~~),欢迎大家指点呀~~;如果想要完整代码的可以私信我;(介于很多朋友私信我要代码,我已经把代码上传到了github上面以免我回复不及时。github地址能点两个星嘛再好不过。不点嘛也就算了。祝大家学业有成!!
//如果对AVL树的操作还有问题的可以参照一下《数据结构第二版》陈越老师的这本书(书上解析更详细)

  • 11
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
大二的课程设计 一、 用户登录系统的模拟 【问题描述】在登录服务器系统时,都需要验证用户名和密码,如telnet远程登录服务器。用户输入用户名和密码后,服务器程序会首先验证用户信息的合法性。由于用户信息的验证频率很高,系统有必要有效地组织这些用户信息,从而快速查找和验证用户。另外,系统也会经常会添加新用户、删除老用户和更新用户密码等操作,因此,系统必须采用动态结构,在添加、删除或更新后,依然能保证验证过程的快速。请采用相应的数据结构模拟用户登录系统,其功能要求包括用户登录、用户密码更新、用户添加和用户删除等。 【基本要求】 1. 要求自己编程实现二叉树结构及其相关功能,以存储用户信息,不允许使用标准模板类的二叉树结构和函数。同时要求根据二叉树的变化情况,进行相应的平衡操作,即AVL平衡树操作,四种平衡操作都必须考虑。测试时,各种情况都需要测试,并附上测试截图; 2. 要求采用类的设计思路,不允许出现类以外的函数定义,但允许友元函数。主函数中只能出现类的成员函数的调用,不允许出现对其它函数的调用。 3. 要求采用多文件方式:.h文件存储类的声明,.cpp文件存储类的实现,主函数main存储在另外一个单独的cpp文件中。如果采用类模板,则类的声明和实现都放在.h文件中。 4. 不强制要求采用类模板,也不要求采用可视化窗口;要求源程序中有相应注释; 5. 要求测试例子要比较详尽,各种极限情况也要考虑到,测试的输出信息要详细易懂,表明各个功能的执行正确; 6. 要求采用Visual C++ 6.0及以上版本进行调试;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值