Merge K Sorted Lists - Leetcode 23 - Python

แชร์
ฝัง
  • เผยแพร่เมื่อ 17 ม.ค. 2025

ความคิดเห็น • 190

  • @NeetCode
    @NeetCode  4 ปีที่แล้ว +23

    🚀 neetcode.io/ - I created a FREE site to make interview prep a lot easier, hope it helps! ❤

    • @BorisDJ007
      @BorisDJ007 4 ปีที่แล้ว +2

      Rem best girl

    • @tanish5662
      @tanish5662 ปีที่แล้ว

      I have a doubt on the time complexity in this question, I got log(Nklogk) as there is for loop inside while loop, Am i right? Please reply

    • @vijethkashyap151
      @vijethkashyap151 ปีที่แล้ว

      @@tanish5662 Can you explain please? There are n linked lists in the 'lists' list and per iteration we merge two items in the list, but we iterate through all items in each element of 'lists' atleast once, so i guess it's O(n log(k))

    • @tanish5662
      @tanish5662 ปีที่แล้ว

      Hi @@vijethkashyap151, its been 4 weeks and I don't remember my thought process, However I think I got this time complexity as there is a for loop inside while loop. To be precise, by just looking at the code I can say there are 3 nested loops, that might be one of the reason. If u think one of the loop is O(1), just tell me which one. I also remember asking chat gpt 4 about this and it agreed with me.

  • @pawandeepchor89
    @pawandeepchor89 ปีที่แล้ว +12

    I love the way you get right into the crux of the problem rather explaining unwanted details. Cool video and keep them coming, thanks.

  • @lugiadark21
    @lugiadark21 3 ปีที่แล้ว +16

    I code in Java, but with your explanations, I do not even need to look at your code. Keep up the good work!! You make everything super simple!!

  • @sistaseetaram9008
    @sistaseetaram9008 3 ปีที่แล้ว +11

    You are the best at simplifying problems ... Glad I found you.

  • @usmanmalik-xk5vi
    @usmanmalik-xk5vi 9 หลายเดือนก่อน +4

    Bro is a genius , breaks down hard problems into such easy solutions.

  • @lilianjiang22
    @lilianjiang22 3 ปีที่แล้ว +65

    Nice solution! But you can also use heap which is much easier and more simple.

    • @terrancejarod5187
      @terrancejarod5187 ปีที่แล้ว +5

      heap runs in O(knlogn) which is a lot slower than O(nlogn) which is the time complexity of the solution in the video

    • @dvm9999
      @dvm9999 11 หลายเดือนก่อน +20

      @@terrancejarod5187 its wrong to say O(knlogn).It would be O(nlogk)

    • @yunierperez2680
      @yunierperez2680 10 หลายเดือนก่อน +4

      I implemented both in Java using PriorityQueue (Heap) and the one from this video, runtime is quite similar. I think both are O(n log k)

    • @AbhijithVMohan
      @AbhijithVMohan 9 หลายเดือนก่อน +2

      ​ @terrancejarod5187 First we heapify k elements which can be done in O(k). Then while heap is not empty, we pop from heap with O(lg k), advance the list and push to heap with O(lgk) if list is not none. So apart from the heapify, each node takes O(lgk) time.
      Time complexity is thus O(k) + O(nlgk) where n is the total number of elements across all lists and k is the number of lists.

  • @jayshekarharkar8119
    @jayshekarharkar8119 8 หลายเดือนก่อน +9

    I'm not sure if what you described matches the implementation. Am I correct in understanding that the implementation doesn't use a divide and conquer approach but rather merges two lists at a time and then merges the resulting list with the next list in the list of lists? From an arithmetic standpoint, it seems like the sum you're computing follows the pattern k + (k-1) + (k-2) + ... + 1, which simplifies to \( \frac{k(k+1)}{2} \). Consequently, the overall time complexity remains. O(k.n) and not O(nlog k) as with a divide and conquer strategy. Could you confirm if my understanding is correct?

    • @samirkhandekar4678
      @samirkhandekar4678 6 หลายเดือนก่อน +1

      I have the same doubt. I also feel it is O(kn)

    • @Ankitb10
      @Ankitb10 5 หลายเดือนก่อน +1

      It is a little difficult to see at first but it is divide and conquer. Instead of recursively splitting in half, it merges 2 together and adds the combined list to the *end* . Then takes the next 2 from the *start* of the lists.
      Say you had four linked lists in lists. lists = [a, b, c, d]
      Then lists would look like the following after each iteration where the result of merging a and b is ab:
      [a,b,c,d]
      [c,d,ab]
      [ab,cd]
      [abcd]
      It's not:
      [a,b,c,d]
      [ab,c,d]
      [abc,d]
      [abcd]
      Hope that helps!

    • @sliderBro
      @sliderBro 2 หลายเดือนก่อน +1

      @@Ankitb10 The for loop on line 14 takes 2 consecutive (distinct) pairs of indices at a time. So if we start with lists = [a, b, c, d], the first iteration of the for loop merges lists at 0 and 1 (and appends to mergedLists which is now [ab]), and then the second iteration of the for loop merges lists at 2 and 4 (mergedLists is now [ab, cd]).
      Overall,
      Before while loop, lists = [a, b, c, d]
      After first while loop, lists = [ab, cd]
      After second while loop, lists = [abcd]
      You can think of it as starting at the leaves of the binary tree and combining 2 *distinct* leaves at each level, so it is indeed log(N).

  • @pekarna
    @pekarna 2 ปีที่แล้ว +31

    Thinking about an alternative:
    Have a priority queue filled with the heads of the lists;
    pop the smallest,
    add it to the result list,
    push the head of the list it originated from,
    repeat.
    If I am not mistaken, this should join K lists in O(n).

    • @harrywang9375
      @harrywang9375 2 ปีที่แล้ว +23

      Inserting an element into a priority queue is O(logn). Inserting n elements would then be O(nlogn).
      But seeing as you would only ever have k elements in your priority queue, you would get log k. And since you are inserting n nodes, you will get O(nlogk)

    • @Dun1007
      @Dun1007 2 ปีที่แล้ว +6

      It is O(nlogk) runtime and O(k) space which is almost as good as mergesort but not O(1) space

    • @harrywang9375
      @harrywang9375 2 ปีที่แล้ว +2

      @@Dun1007 Merge sort should be O(n) space.

    • @calvio2835
      @calvio2835 2 ปีที่แล้ว

      Why do you need a priority queue to compare and find smallest head? It just becomes finding smallest node in list of nodes

    • @harrywang9375
      @harrywang9375 2 ปีที่แล้ว +9

      ​@@calvio2835 You don't need any data structures at the end of the day. Might as well do two sum in O(n^2) time but you are definitely not going to pass an interview. How do you think we're going to find the next smallest head? Linear scan? That's inefficient given that you have to do that every single time you move a node. Priority queue is better than running a linear scan every time you pop an element.
      You don't need a priority queue but since we're optimizing for time, a priority queue is superior. Why run the algorithm in O(nk) time when you could run it in O(nlogk)?

  • @Urvashi7ful
    @Urvashi7ful 2 หลายเดือนก่อน +1

    First leetcode Hard I solved myself, thanks to your awesome explanations! Of course, seems like this problem really is a medium lol.

  • @n.h.son1902
    @n.h.son1902 7 หลายเดือนก่อน +4

    I've come up with the heap solution and I'm glad to know there's also another solution that only uses basic data structures like linked lists to solve it. Pretty amazing!!

  • @Fatmatlili95
    @Fatmatlili95 3 หลายเดือนก่อน

    Thank you for the explanation! I believe the complexity in the initial (brute force) algorithm is nk^2 and in this algorithm is nklog(k)

  • @thisisnotok2100
    @thisisnotok2100 ปีที่แล้ว +1

    I used a sorted deque (ascending) to solve this. When I used the first element, I would pop it, point at the next, then insert into the deque in the correct position based off of the new head.
    This was fast enough for leetcode's judges, but I don't know how to describe the time complexity of it.

    • @ashkan.arabim
      @ashkan.arabim 5 หลายเดือนก่อน +1

      inserting anything in the middle of a sequence is O(n) worst case. your solution would be O(n^2).

  • @cc-to2jn
    @cc-to2jn 2 ปีที่แล้ว +8

    btw u can simply it by using a queue. Convert the lists into a queue, and then u can simply popleft and append at the end

    • @cc-to2jn
      @cc-to2jn 2 ปีที่แล้ว +8

      q=deque([l for l in lists])
      while len(q)!=1:
      l1=q.popleft()
      l2=q.popleft()

      mList=merge(l1,l2)
      q.append(mList)

      return q[0]

  • @devakinandan23
    @devakinandan23 6 หลายเดือนก่อน +1

    you explain Leetcode HARD in 5 minutes (P.S i watch in 2x). you are awesome bro. i am your fan

  • @shashikantdivekar7839
    @shashikantdivekar7839 3 ปีที่แล้ว +6

    Thank you Sir. Very well explained. My favorite youtube channel for python/leetcode studies.

  • @idgafa
    @idgafa ปีที่แล้ว

    if you implemented the merge 2 lists, we can just start merging from reverse order and remove the merged row from lists:
    while len(lists) > 1:
    lists[-2] = merge(lists[-1], lists[-2])
    lists.pop()
    return lists[0]
    or this option seems better:
    while len(lists) > 1:
    for i in range(len(lists)//2):
    lists[i] = merge(lists[i], lists[-1])
    lists.pop()
    return lists[0]

  • @zl7460
    @zl7460 2 ปีที่แล้ว

    4:00 n log k complexity is true because each level costs O(n) total and there are log k levels. Wouldn't choose this simple solution without realizing this math first (since there are k-1 steps, doesn't seem like n log k at first glance). So I coded a complicated n log k solution instead xD

  • @rungxanh2901
    @rungxanh2901 2 ปีที่แล้ว +3

    I don't quite understand line 18. Wouldn't it replace the original 'lists' parameter? If so, where do the rest of the lists to be merged go?

    • @dtcx
      @dtcx ปีที่แล้ว

      The input is a list of linked lists. Say you got 4 lists - the for loop merges 1st and 2nd together into a single linked list and apppends the head of that linked list to the merged_lists (list). Then it merges 3d and 4th together and appends the result (head of the linked list) to the merged_lists again. Now the length of the merged_list is 2 (becase you appended 2 heads). It'll now overwrite the orginal "lists" parameter (4 linked lists) with merged_lists (2 linked lists). Becuse it's greater than 1, it'll run the for loop again merging those 2 linked lists into a single one. Then again it'll overwrite the "lists" parameter (2 linked lists) with merged_lists (1 linked list). At that point the while loop (while len(lists) > 1) exits and returns the head of the linked list.

    • @OwenWu-f9t
      @OwenWu-f9t ปีที่แล้ว

      @@dtcx but why can't we create an empty list - my list = [] and then set my list = merged lists and return mylist[0]? why do we have to overwrite the input? I thought we only care about what we return? why the first doesn't work but the 2nd does?
      while len(lists) > 1:
      res = []
      merged = []
      for i in range(0, len(lists), 2):
      list1 = lists[i]
      if (i + 1) < len(lists):
      list2 = lists[i + 1]
      else:
      list2 = None
      merged.append(self.ml(list1, list2))
      res = merged
      return res[0]
      vs.
      while len(lists) > 1:
      merged = []
      for i in range(0, len(lists), 2):
      list1 = lists[i]
      if (i + 1) < len(lists):
      list2 = lists[i + 1]
      else:
      list2 = None
      merged.append(self.ml(list1, list2))
      lists = merged
      return lists[0]

    • @hoixthegreat8359
      @hoixthegreat8359 5 หลายเดือนก่อน

      @@OwenWu-f9t ​ functions don't modify things outside their score unless we tell them to, and so lists (outside of the function) will retain its original value
      e.g.
      func(x, y):
      x = 2, y = 1
      ## will print x = 2, y = 1
      print(x, y)
      x = 100, y = 200
      func(x, y)
      ## will print x = 100, y 200
      print(x, y)
      and as for why the first one doesn't work-what about when len(lists) == 0?

  • @meylyssa3666
    @meylyssa3666 ปีที่แล้ว

    great explanation! Very clear and concise!

  • @MOHDABDULRAHMAN-o4e
    @MOHDABDULRAHMAN-o4e ปีที่แล้ว +2

    thank you very much, I personally think the previous LRU cache problem should be in hard category instead of this one .

  • @hammamziadeh4093
    @hammamziadeh4093 2 ปีที่แล้ว +17

    What's the space complexity of this?

    • @charleskorey6515
      @charleskorey6515 2 ปีที่แล้ว

      O(1)

    • @elibarlow682
      @elibarlow682 2 ปีที่แล้ว +7

      should be O(n) -- the mergedList array holds 2 lists (as a merged list) whose total number of elements will increase as n increases. K-way merge on a heap is O(k) in space.

    • @charleskorey6515
      @charleskorey6515 2 ปีที่แล้ว

      Not sure about Python but it is O(1) space for C++ where you don’t have to use additional space but you just need to move the pointers

    • @usa5450
      @usa5450 ปีที่แล้ว

      Clearly is O(n)

    • @nikhil199029
      @nikhil199029 7 หลายเดือนก่อน

      Pointers are also taking space bro​@@charleskorey6515

  • @kirillzlobin7135
    @kirillzlobin7135 ปีที่แล้ว

    You are the legend. Thank you for your job

  • @nguyenbach4710
    @nguyenbach4710 ปีที่แล้ว +2

    GENIUS PLZ DON'T STOP

  • @TheAlchemist444
    @TheAlchemist444 2 ปีที่แล้ว +1

    You're awesome man, can't thank you enough!

  • @RaoVenu
    @RaoVenu 2 ปีที่แล้ว +6

    Isn't the time complexity O(NKlgK). The first step the depth is lgK and there are k elements. So it is O(KlgK) initially. Now for every subsequent level it is O(KlgK) N/K times. Now to merge all of them together it would be O(NKlgK).
    using a MinHeap would make it O(NlgK)

  • @dhananjaym2311
    @dhananjaym2311 5 หลายเดือนก่อน

    Hey! which keyboard do you use?

  • @PerseRos285
    @PerseRos285 4 หลายเดือนก่อน

    mergeKLists can be a recursive function and in this case the code is much cleaner (but I suppose space complexity in this case is O(log k)):
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    n = len(lists)
    if n == 0:
    return None
    elif n == 1:
    return lists[0]

    m = n//2
    return self.merge2_lists(self.mergeKLists(lists[:m]), self.mergeKLists(lists[m:]))

  • @sujitnoronha5030
    @sujitnoronha5030 4 ปีที่แล้ว +2

    Really good explanation!

  • @sabisingh9635
    @sabisingh9635 5 หลายเดือนก่อน

    Can anyone explain what’s happening on line 17. He mentions he’s going to append it but he instead typed mergelists which is not making sense. Thanks in advance. 7:04

  • @Historyiswatching
    @Historyiswatching 2 ปีที่แล้ว +2

    Why do you have to return lists[0]? Could we just return mergedList?

    • @nikhil_a01
      @nikhil_a01 2 ปีที่แล้ว +1

      If the original input 'lists' only had a single element we'd skip over the while loop because the condition is 'while len(lists) > 1'. In that case mergedList wouldn't be defined. But returning lists[0] would do the right thing: if there's only a single list it's by definition already merged so we can just return it.

  • @NEO-wl9ox
    @NEO-wl9ox ปีที่แล้ว

    pretty sophisticated and enrapturing solution , and I think alternatively we could pick out minheap to sort this problem

  • @reinforcer9000
    @reinforcer9000 3 ปีที่แล้ว +1

    For loops for divide and conquer instead of recursion. Nice

  • @tuanchu8022
    @tuanchu8022 ปีที่แล้ว

    Thanks for your explanation. Can you please make a video that explains the Leetcode 632: Smallest range covering elements from K lists?

  • @xxRAP13Rxx
    @xxRAP13Rxx ปีที่แล้ว

    Great video! Does the mergedLists variable take up O(K) memory complexity?

  • @Voidwanderer571
    @Voidwanderer571 11 หลายเดือนก่อน

    After watching your life story video I just realized this video was uploaded at your life rock bottom... But you still did a great job explaining even at that point!

  • @Historyiswatching
    @Historyiswatching 2 ปีที่แล้ว

    This is clever! Is it considered a divide and conquer solution?

  • @jayeshkaushik2975
    @jayeshkaushik2975 3 ปีที่แล้ว

    Thanks sir, These videos are very help full.

  • @asifchoudhuryca
    @asifchoudhuryca 2 ปีที่แล้ว +1

    How the case of len(lists)==1 will be handled?

  • @tanmaysatsangi131
    @tanmaysatsangi131 3 ปีที่แล้ว +3

    Hii @NeetCode can u plz explain, what is the difference between 'len(lists) == 0' and 'not list'.

    • @nukagvilia4141
      @nukagvilia4141 3 ปีที่แล้ว

      I'm curious about that too.

    • @DragonStoneCreations
      @DragonStoneCreations 3 ปีที่แล้ว

      I think its 1. A empty list and 2. NULL value

    • @mindsetnuggets
      @mindsetnuggets 2 ปีที่แล้ว +8

      It's the same. Either of them will check for empty list.

    • @tanmaysatsangi131
      @tanmaysatsangi131 2 ปีที่แล้ว +1

      @@mindsetnuggets thanks

  • @soroushbassam
    @soroushbassam 4 ปีที่แล้ว +1

    the first if statement can just be `if not lists`...that'll check if the list is empty.

  • @howardlam6181
    @howardlam6181 2 ปีที่แล้ว

    basically asking for a merge sort algorithm with linked lists structure rather than vectors, which is actually simpler because we don't have to allocate memory.

  • @amynguy
    @amynguy 9 หลายเดือนก่อน

    its a little confusing how you switch back and forth with the definition on "n". Should it be average number of elements in each list or total elements?

  • @akhma102
    @akhma102 4 หลายเดือนก่อน

    Thank you Neet!

  • @Sheik694
    @Sheik694 11 หลายเดือนก่อน +1

    I am having doubt, how come the time complexity is O(n log k) ? instead of O ((n*log k) log k) ?
    I understood the outside (log k) part -> we are going to repeat the merge operations for log k times as the lists size halved each time. (first while loop)
    But what about the inner for loop and upcoming merge operation for each pair ?
    In the first while loop: my inner for loop will run k/2 times and each merge operation will cost you n times.
    so it is (k/2 * n) times
    In the second while loop: my inner for loop will run k/4 times and merge is n times.
    so it is (k/4 * n) times
    right ?

    • @iy9303
      @iy9303 8 หลายเดือนก่อน

      same thoughts here, @NeetCode could you please explain?

  • @capooti
    @capooti 3 ปีที่แล้ว +5

    Thanks a lot for the beautiful channel, I am a big fan and love all of your solutions. In this case however I believe an easiest approach is with a min heap (that is also the category where Blind 75 put this problem in):
    # O(n log k)|O(n+k) where n=total number of nodes, k=number of lists
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    heap = []
    for idx, lst in enumerate(lists):
    if lst:
    heapq.heappush(heap, (lst.val, idx))
    dummy = res = ListNode(0)
    while heap:
    _, idx = heapq.heappop(heap)
    curr = lists[idx]
    res.next = curr
    if curr.next:
    heapq.heappush(heap, (curr.next.val, idx))
    lists[idx] = lists[idx].next
    res = res.next
    return dummy.next
    Thanks again and keep up the great work!

    • @konark709
      @konark709 3 ปีที่แล้ว

      space complexity is O(N) for priority queue, with merge sort you're splicing the lists together

    • @neetamlimbu5335
      @neetamlimbu5335 2 ปีที่แล้ว

      @@konark709 what is the space complexity for the above algorithm?

  • @yossarian2909
    @yossarian2909 2 ปีที่แล้ว +5

    Can we use recursion (divide and conquer) instead of the for loop to parse through the list?

    • @astik2002
      @astik2002 ปีที่แล้ว

      I.m also having the same thought process, like using merge sort

    • @tb0707-m8w
      @tb0707-m8w ปีที่แล้ว +1

      I think it takes more memory. Correct me If I'm wrong.

  • @skairways
    @skairways 6 หลายเดือนก่อน

    That is pretty neat solution & explanation, beats 1.7x my minHeap solution

  • @rishikeshjaadhav2405
    @rishikeshjaadhav2405 2 ปีที่แล้ว

    Thank you soo much for this!!

  • @inupete69
    @inupete69 ปีที่แล้ว

    funny thing about this (in python)
    merge sort will not get you the fastest result.
    you can traverse each listnode and add their vals to a single unsorted list. call python's (reverse) sort method on it. turn this reverse sorted list into a linkedlist, and return that. much faster.
    but interviewers will prefer to see the merge sort method.

  • @Lulit999
    @Lulit999 2 ปีที่แล้ว

    The eastiest to understand solution with O(nlogn) time and O(n) memory complexity is to use Counter, where we store node value as key, and as value number of occurencies in all lists. Then we sort keys in counter and just generate the result.
    class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    dummy = ListNode(0)
    tail = dummy
    counter = Counter()

    for single_list in lists:
    while single_list:
    counter[single_list.val] += 1
    single_list = single_list.next


    for key in sorted(counter.keys()):
    while counter[key] > 0:
    tail.next = ListNode(key)
    counter[key] -= 1
    tail = tail.next

    return dummy.next

    • @nikhil_a01
      @nikhil_a01 2 ปีที่แล้ว

      If you're going to sort the values, there's not really any advantage to storing them in a Counter. You can just put all the values in a Python list and sort that. LeetCode even has that as one of the solutions on their official solutions page:
      class Solution:
      def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
      self.nodes = []
      head = point = ListNode(0)
      for l in lists:
      while l:
      self.nodes.append(l.val)
      l = l.next
      for x in sorted(self.nodes):
      point.next = ListNode(x)
      point = point.next
      return head.next

  • @kafychannel
    @kafychannel 2 ปีที่แล้ว +1

    get it thanks a lot!

  • @AnnieBox
    @AnnieBox 3 ปีที่แล้ว +1

    Do #43 and #73 on the list are repeated? Both of them are 'Merge K Sorted Lists'~~

    • @minyoungan9515
      @minyoungan9515 3 ปีที่แล้ว

      Good chatch. I guess two ways to solve the problem: Divide-and-Conquer and Heap

  • @sahilkapur2268
    @sahilkapur2268 4 ปีที่แล้ว +4

    The solution to this question has some other recommended solutions as well. I get that this is probably the best solution to visualize and explain, but how does it to compare to the others in runtime analysis?
    Also was anyone else a lil mad when one of the recommended solutions was literally dump everything into an array and sort LOL

    • @tyronelagore1479
      @tyronelagore1479 4 ปีที่แล้ว +3

      Not sure if it's leetcode being weird or not, but the the dump into an array and sort method performed faster for me than this video method. (video method actually didn't complete in time limit for me)

    • @OM-el6oy
      @OM-el6oy 3 ปีที่แล้ว

      That's what I did when I first saw this problem. It feels kind of like cheating but it works and has a decent time complexity. Probably not what the interviewer is looking for though.

    • @chilly2171
      @chilly2171 3 ปีที่แล้ว +2

      @@OM-el6oy The time complexity is too slow. He's hiding it for the views.

    • @chilly2171
      @chilly2171 3 ปีที่แล้ว +1

      @@OM-el6oy He's using a brute force approach, and not using divide and conquer.

  • @midhileshmomidi3120
    @midhileshmomidi3120 2 ปีที่แล้ว +1

    Using heaps
    class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    heap = []
    for idx,element in enumerate(lists):
    print(element)
    if element:
    heappush(heap,(element.val,idx))
    dummy = curr = ListNode(0)
    while heap:
    val,idx = heappop(heap)
    curr.next = ListNode(val)
    curr = curr.next
    if lists[idx].next:
    lists[idx] = lists[idx].next
    heappush(heap,(lists[idx].val,idx))
    return dummy.next

    • @lingyuhu4623
      @lingyuhu4623 ปีที่แล้ว

      Hello, does priority queue support compare between instances of ListNode?

    • @mahdirahman6222
      @mahdirahman6222 ปีที่แล้ว

      @@lingyuhu4623 Nope it doesn't thats why we use the index. So for e.g lists[idx] represents the next node to add to the heap. Index 0 means that node came from first list and so on

  • @kushagraverma7855
    @kushagraverma7855 2 ปีที่แล้ว +9

    i think using heaps (k-way merge) would have been cleaner

  • @prerakchoksi2379
    @prerakchoksi2379 2 ปีที่แล้ว

    For anyone sugestiong to use heap, heres why you shouldn't:
    Suppose we have n lists and each list has say k elements.
    No in order to insert those values to heap, it'll take n*k*log(something). While here its less ig. Please correct me if I am wrong.

    • @attkgreenpiggy
      @attkgreenpiggy 2 ปีที่แล้ว +5

      His n in O(n*k) and O(n*log(k)) is different from the heap implementation's n in O(n*k*log(k)). In the heap n, n represents the average number of elements in a single linked list, while his n represents the total number of elements in all linked lists. The time complexities are actually the same for both implementations once you account for this difference in the definition of n.

  • @mingjunma293
    @mingjunma293 3 ปีที่แล้ว +4

    why return lists[0]?

    • @KolydoscopeMusic
      @KolydoscopeMusic 3 ปีที่แล้ว +4

      Because by the time the while loop is complete and the length of lists is equal to one, the only list in 'lists' will be the sorted one of all the ListNodes, so you can just return it (which would be the first and only element in lists -> lists[0])

    • @mingjunma293
      @mingjunma293 3 ปีที่แล้ว +1

      @@KolydoscopeMusic thx!!

  • @jesseli7086
    @jesseli7086 2 ปีที่แล้ว +1

    Center blew the snap, you coding style as good as the sound from your keyboard

  • @EE12345
    @EE12345 11 หลายเดือนก่อน

    So between this and the heap solution, both have time complexity O(NLogK), but this solution has space complexity O(1) not counting the result as extra space while the heap solution has O(K) space where K is the number of lists. So probably better off learning this solution even though the heap one is simple and efficient, it's not optimal.

  • @satishkk7548
    @satishkk7548 2 ปีที่แล้ว

    is this a duplicated in blind75 list?

  • @PippyPappyPatterson
    @PippyPappyPatterson 2 ปีที่แล้ว +1

    What's the complexity of this solution?

  • @אליששון-כ2ש
    @אליששון-כ2ש 27 วันที่ผ่านมา

    @NeetCode, it seems that the complexiy of the first bruteforce is O(k^2*n) and the second is O(k log(k) * n)
    why?
    The second make at 1# level: k/2* n,
    at #2 level we have k/2 lists each with 2n: so we have: k/4 * 2n
    so we have k/2 * n + k/4 * 2n + k/8 * 4n = n(k/2+ k/2 ...) = n * k * logk

  • @ashishchoudhary1664
    @ashishchoudhary1664 9 หลายเดือนก่อน

    I used a hashmap to kepe track of the seen values(and append after them when seen again).
    I used two stacks, one for keeping smaller values than current value and other for keeping larger values than current value. I keep poping the values from one and append to another till I find the appropriate place to insert the current value.
    My solution beats 92% users in runtime with 69ms. But i'm not sure if its he right approach.
    class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    head = None
    hashmap = {}
    stack_left = [float("-infinity")]
    stack_right = [float("infinity")]
    for l in lists:
    curr = l
    while curr:
    nxt = curr.next
    if curr.val in hashmap:
    temp = hashmap[curr.val].next
    hashmap[curr.val].next = ListNode(curr.val, temp)
    curr = hashmap[curr.val].next
    else:
    while curr.val < stack_left[-1]:
    stack_right.append(stack_left.pop())
    while curr.val > stack_right[-1]:
    stack_left.append(stack_right.pop())

    if stack_left[-1] > float("-infinity"):
    temp = hashmap[stack_left[-1]].next
    hashmap[stack_left[-1]].next = ListNode(curr.val, temp)
    curr = hashmap[stack_left[-1]].next
    else:
    head = ListNode(curr.val, head)
    curr = head
    stack_left.append(curr.val)
    hashmap[curr.val] = curr
    curr = nxt

    return head

  • @mattk.9937
    @mattk.9937 2 วันที่ผ่านมา

    Could you just iterate through each linked list and append the values to a stack and then reverse sort the stack, popping off each item while making a new linked list?
    if not lists:
    return
    stack = []
    for node in lists:
    n = node
    while n:
    stack.append(n.val)
    n = n.next
    stack.sort(reverse=True)
    if not stack:
    return
    retnode = ListNode(stack.pop())
    go = retnode
    while stack:
    go.next = ListNode(stack.pop())
    go = go.next
    return retnode

  • @RakeshKumar-fu2hp
    @RakeshKumar-fu2hp 3 ปีที่แล้ว +2

    Thanks for the video. Very easy way to approach the problem. A minor question. Line # 17 does not work for me. I have to replace with mergeLists.append(self.mergeLists(l1, l2)). What is the difference in your way (without append) and my way ?

    • @hamoodhabibi7026
      @hamoodhabibi7026 3 ปีที่แล้ว +15

      He fixes it to your way later on in the video. It was a mistake.

  • @sheeesh404
    @sheeesh404 3 ปีที่แล้ว +5

    Why not use a maxheap?

  • @sijiexiang8677
    @sijiexiang8677 ปีที่แล้ว

    What is n here? total number of the nodes?

  • @AndyT-s2c
    @AndyT-s2c ปีที่แล้ว

    Great solution! There is a kind of a bug in your typescript solution but it technically runs anyway regardless of fixing it. For about an hour, I was wondering why it didn't work when I converted it to JavaScript. My while loop in the mergeList function had while not null for l1 and l2 instead of just while l1 and l2. It turns out that line 22 in your solution doesn't ever fire off the else statement, so l2 doesn't get set to None (or null if using another language). It runs because of the while l1 and l2 which accounts for undefined, which is what l2 could be in the case of the first test case with list of length 3. l2 gets declared but doesn't get defined because it never activates the else. Like if len of the lists was 3 and i = 2, it would be compare (2 + 1) > 3. To test it I even removed the else line and it still ran fine. You can fix it by changing it to if (i + 1) > lists.length - 1 to fix it. I would imagine this is also the case with the python solution.

    • @dtcx
      @dtcx ปีที่แล้ว

      Your comparison opertor should actually be "less than". e.g l2 = lists[i+1] if (i+1) < len(lists) else None. Because you want your index to be less than the length of the list. Otherwise you'll get "list out of range error"

  • @yitongxie6574
    @yitongxie6574 2 ปีที่แล้ว

    it didn't come up to me that this is exactly the merge sort, even though I noticed the pattern in the two merge problem

  • @chianlee1381
    @chianlee1381 ปีที่แล้ว

    From 2023.9.24 this solution will result in TLE, I am assuming the dummy node initialization and the merging of the two lists is much more inefficient than the priority queue. Any comments?

    • @vaisakhkusabhadran4222
      @vaisakhkusabhadran4222 ปีที่แล้ว

      No it doesn't fail. The code is passing all the test cases in LC 2023.12.29

    • @saadmomin820
      @saadmomin820 7 หลายเดือนก่อน

      Yeah it results TLE 2024.06.03

  • @ashkan.arabim
    @ashkan.arabim 5 หลายเดือนก่อน

    everyone suggesting a minheap solution is forgetting the fact that this solutions runs in O(1) memory. heap needs extra space.

  • @akshayar5993
    @akshayar5993 ปีที่แล้ว +1

    Can you also focus on the space complexity while solving a question please

  • @RM-xr8lq
    @RM-xr8lq ปีที่แล้ว +1

    divide and conquer is used to iterate on the O(nk) approach of merging one by one
    for each step, `n` nodes (all of them) are compared during the merge process -- O(n)
    at the end of each step, the number of lists that need to be merged has been halved
    therefore the number of iterations needed is the number of times you can halve `k` until you get to 1. that is `log k` (base 2). so each step will be done `log k` times -- O(log k)
    thus the time complexity is O(n log k)

    • @hagridhaired
      @hagridhaired ปีที่แล้ว

      what's the space complexity?

    • @Nebukanezzer
      @Nebukanezzer 8 หลายเดือนก่อน

      This is wrong, because each step doubles the length of n, so it scales with k.
      The final merge, for instance, is two lists of about (nk / 2) size. The complexity cannot be lower than one of the steps so it has to be at least nk.

    • @Nebukanezzer
      @Nebukanezzer 8 หลายเดือนก่อน +1

      The first merges, are all n steps, but there are k/2 of them.
      So that is also nk.
      There are log k steps in this whole process, so the whole complexity is nk log k.

  • @EquinoXReZ
    @EquinoXReZ ปีที่แล้ว

    Whats the space complexity?

    • @RM-xr8lq
      @RM-xr8lq ปีที่แล้ว

      O(k), `k` is number of linked lists
      auxiliary space is used in the `mergedLists` variable

  • @dannggg
    @dannggg 2 ปีที่แล้ว

    I solved this problem in like 15 minutes but was not efficient, fast but took a ton of memory. other problems that are medium and easy took me hours vs this hard problem. (im not even smart lol)

  • @gameplaysrules2023
    @gameplaysrules2023 7 หลายเดือนก่อน

    for me , an easy way to solve this problem was with a priority queue

  • @yousifsalam
    @yousifsalam 2 ปีที่แล้ว

    What's the point of line 18, the one that sets "lists" equal to "mergedLists"? :
    while len(lists) > 1:
    mergedLists = []
    for i in range(0, len(lists), 2):
    l1 = lists[i]
    l2 = lists[i + 1] if (i + 1) < len(lists) else None
    mergedLists.append(self.mergeList(l1, l2))
    lists = mergedLists
    return lists[0]

    • @JiaTanchun
      @JiaTanchun ปีที่แล้ว +1

      You can imagine lists is [[1],[2],[3],[4],[5],[6]],after first mergeList, the mergedLists becomes[[1,2],[3,4],[5,6]],then let this mergedLists become Lists again, so the “while len(Lists)>1:” is true again and you start a new loop, and then you do it again becomes[[1,2,3,4],[5,6]], then finally the List becomes[[1,2,3,4,5,6]],return Lists[0]

    • @evynsrefuge
      @evynsrefuge ปีที่แล้ว

      I was so confused by this because I didn't realize the number 2 in "for I in range" line meant how much to increment by and not that the range of i was only from 0 to 2 😭 I was like why is the for loop only executing once

    • @OwenWu-f9t
      @OwenWu-f9t ปีที่แล้ว

      @@evynsrefuge for I in range(starting, uptonotinaluding, stepsize):

  • @aaron10k
    @aaron10k 10 หลายเดือนก่อน

    there's a linear time solution using hash map

  • @deerajspritle2264
    @deerajspritle2264 ปีที่แล้ว

    def mergeKLists(lists):
    result = sum(lists, [])
    return sorted(result)

  • @TacoMaster07
    @TacoMaster07 11 หลายเดือนก่อน

    did u purposely pick those numbers to add up to 23?

  • @ismailsaleh3536
    @ismailsaleh3536 ปีที่แล้ว +1

    i cant understand why it is nlogk not n*k

    • @samarthtandale9121
      @samarthtandale9121 ปีที่แล้ว

      See, in each iteration we are traversing all the node and the total number of iterations is logk, that's why

  • @stunning-computer-99
    @stunning-computer-99 2 ปีที่แล้ว

    How come this one is considered as a divide and conquer ? can't wrap my head around

    • @nikhil_a01
      @nikhil_a01 2 ปีที่แล้ว

      We have k lists and we group them into k/2 pairs, then merge each pair. Now we have k/2 lists and we merged that into k/4 pairs. So we are dividing the number of lists in half each time.
      E.g. Say we have four lists. Merge list 1 and list 2 together, and merge list 3 and list 4 together. That gives us two merged lists. Now merge those two into a single result.

  • @WaldoTheWombat
    @WaldoTheWombat 2 ปีที่แล้ว

    Why is the approach showed in the video better than the following approach?
    dummy = ListNode()
    min_node = all_lists[0]
    while len(all_lists):
    i = 0
    while i < len(all_lists)
    if node is None:
    all_lists.pop(i)
    elif node.val < min_node.val:
    min_node = node
    i += 1
    dummy.next = min_node
    I don't see why it would be slower.

  • @yvonnelin1242
    @yvonnelin1242 2 ปีที่แล้ว

    Does anyone encounter a problem with this problem? I used the same code but it won't work in lc since the time exceeds

    • @Lambyyy
      @Lambyyy 2 ปีที่แล้ว

      It still works, you just have to make sure "l2 = lists[i+1] if (i+1) < len(lists) else None" is written out correctly, if you do it another way then it won't work. Otherwise you can use the heap method.

  • @adampajda3204
    @adampajda3204 5 หลายเดือนก่อน

    Recursive solution using mergeSort:
    class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    if not lists:
    return None
    return self.merge_sort(lists)[0]
    def merge_sort(self, nodes):
    if len(nodes) == 1:
    return nodes
    # print(nodes)
    middle = len(nodes) // 2
    nodes_left = self.merge_sort(nodes[0: middle])
    nodes_right = self.merge_sort(nodes[middle: len(nodes)])
    return self.merge(nodes_left[0], nodes_right[0])
    def merge(self, nodes_left, nodes_right):
    head = ListNode(-1)
    tail = head
    while nodes_left and nodes_right:
    if nodes_left.val

  • @halcyonramirez6469
    @halcyonramirez6469 ปีที่แล้ว

    the most straighforward intuitive approach is.
    just iterate through all the linked list and get their values append those values to a list.
    then sort.
    then just convert the combined sorted list to a linked list.
    there were no restrictions so its the simplest approach.
    that approach beats 80% of python submissions

    • @JLJConglomeration
      @JLJConglomeration ปีที่แล้ว +3

      your solution would be O(NK*log(NK)) time and O(NK) space where K is the # of lists and N is average length of lists. The optimal solution is O(NK*log(K)) time and O(K) space.
      It's important to use big O when analyzing optimal, interviewers do not care about Leetcode submission times

  • @yunfanzhou904
    @yunfanzhou904 2 ปีที่แล้ว

    I'm curious about the space complexity in this solution, since we are creating a new list called mergedLists in the while loop, so I think we will create logK lists, so space complexity should be logK, right?

    • @zdfdsfdsfdsfd
      @zdfdsfdsfdsfd 2 ปีที่แล้ว +3

      With Neetcode's implementation, mergedList is the only source of non-constant space complexity. As the length of lists (our input) increases, mergedList at its worst case stores k // 2 (O(k)) partially merged lists on the first pass of the merge step. Therefore, space complexity is O(k).
      O(log(k)) is how many passes the merge step will take to make lists length 1. On each pass, the helper merge two lists method takes O(n) where n is the combined length of the two lists. Therefore, time complexity is O(n*logk).

  • @g6negao
    @g6negao 5 หลายเดือนก่อน

    Using heapq
    class Solution:
    def mergeKLists(self, nList: List[Optional[ListNode]]) -> Optional[ListNode]:
    if len(nList) == 0: return None
    heap = []
    for i in range(len(nList)):
    if not nList[i]: continue
    heapq.heappush(heap, (nList[i].val, i, nList[i]))
    if len(heap) == 0: return None
    dummy = ListNode(0)
    d_node = dummy
    while heap:
    val, j , node = heapq.heappop(heap)
    d_node.next = node
    d_node = d_node.next
    if node.next:
    heapq.heappush(heap, (node.next.val, j, node.next))
    return dummy.next

  • @EranM
    @EranM 8 หลายเดือนก่อน

    I'm quite surprised you didn't use a heap :/

  • @Ben-pb7ct
    @Ben-pb7ct 2 ปีที่แล้ว

    👍

  • @vishalrautela4754
    @vishalrautela4754 2 ปีที่แล้ว

    Can any one explain why While len(lists)> 1 is used????

    • @OwenWu-f9t
      @OwenWu-f9t ปีที่แล้ว

      as long as your have more than one sublist in your lists input, you have to keep merging them together, so while len(lists) is not bigger than 1 meaning we have 1 or less lists, then we have our result and want to return the final list

  • @AustinCS
    @AustinCS 5 หลายเดือนก่อน

    I really question the runtime analysis here.

  • @omrajgure4553
    @omrajgure4553 2 ปีที่แล้ว

    awesome

  • @eba-pachi
    @eba-pachi 6 หลายเดือนก่อน

    this implementation is simpler and it keeps the same runtime complexity (tho it runs slower in leetcode)
    class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    res = None
    for l in lists:
    res = merge(res, l)
    return res

    • @hoixthegreat8359
      @hoixthegreat8359 5 หลายเดือนก่อน +2

      no it has a different runtime. it's literally the code equivalent of the example he went through at the start, and its O(nk).

    • @eba-pachi
      @eba-pachi 5 หลายเดือนก่อน

      @@hoixthegreat8359 you are correct, I found out later and forgot I did the comment

  • @VarunMittal-viralmutant
    @VarunMittal-viralmutant 2 ปีที่แล้ว

    Similar code using 'heapq' and iterators
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    def l_iter(self):
    _iter = self
    while _iter:
    yield _iter.val
    _iter = _iter.next
    ListNode.__iter__ = l_iter
    filtered_lists = filter(lambda x: x is not None, lists)
    list_head = list_tail = None
    counter = count(0)
    stack = []
    for l in filtered_lists:
    itr = iter(l)
    heapq.heappush(stack, (next(itr), next(counter), itr))
    while stack:
    try:
    val, _, itr = heapq.heappop(stack)
    if not list_head:
    list_head = list_tail = ListNode(val)
    else:
    list_tail.next = ListNode(val)
    list_tail = list_tail.next
    heapq.heappush(stack, (next(itr), next(counter), itr))
    except:
    pass
    return list_head

  • @keerthi.m
    @keerthi.m 2 ปีที่แล้ว

    Can I have the same question solution in Java?

  • @netraamrale8119
    @netraamrale8119 2 ปีที่แล้ว

    best

  • @xxxyy7452
    @xxxyy7452 ปีที่แล้ว

    learn leetcode in here to beat 100 liked question

  • @rohithbhandari7836
    @rohithbhandari7836 9 หลายเดือนก่อน

    Anyone getting type error for this solution

    • @bboy12890
      @bboy12890 8 หลายเดือนก่อน

      im getting index out of range error for l1 even though its only while len lists > 1 idk

  • @thecomm0nman597
    @thecomm0nman597 ปีที่แล้ว

    I think time complexity of 1st solution should be O(n2k) and for optimized solution O(nlognk).
    where n is size of array , k size of list.
    if I am wrong plz somebody help.
    Thanks in advance.