[Java] Chapter02_01 배열

2020. 10. 3. 23:30Do it! 자료구조와 함께배우는 알고리즘 입문 JAVA편


Chpater02 연습문제 코드 바로가기


[잡담]


안녕하세요! 언텐빌Y 입니다. 오늘 드디어 2강 포스팅을 하게되었네요.

이제는 영어 공부 포스팅까지 병행하려니 시간이... ㅠㅠㅠㅠ 물론 정리도하면서 공부하느라 


이 책을 공부하면서 막힌적이없었는데... 배열에서 한번막혀서 삽질을 엄청했습니다.

오늘은 그 막힌부분까지만 작성하고 다음포스팅에 마저 2강을 마무리짓도록하겠습니다.


배열은 무엇인지, 또 제가 삽질한부분은 어떤것인지 자료구조는 무엇인지를 알아보겠습니다!



[오늘의 코드]

- 오늘은 아래 코드들을 통해 배열이라는 것을 배울 것이다. 배열이란 개념은 초반에 상당히 

  어려울 수 있는 개념인데 이 글을 보며 극복을 해보자.

- 코드 아래에는 코드에 대한 설명이 적혀있고, 아래로 스크립하다보면 [Java Knowledge]라는 

  부분에 문법과 같은 상세한 설명들이 적혀있다. 


▷ 랜덤으로 값을 입력받아 배열의 최댓값을 구하는 코드

package Practice2;

import java.util.Random;


class MaxArrayRand{

static int maxOf(int[] a) {

      int max = a[0];

      for (int i = 1; i < a.length; i++)

         if (a[i] > max)

            max = a[i];

      return max;

   }


public static void main(String[] args) {

          Random rand = new Random();


          System.out.println("키의 최댓값을 구합니다.");

          int num = 1 + rand.nextInt(20);

          int[] height = new int[num];


          System.out.println("사람수는 " + num + "명입니다.");

          System.out.println("키는 아래처럼 됩니다.");

          for (int i = 0; i < num; i++) {

             height[i] = 150 + rand.nextInt(50);

             System.out.println("height[" + i + "]:" + height[i]);

          }


          System.out.println("최댓값은 " + maxOf(height) + "입니다.");

       }

}


1. maxOf라는 최댓값을 구하는 메소드를 만들어준다.

- max 변수선언과 동시에 a[0]을 초기화해준다.

- for문을작성하고 a[i]가 max보다 크면 max=a[i]을 반복한다.

- 예를 들어, 배열의 길이가 4이면 3번반복하고 a[1],a[2],a[3]은 각각 max값과 비교해 만약        max값보다 크면 max값에 a[1또는2또는3]이 저장되게 된다.

- 최댓값이 정해지면 max값을 반환한다.

2. 메인함수에서 우선 난수를 발생하게하기위해 Random 클래스를 import하여준다.

3. Random 변수 rand를 지정해준다.

4. 키의 최댓값을 구하는 프로그램임을 출력한다.

5. 키를 비교할 배열의 요소수를 결정하기위해 난수발생을 이용하여 num값을 정해준다.

6. height라는 배열을 선언한다.

7. for문과 난수발생을 이용하여 height배열에 요소를 초기화 시켜준다.

- 배열의 요소수만큼 반복하며 height[0] = 150 + (1~49) 이런식으로 저장한다.

- height[0] : 188 이런식으로 출력한다.

8. 마지막으로 최댓값을 출력하기위해 maxOf함수를 호출하고 출력한다.




▷ 기수 변환 코드(어렵습니다..☆☆☆☆☆)


package Practice2;

import java.util.Scanner;


class CardConvRev {

static int cardConvR(int x, int r, char[] d) {

int digits = 0;

String dchar = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";


do {

d[digits++] = dchar.charAt(x % r); // r로 나눈 나머지를 저장

x /= r;

} while (x != 0);

return digits;

}


public static void main(String[] args) {

Scanner stdIn = new Scanner(System.in);

int no; // 변환하는 정수

int cd; // 기수

int dno; // 변환 후의 자릿수

int retry; // 다시 한 번?

char[] cno = new char[32]; // 변환 후 각 자리의 숫자를 넣어두는 문자의 배열


System.out.println("10진수를 기수 변환합니다.");

do {

do {

System.out.print("변환하는 음이 아닌 정수:");

no = stdIn.nextInt();

} while (no < 0);


do {

System.out.print("어떤 진수로 변환할까요? (2~36):");

cd = stdIn.nextInt();

} while (cd < 2 || cd > 36);


dno = cardConvR(no, cd, cno); // no를 cd진수로 변환


System.out.print(cd + "진수로는 ");

for (int i = dno - 1; i >= 0; i--) // 배열 끝부터 차례로 나타냄

System.out.print(cno[i]);

System.out.println("입니다.");


System.out.print("한 번 더 할까요? (1.예/0.아니오):");

retry = stdIn.nextInt();

} while (retry == 1);

}

}


1. cardConvR라는 기수변환을 하는 메소드를 만들어준다.

- int x는 변환받을 숫자, int r은 변환하고 싶은 기수, char[] d는 변환후 숫자를 넣는 배열  

- digits 변수를 선언하고 0으로 초기화한다.(배열의 자릿수)

- String 클래스를 사용해 문자열 dchar에 0~Z를 초기화시킨다.

- do문을 이용해 x가 0이 아닐때까지 do문의 내용을 반복한다.

- charAt 메서드를 이용해 dchar 문자열에 접근하여 x%r의 나머지를 괄호안에 넣고

  그 나머지의 값번쨰에 있는 문자를 d배열에 저장한다.

- 이때, d[0]부터 시작하고 digits++이므로 실행후 digits +1이 된다.

- do문이 끝나면 digits값을 반환한다.

2. 메인함수에서 우선 입력을 받기위해 Scanner 클래스를 import하여준다.

3. 사용할 변수들을 선언해준다.

4. do문(한번더할지 판단하기위해서)을 작성한다.

- 변환받을 정수를 입력받는 do문을 작성한다(음수를 입력하면 반복)

- 변환할 진수를 입력받는 do문을 작성한다(2미만 37이상 입력하면 반복)

- cardConvR 함수를 호출한다. -> 반환받는값은 digits(자릿수)이다.

- 바뀐 숫자를 출력하는 for문을 작성한다.

- i는 자릿수-1 로시작하여 0보다 작을때까지 반복

- 바뀐 값이 저장되어있는 배열을 끝에서부터(d[dno-1]부터) 출력한다.

-한 번 더 할지 물어보는 문구를 출력한다.(1입력:다시, 0입력:끝)



[Java knowledge]


▷ 자료구조란?


- 데이터 단위와 데이터 자체 사이의 물리적 또는 논리적인 관계

- 자료구조는 쉽게 말해 자료를 효율적으로 이용할 수 있도록 컴퓨터에 저장하는 방법을 말한다.



▷ 배열


- 배열은 같은 자료형의 변수로 이루어진 구성 요소(component)가 모인 것이다.

- 배열은 같은 형의 구성요소가 연속하여 줄지어 있는 단순한 자료구조이다.


- *배열의 선언 방식*

  int[] a;

  int a[]; 

  -> a는 자료형이 int형인 배열 -> 단순한 int형이 아닌 int형 배열임을 확실히 나타내는 

     위에있는 방식이 훨씬 많이 사용된다.

  

  a = new int[5];    -> a는 길이가 5인 배열을 참조한다.

  -> 위 선언은 int형의 배열 본체를 new를 통해 생성하고 그것을 변수 a가 "참조"하도록 설정함.


int[] a = new int[5];    -> 배열 선언은 이런식으로 하면 된다.


int[] a = {1, 2, 3, 4, 5};    -> 초기화를 사용하면 배열 본체 생성과 동시에 각 요소의 초기화 가능.

int[] a = new int[] {1, 2, 3, 4, 5}; 를 사용하면 좀 더 명확하게 선언할 수 있다.


- 배열의 개별 요소에 접근하기 위해 사용하는 것이 연산자 [ ] 안에 넣는 정수형 인덱스이다.

- 첫 번째 배열 요소의 인덱스는 0으로 정해져 있다.

- ex) a[0], a[1], ....., a[i]

-> 즉, 배열에서 구성 요소는 a[0]과 같은 것을 말한다.


- 배열의 구성 요소의 개수를 구성 요솟수라하고  length라는 변수가 만들어진다.

- 배열의 구성 요솟수 = 배열의 길이(length)

- 배열의 구성 요솟수를 알고 싶으면 [배열 변수 이름.length]를 쓰면된다.


- 배열 a가 있을 때 각 요소의 자료형은 int형이지만 배열 a자체의 자료형은 int[5]형이다.

  즉,  a[0]은 int형, a는 int[5]형이다.

- 배열의 구성요소는 자동으로 0으로 초기화되는 규칙이 있다. 

  이는 보통의 변수와 크게 다르므로 꼭 기억해 두어야 한다.


- 배열의 복제(클론) : 배열 이름.clone();    -> int[] a = b.clone();

  이것을 사용하면 배열 b를 복제하여 그것을 참조한다.



▷ 난수(Random)


- 배열의 요소에 값을 하나씩 기입하기 귀찮을 때 각 요소에 난수를 대입하면 된다.

- import java.util.Random;    ->Random 클래스를 사용해 난수를 발생시킬 수 있다.

  Random rand = new Random();    -> Random 변수 rand를 지정하고 랜덤기능 사용.

  int a = rand.nextInt(90);    -> 난수를 생성하는 메서드 nextInt를 호출한다.

   -> 괄호 안에 들어가는 숫자는 난수를 발생시킬 범위이다.

        즉, n이 들어가면 0~n-1까지의 난수가 생성된다.



▷ 배열을 역순으로 정렬하는 방법(알고리즘)


- 예를 들어, 배열 a의 요소 개수가 5이고 첫 번째부터 순서대로 {1, 2, 3, 4, 5}가 들어 있다고 할 때

  이를 {5, 4, 3, 2, 1}로 바꾸는 방법

- a[0]과 a[4]을 교환 / a[1] 과 a[3]을 교환 / a[2]는 교환할 필요 없음.

-> 이것이 말하는 것은 맨 처음 값과 맨 끝의 값을 교환하고 교환한 값은 뛰어넘고 두번째값과 

    맨 끝에서 2번째값을 교환하는 식으로 하면 역순정렬이 된다.

- 교환 횟수는 요솟수/2 이며 요솟수가 짝수던 홀수던지 요솟수/2를 하면 된다.

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

a[i]와 a[n-i-1]의 값을 교환하는 코드

  이런식으로 작성하면 된다.


- a[i]와 a[n-i-1]의 값을 교환하는 코드

  static void swap(int[] a, int idx1, int idx2) {

int t = a[idx1];    a[idx1]=a[idx2];    a[idx2] = t;

  } 이렇게 교환하면 된다.

  이를 설명해보면 x=1의 값을 y=2의 값과 교환하려고 할 때 y = x 이렇게 해버리면 x=1 y=1이

  되어버려서 교환이 불가능하다. 따라서 수를 잠시 보관하는 변수가 필요한데 그것을 t라고 하면

  t=x; -> t=1, x=1    / x=y; => x=2, y=2    / y=t; -> x=2, y=1이 되어 교환이 가능해진다.



▷ 두 배열을 비교하는 알고리즘



- static boolean equals(int[] a, int[] b) {

if(a.length !=b.length)

return false;


for (int i = 0; i < a.length; i++) {

if(a[i] != b[i])

return false;

}


retrun true;

  }

-> 1. 두 배열 a,b의 길이를 비교 -> 길이가 다르면 배열이 같지 않으므로 false(거짓) 반환

    2. for문을 통해 두 배열을 인덱스 0부터 비교하여 다른 요소를 발견하면 false(거짓) 반환

    3. return true; 에 도달하면 for문이 끝까지 실행되었으니 이 배열은 같다. 따라서 true(참) 반환



▷ String 클래스


- 문자열을 나타내는 것 : java.lang 패키지에 소속되어 있다.

- 이 형은 기본형(int, double 등)이 아니다.

- String 클래스는 문자열을 넣어두기 위한 문자 배열, 문자 수를 나타내는 필드 등을 갖고 있다.

- 선언 방식 : String a = "ABC";

- String 클래스는 많은 생성자와 메서드를 제공한다.

  1. char charAt(int i) -> 인덱스가 i인 곳의 문자를 가져옵니다.

  2. int length() -> 문자열의 문자 수를 가져옵니다.

  3. boolean equals(String s) -> 문자열 s와 같은지를 조사합니다.



▷ 기수 변환


- 기수란 수를 나타내는 데 기초가 되는 수로, 10진법에서는 0~9까지의 정수를 말한다.

- 프로그래밍에서 정수 상수가 0x, 0X로 시작되면 16진수이고, 0으로 시작되면 8진수, 두 경우에 
  해당하지 않으면 10진수로 간주한다.


- 10진수 정수를 n진수 정수로 변환하려면 정수를 n으로 나눈 나머지를 구하는 동시에 그 몫에

  대해 나눗셈을 반복하여야 한다.

  예를 들어, 33을 4진수 정수로 변환할 때, 

  33/4 -> 몫:8, 나머지:1     -> 다시 몫 8을 4로 나누면

  8/4  -> 몫:2, 나머지:0      -> 다시 몫 2를 4로 나누면

  2/4  -> 몫:0, 나머지:2      -> 몫이 0이 되었으므로 멈춤

  이런식으로 구한 나머지를 거꾸로 늘어 놓은 숫자가 기수로 변환한 숫자이다.

  즉, 33을 4진수 정수로 변환하면 201 = 2*4^2 + 0*4^1 + 1*4^0 = 32 + 0 + 1 = 33 이다.


- class CardConvRev {

static int cardConvR(int x, int r, char[] d) {

int digits = 0; // 변환 후의 자릿수

String dchar = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";


do {

d[digits++] = dchar.charAt(x % r); // r로 나눈 나머지를 저장

x /= r;

} while (x != 0);

return digits;

}

  1. 여기에서 문자열 dchar에 0~Z를 초기화해둔다. -> dchar.charAt(0) 은 문자 '0' 이런식이다.

  2. dchar.charAt(x%r)에서 x가 73이고 r이 16이라면 x%r은 9이다. 따라서 dchar.charAt(x%r)가 

     반환하는 것은 문자 '9'이다.

  3. x를 r로 나눈 나머지를 인덱스로 하는 문자를 배열 d의 요소 d[digits]에 대입하고 digits값을 

     증가시킨다.

  4. x = x / r 을 한다.

  5. 3~4까지의 작업을 x가 0이 될때까지 반복한다.

  6. 나머지를 구하는 순서대로 배열 d의 저장되어 배열 d의 맨 앞쪽(d[0])이 우리가 변환하여 

     구할 수의 마지막 자리가 된다. -> d값은 역순으로 배치되어 있다.

  7. 이 함수가 끝나면 위의 올려둔 본문에서 작업을 거쳐 기수를 변환한 값을 출력한다.



▷ 삽질 했던 것

  

- 기수변환 코드를 보며 분명히 cardConvR 함수에서 d배열에 값이 저장되는데 본문에서는 

  cno배열을 사용하여 변환한 수를 출력한다.

  내가 C를 배웠을 때 알기로는 함수를 호출할 때 쓰는 인자와 함수에 있는 매개변수는 int a = b 

  이런식으로 복사하여 참조하는것으로 알고있었는데 여기코드에서는 마치 C언어 포인터같이

  주소를 공유하는것처럼 결과값이 나온다.

  그래서 내가 배열 코드를 짜서 실험을 해보았다.


위 코드는 배열 a와 b가 주소를 공유한다면 b의 값을 바꾸었을때 a의 값도 바뀌어야하는데 

컴파일해보니 a의 값은 바뀌지않았다. 그래서 매개변수와 인자를 통한 실험을 밑에처럼 해보았다.



위처럼 코드를 짜서 실행해보니 배열 a가 바뀌니 b도 바뀌었다. 이는 주소를 공유한다는 뜻이다.

그래서 뭐가 다른점인가를 계속생각해보다가 맨위코드는 구성요소를 참조했고 잘 바뀐코드는 

배열자체를 참조하였던 것이다. 그래서 배열자체를 참조하는 방식으로 실험을 해봤다.


그래서 결국 위의 코드가 완성되었고 a의 값이 바뀌니 b의 값도 잘 바뀌었다.


그래서 결론은 a[i] = b[i] 이런식으로 참조하는것은 a[i]는 int b와 같은 하나의 변수처럼 취급하기 때문에 a = b를 하는것과 똑같이 되어버린다. 즉, 그냥 복사를 해버린다는 것이다.

하지만 int[] a = b; 이런식으로 배열자체를 참조하게되면 b배열이 가지는 배열 본체를 a가 같이 참조하기때문에 a가 바뀌면 b도 바뀌게된다.





2강 시작이 순조롭네요 다음에 또 뵙겠습니다.