난이도 : medium
- 링크드리스트의 head가 주어짐
- 오름차순으로 정렬된 링크드리스트 참조객체를 리턴하라
- node 갯수 : [0, 5 * 104]
- -105 <= Node.val <= 105
1. 접근법
- 시간복잡도를 줄일 수 있는 병합정렬을 이용하여 정렬 수행
- 주어진 링크드리스트를 반으로 자름
- 정렬 및 병합
2. 의사코드
// 재귀함수 구현을 위한 종료조건 작성
if (head == null || head.next == null) {
return head;
}
// 현재 head가 가리키는 링크드리스트에서 중간에 위치한 노드를 리턴
// 해당 중간노드와 이전 노드의 연결을 끊어줌
ListNode mid = getMid(head);
// head와 mid를 대상으로 재귀적으로 호출하여 정렬함
ListNode left = sortList(head);
ListNode right = sortList(mid);
// 반씩 잘려진 링크드리스트를 다시 오름차순으로 병합하고 리턴함
return merage(left, right);
3. 구현 코드
public class SortList {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode mid = getMid(head);
ListNode left = sortList(head);
ListNode right = sortList(mid);
return merge(left, right);
}
private ListNode getMid(ListNode head) {
ListNode temp = head;
int count = 0;
while (temp.next != null) {
temp = temp.next;
count++;
}
temp = head;
ListNode before = head;
count = (count/2) + (count%2);
while (count > 0) {
before = temp;
temp = temp.next;
count--;
}
before.next = null;
return temp;
}
private ListNode merge(ListNode a, ListNode b) {
ListNode dummy = new ListNode();
ListNode current = dummy;
while (a != null && b != null) {
if (a.val < b.val) {
dummy.next = new ListNode(a.val);
a = a.next;
} else if (b.val < a.val) {
dummy.next = new ListNode(b.val);
b = b.next;
} else {
dummy.next = new ListNode(a.val, new ListNode(a.val));
dummy = dummy.next;
a = a.next;
b = b.next;
}
dummy = dummy.next;
}
if (a != null) {
dummy.next = a;
}
if (b != null) {
dummy.next = b;
}
return current.next;
}
- 노드의 중간 노드를 가져오기 위해서 중간 노드의 위치를 찾음
- 찾은 노드의 직전 노드의 next를 null로 하여 연결을 끊어줌
- left, right 노드를 오름차순으로 병합함
- 중복 값의 노드도 포함시켜서 처리함
4. 시간복잡도, 공간복잡도 예상
- 시간복잡도 : O(nlogn) - 분할 * (정렬 + mid 노드 찾기)
- 노드 분할 : O(logn)
- 정렬 + mid 노드 찾기 : 각각 O(n)
- 공간복잡도 : O(n) - 링크드리스트 길이만큼 새로운 링크드리스트를 생성함
5. 개선점
- 중간 노드를 찾는 부분을 투포인터를 사용하여 연산 횟수를 줄였음
private ListNode getMid(ListNode head) {
ListNode prev = null, slow = head, fast = head;
while (fast != null || fast.next != null) {
prev = slow;
slow = slow.next;
fast = fast.next.next;
}
prev.next = null;
return slow;
}
6. 회고
다른 블로그나 유튜브에서도 동일하게 풀었다
slow, fast는 linkedlist에서 자주 쓰이는 투포인터 패턴이니 기억하자. 부족한점이니 숙지하기
(이해는 했지만 한두문제 풀어본게 다라.. 반복문을 돌리게 된다)
'Algorithm > (Java) PS' 카테고리의 다른 글
Leetcode Top Interview 150] 530. Minimum Absolute Difference in BST (0) | 2023.09.06 |
---|---|
[Leetcode Top Interview 150] 162. Find Peak Element (0) | 2023.09.05 |
[Leetcode Top Interview 150] 1. Two Sum (0) | 2023.09.01 |
[Leetcode Top Interview 150] 150. Evaluate Reverse Polish Notation (0) | 2023.09.01 |
[Leetcode Top Interview 150] 155. Min Stack (0) | 2023.08.31 |