Skip to main content
Engineering LibreTexts

1.2: Building a Chain from Nodes

  • Page ID
    49259
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Although the node structure is simple, it actually allows us to compute things that we couldn't have computed with just fixed-size integers alone.

    But first, we'll look at a program that doesn't need to use nodes. The following program will read in (from an input stream; which can either be from the user or a file) a series of numbers until the end-of-file is reached and then output what the largest number is and the average of all numbers:

    program(input-stream in, output-stream out)
      let total := 0
      let count := 0
      let largest := 
      
      while has-next-integer(in):
        let i := read-integer(in)
        total := total + i
        count := count + 1
        largest := max(largest, i)
      repeat
       
      println out "Maximum: " largest
      
      if count != 0:
        println out "Average: " (total / count)
      fi
    end
    

    But now consider solving a similar task: read in a series of numbers until the end-of-file is reached, and output the largest number and the average of all numbers that evenly divide the largest number. This problem is different because it's possible the largest number will be the last one entered: if we are to compute the average of all numbers that divide that number, we'll need to somehow remember all of them. We could use variables to remember the previous numbers, but variables would only help us solve the problem when there aren't too many numbers entered.

    For example, suppose we were to give ourselves 200 variables to hold the state input by the user. And further suppose that each of the 200 variables had 64-bits. Even if we were very clever with our program, it could only compute results for \(2^{64\cdot 200}\) different types of input. While this is a very large number of combinations, a list of 300 64-bit numbers would require even more combinations to be properly encoded. (In general, the problem is said to require linear space. All programs that need only a finite number of variables can be solved in constant space.)

    Instead of building-in limitations that complicate coding (such as having only a constant number of variables), we can use the properties of the node abstraction to allow us to remember as many numbers as our computer can hold:

    program(input-stream in, output-stream out)
      let largest := 
      let nodes := null
      
      while has-next-integer(in):
        let i := read-integer(in)
        nodes := make-node(i, nodes) // contain the value i,
                                     // and remember the previous numbers too
        largest := max(largest, i)
      repeat
      println out "Maximum: " largest
    
      // now compute the averages of all factors of largest
      let total := 0
      let count := 0
      while nodes != null:
        let j := get-value(nodes)
        if j divides largest:
          total := total + j
          count := count + 1
        fi
        nodes := get-next(nodes)
      repeat
      if count != 0:
        println out "Average: " (total / count)
      fi
    end
    

    Above, if n integers are successfully read there will be n calls made to make-node. This will require n nodes to be made (which require enough space to hold the value and next fields of each node, plus internal memory management overhead), so the memory requirements will be on the order of \(O(n)\). Similarly, we construct this chain of nodes and then iterate over the chain again, which will require \(O(n)\) steps to make the chain, and then another \(O(n)\) steps to iterate over it.

    Note that when we iterate the numbers in the chain, we are actually looking at them in reverse order. For example, assume the numbers input to our program are 4, 7, 6, 30, and 15. After EOF is reached, the nodes chain will look like this:

    DataStructuresLinkedListofN.png
    Figure \(\PageIndex{1}\) (via Wikibooks)

    Such chains are more commonly referred to as linked-lists. However, we generally prefer to think in terms of lists or sequences, which aren't as low-level: the linking concept is just an implementation detail. While a list can be made with a chain, in this book we cover several other ways to make a list. For the moment, we care more about the abstraction capabilities of the node than we do about one of the ways it is used.

    The above algorithm only uses the make-node, get-value, and get-next functions. If we use set-next we can change the algorithm to generate the chain so that it keeps the original ordering (instead of reversing it).

    program (input-stream in, output-stream out)
      let largest := 
      let nodes := null
      let tail_node := null
      
      while has-next-integer (in):
        let i := read-integer (in)
        if (nodes == null)
          nodes := make-node(i, null) // construct first node in the list
          tail_node := nodes //there is one node in the list=> first and last are the same
        else
          tail_node := set-next (tail_node, make-node (i, null)) // append new node to the end of the list
        largest := max(largest, i)
      repeat
      println out "Maximum: " largest
    
      // now compute the averages of all factors of largest
      let total := 0
      let count := 0
      while nodes != null:
        let j := get-value(nodes)
        if j divides largest:
          total := total + j
          count := count + 1
        fi
        nodes := get-next(nodes)
      repeat
      if count != 0:
        println out "Average: " (total / count)
      fi
    end
    

    This page titled 1.2: Building a Chain from Nodes is shared under a CC BY-SA license and was authored, remixed, and/or curated by Wikibooks - Data Structures (Wikipedia) .

    • Was this article helpful?