본문 바로가기
Java

자바와 자료구조 / 배열, 스택, 큐

by 리잼 2022. 11. 29.
반응형

1. 여러가지 자료구조

자료구조란? ( Data Structure )

  • 프로그램에서 사용할 많은 데이터를 메모리 상에서 관리하는 여러 구현방법들
  • 효율적인 자료구조가 성능 좋은 알고리즘의 기반이 됨
  • 자료의 효율적인 관리는 프로그램의 수행속도와 밀접한 관련이 있음
  • 여러 자료 구조 중에서 구현하려는 프로그램에 맞는 최적의 자료구조를 활용해야 하므로
    자료구조에 대한 이해가 중요.

자료 구조에는 어떤 것들이 있나

  • 한 줄로 자료를 관리하기 ( 선형 자료구조 )

배열 ( Array )

  • 선형으로 자료를 관리
  • 정해진 크기의 메모리를 먼저 할당받아 사용
  • 자료의 물리적 위치와 논리적 위치가 같음

연결 리스트 ( LinkedList )

  • 선형으로 자료를 관리
  • 자료가 추가될 때마다 메모리를 할당 받고 자료는 링크로 연결된다
  • 자료의 물리적 위치와 논리적 위치가 다를 수 있다

리스트에 자료 추가하기

리스트에서 자료 삭제

스택 ( Stack ) 

  • 가장 나중에 입력된 자료가 가장 먼저 출력되는 자료 구조 ( LIFO )

큐 ( Queue ) 

  • 가장 먼저 입력된 자료가 가장 먼저 출력되는 자료 구조 ( FIFO )

트리 ( Tree ) 

  • 부모 노드와 자식 노드간의 연결로 이루어진 자료 구조

힙 ( Heap )

  • Priority queue를 구현 ( 우선 큐 )
  • Max heap : 부모 노드는 자식 노드보다 항상 크거나 같은 값을 갖는 경우
  • Min heap : 부모 노드는 자식 노드보다 항상 작거나 같은 값을 같는 경우

이진 트리 ( binary tree )

  • 부모노드에 자식노드가 두개 이하인 트리

이진 검색 트리 ( binary search tree )

자료(key)의 중복을 허용하지 않음
왼쪽 자식 노드는 부모 노드보다 작은 값, 오른쪽 자식 노드는 부모 노드보다 큰 값을 가짐
자료를 검색에 걸리는 시간이 평균 log(n) 임
inorder traversal 탐색을 하게 되면 자료가 정렬되어 출력됨

ex) [ 23, 10, 48, 15, 7, 22, 56 ] 순으로 자료를 넣을 때 BST

  • JDK 클래스 : TreeSet, TreeMap ( Tree로 시작되는 클래스는 정렬을 지원 함 )

 

그래프 ( Graph )

  • 정점과 간선들의 유한 집합 G = ( V,E)
  • 정점 ( vertex ) : 여러 특성을 가지는 객체, 노드(node)
  • 간선( edge ) : 이 객체들의 연결관계를 나타냄, 링크(link)
  • 간선은 방향성이 있는 경우와 없는경우가 있다.
  • 그래프를 구현하는 방법 : 인접 행렬 ( adjacency matrix ), 인접 리스트( adjacency list )
  • 그래프를 탐색하는 방법 : BFS( bread first search ), DFS( depth first search )

그래프의 예

해싱 ( hashing )

  • 자료를 검색하기 위한 자료구조
  • 검색을 위한 자료구조
  • 키(key)에 대한 자료를 검색하기 위한 사전(dictionary)개념의 자료구조
  • key는 유일하고 이에 대한 value를 쌍으로 저장
  • index = h(key) : 해시 함수가 key에 대한 인덱스를 반환해줌, 해당 인덱스 위치에 자료를 저장하거나 검색하게 됨
  • 해시 함수에 의해 인덱스 연산이 산술적으로 가능 O(1)
  • 저장 되는 메모리 구조를 해시테이블 이라고 함
  • JDK 클래스 : HashMap, Properties

 


2. 배열 ( Array ) 구현하기

Array의 특징

  • 동일한 데이터 타입을 순서에 따라 관리하는 자료구조
  • 정해진 크기가 있음
  • 요소의 추가와 제거시 다른 요소들의 이동이 필요함
  • 배열의 i번째 요소를 찾는 인덱스 연산이 빠름
  • jdk 클래스 : ArrayList, Vector

Array 구현

MyArray.java

public class MyArray {

	int[] intArr;   	//int array
	int count;  		//개수
		
	public int ARRAY_SIZE;
	public static final int ERROR_NUM = -999999999;
	
	public MyArray()
	{
		count = 0;
		ARRAY_SIZE = 10;
		intArr = new int[ARRAY_SIZE];
	}
	
	public MyArray(int size)
	{
		count = 0;
		ARRAY_SIZE = size;
		intArr = new int[size];
	}
	
	public void addElement(int num)
	{
		if(count >= ARRAY_SIZE){
			System.out.println("not enough memory");
			return;
		}
		intArr[count++] = num;
				
	}

	public void insertElement(int position, int num)
	{
		int i;
		
		if(count >= ARRAY_SIZE){  //꽉 찬 경우
			System.out.println("not enough memory");
			return;
		}
		
		if(position < 0 || position > count ){  //index error
			System.out.println("insert Error");
			return;
		}
		
		for( i = count-1; i >= position ; i--){
			intArr[i+1]  = intArr[i];        // 하나씩 이동
		}
		
		intArr[position] = num;
		count++;
	}
	
	public int removeElement(int position)
	{
		int ret = ERROR_NUM;
		
		if( isEmpty() ){
			System.out.println("There is no element");
			return ret;
		}
		
		if(position < 0 || position >= count ){  //index error
			System.out.println("remove Error");
			return ret;
		}
		
		ret = intArr[position];
		
		for(int i = position; i<count -1; i++ )
		{
			intArr[i] = intArr[i+1];
		}
		count--;
		return ret;
	}
	
	public int getSize()
	{
		return count;
	}
	
	public boolean isEmpty()
	{
		if(count == 0){
			return true;
		}
		else return false;
	}
	
	public int getElement(int position)
	{
		if(position < 0 || position > count-1){
			System.out.println("검색 위치 오류. 현재 리스트의 개수는 " + count +"개 입니다.");
			return ERROR_NUM;
		}
		return intArr[position];
	}
	
	public void printAll()
	{
		if(count == 0){
			System.out.println("출력할 내용이 없습니다.");
			return;
		}
			
		for(int i=0; i<count; i++){
			System.out.println(intArr[i]);
		}
		
	}
	
	public void removeAll()
	{
		for(int i=0; i<count; i++){
			intArr[i] = 0;
		}
		count = 0;
	}
}

MyArrayTest.java

public class MyArrayTest {

	public static void main(String[] args) {

		MyArray array = new MyArray();
		array.addElement(10);
		array.addElement(20);
		array.addElement(30);
		array.insertElement(1, 50);
		array.printAll();
		
		System.out.println("===============");
		array.removeElement(1);
		array.printAll();
		System.out.println("===============");
			
		array.addElement(70);
		array.printAll();
		System.out.println("===============");
		array.removeElement(1);
		array.printAll();
		
		System.out.println("===============");
		System.out.println(array.getElement(2));
		
	}
}

MyObjectArray.java

public class MyObjectArray {

	private int cout;
	private Object[] array;
	public int ARRAY_SIZE;
	
	public MyObjectArray()
	{
		ARRAY_SIZE = 10;
		array = new Object[ARRAY_SIZE];
	}
	
	public MyObjectArray(int size)
	{
		ARRAY_SIZE = size;
		array = new Object[ARRAY_SIZE];
	}
}


3. 연결 리스트 ( LinkedList ) 구현하기

LinkedList 특징

  • 동일한 데이터 타입을 순서에 따라 관리하는 자료구조
  • 자료를 저장하는 노드에는 자료와 다음 요소를 가리키는 링크(포인트)가 있음
  • 자료가 추가될 때 노드 만큼의 메모리를 할당 받고 이전 노드의 링크로 연결함 (정해진 크기가 없음)
  • 연결 리스트의 i번째 요소를 찾는게 걸리는 시간은 요소의 개수에 비례 : O(n)
  • jdk 클래스 : LinkedList

LinkedList 구현

MyListNode.java

public class MyListNode {

	private String data;	 // 자료
	public MyListNode next;  // 다음 노드를 가리키는 노드
	
	public MyListNode() {
		data = null;
		next = null;
	}
	
	public MyListNode(String data) {
		this.data = data;
		this.next = null;
	}
	
	public MyListNode(String data, MyListNode link) {
		this.data = data;
		this.next = link;
	}
	
	public String getData() {
		return data;
	}		
}

MyLinkedList.java

public class MyLinkedList {
	
	private MyListNode head;
	int count;
	
	public MyLinkedList() {
		head = null;
		count = 0;
	}
	
	public MyListNode addElement(String data) {
		
		MyListNode newNode;
		if(head == null) { // 맨 처음일 때
			newNode = new MyListNode(data);
			head = newNode;
		} else {
			newNode = new MyListNode(data);
			MyListNode temp = head;
			while(temp.next != null) { // 맨 뒤로 가서
				temp = temp.next;
			}
			temp.next = newNode;
		}
		count++;
		
		return newNode;
	} 
	
	public MyListNode insertElement(int position, String data) {
		
		int i;
		MyListNode tempNode = head;
		MyListNode preNode = null;
		
		MyListNode newNode = new MyListNode(data);
		
		if(position <0 || position > count) {
			System.out.println("추가할 위치 오류입니다. 현재 리스트의 갯수는" + count + " 개 입니다.");
			return null;
		}
		
		if(position == 0) { // 맨앞에 들어갈 경우
			newNode.next = head;
			head = newNode;
		} else {
			for(i=0; i<position; i++) {
				preNode = tempNode;
				tempNode = tempNode.next;
			}
			newNode.next = preNode.next;
			preNode.next = newNode;
		}
		count++;
		return newNode;
	}
	
	public MyListNode removeElement(int position) {
		int i;
		MyListNode tempNode = head;
		MyListNode preNode = null;
		
		if (position >= count) {
			System.out.println("삭제할 위치 오류입니다. 현재 리스트의 갯수는" + count + " 개 입니다.");
			return null;
		}
		
		if (position == 0) { // 맨 앞을 삭제
			head = tempNode.next;
		} else {
			for(i=0; i<position; i++) {
				preNode = tempNode;
				tempNode = tempNode.next;
			}
			preNode.next = tempNode.next;
		}
		count--;
		System.out.println(position + "번 째 항목 삭제 되었습니다.");
		return tempNode;
	}
	
	public String getElement(int position)
	{
		int i;
		MyListNode tempNode = head;
		
		if(position >= count ){
			System.out.println("검색 위치 오류 입니다. 현재 리스트의 개수는 " + count +"개 입니다.");
			return new String("error");
		}
		
		if(position == 0){  //맨 인 경우

			return head.getData();
		}
		
		for(i=0; i<position; i++){
			tempNode = tempNode.next;
			
		}
		return tempNode.getData();
	}

	
	public void printAll()
	{
		if(count == 0){
			System.out.println("출력할 내용이 없습니다.");
			return;
		}
		
		MyListNode temp = head;
		while(temp != null){
			System.out.print(temp.getData());
			temp = temp.next;
			if(temp!=null){
				System.out.print("->");
			}
		}
		System.out.println("");
	}

	public int getSize()
	{
		return count;
	}

	public void removeAll()
	{
		head = null;
		count = 0;
		
	}
    
    	public boolean isEmpty() {
		if(head==null) return true;
		else return false;
	}

}

MyLinkedListTest.java

public class MyLinkedListTest {

	public static void main(String[] args) {

		MyLinkedList list = new MyLinkedList();
		list.addElement("A");
		list.addElement("B");
		list.addElement("C");
		list.printAll();
		list.insertElement(3,"D");
		list.printAll();
		list.removeElement(0);
		list.printAll();
		list.removeElement(1);
		list.printAll();
						
		list.insertElement(0, "A-1");
		list.printAll();
		System.out.println(list.getSize());
		
		list.removeElement(0);
		list.printAll();
		System.out.println(list.getSize());
		
		list.removeAll();
		list.printAll();
		list.addElement("A");
		list.printAll();
		System.out.println(list.getElement(0));
		list.removeElement(0);

	}

}


4. 스택 구현하기

Stack의 특징

  • 맨 마지막 위치(top)에서만 자료를 추가, 삭제, 꺼내올 수 있음( 중간 자료를 꺼낼 수 없다 )
  • LIFO (후입 선출)구조
  • 택배 상자가 쌓여있는 모양
  • 가장 최근의 자료를 찾아오거나 게임에서 히스토리를 유지하고 이를 무를 때 사용이 가능함
  • 함수의 메모리는 호출 순서에 따른 stack 구조
  • JDK 클래스 : Stack

배열을 사용하여 Stack 구현

MyArrayStack.java

import ch02.MyArray;

public class MyArrayStack {
	
	MyArray arrayStack;
	int top;
	
	public MyArrayStack() {
		top = 0;
		arrayStack = new MyArray();
	}
	
	public MyArrayStack(int size) {
		top = 0;
		arrayStack = new MyArray(size);
	}
	
	public void push(int data) {
		if (isFull()) {
			System.out.println("Stack is Full");
			return;
		}
		arrayStack.addElement(data);
		top++;
	}
	
	public int pop() {
		if (isEmpty()) {
			System.out.println("Stack is Empty");
			return MyArray.ERROR_NUM;
		}
		return arrayStack.removeElement(--top);
	}
	
	public int peek() {
		if (isEmpty()) {
			System.out.println("Stack is Empty");
			return MyArray.ERROR_NUM;
		}
		return arrayStack.getElement(--top);
	}
	
	public boolean isFull() {
		if ( top == arrayStack.ARRAY_SIZE ) {
			return true;
		} else return false;
	}
	
	public boolean isEmpty() {
		if ( top == 0 ) {
			System.out.println("Stack is Empty");
			return true;
		} else return false;
	}
	
	public void printAll() {
		arrayStack.printAll();
	}
}

MyArraySatckTest.java

public class MyArrayStackTest {

	public static void main(String[] args) {

		MyArrayStack stack = new MyArrayStack(3);
		stack.push(10);
		stack.push(20);
		stack.push(30);
		stack.push(40);
		
		stack.printAll();
		
		System.out.println(stack.pop());
		System.out.println(stack.pop());
		System.out.println(stack.peek());		
	}

}


5. 큐 ( Queue ) 구현하기

Queue의 특징

  • 맨 앞(front)에서 자료를 꺼내거나 삭제하고, 맨 뒤(rear)에서 자료를 추가함
  • FIFO (선입선출)구조
  • 일상 생활에서 1열로 줄 서있는 모양
  • 순차적으로 입력된 자료를 순서대로 처리하는데 많이 사용되는 자료구조
  • 콜 센터에 들어온 문의전화, 메세지 큐 등에 활용됨
  • JDK클래스 : ArrayList

연결 리스트를 활용하여 Queue구현하기

MyLinkedQueue.java

import ch03.MyLinkedList;
import ch03.MyListNode;

interface Queue {
	public void enQueue(String data);
	public String deQueue();
	public void printQueue();
}

public class MyLinkedQueue extends MyLinkedList implements Queue {

	MyListNode front;
	MyListNode rear;
	

	@Override
	public void enQueue(String data) {

		MyListNode newNode;
		if(isEmpty()) {
			newNode = addElement(data);
			front = newNode;
			rear = newNode;
		} else {
			newNode = addElement(data);
			rear = newNode;
		}
		System.out.println(newNode.getData() + " added");
	}

	@Override
	public String deQueue() {
		if (isEmpty()) {
			System.out.println("Empty");
			return null;
		}
		String data = front.getData();
		front = front.next;
		
		if ( front == null ) {
			rear = null;
		}
		return data;
	}

	@Override
	public void printQueue() {

		printAll();
		
	}

}

MyLinkedQueueTest.java

public class MyLinkedQueueTest {

	public static void main(String[] args) {

		MyLinkedQueue listQueue = new MyLinkedQueue();
		listQueue.enQueue("A");
		listQueue.enQueue("B");
		listQueue.enQueue("C");
		
		listQueue.printAll();
		
		System.out.println(listQueue.deQueue());
		System.out.println(listQueue.deQueue());
		
	}

}

반응형