<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ?LeetCode刷題實(shí)戰(zhàn)82:刪除排序鏈表中的重復(fù)元素 II

          共 4001字,需瀏覽 9分鐘

           ·

          2020-11-03 09:51

          算法的重要性,我就不多說了吧,想去大廠,就必須要經(jīng)過基礎(chǔ)知識(shí)和業(yè)務(wù)邏輯面試+算法面試。所以,為了提高大家的算法能力,這個(gè)公眾號(hào)后續(xù)每天帶大家做一道算法題,題目就從LeetCode上面選 !

          今天和大家聊的問題叫做?刪除排序鏈表中的重復(fù)元素 II,我們先來看題面:

          https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/

          Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

          題意


          給定一個(gè)排序鏈表,刪除所有含有重復(fù)數(shù)字的節(jié)點(diǎn),只保留原始鏈表中 沒有重復(fù)出現(xiàn) 的數(shù)字。


          樣例

          示例 1:

          輸入: 1->2->3->3->4->4->5
          輸出: 1->2->5

          示例 2:

          輸入: 1->1->1->2->3
          輸出: 2->3



          解題

          https://www.cnblogs.com/techflow/p/13235840.html

          解法

          前面說了這題的質(zhì)量很高,這題是屬于典型的解法赤裸裸,但是很多人就是寫不出來的題,非常考察基本功。適合用在校招面試當(dāng)中,如果我有幸去面試校招生, 我可能會(huì)選這道題。不存在算法會(huì)不會(huì)的問題,寫不出來一定是基本功不夠扎實(shí)。
          鏈表已經(jīng)有序了,那么相同的元素必然會(huì)排在一起,我們只需要將它們移除就可以了。但是說起來簡單,要在鏈表當(dāng)中實(shí)現(xiàn)并不容易。難點(diǎn)主要有兩個(gè),一個(gè)是鏈表增刪節(jié)點(diǎn)的操作很多人不熟悉,尤其是像是C++這樣的語言涉及指針,可能更不容易。另外一個(gè)難點(diǎn)就在題意當(dāng)中,我們要做的不是去重,而是要所有重復(fù)的元素全部刪除
          看起來似乎和去重沒什么差別, 如果你真這么想,并且著手去實(shí)現(xiàn),那么幾乎可以肯定一定會(huì)遇到問題。
          因?yàn)殒湵硎菃蜗虻模僭O(shè)你當(dāng)前的指針是cur,當(dāng)你發(fā)現(xiàn)cur這個(gè)指針的元素存在重復(fù)的時(shí)候,你需要連當(dāng)前這個(gè)節(jié)點(diǎn)一起刪除。我們都知道,單向鏈表是不能走回頭路的,而刪除節(jié)點(diǎn),必須要用到前一個(gè)節(jié)點(diǎn)的指針。再加上判斷元素重復(fù)需要用到的指針,會(huì)需要我們同時(shí)維護(hù)多個(gè)指針,增加代碼的編碼難度。
          針對(duì)這個(gè)問題,我們有兩種解決思路。第一種是我們不在原鏈表上處理,而是創(chuàng)建一個(gè)新的鏈表進(jìn)行返回。所以我們要做的就不是刪除元素,而是插入元素,只有發(fā)現(xiàn)當(dāng)前元素不存在重復(fù)的時(shí)候才會(huì)插入鏈表。最后返回的也是一個(gè)全新的鏈表。
          這當(dāng)然是可以的,也是沒有問題了,我第一次做這道題就是采取的這種措施。相比之下, 這種方法會(huì)容易一些,因?yàn)槲覀?strong style="color: rgb(71, 193, 168);">不需要判斷太多的指針和位置,我貼一下當(dāng)時(shí)的代碼:

          class?Solution?{
          public:
          ????ListNode* deleteDuplicates(ListNode* head) {
          ??????????// 如果元素少于2個(gè)則直接返回
          ????????if?(head == nullptr || head->next == nullptr) return?head;
          ????????int cur = head->val;
          ??????????// 初始化
          ????????bool flag = false;
          ????????ListNode* ct = new?ListNode(0);
          ????????ListNode* pnt = ct;
          ????????head = head->next;
          ??????????// 遍歷元素
          ????????while?(head) {
          ????????????int hd = head->val;
          ???????????????// 判斷當(dāng)前元素與之前的元素是否相等
          ????????????if?(hd != cur) {
          ???????????????????// 之前的元素沒有出現(xiàn)重復(fù)
          ????????????????if?(!flag) {
          ???????????????????????// 把之前的元素存入鏈表
          ????????????????????pnt->next = new?ListNode(cur);
          ???????????????????????// 鏈表移動(dòng)
          ????????????????????pnt = pnt->next;
          ????????????????}else?flag = false;
          ???????????????????// 更新之前的元素
          ????????????????cur = hd;
          ????????????}else?flag = true;
          ????????????head = head->next;
          ????????}
          ??????????// 單獨(dú)處理最后一個(gè)元素
          ????????if?(!flag) pnt->next = new?ListNode(cur);
          ????????return?ct->next;
          ????}
          };


          雖然題目當(dāng)中沒有對(duì)解法做出限制,也沒有規(guī)定我們必須要在原鏈表上進(jìn)行處理,但是這種創(chuàng)建新鏈表的方法終歸有繞開問題的嫌疑。所以我們還有第二種解法,就是直面問題,我們維護(hù)多個(gè)指針,判斷當(dāng)前位置的下一個(gè)元素是否構(gòu)成重復(fù)。如果重復(fù),則刪除掉重復(fù)的部分。
          正如我們之前所說的那樣,在單向鏈表當(dāng)中很難刪除當(dāng)前元素,所以我們判斷下一個(gè)元素是否會(huì)構(gòu)成重復(fù)。如果重復(fù)的話,進(jìn)行刪除要可行許多。這樣也有一個(gè)問題就是,有可能鏈表的第一個(gè)元素就是重復(fù)的,我們沒有辦法找到第一個(gè)元素的上一個(gè)元素。針對(duì)這個(gè)問題,我們采用的方法是人為給它創(chuàng)造一個(gè)元素放在首元素之前。這樣我們整個(gè)流程就可以串起來了,唯一的難點(diǎn)就是編碼了。
          我們仔細(xì)一些,寫出代碼還是可以的:

          class Solution:
          ????def deleteDuplicates(self, head: ListNode) -> ListNode:
          ????????# 創(chuàng)建一個(gè)新的元素放在鏈表頭部,這樣原本第一個(gè)元素就是頭指針的下一個(gè)元素了
          ????????node = ListNode()
          ????????node.next?= head
          ????????# pnt指向新創(chuàng)建的元素
          ????????pnt = node
          ????????
          ????????while?pnt.next?is?not None:
          ????????????# cur指向當(dāng)前位置的下一個(gè)位置
          ????????????# 我們要做的即判斷cur這個(gè)指針的元素是否重復(fù)
          ????????????cur = pnt.next
          ????????????if?cur.next?is?None:
          ????????????????break
          ?????????????
          ????????????# ptr指向cur的next
          ????????????ptr?= cur.next
          ????????????# 如果ptr和cur相等,那么出現(xiàn)重復(fù),我們需要跳過所有相等的元素
          ????????????if?ptr?is?not None and?ptr.val == cur.val:
          ????????????????while?ptr?is?not None and?ptr.val == cur.val:
          ????????????????????ptr?= ptr.next
          ????????????????pnt.next?= ptr
          ????????????# 否則說明不重復(fù),移動(dòng)pnt
          ????????????else:
          ????????????????pnt = pnt.next
          ????????????????
          ????????# 由于我們開始的時(shí)候人為添加了一個(gè)輔助元素
          ????????# 返回的時(shí)候要將它去除
          ????????return?node.next

          總結(jié)

          這道題的算法很簡單,我想大部分人都能想出解法來,但是要將解法實(shí)現(xiàn)并不太容易。這當(dāng)中用到了很多小技巧,比如我們認(rèn)為創(chuàng)建了一個(gè)新的頭結(jié)點(diǎn),比如我們將刪除當(dāng)前元素轉(zhuǎn)化成了刪除下一個(gè)元素等等。這些技巧雖然算不上什么,但是靈活使用,可以大大降低我們編碼的復(fù)雜度,也正是因?yàn)檫@一點(diǎn),這題的質(zhì)量非常高,值得一做。
          很多人非常討厭涉及鏈表的問題,覺得鏈表很難操作,容易寫錯(cuò),但實(shí)際上這是基本功的很重要的一部分。很多公司喜歡考察候選人的基本功,提升這方面的能力對(duì)于我們應(yīng)聘或者是工作非常有幫助。
          好了,今天的文章就到這里,如果覺得有所收獲,請(qǐng)順手點(diǎn)個(gè)在看或者轉(zhuǎn)發(fā)吧,你們的支持是我最大的動(dòng)力。


          上期推文:

          LeetCode40-60題匯總,速度收藏!
          LeetCode刷題實(shí)戰(zhàn)61:旋轉(zhuǎn)鏈表
          LeetCode刷題實(shí)戰(zhàn)62:不同路徑
          LeetCode刷題實(shí)戰(zhàn)63:不同路徑 II
          LeetCode刷題實(shí)戰(zhàn)64:最小路徑和
          LeetCode刷題實(shí)戰(zhàn)66:加一
          LeetCode刷題實(shí)戰(zhàn)67:二進(jìn)制求和
          LeetCode刷題實(shí)戰(zhàn)68:文本左右對(duì)齊
          LeetCode刷題實(shí)戰(zhàn)69:x 的平方根
          LeetCode刷題實(shí)戰(zhàn)70:爬樓梯
          LeetCode刷題實(shí)戰(zhàn)71:簡化路徑
          LeetCode刷題實(shí)戰(zhàn)72:編輯距離
          LeetCode刷題實(shí)戰(zhàn)73:矩陣置零
          LeetCode刷題實(shí)戰(zhàn)74:搜索二維矩陣
          LeetCode刷題實(shí)戰(zhàn)75:顏色分類
          LeetCode刷題實(shí)戰(zhàn)76:最小覆蓋子串
          LeetCode刷題實(shí)戰(zhàn)77:組合
          LeetCode刷題實(shí)戰(zhàn)78:子集
          LeetCode刷題實(shí)戰(zhàn)79:單詞搜索

          瀏覽 23
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  麻豆九色| 操妞视频在线观看免费网站 | 这里只有精品2001 | 亚洲A V站 | 波多野吉衣av无码 |