Ralph Morelli, Ralph Walde, Beryl Hoffman, David G. Cooper
Section9.5Array Algorithms: Sorting
Sorting an array is the process of arranging its elements in ascending or descending order. Sorting algorithms are among the most widely used algorithms. Any time large amounts of data are maintained, there is some need to arrange them in a particular order. For example, the telephone company needs to arrange its accounts by the last name of the account holder as well as by phone number.
The first sorting algorithm we’ll look at is known as insertion sort, so named because as it traverses through the array from the first to the last element, it inserts each element into its correct position in the partially sorted array.
For an array of N elements, let’s think of the array as divided into two parts. The sorted part will be the left hand side of the array. And the unsorted part will be the right hand side of the array.
Sorted | Unsorted Before inserting the kth element
5 14 19 | 1 0 67 4
^
k
On each iteration insertion sort will insert the first element in the unsorted part of the array, the kth element, into its correct position in the sorted part of the array.
InsertionSort of an array, arr, of N elements into ascending order
1.For k assigned 1 through N-12.Save the kth element, arr[k], in temp.3.Set i topointtothe largest element in the sorted part
4.While i >=0 AND the ith element is greater than temp
5.Shift the ith element tothe right
6.Set i topointtothe next element in the sorted part
7.Insert temp, the kth element, at its correct location.
As you see, the algorithm has nested loops, an outer for-loop and an inner while-loop. Each iteration of the outer loop will insert one element, the kth element, into its proper place in the sorted part of the array. The role of the inner while-loop is to move the already sorted elements out of the way.
The outer (k) loop, iterates from 1 to N-1. On each iteration, it saves the kth element in temp. It then sets the while-loop variable, i, to point to the largest element in the sorted part of the array, the element just to the left of the kth element.
The inner loop proceeds from right to left — that is, from k-1 towards 0. It shifts all numbers greater than temp to the right. It terminates when it finds a number smaller or equal to temp.
The vertical line marks the boundary between the sorted and unsorted portions of the array. The outer loop will look at each of the remaining elements, starting at arr[1], one at a time, inserting them into their proper positions in the sorted portion of the array. The outer loop saves the kth element (20) in temp.
The outer loop will complete its work by inserting 20 in the location previously occupied by 21, in effect swapping 20 and 21. At this point, after one complete iteration of the outer loop, the first two numbers are in the correct order relative to each other:
For the next element, 27, none of elements in the sorted portion need to be moved because they are all less than 27. The inner loop will not iterate at all in this case. So after two complete iterations we have:
However, for the last element, 19, all of the elements in the sorted part of the array need to be shifted one space to the right. Let’s trace each iteration of the inner loop starting from this point, where k = 4 and i = 3:
k
20212427|19Save19 in temp: temp=19, i=320212427|27Move27tothe right: temp=19, i=220212424|27Move24tothe right: temp=19, i=120212124|27Move21tothe right: temp=19, i=020202124|27Move20tothe right: temp=19, i=-1 inner loop terminates
1920212427|Insert temp=19 at index i+1
It’s important to see that when the inner loop terminates i will always be one space to the left of where we make the insertion. That’s why the insertion is made at arr[i+1].
It’s important to see, again, that after the while-loop terminates the variable i will always point one space to the left of where the kth element should be inserted. Thus, the insertion is made at arr[i+1].
This is necessary because we want i to iterate from right to left, but not to go past 0. But we also want the loop to stop if arr[i] isn’t greater than temp. We want it to stop as soon as we find a number that’s less than or equal to temp. That’s where temp needs to be inserted.
There are also a couple of syntax points that are important to observe. First, note in the declaration of the insertionSort() method how empty brackets ([]) are used to declare an array parameter. If the brackets were omitted, then arr would be indistinguishable from an ordinary int parameter.
When declaring an array parameter, empty brackets must be used either after the array name or after the type name to distinguish it from a non-array parameter.
Incorrect. That’s how it would look after 2 passes.
0 1 18 24 85 90 34 18
Incorrect. That’s how it would look after 5 passes.
0 1 18 24 90 85 34 18
Correct. The left side would be sorted up to the 5th element.
0 1 18 18 24 34 85 90
Incorrect. The array would not be completely sorted after 4 passes.
Hint.
Trace through four complete iterations of the outer loop. After four passes the numbers up to and including location 4 in the original array should be in the correrct order.
To illustrate the selection sort algorithm, suppose you want to sort a deck of 25 index cards, numbered from 1 to 25, layed out on the table. Starting with the first card, assume it is the smallest card. Compare it with the second, third, fourth and every other card in the deck. Whenever you find a card that is smaller than the first card, swap it with the first card.
Complete the process again starting this time with the second card. Find the smallest card in the remaining deck and swap it with the second card. Repeating this process 24 times a for deck of 25 cards will correctly sort the deck.
Selection sort of an N element array <c>arr</c> from small tolarge1.For k assigned 0toN-2// Outer loop2. smallest = k // Assume kth is smallest so far3.For j assigned k+1toN-1// Inner loop: finds smallest4.If arr[j]< arr[smallest]5. smallest = j // Remember which is smallest6.Swap arr[k] and arr[smallest]// Swap kth and smallest
For an array of N elements, you need to repeat the outer loop N-1 times. On each iteration of the outer loop,the inner loop takes care of finding the next smallest element.
In the outer loop, the algorithm assumes that the kth element is the smallest so far (line 2). It usually won’t be, of course. But it will be after the inner loop terminates and kth element is swapped with the smallest.
The inner loop finds smallest by iterating through the rest of the array (from k+1 to N-1) comparing each element with the smallest so far (lines 4) and remembering the location of the element that is currently the smallest (lines 5).
Subsection9.5.3Algorithm: Swapping Memory Elements
An important feature of the selection sort algorithm is its need to swap two array elements. Swapping two memory elements, whether they are array elements or not, requires the use of a temporary variable. For example, to swap the jth and kth elements in an int array named arr, you would use the following algorithm:
The temp variable temporarily stores the jth element so its value is not lost when its location is overwritten by the kth element. The need for this variable is a subtlety that beginning programmers frequently overlook. But consider what would happen if we used the following erroneous algorithm:
voidswap(int arr[],int el1,int el2){int temp = arr[el1];// Assign first element to temp
arr[el1]= arr[el2];// Overwrite first with second
arr[el2]= temp;// Overwrite second with first}// swap()
ExercisesSelf-Study Exercises
1.Swapping values in variables.
Suppose the double variables var1 and var2 have been properly declared and initialized. Arrange the following blocks to swap their values.
Subsection9.5.4Passing a Value and Passing a Reference
Recall from Chapter 3 that when an Object is passed to a method, a copy of the reference to the Object is passed. Because an array is an object, a reference to the array is passed to insertionSort(), rather than the whole array itself. This is in contrast to how a value of a primitive type is passed. In that case, a copy of the actual value is passed.
When a value of a primitive data type — int, double, char, boolean — is passed as an argument to a method, a copy of the value is passed; when a reference to an Object is passed, a copy of the reference is passed.
One implication of this distinction is that for arguments of primitive type, the original argument cannot be changed from within the method because the method has only a copy of its value. For example, the following method takes an int parameter n, which is incremented within the method:
publicvoidadd1(int n){System.out.print("n = "+ n);
n = n +1;System.out.println(", n = "+ n);}
But because n is a parameter of primitive type, incrementing it within the method has no effect on its associated argument. Thus, in the following segment, the value of Num — n’s associated argument — will not be affected by what goes on inside the add() method. The output produced by the code segment is shown in the comments:
The case is much different when we pass a reference to an object. In that case, the object itself can be manipulated from within the method. The insertionSort() method is a good illustration. In the following code segment, the array anArr is printed, then sorted, and then printed again:
As you can see, the object itself (the array) has been changed from within the method. This shows that changes within insertionSort to the array referenced by arr are actually being made to anArr itself. If fact, because insertionSort() is passed a copy of the reference variable anArr, both arr and anArr are references to the very same object — that is, to the same array (Figure 9.5.9).
The justification for passing a reference to an object rather than the entire object itself is a matter of efficiency. A reference uses just 4 bytes of data, whereas an object may use thousands of bytes. It would just be too inefficient to copy hundreds of bytes each time an object is passed to a method. Instead, the method is passed a reference to the object, thereby giving it access to the object without incurring the expense of copying large amounts of data. Indeed, Java provides no way to pass a copy of an object to a method.