Steven S. Skiena
Search, Insert, Delete
There are three fundamental operations we need for any database:
We will see a wide variety of different implementation of these operations over the course of the semester.
How would you implement these using an array?
With linked lists, we can creating arbitrarily large structures, and never have to move any items.
Most of these operations should be pretty simple now that you understand pointers!
Searching in a Linked List
Procedure Search(head:pointer, key:item):pointer; Var p:pointer; found:boolean; Begin found:=false; p:=head; While (p # NIL) AND (not found) Do Begin If (p^.info = key) then found = true; Else p = p^.next; End; return p; END;
Search performs better when the item is near the front of the list than the back.
What happens when the item isn't found?
Insertion into a Linked List
The easiest way to insert a new node p into a linked list is to insert it at the front of the list:
p^.next = front; front = p;
To maintain lists in sorted order, however, we will want to insert a node between the two appropriate nodes. This means that as we traverse the list we must keep pointers to both the current node and the previous node.
MODULE Intlist; (*16.07.94. RM, LB*) (* Implementation of sorted integer lists. *) REVEAL (*reveal inner structure of T*) T = BRANDED REF RECORD key: INTEGER; (*key value*) next: T := NIL; (*pointer to next element*) END; (*T*) PROCEDURE Create(): T = (* returns a new, empty list *) BEGIN RETURN NIL; (*creation is trivial; empty list is NIL*) END Create; PROCEDURE Insert(VAR list: T; value:INTEGER) = (* inserts new element in list and maintains order *) VAR current, previous: T; new: T := NEW(T, key:= value); (*create new element*) BEGIN IF list = NIL THEN list:= new (*first element*) ELSIF value < list.key THEN (*insert at beginning*) new.next:= list; list:= new; ELSE (*find position for insertion*) current:= list; previous:= current; WHILE (current # NIL) AND (current.key <= value) DO previous:= current; (*previous hobbles after*) current:= current.next; END; (*after the loop previous points to the insertion point*) new.next:= current; (*current = NIL if insertion point is the end*) previous.next:= new; (*insert new element*) END; (*IF list = NIL*) END Insert;
Make sure you understand where these cases come from and can verify why all of them work correct.
Deletion of a Node
To delete a node from a singly linked-list, we must have pointers to both the node-to-die and the previous node, so we can reconnect the rest of the list.
PROCEDURE Remove(VAR list: T; value:INTEGER; VAR found: BOOLEAN) = (* deletes (first) element with value from sorted list, or returns false in found if the element was not found *) VAR current, previous: T; BEGIN IF list = NIL THEN found:= FALSE ELSE (*start search*) current:= list; previous:= current; WHILE (current # NIL) AND (current.key # value) DO previous:= current; (*previous hobbles after*) current:= current.next; END; (*holds: current = NIL or current.key = value, but not both*) IF current = NIL THEN found:= FALSE (*value not found*) ELSE found:= TRUE; (*value found*) IF current = list THEN list:= current.next (*element found at beginning*) ELSE previous.next:= current.next END; END; (*IF current = NIL*) END; (*IF list = NIL*) END Remove;
Passing Procedures as Arguments
Note the passing of a procedure as a parameter - it is legal, and useful to make more general functions, for example a sort routine for both increasing and decreasing order, or any order.
PROCEDURE Iterate(list: T; action: Action) = (* applies action to all elements (with key value as parameter) *) BEGIN WHILE list # NIL DO action(list.key); list:= list.next; END; END Iterate; BEGIN (* Intlist *) END Intlist.
Pointers and Parameter Passing
Pointers provide, for better or (usually) worse, and alternate way to modify parameters. Let us look at two different ways to swap the ``values'' of two pointers.
Procedure Swap1(var p,q:pointer); Var r:pointer; begin r:=q; q:=p; p:=r; end;
This is perhaps the simplest and best way - we just exchange the values of the pointers...
Alternatively, we could swap the values of what is pointed to, and leave the pointers unchanged.
Procedure Swap2(p,q : pointer); var tmp : node; begin tmp := q^; (*1*) q^ := p^; (*2*) p^ := tmp; (*3*) end;
After step (*1*):
After step (*2*):
After step (*3*):
Side Effects of Pointers
If swap2, since we do not change the values of p and q, they do not need to be var parameters!
However, copying the values did not do the same thing as copying the pointers, because in the first case the physical location of the data changed, while in the second the data stayed put.
If data which is pointed to moves, the value of what is pointed to can change!
Moral: you must be careful about the side effects of pointer operations!!!
C language does not have var parameters. All side effects are done by passing pointers. Additional pointer operations in C language help make this practical.