Skip to content

Positional

Stacks, queues, and double ended queues only allow update operations that occur at one end of a sequence or the other. But these are too limiting - we want to be able to refer to elements anywhere in a sequence, and to perform arbitrary insertions and deletions.

Using indices is not preffered as: 1. linked lists don't allow access through indexing without traversing the list incrementally from the begening or end 2. it is not convenient to describe the location of an object purely by reference to the beginning or end of the data structure

Position Abstraction

Two ADTS: 1. Postion - A marker or token within the broader positional list. A position p is unaffected by changes elsewhere in a list; the only way in which a position becomes invalid is if an explicit command is issued to delete it. 2. PositionalList - serves as a container of sequential elements that allow positional access.

In the implementation below refer to the Doubly Linked List class defined in Linked Lists: Doubly Linked List

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class Position

    def __init__(self, container, node):
        self._container = container
        self._node = node

    def element(self):
        return self._node._element

    def __eq__(self, other):
        return type(other) is type(self) and other._node is self._node

    def __ne__(self, other):
        return not (self == other)

class PositionalList(DoublyLinkedBase):

    def _validate(self, position):
        if not isinstance(position, self.Position):
            raise TypeError('p must be proper Position type')
        if p._container is not self:
            raise ValueError('p does not belong to this container')
        if p._node._next is None:
            raise ValueError('p is no longer valid')
        return p._node

    def _make_position(self, node):
        if node is self._header or node is self._trailer:
            return None
        else:
            return self.Position(self, node)

    def first(self):
        return self._make_position(self._header._next)

    def last(self):
        return self._make_position(self._trailer._prev)

    def before(self, position):
        node = self._validate(position)
        return self._make_position(node._prev)

    def after(self, position):
        node = self._validate(position)
        return self._make_position(node._next)

    def __iter__(self):
        cursor = self.first()
        while cursor is not None:
            yield cursor.element()
            cursor = self.after(cursor)

    def _insert_between(self, element, predecessor, successor):
        node = super()._insert_between(element, predecessor, successor)
        return self._make_position(node)

    def add_first(self, element):
        return self._insert_between(element, self._header, self._header._next)

    def add_last(self, element):
        return self._insert_between(element, self._trailer._prev, self._trailer)

    def add_before(self, position, element):
        original = self._validate(position)
        return self._insert_between(element, original._prev, original)

    def add_after(self, position, element):
        original = self._validate(position)
        return self._insert_between(element, original, original._next)

    def delete(self, postion):
        original = self._validate(position)
        return self._delete_node(original)

    def replace(self, position, element):
        original = self._validate(position)
        old_value = original._element
        original._element = element
        return old_value

Sorting a Positional List

We can implement Insertion Sort for a PositionalList.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def insertion_sort(pos_list):
    # no need to sort an list of 1
    if len(pos_list) > 1:

        marker = pos_list.first()

        while marker != pos_list.last():
            # get the next item to compare against
            pivot = pos_list.after(marker)
            value = pivot.element()
            # pivot already sorted
            if value > marker.element():
                # pivot becomes the leftmost new marker
                marker = pivot
            # need to sort pivot 
            else:
                # find leftmost item greater than value
                walk = marker
                while walk != pos_list.first() and pos_list.before(walk).element() > value:
                    walk = pos_list.before(walk)

                pos_list.delete(pivot)
                # reinsert value before walk
                pos_list.add_before(walk, value)

References


  1. Skiena, Steven S.. (2008). The Algorithm Design Manual, 2nd ed. (2). : Springer Publishing Company. 

  2. Goodrich, M. T., Tamassia, R., & Goldwasser, M. H. (2013). Data Structures and Algorithms in Python (1st ed.). Wiley Publishing. 

  3. Cormen, T. H., Leiserson, C. E., & Rivest, R. L. (1990). Introduction to algorithms. Cambridge, Mass. : New York, MIT Press.