朋友来福州找我玩,周五晚上和周末都在外面愉快的玩耍,荒废了博客,有点惭愧啊,,,感觉对不起博客。。
今天主要是根据《代码面试指南》中的第一题来认识一下类中方法的使用,更重要一点是能巩固一下数据结构的知识,感觉这是最重要的东西,好久没接触了,原来就菜得不行,现在更菜了(More vegetable !)。
题目要求:
实现一个特殊的栈,在实现栈的基础上,再实现返回栈中最小元素的操作
设计要求:
pop,push的操作复杂度都是O(1),getmin的操作复杂度也必须为O(1)。
思路分析:
这题目,要是放在用C/C++,一个最暴力的方法就是用数组模拟栈,每次getmin使都通过遍历数组, 但是如果用容器或者是现成的类来写的话,一般是不支持对栈进行遍历的,一般只允许对栈顶的元素进行处理,而且这种getmin的复杂度是O(n) (这里的n是栈中的元素个数)。
emmm. 我们可以用两个栈,其中一个栈用来正常存储元素,这个栈我们称之为stackDate, 另一个栈我们用来存储当前stackDate中的最小值,我们称之为stackMin。在这边,用个数学式子就可以清楚的表示出这个getmin的原理是啥了:
假设stackDate当前有 i个元素 ( 1<= i ), 如果现在要push入一个newNum, 那么大伙想一想,push之后,stackDate中的最小值是不是: newMin = min(newNum , stackMin栈顶的元素),说白了也就是,新的最小值将会是前i个数的最小值,newNum这两者中当前较小的数。
伪代码:
push规则:
stackDate.push(newNum);
if( stackDate 为空)
stackMin.push(newNum );
else
{
if( newNum <= stackMin栈顶的元素 )
stackMin.push(newNum); //这说明了前i+1个数的最小值等于第i+1个元素
}
pop规则:
if(stackDate 为空)
cout << "stackDate is 空" << endl;
else
{ //这里stackDate栈顶的元素只可能<=stackMin栈顶的元素
if(stackDate栈顶的元素 > stackMin栈顶的元素 )
stackDate.pop();
else if (stackDate栈顶的元素 == stackMin栈顶的元素)//说明当前stackMin栈顶的元素就是由当前stackDate
// 栈顶的元素贡献的。
stackDate.pop(), stackMin.pop();
}
复杂度分析:
只要按照上诉规则在push和pop的过程始终维护stackMin,stackDate栈中的最小值就始终等于stackMin的栈顶元素。
所以getMin 的操作复杂度就是等于O(1)。
附Java代码:
package code_180;
import java.util.*;
import java.util.Stack;
public class stack_Min {
private Stack<Integer> stackDate ;
private Stack<Integer> stackMin;
stack_Min(){
this.stackDate = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
public void push( int newNum) {
this.stackDate.push(newNum);
if(this.stackMin.empty() )
this.stackMin.push(newNum);
else {
if(newNum <= this.stackMin.peek() )
this.stackMin.push(newNum);
}
}
public void pop() {
if( this.stackDate.isEmpty() == false ) {
if( this.stackDate.peek() == this.stackMin.peek()) {
this.stackMin.pop();
//System.out.println("ok");
}
}
}
public int getMin() {
if(this.stackMin.empty() == false )
return this.stackMin.peek();
else
throw new RuntimeException("Your task is empty");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
stack_Min stackMy = new stack_Min();
int []arrayTest = {3,4,5,1,2,1};
for(int i = 0 ;i < arrayTest.length; i++)
{
stackMy.push( arrayTest[i]);
System.out.println(stackMy.getMin() + "-----------");
}
for(int i = 0 ;i < arrayTest.length; i++)
{
stackMy.pop();
System.out.println(stackMy.getMin() + "+++++++");
}
System.out.println(stackMy.getMin());
}
}
从中学到的一些东西:
1.不添加任何访问权限限定的成员采用的是默认的访问权限。称为default或package,default权限意味着可以被这个类bens
和同一个包的类访问。
2. java中的方法应该保证在任何情况下都有返回值,这是和c/c++ 区别蛮大的一点:
例如:
public class Test{
int example(boolean num){
if(num == true )
return 1;
}
}
在这种情况下是会错误的,因为num== false的情况下,该函数没有返回值。如果不想在
num == false的情况下返回int.
我们可以throw 一个 RuntimeException(),更改如下:
public class Test{
public int example(boolean num){
if(num == true )
return 1;
else
throw new RuntimeException("Your task is empty");
//当然了,返回的内容可以自定义。
}
}
3.错误类型 java.lang.NullPointerException中的一种情况。
new某个类的实例对象时,如果有成员本身就是个类,那么要在构造函数中写new改成员类的语句,不然默认的构造函数是
无法帮你实例出一个成员的类,如上面双栈的题目中的
stack_Min(){
this.stackDate = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
4.静态方法只能直接访问静态变量或者方法,如果要访问非静态变量或者方法的话,应该在静态方法中new 一个该类的对象,然后用 对象名.方法名()来调用。
而非静态方法即可以直接访问静态变量或者方法,又可以直接访问非静态方法或者变量。
5.在java中为什么main方法必须是静态的解释
因为java都是以类组织在一起的,当我们运行某个程序的时候,我们并不知道这个main方法放在那个类中,也不知道是否要产生一个类的对象,所以,为了解决这个问题我们将main方法定义为static的,这样的话当我们在执行一个java代码的时候,编译器就会在类中去寻找静态的main方法,而不产生类的对象,当JVM加载类的时候main方法自然也就被加载了而用来作为程序的入口。