반응형
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | package LinkedList; public class LinkedListMain { public static void main(String[] args) { LinkedList numbers = new LinkedList(); numbers.addFirst(30);//맨 앞에 추가 numbers.addFirst(20); numbers.addFirst(10); System.out.println(numbers);//출력 numbers.addLast(40);//맨 뒤에 추가 numbers.addLast(50); numbers.addLast(60); System.out.println(numbers); numbers.add(2,25);//인덱스 2위치에 25추가 System.out.println(numbers.node(2));//인덱스 2번째값을가져와보자 System.out.println(numbers); System.out.println(numbers.removeFirst());//맨앞 요소 제거 System.out.println(numbers); System.out.println(numbers.remove(2));//지정된 위치의 요소 제거 System.out.println(numbers); System.out.println(numbers.removeLast());//맨뒤 요소 제거 System.out.println(numbers); System.out.println(numbers.size());//노드 수 반환 System.out.println(numbers.get(1));//지정된 위치의 객체를 반환 System.out.println("인덱스"); System.out.println(numbers.get(numbers.size()-1));//맨뒤의 위치의 값을 반환 System.out.println(numbers.indexOf((Object)10));//10의 값에 해당하는 위치 반환 System.out.println("반복자"); LinkedList.ListIterator i = numbers.listIterator(); System.out.println(numbers); i.add(5);//값 추가 System.out.println(numbers); i.add(4); System.out.println(numbers); System.out.println(i.next());//탐색 System.out.println(numbers); i.add(15); System.out.println(numbers); while(i.hasNext()){//모든 노드 순차적 조회 System.out.println(i.next()); } /* for(int i=0; i<numbers.size(); i++){ System.out.println(numbers.get(i)); } 위와 같은 방법으로 반복을 사용해도되지만 linkedlist에서는 이것은 바람직하지 않은 방법이다 왜냐하면 ArrayList와 다르게 LinkedList에서 get은 효율적이지 않기 때문 get을 호출할 때마다 내부적으로는 반복문이 실행. 이런 경우 Iterator를 사용하는 것이 유리 Iterator는 내부적으로 반복 처리된 노드가 무엇인지에 대한 정보를 유지하기 때문 */ i = numbers.listIterator(); while(i.hasNext()) {//더블링크드 리스크가 아니라서 이 방법은 효율적이지않음 if((int)i.next()==20){//여기서 노드를 찾는과정을 해왔는데 i.remove();//여기서 또 node메소드를 통해 노드 탐색을해서 비효율 }//그래서 단방향에서 양방향 연결 리스트가 나온것이다. } System.out.println(numbers); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | package LinkedList; public class LinkedList { private Node head;// 첫번째 노드를 가리키는 필드,변수,참조값 private Node tail;// 마지막 노드를 가리키는 필드,변수 private int size = 0; //엘리먼트 갯수 private class Node{//링크드 리스트에서는 하나의 엘리먼트가(노드)하나의 객체다 //그객체를 LinkedList의 innerclass로 정의했다. private Object data;//데이터가 저장될 변수-실제 저장값 private Node next;//다음 노드를 가리키는 변수,참조값 public Node(Object input) {//객체생성 초기화 this.data = input; this.next = null;//생성시는 미정 } public String toString(){//노드의 값을 쉽게 출력위해 return String.valueOf(this.data); } } public void addFirst(Object input){//머리에 추가 Node newNode = new Node(input);//노드를 생성 newNode.next = head;//새로운 노드의 다음 노드로 해드 지정 head = newNode;//헤드로 새로운 노드를 지정 size++; if(head.next == null){ tail = head; } } public void addLast(Object input){//꼬리에 추가 if(size == 0){//리스트의 노드가 하나도 없다면 첫번째 노드를 추가하는 메소드를 사용. addFirst(input); } else {//기존 노드가 하나라도 있다면 Node newNode = new Node(input); tail.next = newNode;//마지막 노드의 다음 노드로 생성한 노드를 지정. tail = newNode; //마지막 노드를 갱신. size++;//엘리먼트 개수 1 증가 } } Node node(int index) {//여러곳에서 내부적으로 node를 조회하기 위해 씀 //원하는 인덱스의 노드를 가져오기는 하지만 외부에서 가져다쓰지는 못하게 public하지말자 Node x = head;//어떤값을 조회하기 위해선 항상 head부터 시작한다 for (int i = 0; i < index; i++) x = x.next; return x; } public void add(int k, Object input){// 특정 위치 값 추가 // 만약 k가 0이라면 첫번째 노드에 추가하는 것이기 때문에 addFirst를 사용합니다. if(k == 0){ addFirst(input); } else { Node temp1 = node(k-1); Node temp2 = temp1.next;//k번째 노드를 temp2로 지정 Node newNode = new Node(input);//새로운 노드를 생성. temp1.next = newNode;// temp1의 다음 노드로 새로운 노드를 지정 newNode.next = temp2;// 새로운 노드의 다음 노드로 temp2를 지정 size++; if(newNode.next == null){//마지막 꼬리에 추가시 검사사항 tail = newNode;//추가한 노드가 마지막 노드이기 때문에 tail로 지정. } } } public String toString() {//리스트안에 데이터 전부 출력 if(head == null){//리스트에 데이터가 없다면 return "[]"; } Node temp = head;//항상 시작은 헤드 지정 String str = "["; while(temp.next != null){//head의 다음 노드가 없을 때까지 반복문을 실행 str += temp.data + ","; temp = temp.next; } str += temp.data;//마지막 노드를 출력결과에 포함 return str+"]"; } public Object removeFirst(){//첫번째 노드 삭제 if(head != null) { Node temp = head;//첫번째 노드를 temp로 지정 head = temp.next;//head의 값을 두번째 노드로 변경. Object returnData = temp.data;//데이터 삭제 전에 리턴할 값을 임시 변수에 담자. temp = null;//null로 지정해서 가비지 컬렌션이 됨 size--; return returnData; }else return (Object)"삭제할 노드가 없습니다"; } public Object remove(int k){ if(k == 0) {//삭제할려는값이 첫번째값이라면 return removeFirst(); } Node temp = node(k-1);//삭제 노드 전 노드를 temp의 값으로 지정 Node todoDeleted = temp.next;// 삭제 노드를 todoDeleted에 기록 // 삭제 노드를 지금 제거하면 삭제 앞 노드와 삭제 뒤 노드를 연결할 수 없다. temp.next = temp.next.next;//삭제노드 전과 후를 연결 // 삭제된 데이터를 리턴하기 위해서 returnData에 데이터를 저장합니다. Object returnData = todoDeleted.data; if(todoDeleted == tail){//삭제할려는데이터가 마지막값이라면 tail = temp; } todoDeleted = null; size--; return returnData; } public Object removeLast(){ return remove(size-1); //tail값만 삭제해서는 안되고 삭제전 노드를 tail로 지정해야한다. //이거는 어레이리스트랑 반대로 추가가 삭제시 노드를 계속 탐색 해가야한다. //이러한 단점때문에 양방향 연결리스트가 나왔다. } public int size(){//리스트가 가진 데이터의수 return size; } public Object get(int k){//특정 엘리먼트 값 조회 Node temp = node(k); return temp.data; } public int indexOf(Object data){//특정데이터가 어떤 위치에 있는지 검색 Node temp = head;//먼저 첫번째 노드를 템프로 지정 int index = 0;// 탐색 대상이 몇번째 엘리먼트에 있는지를 의미하는 변수로 index를 사용합니다. while(temp.data != data){//탐색 값과 탐색 대상의 값을 비교. temp = temp.next;//다음 노드를 계속 검색 index++; if(temp == null)// temp의 값이 null이라는 것은 더 이상 탐색 대상이 없다는 것 return -1; } return index;// 탐색 대상을 찾았다면 대상의 인덱스 값을 리턴 } public ListIterator listIterator() { return new ListIterator(); // 반복자를 생성해서 리턴 } public class ListIterator{//이너 클래스 private Node lastReturned;//반환하는 노드 private Node next;//head로 초기화 private int nextIndex;//몇번 next 메소드가 호출됬는지 ListIterator(){ next = head;//next변수 초기화 nextIndex = 0;//인덱스 초기화 } public boolean hasNext() {//다음 노드가 있는가 return nextIndex < size(); } public Object next() { lastReturned = next; next = next.next; //next의 참조값이 기존 next.next로 변경. nextIndex++; return lastReturned.data; } public void add(Object input){//이터레이터 반복 과정중 노드를 추가하는 경우 //이 로직은 더좋은 로직이 있을수 있다. 전체적인 흐름차원에서 어레이 리스트랑 뭐가 다른지 비교차원 접근해보자 Node newNode = new Node(input); if(lastReturned == null){//처음위치 추가//한번도 next메소드 실행 안한상태 head= newNode;//추가노드를 헤드로 지정 newNode.next = next;//원래 헤드를 추가한 노드의 next로 지정 } else {//lastReturned가 설정되었다면 중간 또는 끝에 추가하는경우 lastReturned.next = newNode; if(next != null) {//마지막까지 탐색을 안했다면 newNode.next = next; }else {//마지막까지 탐색을 했다면 tail = newNode; } } lastReturned = newNode; nextIndex++; size++; } public void remove(){ if(nextIndex == 0){ //네스트 메소드 한번도 호출안한상태//삭제할것이 없음 throw new IllegalStateException(); } LinkedList.this.remove(nextIndex-1);//LinkedList 클래스의 remove임 //그러나 이 remove를 이용하면 node 메소드를 통해 또 반복적으로 순회를 해서 찾아가서 비효율적 nextIndex--;//단방향 연결리스트에서는 prev가 없어서 이렇게 비효율적이다. } } } | cs |
반응형
'Basic > 자료구조,알고리즘' 카테고리의 다른 글
원형 연결리스트(CircularLinkedList)직접 구현 (0) | 2018.12.06 |
---|---|
Doubly linked list(이중 연결 리스트) 직접 구현 (0) | 2018.11.29 |
양방향 연결리스트(Doubly LinkedList) api 예제 (0) | 2018.11.29 |
Linked list 와 Doubly linked list 의 개념과 차이점 (0) | 2018.11.29 |
ArrayList 직접 구현 (0) | 2018.11.29 |