O(1)複雜度來返回棧中最小元素——Java從0開始學習系列之路(2)

朋友來福州找我玩,週五晚上和週末都在外面愉快的玩耍,荒廢了博客,有點慚愧啊,,,感覺對不起博客。。

今天主要是根據《代碼面試指南》中的第一題來認識一下類中方法的使用,更重要一點是能鞏固一下數據結構的知識,感覺這是最重要的東西,好久沒接觸了,原來就菜得不行,現在更菜了(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方法自然也就被加載了而用來作爲程序的入口。



  •  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章