Lists

These notes cover lists.

  1. Singly-Linked Lists
  2. Problems
    1. List to Array
    2. Sum List
    3. Sequential Search
    4. ith Element
    5. Reverse List
    6. Zipper List
    7. List Merge
    8. Univalue List
    9. Remove Node

Singly-Linked Lists

Here is a basic linked list, storing integers:

Each box is a node with a pointer to the next node (called the node's pointee), and the last node (above, the box containing 1) has a null pointee. We call the first node the head and the last node the tail. Linked lists have some notion of "ordering" in the sense that each node points to the next. They do not, however, have any inherent indices, as arrays do. Accordingly (and this is true for other connected data structures), the data stored within a linked list are spread across memory. As such, they do not perform nearly as well as arrays when it comes to both caching and read time. In particular, lists do not have constant time access — we must traverse the list to read.

With lists, however, we can insert in O(1){\bigO{1}} (assuming a correct implementation). We set create a new node p{p} containing the data 6, then traverse the list until we reach the node just before the point of insertion:

Then we have p{p} point to node-5's pointee ({5}p{\set{5}\to p}), followed by node-5 changing its pointee to p{p} (p{7}{p \to \set{7}}).

For the puzzles that follow, solutions use the following structure for a node:

node definition. Where comparable{\df{comparable}} is a data type such that =,{=}, <,{\lt,} and >{\gt} return either true or false, and node{\df{node}^*} is a node pointer type, a node{\df{node}} is a product type comparable×node,{\df{comparable} \times \df{node}^*,} where comparablevalue{\df{comparable} \bij \tx{value}} and nodenext.{\df{node}^* \bij \tx{next}.}

  • structure node contains{{\textbf{structure}}\df{ node } \textbf{contains}}
    1. comparable  value{\df{comparable}~~\tx{value}}
    2. node  next{\df{node}^*~~\tx{next}}
  • end{\textbf{end}}

Problems

List to Array

Given the head of a linked list L,{L,} write an algorithm that returns the values stored in L{L} as an array.

This problem asks for a simple traversal. As we're asked to traverse the entire list, the approach below has a time complexity of Θ(n),{\bigTheta{n},} and a space complexity of Θ(n).{\bigTheta{n}.}

list-to-array

  • Input: the head h{h} of a linked list L.{L.}
  • Output: an array A,{A,} containing the values stored in L.{L.}
  1. init
    1. ph{\let{p}{h}}
    2. Anew empty array{\let{A}{\df{new empty array}}}
  2. while p{p \neq \nil} start loop body
    1. Ap[value]{A \push p \ix{\tx{value}}}
    2. pp[next]{\let{p}{p \ix{\tx{next}}}}
    3. goto(line 3){\goto{3}} end loop body
  3. return A{A}

We can also implement this recursively:

recursive-list-to-array

  • Input: the head of a linked list L,{L,} h.{h.}
  • Output: an array A,{A,} containing the values stored in L.{L.}
  1. init
    1. ph{\let{p}{h}}
    2. Anew empty array{\let{A}{\df{new empty array}}}
  2. function traverse(p):node{\df{traverse}(p): \df{node}^* \mapsto \nil}
    1. if p={p = \nil} then return {\nil}
    2. else
      1. Ap[value]{A \push p \ix{\tx{value}}}
      2. traverse(p[next]){\df{traverse}(p\ix{\tx{next}})}
  3. traverse(p){\df{traverse}(p)}
  4. return A{A}

With the recursive approach, it's important to avoid situations of pass-by-value. For some languages (e.g., JavaScript), passing the entire array A{A} (rather than a reference or pointer to A{A}) in a recursive call requires copying each value in A{A} to a new array A.{A'.} This results in a time complexity of O(n2).{\bigO{n^2}.}

Sum List

Given the head h{h} of a linked list containing integers, write an algorithm that returns the total sum of all values in the linked list.

Like list to array, computing the sum can be done iteratively:

listsum

  • Input: h,{h,} the head of a linked list L{L} containing integers.
  • Output: S,{S,} the sum total of each integer in L.{L.}
  1. init
    1. ph{\let{p}{h}}
    2. S0{\let{S}{0}}
  2. while p{p \neq \nil} begin loop body
    1. sump[value]{\let{sum}{p \ix{\tx{value}}}}
    2. pp[next]{\let{p}{p \ix{\tx{next}}}}
    3. goto(line 3){\goto{3}} end loop body
  3. return S{S}

This algorithm has a time complexity of O(n),{\bigO{n},} and a space complexity of O(1).{\bigO{1}.} A recursive implementation would also run on O(n){\bigO{n}} time, but with the added cost of O(n){\bigO{n}} space complexity, due to the call stack.

listsum

  • Input: h,{h,} the head of a linked list L{L} containing integers.
  • Output: S,{S,} the sum total of each integer in L.{L.}
  1. if h={h = \nil} then return 0{0}
  2. else h[value]+listsum(h[next]){\rw{h}{\tx{value}} + \df{listsum}(\rw{h}{\tx{next}})}

Given the head of a linked list and a target t,{t,} write an algorithm that returns true (or 1) if t{t} is in the linked list, and false (or 0) otherwise. The target t{t} is of a type where ={=} is meaningful.

This is another problem handled easily by traversal. The algorithm below has a time complexity of order O(n){\bigO{n}} — since we must traverse the list — and the space complexity of order O(1).{\bigO{1}.}

seqfind

  • Input: h,{h,} the head of a linked list L,{L,} and t,{t,} a target.
  • Output: true if tL,{t \in L,} false otherwise.
  1. init ph{\let{p}{h}}
  2. while p{p \neq \nil} begin loop body
    1. if p[value]=t{\rw{p}{\tx{value}}=t} then return true{\df{true}}
    2. pp[next]{\let{p}{p \ix{\tx{next}}}}
    3. goto(line 1){\goto{1}} end loop body
  3. return false{\df{false}}

As usual, we can also implement this recursively:

seqfind

  • Input: h,{h,} the head of a linked list L,{L,} and t,{t,} a target.
  • Output: true if tL,{t \in L,} false otherwise.
  1. if h={h = \nil} return false{\df{false}}
  2. else if h[value]=t{\rw{h}{\tx{value}} = t} then return true{\df{true}}
  3. else seqfind(h[next]){\df{seqfind}(\rw{h}{\tx{next}})}

ith Element

Given the head of a linked list h{h} and index IN,{I \in \nat,} write an algorithm that returns the list element at the specified index. If there is no such element, return null.

There are many ways to go about this problem, some of which are tempting but wrong. A common mistake is an algorithm like the following:

getith

  • Input: the head of a linked list h{h} and an index I{I}
  • Output: the element at index I{I}
  1. init
    1. ph{\let{p}{h}}
    2. i0{\let{i}{0}}
  2. while i<I{i \lt I} begin loop body
    1. pp[value]{\let{p}{p\ix{\tx{value}}}}
    2. +(i,1){+(i,1)}
    3. goto(line 3){\goto{3}} end loop body
  3. return p[value]{p\ix{\tx{value}}}

The problem with this approach is that there's no guard against p=.{p = \nil.} In the event that occurs — which is well within reason to expect — the very last line will return an error (hopefully; not all compilers or transpilers are so forgiving about dereferencing {\nil}). A correct approach would look like the following:

getith

  • Input: the head of a linked list h{h} and an index I{I}
  • Output: the element at index I{I}
  1. init
    1. ph{\let{p}{h}}
    2. i0{\let{i}{0}}
  2. while p{p \neq \nil} begin loop body
    1. if i=I{i = I} return p[value]{p\ix{\tx{value}}}
    2. pp[next]{\let{p}{\rw{p}{\tx{next}}}}
    3. +(i,1){+(i,1)}
    4. goto(line 3){\goto{3}} end loop body
  3. return {\nil}

This algorithm has a space complexity of order O(1){\bigO{1}} and a time complexity of order O(n).{\bigO{n}.}

getith

  • Input: the head of a linked list h{h} and an index I{I}
  • Output: the element at index I{I}
  1. if h={h = \nil} return {\nil}
  2. else if I=0{I = 0} return h[value]{h\ix{\tx{value}}}
  3. else return getith(h[next],(I,1)){\df{getith}(h\ix{\tx{next}},-(I,1))}

The recursive solution has a time complexity of O(n),{\bigO{n},} but unlike the iterative solution, has a space complexity of O(n).{\bigO{n}.}

Reverse List

Given the head h{h} of a linked list L,{L,} write an algorithm that returns L{L} with its nodes reversed.

Before we reverse L,{L,} we return {\nil} immediately if h=.{h = \nil.} Otherwise, we continue. Suppose the list were:

We begin with three pointers, p{p} (for previous), c{c} (for current), and n{n} (for next). Initially, p{p} points to nothing, c{c} points to h,{h,} and n{n} points to h[next].{h\ix{\tx{next}}.} Then, so long as n:{n \neq \nil:} (1) Assign p{p} to c{c}'s next (make current point to previous). (2) Assign c{c} to p{p} (move previous to current). (3) Assign c{c} to n{n} (move current to next). And (4) Assign n{n}'s next to n{n} (move n{n} forward).

c[next]pc n        p1 23456pcpc n        1 23456cnp cn        1 23456nn[next]p c n      1 23456c[next]pp c n      12 3456pc  pc n      12 3456cn  p cn      12 3456nn[next]  p c n    12 3456c[next]p  p c n    123 456pc    pc n    123 456cn    p cn    123 456nn[next]    p c n  123 456c[next]p    p c n  1234 56pc      pc n  1234 56cn      p cn  1234 56nn[next]      p c n1234 56c[next]p      p c n12345 6pc        pc n12345 6cn        p cn12345 6nn[next]        p cn12345 6 \begin{array}{:c:c:} {\let{\rw{c}{\tx{next}}}{p}}& {\begin{matrix} &{c}&~&{n}&~&{~}&~&{~}&~&{~}&~&{~}& \\ p\gets&\boxed{1}&~&\boxed{2}&\to&\boxed{3}&\to&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{p}{c}}& {\begin{matrix} &{pc}&~&{n}&~&{~}&~&{~}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&~&\boxed{2}&\to&\boxed{3}&\to&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{c}{n}}& {\begin{matrix} &{p}&~&{cn}&~&{~}&~&{~}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&~&\boxed{2}&\to&\boxed{3}&\to&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{n}{\rw{n}{\tx{next}}}}& {\begin{matrix} &{p}&~&{c}&~&{n}&~&{~}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&~&\boxed{2}&\to&\boxed{3}&\to&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{\rw{c}{\tx{next}}}{p}}& {\begin{matrix} &{p}&~&{c}&~&{n}&~&{~}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&~&\boxed{3}&\to&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{p}{c}}& {\begin{matrix} &{~}&~&{pc}&~&{n}&~&{~}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&~&\boxed{3}&\to&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{c}{n}}& {\begin{matrix} &{~}&~&{p}&~&{cn}&~&{~}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&~&\boxed{3}&\to&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{n}{\rw{n}{\tx{next}}}}& {\begin{matrix} &{~}&~&{p}&~&{c}&~&{n}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&~&\boxed{3}&\to&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{\rw{c}{\tx{next}}}{p}}& {\begin{matrix} &{~}&~&{p}&~&{c}&~&{n}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&~&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{p}{c}}& {\begin{matrix} &{~}&~&{~}&~&{pc}&~&{n}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&~&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{c}{n}}& {\begin{matrix} &{~}&~&{~}&~&{p}&~&{cn}&~&{~}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&~&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{n}{\rw{n}{\tx{next}}}}& {\begin{matrix} &{~}&~&{~}&~&{p}&~&{c}&~&{n}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&~&\boxed{4}&\to&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{\rw{c}{\tx{next}}}{p}}& {\begin{matrix} &{~}&~&{~}&~&{p}&~&{c}&~&{n}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&\gets&\boxed{4}&~&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{p}{c}}& {\begin{matrix} &{~}&~&{~}&~&{~}&~&{pc}&~&{n}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&\gets&\boxed{4}&~&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{c}{n}}& {\begin{matrix} &{~}&~&{~}&~&{~}&~&{p}&~&{cn}&~&{~}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&\gets&\boxed{4}&~&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{n}{\rw{n}{\tx{next}}}}& {\begin{matrix} &{~}&~&{~}&~&{~}&~&{p}&~&{c}&~&{n}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&\gets&\boxed{4}&~&\boxed{5}&\to&\boxed{6}& \\ \end{matrix}} \\ {\let{\rw{c}{\tx{next}}}{p}}& {\begin{matrix} &{~}&~&{~}&~&{~}&~&{p}&~&{c}&~&{n}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&\gets&\boxed{4}&\gets&\boxed{5}&~&\boxed{6}& \\ \end{matrix}} \\ {\let{p}{c}}& {\begin{matrix} &{~}&~&{~}&~&{~}&~&{~}&~&{pc}&~&{n}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&\gets&\boxed{4}&\gets&\boxed{5}&~&\boxed{6}& \\ \end{matrix}} \\ {\let{c}{n}}& {\begin{matrix} &{~}&~&{~}&~&{~}&~&{~}&~&{p}&~&{cn}& \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&\gets&\boxed{4}&\gets&\boxed{5}&~&\boxed{6}& \\ \end{matrix}} \\ {\let{n}{\rw{n}{\tx{next}}}}& {\begin{matrix} &{~}&~&{~}&~&{~}&~&{~}&~&{p}&~&{c}&n \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&\gets&\boxed{4}&\gets&\boxed{5}&~&\boxed{6}& \\ \end{matrix}} \\ \end{array}

Once we've gone through the entire loop, all that's left is c[next]p:{\let{c\ix{\tx{next}}}{p}:}

        p cn123456 {\begin{matrix} &{~}&~&{~}&~&{~}&~&{~}&~&{p}&~&{c}&n \\ \nil\gets&\boxed{1}&\gets&\boxed{2}&\gets&\boxed{3}&\gets&\boxed{4}&\gets&\boxed{5}&\gets&\boxed{6}& \\ \end{matrix}} \\

The iterative solution has a time complexity of order O(n){\bigO{n}} and a space complexity of O(1).{\bigO{1}.}

reverse

  • Input: h,{h,} the head of a linked list L{L}
  • Output h,{h,} the head of a reversed L{L}
  1. if h={h = \nil} return h{h}
  2. else
    1. p{\let{p}{\nil}}
    2. ch{\let{c}{h}}
    3. nn[next]{\let{n}{\rw{n}{\tx{next}}}}
    4. while n{n \neq \nil}
      1. c[next]p{\let{c\ix{\tx{next}}}{p}}
      2. pc{\let{p}{c}}
      3. cn{\let{c}{n}}
      4. nn[next]{\let{n}{n\ix{\tx{next}}}}
      5. goto(line 5){\goto{5}}
    5. c[next]p{\let{c\ix{\tx{next}}}{p}}
    6. hc{\let{h}{c}}
    7. return h{h}

A recursive approach is much more concise:

reverse

  • Input: h,{h,} the head of a linked list L{L}
  • Output h,{h,} the head of a reversed L{L}
  1. function f(h,cdefault ){f(h,\let{c}{\df{default}~\nil})}
    1. if h={h = \nil} return c{c}
    2. nh[next]{\let{n}{h\ix{\tx{next}}}}
    3. h[next]c{\let{h\ix{\tx{next}}}{c}}
    4. f(n,h){f(n,h)}

The recursive approach has a time complexity of order O(n),{\bigO{n},} but while it is noticeably simpler, it does have a space complexity of order O(n).{\bigO{n}.}

Zipper List

Given two list heads a{a} and b,{b,} write an algorithm that zips their respective lists: The two lists merged into single linked list, where their nodes alternate. Should one be longer than the other, the final list must terminate with the longer's remaining nodes. Return the head of the zipped list.

ziplist

  • Input: two list heads H1{H_1} and H2.{H_2.}
  • Output: a zipped list h.{h.}
  1. init
    1. headH1{\let{head}{H_1}}
    2. tailhead{\let{tail}{head}}
    3. p1H1[next]{\let{p_1}{\rw{H_1}{\tx{next}}}}
    4. p2H2{\let{p_2}{H_2}}
    5. step0{\let{step}{0}}
  2. while (p1p2){(p_1 \neq \nil \land p_2 \neq \nil)} begin loop body
    1. if (step rem 2)=0{(step \rem 2) = 0}
      1. tail[next]p2{\let{\rw{tail}{\tx{next}}}{p_2}}
      2. p2p2[next]{\let{p_2}{\rw{p_2}{\tx{next}}}}
    2. else
      1. t[next]p1{\let{\rw{t}{\tx{next}}}{p_1}}
      2. p1p1[next]{\let{p_1}{\rw{p_1}{\tx{next}}}}
    3. tailtail[next]{\let{tail}{\rw{tail}{\tx{next}}}}
    4. +(step,1){+(step,1)}
    5. goto(line 6){\goto{6}} end loop body
  3. if p1{p_1 \neq \nil} then tail[next]p1{\let{\rw{tail}{\tx{next}}}{p_1}}
  4. if p2{p_2 \neq \nil} then tail[next]p2{\let{\rw{tail}{\tx{next}}}{p_2}}
  5. return head{head}

Suppose a{a} and b{b} correspond to the following lists.


We're asked to construct the list:

It's easier to view this problem in terms of sets. We're given two posets:

a={3,7,9,11,13}   b={2,4,6}. a=\set{3,7,9,11,13} ~~~ b=\set{2,4,6}.

We then have two functions f{f} and g.{g.} The former returns a current element in a,{a,} the latter, b.{b.} Because the final list is an alternates, we call f{f} and g{g} in alternum:

327469f(a)g(b)f(a)g(b)f(a)g(b). \ax{ 3 & 2 & 7 & 4 & 6 & 9 \\ f(a) & g(b) & f(a) & g(b) & f(a) & g(b) \\ }.

Once this is done, we append the elements of whatever remains, yielding the list:

This algorithm's time complexity is of order O(min{n,m}),{\bigO{\min\set{n,m}},} where n{n} and m{m} are the lengths of the two lists respectively. The use of min{\min} stems from iterating only in so far as the shortest list.

A recursive approach has a time complexity of order O(n),{\bigO{n},} and a space complexity of order O(n).{\bigO{n}.}

ziplist

  • Input: two list heads H1{H_1} and H2.{H_2.}
  • Output: a zipped list h.{h.}
  1. if (H1=)(H2=){(H_1 = \nil) \land (H_2 = \nil)} return {\nil}
  2. if (H1=){(H_1 = \nil)} return H2{H_2}
  3. if (H2=){(H_2 = \nil)} return H1{H_1}
  4. next1=H1[next]{\let{next1}=\rw{H_1}{\tx{next}}}
  5. next2=H2[next]{\let{next2}=\rw{H_2}{\tx{next}}}
  6. H1[next]H2{\let{\rw{H_1}{\tx{next}}}{H_2}}
  7. H2[next]ziplist(next1, next2){\let{\rw{H_2}{\tx{next}}}{\df{ziplist}(next1,~next2)}}
  8. return H1{H_1}

List Merge

Let H1{H_1} and H2{H_2} be the heads of two nonempty lists L1=(a0,a1,a2,,am1){L_1=(a_0,a_1,a_2,\ldots,a_{m-1})} and L2=(b0,b1,b2,,bm1){L_2=(b_0,b_1,b_2,\ldots,b_{m-1})} respectively, where L1{L_1} has a length m{m} and L2{L_2} has a length n,{n,} not necessarily equal. The elements of L1{L_1} and L2{L_2} are integers, sorted in increasing order. Write an algorithm that merges L1{L_1} and L2,{L_2,} the two lists together into single sorted linked list L3,{L_3,} whose elements are the elements of L1{L_1} and L2{L_2} sorted in increasing order. The algorithm must merge-in-place and the head of L3.{L_3.}

This algorithm has a time complexity of order O(min{n,m}){\bigO{\min\set{n,m}}} and a space complexity of order O(1).{\bigO{1}.}

sortedmerge

  • Input: a list head H1{H_1} and a list head H2,{H_2,} wherein elements are sorted in increasing order.
  • Output: the head of a list comprising nodes of H1{H_1} and H2,{H_2,} wherein elements are sorted in increasing order.
  1. init
    1. Dnew node{\let{D}{\df{new node}}}
    2. tD{\let{t}{D}}
    3. if H1[next]>H2[next]{\rw{H_1}{\tx{next}} \gt \rw{H_2}{\tx{next}}}
      1. then t[next]H1[next]{\let{\rw{t}{\tx{next}}}{\rw{H_1}{\tx{next}}}}
    4. else
      1. t[next]H2[next]{\let{\rw{t}{\tx{next}}}{\rw{H_2}{\tx{next}}}}
    5. aH1{\let{a}{H_1}}
    6. bH2{\let{b}{H_2}}
  2. while (t[next]){(t\ix{\tx{next}}\neq\nil)}
    1. if (a[value]<b[value]){(a\ix{\tx{value}} \lt b\ix{\tx{value}})} then
      1. t[next]a{\let{t\ix{\tx{next}}}{a}}
      2. aa[next]{\let{a}{a\ix{\tx{next}}}}
    2. else
      1. t[next]b{\let{t\ix{\tx{next}}}{b}}
      2. bb[next]{\let{b}{b\ix{\tx{next}}}}
    3. goto(line 9){\goto{9}}
  3. if a{a \neq \nil} then t[next]a{\let{\rw{t}{\tx{next}}}{a}}
  4. if b{b \neq \nil} then t[next]b{\let{\rw{t}{\tx{next}}}{b}}
  5. return D[next]{\rw{D}{\tx{next}}}
merge list

Univalue List

Given an integer list head h,{h,} write an algorithm that returns true if the list contains exactly one unique value (i.e., all the list's nodes store the same integer), and false otherwise. The list is guaranteed to be non-empty.

The approach here is fairly straightforward. We set initialize a pointer p{p} with the head h,{h,} then iterate through the list. If the data stored in p{p}'s pointee is not equal to the data stored in h{h}'s pointee (the first integer in the list), then we immediately return false, as there are multiple unique values. This algorithm has a time complexity of order O(n),{\bigO{n},} and space complexity of order O(1).{\bigO{1}.}

univalue

  • Input: the head of a list h.{h.}
  • Output: a Boolean result.{result.}
  1. init
    1. ph{\let{p}{h}}
  2. while p{p \neq \nil} begin loop body
    1. if p[value]h[value]{\rw{p}{\tx{value}}\neq\rw{h}{\tx{value}}} then
      1. return false{\df{false}}
    2. pp[next]{\let{p}{\rw{p}{\tx{next}}}}
    3. goto(line 2){\goto{2}} end loop body
  3. return true{\df{true}}

Remove Node

Let h{h} be a head of a linked list L,{L,} and t{t} an integer. Write an algorithm that deletes the L{L} node containing t{t} in place, and returns h.{h.} If there are multiple instances of t,{t,} remove only the first instance.

This problem is conducive to a three-pointer approach. We use three pointers, t{t} (a tailer), p{p} (a passenger), and d{d} (a driver), where d{d} is always ahead of p,{p,} and p{p} is always ahead of t.{t.} Initially, t{t} points to ,{\nil,} p{p} points to the head h,{h,} and d{d} points to h[next]{h\ix{\tx{next}}} (the head's next pointer). Once established, we traverse, so long as p[value]{p\ix{\tx{value}}} isn't equal to the target value t.{t.} There are two possible cases with this approach. First, the target value could be stored in h,{h,} in which case we never transition from our initial state:

t{t}p{p}d{d}
1{1 \mapsto}2{2 \mapsto}3{3 \mapsto}4{4 \mapsto\nil}

The second case is everything else — where we do transition states:

t{t}p{p}d{d}
1{1 \mapsto}2{2 \mapsto}3{3 \mapsto}4{4 \mapsto\nil}

In the first case, t{t} is nothing more than a null pointer, so it isn't of any use. This causes us to branch at the end of the loop. The algorithm below has a time complexity of order O(n),{\bigO{n},} and a space complexity of O(1).{\bigO{1}.}

remove-node

  • Input: a list head h.{\h.}
  • Output: the list head h.{\h.}
  1. init
    1. t{\let{t}{\nil}}
    2. ph{\let{p}{\h}}
    3. dh[next]{\let{d}{\h{\ix{\tx{next}}}}}
  2. while p[value]{\rw{p}{\tx{value}}\neq \nil}
    1. tp{\let{t}{p}}
    2. pd{\let{p}{d}}
    3. dd[next]{\let{d}{d\ix{\tx{next}}}}
    4. goto(line 4){\goto{4}}
  3. if t={t = \nil} then
    1. hd{\let{\h}{d}}
  4. else
    1. t[next]d{\let{\rw{t}{\tx{next}}}{d}}
    2. p{\let{p}{\nil}}
  5. return h{\h}