자바에서 연결 목록을 재귀 적으로 뒤집기


101

나는 한동안 클래스를 위해 Java 프로젝트에서 일하고 있습니다. 이것은 연결 목록 (여기서는라고하며라는 AddressList단순 노드를 포함 함 ListNode) 의 구현입니다 . 문제는 모든 것이 재귀 알고리즘으로 이루어져야한다는 것입니다. 나는 한 가지 방법으로 모든 것을 잘 할 수 있었다.public AddressList reverse()

ListNode :

public class ListNode{
  public String data;
  public ListNode next;
}

지금 내 reverse함수는 재귀를 허용하기 위해 인수를 취하는 도우미 함수를 호출합니다.

public AddressList reverse(){
  return new AddressList(this.reverse(this.head));
}

내 도우미 기능에 private ListNode reverse(ListNode current).

현재 스택을 사용하여 반복적으로 작동하지만 사양이 요구하는 것은 아닙니다. 나는 C에서 재귀 적으로 뒤집어서 자바 코드로 직접 변환 한 알고리즘을 찾았지만 작동했지만 이해하지 못했다.

편집 : 신경 쓰지 마세요, 그동안 알아 냈어요.

private AddressList reverse(ListNode current, AddressList reversedList){
  if(current == null) 
      return reversedList;
  reversedList.addToFront(current.getData());
  return this.reverse(current.getNext(), reversedList);
}

내가 여기있는 동안이 경로에 문제가있는 사람이 있습니까?


2
아니요, 솔루션에는 문제가 없습니다. 반대로 원래 목록을 그대로 유지한다는 점에서 선호되는 "Little Lisper"솔루션보다 "더 좋습니다". 이는 변경 불가능한 값이 강력하게 선호되는 멀티 코어 설정에서 특히 유용합니다.
Ingo

답변:


318

하나의 답장에 그것을 설명하는 코드가 있지만, 작은 질문을하고 답함으로써 밑바닥부터 시작하는 것이 더 쉬울 수 있습니다 (이것은 The Little Lisper의 접근 방식입니다).

  1. null (빈 목록)의 반대는 무엇입니까? 없는.
  2. 단일 요소 목록의 반대는 무엇입니까? 요소.
  3. n 요소 목록의 반대는 무엇입니까? 나머지 목록의 역순에 첫 번째 요소가옵니다.

public ListNode Reverse(ListNode list)
{
    if (list == null) return null; // first question

    if (list.next == null) return list; // second question

    // third question - in Lisp this is easy, but we don't have cons
    // so we grab the second element (which will be the last after we reverse it)

    ListNode secondElem = list.next;

    // bug fix - need to unlink list from the rest or you will get a cycle
    list.next = null;

    // then we reverse everything from the second element on
    ListNode reverseRest = Reverse(secondElem);

    // then we join the two lists
    secondElem.next = list;

    return reverseRest;
}

30
와, "세 가지 질문"이 마음에 듭니다.
sdellysse

4
감사. 작은 질문은 Lisp 학습의 기초가되어야합니다. 그것은 또한 newbs로부터 귀납을 숨기는 방법이기도합니다. 이것이 본질적으로이 패턴입니다. 이 유형의 문제를 해결하고 싶다면 Little Lisper를 읽는 것이 좋습니다.
plinth

44
예외적 인 상황에 대한 예외. if에서 테스트 할 수있는 알려진 조건에 catch를 사용하는 이유는 무엇입니까?
Luke Schafer

4
list.next가 여전히 secondElem이기 때문에 secondElem 변수를 만들 필요가 없다고 생각합니다. "ListNode reverseRest = Reverse (secondElem);"이후에 먼저 "list.next.next = list"를 수행 한 다음 "list.next = null"을 수행 할 수 있습니다. 그리고 그게 다야.
ChuanRocks 2013

3
list.next = null 인 이유를 설명해 주시겠습니까? 나는주기를 이해하려고했지만 이해하지 못했습니다.
Rohit

29

인터뷰에서이 질문을 받았는데, 조금 긴장해서 더듬 거려서 짜증이났다.

이것은 reverse (head, NULL)로 호출되는 단일 연결 목록을 뒤집어 야합니다. 그래서 이것이 당신의 목록이라면 :

1-> 2-> 3-> 4-> 5-> null
다음과 같이됩니다.
5-> 4-> 3-> 2-> 1-> null

    //Takes as parameters a node in a linked list, and p, the previous node in that list
    //returns the head of the new list
    Node reverse(Node n,Node p){   
        if(n==null) return null;
        if(n.next==null){ //if this is the end of the list, then this is the new head
            n.next=p;
            return n;
        }
        Node r=reverse(n.next,n);  //call reverse for the next node, 
                                      //using yourself as the previous node
        n.next=p;                     //Set your next node to be the previous node 
        return r;                     //Return the head of the new list
    }
    

편집 : 이것에 대해 6 개의 편집처럼 끝났고, 그것이 여전히 나를 위해 조금 까다 롭다는 것을 보여줍니다.


2
솔직히 말해서 자바가 명시되어 있다면 인터뷰에서 "반복적이어야한다"는 요구 사항에 약간 미안해 할 것입니다. 그렇지 않으면 나는 p = null로 갈 것입니다; while (n.next! = null) {n2 = n.next; n.next = p; p = n; n = n2;} n.next = p; 반환 n ;. O (N) 스택은 새를위한 것입니다.
Steve Jessop

네, 머리에 대한 null 검사도 있습니다. 이것은 Java입니다.
Steve Jessop

23

나는 절반을 통과했지만 (널이 될 때까지, 주각이 제안한 하나의 노드) 재귀 호출을 한 후 트랙을 잃었습니다. 그러나 주각으로 게시물을 읽은 후 다음과 같이 생각했습니다.

Node reverse(Node head) {
  // if head is null or only one node, it's reverse of itself.
  if ( (head==null) || (head.next == null) ) return head;

  // reverse the sub-list leaving the head node.
  Node reverse = reverse(head.next);

  // head.next still points to the last element of reversed sub-list.
  // so move the head to end.
  head.next.next = head;

  // point last node to nil, (get rid of cycles)
  head.next = null;
  return reverse;
}

아주 nice.just 같은 죄수 :)
Karthikeyan D

9

여기에 또 다른 재귀 솔루션이 있습니다. 다른 것보다 재귀 함수 내에 코드가 적으므로 조금 더 빠를 수 있습니다. 이것은 C #이지만 Java는 매우 유사 할 것이라고 생각합니다.

class Node<T>
{
    Node<T> next;
    public T data;
}

class LinkedList<T>
{
    Node<T> head = null;

    public void Reverse()
    {
        if (head != null)
            head = RecursiveReverse(null, head);
    }

    private Node<T> RecursiveReverse(Node<T> prev, Node<T> curr)
    {
        Node<T> next = curr.next;
        curr.next = prev;
        return (next == null) ? curr : RecursiveReverse(curr, next);
    }
}

8

알고리즘은 다음 모델에서 작업해야합니다.

  • 머리를 추적하다
  • 링크 목록 끝까지 반복
  • 역 연결

구조:

Head    
|    
1-->2-->3-->4-->N-->null

null-->1-->2-->3-->4-->N<--null

null-->1-->2-->3-->4<--N<--null

null-->1-->2-->3<--4<--N<--null

null-->1-->2<--3<--4<--N<--null

null-->1<--2<--3<--4<--N<--null

null<--1<--2<--3<--4<--N
                       |
                       Head

암호:

public ListNode reverse(ListNode toBeNextNode, ListNode currentNode)
{               
        ListNode currentHead = currentNode; // keep track of the head

        if ((currentNode==null ||currentNode.next==null )&& toBeNextNode ==null)return currentHead; // ignore for size 0 & 1

        if (currentNode.next!=null)currentHead = reverse(currentNode, currentNode.next); // travarse till end recursively

        currentNode.next = toBeNextNode; // reverse link

        return currentHead;
}

산출:

head-->12345

head-->54321

7

LISP와 비슷한 더 깨끗한 솔루션이라고 생각합니다.

// Example:
// reverse0(1->2->3, null) => 
//      reverse0(2->3, 1) => 
//          reverse0(3, 2->1) => reverse0(null, 3->2->1)
// once the first argument is null, return the second arg
// which is nothing but the reveresed list.

Link reverse0(Link f, Link n) {
    if (f != null) {
        Link t = new Link(f.data1, f.data2); 
        t.nextLink = n;                      
        f = f.nextLink;             // assuming first had n elements before, 
                                    // now it has (n-1) elements
        reverse0(f, t);
    }
    return n;
}

7

나는 이것이 오래된 게시물이라는 것을 알고 있지만 대부분의 답변은 꼬리 재귀가 아닙니다. 즉 재귀 호출에서 돌아온 후 일부 작업을 수행하므로 가장 효율적이지 않습니다.

다음은 꼬리 재귀 버전입니다.

public Node reverse(Node previous, Node current) {
    if(previous == null)
        return null;
    if(previous.equals(head))
        previous.setNext(null);
    if(current == null) {    // end of list
        head = previous;
        return head;
    } else {
                    Node temp = current.getNext();
        current.setNext(previous);
        reverse(current, temp);
    }
    return null;    //should never reach here.
} 

전화 :

Node newHead = reverse(head, head.getNext());

9
메서드에서 "head"라는 변수를 참조하지만 어디에도 선언되어 있지 않습니다.
마라톤 '

아마 노드 헤드 속성이 포함 된 목록 클래스에 대한 방법입니다
ChrisMcJava

4
void reverse (node1, node2) {
if (node1.next! = null)
      reverse (node1.next, node1);
   node1.next = node2;
}
이 메서드를 reverse (start, null);

4
public Node reverseListRecursive(Node curr)
{
    if(curr == null){//Base case
        return head;
    }
    else{
        (reverseListRecursive(curr.next)).next = (curr);
    }
    return curr;
}

3
public void reverse() {
    head = reverseNodes(null, head);
}

private Node reverseNodes(Node prevNode, Node currentNode) {
    if (currentNode == null)
        return prevNode;
    Node nextNode = currentNode.next;
    currentNode.next = prevNode;
    return reverseNodes(currentNode, nextNode);
}

이것이 최선의 해결책이라고 생각합니다. 간단하고 꼬리 재귀를 최적화 할 수 있고 하나의 null 검사 만 가능합니다.
sdanzig

2
public static ListNode recRev(ListNode curr){

    if(curr.next == null){
        return curr;
    }
    ListNode head = recRev(curr.next);
    curr.next.next = curr;
    curr.next = null;

    // propogate the head value
    return head;

}

이것이 최선의 해결책이지만 설명이 제공 되지 않기 때문에 최선의 대답 은 아닙니다. :). 처음에는 비슷한 솔루션을 도출했지만 헤드 참조를 잃었습니다. 이 솔루션은이를 해결합니다.
OpenUserX03

2

재귀 알고리즘으로 되돌립니다.

public ListNode reverse(ListNode head) {
    if (head == null || head.next == null) return head;    
    ListNode rHead = reverse(head.next);
    rHead.next = head;
    head = null;
    return rHead;
}

반복적으로

public ListNode reverse(ListNode head) {
    if (head == null || head.next == null) return head;    
    ListNode prev = null;
    ListNode cur = head
    ListNode next = head.next;
    while (next != null) {
        cur.next = prev;
        prev = cur;
        cur = next;
        next = next.next;
    }
    return cur;
}

불행히도 재귀 역이 잘못되었습니다!
Sree Aurovindh 2013 년

@SreeAurovindh-왜?
rayryeng

2

이 솔루션은 인수가 필요하지 않음을 보여줍니다.

/**
 * Reverse the list
 * @return reference to the new list head
 */
public LinkNode reverse() {
    if (next == null) {
        return this; // Return the old tail of the list as the new head
    }
    LinkNode oldTail = next.reverse(); // Recurse to find the old tail
    next.next = this; // The old next node now points back to this node
    next = null; // Make sure old head has no next
    return oldTail; // Return the old tail all the way back to the top
}

이것이 작동 함을 보여주는 지원 코드는 다음과 같습니다.

public class LinkNode {
    private char name;
    private LinkNode next;

    /**
     * Return a linked list of nodes, whose names are characters from the given string
     * @param str node names
     */
    public LinkNode(String str) {
        if ((str == null) || (str.length() == 0)) {
            throw new IllegalArgumentException("LinkNode constructor arg: " + str);
        }
        name = str.charAt(0);
        if (str.length() > 1) {
            next = new LinkNode(str.substring(1));
        }
    }

    public String toString() {
        return name + ((next == null) ? "" : next.toString());
    }

    public static void main(String[] args) {
        LinkNode head = new LinkNode("abc");
        System.out.println(head);
        System.out.println(head.reverse());
    }
}

2

다음은 간단한 반복 접근 방식입니다.

public static Node reverse(Node root) {
    if (root == null || root.next == null) {
        return root;
    }

    Node curr, prev, next;
    curr = root; prev = next = null;
    while (curr != null) {
        next = curr.next;
        curr.next = prev;

        prev = curr;
        curr = next;
    }
    return prev;
}

다음은 재귀 적 접근 방식입니다.

public static Node reverseR(Node node) {
    if (node == null || node.next == null) {
        return node;
    }

    Node next = node.next;
    node.next = null;

    Node remaining = reverseR(next);
    next.next = node;
    return remaining;
}

1

Java는 항상 값으로 전달되므로 Java에서 연결 목록을 재귀 적으로 되돌리려면 재귀가 끝날 때 "새 헤드"(되 돌린 후 헤드 노드)를 반환해야합니다.

static ListNode reverseR(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }

    ListNode first = head;
    ListNode rest = head.next;

    // reverse the rest of the list recursively
    head = reverseR(rest);

    // fix the first node after recursion
    first.next.next = first;
    first.next = null;

    return head;
}

1

PointZeroTwo는 Java에서 우아한 대답과 동일합니다 ...

public void reverseList(){
    if(head!=null){
        head = reverseListNodes(null , head);
    }
}

private Node reverseListNodes(Node parent , Node child ){
    Node next = child.next;
    child.next = parent;
    return (next==null)?child:reverseListNodes(child, next);
}

이것은 list 메소드가 항상 목록을 인수로 받아들이는 것이 아니라 자체 자식으로 반전되기를 원하기 때문에 완벽합니다. 감사합니다
Manu

0
public class Singlelinkedlist {
  public static void main(String[] args) {
    Elem list  = new Elem();
    Reverse(list); //list is populate some  where or some how
  }

  //this  is the part you should be concerned with the function/Method has only 3 lines

  public static void Reverse(Elem e){
    if (e!=null)
      if(e.next !=null )
        Reverse(e.next);
    //System.out.println(e.data);
  }
}

class Elem {
  public Elem next;    // Link to next element in the list.
  public String data;  // Reference to the data.
}

0
public Node reverseRec(Node prev, Node curr) {
    if (curr == null) return null;  

    if (curr.next == null) {
        curr.next = prev;
        return curr;

    } else {
        Node temp = curr.next; 
        curr.next = prev;
        return reverseRec(curr, temp);
    }               
}

다음을 사용하여 호출하십시오. head = reverseRec (null, head);


0

다른 사람들이 한 것은 콘텐츠 게임입니다. 제가 한 것은 링크드리스트 게임입니다. 그것은 멤버의 가치를 뒤집지 않고 LinkedList의 멤버를 뒤집습니다.

Public LinkedList reverse(LinkedList List)
{
       if(List == null)
               return null;
       if(List.next() == null)
              return List;
       LinkedList temp = this.reverse( List.next() );
       return temp.setNext( List );
}

SRY 내가 널 값으로, 당신은 또한 꼬리의 다음을 설정하는 도우미 메서드를 필요 잊어
니마 Ghaedsharafi을

0
package com.mypackage;
class list{

    node first;    
    node last;

    list(){
    first=null;
    last=null;
}

/*returns true if first is null*/
public boolean isEmpty(){
    return first==null;
}
/*Method for insertion*/

public void insert(int value){

    if(isEmpty()){
        first=last=new node(value);
        last.next=null;
    }
    else{
        node temp=new node(value);
        last.next=temp;
        last=temp;
        last.next=null;
    }

}
/*simple traversal from beginning*/
public void traverse(){
    node t=first;
    while(!isEmpty() && t!=null){
        t.printval();
        t= t.next;
    }
}
/*static method for creating a reversed linked list*/
public static void reverse(node n,list l1){

    if(n.next!=null)
        reverse(n.next,l1);/*will traverse to the very end*/
    l1.insert(n.value);/*every stack frame will do insertion now*/

}
/*private inner class node*/
private class node{
    int value;
    node next;
    node(int value){
        this.value=value;
    }
    void printval(){
        System.out.print(value+" ");
    }
}

 }

0

해결책은 다음과 같습니다.

package basic;

import custom.ds.nodes.Node;

public class RevLinkedList {

private static Node<Integer> first = null;

public static void main(String[] args) {
    Node<Integer> f = new Node<Integer>();
    Node<Integer> s = new Node<Integer>();
    Node<Integer> t = new Node<Integer>();
    Node<Integer> fo = new Node<Integer>();
    f.setNext(s);
    s.setNext(t);
    t.setNext(fo);
    fo.setNext(null);

    f.setItem(1);
    s.setItem(2);
    t.setItem(3);
    fo.setItem(4);
    Node<Integer> curr = f;
    display(curr);
    revLL(null, f);
    display(first);
}

public static void display(Node<Integer> curr) {
    while (curr.getNext() != null) {
        System.out.println(curr.getItem());
        System.out.println(curr.getNext());
        curr = curr.getNext();
    }
}

public static void revLL(Node<Integer> pn, Node<Integer> cn) {
    while (cn.getNext() != null) {
        revLL(cn, cn.getNext());
        break;
    }
    if (cn.getNext() == null) {
        first = cn;
    }
    cn.setNext(pn);
}

}


0
static void reverseList(){

if(head!=null||head.next!=null){
ListNode tail=head;//head points to tail


ListNode Second=head.next;
ListNode Third=Second.next;
tail.next=null;//tail previous head is poiniting null
Second.next=tail;
ListNode current=Third;
ListNode prev=Second;
if(Third.next!=null){



    while(current!=null){
    ListNode    next=current.next;
        current.next=prev;
        prev=current;
        current=next;
    }
    }
head=prev;//new head
}
}
class ListNode{
    public int data;
    public ListNode next;
    public int getData() {
        return data;
    }

    public ListNode(int data) {
        super();
        this.data = data;
        this.next=null;
    }

    public ListNode(int data, ListNode next) {
        super();
        this.data = data;
        this.next = next;
    }

    public void setData(int data) {
        this.data = data;
    }
    public ListNode getNext() {
        return next;
    }
    public void setNext(ListNode next) {
        this.next = next;
    }





}

0
private Node ReverseList(Node current, Node previous)
    {
        if (current == null) return null;
        Node originalNext = current.next;
        current.next = previous;
        if (originalNext == null) return current;
        return ReverseList(originalNext, current);
    }

ReverseList (head, null)로 시작
2014 년

0
//this function reverses the linked list
public Node reverseList(Node p) {
    if(head == null){
        return null;
    }
    //make the last node as head
    if(p.next == null){
        head.next = null;
        head = p;
        return p;
    }
    //traverse to the last node, then reverse the pointers by assigning the 2nd last node to last node and so on..
    return reverseList(p.next).next = p;
}

0
//Recursive solution
class SLL
{
   int data;
   SLL next;
}

SLL reverse(SLL head)
{
  //base case - 0 or 1 elements
  if(head == null || head.next == null) return head;

  SLL temp = reverse(head.next);
  head.next.next = head;
  head.next = null;
  return temp;
}

0

기사에서 영감을 얻음재귀 데이터 구조의 변경 불가능한 구현을 논의 Swift를 사용하여 대체 솔루션을 구성했습니다.

다음 주제를 강조하여 최고의 답변 문서 솔루션 :

  1. nil (빈 목록)의 반대는 무엇입니까?
    • 여기서는 중요하지 않습니다. Swift에는 보호 기능이 없기 때문입니다.
  2. 단일 요소 목록의 반대는 무엇입니까?
    • 요소 자체
  3. n 요소 목록의 반대는 무엇입니까?
    • 두 번째 요소의 반대 다음 첫 번째 요소.

아래 솔루션에서 해당하는 경우이를 호출했습니다.

/**
 Node is a class that stores an arbitrary value of generic type T 
 and a pointer to another Node of the same time.  This is a recursive 
 data structure representative of a member of a unidirectional linked
 list.
 */
public class Node<T> {
    public let value: T
    public let next: Node<T>?

    public init(value: T, next: Node<T>?) {
        self.value = value
        self.next = next
    }

    public func reversedList() -> Node<T> {
        if let next = self.next {
            // 3. The reverse of the second element on followed by the first element.
            return next.reversedList() + value
        } else {
            // 2. Reverse of a one element list is itself
            return self
        }
    }
}

/**
 @return Returns a newly created Node consisting of the lhs list appended with rhs value.
 */
public func +<T>(lhs: Node<T>, rhs: T) -> Node<T> {
    let tail: Node<T>?
    if let next = lhs.next {
        // The new tail is created recursively, as long as there is a next node.
        tail = next + rhs
    } else {
        // If there is not a next node, create a new tail node to append
        tail = Node<T>(value: rhs, next: nil)
    }
    // Return a newly created Node consisting of the lhs list appended with rhs value.
    return Node<T>(value: lhs.value, next: tail)
}

0

재귀를 사용하여 연결 목록을 뒤집습니다. 아이디어는 링크를 반전하여 링크를 조정하는 것입니다.

  public ListNode reverseR(ListNode p) {

       //Base condition, Once you reach the last node,return p                                           
        if (p == null || p.next == null) { 
            return p;
        }
       //Go on making the recursive call till reach the last node,now head points to the last node

        ListNode head  = reverseR(p.next);  //Head points to the last node

       //Here, p points to the last but one node(previous node),  q points to the last   node. Then next next step is to adjust the links
        ListNode q = p.next; 

       //Last node link points to the P (last but one node)
        q.next = p; 
       //Set the last but node (previous node) next to null
        p.next = null; 
        return head; //Head points to the last node
    }

1
제공하는 솔루션에 대한 설명을 추가하여 답변을 더 자세히 설명해 주시겠습니까?
abarisone

1
댓글을 추가했습니다. 고마워요
gurubelli

0
public void reverseLinkedList(Node node){
    if(node==null){
        return;
    }

    reverseLinkedList(node.next);
    Node temp = node.next;
    node.next=node.prev;
    node.prev=temp;
    return;
}

-1
public void reverse(){
    if(isEmpty()){
    return;
     }
     Node<T> revHead = new Node<T>();
     this.reverse(head.next, revHead);
     this.head = revHead;
}

private Node<T> reverse(Node<T> node, Node<T> revHead){
    if(node.next == null){
       revHead.next = node;
       return node;
     }
     Node<T> reverse = this.reverse(node.next, revHead);
     reverse.next = node;
     node.next = null;
     return node;
}

-1

누군가 Scala 구현을 찾고 있다면 다음과 같은 참고 자료가 있습니다.

scala> import scala.collection.mutable.LinkedList
import scala.collection.mutable.LinkedList

scala> def reverseLinkedList[A](ll: LinkedList[A]): LinkedList[A] =
         ll.foldLeft(LinkedList.empty[A])((accumulator, nextElement) => nextElement +: accumulator)
reverseLinkedList: [A](ll: scala.collection.mutable.LinkedList[A])scala.collection.mutable.LinkedList[A]

scala> reverseLinkedList(LinkedList("a", "b", "c"))
res0: scala.collection.mutable.LinkedList[java.lang.String] = LinkedList(c, b, a)

scala> reverseLinkedList(LinkedList("1", "2", "3"))
res1: scala.collection.mutable.LinkedList[java.lang.String] = LinkedList(3, 2, 1)

반대 투표를 한 사람이 자신의 행동에 대한 설명을 해주면 내 답변을 개선 할 수있어 기쁩니다. 어쨌든, 그것은 여전히 : 스칼라에서 나를 위해 작동
벤 카트 Sudheer 레디 Aedama

다운 보터가 알고 있듯이 이것은 재귀 적 (사실은 꼬리 재귀 적) 솔루션입니다.
Venkat Sudheer Reddy Aedama 2014-06-15

Scala는 둘 다 JVM에서 실행 되더라도 Java가 아닙니다.
Bill Lynch

@sharth 와우, 반갑습니다. 내 대답의 첫 번째 줄을 귀찮게 읽었습니까?
Venkat Sudheer Reddy Aedama

@VenkatSudheerReddyAedama 원래 질문이 Java 구현을 요청했기 때문에 반대 투표를 받았습니다. Scala는 JVM에서 실행되지만 질문에 답하는 데 도움이되지는 않습니다. 매우 우아하지만. FWIW, 나는 당신을 비하하지 않았습니다.
rayryeng 2014-06-17
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.