본문 바로가기

C언어/자료구조

2-1장 배열(2)

1. 배열 요소를 역순으로 정렬하기 

 

- 맨 앞의 요소와 맨뒤에 요소 교환

- 교환 횟수 : 요소개수/2

- 요소 개수가 n인 배열 요소를 역순으로 정렬하는 코드 

for(int i=0; i<n/2; i++)

 -> 왼쪽 요소의 인덱스 i (n이 7이면 0->1->2)

 -> 오른쪽 요소의 인덱스 n-i-1 (n이 7이면 6->5->4)

 

- 두값의 교환

for(int i=0; i<n/2; i++) {
	int t=a[i];
    a[i]=a[i-i-1]; //a[i]와 a[n-i-1]값을 교환
    a[n-i-1]=t;
}

- 배열 요소를 역순으로 정렬

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

//type형 x값과 y값을 교환
#define swap(type,x,y) do { type t= x; x=y; y=t;} while(0)

//요소개수가 n인 배열 a의 요소를 역순으로 정렬
void arr_reverse(int a[], int n) 
{
	for(int i=0; i<n/2; i++)
		swap(int, a[i], a[n-i-1]);
}

int main()
{
	int nx; //배열 x의 요소 개수 
	
	printf("요소 개수:");
	scanf("%d",&nx);
	int *x = (int*)calloc(nx,sizeof(int));
	for(int i=0; i <nx; i++)
	{
		printf("x[%d]:",i);
		scanf("%d",&x[i]);
	}
	arr_reverse(x,nx); //배열 x의 요소를 역순으로 정렬  
	printf("배열의 요소를 역순으로 정렬했습니다.\n");
	for(int i=0; i <nx; i++)
	{
		printf("x[%d]= %d\n", i, x[i]);
	}
	free(x); //배열 x 해제 
	
 	return(0); 

}

 

 

2. 기수 변환하기 

 

- 10진수 정수를 n진수 정수로 변환

- 정수를 n으로 나눈 나머지를 구하는 동시에 그 몫에 대해 나눗셈 반복 

- 위 과정을 몫이 0이 될때까지 반복

- 구한 나머지를 거꾸로 늘어놓은 숫자가 기수로 변환한 숫자 

 

ex) 59를 16진수로 변환

 

- 기수 변환 수행 

//읽은 10진수를 2진수 ~36진수로 기수 변환하여 표시 
#include <stdio.h>

//type형 x값과 y값을 교환
#define swap(type,x,y) do { type t=x; x=y; y=t;} while(0)

//정수값 x를 n진수로 변환한 숫자 문자의 정렬을 배열 d에 저장
int card_conv(unsigned x, int n, char d[]) 
{
    char dchar[]="0123456789ABCDEFGHIJKLMNOPQEARUVWXYZ";
    int digits = 0; //변환 후 자리수 
    
    if(x==0) //0이면
    	d[digits++] = dchar[0]; //변환후에도 0
    else 
    	while(x){
        	d[digits++] = dchar[x%n]; //n으로 나눈 나머지를 저장
            x/=n;
        }
    for (int i=0; i < digits/2; i++) //배열 d를 역순으로 정렬
    	swap(char, d[i], d[digits-i-1]);
    
    return digits;
}

card_convr 함수 

 

 - 정수 x를 n진수로 변환한 숫자 문자의 정렬을 char형 배열 d에 저장하고 그 자릿수(배열에 저장한 문자수)를 변환

 -먼저 x를 n으로 나눈 나머지를 인덱스로 하는, 배열 dchar 안의 문자 dchar[x%n]을 배열 d의 요소 d[digits]에 대입하고 

digits 값을 증가 

 - x를 n으로 나눔 

 - 이 작업을 x가 0이 될때까지 반복 

 

int main(void)
{
    puts("10진수를 기수 변환합니다.");
    
    int retry;
    
    do {
    	unsigned no; //변환하는 정수
        int cd; //기수 
        char cno[512]; //변환한 값의 각 자리의 숫자를 저장하는 문자 배열 
        
        printf("변환하는 음이 아닌 정수: ");
        scanf("%u", &no);
        
        do{
        	printf("어떤 진수로 변환할까요?(2-36): ");
            scanf("%u", &cd);
           }while(cd <2 || cd >36);
           
        int dno = card_conv(no, cd, cno); //no를 dno자리의 cd진수로 변환
        
        printf("%d진수로는", cd);
        for(int i=0; i <dno; i++) //각 자리의 문자를 차례로 출력
        	printf("%c", cno[i]);
        printf("입니다.\n");
        
        printf("한번 더 할까요? (1 예/0 아니오); ");
        scanf("%d", &retry);
       } while(retry==1);
       
       return 0;
    }

 

 

cf. 전위형 증가 연산자 ++a 

 -식 전체를 평가하기 전에 피연산자의 값을 증가 

 - a=3 이고 b=++a 

  -> a가 증가한 값인 4가 됨, 그런 다음 ++a를 평가한 값 4를 b에 대입 

 

cf. 후위형 증가 연산자 a++

 - 식 전체를 평가한 후에 피연산자의 값을 증가

 - a=3이고 b=a++

  -> a++를  평가한 값 3을 b에 대입, 그런 다음 ++가 수행되어 a는 4가 됨

 

 

3. 소수 나열하기 

 

- 소수 : 자신과 1 이외의 정수로 나누어 떨어지지 않는 정수 

- 2부터 n-1까지의 어떤 정수로도 나누어 떨어지지 않음

 

-1000 이하의 모든 소수를 나열(버전1)

#include <stdio.h>

int main(void)
{
    unsigned long counter=0; //나눗셈 횟수 
    for(int n =2; n <= 1000; n++) {
    	int i;
        for(i=2; i<n; i++) {
        	counter ++;
            if(n%i==0) //나누어 떨어지면 소수가 아님 
            	break; //더 이상의 반복은 불필요 
        }
        if(n==i) //마지막까지 나누어 떨어지지 않음 
        	printf("%d\n", n);
     }
     printf("나눗셈을 실행한 횟수: %lu\n", counter);
     
     return 0; 
 }

ex) 9일때 소수인지 판단하는 방법

 - 안쪽의 for문에서 i값을 2,3,...8로 증가 9는 i가 3일 때 나누어떨어지므로 break문이 동작하여 for문 중단

 - 나눗셈은 2와 3일 때 2회만 진행

 - for문을 벗어날 떄의 i값은 3

 

ex) 13일때 소수인지 판단하는 방법

 - 안쪽의 for문에서 i값을 2,3,..12로 증가 13은 한 번도 나누어떨어지지 않음

 - 11회 나눗셈이 모두 수행

 - for문을 벗어날 떄의 i값은 13

더보기

n이 소수인 경우 : for문이 끝까지 수행됨 -> i는 n과 같은값

n이 합성수인 경우 : for문이 중단됨 -> i는 n보다 작은 값

 

- n이 2또는 3으로 나누어떨어지지 않으면 2x2인 4또는 2x3인 6으로도 나누어 떨어지지 않음 

 -> 불필요한 나눗셈 실행 

 -> ex) 7이 소수인지는 7보다 작은 소수 2,3,5로 나눗셈을 실행하면 충분 

 

3.1 알고리즘 개선 (1)

 

- 소수를 구하는 과정에서 그때까지 구한 소수를 배열 prime의 요소로 저장

- n이 소수인지를 판단할 때 쌓아 놓은 소수로 나눗셈 수행

- ptr : 배열에 저장한 소수의 개수를 나타내는 변수 

 

//1000 이하의 소수를 나열(버전 2)
#include <stdion.h>

int main(void)
{
    int prime[500]; //소수를 저장하는 배열
    int ptr = 0; //이미 얻은 소수의 개수 
    unsigned long counter = 0; //나눗셈 횟수 
    prime[ptr++] = 2; //2는 소수입니다
    for(int n= 3; n <=1000; n+=2){ //홀수만을 대상
    	int i;
        for(i=1; i<ptr; i++) { //이미 얻은 소수로 나눔
        	counter++;
            if(n% prime[i]==0) //나누어떨어지므로 소수가 아님
            	break;
        }
        if(ptr==i) //마지막까지 나누어떨어지지 않으면
        	prime[ptr++] = n; //배열에 저장 
    }
    for(int i=0; i <ptr; i++) 
    	printf("%d\n", prime[i]);
    printf("나눗셈을 실행한 횟수: %lu\n", counter);
    
    return 0;
    
  }

ex) 3이 소수인지 판단 ( n=3, ptr 1->2)

 - n값 3이 prime[1]에 저장 

 

ex) 5가 소수인지 판단 (n=5이고 ptr 2->3)

 - prime[1]에 저장한 3으로 나눗셈을 실행, 나누어 떨어지지 않으므로 소수라고 판단

 - n값 5를 prime[2]에 저장 

 

ex) 7이 소수인지 판단(n=7이고 ptr 3->4)

 -prime[1]에 저장한 3과 prime[2]에 저장한 5로 나눗셈 실행 , 나누어 떨어지지 않으므로 소수라고 판단

 - n값 7을 prime[3]에 저장 

 

ex) 9가 소수인지 판단 (n=9이고 ptr 4->5)

 - prime[1]에 저장한 3으로 나눗셈 실행

 - 3으로 나누어 떨어지므로 합성수로 판단 

 

3.2 알고리즘 개선(2)

 

- 100의 약수 (2,4,5,10,20,25,50,100)

 (2x50), (4x25), (5x10). (10x10), (20x5), (25x4), (50x2)

- 정사각형 (10x10)을 중심으로 대칭 

- 10까지만 소수로 나눗셈을 시도하고 그 과정에서 나누어 떨어지지 않으면 소수라고 판단해도 됨

더보기

n의 재곱근 이하의 어떤 소수로도 나누어 떨어지지 않으면 소수 

prime[i]의 제곱이 n 이하인가? 

//1000 이하의 소수를 나열 (버전3)
#include <stdio.h>

int main(void)
{ 
    int prime[500]; //소수를 저장하는 배열
    int ptr =0; //이미 얻은 소수의 개수 
    unsigned long counter = 0; //곱셈과 나눗셈의 실행 횟수 
    prime[ptr++] =2; //2는 소수
    prime[ptr++] =3; //3은 소수
    
    for(int n=5; n <=1000; n+=2) { //홀수만을 대상
    	int i;
        int flag=0;
        for(i=1; counter++,prime[i]*prime[i]<=n; i++) {
        	counter ++;
            if(n%prime[i] == 0) { //나누어 떨어지면 소수가 아님
            	flag=1;
                break;
            }
        }
        if(!flag) //마지막까지 나누어 떨어지지 않음
        	prime[ptr++] = n; //배열에 저장 
     }
     for(int i=0; i<ptr; i++) 
     	printf("%d\n", prime[i]);
     printf("곱셈과 나눗셈을 실행한 횟수: %lu\n", counter);
     
     return 0;
 }

 

cf. 쉼표 연산자 

opt1, opt2 

 - opt1을 평가하고 그 다음에 opt2를 평가 

 - 이 식 전체를 평가하여 얻은 값은 오른쪽 피연산자 opt2를 평가하여 얻은 자료형을 가진 값 

counter ++. prime[i]*prime[i] <=n

-> counter을 증가시키고 prime[i]*prime[i] <= n을 평가 

-> for문의 반복을 계속할지는 prime[i]*prime[i]<=n의 값에 달려 있음

 

4. 다차원 배열 만들기 

 

-2차원 배열 int a[3][3];

-2차원 배열의 요소는 1차원 배열 

a[0] a[0][0]
a[0][1]
a[0][2]
a[1] a[1][0]
a[1][1]
a[1][2]
a[2] a[2][0]
a[2][1]
a[2][2]

-행의 개수 (a의 요소 개수) / 열의 개수 (요소의 요소 개수)

 

 

5. 날짜를 계산하는 프로그램 만들기 

 

- 년, 월, 일의 3개 값이 주어지면 이를 이용해 연도의 지난 날 수를 구하는 프로그램

- issleap 함수 

 -> 매개 변수 year로 받은 연도가 윤년이면 1, 평년이면 0을 반환

- mdays[0] 평년

  mdays[1] 윤년

 

- 윤년 구하기 

 -> 4의 배수 가운데 100의 배수를 제외하고, 제외한 배수 가운데 400의 배수를 다시 포함

year%4==0 && year %100!=0 || year %400 ==0;

ex) 4월 15일

 - 1월 날 수 + 2월 날 수 + 3월 날수 +15

//한 해의 지난 날 수를 구하여 출력

#include <stdio.h>

//각 달의 날 수
int mdays[][12] = {
	{31,28,31,30,31,30,31,31,30,31,30,31},
    {31,29,31,30,31,30,31,31,30,31,30,31}
};

//year년이 윤년인가?
int isleap(int year)
{
	return year%4==0 && year %100 !=0 || year %400 ==0;
}

//y년 m월 d일의 그 해 지난 날 수를 구함
int dayof_year(int y, int m, int d)
{
	int days= d;
    for(int i=1; i<m; i++)
    	days +=mdays[isleap(y)][i-1];
    return days;
 }
 
 int main(void)
 {
 	int retry;
    do {
    int year,month, day; //년,월,일
    printf("년: "); scanf("%d", &year);
    printf("월: "); scanf("%d", &month);
    printf("일: "); scanf("%d", &day);
    printf("%d년의 %d일쨰입니다.\n", year, dayof_year(year,month,day));
    printf("다시 할까요?(1 예/0 아니오): ");
    scanf("%d",&retry);
    } while(retry==1);
    
    return 0;
 
}

'C언어 > 자료구조' 카테고리의 다른 글

3-2장 선형 검색  (0) 2023.07.25
3-1장 검색 알고리즘이란?  (0) 2023.07.25
2-2 구조체란?  (0) 2023.07.24
2-1 장 배열(1)  (0) 2023.04.08
1장 기본 알고리즘  (0) 2023.04.06