演算法題摘錄三

2021-09-07 09:24:22 字數 3602 閱讀 7256

1:判斷給定陣列是不是二叉搜尋樹的前/後序遍歷序列

二叉搜尋樹的特點是:左子樹的值《根結點值《右子樹的值。而前/後序遍歷序列的首/尾數是根結點,依據根結點可以把陣列其餘部分劃分為左子樹、右子樹,然後根據左右子樹序列又可以遞迴地確定父結點並劃分子樹......如果能遞迴遍歷完整個陣列則說明合法,否則非法。

下面是我實現的判斷後序遍歷序列的**:

public

boolean issquenceofbst(int nums,int start,int

end)

//序列的end就是當前層的根結點

int curr_root=end;

//由根結點劃分序列得到左子樹的結束位

int leftend=0;

for (int i = start; i )

}//由左子樹結束位+1得到右子樹開始位

int rightstart=leftend+1;

//如果左子樹序列不為空,則遞迴左子樹

boolean isleftofbst=true

;

if(leftend>0)

//同理,先判斷右子樹序列是否存在,是則遞迴右子樹

//這裡要注意右子樹是rightstart到end-1!

boolean isrightofbst=true

;

if(rightstart)

return isleftofbst&&isrightofbst;

}

2:給定一棵二叉樹,判斷給的序列是否對應二叉樹的前/後/中序遍歷序列

傳統解法:先按前/後/中序遍歷整個樹,把每個結點的值儲存到乙個陣列中,然後把結果陣列與所給陣列一一比較即可。這是由樹求陣列去判陣列。

遞迴解法:比如我們判斷後序遍歷序列的合法性:我們從二叉樹的根節點開始遍歷,curr_root記錄當時樹的根節點。由curr_root與當前序列最後乙個值比較判斷當前層序列的合法性;然後由curr_root—>left值劃分遍歷序列得到左子樹序列的最後一位leftend,由rightstart=leftend+1得到右子樹序列的開始下標。然後遞迴左右子樹,比較子樹的根結點與所給序列的末尾值比較,最後返回左右子樹的遍歷結果即可。遞迴邊界是:curr_root的子樹為空。或者當前樹的根不等於當前序列末位值。

3:列印二叉樹中結點值和為給定值的所有路徑

從根節點開始處理每乙個結點,把結點入棧,並統計當前路徑耗散值。如果子節點非空,則遞迴處理子節點。當前結點為葉結點,則比較路徑耗散值與給定和是否相等,是則列印棧中元素(路徑),否則不列印;然後回溯:彈出當前結點。

vectorpath=new vector();//由於列印路徑時需要從頭列印,所以這裡用vector替代了stack。因為vector可以從頭遍歷,又包含了棧的丟擲**者的功能

public

void printpath(mytreenode currroot,vectorpath,int currsum,int

total)

//計算當前結點耗散值

currsum+=currroot.val;

//如果當前結點是葉結點並且耗散值等於所求路徑和,則列印這條路徑

if(currsum==total && currroot.leftnode==null && currroot.rightnode==null

) }

//如果路徑不是葉結點,並且左子結點存在,則遞迴左子結點

if(currroot.leftnode!=null

)

//如果右子結點存在,遞迴右子結點

if(currroot.rightnode!=null

)

//回溯:把路徑上當前點刪除

path.remove(path.size()-1);

}

4:複雜鍊錶的複製

傳統解法:首先遍歷一次原鍊錶,用next指標把新鍊錶建立起來;然後再從頭到尾遍歷原煉表和新鍊錶,把指向任意結點的指標一一賦值給新鍊錶對應結點。複雜度o(n^2)

優化解法:既然乙個結點對應乙個任意指標,那麼我們在第一次遍歷原鍊錶建立新煉表時把每個結點對應的任意指標存到乙個map中,然後再遍歷新煉表為每個結點通過map.get(當前結點)獲取對應的任意指標進行賦值。

5:把一顆二叉搜尋樹轉換成排好序的雙向鍊錶,要求只能改變樹的指標,不能新建任何結點

由二叉搜尋樹的中序遍歷是遞增序列可知,要把bst轉換成排好序的雙向鍊錶其實就是中序遍歷bst並修改指標的過程。

觀察可得:如果乙個結點n的左右子樹非空,那麼n的前指標指向左子樹排序後序列的尾結點,n的後指標執行右子樹排列後序列的頭結點。而對於n的左右子樹序列的得出,毫無疑問就是遞迴。

6:求乙個字串所有字元的全排列

首先,求出第乙個字元的所有可能情況:其實就是把字串的第乙個字元依次和後面的字元交換;

然後,在第乙個字元確定的情況下,把第二個字元依次和後面的字元交換;其實就是遞迴:把從第二個字元起的字串傳進遞迴函式進行處理;

......直到遞迴到了每種情況下的字串的最後乙個字元,結束遞迴,輸出此時的字串。回溯:把當前步的交換復位。

public

void permutation(char chars,int

begin)

//如果以及遞迴到最後乙個字元了,則得到乙個排列情況,進行輸出

if(begin==chars.length-1)

else

}

}

7:找出陣列**現次數超過陣列長度一半的元素

map解法:這種對映關係的題,第一時間想到map——遍歷整個陣列,把數字與其出現次數關聯起來。然後把map中value值最大的key輸出即可。

優化解法:我們可以設定乙個活動開關:每當當前數與儲存數相同,則開關值+1;否則開關值-1;檢查開關值,如果開關值為0,則把當前數設定為儲存數,並把開關值置1,統計新儲存數的出現情況。由於出現次數超過陣列一半,那麼遍歷完後開關值大於等於1時對應的儲存數就是陣列**現次數大於一半的元素。

8:輸入乙個陣列,找出最小的k個數

傳統解法:排序,輸出前k個數。複雜度o(nlogn)

優化解法:最大堆的使用。首先用輸入的陣列的前k個數建立其乙個有k個結點的最大堆。然後每輸入乙個數,先與堆頭比較,若小於堆頭,則把堆頭的值替換成新輸入的值,然後對堆頭進行下沉操作使結點處於合適位置從而更新堆頭最大值。在陣列輸入完後,得到的k結點最大堆就是陣列最小的k個數。

9:求連續子陣列最大和

雖然動態規劃常用遞迴的形式去描述,但是最終我們都會用迴圈來編碼。

public

int max_son(int nums,int

n)else

//更新最大連續和陣列中的最大值

currmax=currmax>res[i]?currmax:res[i];

}//返回最大的連續和

return

currmax;

}

10:從1數到n,求數字1出現的次數(在任意位上)

傳統解法:定義乙個函式專門用於統計乙個數n的各位上1的個數。然後迴圈遍歷 i:1~n ,count+=sumofone(i),最後count就是所求。缺點是容易超時。

優化:找規律來求。

演算法題摘錄六

1 正規表示式匹配 出現0次 pattern curr 2 與str curr 比較 出現1次 pattern curr 2 與str curr 1 比較 出現多次 pattern curr 與str curr 1 比較 這三種情況都可能出現,遞迴這三種情況取或即可。而每一位pattern匹配str...

演算法題C (三)

本部落格目錄 給定乙個單向鍊錶的頭節點head,節點的值型別是整型,再給定乙個整數p。實現乙個調整鍊錶的函式,將鍊錶調整為左部分都是值小於 p的節點,中間部分都是值等於p的節點,右部分都是值大於 p的節點。空間複雜度為o n 的演算法 建立輔助陣列,先在陣列上排序,之後連線 空間複雜度o n lis...

日常演算法題(三)

1.對稱二叉樹 給定乙個二叉樹,檢查它是否是映象對稱的。例如,二叉樹 1,2,2,3,4,4,3 是對稱的。但是下面這個 1,2,2,null,3,null,3 則不是映象對稱的 想法 直接遞迴遍歷判斷是否相等即可 public boolean issymmetric treenode root p...