본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


JAVA

JAVA 스터디 7 - 자료구조 LinkedList , Stack , Queue

반응형

 

과제 2. LinkedList를 구현하세요.

  • LinkedList에 대해 공부하세요.
  • 정수를 저장하는 ListNode 클래스를 구현하세요.
  • ListNode add(ListNode head, ListNode nodeToAdd, int position)를 구현하세요.
  • ListNode remove(ListNode head, int positionToRemove)를 구현하세요.
  • boolean contains(ListNode head, ListNode nodeTocheck)를 구현하세요.

과제 3. Stack을 구현하세요.

  • int 배열을 사용해서 정수를 저장하는 Stack을 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.

과제 4. 앞서 만든 ListNode를 사용해서 Stack을 구현하세요.

  • ListNode head를 가지고 있는 ListNodeStack 클래스를 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.

과제 5. Queue를 구현하세요.

  • 배열을 사용해서 한번
  • ListNode를 사용해서 한번.

1.LinkedList 란?

LinkedList는 ArrayList와 같이 여러가지 같은 타입의 오브젝트들을 모아 놓은 클래스이다. LinkedList는  ArrayList와 함께 List Interface를 구현하고 있기때문에  서로같은  메서드들(add , remove 등등 . .)을 포함하고 있다. 

 그러나 , ArrayList 와 LinkedList는 같은 방법으로 사용될 수는 있지만. 그 둘은 굉장히 다르게 만들어졌다 . 

 

ArrayList 작동방식

 

 ArrayList는 기본 배열을 구조를 가지고 있다 . 어떤 개체가 추가 될때 , 그 개체는 배열에 배치된다 . 배열의 크기가 충분하지 않은 경우 새로운 더큰 배열이 만들어지고  이전의 배열을 대체하며 , 이전 배열은 사라진다. 각 요소는 인덱스를 가지고 있기 때문에 한번에 참조가 가능해 데이터의 검색에 유리한 구현체이다

 

LinkedList 작동방식

 

 LinkedList는 아이템을 "containers"에 배치한다 . 리스트는 첫 번째 컨테이너에 대한 링크가 있고 각 컨테이너에는 각각의 하나의 컨테이너에서 다음 컨테이너로 링크로 연결되 있다. 리스트에 요소를 추가하면 그 요소에 새컨테이너가 배치되고 해당 컨테이너가 목록의 다른 컨테이너 중 하나에 연결된다.  각 노드가 이전 노드와 다음 노드의 상태만 알고 있다 , ArrayList와 다르게 데이터 추가 ,삭제시 불필요한 데이터의 복사가 없어 데이터의 추가 ,삭제에 유리하지만, 데이터 검색시에는 처음부터 노드를 순회해야 하기 때문에 성능상 불리하다.

 

출처 https://codenuclear.com/difference-between-arraylist-and-linkedlist-arraylist-vs-linkedlist/
출처 https://codenuclear.com/difference-between-arraylist-and-linkedlist-arraylist-vs-linkedlist/

위는 LinkedList와 ArrayList의 아이템 삭제시 메모리 구조를 비교한 그림이다 . 

ArrayList는 새로운 임시 배열을 만들고 3번아이템을 제외한 아이템을 새로운 배열에 복사 한다 .

LinkedList는 3번 아이템이 제거 되고 2번과 4번 container가 link로 연결된다. 

 

LinkedNode 구현

 

 LinkedList와 관련메서드 addFirst, addLast, add, removeFirst , removeLast , remove , get , getSize ,toString을

구현해 보았다 . 

 

package create_data_structure;

public class LinkedList {
    //첫 번째 노드
    private Node head;
    //끝 노드
    private Node tail;
    //List 크기
    private int size = 0;

    public void addFirst(Object input) {
        //파라미터로 값을 받았고 새로운 노드를 생성
        Node newNode = new Node(input);
        //필드의 head 값은 이전 노드를 가리키고 있음으로
        //새로운 노드의 next 가 head가 된다는 것은
        //새로운 노드가 첫번째로 지정되고 head가 그 다음으로 지정되는 것이다.
        newNode.next = head;
        //새로운 node가 head가 되었다.
        head = newNode;
        //노드가 추가 되었음으로 사이즈가 1증가한다.
        size++;
        //다음 노드가 없다면 그 노드는 tail이 된다.
        if (head.next == null){
            tail = newNode;
        }
    }
    
    //끝 부분에 데이터를 추가하는 메서드
    public void addLast(Object input) {
        //새로운 노드생성
        Node newNode = new Node(input);
        //만약 저장된 node가없다면  addFirst를 하든 addLast하든 상관이 없다
        if(size == 0){
            addFirst(input);
        }else{
            //현재  tail의  다음을 새로운 노드로 지정해주고
            tail.next = newNode;
            //새로운 노드는 tail이 된다.
            tail = newNode;
            //사이즈를 1증가 시킨다
            size++;
        }
    }
    //내부적 사용으로는 private 가  좋지만 테스트용으로 public사용
    Node node(int index){
        //첫번째 노드를 가져와 x에 놨다
        Node x = head;
        //두번째 값을 가져온다.
        for (int i = 0 ; i<index; i++){
            x = x.next;
        }
        return x;
    }
    //노드를 특정 인덱스에 추가시키는 메서드
    public  void  add(int k ,Object input ){
        if(k == 0){
            addFirst(input);
        }else{
            Node temp1 = node(k - 1);
            Node temp2 = temp1.next;
            Node newNode = new Node(input);
            temp1.next = newNode;
            newNode.next = temp2;
            size++;
            if(newNode.next == null){
                tail = newNode;
            }
        }
    }
    //데이터 값 출력을 위한 toString 구현
    public String toString(){
        //헤드가 없다면 저장된 노드가 없는 것이다
        if(head == null){
            //빈 괄호를 리턴한다
            return "[]";
        }
        //헤드가 있다면 헤드를 가져와서
        Node temp = head;
        //괄호를 열고
        String str = "[";
        while (temp.next != null){
            //헤드서부터 끝까지의 node를 더해준다.
            str += temp.data + ",";
            //temp가 다음 node가 됨으로써 하나씩 다음으로 넘어간다
            temp = temp.next;
        }
        //마지막 노드는 위의 반복문 조건에 해당하지 않기떄문에
        //수동으로 마지막 노드를 추가해 준다.
        str += temp.data;
        return str+"]";
    }
    //첫번째 것을 지우고 그 값을 리턴한다.
    public Object removeFirst() {
        //탐색을 위해 첫번째 노드를 찾는다.
        Node temp = head;
        //첫번째 노드를 지워야하기떄문에 head를 다음으로 변경했다.
        head = head.next;
        Object returnData = temp.data;
        temp = null;
        size--;
        return returnData;
    }

    public Object remove(int i) {
        if(i == 0){
            removeFirst();
            return removeFirst();
        }else {
            Node temp = node(i-1);
            Node todoDeleted = temp.next;
            temp.next = temp.next.next;
            Object returnData = todoDeleted.data;
            if(todoDeleted == tail){
                tail = temp;
            }
            todoDeleted = null;
            size--;

            return returnData;
        }

    }

    public Object removeLast() {
        return remove(size-1);
    }

    public Object get(int i) {
        return node(i).data;
    }
    public int getSize(){
        return size;
    }


    //객체와 객체를 연결하여 만든다 (노드 , 엘리먼트라고도한다)
    //inner class로 만들었다
    //노드는 데이터와 , 다음 노드에 대한 정보를 포함한다
    private class Node{
        private Object data;
        private Node next;

        public Node(Object input) {
            this.data = input;
            this.next = null;
        }
        //데이터 값 출력을 위한 toString 구현
        public String toString(){
            return String.valueOf(this.data);
        }
    }
}

 

2.Stack 이란?

과제 3. Stack을 구현하세요.

  • int 배열을 사용해서 정수를 저장하는 Stack을 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.

과제 4. 앞서 만든 ListNode를 사용해서 Stack을 구현하세요.

  • ListNode head를 가지고 있는 ListNodeStack 클래스를 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.

과제 5. Queue를 구현하세요.

  • 배열을 사용해서 한번
  • ListNode를 사용해서 한번.

스택은 제한적으로 접근할 수 있는 나열된 구조이다. 후입선출(LIFO Last IN First Out)의 자료구조이며 , 접근이 목록의 끝(Top또는 Top Pointer)에서만 일어나기 떄문에 Pushdown List라고도 한다. 스택에서 입력은 push , 출력은 pop ,Top의 위치 데이터 확인은 peek을 사용한다. 

 스택은 추상자료형(Abstact Data Type)으로 수학적 모델을 가졌으며 구현 방법을 따로 명시하고 있지 않다는 점에서 자료구조와 차이를 보인다. 이러한 특징은 다양한 방법으로 구현될 수 있음을 의미한다

 

Stack의 사용

 

운영체제(OS)

-프로그램에서 사용되는 함수들을 스택 자료형에 저장하여 사용한다.

컴파일러

-수학 기호들을 기계어로 변환시 사용한다 (괄호 등을 매칭하는 경우).

가상머신(JVM)

-JVM 내에서 메서드가 실행 , 종료될 때 스택 프레임을 이용하여 관리한다.

 

Stack 의 구현 (배열로 구현)

 

Stack은 두가지 (Array와 LinkedList)로 구현할 것이기 떄문에 interface로 만들어 각각 구현하도록 하였다.

 

public interface Stack {
    boolean isEmpty();
    boolean isFull();
    void push(int item);
    int pop();
    int peek();
    String toString();
}
public class ArrayStack implements Stack{

    private int top;
    private int stackSize;
    private int itemArray[];

    public ArrayStack(int stackSize){
        //top은 제일 위의 위치한 요소를 가리킨다 . pointer개념
        //인덱스에서는 0번이 첫번째이기 떄문에 객체가 생성된 시점에서 요소가 존재하지 않기떄문에
        //top은 -1 이다.
        top = -1;
        //현재 stack의 크기는 배열의 크기와 같다.
        this.stackSize = stackSize;
        //배열을 생성하고 변수에 넣어준다.
        itemArray = new int[this.stackSize];
    }

    @Override
    //스텍이 비었는지 확인하는 메서드
    public boolean isEmpty() {
        //top이 -1이면 배열에 요소가 없는 상태이다.
        return (top == -1);
    }

    @Override
    public boolean isFull() {
        //만약 top이 stackSize-1 즉 마지막 인덱스라면 스텍이 꽉찬상태이다.
        return (top == this.stackSize-1);
    }

    @Override
    public void push(int item) {
        //꽉찬 상태인지 확인한다
        if(isFull()){
            System.out.println("StackOverflow Error!");
        }else{
            //top을 1 증가시키고 top에 해당하는 별의 인덱스에 item을 넣어준다.
            this.itemArray[++top] = item;
            //추가된 item을 출력한다.
            System.out.println("inserted Item :" + item);
        }
    }
    
    //스텍에서 맨위에 요소를 제거하는 메서드
    @Override
    public int pop() {
        //스텍이 비어있다면 pop할 수 없다.
        if(isEmpty()){
            System.out.println("StackUnderflow Error!");
            return 0;
        }else {
            //스택사이즈를 1줄인다.
            stackSize--;
            //새로운 배열을 만들고
            int [] newItemArray = new int[stackSize];
            //이전의 배열의 요소를 하나씩 새로운 배열에 넣어준다.
            for (int i =0;  i<newItemArray.length; i++) {
                newItemArray[i] = itemArray[i];
            }
            //새로운 배열을 itmeArray 변수에 담아주고
            itemArray = newItemArray;
            //top을 1감소 시켜준 후 해당값을 리턴한다.
            return itemArray[--top];
        }
    }
    //제일 위에 위치한 것을 리턴함
    @Override
    public int peek() {
        //스택이 비어 있다면 peek을 찾을 수 없다
        if(isEmpty()){
            System.out.println("StackUnderflow Error! -can not found peek");
            return 0;
        }
        //현재 top의 요소를 리턴한다.
        return itemArray[top];
    }
    //stack을 문자열로 리턴하는 메서드
    public String toString() {
        //만약 스택이 비어있다면 빈괄호를 리턴한다.
        if (stackSize == 0) {
            //빈 괄호를 리턴한다
            return "[]";
        }
        //괄호를 열어 주고 변수에 담는다.
        String array = "[";
        //열린괄호안에 itemArray의 요소를 하나씩  콤마와함께 넣어준다.

        for (int i =0; i<itemArray.length-1; i++) {
            if (!(i == itemArray.length-1)) {
                array += itemArray[i] + ",";
            }
        }
        //만약 마지막 요소라면 여기서 콤마없이 추가된다
        array += itemArray[itemArray.length - 1];
        //괄호를 닫고
        array = array + "]";
        //리턴한다.
        return array;
    }
}

 

반응형