IntLinkedBag 클래스

2025. 4. 18. 18:57자료구조

코드 설명

/******************************************************************************
* IntLinkedBag은 int 숫자들의 컬렉션입니다.
*
* @note
*   (1) 요소 수가 Int.MAX_VALUE를 초과하면 countOccurrences, 
*   size, grab 메서드의 결과가 올바르지 않을 수 있습니다.
*   <p>
*   (2) 이 클래스의 선형 알고리즘들은 느리므로, 많은 요소를 담는 경우
*   성능이 좋지 않습니다.
*
******************************************************************************/
public class IntLinkedBag implements Cloneable
{
   // IntLinkedBag 클래스의 불변 조건:
   //   1. 이 가방의 요소들은 연결 리스트로 저장됩니다.
   //   2. 리스트의 첫 번째 노드는 head 인스턴스 변수에 저장됩니다.
   //   3. 리스트의 전체 요소 수는 manyNodes 인스턴스 변수에 저장됩니다.
   private IntNode head;
   private int manyNodes;   


   /**
   * 빈 가방을 초기화합니다.
   * @param - 없음
   * @postcondition
   *   이 가방은 비어 있습니다.
   **/
   public IntLinkedBag( )
   {
      head = null;
      manyNodes = 0;
   }
        
 
   /**
   * 새로운 요소를 가방에 추가합니다.
   * @param element
   *   추가할 새로운 요소
   * @postcondition
   *   요소의 복사본이 가방에 추가됩니다.
   * @exception OutOfMemoryError
   *   새로운 IntNode를 생성할 메모리가 부족한 경우
   **/
   public void add(int element)
   {      
      head = new IntNode(element, head);
      manyNodes++;
   }


   /**
   * 다른 가방의 내용을 이 가방에 추가합니다.
   * @param addend
   *   이 가방에 추가될 요소들을 가진 가방
   * @precondition
   *   매개변수 addend는 null이 아니어야 합니다.
   * @postcondition
   *   addend의 요소들이 이 가방에 추가됩니다.
   * @exception NullPointerException
   *   addend가 null인 경우
   * @exception OutOfMemoryError
   *   가방의 크기를 늘릴 메모리가 부족한 경우
   **/
   public void addAll(IntLinkedBag addend)
   {
      IntNode[] copyInfo;

      if (addend.manyNodes > 0)
      {
         copyInfo = IntNode.listCopyWithTail(addend.head);
         copyInfo[1].setLink(head); // 복사된 리스트의 꼬리를 내 head에 연결
         head = copyInfo[0];        // head를 복사된 리스트의 head로 설정
         manyNodes += addend.manyNodes;
      }
   }

     
   /**
   * 여러 개의 새로운 요소들을 이 가방에 추가합니다.
   * @param elements
   *   (가변 인자)
   *   삽입될 하나 이상의 요소들
   * @postcondition
   *   각 요소의 복사본이 이 가방에 추가됩니다.
   * @exception OutOfMemoryError
   *   가방의 크기를 늘릴 메모리가 부족한 경우
   **/
   public void addMany(int... elements)
   {
      for (int i : elements)
         add(i); 	       
   }

     
   /**
   * 이 가방의 복사본을 생성합니다.
   * @param - 없음
   * @return
   *   이 가방의 복사본을 반환합니다. 복사본과 원본은 이후의 변경에 영향을 미치지 않습니다.
   * @exception OutOfMemoryError
   *   복사를 위한 메모리가 부족한 경우
   **/ 
   public Object clone()
   {
      IntLinkedBag answer;

      try
      {
         answer = (IntLinkedBag) super.clone();
      }
      catch (CloneNotSupportedException e)
      {
         throw new RuntimeException("이 클래스는 Cloneable을 구현하지 않았습니다.");
      }

      answer.head = IntNode.listCopy(head);
      return answer;
   }
   

   /**
   * 특정 요소가 이 가방에 몇 번 포함되어 있는지 확인합니다.
   * @param target
   *   개수를 셀 요소
   * @return
   *   target이 가방에 존재하는 횟수
   **/
   public int countOccurrences(int target)
   {
      int answer = 0;
      IntNode cursor = IntNode.listSearch(head, target);

      while (cursor != null)
      {
         answer++;
         cursor = cursor.getLink();
         cursor = IntNode.listSearch(cursor, target);
      }
      return answer;
   }

    
   /**
   * 이 가방에서 임의의 요소를 반환합니다.
   * @param - 없음
   * @precondition
   *   가방이 비어 있지 않아야 합니다.
   * @return
   *   가방에서 임의로 선택된 요소 하나
   * @exception IllegalStateException
   *   가방이 비어 있는 경우
   **/
   public int grab()
   {
      if (manyNodes == 0)
         throw new IllegalStateException("가방이 비어 있습니다.");

      int i = (int)(Math.random() * manyNodes) + 1;
      IntNode cursor = IntNode.listPosition(head, i);
      return cursor.getData();
   }
   
             
   /**
   * 지정된 요소를 가방에서 하나 제거합니다.
   * @param target
   *   제거할 요소
   * @postcondition
   *   target이 가방에 존재하면 하나가 제거되고 true를 반환합니다.
   *   존재하지 않으면 가방은 변경되지 않고 false를 반환합니다.
   **/
   public boolean remove(int target)
   {
      IntNode targetNode = IntNode.listSearch(head, target);

      if (targetNode == null)
         return false;
      else
      {
         targetNode.setData(head.getData());
         head = head.getLink();
         manyNodes--;
         return true;
      }
   }
        
      
   /**
   * 이 가방에 있는 요소의 개수를 반환합니다.
   * @param - 없음
   * @return
   *   이 가방에 있는 전체 요소 수
   **/                           
   public int size()
   {
      return manyNodes;
   }
   

   /**
   * 두 가방의 요소를 모두 포함하는 새 가방을 생성합니다.
   * @param b1
   *   첫 번째 가방
   * @param b2
   *   두 번째 가방
   * @precondition
   *   b1과 b2는 null이 아니어야 합니다.
   * @return
   *   b1과 b2의 합집합을 담는 새 가방
   * @exception IllegalArgumentException
   *   하나라도 null인 경우
   * @exception OutOfMemoryError
   *   새 가방 생성을 위한 메모리가 부족한 경우
   **/   
   public static IntLinkedBag union(IntLinkedBag b1, IntLinkedBag b2)
   {       
      IntLinkedBag answer = new IntLinkedBag();

      answer.addAll(b1);
      answer.addAll(b2);     
      return answer;
   }
}
  • IntLinkedBag은 int 형 정수 데이터를 연결 리스트로 저장하는 가방의 자료구조이다.
  • 요소들의 순서는 중요하지 않고, 중복 요소를 허용한다.
  • 내부적으로 IntNode 클래스를 활용한다.

인스턴스 변수

private IntNode head;
private int manyNodes;
  • head: 연결 리스트의 첫 번째 노드 참조
  • manyItems: 가방에 들어 있는 전체 요소의 수

생성자

public IntLinkedBag()
head = null;
manyNodes = 0;
  • 빈 가방을 초기화해 준다.
  • 연결 리스트가 비어있음을 나타낸다.

메서드

public void add(int element)
head = new IntNode(element, head);
manyNodes++;
  • head 앞에 새로운 노드를 추가해 준다.
  • 연결 리스트는 앞에서부터 삽입된다.
public void addAll(IntLinkedBag addend)
copyInfo = IntNode.listCopyWithTail(addend.head);
copyInfo[1].setLink(head);
head = copyInfo[0];
manyNodes += addend.manyNodes;
  • addend의 연결 리스트를 복사해서 head 앞에 연결한다. (깊은 복사)
  • 꼬리(tail)는 현제 head와 연결해 준다.
public void addMany(int... elements)
  • 가변인자를 활용하는 여러 요소를 한 번에 추가한다.
for (int i : elements)
    add(i);
  • add()를 반복적으로 호출한다.
  • 맨 앞부터 삽입되므로 결과적으로 마지막 요소가 가장 앞에 위치한다.
public Object clone()
  • 복사본을 반환해 준다.
answer = (IntLinkedBag) super.clone();
answer.head = IntNode.listCopy(head);
  • super.clone의 얕은 복사를 실행해 준다.
  • 이후에 IntNode.listCopy(head)로 연결 리스트 전체를 깊은 복사 수행해 준다.
public int countOccurrences(int target)
  • target값이 몇 번 등장하였는지 개수를 세준다.
IntNode cursor = IntNode.listSearch(head, target);
while (cursor != null) {
    answer++;
    cursor = cursor.getLink();
    cursor = IntNode.listSearch(cursor, target);
}
  • listSearch()로 target값이 있는 노드를 찾는다.
  • 찾은 노드 이후부터 다시 찾아준다.
  • 중복된 값들은 모두 카운트해준다.
public int grab()
  • 가방의 임의의 요소 하나를 반환해 준다.
int i = (int)(Math.random() * manyNodes) + 1;
IntNode cursor = IntNode.listPosition(head, i);
return cursor.getData();
  • 전체 개수 내에서 무작위 인덱스를 선택해 준다.
  • 해당 인덱스 위치의 데이터를 반환해 준다.
public boolean remove(int target)
  • 특정 요소 하나만 제거한다.
IntNode targetNode = IntNode.listSearch(head, target);
if (targetNode == null) return false;

targetNode.setData(head.getData());
head = head.getLink();
manyNodes--;
return true;
  • 찾은 노드의 데이터를 head의 데이터로 덮어쓴다.
  • head를 다음 노드로 옮긴다.
  • 리스트 순서가 중요하지 않기 때문에 가능한 기법이다.
public int size()
  • 현제 가방에 저장된 요소 개수 반환해 준다.
  • manyNodes값만 리턴해주면 된다.
public static IntLinkedBag union(IntLinkedBag b1, IntLinkedBag b2)
  • 두 가방의 요소를 모두 포함하는 새로운 가방을 반환해 준다.
IntLinkedBag answer = new IntLinkedBag();
answer.addAll(b1);
answer.addAll(b2);
return answer;
  • b1, b2를 모두 깊은 복사되어 answer에 병합된다.
  • 순서는 중요하지 않기 때문에 어떤 순서로 addAll을 호출해도 상관없다.

실행 예제

public class IntLinkedBagTest {
    public static void main(String[] args) {
        // 1. 빈 가방 생성
        IntLinkedBag bag1 = new IntLinkedBag();
        System.out.println("새로운 빈 가방 생성. 크기: " + bag1.size());

        // 2. 요소 추가
        bag1.add(10);
        bag1.add(20);
        bag1.add(10);
        System.out.println("10, 20, 10 추가 후 크기: " + bag1.size()); // 3

        // 3. 요소 개수 세기
        System.out.println("10의 개수: " + bag1.countOccurrences(10)); // 2
        System.out.println("20의 개수: " + bag1.countOccurrences(20)); // 1
        System.out.println("30의 개수: " + bag1.countOccurrences(30)); // 0

        // 4. grab(): 랜덤 요소 뽑기
        System.out.println("랜덤 grab: " + bag1.grab());

        // 5. 요소 제거
        boolean removed = bag1.remove(10);
        System.out.println("10 제거됨?: " + removed);
        System.out.println("10 제거 후 개수: " + bag1.countOccurrences(10));
        System.out.println("현재 가방 크기: " + bag1.size());

        // 6. 복수 요소 추가
        bag1.addMany(50, 60, 70);
        System.out.println("50, 60, 70 추가 후 크기: " + bag1.size());

        // 7. 다른 가방 만들고 addAll로 병합
        IntLinkedBag bag2 = new IntLinkedBag();
        bag2.add(100);
        bag2.add(200);
        bag1.addAll(bag2);
        System.out.println("bag2 병합 후 bag1 크기: " + bag1.size());

        // 8. 복제 테스트
        IntLinkedBag bag3 = (IntLinkedBag) bag1.clone();
        System.out.println("bag1 복제 -> bag3 생성. 크기: " + bag3.size());

        // 9. union 사용해 두 가방 합치기
        IntLinkedBag mergedBag = IntLinkedBag.union(bag1, bag2);
        System.out.println("bag1 + bag2 합집합 bag 생성. 크기: " + mergedBag.size());

        // 10. 빈 가방에서 grab 시도 (예외 발생 테스트)
        try {
            IntLinkedBag emptyBag = new IntLinkedBag();
            emptyBag.grab(); // 예외 발생
        } catch (IllegalStateException e) {
            System.out.println("빈 가방에서 grab -> 예외 발생: " + e.getMessage());
        }
    }
}

'자료구조' 카테고리의 다른 글

IntStack 클래스  (0) 2025.04.19
IntNode 클래스  (0) 2025.04.19
IntArrayBag 클래스  (0) 2025.04.18
Location 클래스  (1) 2025.04.17
Throttle 클래스  (0) 2025.04.17