Решение задач на динамические массивы
Приемы чтения и записи динамических массивов
Для формирования динамического массива, то есть для выделения участка памяти заданного размера для хранения данных определенного типа, необходимо выполнить следующие действия:
- Описать указатель (например, переменную p ) определенного типа.
- Начиная с адреса, определенного указателем, с помощью функций calloc, malloc или операции new выделить участок памяти определенного размера. После этого p будет адресом первого элемента выделенного участка оперативной памяти (0-й элемент массива), p+1 будет адресовать следующий элемент в выделенном участке памяти (1-й элемент динамического массива), ..., p+i является адресом i-го элемента. Необходимо только следить, чтобы не выйти за границы выделенного участка памяти. К i-му элементу динамического массива p можно обратиться одним из двух способов *(p+i) или p[i].
- Когда участок памяти будет не нужен, его необходимо освободить с помощью функции free() или операции delete.
Работа с двумерными динамическими массивами основана на двух следующих способах.
- Используется двойного указателя – указателя на указатель.
float **a; //это указатель на float *, или указатель на массив
- Применяется одинарный указатель. В этом случае двумерный динамический массив рассматривается как аналог одномерного массива. При работе с динамическими матрицами следует помнить, что выделенный участок памяти под двумерный массив A(N,M) представляет собой участок памяти размером N*M элементов. Поэтому выделение памяти будет выглядеть следующим образом:или
A = (тип *) calloc(N*M, sizeof(тип));
Для обращения к элементу Ai,j необходимо по номеру строки i и номеру столбца j вычислить номер этого элемента k в одномерном динамическом массиве. Учитывая, что в массиве элементы нумеруются с нуля, k=i*M+j. Статический элемент матрицы a[i][j] записывается как a[i*М+j] или *(a+i*М+j).A = (тип *) malloc(N*M*sizeof(тип));
Чтение и запись данных при работе с динамическими массивами аналогична работе со статическими массивами. Продемонстрируем это в следующем примере.
Пример 1. Вычислить и запомнить средние арифметические значения положительных элементов каждой строки матрицы A(K,L). Имена входного и выходного файла задаются пользователем. Входной файл в первой строке содержит размерность матрицы, со второй строки задается сама матрица.
#include "stdafx.h" #include <iostream> using namespace std; void input (int n,int m, int *mas, FILE *t); void out (int n,int m,int *mas); void outmas (int n, float *ml, FILE *g); void work(int n, int m, int *mas, float *ml); int _tmain(int argc, _TCHAR* argv[]) { int *mass,k,l; float *ml; float sr; char file1[10],file2[10]; FILE *t,*g; printf("Введите имя входного файла: "); scanf("%s",file1); printf("Введите имя выходного файла: "); scanf("%s",file2); t=fopen(file1,"r");//открытие файла для чтения g=fopen(file2,"w");//открытие файла для записи fscanf(t,"%d",&k); fscanf(t,"%d",&l); mass=(int*)malloc(k*l*sizeof(int)); //выделение памяти для массива ml=(float*)malloc(k*sizeof(float)); //выделение памяти для массива printf("\nМассив:\n"); input(k,l,mass,t); out(k,l,mass); work(k,l,mass,ml); printf("\nСредние арифметические значения положительных элементов каждой строки матрицы\n"); outmas(k,ml,g); fclose(t); //закрытие файла fclose(g); //закрытие файла free(ml); //освобождение памяти free(mass); //освобождение памяти system("pause"); return 0; } void input(int n, int m,int *mas, FILE *t){ int i,j; for (i=0;i<n;i++) for (j=0;j<m;j++) fscanf(t,"%d",mas+i*m+j); } void out (int n,int m, int *mas){ int i,j; for (i=0;i<n;i++) { for (j=0;j<m;j++) printf("%4d",mas[i*m+j]); printf("\n"); } } void outmas (int n, float *ml, FILE *g){ int i; for (i=0;i<n;i++) { printf("%6.2f\n",ml[i]); fprintf(g,"%6.2f\n",ml[i]); } } void work(int n, int m, int *mas, float *ml) { int i,j,kol; for (i=0;i<n;i++){ ml[i]=0; kol=0; for (j=0;j<m;j++) if (mas[i*m+j]>0) { ml[i]+=mas[i*m+j]; kol++; } if (kol>0) ml[i]/=kol; } }
Поиск, перестановка и сортировка в динамических массивах
Задачи по обработке динамических массивов (задачи на поиск, перестановки и сортировки в динамических массивах) реализуются аналогично соответствующим задачам по обработке данных статических массивов.
При этом в задачах на обработку двумерных массивов необходимо определить способ просмотра массива (по строкам, по столбцам, вдоль диагоналей и т.д.). При выборе обхода матрицы следует помнить, что параметр внешнего цикла меняется медленнее, чем параметр вложенных в него циклов. Под сортировкой двумерного массива понимают упорядочивание элементов, объединенных в строки или столбцы. В этом случае строку или столбец рассматривают как одномерный массив.
Пример 2. Дана прямоугольная матрица переставить столбцы таким образом, чтобы их максимальные элементы образовывали неубывающую последовательность. Разрешается использовать только дополнительный одномерный динамический массив.
#include "stdafx.h" #include <iostream> using namespace std; #include <time.h> void Initialization(int **mas, int *k, int *l,int **vmas); void Print(int *mas, int k, int l); void Work(int *mas, int k, int l,int *vmas); void FindMax(int *mas, int k, int l,int *vmas); void Obmen(int *mas,int k,int l,int i,int q); void Distraction(int *mas,int *vmas); int _tmain(int argc, _TCHAR* argv[]) { int *mass, n, m, *vmass; Initialization(&mass, &n, &m, &vmass); cout << "Исходная матрица" << "\n"; Print(mass, n, m); Work(mass, n, m, vmass); cout << "Преобразованная матрица" << "\n"; Print(mass, n, m); Distraction(mass, vmass); system("pause"); return 0; } void Initialization(int **mas, int *k, int *l,int **vmas){ int i, j, a, b; cout << "Введите размерность матрицы:" << "\n"; cout << "n = "; cin >> *k; cout << "m = "; cin >> *l; cout << "Введите границы генерации элементов матрицы:"<<"\n"; cout << "a = "; cin >> a; cout << "b = "; cin >> b; srand(time(NULL)*1000); *mas = new int[(*k)*(*l)]; *vmas = new int[(*l)]; for (i = 0; i < *k ; i++) for (j = 0; j < *l ; j++) (*mas)[i*(*l)+j] = rand()%(b-a)+a; } void Print(int *mas, int k, int l){ int i, j; for (i = 0; i < k ; i++){ for (j = 0; j < l ; j++) cout << mas[i*l+j] << " "; cout << "\n"; } } void Work(int *mas, int k, int l,int *vmas){ int i, j, q, r; FindMax(mas, k, l, vmas); for( i=0; i < l; i++) { // i - номер текущего шага q=i; for( j=i+1; j < l; j++) //цикл выбора наименьшего максимального элемента if (vmas[j]<vmas[q]) q=j;//q - индекс наименьшего максимального элемента r = vmas[q]; // меняем местами наименьший с vmas[i] vmas[q] = vmas[i]; vmas[i] = r; Obmen(mas, k, l, i, q); //меняем местами столбцы с номерами i и q матрицы mas } } void FindMax(int *mas, int k, int l,int *vmas){ int i, j; for (j = 0; j < l; j++){ vmas[j] = mas[j]; for (i = 1; i < k; i++) if (vmas[j] < mas[i*l+j]) vmas[j] = mas[i*l+j]; } } void Obmen(int *mas,int k,int l,int i,int q){ int j, r; for (j = 0; j < k; j++){ r = mas[j*l+i]; mas[j*l+i] = mas[j*l+q]; mas[j*l+q] = r; } } void Distraction(int *mas,int *vmas){ delete(vmas); delete(mas); }
Ключевые термины
"Висячий" указатель – указатель на освобожденный блок динамической памяти; признак ошибочного использования динамической памяти.
"Утечка" памяти – процесс выделения динамической памяти без ее освобождения, приводящий к состоянию нехватки памяти; признак ошибочного использования динамической памяти.
Использование неинициализированного указателя – попытка обращения к неопределенному адресу памяти или к значению NULL; признак ошибочного использования динамической памяти.
Многомерный динамический массив – это многомерный массив, расположенный в динамической памяти.
Ошибки при работе с динамической памятью – приемы некорректной работы с динамической памятью, приводящие к выполнению логически неверных действий в программе.
Краткие итоги
- Динамическая память не инициализируется автоматически и должна быть явно освобождена.
- Динамическое управление памятью имеет как преимущества, так и недостатки.
- При некорректной работе с динамической памятью можно совершить большое количество ошибок, которые имеют различные последствия и различную степень тяжести. К основным ошибкам относятся: попытка воспользоваться неинициализированным указателем; "висячие" указатели, "утечка" памяти, попытка освободить динамическую память, не выделенную ранее; попытка освободить нединамическую память.
- Во избежание ошибок целесообразно после выделения динамической памяти проверять ее значение, которое возвращается функцией или оператором.
- Работа с двумерными динамическими массивами можно организовать двумя способами: через двойной указатель (указатель на указатель) и через одинарный указатель (двумерный динамический массив рассматривается как аналог одномерного динамического массива).
- В многомерных динамических массивах память распределяется аналогично двумерным динамическим массивам.
- Задачи по обработке динамических массивов (задачи на поиск, перестановки и сортировки в динамических массивах) реализуются аналогично соответствующим задачам по обработке данных статических массивов.