劍指offer51到60題總覽:
- 構(gòu)建乘積數(shù)組
- 正則表達(dá)式匹配
- 表示數(shù)值的字符串
- 字符流中第一個(gè)不重復(fù)的字符
- 鏈表中環(huán)的入口結(jié)點(diǎn)
- 刪除鏈表中重復(fù)的結(jié)點(diǎn)
- 二叉樹的下一個(gè)結(jié)點(diǎn)
- 對稱的二叉樹
- 按之字形打印二叉樹
- 把二叉樹打印成多行
51功戚、構(gòu)建乘積數(shù)組
題目描述
給定一個(gè)數(shù)組A[0,1,...,n-1],請構(gòu)建一個(gè)數(shù)組B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]娶眷。不能使用除法。
解題思路
兩次遍歷啸臀,第一次計(jì)算下三角届宠,第二次計(jì)算上三角。比如計(jì)算下三角,設(shè)置一個(gè)temp豌注,temp=A[0]伤塌,將temp賦值給B[1];temp=temp*A[1]幌羞,將temp賦值給B[2]寸谜。
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int[] B = new int[A.length];
if(A.length == 0){return B;}
if(A.length == 1){
B[0] = 1;
return B;
}
B[0] = 1;//先將B[0]賦值為1,計(jì)算下三角時(shí)用不到B[0]
int temp = 1;//用來保存當(dāng)前乘積
for(int i=1; i<A.length; i++){//計(jì)算下三角
temp = temp * A[i-1];
B[i] = temp;
}
temp = 1;//計(jì)算上三角前將temp置為0
for(int i=A.length-2; i>=0; i--){//計(jì)算上三角
temp = temp * A[i+1];
B[i] *= temp;//B[i]應(yīng)該在計(jì)算完下三角的基礎(chǔ)上乘temp
}
return B;
}
}
52属桦、正則表達(dá)式匹配
題目描述
請實(shí)現(xiàn)一個(gè)函數(shù)用來匹配包括'.'和'*'的正則表達(dá)式熊痴。模式中的字符'.'表示任意一個(gè)字符,而'*'表示它前面的字符可以出現(xiàn)任意次(包含0次)聂宾。 在本題中果善,匹配是指字符串的所有字符匹配整個(gè)模式。例如系谐,字符串"aaa"與模式"a.a"和"ab*ac*a"匹配巾陕,但是與"aa.a"和"ab*a"均不匹配。
解題思路
考慮各種情況:
- 如果strIndex和patternIndex都到尾了纪他,則匹配成功鄙煤。
- 如果strIndex沒有到尾,但patternIndex已經(jīng)到尾了茶袒,str還有最后一部分沒有匹配完梯刚,則匹配失敗。
- 如果遇到pattern當(dāng)前字符的下一個(gè)字符是*薪寓,有幾種情況:
- pattern當(dāng)前字符是.亡资,則可視作匹配0個(gè)字符,strIndex+0向叉,patternIndex+2锥腻,繼續(xù)遞歸;可視作匹配1個(gè)字符母谎,strIndex+1瘦黑,patternIndex+2,繼續(xù)遞歸奇唤;可視作暫時(shí)匹配1個(gè)字符供璧,patternIndex不移動(dòng),后面可再選擇匹配0個(gè)或1個(gè)或再暫時(shí)匹配一個(gè)字符冻记,strIndex+1,patternIndex+0来惧,繼續(xù)遞歸冗栗。
- pattern當(dāng)前字符不是.而是一個(gè)字母或者其他,則要看pattern當(dāng)前字符和str當(dāng)前字符是否一樣:不一樣,則匹配失斢缇印钠至;一樣,則和pattern當(dāng)前字符是.一樣胎源,可以選擇匹配0個(gè)或1個(gè)或暫時(shí)匹配一個(gè)字符棉钧,繼續(xù)遞歸。
3.1
public class Solution {
public boolean match_1(char[] str, char[] pattern) {
if (str == null || pattern == null) {
return false;
}
int strIndex = 0;
int patternIndex = 0;
return matchCore(str, strIndex, pattern, patternIndex);
}
public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
//有效性檢驗(yàn):str到尾涕蚤,pattern到尾宪卿,匹配成功
if (strIndex == str.length && patternIndex == pattern.length) {
return true;
}
//pattern先到尾,匹配失敗
if (strIndex != str.length && patternIndex == pattern.length) {
return false;
}
//模式第2個(gè)是*万栅,則分兩種情況佑钾,一種是*前面是.一種是*是一般字符。
//如果是一般字符烦粒,則又可分為pattern當(dāng)前字符與str當(dāng)前字符匹配成功和不匹配兩種情況
//而*前面是.和pattern當(dāng)前字符與str當(dāng)前字符匹配成功的情況可以寫到一起休溶。
if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex])
|| (strIndex != str.length && pattern[patternIndex] == '.')) {
//*前面是.,或*前面的一般字符是與str的當(dāng)前字符匹配成功
return matchCore(str, strIndex, pattern, patternIndex + 2) //模式后移2扰她,視為x*匹配0個(gè)字符
|| matchCore(str, strIndex + 1, pattern, patternIndex + 2) //視為模式匹配1個(gè)字符兽掰,模式向后移動(dòng)兩位
|| matchCore(str, strIndex + 1, pattern, patternIndex); //當(dāng)前只匹配1個(gè),模式?jīng)]有移動(dòng)徒役,再遞歸可匹配str中的下一個(gè)或匹配0個(gè)
} else {
//*前面的字符與當(dāng)前字符不匹配孽尽,模式向后移動(dòng)兩位
return matchCore(str, strIndex, pattern, patternIndex + 2);
}
}
//模式第2個(gè)不是*,且字符串第1個(gè)跟模式第1個(gè)匹配廉涕,則都后移1位泻云,否則直接返回false
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex])
|| (strIndex != str.length && pattern[patternIndex] == '.')) {
return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
}
return false;
}
}
53、表示數(shù)值的字符串
題目描述
請實(shí)現(xiàn)一個(gè)函數(shù)用來判斷字符串是否表示數(shù)值(包括整數(shù)和小數(shù))狐蜕。例如宠纯,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示數(shù)值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是层释。
解題思路
用正則表達(dá)式婆瓜。
public class Solution {
public boolean isNumeric(char[] str) {
String s = String.valueOf(str);
return s.matches("[\\+-]?[0-9]*(\\.[0-9]*)?([eE][\\+-]?[0-9]+)?");
}
}
54、字符流中第一個(gè)不重復(fù)的字符
題目描述
請實(shí)現(xiàn)一個(gè)函數(shù)用來找出字符流中第一個(gè)只出現(xiàn)一次的字符贡羔。例如廉白,當(dāng)從字符流中只讀出前兩個(gè)字符"go"時(shí),第一個(gè)只出現(xiàn)一次的字符是"g"乖寒。當(dāng)從該字符流中讀出前六個(gè)字符“google"時(shí)再愈,第一個(gè)只出現(xiàn)一次的字符是"l"膳凝。
輸出描述:
如果當(dāng)前字符流沒有存在出現(xiàn)一次的字符,返回#字符。
public class Solution {
int arr[] = new int[256];//建立所有可能字符的數(shù)組
int index;
public Solution(){
for(int i=0; i<arr.length; i++){//將數(shù)組所有值賦值為-1
arr[i] = -1;
}
index = 0;//index為當(dāng)前插入的字符的個(gè)數(shù)
}
//Insert one char from stringstream
public void Insert(char ch)//因?yàn)橐l繁插入,所以不建議在insert方法中設(shè)置循環(huán)
{
if(arr[ch] == -1){//-1表示之前沒有出現(xiàn)過,現(xiàn)在出現(xiàn)了
arr[ch] = index;//將對應(yīng)字符的位賦值為字符流的index
}else if(arr[ch] >= 0){//>=0表示之前出現(xiàn)過一次,而且是唯一一次,而此時(shí)出現(xiàn)了第二次
arr[ch] = -2;//出現(xiàn)了第二次谆膳,將對應(yīng)字符的位賦值為-2,>=0的情況只保存當(dāng)前只出現(xiàn)了一次撮躁,并記錄在字符流中的index
}
index++;//插入了一個(gè)字符漱病,index+1
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
int result_index = index;//給返回結(jié)果一個(gè)初始值
for(int i=0; i<arr.length; i++){//遍歷數(shù)組
if(arr[i] >= 0){//遇到一個(gè)只出現(xiàn)一次的字符
if(result_index == index){result_index = i;}//如果是第一個(gè)只出現(xiàn)一次的字符,則將result_index更新
else{
if(arr[i] < arr[result_index]){//找到了新的只出現(xiàn)一次的字符把曼,且其保存的字符流的位置更靠前杨帽,更新result_index。
result_index = i;
}
}
}
}
if(result_index == index){return '#';}//如果整個(gè)遍歷沒有找到只出現(xiàn)一次的字符祝迂,返回#
else{return (char)result_index;}//將int類型的下標(biāo)轉(zhuǎn)化為char類型
}
}
55睦尽、鏈表中環(huán)的入口結(jié)點(diǎn)
題目描述
給一個(gè)鏈表,若其中包含環(huán)型雳,請找出該鏈表的環(huán)的入口結(jié)點(diǎn)当凡,否則,輸出null纠俭。
解題思路
方法很多沿量,記錄兩種,都是快慢指針的思想冤荆。設(shè)置一個(gè)慢指針slow朴则,一次走一步;設(shè)置一個(gè)快指針fast钓简,一次走兩步乌妒。如果存在環(huán),則快慢指針一定會(huì)在環(huán)內(nèi)相遇外邓。如果不存在環(huán)撤蚊,則fast會(huì)率先走到鏈表尾,則返回null损话。在環(huán)內(nèi)相遇之后侦啸,有兩種方法尋找入環(huán)結(jié)點(diǎn):
- 從快慢指針相遇的結(jié)點(diǎn)開始,fast結(jié)點(diǎn)不走丧枪,slow再走一圈光涂,得到環(huán)內(nèi)包含的結(jié)點(diǎn)數(shù)c。然后令p1=pHead拧烦,p1先走c步(此時(shí)已經(jīng)知道有環(huán)忘闻,不用擔(dān)心會(huì)走到空結(jié)點(diǎn)),令p2=pHead恋博,然后p1和p2一起走服赎,每次都走一步(p1始終超前p2 c步)葵蒂。則他們第一次相等的時(shí)候指向的節(jié)點(diǎn),就是入環(huán)結(jié)點(diǎn)重虑。
- 從快慢指針相遇的結(jié)點(diǎn)開始,我們有以下推導(dǎo):
參考鏈接:https://www.nowcoder.com/questionTerminal/253d2c59ec3e4bc68da16833f79a38e4
假設(shè)x為環(huán)前面的路程(黑色路程)秦士,a為環(huán)入口到相遇點(diǎn)的路程(藍(lán)色路程缺厉,假設(shè)順時(shí)針走), c為環(huán)的長度(藍(lán)色+橙色路程)
當(dāng)快慢指針相遇的時(shí)候:
此時(shí)慢指針走的路程為隧土,為慢指針在環(huán)內(nèi)轉(zhuǎn)的圈數(shù)提针,這個(gè)圈數(shù)并不重要。
快指針走的路程為曹傀,為快指針在環(huán)內(nèi)轉(zhuǎn)的圈數(shù)辐脖。
我們設(shè)置快指針一次走兩步,慢指針一次走一步皆愉,則相遇時(shí)他們的路程有如下關(guān)系:
則有:
從而可以推導(dǎo)出:
上式第二步中嗜价,我們提出一個(gè),則可以表示相遇點(diǎn)后幕庐,環(huán)后面部分的路程久锥。(橙色路程)
所以,我們可以讓一個(gè)指針p從起點(diǎn)A開始走异剥,讓slow指針從相遇點(diǎn)B開始繼續(xù)往后走瑟由,p和slow指針?biāo)俣纫粯樱看沃蛔咭徊皆┦伲瑒t當(dāng)指針p走到環(huán)入口點(diǎn)的時(shí)候(此時(shí)剛好走了x)slow指針也一定剛好到達(dá)環(huán)入口點(diǎn)歹苦。p和slow恰好相遇在環(huán)的入口點(diǎn)。返回p或slow即可督怜。這種方法需要一點(diǎn)推導(dǎo)所以可能想不到殴瘦,但是推出來之后寫代碼就變得更簡單了。
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead==null || pHead.next==null || pHead.next.next==null){return null;}
ListNode slow = pHead.next;//slow先走一步
ListNode fast = pHead.next.next;//fast先走兩步
while(slow != fast){//當(dāng)還沒有相遇的時(shí)候
if(fast.next!=null && fast.next.next!=null){//沒有走到鏈表尾
slow = slow.next;//slow和fast繼續(xù)走
fast = fast.next.next;
}else{//如果走到了鏈表尾亮蛔,則一定沒有環(huán)痴施,直接返回null
return null;
}
}//循環(huán)出來了一定是slow和fast相遇了
ListNode p = pHead;//p指向鏈表頭,與slow一起走究流,p和slow一次都只走一步
while(p!=slow){//p和slow一起走辣吃,此時(shí)有環(huán)則他們一定會(huì)在入環(huán)結(jié)點(diǎn)相遇。
p = p.next;
slow = slow.next;
}
return p;
}
}
56芬探、刪除鏈表中重復(fù)的結(jié)點(diǎn)
題目描述
在一個(gè)排序的鏈表中神得,存在重復(fù)的結(jié)點(diǎn),請刪除該鏈表中重復(fù)的結(jié)點(diǎn)偷仿,重復(fù)的結(jié)點(diǎn)不保留哩簿,返回鏈表頭指針宵蕉。 例如,鏈表1->2->3->3->4->4->5 處理后為 1->2->5
解題思路
如果鏈表最前面就有重復(fù)的节榜,則將鏈表頭前面的重復(fù)結(jié)點(diǎn)全部跳過羡玛,讓pHead指向第一個(gè)不重復(fù)的結(jié)點(diǎn),返回新的頭結(jié)點(diǎn)宗苍。如果鏈表最前面不是重復(fù)的稼稿,則找到最后一個(gè)不重復(fù)結(jié)點(diǎn),遞歸刪除后面的重復(fù)結(jié)點(diǎn)讳窟。這里不加速找到最后一個(gè)不重復(fù)的結(jié)點(diǎn)也可以让歼,如果頭結(jié)點(diǎn)不是重復(fù)結(jié)點(diǎn),則遞歸頭結(jié)點(diǎn)后面的結(jié)點(diǎn)丽啡,直到遞歸到頭結(jié)點(diǎn)就是重復(fù)結(jié)點(diǎn)谋右。
public class Solution {
public ListNode deleteDuplication(ListNode pHead){
if(pHead == null){return null;}
if(pHead.next == null){return pHead;}
ListNode p = pHead;
if(p.val == p.next.val){//如果p點(diǎn)與p后面的結(jié)點(diǎn)重復(fù)
while(p.next!=null && p.val == p.next.val){//跳過所有的重復(fù)結(jié)點(diǎn),直到p結(jié)點(diǎn)不重復(fù)补箍,或者p為最后一個(gè)結(jié)點(diǎn)改执。
p = p.next;//如果p為重復(fù)結(jié)點(diǎn),且p后面還有結(jié)點(diǎn)馏予,則p后移一位
}
pHead = deleteDuplication(p.next);//如果是p和p后面的結(jié)點(diǎn)不重復(fù)了天梧,則遞歸p.next;或者p為最后一個(gè)結(jié)點(diǎn)了霞丧,但是p結(jié)點(diǎn)是重復(fù)結(jié)點(diǎn)呢岗,依舊遞歸p.next,這時(shí)遞歸返回null
}else{//如果p不是重復(fù)結(jié)點(diǎn)蛹尝,則看p的后面的結(jié)點(diǎn)是不是重復(fù)結(jié)點(diǎn)
while(p.next.next!=null && p.next.val != p.next.next.val){//向后找重復(fù)結(jié)點(diǎn)后豫,p指向最后一個(gè)不重復(fù)結(jié)點(diǎn)
p = p.next;
}//這里的循環(huán)不要也可以,直接p.next = deleteDuplication(p.next);也行
p.next = deleteDuplication(p.next);//遞歸刪除p結(jié)點(diǎn)后面的重復(fù)結(jié)點(diǎn)突那,并將刪除后返回的頭結(jié)點(diǎn)連接到p的后面
}
return pHead;
}
}
57挫酿、二叉樹的下一個(gè)結(jié)點(diǎn)
題目描述
給定一個(gè)二叉樹和其中的一個(gè)結(jié)點(diǎn),請找出中序遍歷順序的下一個(gè)結(jié)點(diǎn)并且返回愕难。注意早龟,樹中的結(jié)點(diǎn)不僅包含左右子結(jié)點(diǎn),同時(shí)包含指向父結(jié)點(diǎn)的指針猫缭。
解題思路
畫圖葱弟,可知各種情況的下一個(gè)結(jié)點(diǎn)。
- 如果pNode為空猜丹,則返回null芝加。
- 如果pNode不為空,判斷pNode是否有右子樹射窒,如果有右子樹藏杖,則返回右子樹中最左邊的結(jié)點(diǎn)将塑。如果沒有右子樹,則向上找pNode的父結(jié)點(diǎn)蝌麸。
- 如果沒有父結(jié)點(diǎn)点寥,則pNode是根節(jié)點(diǎn),中序遍歷已經(jīng)遍歷完祥楣,返回null开财;如果有父結(jié)點(diǎn),且如果它是父結(jié)點(diǎn)的左結(jié)點(diǎn)误褪,直接返回pNode的父結(jié)點(diǎn);如果有父結(jié)點(diǎn)碾褂,且如果它是父結(jié)點(diǎn)的右結(jié)點(diǎn)兽间,則要看pNode的祖父結(jié)點(diǎn)。
- 如果沒有祖父結(jié)點(diǎn)正塌,則中序遍歷已經(jīng)遍歷完嘀略,返回null;如果pNode的父結(jié)點(diǎn)是祖父結(jié)點(diǎn)的左結(jié)點(diǎn)乓诽,則返回祖父結(jié)點(diǎn)帜羊;如果有父結(jié)點(diǎn),且如果pNode的父結(jié)點(diǎn)是祖父結(jié)點(diǎn)的右結(jié)點(diǎn)鸠天,則中序遍歷已經(jīng)遍歷完讼育,返回null。
public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode){
if(pNode == null){return null;}//如果結(jié)點(diǎn)為空稠集,則返回null
if(pNode.right!=null){//如果結(jié)點(diǎn)有右子樹奶段,則找到子樹的最左的結(jié)點(diǎn)
pNode = pNode.right;
while(pNode.left!=null){//找到最左的結(jié)點(diǎn)
pNode = pNode.left;
}
return pNode;
}else{//如果改結(jié)點(diǎn)沒有右子樹,就只能往上找其父結(jié)點(diǎn)
if(pNode.next!=null && pNode.next.left==pNode){//有父結(jié)點(diǎn)剥纷,且是父結(jié)點(diǎn)的左結(jié)點(diǎn)痹籍,則直接返回pNode的父結(jié)點(diǎn)
return pNode.next;
}else if(pNode.next!=null && pNode.next.right==pNode){//有父結(jié)點(diǎn),且是父極點(diǎn)的右結(jié)點(diǎn)
if(pNode.next.next!=null && pNode.next.next.left==pNode.next){
//如果祖父結(jié)點(diǎn)存在晦鞋,且其父結(jié)點(diǎn)是祖父結(jié)點(diǎn)的左結(jié)點(diǎn)蹲缠,則返回其祖父結(jié)點(diǎn)
return pNode.next.next;
}else if(pNode.next.next!=null && pNode.next.next.right==pNode.next){
//如果祖父結(jié)點(diǎn)存在,且其父結(jié)點(diǎn)是祖父結(jié)點(diǎn)的右結(jié)點(diǎn)悠垛,則返回null线定,中序遍歷后面沒有結(jié)點(diǎn)了
return null;
}else{//如果祖父結(jié)點(diǎn)不存在,也就是其父結(jié)點(diǎn)就是根結(jié)點(diǎn)鼎文,中序遍歷后面已經(jīng)沒有結(jié)點(diǎn)了渔肩,返回null
return null;
}
}else{//如果他沒有父結(jié)點(diǎn),則它是根節(jié)點(diǎn)
return null;//這種情況就是根節(jié)點(diǎn)只有左子樹拇惋,右邊全為空周偎,根節(jié)點(diǎn)就是最后一個(gè)結(jié)點(diǎn),返回null
}
}
}
}
58蓉坎、對稱的二叉樹
題目描述
請實(shí)現(xiàn)一個(gè)函數(shù),用來判斷一顆二叉樹是不是對稱的蛉艾。注意钳踊,如果一個(gè)二叉樹同此二叉樹的鏡像是同樣的,定義其為對稱的勿侯。
解題思路
根節(jié)點(diǎn)只有一個(gè)拓瞪,不用管其對稱性,拋開根節(jié)點(diǎn)助琐,看根節(jié)點(diǎn)的左右子樹祭埂。
如果左右兩顆子樹是鏡像的,則這兩顆子樹的根節(jié)點(diǎn)的值是一樣的兵钮,且其left.left與right.right是鏡像的蛆橡;其left.right與right.left是鏡像的。
public class Solution {
boolean isSymmetrical(TreeNode pRoot){
if(pRoot==null){return true;}//如果根節(jié)點(diǎn)為空掘譬,則返回true
return isSym(pRoot.left, pRoot.right);//看根節(jié)點(diǎn)的左右子樹是否是鏡像的
}
boolean isSym(TreeNode left, TreeNode right){
if(left==null && right==null){return true;}//如果兩顆樹都為空泰演,則返回true
if(left!=null && right==null){return false;}//如果左不空,右空葱轩,則不是鏡像的
if(left==null && right!=null){return false;}//如果左空睦焕,右不空,則不是鏡像的
if(left.val != right.val){return false;}//如果左右都不空酿箭,但是左右子樹的根節(jié)點(diǎn)的值不一樣复亏,則不是鏡像的
//左右都不空,則查看left.left與right.right是否鏡像缭嫡,其left.right與right.left是否鏡像
return isSym(left.left, right.right) && isSym(left.right, right.left);
}
}
59缔御、按之字形打印二叉樹
題目描述
請實(shí)現(xiàn)一個(gè)函數(shù)按照之字形打印二叉樹,即第一行按照從左到右的順序打印妇蛀,第二層按照從右至左的順序打印耕突,第三行按照從左到右的順序打印,其他行以此類推评架。
解題思路
設(shè)置一個(gè)boolean left2right來判斷是應(yīng)該從左往右還是從右往左眷茁。
有一種方法是用兩個(gè)棧,一個(gè)是奇數(shù)層棧纵诞,一個(gè)是偶數(shù)層棧上祈。可參考牛客網(wǎng)登刺。
層次遍歷一棵樹容易想到用隊(duì)列籽腕,可以簡單地在將結(jié)點(diǎn)的value插到list的最前面,但是這種方法肯定不能用于海量數(shù)據(jù)纸俭。java中的LinkedList可以用來實(shí)現(xiàn)隊(duì)列皇耗,這個(gè)隊(duì)列的實(shí)現(xiàn)是雙向鏈表,是可以正向迭代和反向迭代的揍很。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Iterator;
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> final_list = new ArrayList<>();
if(pRoot==null){return final_list;}
boolean left2right = true;
ArrayList<Integer> list = new ArrayList<>();
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(null);//分隔符在一層結(jié)點(diǎn)的前面比較方便
queue.offer(pRoot);
while(queue.size()!=1){//到最后只剩下一個(gè)null的時(shí)候結(jié)束循環(huán)
TreeNode node = queue.poll();//隊(duì)頭結(jié)點(diǎn)出隊(duì)列
if(node == null){//遇到分界符號
Iterator<TreeNode> iter = null;//對隊(duì)列中的結(jié)點(diǎn)進(jìn)行迭代郎楼,但不出隊(duì)列
if(left2right){
iter = queue.iterator();//正向迭代器
}else{
iter = queue.descendingIterator();//反向迭代器
}
left2right = !left2right;//將迭代方向反向
while(iter.hasNext()){
TreeNode temp = (TreeNode)iter.next();//迭代隊(duì)中的每個(gè)元素,加入list
list.add(temp.val);
}//一般遍歷樹時(shí)我們將結(jié)點(diǎn)出隊(duì)窒悔,并將val加入list呜袁,但是這里只對隊(duì)中的元素迭代,并不出隊(duì)简珠。另外傅寡,此時(shí)遍歷時(shí),隊(duì)中只有結(jié)點(diǎn)北救,沒有null
final_list.add(new ArrayList<Integer>(list));//拷貝一個(gè)list加入final_list
list.clear();//清空list
queue.offer(null);//隊(duì)中元素迭代完了之后加入null
}else{
if(node.left!=null){queue.offer(node.left);}//我們將元素迭代完了之后再將元素遍歷一遍,此時(shí)只需要將這里元素的左右結(jié)點(diǎn)加入隊(duì)即可
if(node.right!=null){queue.offer(node.right);}
}
}
return final_list;
}
}
60芜抒、把二叉樹打印成多行
題目描述
從上到下按層打印二叉樹珍策,同一層結(jié)點(diǎn)從左至右輸出。每一層輸出一行宅倒。
解題思路
層次遍歷很容易想到用隊(duì)列攘宙。牛客評論區(qū)還有利用遞歸方法的拐迁,遞歸是深度優(yōu)先蹭劈,但是他通過對方法傳入深度值來指定將結(jié)點(diǎn)加入哪一個(gè)list,從而使返回結(jié)果看起來像層次遍歷线召。
隊(duì)列版本:
import java.util.ArrayList;
import java.util.LinkedList;
public class Solution {
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> final_list = new ArrayList<>();
if(pRoot==null){return final_list;}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(pRoot);
queue.offer(null);//分界符號
final_list.add(new ArrayList<Integer>());//新建一個(gè)list加入fianl_list
while(queue.size()!=1){//如果隊(duì)列只剩下一個(gè)元素铺韧,則這個(gè)元素一定是分界符號
TreeNode node = queue.poll();//取隊(duì)頭元素
if(node!=null){//如果隊(duì)頭不是分界符號
final_list.get(final_list.size()-1).add(node.val);//將val加入相應(yīng)list
if(node.left!=null){queue.offer(node.left);}//如果左子樹非空,則將左子樹的根節(jié)點(diǎn)加入隊(duì)列
if(node.right!=null){queue.offer(node.right);}//如果右子樹非空缓淹,則將右子樹的根節(jié)點(diǎn)加入隊(duì)列
}else{//如果遇到了分界符號哈打,則分界符號前面的層已經(jīng)遍歷完
final_list.add(new ArrayList<Integer>());//新建一個(gè)list,為下一層的遍歷做準(zhǔn)備
queue.offer(null);//下一層已經(jīng)全部入隊(duì)列讯壶,最后加入分界符號
}
}
return final_list;
}
}
遞歸版本:
import java.util.ArrayList;
public class Solution {
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> final_list = new ArrayList<>();
if(pRoot==null){return final_list;}
return depth(pRoot, 1, final_list);
}
ArrayList<ArrayList<Integer>> depth(TreeNode p, int depth, ArrayList<ArrayList<Integer>> final_list){
if(depth > final_list.size()){//當(dāng)前要遍歷新層
final_list.add(new ArrayList<Integer>());//建一個(gè)新層對應(yīng)的list加入final_list
final_list.get(depth-1).add(p.val);//將當(dāng)前結(jié)點(diǎn)加入對應(yīng)的list
}else{//當(dāng)前層對應(yīng)的list已經(jīng)建立料仗,則將當(dāng)前結(jié)點(diǎn)的val加入對應(yīng)list
final_list.get(depth-1).add(p.val);
}
if(p.left!=null){depth(p.left, depth+1, final_list);}//左不為空則遞歸左
if(p.right!=null){depth(p.right, depth+1, final_list);}//右不為空則遞歸右
return final_list;
}
}
結(jié)尾
如果您發(fā)現(xiàn)我的文章有任何錯(cuò)誤,或?qū)ξ业奈恼掠惺裁春玫慕ㄗh伏蚊,請聯(lián)系我立轧!如果您喜歡我的文章,請點(diǎn)喜歡~*我是藍(lán)白絳,感謝你的閱讀氛改!