push_swap의 핵심은 stackB에 데이터의 section을 잘 나누어 대충 쌓아 놓는것이다.
Stack의 성질에 의해, StackB에는 내림차순으로 데이터를 정렬하면 StackA로 가져올 때, 삽입 위치를 빠르게 찾아갈 수 있다.
따라서, 최초데이터를 잘 나눠서 작은값 부터 -> 큰 값을 모두 B로 저장한다.
push_swap은 최소 명령어 수행
이 핵심이다. 따라서 명령어를 최소화 할 수 있는 전략을 고민해야 한다.
먼저 병합정렬 이해하기..
분할은 log2^n번일어남
n = 16
n = 9
n = 8
n = 12
병합정렬은 최소한으로 쪼갠 후, 정렬하면서 병합.
Point
n개의 원소를 가진 배열을 정렬하기 위해서 반씩 쪼개서, 합치면서 정렬을 한다.
가장 작은 단위부터 두 묶음씩 차근차근 정렬하면서 올라오기 때문에..
합치는 과정에서 비교연산의 횟수를 줄일 수 있다.
(정렬되었기 때문에 stop point를 찾기가 쉬워짐.)
이 병합 정렬을.. 어떻게 stack에 적용을 할 수 있는가..............
먼저, 러프하게 짜보기..
설계...
1. 스택A에서 -> 스택B로 내림차순으로 보낸다.
2. 스택B -> 스택A로 전부 가져온다.
5 3 2 0
에서 4 삽입할 때,3 2 0 5
만들고 삽입하고 4 3 2 0 5
에서 다시 최댓값을 원래대로 하기위해서 1내려주기4 3 2 0
에서 1삽입시 내려서 0 4 3 2
만들고, 1넣고 1 0 4 3 2
에서 다시 최대값을 원위치 시키기위해서 위로 + 1만큼 돌리기1 4 2 5 6 7
이렇게 있다면 4 1
로 저장 했을태고2 5 6 7
은 정렬 된상태에서, b를 다시 가져오면 1 4 2 5 6 7
로 되버림.이 방법으로는... 최적화하는 방법을 못찾겠다...
설계..
1. 최초 stack을 내림차순정렬해서 int **로 저장해둔다.
- 정렬 된 데이터를 참조하여 분할 범위를 정확히 반씩 줄여나갈 수 있다.
2. pivot을 현재 탐색 범위의 중간번째 데이터 값으로 정한다.
3. pivot이하의 값중 가장 빨리 보낼 수 있는 데이터를 찾아서, 먼저 B로보낸다.
4. B로 보내면서, B스택을 내림차순한다.
5. A가 비워지면 모든데이터를 A로 push만 한다.
* 1에서 내림차순한 이유
- pivot기준으로 작거나 같은 값을 B로 보내기 때문에
- 최초의 좌측범위를 볼때는 pivot값의 위치(인덱스)가 점점 줄어든다. 따라서 B로 보내는 범위를 좁혀가기 위한 전략으로 내림차순을 선택.
ㅇ 100일때 최초 50번째 이하의 값을 B로(50개)
ㅇ 다음에는 75번째 이하의 값을 모두 B로(25개)
ㅇ ...
50
49
48
.
.
[ 21 ~ 39 ] 가 들어와야 함
.
.
20
19
.
.
.
3
2
1
leaks push_swap
으로 확인.
int main(int argc, const char *argv[])
{
t_list *list;
if (argc < 2)
return (error_occur(0));
else
{
if (!make_list(&list, argc, argv))
return (0);
while(1); // for leak test
}
ft_lstiter(list, &print_list);
// ft_lstclear(&list, &delete_content); // check need
}
joopark
님의 답변으로 해답을 구했다.main 함수에서 malloc을 이용해 힙 영역에 동적 할당한 부분을 main 함수가 종료될 때 free 시켜주어야 합니다.
return 전에 while문으로 종료가 되지 않도록 막아 놓는다면 그 순간에는 아직 종료가 되지 않았으므로 누수가 없다고 나오며 프로그램 종료와 함께 그 프로세스의 누수를 검사하면 main문에서 할당한 부분이 해제되지 않았기 때문에 누수가 발견될 겁니다.
=> leaks -atExit -- ./[프로그램]로 해결가능.
ex) leaks -atExit -- ./push_swap 1 2 3
leaks
로 누수를 감지할 수 없다.a = 0
의차이.#include <stdlib.h>
#include <stdio.h>
int nothing(void)
{
return 1;
}
void test(void)
{
char *a = (char *)malloc(10);
a[0] = 'a';
return ;
}
void test2(void)
{
char *a = (char *)malloc(10);
a[0] = 'a';
a = 0;
return ;
}
int main1(void)
{
test();
while(1);
return (0);
}
int main2(void)
{
test2();
while(1);
return (0);
}
int main3(void)
{
test();
nothing();
while(1);
return (0);
}
int main4(void)
{
test2();
nothing();
while(1);
return (0);
}
void main5(void)
{
char *a = (char *)malloc(10);
a[0] = 'a';
while(1);
return (0);
}
test에서 malloc된 메모리주소가, 함수가 종료될 때 반환되고..다른 함수가 호출되면서 덮어쓰인다.. ?
test에서는 malloc한 메모리를 함수 종료시 반환할 수 있음. 따라서 다음 함수 호출 시 그냥 덮여쓰임.
test2에서는, 동적할당 메모리를 0으로 바꾸었기 때문에 stack에서 함수가 종료될 때 사용한 메모리 주소를 반환하지 못함. 따라서 동적할당한 메모리에 다시 접근할 수 없음.
위의 유추가 맞다면 main1, 2에서는 왜 leak이 발생하지 않은거지 ??
더 이상 heap영역을 사용하지 않으니깐 프로그램이 종료되기 전까지는 leak의 유무를 판단할 수 없다 ??
=> 덮어쓰이는 개념은 아닌 것 같다. malloc은 heap영역이고, 함수 호출시 stack을 쓰니깐.
시점의 차이
로 leak검출 결과 차이가 있는것 같다..위 원인을 찾으면.. 무한루프심어도 된다..
# 랜덤으로 num개의 데이터를 만들어서 수행
[1] ./test.sh [num]
# 3 ~ 9개의 원소를 가지는 데이터의 순열을 찾아 돌려봄.
# 5이상은 엄청느림. 중간에 못멈춰서 kill해야 함..
# 왠만하면 3 ~ 4만하기..
[2] ./test.sh all [num](3 ~ 9) => [num] element permutation test