@@ -20,7 +20,7 @@ public class TimSort {
2020 private static class IntMerge {
2121
2222 private int [] array ;
23- private final int [] runBase ;
23+ private final int [] startPointOfRun ;
2424 private final int [] runLen ;
2525 private int stackSize = 0 ; // run 스택의 원소 개수를 가리킬 변수
2626
@@ -51,17 +51,187 @@ public IntMerge(int[] a) {
5151 int stackLen = (len < 120 ? 5 :
5252 len < 1542 ? 10 :
5353 len < 119151 ? 19 : 35 );
54- runBase = new int [stackLen ];
54+ startPointOfRun = new int [stackLen ];
5555 runLen = new int [stackLen ];
5656 }
5757
58- public void pushRun (int runBase , int runLen ) {
59- this .runBase [stackSize ] = runBase ;
58+ public void pushRun (int startPoint , int runLen ) {
59+ this .startPointOfRun [stackSize ] = startPoint ;
6060 this .runLen [stackSize ] = runLen ;
6161 stackSize ++;
6262 }
63+
64+ public void merge () {
65+ while (stackSize > 1 ) {
66+
67+ /*
68+ * stack의 상위 3개 원소를 비교하며
69+ * 병합하기 위한 상위 3개 원소의 중간 피벗
70+ *
71+ */
72+ int pivot = stackSize - 2 ;
73+
74+ /*
75+ * 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
76+ * 2. runLen[i - 2] > runLen[i - 1]
77+ *
78+ * runLen[n-1] = A, runLen[n] = B, runLen[n+1] = C를 상위 세 요소로 합니다.
79+ * 운영상 루프는 다음과 같은 경우에 기초한다.
80+ * 1. A <= B + C 및 A < C일 경우 A 과 B 병합. 즉, n-1 과 n 병합
81+ * 2. A <= B + C 및 A >= C일 경우, B와 C 병합. 즉, n 과 n+1 병합
82+ * 3. A > B + C 및 B <= C일 경우, B와 C 병합. 즉, n 과 n+1 병합
83+ * 4. A > B + C 및 B > C일 경우 루프 종료
84+ */
85+
86+ // stack 요소가 3개 이상일 경우에만 3개를 비교할 수 있음
87+ if (stackSize > 2 ) {
88+ // A <= B + C
89+ if (runLen [pivot - 1 ] <= runLen [pivot ] + runLen [pivot + 1 ]) {
90+ // A < C
91+ if (runLen [pivot - 1 ] < runLen [pivot + 1 ]) {
92+ // A 와 B 병합 (pivot-1 and pivot merged)
93+ //merge(n - 1)
94+ }
95+ else { // A >= C
96+ //merge(n)
97+ }
98+ }
99+ // A > B + C && B <= C
100+ else if (runLen [pivot ] <= runLen [pivot + 1 ]) {
101+ //merge(n)
102+ }
103+ else {
104+ break ;
105+ }
106+ }
107+ // 유일하게 두 개만 남은 경우
108+ else if (stackSize > 1 ) {
109+ // merge(n)
110+ }
111+ }
112+ }
113+
114+ /**
115+ * run[idx] 와 run[idx + 1]이 병합 됨
116+ * @param idx 병합되는 두 서브리스트(run) 중 낮은 인덱스
117+ */
118+ private void merge (int idx ) {
119+
120+ int start1 = startPointOfRun [idx ];
121+ int length1 = runLen [idx ];
122+ int start2 = startPointOfRun [idx + 1 ];
123+ int length2 = runLen [idx + 1 ];
124+ }
125+
126+
127+ /**
128+ * gallop_right() 함수를 수행하여 RUN B 의 첫번째 원소보다
129+ * 큰 원소들이 첫번째 출현하는 위치를 RUN A 에서 찾는다.
130+ *
131+ *
132+ * @param key run B의 startPoint key
133+ * @param array 배열
134+ * @param startPoint run A의 startPoint
135+ * @param len run A 의 길이
136+ * @param hint
137+ * @return
138+ */
139+ private int gallopRight (int key , int [] array , int startPoint , int len , int hint ) {
140+
141+ /*
142+ * lastOffset과 다음 오프셋인 offset 사이를 구하고 이 구간에 대해
143+ * 이분탐색을 통해 최종 오프셋을 반환한다.
144+ */
145+ int lo = 0 ;
146+ int hi = 1 ;
147+
148+ /*
149+ * RUN A의 시작지점 값이 RUN B의 시작지점보다 클 경우
150+ */
151+ if (key < array [startPoint + hint ]) {
152+
153+ int maxOffset = hint + 1 ; // 최대 오프셋은 RUN A의 길이다.
154+
155+ // Gallop left until a[b + hint - hi] <= key < array[b + hint - lo]
156+ while (hi < maxOffset && key < array [startPoint + hint - hi ]) {
157+ lo = hi ;
158+ hi = (hi << 1 ) + 1 ; // 2배씩 건너뜀 탐색
159+
160+ // overflow가 발생시 maxOffset으로 만들어 while문의 break를 건다.
161+ if (hi <= 0 ) {
162+ hi = maxOffset ;
163+ break ;
164+ }
165+ }
166+
167+ // 최대로 가질 수 있는 오프셋을 벗어났을 경우 최대 값으로 초기화
168+ if (hi > maxOffset ) {
169+ hi = maxOffset ;
170+ }
171+
172+ int temp = lo ;
173+ lo = hint - hi ;
174+ hi = hint - temp ;
175+ }
176+
177+ else {
178+ // Gallop right until a[b + hint + lo] <= key < a[b + hint + hi]
179+ int maxOffset = len - hint ; // 최대로 가질 수 있는 offset
180+
181+ while (hi < maxOffset && array [startPoint + hint + hi ] <= key ) {
182+ lo = hi ;
183+ hi = (hi << 1 ) + 1 ;
184+
185+ if (hi <= 0 ) { // overflow
186+ hi = maxOffset ;
187+ break ;
188+ }
189+ }
190+
191+ if (hi > maxOffset ) {
192+ hi = maxOffset ;
193+ }
194+
195+ lo += hint ;
196+ hi += hint ;
197+ }
198+
199+ lo ++;
200+
201+ // binary search
202+ while (lo < hi ) {
203+
204+ /*
205+ * 중간 값을 구할 때 (lo + hi) / 2 를 하면
206+ * (lo + hi) 여기서 int overflow가 발생할 수 있다.
207+ *
208+ * 그러므로 hi - lo의 차이값에 2를 나눈 뒤,
209+ * lo을 더하는 방식으로 해준다.
210+ *
211+ * ex) lo = 3, hi = 7
212+ * 3 + ((7 - 3) / 2)
213+ * = 3 + (4 / 2)
214+ * = 3 + 2
215+ * = 5
216+ * ( == ((3 + 7) / 2 = 5) )
217+ */
218+ int mid = lo + ((hi - lo ) >>> 1 );
219+
220+ if (key < array [startPoint + mid ]) {
221+ hi = mid ;
222+ }
223+ else {
224+ lo = mid + 1 ;
225+ }
226+ }
227+ return hi ;
228+ }
63229 }
64230
231+
232+
233+
234+
65235 public static void sort (int [] a ) {
66236 sort (a , 0 , a .length );
67237 }
@@ -100,11 +270,28 @@ public static void sort(int[] a, int lo, int hi) {
100270 * minRun = 18 이라고 가정
101271 * [0, 2, 3, 8, 16, 4, 7, 26, 13, ..., 21]
102272 * incLength = 5
103- *
273+ * -> (lo + incLength) 이 이진삽입정렬 수행 시작점이 됨
104274 */
105275 if (incLength < minRun ) {
106276
277+ // 최소 run 크기가 남은 원소 개수보다 작을 수 있으므로 이를 처리해준다.
278+ int endPoint = remain < minRun ? remain : minRun ;
279+
280+ /*
281+ * BinarySort(array, lo, hi, start);
282+ * index[lo] <= index < index[endPoint] 구간을 삽입 정렬을 하되,
283+ * index[lo + incLength] 부터 삽입정렬을 시작함.
284+ * (이전 인덱스는 이미 오름차순 상태임)
285+ */
286+ BinarySort (a , lo , lo + endPoint , lo + incLength );
287+
288+ // 이진 삽입 정렬이 수행되었기에 증가하는 길이는 endPoint가 된다.
289+ incLength = endPoint ;
107290 }
291+
292+ // stack에 run의 시작점과 해당 run의 길이를 스택에 push한다.
293+ im .pushRun (lo , incLength );
294+
108295 } while (remain > 0 );
109296
110297 }
@@ -180,7 +367,7 @@ private static void reversing(int[] a, int lo, int hi) {
180367
181368 /*
182369 * arrayLength / runSize 이 2의 제곱근에 가까울수록 좋음(병합정렬 특성)
183- * 즉, 나오는 수는 THRESHOLD <= runSize <+ THRESHOLD 사잇값이 됨.
370+ * 즉, 나오는 수는 THRESHOLD <= runSize <= THRESHOLD 사잇값이 됨.
184371 *
185372 * 이를 구하기 위해 runSize을 THRESHOLD(32) 보다 작을 떄 까지 2씩 매 번 나눠가며
186373 * runSize 로 나눴을 때 근접하도록 한다.
0 commit comments