0

Following is the method I wrote for merging two Linked-Lists (MergeTwoLists(ListNode list1, ListNode list2)). Following is the entire program:

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LinkedList
{
     public class ListNode
    {
        public int val;
        public ListNode next;
        public ListNode(int val = 0, ListNode next = null)
        {
            this.val = val;
            this.next = next;
        }
     }


    internal class Program
    {
        static void Main(string[] args)
        {
            ListNode list1= new ListNode(1, new ListNode(2, new ListNode(4, null)));//Heap -> list1 is a pointer to an object on the heap (list1 pointer itself is on the stack).
            ListNode list2 = new ListNode(1, new ListNode(3, new ListNode(4, null)));//Heap -> list2 is a pointer to an object on the heap (list2 pointer itself is on the stack).
            ListNode list3 = MergeTwoLists(list1,list2);//list3 points to Heap. list3 is also a pointer to an object on the heap. And initially, that object on Heap = null. The list3 pointer (variable), is itself on the stack.
        }

        public static ListNode MergeTwoLists(ListNode list1, ListNode list2) //pass by Reference
        {
            if (list1 == null)
                return list2;
            else if (list2 == null)
                return list1;
            ListNode l3 = new ListNode(0,null),l1=list1,l2=list2;
            ListNode l4 = l3;
            while (list1 != null && list2 != null)//list1 = 1->2->4->null
            {                               //list2 = 1->3->4->null
                if (list1.val <= list2.val)
                {
                    l4.next = list1;
                    list1 = list1.next;//when we are first time in the while loop, list1 now = 2->4->null but l1 (which = list1) still = 1->2->4->null!!
                }
                else
                {
                    l4.next = list2;
                    list2 = list2.next;
                }
                l4 = l4.next;
            }
            l4.next=list1==null?list2:list1;
            return l3.next;
        }
    }
}

As shown above (as commented above), when we enter the while loop in the method MergeTwoLists the first time - list1 = list1.next. And list1 now = 2->4->null (list1 was 1->2->4->null originally). But, l1 = list1, so the changes in list1 should be reflected in l1. And l1 should now also = 2->4->null (initially, both l1 = list1 = 1->2->4->null). But, l1 now still = 1->2->4->null whereas list1 now = 2->4->null. Why is it so? Am I missing something?

Diagrammatically, what happens in the Heap and Stack (in my opinion): LinkedList object on HEAP, referenced by pointers (variables) on STACK

So, why this incompatible behavior that I am seeing? I will be grateful, if you can tell me what is happening in the HEAP and STACK, exactly.

Vikram Singh
  • 435
  • 2
  • 10
  • 1
    `list1` in `Main` cannot get modified by the method because it is not passed by `ref` – UnholySheep Jul 27 '22 at 19:48
  • 2
    TL;DR you are confused about the difference between a reference type and passing by reference. – Nick Bailey Jul 27 '22 at 19:51
  • @NickBailey, In my opinion, the LinkedList node is passed by reference (since it is a class - reference type). Am I correct? However, my question is about assigning one LinkedList to another. As I put in my problem statement, I assigned l1=list1. And list1=1->2->4->null. So, l1 also became 1->2->4->null. But, when I did list1=list1.next, list1 became 2->4->null. But l1 remained the same - 1->2->4->null. How is this possible - since both list1 and l1 variables are on the stack and point to the same LinkedList object on the heap? – Vikram Singh Jul 27 '22 at 20:10
  • 1
    @VikramSingh `list1 = list1.next` changes the reference stored in `list1` variable and for obvious reasons does not change reference stored in`l1`. – Guru Stron Jul 27 '22 at 20:24
  • @UnholySheep, I think you didn't catch the exact problem I am stating. My question is not about passing LinkedList from one method to another. My question is about how the LinkedList assignment is not working in the method - MergeTwoLists. As I put in my problem statement, I assigned l1=list1. And list1=1->2->4->null. So, l1 also became 1->2->4->null. But, when I did list1=list1.next, list1 became 2->4->null. But l1 remained the same - 1->2->4->null. How is this possible - since both list1 and l1 variables are on the stack and point to the same LinkedList object on the heap? – Vikram Singh Jul 27 '22 at 20:27
  • You aren't changing the `ListNode` object itself, you are changing what two separate variables are refering to. Both `l1` and `list1` are reference variables, they are refering to objects. Changing what one of them refers to has no effect on what the other refers to – UnholySheep Jul 27 '22 at 20:30
  • @GuruStron, thanks for catching the question, that I was actually asking! – Vikram Singh Jul 27 '22 at 20:36

2 Answers2

1

When you do var l1 = list1 in MergeTwoLists it makes both variable store on stack reference to the same heap location, but when you do list1=list1.next the reference stored in list1 changes, but the one in l1 doesn't (cause you have not reassigned it). If you have done something like list1.next = null then you will be able to see the change "propagated" to l1 also since ListNode is a reference type.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • This is basically correct, though there are a few key points worth clarifying: – Nick Bailey Jul 27 '22 at 21:09
  • @NickBailey, can you please mention the few points, that are worth clarifying? – Vikram Singh Jul 27 '22 at 21:27
  • Oh sorry, I think I forgot to press send: value types are not guarenteed yo be stack allocated and reference types are not guarenteed to be heap allocated. The runtime is free to violate those rules if it detects that it safely can and it would improve performance. – Nick Bailey Jul 29 '22 at 00:02
0

I did some more digging, to see what is exactly happening on the heap and the stack. For example:

1. var a = new Student();
2. var b = a;

The above code will result in the following on the heap and stack - enter image description here

But, now when we later do:

3. a = new Student();

The newly added code (at line 3.) will result in the following, on the heap and stack - HeapStack2

This is the same phenomenon that is happening, when we do:

  1. var l1 = list1 in MergeTwoLists.

Originally, list1 and l1 point to the same object on the heap (as shown in line 1. above). Later when we do list1 = list1.next, list1 variable (on the stack), now points to the next list1 object on the heap. But, l1 variable (on the stack), still points to the original list1 object on the heap (as shown above, in the Student() object example).

Vikram Singh
  • 435
  • 2
  • 10