LinkedList Summary

Idea

How to think about linked-list questions:
The essence of linkedlist type question is to play with pointers.
two types of operations:

  • re-point pointers (i.e. also cut the old pointer connection) (op1)
    -- can think of these as "core operations"
  • create tracking pointer and moving tracking pointers (op2)
    -- can think of these as "supportive operations"

So when we meet a new linkedlist question, ask:

  • what kind of op1 we need (re-point the pointers .. )?
  • what kind of op2 we need (to track the nodes)?

In some algorithm, we cannot incoporate the head node operation into loop logic, need to do it separately.

It often easier to consider from a middle point, i.e. we already have performed the sets of operations for the first few nodes. It can be trickier to directly think what should be done to the head node, because head node operation can be somewhat different from other nodes.

It is highly preferred to test 'curr' directly, instead of test on 'curr.next' within the while loop. One advantage of using 'curr' is that we can therefore make sure curr.next always can be used.

A clever trick to avoid deal with head node using separate logic is to initialize a extra head node, and only return head.next at the end

Many algorithm need to have 2 or 3 tracking pointers, in order to prevent losing of nodes.

  • prev
  • curr
  • next

Most of the LL questions use iterative approach. I will start to see more about recursive approach on LL later.

Question List:

206 Reverse linkedlist

http://www.geeksforgeeks.org/write-a-function-to-reverse-the-nodes-of-a-linked-list/

curr
prev
tmp
curr = head (op2)
prev = None (op2)
while curr:
  next = curr.next #(op2)
  curr.next = prev #(op1) # only core operation, which is reverse pointer direction
  prev = curr #(op2)
  curr = next #(op2) 
return prev

328. Odd Even Linked List

class Solution(object): 
  def oddEvenList(self, head): 
    """ 
    :type head: ListNode :rtype: ListNode 
    """ 
    if not head: 
      return head 

    odd=head # op2, track the last cleaned odd list node
    even=head.next # op2, track the last cleaned even list node

    while even and even.next!=None: 
      temp = even.next  #op2
      even.next = even.next.next  # op1
      temp.next = odd.next  #op1
      odd.next = temp #op1
      odd=odd.next  #op2
      even=even.next #op2
    return head

83. Remove Duplicates from Sorted List

    def deleteDuplicates(self, head):
        """
        this algorithm also involves many tricky points !! 
        
        be extremely careful about pointer moving, consider it case by case
        some times pointer moving is within testing logic, sometimes it is not !!! 
            in this case, the pointer moving is within testing logic 
        
        """
        if head == None or head.next == None:
            return head
        unique_values = set()
        unique_values.add(head.val) # base on our logic, the first node need to be take care outside loop
        curr = head.next
        prev = head
        
        while curr != None:
            if curr.val not in unique_values:
                unique_values.add(curr.val)
                curr = curr.next #op2
                prev = prev.next #op2
            else:
                prev.next = curr.next #op1 # this doesn't apply to first node
                curr = curr.next #op2, this shouldn't be within the test logic
                # CAREFUL! this case, we don't need to move prev at all !! (no prev = prev.next)
                
        return head

234. Palindrome Linked List

  • first, find the middle node
  • second, reverse the first part and compare
    • it involves inverse linked list question

21. Merge Two Sorted Lists

147. Insertion Sort List

the deal with head node in this case is an Art !!!

There will be a loop of last node and curr node after each iteration for this algorithm, but don't care.

Need to revisit this algorithm again!!

public class Solution {
public ListNode insertionSortList(ListNode head) {
        if( head == null ){
            return head;
        }

        ListNode helper = new ListNode(0); //new starter of the sorted list
        //helper.next = head;
        ListNode cur = head; //the node will be inserted
        ListNode pre = helper; //insert node between pre and pre.next
        ListNode next = null; //the next node will be inserted
        //not the end of input list
        while( cur != null ){ // this logic test still use cur itself, as usually
            next = cur.next;
            //find the right place to insert
            while( pre.next != null && pre.next.val < cur.val ){ // this logic test use pre.next (so it is called pre ...lol)
                /*
                I see why this while line is correct:
                    1. we don't need helper.next = head during initialization (not necessary)
                    2. when pre=helper, pre.next == null, therefore the second part of while test will not be run
                            therefore we won't have pre.next.val NullPointerError
                    3. we should use pre.next to compare, not pre itself
                */
                pre = pre.next;
            }
            //insert between pre and pre.next
            cur.next = pre.next;
            pre.next = cur; // here, although we don't have helper.next = head, but at this place, we build connection 
                            // of helper.next after the first iteration
            pre = helper;
            cur = next;
        }

        return helper.next;
    }
}
# have exceed time limit error: 

class Solution(object):
    def insertionSortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        """
        Need 4 pointers: head, prev, curr, next
        I feel the tricky part is to consider how to deal with head node
        - start from middle, i.e. assume first few nodes already get sorted
        i.e. prev, curr, and head is already in the right position
            next is usually moved in the beginning of a new loop
            
        - a clever trick to avoid deal with head node using separate logic is 
            to initialize a extra head node, and only return head.next at the end
            
        """
        if not head or not head.next:
            return head
        helper = ListNode(0)
        prev = helper
        curr = head
        
        """
        # the following logic is wrong: 
        while curr: # test on curr, compare and decide where to move it to
            while (not prev.next) or (prev.next.val < curr.val):
                # if (not prev.next) fail, then (prev.next.val < curr.val) won't be executed, so it is safe
                prev = prev.next # move forward(op2)
            next = prev.next # keep tracking prev.next (op2)
            prev.next = curr # op1
            curr.next = next # op1
        """
        
        # Time Limit Exceeded error... have no idea why .... 
        count = 0
        while curr:
            print '--count--', count
            count += 1
            next = curr.next
            #print '--prev.next--',prev.next
            while (prev.next != None) and (prev.next.val < curr.val):
                prev = prev.next
            prev.next = curr
            curr.next = next
            prev = helper
            curr = next
            
        return helper.next

86. Partition List

  • intuitively, we can create two new linked list, one for all nodes smaller than x, one for larger.
# use dummy head
def partition(self, head, x):
    h1 = l1 = ListNode(0)
    h2 = l2 = ListNode(0)
    while head:
        if head.val < x:
            l1.next = head
            l1 = l1.next
        else:
            l2.next = head
            l2 = l2.next
        head = head.next
    l2.next = None
    l1.next = h2.next
    return h1.next 

141. Linked List Cycle

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        fast = slow = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast is slow:
                return True
        return False

142. Linked List Cycle II

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        fast = slow = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast is slow:
                break
        
        if not fast or not fast.next:
            # here we need to check fast again. because the above while loop may break at fast==None
            # ATTENTION: this will not work:
            #       if not (fast or fast.next): <- think WHY ! 
            return None
        
        slow = head
        while slow is not fast:
            slow = slow.next
            fast = fast.next
        return slow

109. Convert Sorted List to Binary Search Tree

// copied from leetcode solution 
public TreeNode sortedListToBST(ListNode head) { 
  // base case 
  if (head == null) return null; 
  ListNode slow = head; 
  ListNode fast = head; 
  ListNode prev = null; 
  // find the median node in the linked list, after executing this loop 
  // fast will pointing to the last node, while slow is the median node.   
   while (fast.next != null) { 
    fast = fast.next; 
    if (fast.next == null) { break; } 
    fast = fast.next; 
    prev = slow; 
    slow = slow.next; 
  } 
  if (prev != null) prev.next = null; 
  else head = null; 

  TreeNode root = new TreeNode(slow.val); 
  root.left = sortedListToBST(head); 
  root.right = sortedListToBST(slow.next); 
  return root; 
}

Another similar solution, using fast/slow pointers

2. Add Two Numbers

This code is great! Avoid extra test on if left or right is None

def addTwoNumbers(self, l1, l2):
    dummy = cur = ListNode(0)
    carry = 0
    while l1 or l2 or carry:
        if l1:
            carry += l1.val
            l1 = l1.next
        if l2:
            carry += l2.val
            l2 = l2.next
        cur.next = ListNode(carry%10)
        cur = cur.next
        carry //= 10
    return dummy.next 

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子河胎,更是在濱河造成了極大的恐慌纺棺,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異没龙,居然都是意外死亡链韭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門窍株,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)民轴,“玉大人,你說(shuō)我怎么就攤上這事球订『舐悖” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵冒滩,是天一觀的道長(zhǎng)微驶。 經(jīng)常有香客問(wèn)我,道長(zhǎng)开睡,這世上最難降的妖魔是什么因苹? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮篇恒,結(jié)果婚禮上扶檐,老公的妹妹穿的比我還像新娘。我一直安慰自己胁艰,他們只是感情好款筑,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布智蝠。 她就那樣靜靜地躺著,像睡著了一般奈梳。 火紅的嫁衣襯著肌膚如雪寻咒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天颈嚼,我揣著相機(jī)與錄音毛秘,去河邊找鬼。 笑死阻课,一個(gè)胖子當(dāng)著我的面吹牛叫挟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播限煞,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼抹恳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了署驻?” 一聲冷哼從身側(cè)響起奋献,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎旺上,沒(méi)想到半個(gè)月后瓶蚂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宣吱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年窃这,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片征候。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杭攻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疤坝,到底是詐尸還是另有隱情兆解,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布跑揉,位于F島的核電站锅睛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏畔裕。R本人自食惡果不足惜衣撬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一乖订、第九天 我趴在偏房一處隱蔽的房頂上張望扮饶。 院中可真熱鬧,春花似錦乍构、人聲如沸甜无。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)岂丘。三九已至陵究,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奥帘,已是汗流浹背铜邮。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寨蹋,地道東北人松蒜。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像已旧,于是被迫代替她去往敵國(guó)和親秸苗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容