朋友來福州找我玩,週五晚上和週末都在外面愉快的玩耍,荒廢了博客,有點慚愧啊,,,感覺對不起博客。。
今天主要是根據《代碼面試指南》中的第一題來認識一下類中方法的使用,更重要一點是能鞏固一下數據結構的知識,感覺這是最重要的東西,好久沒接觸了,原來就菜得不行,現在更菜了(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方法自然也就被加載了而用來作爲程序的入口。