Προγραμματισμός Ι (ΗΥ120) Διάλεξη 10: Ταξινόμηση Πίνακα Αναζήτηση σε Ταξινομημένο Πίνακα
Πρόβλημα Δίνεται πίνακας t από Ν ακεραίους. Ζητούμενο: να ταξινομηθούν τα περιεχόμενα του πίνακα σε αύξουσα αριθμητική σειρά: i : 0<=i<N-1 => data[i] <= data[i+1] Μια λύση (ταξινόμηση επιλογής selection sort): Αρχίζοντας από το 1ο στοιχείο και φτάνοντας μέχρι το Νο στοιχείο του πίνακα, βρίσκουμε το στοιχείο με την μικρότερη τιμή και ανταλλάσσουμε την τιμή του με την τιμή του 1ου στοιχείου. Επαναλαμβάνουμε τη διαδικασία, αρχίζοντας από το 2ο στοιχείο, μετά αρχίζοντας από το 3ο στοιχείο κλπ μέχρι και το Ν-1ο στοιχείο. 2
i 3 data ταξινομημένα στοιχεία περιοχή που δεν έχει ταξινομηθεί ακόμα θέση όπου θα αποθηκευτεί η μικρότερη τιμή που θα βρεθεί στα data[i] data[n-1]
/* ταξινόμηση πίνακα */ int data[n]; /* πίνακας με Ν ακεραίους */ int i,j; /* μετρητές βρόγχων */ int min; /* θέση τοποθέτησης μικρότερου στοιχείου */ int tmp; /* βοηθητική μεταβλητή για ανταλλαγή θέσης */ 4 /* αρχικοποίηση πίνακα t με στοιχεία */ for (i=0; i<n; i++) { min = i; for (j=i+1; j<n; j++) { if (data[j] < data[min]) { min = j; if (min!= i) { tmp = data[i]; data[i] = data[min]; data[min] = tmp; Ανταλλαγή τιμών 2 μεταβλητών (swap)
Insertion Sort: Ιδέα 5 Ιδέα: Εξετάζουμε τους ακεραίους έναν-ένα. Κάθε νέο ακέραιο που εξετάζουμε τον βάζουμε στη "σωστή" θέση ανάμεσα στους ακεραίους που έχουμε ήδη εξετάσει. Σχήμα από το βιβλίο: "Computer Science: A Structured Approach Using C" by Behrouz Richard F. Gilberg
Insertion Sort: Βήματα 6 Σε κάθε βήμα: Παίρνουμε το πρώτο μη-ταξινομημένο στοιχείο και βρίσκουμε σε ποια θέση πρέπει να μπει στο ταξινομημένο κομμάτι. Αν χρειαστεί, μετακινούμε τα ταξινομημένα στοιχεία για να ανοίξει χώρος. Ο τοίχος μετακινείται μια θέση δεξιά. Συνεχίζουμε με τον ίδιο τρόπο μέχρι ο τοίχος να φτάσει στην άκρη και να έχουν ταξινομηθεί όλα τα στοιχεία.
Insertion Sort: Παράδειγμα 7 Σχήμα από το βιβλίο: "Computer Science: A Structured Approach Using C" by Behrouz Richard F. Gilberg
Insertion Sort: Παρατηρήσεις Όταν ψάχνουμε την κατάλληλη θέση για τον αριθμό που ήταν δεξιά από τον τοίχο, προσέχουμε να σταματήσουμε στην άκρη του πίνακα και να μη βγούμε εκτός ορίων (παράδειγμα: η εισαγωγή του αριθμού 8) Αν ο αριθμός που θέλουμε να τοποθετήσουμε είναι μεγαλύτερος του πρώτου αριθμού που βρίσκεται αριστερά από τον τοίχο, τότε δε χρειάζεται να κάνουμε μετακινήσεις. Αυτό σημαίνει, πως αν ο πίνακας τύχει να είναι ήδη ταξινομημένος, τότε ο αλγόριθμος αυτός απλά θα κάνει μια διάτρεξη του πίνακα χωρίς αλλαγές. Ανάμεσα στους απλούς, μη-αναδρομικούς αλγόριθμους ταξινόμησης (πχ bubblesort, selection sort, insertion sort), ο insertion sort είναι ο πιο γρήγορος. Ανάμεσα σε όλους του αλγορίθμους ταξινόμησης που βασίζονται σε συγκρίσεις στοιχείων, ο insertion sort είναι πιο γρήγορος αν το πλήθος των στοιχείων που ταξινομούμε είναι μικρό (πχ μερικές δεκάδες) 8
Insertion Sort: Κώδικας 9 void insertionsort ( int nums[], int size) { int wall, numtosort; for (wall=1; wall<size; wall++) { numtosort = nums[wall]; /* πρώτο μη-ταξινομημένο */ /* βάλε το στο ταξινομημένο κομμάτι */
Insertion Sort: Κώδικας 10 void insertionsort ( int nums[], int size) { int wall, numtosort, j; for (wall=1; wall<size; wall++) { numtosort = nums[wall]; /* πρώτο μη-ταξινομημένο */ j = wall-1; while (j >= 0 && nums[j] > numtosort) { /* όσο δεν έχουμε φτάσει στην άκρη και είναι μεγαλύτερα τα στοιχεία, */ nums[j+1] = nums[j]; /* κάνε χώρο */ j--; /* στη θέση j+1 βρίσκεται η θέση που ανοίξαμε */ nums[j+1] = numtosort;
Πρόβλημα Δίνεται πίνακας t από Ν ακεραίους, τα περιεχόμενα του οποίου είναι ταξινομημένα. Ζητούμενο: Να βρεθεί η θέση όπου έχει αποθηκευθεί μια τιμή στον πίνακα (εφόσον υπάρχει) Δηλαδή για μια τιμή v, να βρεθεί η τιμή i: i!=ν => t[i]==v i=n => j : 0<=j<N : t[j]!=v Εξετάζουμε δύο «κλασικές» λύσεις ακολουθιακή αναζήτηση δυαδική αναζήτηση (με διχοτόμηση) 11
Σειριακή αναζήτηση Αρχίζουμε από το πρώτο στοιχείο του πίνακα data[pos], pos=0, που έχει (εξ ορισμού) την μικρότερη τιμή και εκτελούμε σε βρόχο τις εντολές 2 έως 5. Αν pos==n, δηλαδή ο πίνακας δεν έχει στοιχείο data[pos], τότε τελειώσαμε (δεν υπάρχει στοιχείο με τιμή val). Διαφορετικά, αν data[pos]==val, τότε τελειώσαμε (αφού βρήκαμε το στοιχείο με τιμή val). Διαφορετικά, αν data[pos]>val, τότε πάλι τελειώσαμε (είναι σίγουρο ότι δεν υπάρχει στοιχείο με τιμή val). Διαφορετικά, (ισχύει data[pos]<val), τότε επαναλαμβάνουμε την διαδικασία από το 2 για το επόμενο στοιχείο (pos=pos+1). 12 Χρειάζονται κατά μέσο όρο «τάξη μεγέθους» Ν/2 βήματα σύγκρισης.
0 pos Ν-1 13 data περιοχή πίνακα που έχει ήδη ελεγχθεί: 0<=j<pos:data[j]<val στοιχεία προς έλεγχο επόμενο στοιχείο προς έλεγχο
/* σειριακή αναζήτηση σε ταξινομημένο πίνακα */ int data[n]; /* πίνακας με Ν ακεραίους */ int pos; /* τρέχουσα θέση */ int val; /* τιμή στοιχείου που αναζητούμε */ 14... /* αρχικοποίηση & ταξινόμηση πίνακα data */ for (pos=0; pos<n; pos++) { if (data[pos] >= val) { break; if (pos == N) { printf("not found\n"); else { if (data[pos]!= val) { printf("not found\n"); else { printf("found at position %d\n", pos);
/* σειριακή αναζήτηση σε ταξινομημένο πίνακα */ int data[n]; /* πίνακας με Ν ακεραίους */ int pos; /* μεταβλητή βρόγχου */ int val; /* τιμή στοιχείου που αναζητούμε */ 15... /* αρχικοποίηση & ταξινόμηση πίνακα t */ for (pos=0; (pos<n) && (data[pos]<val); pos++) { if (pos == N) { printf("not found\n"); else if (data[pos]!= val) { η σύμβαση εκτέλεσης του && εγγυάται printf("not found\n"); ότι δεν θα επιχειρηθεί πρόσβαση t[i] όταν το i φτάσει το όριο Ν του πίνακα else { printf("found at position %d\n", pos);
Δυαδική αναζήτηση Θέτουμε τα όρια αναζήτησης beg=0 και end=n-1. Αρχίζουμε από το μεσαίο στοιχείο του πίνακα t[m], όπου m = (beg+end)/2. Αν t[m] == v, τελειώσαμε. Αν t[m] < v, συνεχίζουμε με τον (μισό) υποπίνακα που έχει στοιχεία με μεγαλύτερες τιμές του t[m] δηλαδή θέτουμε beg = m+1 (και πίσω στο 2). Αλλιώς (t[m] > v), συνεχίζουμε με τον (μισό) υποπίνακα που έχει στοιχεία με μικρότερες τιμές του t[m] δηλαδή θέτουμε end = m-1 (και πίσω στο 2). Αν beg > end τελειώσαμε (η τιμή v δεν υπάρχει). Χρειάζονται «τάξη μεγέθους» log 2 N βήματα. 16
0 beg m end Ν-1 17 data περιοχή που έχει αποκλεισθεί : 0<=i<beg: data[i]<val περιοχή προς έλεγχο επόμενο στοιχείο προς έλεγχο περιοχή που έχει αποκλεισθεί : end<=i<n: data[i]>val
/* δυαδική αναζήτηση σε ταξινομημένο πίνακα */ int data[n]; /* πίνακας με Ν ακεραίους */ int beg,end; /* όρια αναζήτησης [beg,end) */ int m; /* θέση μεσαίου στοιχείου */ int val; /* τιμή στοιχείου που αναζητούμε */ 18... /* αρχικοποίηση & ταξινόμηση πίνακα t */ beg = 0; end = N-1; while (beg <= end) { m = (beg+end)/2; if (data[m] == val) { break; else { if (data[m] < val) { beg = m+1; /* (data[m] > val) */ else { end = m-1; if (beg > end) { printf("not found\n"); else { printf("found at position %d\n", m);
Σύγκριση 19 # στοιχείων μ.ο βημάτων μέγιστα βήματα πίνακα σειριακής αναζ. δυαδικής αναζ. Ν Ν/2 log N 100 50 7 1.000 500 10 1.000.000 500.000 20 1.000.000.000 500.000.000 30 Η διαφορά γίνεται τεράστια για μεγάλες τιμές του Ν.