Check Balance with Stack(JAVA实现)

Check Balance with Stack

新开了一门外教课程,Object-oriented Programming(JAVA), 记录一些学习经验,以及部分和c++的区别感悟。本来本次作业不准备记录,但课上老师的一些point个人感觉很重要,故决定记录。
本文主要有三部分:

  1. Check Balance的面向题目实现和面向工程实现的感悟(Java API, Stack<>)
  2. Stack的 ArrayList 实现 – ArrayStack
  3. Stack的 linked-list(ListNode) 实现 – ListStack

Plus: 之前学习数据结构对队列栈等的知识有所掌握,但都是基于c++实现的,且部分过程没有考虑太深。这次学习中学习到了很多。题目要求基本和 leetcode有效的括号 题目一样,在此不再赘述。

1. Check Balance的面向题目实现和面向工程实现的感悟(Java API, Stack<>)

首先是面向题目的实现,很容易对这三类括号做if else判断,并且很容想到结合栈解决。代码如下:

public class CheckBalance {	 

	private String str;
	private int index_num;  // 题目要求记录错误位置,本文可忽略
	
	public CheckBalance(String input) {
		this.str = input;
		this.index_num = 0;
	}
	
	public boolean balanced() {
		Stack<Character> sta = new Stack<Character>();
		for (int i = 0; i < str.length(); i++)
		{
			if (str.charAt(i) == '[' || str.charAt(i) == '{' || str.charAt(i) == '(')
			{
				sta.push(str.charAt(i));
			}
			
			if (str.charAt(i) == ']')
			{
				if (sta.isEmpty() || sta.peek() != '[') { this.index_num = i; return false; }
				else { sta.pop(); }
			}
			
			if (str.charAt(i) == '}')
			{
				if (sta.isEmpty() || sta.peek() != '{') { this.index_num = i; return false; }
				else { sta.pop(); }
			}
			
			if (str.charAt(i) == ')')
			{
				if (sta.isEmpty() || sta.peek() != '(' ) { this.index_num = i; return false; }
				else { sta.pop(); }
			}
		}
		
		if (sta.isEmpty()) { return true; }
		else { this.index_num = str.length(); return false; }
	}

	public int index() {
		return this.index_num;
	}

然后是面向工程的实现,用一个字符串记录保存括号的情况,这样以后维护起来容易(比如增加检查 <> 需求,只需要修改一小部分,在数组里添加这对括号即可);并且这样相当于完成了对检查括号平衡这一类问题的解决逻辑,相对于只是解决简单的三类括号的平衡问题,更加符合需求。这里借用老师的话: Good code is dynamic code. 代码如下:
Plus. 再增加一些成员函数,封装类似于sta.peek() != OPENINGS.charAt(temp)这样的语句,使得代码的可读性会更好,但为了与之前的对比,本文不做进一步逻辑抽象。

public class CheckBalance {
	
	private static final String OPENINGS = "([{";
	private static final String CLOSINGS = ")]}";
	private String str;
	private int index_num;

	public CheckBalance(String input) {
		this.str = input;
		this.index_num = 0;
	}

	public boolean balanced() {
		Stack<Character> sta = new Stack<Character>();
		for (int i = 0; i < str.length(); i++)
		{
			if (OPENINGS.indexOf(str.charAt(i)) > -1)
			{
				sta.push(str.charAt(i));
			}
			
			if (CLOSINGS.indexOf(str.charAt(i)) > -1)
			{
				int temp = CLOSINGS.indexOf(str.charAt(i));
				if (sta.isEmpty() || sta.peek() != OPENINGS.charAt(temp) ) { this.index_num = i; return false; }
				else { sta.pop(); }
			}
		}
		
		if (sta.isEmpty()) { return true; }
		else { this.index_num = str.length(); return false; }
	}
	
	public int index() {
		return this.index_num;
	}
}

2. Stack的ArrayList实现 – ArrayStack

用ArrayList(Java API)实现Stack。本部分无特别之处。个人感觉与cpp中用vector实现stack区别不大。需要注意的是,创建ArrayList时指定类型必须是对象类型,它不能是原始类型。(int 改为 Integer,即primitive types to wrapper classes)

3. Stack的ListStack实现 – ListStack

这里的interesting point比较多。
1.首先,与cpp相同的是,java new出来的也是动态内存,但是,cpp需要手动delete释放(即cpp内存管理知识,堆区等等),不然会内存泄漏,但是java自带垃圾回收机制,所以不需要手动释放空间。(这里具体的垃圾回收机制目前没学到,JVM知识之后若决定继续学习Java再补充)
2.第二,Java的内存回收机制,导致在链表情况下,如给定一个链表和头指针(java不存在指针,即指向第一个节点的一个头,如ListNode head = new ListNode(val);),若调用head = head.next; 随着调用同时也在丢失链表(因为没有东西再指向那块new出来的动态内存,会直接被回收)。所以,要ListNode current = head; curren = current.next;这样改变cur不会损坏链表。这点与cpp不同,因为cpp中new出来在堆区的动态内存,不delete是不会释放的。
3.第三,这点无关cpp还是java,算是用链表实现栈这种数据结构,进行设计时需要考虑的。因为栈是后入先出的结构,即后push的先pop,所以考虑这种情况下,不应该push or pop在链表尾(即push的数据不应该添加到链表尾,pop的数据不应该去pop尾丢)。原因:首先栈是一头开口的数据结构,若每次在尾部添加,需要用一个while(cur.next != null)循环把cur指到尾节点,这样使得当已存链表已经数据量很大时,push和pop的时间复杂度都为O(n),很不合理。所以用链表实现栈时,我们应该在链表头push,在链表头pop,这样无论链表数据多大,再进行push和pop的时间复杂度都是常数时间。

4.总结

1.代码除了实现功能需求,在工程实现时,要多考虑代码的易维护性,想一想有没有办法使得代码更容易维护,若增加需求对源代码的改动是否很大。同时,要学会在编写代码时进行整体架构的逻辑抽象封装,这样会增加代码的易读性
2. 使用ArrayList等API,注意< primitive types to wrapper classes >
3. cpp的手动内存管理与java的垃圾回收机制的区别,导致某些场景java需注意是否已经改变了动态内存数据。
4. 除了一些具体的场景题目,设计各种数据结构或其他结构时,要注意算法的时间复杂度

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值