깊은바다거북
개발 공부 기록
깊은바다거북
전체 방문자
오늘
어제
  • 분류 전체보기 (219)
    • JAVA (9)
    • JavaScript (15)
    • 스파르타코딩클럽 (11)
      • [내일배움단] 웹개발 종합반 개발일지 (5)
      • [내일배움캠프] 프로젝트와 트러블 슈팅 (6)
    • SQL | NoSQL (4)
    • CS 등등 (0)
    • TIL | WIL (173)
    • 기타 에러 해결 (3)
    • 내 살 길 궁리 (4)

인기 글

최근 글

최근 댓글

태그

  • 자잘한 에러 해결
  • Backtracking(백트래킹)
  • 팀 프로젝트
  • POST / GET 요청
  • Trie
  • BFS(너비우선탐색)
  • Binary Tree(이진 트리)
  • 자바스크립트 기초 문법
  • 최소 힙(Min Heap)
  • 01. 미니 프로젝트
  • DFS(깊이우선탐색)
  • 코딩테스트 연습문제
  • 점화식(Recurrence Relation)
  • 자료 구조
  • 프로그래머스
  • 시간 복잡도
  • BST(이진 탐색 트리)
  • Inorder Traversal(중위 순회)
  • TypeScript
  • Preorder Traversal(전위 순회)
  • 재귀 함수
  • 최대 힙(Max Heap)
  • 혼자 공부하는 자바스크립트
  • Linked List
  • Til
  • 트러블 슈팅 Troubleshooting
  • TIT (Today I Troubleshot)
  • tree
  • Leetcode
  • leetcode-cli
hELLO · Designed By 정상우.
깊은바다거북

개발 공부 기록

TIL | WIL

10/17 (화) 배열에서 K번째로 큰 수 찾기(최소 힙 구현) TIL

2023. 10. 17. 21:00

공부한 것

  • LeetCode #215. Kth Largest Element in an Array
    Kth Largest Element in an Array - LeetCode
    Can you solve this real interview question? Kth Largest Element in an Array - Given an integer array nums and an integer k, return the kth largest element in the array. Note that it is the kth largest element in the sorted order, not the kth distinct element. Can you solve it without sorting?   Example 1: Input: nums = [3,2,1,5,6,4], k = 2 Output: 5 Example 2: Input: nums = [3,2,3,1,2,4,5,5,6], k = 4 Output: 4   Constraints: * 1 <= k <= nums.length <= 105 * -104 <= nums[i] <= 104
    https://leetcode.com/problems/kth-largest-element-in-an-array/description/

    ⇒ 주어진 숫자 배열에서 k번째로 큰 수를 찾아 반환하는 문제. 수들은 중복으로 존재할 수 있고, sort 함수를 사용하지 말 것.

    생각 과정: ‘max heap을 만들어서 k개로 자르고 맨 마지막 수를 반환하기? 안된다. 맨 앞의 수를 제외한 뒤의 수들은 크기 정렬이 안 되어 있다. 그러면 min heap을 만들어서 k개로 자르고 맨 앞의 수를 반환하자.’

    • 1번 풀이 코드:
      function findKthLargest(nums: number[], k: number): number {
      	const minHeap: MinHeap = new MinHeap();
      	for (const num of nums) {
      		// 1. k개에 못 미치면 그냥 넣기
      		// 2. k개이고 지금 넣는 수가 min보다 크거나 같으면면: min을 먼저 빼고, 지금 수를 넣기
      		if (minHeap.getLength() < k)
      			minHeap.insert(num);
      		else if (num >= minHeap.getMin()) {
      			minHeap.popMin();
      			minHeap.insert(num);
      		}
      	}
      
      	return minHeap.getMin(); 
      };
      
      class MinHeap {
      	private heap: number[];
      
      	constructor() {
      		this.heap = [];
      	}
      
      	// heap배열의 마지막 자리에 위치한 요소를 제자리로(Heapify): bubble up
      	private bubbleUp() {
      		let index = this.heap.length - 1;
      		let element, parentIndex, parent;
      
      		while (index > 0) {
      			element = this.heap[index]
      			parentIndex = Math.floor((index - 1) / 2);
      			parent = this.heap[parentIndex];
      			
      			if (element >= parent) break;
      
      			this.heap[parentIndex] = element;
      			this.heap[index] = parent;
      			
      			index = parentIndex;	
      		}
      	}
      
      	// heap배열의 첫 자리에 위치한 요소를 제자리로(Heapify): bubble down
      	private bubbleDown() {
      		let index = 0;
      		let element: number;
      		let leftChildIndex: number, rightChildIndex: number;
      		let leftChild: number, rightChild: number;
      		let swapTarget: null | number;
      		
      		while (true) {
      			swapTarget = null;
      			element = this.heap[index];
      			leftChildIndex = index * 2 + 1;
      			rightChildIndex = index * 2 + 2;
      			
      			if (leftChildIndex < this.heap.length) {
      				leftChild = this.heap[leftChildIndex];
      				if (leftChild < element) {
      					swapTarget = leftChildIndex;
      				}
      			}
      
      			if (rightChildIndex < this.heap.length) {
      				rightChild = this.heap[rightChildIndex];
      				if ((swapTarget === null && rightChild < element) ||
      					(swapTarget && rightChild < leftChild)) {
      					swapTarget = rightChildIndex;
      				}
      			}
      
      			if (swapTarget === null) break;
      
      			this.heap[index] = this.heap[swapTarget];
      			this.heap[swapTarget] = element;
      
      			index = swapTarget;
      		}
      	}
      
      	// 요소를 삽입
      	insert(num: number) {
      		this.heap.push(num);
      		this.bubbleUp();
      	}
      
      	// 최소 요소 추출
      	popMin() {
      		const min = this.heap[0];
      		const end = this.heap.pop();
      		if (this.heap.length > 0) {
      			this.heap[0] = end;
      			this.bubbleDown();
      		}
      		return min;
      	}
      
      	getMin() {
      		return this.heap[0];
      	}
      
      	getLength() {
      		return this.heap.length;
      	}
      }
    • 2번 풀이 코드:
      function findKthLargest2(nums: number[], k: number): number {
      	nums.sort((a, b) => b - a); // 내림차순 정렬
      	return nums[k - 1];
      }

    실행 결과:

    ⇒ sort 메소드를 이용한 풀이 2번과 굳이 비교해보니, Min Heap을 이용하는 풀이 1번이 조금 더 빠름을 알 수 있다. sort()는 시간복잡도가 O(NlogNNlogNNlogN)이고, Heap은 숫자가 들어올 때마다 logN을 진행해야 하므로 O(log(N!)log(N!)log(N!))이 된다. 두 함수의 크기를 비교하면 다음과 같다:

    엄청난 차이는 없지만 NlogN 쪽이 더 오랜 시간이 걸린다는 것을 알 수 있다. Min Heap을 이용하는 쪽이 조금 더 빠르며, ‘현재 최소값보다 더 작은 수가 들어올 때는 해당 logN 과정을 생략’할 수도 있음을 생각해볼 때 조금 더 차이가 발생하게 된다. 그래서 위와 같은 실행 시간 차이가 발생하게 된 것이다.

  • 최소 힙(Min Heap)을 스스로 구현해 보았다. 어제 기억이 살아 있어서 수월했다.

Uploaded by N2T

    'TIL | WIL' 카테고리의 다른 글
    • 10/19 (목) 원점에 가장 가까운 K개의 점 TIL
    • 10/18 (수) 마지막 남은 돌멩이의 무게(최대 힙 구현) TIL
    • 10/16 (월) 스트림에서 K번째로 큰 수 찾기(최소 힙 구현) + 우선순위 큐 개괄 TIL
    • 10/13 (금) 백트래킹 TIL
    깊은바다거북
    깊은바다거북

    티스토리툴바