Προς το περιεχόμενο

ταξινόμηση στην C με malloc.


ioan

Προτεινόμενες αναρτήσεις

Δημοσ.

Έχω μία άσκηση που λέει ως εξής : να γινει ταξινομηση ονοματων μια με βαση το επωνυμο και μια με βαση το ονομα.

το θεμα ειναι ομως οτι πρεπει να γινετε με την χρηση της malloc(χρησιμοποιηση οσης μνημης απαιτειται).

Eπισης μπορουν να δωθουν μεχρι 100 ονοματα.Την ταξινομηση κανονικα,χωρις μνημη την εχω κανει καθως ειναι απλη και εχω χρησιμοποιησει την strcpy,αν και γινεται και με την bubble sort, κανονικα.

εχει κανεις ιδεα πως μπορω να το κανω με την χρησιμοποιηση της malloc?

Δημοσ.

Πια είναι η εκφώνηση της άσκησης ?

 

Ζητείται ένα πρόγραμμα το οποίο να διαβάζει ονοματεπώνυμα με λατινικούς χαρακτήρες από την κύρια είσοδο και να τα τυπώνει ταξινομημένα 2 φορές, μία με βάση το όνομα και μία με βάση το επώνυμο. Ο μέγιστος αριθμός ονοματεπωνύμων που θα μπορεί να δοθεί είναι 100 και το μέγιστο ονοματεπώνυμο είναι 100 χαρακτήρες. Το πρόγραμμα σας θα πρέπει να χρησιμοποιεί όση ακριβώς μνήμη απαιτείται για την αναπαράσταση των ονομάτων και επωνύμων.

Δημοσ.

Σου παραθέτω έναν τρόπο να αποθηκεύεις τα ονοματεπώνυμα δυναμικά, δεσμεύοντας ακριβώς όση μνήμη χρειάζεσαι, τόσο για το πλήθος τους όσο και για το μήκος τους. Ελπίζω να καταλάβεις τι παίζει, το έχω γεμίσει με σχόλια.

 

Σου δίνω επίσης 2 χρησιμότατες συναρτήσεις, μια για να διαβάζεις strings safely (την s_gets()) και μια για να σπας strings σε μικρότερα κομμάτια από αριστερά προς τα δεξιά, χρησιμοποιώντας όποιους διαχωριστικούς χαρακτήρες επιθυμείς (την s_tokenize()). Και οι 2 είναι από μια μικρή βιβλιοθήκη συναρτήσεων που έχω φτιάξει για να επεκτείνω τις λειτουργίες της <string.h>

 

Την s_tokenize() τη χρησιμοποιώ για να απομονώσω το όνομα από το επώνυμο, αλλά πολύ πρόχειρα. Π.χ. αν βάλεις πάνω από 2 λέξεις μέσα στο ονοματεπώνυμο, τότε η 1η εκλαμβάνεται ως όνομα και ΟΛΕΣ οι υπόλοιπες ως επώνυμο. Αν θες να κρατάς και τη 2η ως ξεχωριστό token, πέρνα στο όρισμα 'maxtokens' της s_tokenize() την τιμή 3 και κατόπιν επεξεργάσου μονάχα τα 2 πρώτα στοιχεία του *tokens[] (το οποίο btw, πρέπει επίσης να το δηλώσεις να δέχεται έως 3 strings αντί για 2 που το έχω τώρα).

 

Όσο για τις ταξινομήσεις, είπες πως τις έχεις κάνει οπότε δεν ασχολήθηκα. Από τη στιγμή που γνωρίζεις το n (δηλαδή το μήκος του names) και ξέρεις και τον τρόπο να απομονώνεις το όνομα και το επίθετο στο κάθε names[ i ] (με την s_tokenize()) βγαίνει εύκολα η ταξινόμηση.

 

>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define MAX_NAMES	100
#define MAX_NAMELEN	100+1

//----------------------------------------------------------------------------
// "Σπάει" από αριστερά προς τα δεξιά το s σε έως maxtokens μικρότερα κομμάτια
// (tokens) βάσει των διαχωριστικών χαρακτήρων που περιέχονται στο 'delimiters'.
// Τα αποθηκεύει στο *tokens[] κι επιστρέφει το πλήθος τους.
//
int s_tokenize(char *s, char *tokens[], int maxtokens, char *delimiters)
{
register int i=0;

tokens[0] = strtok(s, delimiters);
if (tokens[0] == NULL)
	return 0;

for (i=1; i < maxtokens && (tokens[i]=strtok(NULL, delimiters)) != NULL; i++)
	; 

return i;
}

//----------------------------------------------------------------------------
// Διαβάζει μέχρι len-1 χαρακτήρες από το πληκτρολόγιο, μηδενίζει τον len-οστο
// και τους αποθηκεύει στο string s

char *s_get(char *s, unsigned int len)
{
       char *cp;

fflush(stdin);
       for (cp=s; (*cp=getc(stdin)) != '\n' && (cp-s) < len-1; cp++ )
               ;
       *cp = '\0';	

       return s;
}
//----------------------------------------------------------------------------
int main( void )
{
char **names;			// array από oνοματεπώνυμα
char *tokens[2];		// το κάθε ονοματεπώνυμο θα το σπάσουμε σε έως 2 tokens
int ntokens;			// εδώ θα κρατάμε την τιμή επιστροφής της s_tokenize()
int n;				// πλήθος διαβασμένων ονοματεπώνυμων
char s[MAX_NAMELEN] = "";	// προσωρινό string
register int i;

// διάβασμα πλήθους ονοματεπώνυμων
do {
	printf("Posa onomata (ews 100)? ");
	fflush(stdin); scanf("%d", &n);
} while (n > MAX_NAMES);

// δέσμευση μνήμης για n ονοματεπώνυμα
names = malloc( n * sizeof(char *) );
if ( !names ) {
	puts("aneprakhs mnhmh, termatismos programmatos");
	exit(1);
}

// διάβασμα n ονοματεπώνυμων, μήκους MAX_NAMELEN το καθένα
// (διαβάζουμε πρώτα το κάθε ονοματεπώνυμο σε ένα προσωρινό string s
//  ώστε να μετρήσουμε το μήκος του (slen) και να δεσμέυουμε ανάλογη
// μνήμη πριν το αντιγράψουμε στο names[i])

for (i=0; i<n; i++)
{	size_t slen;

	printf("Dwste %do onomatepwnymo: ", i+1);
	s_get(s, MAX_NAMELEN);				// διάβασμα του s
	slen = strlen(s);				// μέτρημα του μήκους του
	names[i] = malloc( slen * sizeof(char) );	// δέσμευση ανάλογης μνήμης

	if ( !names[i] )				// αποτυχία του malloc()
	{	int j;
		
		for (j=i-1; j>=0; j--)			// απελευθέρωση της έως τώρα δεσμευμένης μνήμης
			free( names[j] );
		free( names );
		puts("aneparkhs mnhmh, termatismos programmatos");
		exit(1);				// ανώμαλος τερματισμός πργράμματος
	}

	strcpy(names[i], s);				// αντιγραφή του s στο names[i]
}

putchar('\n');

// τύπωμα ονοματεπώνυμων
// (το κάθε names[i] αφού πρώτα το τυπώσουμε, το σπάμε σε 2 κομμάτια
// θεωρώντας πως το 1ο είναι το όνομα και το 2ο είναι το επώνυμο.

for (i=0; i<n; i++)
{
	// τύπωμα πρώτα ολόκληρου του ονοματεπώνουμου
	printf("%do onomatepwnymo: %s\n", i+1, names[i]);

	// και κατόπιν ξεχωριστά το όνομα και το επώνυμο (αν υπάρχει)
	ntokens = s_tokenize(names[i], tokens, 2, " \t\f\r");
	if ( ntokens > 0 )
	{
		printf("\tOnoma: %s", tokens[0]);
		if (ntokens == 2)
			printf("\tEpwnymo: %s", tokens[1]);
	}
	puts("\n\n");

}

// Απελευθέρωση της μνήμης που έχουμε δεσμεύσει
for (i=0; i<n; i++)
	free(names[i]);
free( names );

printf("\npathste ENTER gia ejodo "); fflush(stdin); getchar();
exit(0);
}

 

ΥΓ. Σε ποιο έτος είσαι και σας έβαλαν τέτοια άσκηση;

Δημοσ.

ευχαριστω φιλε, να σε καλα θα το κοιταξω.

2ο ειμαι αλλα το μαθημα ειναι απο το 1ο ετος του 2ου εξαμηνου :P

Δημοσ.

Το ποστάρω εδώ φίλε μου, αντί με pm γιατί μπορεί να φανεί χρήσιμο και σε άλλους.

 

Πρόσθεσα στον κώδικα τις συναρτήσεις s_swap(), sarr_bubble() και sarr_print() με αυτονόητες από το όνομά τους λειτουργίες. Επίσης, επειδή η s_tokenize() κατακερματίζει το αρχικό string κι εμείς το θέλουμε αυθεντικό και μετά το κάλεσμα της συνάρτησης, πριν της περάσω τα names[ i ] εκεί που τυπώνω ξεχωριστά το όνομα και το επώνυμο, τα αντιγράφω πρώτα στο προσωρινό string s και περνάω αυτό ως όρισμα της s_tokenize().

 

Παραθέτω τον κώδικα (τις ταξινομήσεις πρώτα με όνομα και μετά με επώνυμο κάνε τες εσύ φίλε μου, εγώ εδώ απλά ταξινομώ τα ονοματεπώνυμα όπως εισήχθηκαν αρχικά).

 

>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define MAX_NAMES	100
#define MAX_NAMELEN	100+1

// ------------------------------------------------------------------------------------
// Εναλλάσσει τα περιεχόμενα των strings s και t (επιστρέφει 1 σε επιτυχία, αλλιώς 0).
// ΣΗΜΕΙΩΣΗ:	με τα s και t δηλωμένα ως κανονικά strings στην main() (π.χ. str1[] και
//		str2[]) περνάμε τις διευθύνσεις τους ως ορίσματα της συνάρτησης:
// 		s_swap( &str1, &str2 )
//
int s_swap(char **s, char **t)
{
if ( !(*s) || !(*t) )				// ανύπαρκτο s ή t
	return 0;				// πρόωρος τερματισμός

char *dummy = *s;				// προσωρινή αποθήκευση του s
*s = *t;
*t = dummy;

return 1;
}

// ------------------------------------------------------------------------------------
// Ταξινομεί αλφαβητικά με bubble sort ένα array από n strings
//
void sarr_bubble( char *sarr[], int n )
{  
int i,j;

for (i=0; i<n; i++)
	for (j=0; j < (n-1); j++)
		if ( strcmp(sarr[j], sarr[j+1]) > 0 )
			s_swap( &sarr[j+1], &sarr[j] );

return;
}

// ------------------------------------------------------------------------------------
void sarr_print( char *sarr[], int n )
{
register int i;

for (i=0; i<n; i++)
	printf("%3d. %s\n", i+1, sarr[i] );

return;
}

// ------------------------------------------------------------------------------------
// "Σπάει" από αριστερά προς τα δεξιά το s σε έως maxtokens μικρότερα κομμάτια
// (tokens) βάσει των διαχωριστικών χαρακτήρων που περιέχονται στο 'delimiters'.
// Τα αποθηκεύει στο *tokens[] κι επιστρέφει το πλήθος τους.
//
int s_tokenize(char *s, char *tokens[], int maxtokens, char *delimiters)
{
register int i=0;

tokens[0] = strtok(s, delimiters);
if (tokens[0] == NULL)
	return 0;

for (i=1; i < maxtokens && (tokens[i]=strtok(NULL, delimiters)) != NULL; i++)
	; 

return i;
}

// ------------------------------------------------------------------------------------
// Διαβάζει μέχρι len-1 χαρακτήρες από το πληκτρολόγιο, μηδενίζει τον len-οστο
// και τους αποθηκεύει στο string s

char *s_get(char *s, unsigned int len)
{
       char *cp;

fflush(stdin);
       for (cp=s; (*cp=getc(stdin)) != '\n' && (cp-s) < len-1; cp++ )
               ;
       *cp = '\0';	

       return s;
}
// ------------------------------------------------------------------------------------
int main( void )
{
char **names;			// array από ονοματεπώνυμα
char *tokens[2];		// το κάθε ονοματεπώνυμο θα το σπάσουμε σε έως 2 tokens
int ntokens;			// εδώ θα κρατάμε την τιμή επιστροφής της s_tokenize()
int n;				// πλήθος διαβασμένων ονοματεπώνυμων
char s[MAX_NAMELEN] = "";	// προσωρινό string
register int i;

// διάβασμα πλήθους ονοματεπώνυμων
do {
	printf("Posa onomata (ews 100)? ");
	fflush(stdin); scanf("%d", &n);
} while (n > MAX_NAMES);

// δέσμευση μνήμης για n ονοματεπώνυμα
names = malloc( n * sizeof(char *) );
if ( !names ) {
	puts("aneprakhs mnhmh, termatismos programmatos");
	exit(1);
}

// διάβασμα n ονοματεπώνυμων, μήκους MAX_NAMELEN το καθένα
// (διαβάζουμε πρώτα το κάθε ονοματεπώνυμο σε ένα προσωρινό string s
//  ώστε να μετρήσουμε το μήκος του (slen) και να δεσμέυουμε ανάλογη
// μνήμη πριν το αντιγράψουμε στο names[i])

for (i=0; i<n; i++)
{	size_t slen;

	printf("Dwste %do onomatepwnymo: ", i+1);
	fflush(stdin);
	s_get(s, MAX_NAMELEN);				// διάβασμα του s
	slen = strlen(s);				// μέτρημα του μήκους του
	names[i] = malloc( slen * sizeof(char) );	// δέσμευση ανάλογης μνήμης

	if ( !names[i] )				// αποτυχία του malloc()
	{	int j;

		for (j=i-1; j>=0; j--)                  // απελευθέρωση της έως τώρα μνήμης
			free( names[j] );
		free( names );
		puts("aneparkhs mnhmh, termatismos programmatos");
		exit(1);                                // ανώμαλος τερματισμός πργράμματος
	}

	strcpy(names[i], s);				// αντιγραφή του s στο names[i]
}
putchar('\n');

// τύπωμα ονοματεπώνυμων
// (το κάθε names[i] αφού πρώτα το τυπώσουμε, το σπάμε σε 2 κομμάτια
// θεωρώντας πως το 1ο είναι το όνομα και το 2ο είναι το επώνυμο.

for (i=0; i<n; i++)
{
	// τύπωμα πρώτα ολόκληρου του ονοματεπώνυμου
	printf("%do onomatepwnymo: %s\n", i+1, names[i]);

	// και κατόπιν ξεχωριστά το όνομα και το επώνυμο (αν υπάρχει)
	// (επειδή η s_tokenize() κατακερματίζει το αυθεντικό string,
	//  αντιγράφουμε το κάθε names[i] στο προσωρινό string s και
	//  περνάμε αυτό στην s_tokenize() )

	strncpy(s, names[i], MAX_NAMELEN);
	ntokens = s_tokenize(s, tokens, 2, " ");
	if ( ntokens > 0 )
	{
		printf("\tOnoma: %s", tokens[0]);
		if (ntokens == 2)
			printf("\tEpwnymo: %s", tokens[1]);
	}
	puts("\n\n");

}

sarr_bubble(names, n);					// αλφαβητική ταξινόμηση του names

puts("Tajinmhmena Alfabhtika\n\
-----------------------------------------------------------");
sarr_print(names, n);
puts("-----------------------------------------------------------");

// Απελευθέρωση της μνήμης που έχουμε δεσμεύσει
for (i=0; i<n; i++)
	free(names[i]);
free( names );

printf("\npathste ENTER gia ejodo "); fflush(stdin); getchar();
exit(0);
}

 

ΥΓ. Σόρρυ που καθυστέρησα, αλλά πριν λίγο ξεμπέρδεψα με τη δουλειά.

Δημοσ.

πλακα κανεις;, δεν χρειαζεται να απολογεισαι :P

ευχαριστω για τον χρονο σου και την βοηθεια...

Δημοσ.

Λοιπόν σου έφτιαξα άλλη μια συνάρτηση, η οποία σου ταξινομεί αλφαβητικά ένα array από n strings, βάσει τη σειρά όποιας λέξης του πεις (π.χ. 1 = βάσει 1ης λέξης, 2 = βάσει 2ης λέξης, κλπ). Λέξεις (tokens) θεωρεί ότι περικλείεται σε χαρακτήρες από αυτούς που περιέχονται στο όρισμα delims.

 

Παραθέτω και τον κώδικα (ελπίζω να μην έχει κάνα bug, γιατί κουτουλάω κι από τη νύστα) :P

 

>

/ ------------------------------------------------------------------------------------
// Ταξινομεί αλφαβητικά με bubble sort ένα array από n strings, με βάση τη λέξη που
// βρίσκεται στη θέση itok των strings (λέξεις θεωρούνται όσα substrings διαχωρίζονται
// μεταξύ τους από οποιονδήποτε χαρακτήρα περιέχεται μέσα στο delims)
//
void sarr_bubble_by_token( char *sarr[], int n, int itok, char *delims )
{
int i,j;
char s[2][MAX_NAMELEN] = {"", ""};		// 2 προσωρινά strings: s[0] και s[1]
char *stokens[ itok + 1 ];			// για την s_tokenize()


for (i=0; i<n; i++)
{
	for (j=0; j < (n-1); j++)
	{
		// αντιγραφή στο s[0] της itok λέξης του sarr[j] (ή '\0' αν δεν υπαρχει)
		strncpy(s[0], sarr[j], MAX_NAMELEN);
		if ( s_tokenize( s[0], stokens, itok + 1, delims ) >= itok )
			strncpy(s[0], stokens[ itok - 1 ], MAX_NAMELEN);
		else
			*s[0] = '\0';

		// αντιγραφή στο s[1] της itok λέξης του sarr[j+1] (ή '\0' αν δεν υπαρχει)
		strncpy(s[1], sarr[j+1], MAX_NAMELEN);
		if ( s_tokenize( s[1], stokens, itok + 1, delims ) >= itok )
			strncpy(s[1], stokens[ itok - 1 ], MAX_NAMELEN);
		else
			*s[1] = '\0';

		if ( strcmp(s[0], s[1]) > 0 )
			s_swap( &sarr[j+1], &sarr[j] );
	}
}

return;
}

 

Οπότε για να ταξινομήσεις βάσει επωνύμου (2η λέξη) την καλείς ως εξής: sarr_bubble_by_token(names, n, 2, " \t");

(και κατόπιν τυπώνεις το array names με την sarr_print(names, n); για να τα δεις και τυπωμένα :) Για να ταξινομήσεις όμως κατά όνομα (1η λέξη), η σκέτη sarr_bubble() είναι (πάρα) πολύ ταχύτερη.

 

ΥΓ. Μου φαίνεται ιδιαίτερα δύσκολη άσκηση για πρωτοετή πάντως! Aν δεν ήταν υποχρεωτικό τα ονοματεπώνυμα να αποθηκεύονται χύμα σε array από strings, θα μπορούσαμε να τα αποθηκεύουμε σε array από structs, με το καθένα τους να περιέχει ξεχωριστά πεδία για το όνομα και το επώνυμο. Έτσι θα ήταν (πάρα) πολύ ευκολότερη η διαχείριση τους.

 

πλακα κανεις;, δεν χρειαζεται να απολογεισαι :P

ευχαριστω για τον χρονο σου και την βοηθεια...

 

Τίποτα ρε συ, ήταν ευκαιρία να "ξεσκονίσω" κι εγώ λιγάκι την... RAM μου :lol:

 

Πάω να την πέσω, γιατί δεν την παλεύω άλλο. Δοκίμασέ την με διάφορα input την sarr_bubble_by_token() γιατί μπορεί να 'χει κάνα bug.

Αρχειοθετημένο

Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.

  • Δημιουργία νέου...