[ Java学习 ] 实验 银行业务模拟

题目:




说明:

/*

虽然写了3种方法,但是在博客里,只对我自己最满意的法三做了分析

不过其他两个方法,都贴出了代码和注释,说明了我的思路,以及我的bug出在哪里,每种方法是做了什么改进,以及一些总结和分析

*/

/*法一:中转队列法(很不优)
 * 1.法一是我最初的版本,没做任何的优化,除了把找了很久的bug改对以外,就完全是“硬怼”的方法,非常不推荐这个版本,下次一定不会这么写了
 * (以前听到过一句话:第一眼能够想到的方法,往往时空上的效率都不理想...深以为然,这个方法就是这种类型)
 * 2.不推荐是因为,用数组实现队列,其实可能会假溢出,虽然这题看来没什么影响,但养成这样的习惯,毕竟还是不好的
 * 3.不推荐更因为,其实没必要再写一个栈Q3的,一旦数据规模变大,可以说是在时间、空间上都不优,甚至可以说,时间复杂度和空间复杂度都会极高,简称“双差”也不为过,这样的中转站的写法,一定要极力去避免
 * 4.再说一下为什么会写bug,因为今天数据结构才刚学队列,学的不是很熟,就有些按照自己的感觉来写,写时也没有仔细翻书和查资料求证,导致写队列这个类时,出现了bug,这个bug找了我一个下午,询问了多个师兄和老师,最后发现...如果我数据结构学好一些,这是完全可以避免的...
 * (我发现我对STL中的,list、queue、stack等等,我都只是会用,但是如果真正要我自己写代码,我其实是磕磕碰碰,写不出它们的类代码的,这非常不好,所以我打算把循环队列的代码也写一次,见法三)
 * 就算我数据结构学的不是那么好,本来嘛,如果不是太过自信和依赖自己的记忆,如果翻翻书,也是完全不会错的,然而...就是这样一个bug,因为我太过自信,浪费了一个下午,所以我在深刻反省自己...T^T
 */

class Customer
{
	private int num, type, money;
	private int solve; //记录被处理的次序
	
	public Customer(int n, int t, int m)
	{
		num = n; type = t; money = m;
	}
	
	public void setSolve(int n)
	{
		solve = n;
	}
	
	public void showInfo() //show information
	{
		System.out.println("我是第"+solve+"个被处理的,我的个人信息为:标号为"+num+";业务类型为:"+type+";金额为:"+money);
	}
	
	public int getMoney()
	{
		return money;
	}
	
	public int getType()
	{
		return type;
	}
}

class Queue
{
	private Customer[] Customers;
	private int front, rear; //头尾指针
	private int size, limit; //size是队列的真实大小,limit是数组的限定大小
	//队头指针始终指向队列头元素
	//队尾指针始终指向队列尾元素的下一个位置
	
	public Queue(int n)
	{
		this.Customers = new Customer[n];
		front = rear = size = 0;
		limit = n;
	}
	
	public boolean isEmpty() //判空
	{
		return front >= rear;
		//return size == 0;
	}
	
	public boolean isFull() //判满
	{
		return rear >= limit;
		/*
		 * 这种判满方式可能会导致"假溢出",所以此时不一定是真的栈满,这是用普通数组实现列表的巨大劣势,也是循环队列的引入背景,务必注意!!!
		 * 如果要解决这个弊端,大致有三种方法:
		 * 1.用循环队列实现;(1是最经常采用的方法)
		 * 2.用链表实现队列(我觉得2不错,除了写法可能比较麻烦一些,但是链表尤其适合插入和删除)
		 * 3.队列的大小限制开大一些以避免(3其实不太好,因为不知道究竟需要开多大才足够,度很难把握,而且空间上也可能有较大的浪费)
		 */
	}
	
	public void insert(Customer e) //尾插
	{
		if (isFull())
		{
			throw new RuntimeException("队满,无法尾插");
		}
		Customers[rear++] = e;//进队时,新元素按rear指针位置插入,然后队尾指针增一
		size++;
	}
	
	public Customer getFront() //取队首
	{
		if (isEmpty())
		{
			throw new RuntimeException("队已空,无队首");
		}
		return Customers[front]; //头指针始终指向队列头元素
	}
	
	public Customer getRear() //取队尾
	{
		if (isEmpty())
		{
			throw new RuntimeException("队已空,无队尾");
		}
		return Customers[rear - 1]; //尾指针始终指向队列尾元素的下一个位置
	}
	
	public Customer remove() //删除队首
	{
		if (isEmpty())
		{
			throw new RuntimeException("队空,删除失败");
		}
		return Customers[front++]; //返回被删除的元素
	}
}

class BankManage
{
	static int Amount, Code, Order;
	final int SIZE = 5;
	Queue Q1 = new Queue(SIZE);
	Queue Q2 = new Queue(SIZE);//2个队列	
	
	public void initBank() //初始化静态变量,新用户入队列
	{
		Amount = 1000; Code = Order = 1;
		int info [][] = {{1, 700}, {1, 500}, {1, 200}, {2, 300},{2, 400}};
		
		for (int i = 0; i < info.length; i++)
		{
			Customer c = new Customer(Code++, info[i][0], info[i][1]);
			Q1.insert(c);
		}
	}
	public boolean withdraw(Customer c) //取款
	{
		if (c.getMoney() > Amount)
			return false;
		
		Amount -= c.getMoney();
		c.setSolve(Order++);
		c.showInfo();
		return true;
	}
	
	public void deposit(Customer c) //存款
	{
		Amount += c.getMoney();
		c.setSolve(Order++);
		c.showInfo();
	}
	
	public void solveQ2() //检查第二个队列所有客户,能满足的则满足,不满足的重新排到对尾
	{
		Queue Q3 = new Queue(SIZE); //Q3是一个临时队列,先将仍然不能取款的请求放入Q3,在Q2中所有取款判断完后(判断能否执行,能就取款,不能就插入队尾,但为了能够跳出循环,新建Q3作为中转队列),再将Q3中的所有取款操作,重新入队Q2
		while (!Q2.isEmpty())
		{
			Customer tp = Q2.getFront();
			boolean jud = withdraw(tp);
			if (jud) Q2.remove();
			else
			{
				Q3.insert(tp);
				Q2.remove();
			}
		}
		
		while (!Q3.isEmpty())
		{
			Q2.insert(Q3.getFront());
			Q3.remove();
		}
	}
	
	public void testBank()
	{
		while (!Q1.isEmpty())
		{
			Customer tp= Q1.getFront(); // tp(temp)
			if (tp.getType() == 1) //取款类操作
			{
				boolean jud = withdraw(tp);
				if (jud) Q1.remove();
				else
				{
					Q2.insert(Q1.getFront());
					Q1.remove();
				}
			}
			else //存款类操作
			{
				deposit(tp);
				Q1.remove();
				solveQ2();
			}
			
		}
	}
	
}

public class test 
{
	public static void main(String []args)
	{
		BankManage bank = new BankManage();
		bank.initBank();
		bank.testBank();		
	}
}


/* 法二 用Q2原来的大小作为循环次数
 * 法二所做的改进,是将Q2的大小作为循环次数,省去了Q3的使用
 * 法一的Q3,是作为一个临时队列,先将仍然不能取款的请求放入Q3,在Q2中所有取款判断完后(判断能否执行,能就取款,不能就插入队尾,但为了能够跳出循环,新建Q3作为中转队列),再将Q3中的所有取款操作,重新入队Q2
 * 之前之所以定义Q3,是因为,如果用队列是否为空,来作为结束Q2循环的条件,可能会死循环
 * 但是...我当时思路太闭塞了,没有想到,其实完全可以用Q2最初的大小作为循环次数的,这样就完全没必要定义Q3了
 */

class Customer
{
	private int num, type, money;
	private int solve; //记录被处理的次序
	
	public Customer(int n, int t, int m)
	{
		num = n; type = t; money = m;
	}
	
	public void setSolve(int n)
	{
		solve = n;
	}
	
	public void showInfo() //show information
	{
		System.out.println("我是第"+solve+"个被处理的,我的个人信息为:标号为"+num+";业务类型为:"+type+";金额为:"+money);
	}
	
	public int getMoney()
	{
		return money;
	}
	
	public int getType()
	{
		return type;
	}
}

class Queue
{
	private Customer[] Customers;
	private int front, rear; //头尾指针
	private int size, limit; //size是队列的真实大小,limit是数组的限定大小
	//队头指针始终指向队列头元素
	//队尾指针始终指向队列尾元素的下一个位置
	
	public Queue(int n)
	{
		this.Customers = new Customer[n];
		front = rear = size = 0;
		limit = n;
	}
	
	public boolean isEmpty() //判空
	{
		return front >= rear;
	//	return size == 0;
	}
	
	public boolean isFull() //判满
	{
		return rear >= limit;
		/*
		 * 这种判满方式可能会导致"假溢出",所以此时不一定是真的栈满,这是用普通数组实现列表的巨大劣势,也是循环队列的引入背景,务必注意!!!
		 * 如果要解决这个弊端,大致有三种方法:
		 * 1.用循环队列实现;(1是最经常采用的方法)
		 * 2.用链表实现队列(我觉得2不错,除了写法可能比较麻烦一些,但是链表尤其适合插入和删除)
		 * 3.队列的大小限制开大一些以避免(3其实不太好,因为不知道究竟需要开多大才足够,度很难把握,而且空间上也可能有较大的浪费)
		 */
	}
	
	public void insert(Customer e) //尾插
	{
		if (isFull())
		{
			throw new RuntimeException("队满,无法尾插");
		}
		Customers[rear++] = e;//进队时,新元素按rear指针位置插入,然后队尾指针增一
		size++;
	}
	
	public Customer getFront() //取队首
	{
		return Customers[front]; //头指针始终指向队列头元素
	}
	
	public Customer getRear() //取队尾
	{
		return Customers[rear - 1]; //尾指针始终指向队列尾元素的下一个位置
	}
	
	public Customer remove() //删除队首
	{
		if (isEmpty())
		{
			throw new RuntimeException("队空,删除失败");
		}
		size--;
		return Customers[front++]; //返回被删除的元素
	}
	public int getSize()
	{
		return size;
	}
}

class BankManage
{
	static int Amount, Code, Order;
	final int SIZE = 5;
	Queue Q1 = new Queue(SIZE);
	Queue Q2 = new Queue(SIZE);//2个队列	

	
	public void initBank() //初始化静态变量,新用户入队列
	{
		Amount = 1000; Code = Order = 1;
		int info [][] = {{1, 700}, {1, 500}, {1, 200}, {2, 300},{2, 400}};
		
		for (int i = 0; i < info.length; i++)
		{
			Customer c = new Customer(Code++, info[i][0], info[i][1]);
			Q1.insert(c);
		}
	}
	public boolean withdraw(Customer c) //取款
	{
		if (c.getMoney() > Amount)
			return false;
		
		Amount -= c.getMoney();
		c.setSolve(Order++);
		c.showInfo();
		return true;
	}
	
	public void deposit(Customer c) //存款
	{
		Amount += c.getMoney();
		c.setSolve(Order++);
		c.showInfo();
	}
	
	public void solveQ2() //检查第二个队列所有客户,能满足的则满足,不满足的重新排到对尾
	{
		int times = Q2.getSize(); //循环次数
		for (int i = 0; i < times; i++)
		{
			if (Q2.isEmpty()) return;
			Customer tp = Q2.getFront();
	//		if (tp == null) return;
			boolean jud = withdraw(tp);
			if (!jud) Q2.insert(tp);
			Q2.remove();
		}
		
	}
	
	public void testBank()
	{
		while (!Q1.isEmpty())
		{
			Customer tp= Q1.getFront(); // tp(temp)
	//		if (tp == null) return;
			if (tp.getType() == 1) //取款类操作
			{
				boolean jud = withdraw(tp);
				if (jud) Q1.remove();
				else
				{
					Q2.insert(Q1.getFront());
					Q1.remove();
				}
			}
			else //存款类操作
			{
				deposit(tp);
				Q1.remove();
				if (!Q2.isEmpty()) solveQ2();
			}
		}
	}
}

public class test 
{
	public static void main(String []args)
	{
		BankManage bank = new BankManage();
		bank.initBank();
		bank.testBank();		
	}
}


/* 法三:循环队列的实现
 * 循环队列的判空判满操作:
 * 注意!!!对循环队列而言,无法通过 front == rear 来判断队列是空还是满
 * 一般解决这个问题有3种方法:
 * 1. 设置一个布尔变量专门标记队列空满
 * 2. 转换队满的标准,并不将所有位置都有元素看作是队满...而是,只要它只有一个位置没放元素,我们就把它看作是队满了;
 * 换句话说,调整队满的判断标准
 * 这种方法的实现,一般是少用一个元素的空间。约定入队前,测试尾指针在循环意义下 +1后,是否等于头指针,若相等则认为队满 (rear所指的单元始终为空,循环意义+1一般用取余实现)
 * (法2最常用)
 * 3. 使用一个计数器记录队列种元素的总数,即队列长度
 */

class Customer
{
	private int num, type, money;
	private int solve; //记录被处理的次序
	
	public Customer(int n, int t, int m)
	{
		num = n; type = t; money = m;
	}
	
	public void setSolve(int n)
	{
		solve = n;
	}
	
	public void showInfo() //show information
	{
		System.out.println("我是第"+solve+"个被处理的,我的个人信息为:标号为"+num+";业务类型为:"+type+";金额为:"+money);
	}
	
	public int getMoney()
	{
		return money;
	}
	
	public int getType()
	{
		return type;
	}
}

class Queue
{
	private Customer[] Customers;
	private int front, rear; //头尾指针
	private int size, limit; //size是队列的真实大小,limit是数组的限定大小
	//队头指针始终指向队列头元素
	//队尾指针始终指向队列尾元素的下一个位置
	
	public Queue(int n)
	{
		this.Customers = new Customer[n];
		front = rear = size = 0;
		limit = n;
	}
	
	public boolean isEmpty() //判空
	{
		return front == rear;
	//	return size == 0;
	}
	
	public boolean isFull() //判满
	{
	//	return size == limit;
		return (rear + 1) % limit == front; //我们把仅剩一个位置没放元素看作满,而不是所有元素都装了才叫满;此外,判满利用了“循环意义上+1”这一思想
	}
	
	public void insert(Customer e) //尾插
	{
		if (isFull())
		{
		//	System.out.println("front == "+front+ " and rear == "+rear);
			throw new RuntimeException("队满,无法尾插");
		}
		Customers[rear] = e;//进队时,新元素按rear指针位置插入,然后队尾指针增一
		size++;
		rear = (rear + 1) % limit;
	}
	
	public Customer getFront() //取队首
	{
		return Customers[front]; //头指针始终指向队列头元素
	}
	
	public Customer getRear() //取队尾
	{
		return Customers[rear - 1]; //尾指针始终指向队列尾元素的下一个位置
	}
	
	public Customer remove() //删除队首
	{
		if (isEmpty())
		{
			throw new RuntimeException("队空,删除失败");
		}
		size--;
		return Customers[front++]; //返回被删除的元素
	}
	public int getSize()
	{
		return size;
	}
}

class BankManage
{
	static int Amount, Code, Order;
	final int SIZE = 6; //一定要多设置一个,否则会报错: “Exception in thread "main" java.lang.RuntimeException: 队满,无法尾插”
	//因为循环队列的队满并不是所有元素放满,而是恰好有一个位置没放元素,我们就已经当作是满了,这点尤其注意!!!
	Queue Q1 = new Queue(SIZE);
	Queue Q2 = new Queue(SIZE);//2个队列	

	
	public void initBank() //初始化静态变量,新用户入队列
	{
		Amount = 1000; Code = Order = 1;
		int info [][] = {{1, 700}, {1, 500}, {1, 200}, {2, 300},{2, 400}};
		
		for (int i = 0; i < info.length; i++)
		{
			Customer c = new Customer(Code++, info[i][0], info[i][1]);
			Q1.insert(c);
		}
	}
	public boolean withdraw(Customer c) //取款
	{
		if (c.getMoney() > Amount)
			return false;
		
		Amount -= c.getMoney();
		c.setSolve(Order++);
		c.showInfo();
		return true;
	}
	
	public void deposit(Customer c) //存款
	{
		Amount += c.getMoney();
		c.setSolve(Order++);
		c.showInfo();
	}
	
	public void solveQ2() //检查第二个队列所有客户,能满足的则满足,不满足的重新排到对尾
	{
		int times = Q2.getSize(); //循环次数
		for (int i = 0; i < times; i++)
		{
			if (Q2.isEmpty()) return;
			Customer tp = Q2.getFront();
			boolean jud = withdraw(tp);
			if (!jud) Q2.insert(tp);
			Q2.remove();
		}
		
	}
	
	public void testBank()
	{
		while (!Q1.isEmpty())
		{
			Customer tp= Q1.getFront(); // tp(temp)
			if (tp.getType() == 1) //取款类操作
			{
				boolean jud = withdraw(tp);
				if (jud) Q1.remove();
				else
				{
					Q2.insert(Q1.getFront());
					Q1.remove();
				}
			}
			else //存款类操作
			{
				deposit(tp);
				Q1.remove();
				if (!Q2.isEmpty()) solveQ2();
			}
		}
	}
}

public class test 
{
	public static void main(String []args)
	{
		BankManage bank = new BankManage();
		bank.initBank();
		bank.testBank();		
	}
}

法三的一些详细说明:

Customer

数据:

1.      numtypemoney是题目给的数据,分别是顾客标号、业务类型、业务金额;

2.      solve是我自己加上的类型,记录该顾客是第几个被处理的,这样就能方便地在顾客类中,输出该顾客的所有信息了

 

方法:

1.      构造方法,设定numtypemoney的初始值

2.      setSolve()方法,设定顾客的solve值,也就是他是第几个被处理的客户

3.      showInfo()方法,输出顾客的所有有关信息

4.      getMoney()方法、getType()方法,都是起到获得类中的私有值的作用

 

Queue

数据:

1.      Customer类的数组Customers

2.      frontrear头尾指针

3.      sizelimit,前者是队列实际大小,后者是Customers数组开辟的大小

 

方法:

1.      构造方法,为Customers开辟空间,并设定limit值,初始化frontrearsize0

2.      isEmpty()isFull() 分别用于循环队列的判空和判满,遵循循环队列和判满判空原则

3.      inset()方法用于尾插元素,使得元素入队

4.      getFront()getRear()方法,分别用于取队首元素,和取队尾元素

5.      remove()用于删除队首元素,使其出队

6.      getSize()用于获取队列元素(这个在BankManage类的solveQ2()函数,比较有用)

 

BankManage

数据:

1. 静态变量Amount, Code, Order分别表示银行的剩余金额、顾客总数、已处理顾客数

2. 常量SIZE为队列大小的初值

3. Q1Q2为题目提到的两个队列,排队队列和等待队列

 

方法:

1.      initBank()方法,初始化题目给定的5个客户的有关信息

2.      withdraw(Customer c)deposit(Customer c)方法:分别完成取款和存款操作,存款无须判断,只要修改银行余额、设定处理序号,输出顾客信息即可;对于取款,要先判断是否有足够余额,没有则返回false,如果可以取,同理,修改银行余额、设定处理序号,输出顾客信息

3.      solveQ2()方法,在每次有顾客存款后调用,用于判断第二个队列中是否有可处理的请求,如果有则处理,否则入队尾并删除。这里用到了 Q2getSize()函数,用于获取Q2的元素个数,也就是说,只需要判断这么多次即可

4.      testBank()完成所有的存钱取钱操作

 

test

也就是我编写的测试类,建立一个BankManage类对象bank,完成bank的初始化,并调用它的测试函数,查看有关结果,即可完成有关数据的验证



做该题时查阅过的相关资料:

以下是在完成实验的过程中,曾经查阅过的资料,为了方便日后复习,已经都整理为超链接了,可直接点击

队列(顺序存储)C++模板实现

//因为最近才开始学队列,一开始不是很会写它的类代码(还因此出了bug,并找了一下午);后来痛定思痛,好好搜索学习了一下队列的有关知识,包括假溢出等等。

 

 throwsthrownewRuntimeExceptiontry-catch的区别

//因为在别人的队列实现代码中,看到了throw new Runtime Exception这种用法,于是自己搜索了解了下这个用法

 

StackOverflow: The static method…should be accessed in astatic way

//自己的代码出现这个报错以后,搜到的结果

 

java的二维数组中如何计算行数和列数?

 

 java.lang.NullPointerException空指针异常问题

 

理解Java中的引用传递和值传递

 

JAVA是按值传递还是按引用传递的,试试就知道

 

深入理解Java引用类型





  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我了解了你的问题。这里是一个简单的Java银行业务队列模拟程序: ```java import java.util.LinkedList; import java.util.Queue; import java.util.Random; public class BankQueueSimulation { public static void main(String[] args) { Queue<Customer> queue = new LinkedList<>(); Random random = new Random(); int totalTime = 0; int customerNum = 0; int waitTime = 0; for (int i = 0; i < 10; i++) { int serviceTime = random.nextInt(5) + 1; totalTime += serviceTime; Customer customer = new Customer(i, serviceTime); queue.offer(customer); customerNum++; System.out.println("顾客" + customer.getId() + "进入队列,需要服务时间:" + customer.getServiceTime()); } while (!queue.isEmpty()) { Customer customer = queue.poll(); waitTime += totalTime - customer.getServiceTime(); totalTime += customer.getServiceTime(); System.out.println("顾客" + customer.getId() + "开始服务,等待时间:" + (totalTime - customer.getServiceTime())); } System.out.println("共服务" + customerNum + "个顾客,平均等待时间:" + (double) waitTime / customerNum); } } class Customer { private int id; private int serviceTime; public Customer(int id, int serviceTime) { this.id = id; this.serviceTime = serviceTime; } public int getId() { return id; } public int getServiceTime() { return serviceTime; } } ``` 这个程序模拟了一个银行业务队列,其中有10个顾客需要服务,每个顾客需要的服务时间是1到5秒之间的随机数。程序输出了每个顾客进入队列和开始服务的时间,以及最终服务完所有顾客后的平均等待时间。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值