這篇文章主要講解了“如何使用debug調(diào)試”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何使用debug調(diào)試”吧!
成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供天鎮(zhèn)網(wǎng)站建設(shè)、天鎮(zhèn)做網(wǎng)站、天鎮(zhèn)網(wǎng)站設(shè)計、天鎮(zhèn)網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、天鎮(zhèn)企業(yè)網(wǎng)站模板建站服務(wù),10多年天鎮(zhèn)做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
反轉(zhuǎn)鏈表是經(jīng)典的題目,題中信息描述很清晰,給定一個單鏈表,將其反轉(zhuǎn)。
先說說有什么思路呢?從題中給的案例輸出結(jié)果看,是不是只需要將輸入的鏈表的指針改成相反方向,就可以得到要輸出的結(jié)果。
就好比如下圖所示:
但是問題來了,我們是單鏈表,是沒辦法將下個節(jié)點直接指向該節(jié)點的上個節(jié)點。
因此就需要定義一個輔助指針,用來指向該節(jié)點的上個節(jié)點,這樣就能完成,如下圖所示。
那按照我們上面分析也就是將cur指針指向pre節(jié)點就可以了。
注意:此處有坑
當(dāng)我們將當(dāng)前節(jié)點【cur】指向上一個節(jié)點【pre】的時候,如何將指針向下移動呢?
此時的節(jié)點【cur】已經(jīng)指向了上一個節(jié)點【pre】了,所以我們還需要一個臨時變量去保存當(dāng)前節(jié)點的下個節(jié)點,具體為什么這么做,我們在下面代碼演示的時候debug看下過程。
接著我們上面的步驟,將指針向下移動,如圖所示。
移動指針后,再將當(dāng)前節(jié)點的next指針指向上一個節(jié)點。
最后當(dāng)前節(jié)點沒有下個節(jié)點的時候,就結(jié)束遍歷,如圖所示。
按照套路,先初始化節(jié)點對象。
class ListNode { int val; ListNode next; ListNode() { } ListNode(int val) { this.val = val; } ListNode(int val, ListNode next) { this.val = val; this.next = next; } @Override public String toString() { return "ListNode{" + "val=" + val + '}'; } }
創(chuàng)建單鏈表結(jié)構(gòu)。
// 創(chuàng)建單鏈表 ListNode l1 = new ListNode(1); ListNode l2 = new ListNode(2); ListNode l3 = new ListNode(3); ListNode l4 = new ListNode(4); ListNode l5 = new ListNode(5); NodeFun nodeFun = new NodeFun(); nodeFun.add(l1); nodeFun.add(l2); nodeFun.add(l3); nodeFun.add(l4); // 返回創(chuàng)建的鏈表 ListNode node = nodeFun.add(l5);
反轉(zhuǎn)鏈表的代碼。
public ListNode reverseListIteration(ListNode head) { // 定義上節(jié)點輔助指針 ListNode pre = null; // 定義當(dāng)前節(jié)點輔助指針 ListNode cur = head; // 循環(huán)當(dāng)前節(jié)點不為空 while (null != cur) { // 臨時變量保存當(dāng)前節(jié)點的下個節(jié)點 ListNode temp = cur.next; // 當(dāng)前節(jié)點的next指向上節(jié)點 cur.next = pre; // 上節(jié)點向下移動 pre = cur; // 當(dāng)前節(jié)點指向下個節(jié)點 cur = cur.next; } return pre; }
節(jié)點初始化完成了,按照分析我們定義了2個節(jié)點,如上圖第一次遍歷【pre】節(jié)點是null,【cur】從第一個節(jié)點開始。
下一步debug調(diào)試我們先不急,回顧之前說的一個問題,為什么要將當(dāng)前節(jié)點的下一個節(jié)點用臨時變量保存,那我們直接看debug調(diào)試。
第一次遍歷的時候,修改完指針后當(dāng)前節(jié)點已經(jīng)指向上一個節(jié)點了,再看上述題目分析的圖解。
這就是為啥要先把當(dāng)前節(jié)點的下個節(jié)點緩存起來。
上圖debug我們看出,【cur】當(dāng)前節(jié)點的指針已經(jīng)指向null,下一步就是移動指針指向下一個節(jié)點。
我們再接著進(jìn)行debug調(diào)試,按照上述分析,第二步循環(huán)就是將節(jié)點【2】指向上一個節(jié)點【1】,如下圖所示。
現(xiàn)在當(dāng)前節(jié)點【cur】已經(jīng)指向【2】,那它的下個節(jié)點就是【1】,如下圖所示。
經(jīng)過上面的兩步循環(huán),成功的將指針進(jìn)行了反轉(zhuǎn),剩下的節(jié)點循環(huán)也就如出一轍了。
當(dāng)循環(huán)到最后節(jié)點【5】時,下個節(jié)點為null,此時結(jié)束while循環(huán),而節(jié)點【5】也是指向了上一個節(jié)點【4】。
最后我們再看下運行結(jié)果。
如果做過字符串的算法題,里面有個回文字符串的題目。沒錯,它倆的意思是一樣的。
看題目描述得知一個鏈表是不是回文鏈表,就是看鏈表就是看鏈表正讀和反讀是不是一樣的。
假如說,我們拿到了后半部分鏈表,再將其反轉(zhuǎn)。去和鏈表的前半部分比較,值相等就是回文鏈表了。
注意:
這種方式會破壞原鏈表的結(jié)構(gòu),為保證題目的一致性,最后再將鏈表再重新拼接
另外一種解題方式為:將整個鏈表節(jié)點遍歷保存到數(shù)組中,而數(shù)組是有下標(biāo),并可以直接獲取數(shù)組的大小,那么只需從數(shù)組的首尾去判斷即可
反轉(zhuǎn)鏈表上一道題我們已經(jīng)分享了,現(xiàn)在重點是如何獲取后半部分的鏈表。
我們再說說快慢指針的思想,通常我們定義2個指針,一個移動快,一個移動慢。詳細(xì)的案例可以參考本人上一篇文章《開啟算法之路,還原題目,用debug調(diào)試搞懂每一道題》,有一道關(guān)于快慢指針的題目。
定義慢指針每次移動1個節(jié)點,快指針每次移動2個節(jié)點,當(dāng)然我們是需要保證快節(jié)點的下下個
個節(jié)點不為空。
slow = slow.next; fast = fast.next.next;
其實快慢指針的思想就是,假設(shè)鏈表是一個回文鏈表,快指針比慢指針是多走一步,當(dāng)快指針走完的時候,慢指針也就剛好走到該鏈表的一半。
上圖中slow指針正好走到鏈表的一半,此時也就得到鏈表的后半部分了,即slow.next
。
老套路,先創(chuàng)建一個回文鏈表。
ListNode l1 = new ListNode(1); ListNode l2 = new ListNode(2); ListNode l3 = new ListNode(2); ListNode l4 = new ListNode(1); NodeFun nodeFun = new NodeFun(); nodeFun.add(l1); nodeFun.add(l2); nodeFun.add(l3); ListNode head = nodeFun.add(l4);
獲取后半部分鏈表代碼。
private ListNode endOfFirstHalf(ListNode head) { ListNode fast = head; ListNode slow = head; while (fast.next != null && fast.next.next != null) { fast = fast.next.next; slow = slow.next; } return slow; }
反轉(zhuǎn)鏈表的代碼與上題目是一樣的。
最后將兩個鏈表進(jìn)行判斷是否是一樣的。
// 判斷是否回文 ListNode p1 = head; ListNode p2 = secondHalfStart; boolean flag = true; while (flag && p2 != null) { if (p1.val != p2.val) { flag = false; } p1 = p1.next; p2 = p2.next; }
先獲取鏈表的后半部分。
debug開始循環(huán)后,fast直接走到鏈表的第3個節(jié)點【2】
slow.next就是鏈表的后半部分,再將后半部分進(jìn)行鏈表反轉(zhuǎn)
最后我們也就得到如下2個鏈表。
最后將這2個鏈表進(jìn)行比較是否相等,相等則是回文鏈表。
獲取鏈表的中間節(jié)點乍一看和回文鏈表中使用快慢指針獲取后半鏈表有點類似呢?
沒錯,這波操作是類似的,但也并不是完全一樣,其主要思想還是快慢指針。
換句話說,如果你已理解了上面的題,那這道題也就不是什么事了。話不多說,先來分析一波。
同樣我們還是定義slow慢指針每次移動一個節(jié)點,fast快指針每次移動2個節(jié)點。
那么fast快指針移動到最后節(jié)點時,slow慢指針也就是要返回的鏈表。
我想,你是不是有個疑問。就是為什么慢指針是移動一個節(jié)點,快節(jié)點移動2個節(jié)點?如果是偶數(shù)個節(jié)點,這個規(guī)則還正確嗎!那就驗證下。
為了方便,就繼續(xù)上面節(jié)點的遍歷。
題目中描述,如果有2個中間節(jié)點,返回第二個節(jié)點
,所以返回節(jié)點【4,5,6】也就符合要求了
創(chuàng)建鏈表結(jié)構(gòu)。
ListNode l1 = new ListNode(1); ListNode l2 = new ListNode(2); ListNode l3 = new ListNode(3); ListNode l4 = new ListNode(4); ListNode l5 = new ListNode(5); NodeFun nodeFun = new NodeFun(); nodeFun.add(l1); nodeFun.add(l2); nodeFun.add(l3); nodeFun.add(l4); ListNode head = nodeFun.add(l5);
獲取后半部分鏈表代碼。
// 快慢指針 ListNode slow = head; ListNode fast = head; while(fast != null && fast.next != null){ //移動指針 fast = fast.next.next; slow = slow.next; } return slow;
快指針移動到節(jié)點【3】,慢指針移動到節(jié)點【2】
接著再走一步,快指針移動到節(jié)點【5】,慢節(jié)點移動到節(jié)點【3】,到此也就滿足題意的要求了。
這道題要求就是返回倒數(shù)K個節(jié)點,最笨的辦法就是參考上面鏈表反轉(zhuǎn),先將鏈表反轉(zhuǎn)。獲取前K個節(jié)點,將獲取的節(jié)點再次進(jìn)行反轉(zhuǎn)即可得到題目要求。
但是顯然這種方式只能滿足答案輸出,經(jīng)過上面的3道題目,有沒有得到什么啟發(fā)呢?
是的,這道題依然可以使用雙指針解決,是不是感覺雙指針可以解決所有的鏈表問題了(QAQ)。
再仔細(xì)一想,是不是感覺和上一道《鏈表的中間節(jié)點》題目很類似?獲取鏈表的中間節(jié)點是返回后半部分節(jié)點,而本道題是要求返回指定K個節(jié)點。
那就直接說結(jié)論吧,同樣是定義快慢指針。只不過在上道題中快指針是每次移動2個節(jié)點,本道題中給定的K,就是快指針移動的節(jié)點個數(shù)。
同樣初始化指針都在首節(jié)點,如果我們先將fast指針移動K個節(jié)點。
到此才算初始化節(jié)點完成,剩下的操作就是遍歷剩下的鏈表,直到fast指針指向最后一個節(jié)點。
一直遍歷到fast節(jié)點為null,此時返回slow指針?biāo)敢墓?jié)點。
初始化鏈表,由于和前幾道題的操作是一樣的,此處就不在展示。
獲取倒數(shù)第K個節(jié)點的代碼。
public ListNode getKthFromEnd(ListNode head, int k) { ListNode slow = head; ListNode fast = head; // 先將快指針向前移動K while (k-- > 0) { fast = fast.next; } while (fast != null) { fast = fast.next; slow = slow.next; } return slow; }
按照上面圖解分析,fast快指針指向節(jié)點【3】的時候才算真正初始化快慢指針完成。
當(dāng)快指針指向節(jié)點【5】時,slow慢節(jié)點指向節(jié)點【3】
注意:中間省略了一步,即慢指針指向節(jié)點【2】時,快指針指向節(jié)點【4】
節(jié)點【5】是最后一個節(jié)點,再次進(jìn)入while循環(huán)。
最后一次循環(huán)時,慢指針指向了4,快指針下一個節(jié)點已經(jīng)為null,此時結(jié)束循環(huán)。
這道題和上一篇中的題目【刪除排序鏈表中的重復(fù)元素】是一樣的,簡單的做法即利用Set集合保存未重復(fù)的節(jié)點,再遍歷鏈表判斷是否已存在Set集合中。
因此本道題就不在多分析,直接貼上代碼。
Set<Integer> set = new HashSet<>(); ListNode temp = head; while(temp != null && temp.next != null){ set.add(temp.val); if(set.contains(temp.next.val)){ temp.next = temp.next.next; }else{ temp = temp.next; } } return head; }
感謝各位的閱讀,以上就是“如何使用debug調(diào)試”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對如何使用debug調(diào)試這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
新聞名稱:如何使用debug調(diào)試
本文來源:http://aaarwkj.com/article6/jejdig.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計、虛擬主機、微信公眾號、網(wǎng)站策劃、網(wǎng)站收錄、網(wǎng)站營銷
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)