구현단계 보안약점 중 SQL Injection에 대해 다뤄보고자 합니다.

구현단계 보안약점 유형 및 세부항목 소개( http://jwprogramming.tistory.com/262?category=682224 )


1. SQL Injection

1) SQL이란?

- 관계형 데이터베이스(MySQL, Oracle, Maria DB 등) 관리 시스템의 데이터를 관리하기 위해 설계된 프로그래밍 언어입니다.


그렇다면, 이 SQL Injection 공격은 무엇일까요?


대부분의 정보시스템은 데이터베이스를 사용하여 어떠한 데이터(사용자 ID, P/W, 시스템 관련 데이터 등)를 저장하고 있을 것입니다. 이렇게 DB운영자가 데이터베이스 시스템을 사용하기 위해서는 위의 SQL 구문을 이용하여 데이터베이스에 접근하여 데이터를 조회, 삽입, 삭제, 업데이트 등 일련의 행동을 할 것입니다.


예를 들어, SQL을 통해 아래와 같이 items 테이블로부터 사용자의 이름 등의 정보를 조회한다고 가정합니다.

string userName = request.getParameter("username");

string query = "SELECT * FROM items where owner = '"+ userName + "'"; 

위에서 userName은 외부에서 입력받아서 데이터베이스에 접근하게 되는 데이터일 것입니다.

만약 이 userName로 입력받는 값에 대하여 적절한 검증이 이루어지지 않은 채로 아래의 값이 들어오면 어떻게 될까요?

name' OR 'a'='a

해당 SQL Query는 참으로 인식하여, 데이터베이스에 허가되지 않은 사용자가 접근할 수 있게 됩니다. 

간단한 입력값만으로도 데이터베이스의 정보를 조작하거나 열람, 심지어 삭제해버릴 수도 있는 무서운 공격이 됩니다.


따라서, SQL Query에 사용되는 외부 입력값의 경우에는 1) preparedStatement를 이용하여 DB에 컴파일된 쿼리문을 전달하는 방법을 사용하거나, 2) 외부입력값에 대해 필터링하는 구문을 앞단에서 작성해주어야 합니다.

- [preparedStatement를 사용하는 경우]

string userName = request.getParameter("username");

string query = "SELECT * FROM items where owner = '" + userName + "'";

PreparedStatement s_username = con.prepareStatement(query);

s_username.setString(1, userName);

ResultSet rs = s_username.executeQuery();


SQL Injection은 개발자의 개발습관에 따라 쉽게 막을 수 있는 공격이지만,

보안대책을 적절히 구현하지 못하였을 시에는 무서운 공격이 될 수도 있으므로 개발 시에 DB에 접근하는 Query문의 경우에 검증하는 로직을 필히 구현하시기 바랍니다.


WRITTEN BY
SiriusJ

,

소프트웨어 보안약점은 지난번 포스팅(http://jwprogramming.tistory.com/261)에서 소개를 해드렸습니다.


더불어, 현재 공공 정보화사업 대상으로 지침에 의무화 되어 있는 소프트웨어 보안약점 기준(구현단계)은 무엇이 있는지 살펴보고자 합니다.


해당 지침에서는 구현단계에서 확인 및 제거해야 하는 보안약점 유형을 7개로 분류하고 해당 유형별 세부항목을 47개로 아래와 같이 정의하고 있습니다.

------------------------------------------------------------------------------------------------------------------------

유형 1. 입력데이터 검증 및 표현 : 사용자, 프로그램 입력 데이터에 대한 유효성 검증 체계를 갖추고, 실패 시 처리할 수 있도록 설계

- 1) SQL 삽입

- 2) 경로 조작 및 자원 삽입

- 3) 크로스사이트 스크립트

- 4) 운영체제 명령어 삽입

- 5) 위험한 형식 파일 업로드

- 6) 신뢰되지 않는 URL 주소로 자동접속 연결

- 7) XQuery 삽입

- 8) XPath 삽입

- 9) LDAP 삽입

- 10) 크로스사이트 요청 위조

- 11) HTTP 응답분할

- 12) 정수형 오버플로우

- 13) 보안기능 결정에 사용되는 부적절한 입력값

- 14) 메모리 버퍼 오버플로우

- 15) 포맷 스트링 삽입


유형 2. 보안기능 : 인증, 접근통제, 권한관리, 비밀번호 등의 정책이 적절하게 반영될 수 있도록 설계

- 1) 적절한 인증 없는 중요기능 허용

- 2) 부적절한 인가

- 3) 중요한 자원에 대한 잘못된 권한 설정

- 4) 취약한 암호화 알고리즘 사용

- 5) 중요정보 평문저장

- 6) 중요정보 평문전송

- 7) 하드코드된 비밀번호

- 8) 충분하지 않은 키 길이 사용

- 9) 적절하지 않은 난수값 사용

- 10) 하드코드된 암호화 키

- 11) 취약한 비밀번호 허용

- 12) 사용자 하드디스크에 저장되는 쿠키를 통한 정보노출

- 13) 주석문 안에 포함된 시스템 주요정보

- 14) 솔트없이 일방향 해쉬함수 사용

- 15) 무결성 검사없는 코드 다운로드

- 16) 반복된 인증시도 제한 기능 부재


유형 3. 시간 및 상태 : 동시 또는 거의 동시 수행을 지원하는 병렬 시스템, 하나 이상의 프로세스가 동작되는 환경에서 시간 및 상태를 부적절하게 관리하여 발생할 수 있는 보안약점

- 1) 경쟁조건: 검사 시점과 사용 시점(TOCTOU)

- 2) 종료되지 않는 반복문 또는 재귀 함수


유형 4. 에러처리 : 에러를 처리하지 않거나 불충분하게 처리하여 중요정보(시스템 등)가 포함될 때 발생할 수 있는 보안약점

- 1) 오류 메시지를 통한 정보노출

- 2) 오류 상황 대응 부재

- 3) 부적절한 예외 처리


유형 5. 코드오류 : 타입변환 오류, 자원(메모리 등)의 부적절한 반환 등과 같이 개발자가 범할 수 있는 코딩 오류로 인해 유발되는 보안약점

- 1) Null Pointer 역참조

- 2) 부적절한 자원 해제

- 3) 해제된 자원 사용

- 4) 초기화 되지 않은 변수 사용


유형 6. 캡슐화 : 중요한 데이터 또는 기능성을 불충분하게 캡슐화 하였을 때, 인가되지 않은 사용자에게 데이터 누출이 가능해지는 보안약점

- 1) 잘못된 세션에 의한 데이터 정보 노출

- 2) 제거되지 않고 남은 디버그 코드

- 3) 시스템 데이터 정보노출

- 4) Public 메소드로부터 반환된 Private 배열

- 5) Private 배열에 Public 데이터 할당


유형 7. API 오용 : 의도된 사용에 반하는 방법으로 API를 사용하거나, 보안에 취약한 API를 사용하여 발생할 수 있는 보안약점

- 1) DNS lookup에 의존한 보안결정

- 2) 취약한 API 사용

------------------------------------------------------------------------------------------------------------------------

과 같이 분류되고 있습니다.


자세한 내용은 '행정기관 및 공공기관 정보시스템 구축.운영 지침' 에서 참고할 수 있습니다. 


WRITTEN BY
SiriusJ

,

일반적으로 소프트웨어를 개발할 때, 보안을 신경쓰면서 개발하는 분들은 많지 않을 것입니다.


하지만 우리가 C, JAVA 등의 언어를 이용하여 개발할 때, 수많은 보안약점들이 내재되어 있다고 봐도 무방합니다.


이 보안약점이란 무엇일까요?


'소프트웨어 보안약점' 이란 소프트웨어 개발 시, 소프트웨어에 결함이 될 수 있는 논리적인 오류나 버그, 실수 등 이후 취약점으로 발생될 수 있는 근본 원인을 뜻합니다.


그렇다면 '소프트웨어 보안취약점' 이란 무엇일까요?

'소프트웨어 보안취약점' 이란 정보시스템(소프트웨어)에 실제로 내재되어있는 위험요소들로, 공격자가 이를 악용해서 해당 시스템의 정상적인 작동을 방해하거나 정보유출, 권한 상승 등을 일으킬 수 있는 것입니다.

> 소프트웨어 보안약점과 소프트웨어 보안취약점은 용어는 비슷해보이나, 소프트웨어의 보안사고를 대비하기 위해서는 원천적인 소프트웨어 보안약점을 제거하는 것이 필요합니다.


현재는 행정안전부에서 '행정기관 및 공공기관의 정보시스템 구축.운영 지침' 을 통하여 개발자가 구현(개발)단계에서 확인해야 하는 47개의 보안약점을 정의하고 있습니다.

(지금은 행정기관이나 공공기관 등의 공공 정보화 사업에서 의무화 되어 지키도록 강제하고 있지만, 민간에서도 해당 지침을 참고하여, 보다 안전한 소프트웨어를 개발하는 것이 필요하다고 생각합니다.)


크게 7개의 보안약점 유형으로 분류되고 있고, 각 유형별 세부 항목들로는 47개로 정의하고 있으며 이에 대한 자세한 설명은 다음 포스팅에서 다루도록 하겠습니다.


WRITTEN BY
SiriusJ

,

악성코드(Malware)란 사용자 컴퓨터에 악의적인 영향을 끼칠 수 있는 모든 소프트웨어를 말합니다.


[악성코드의 유형]

1. 컴퓨터 바이러스 (Computer virus)

 정상적인 파일이나 시스템 영역에 침입하여 그곳에 자신의 코드를 삽입하거나 설치하는 프로그램을 칭하는 이름으로 감염 방법이나 동작 원리에 따라 메모리 상주형 바이러스, 파일 바이러스, 덮어쓰기, 은폐형 등 여러 가지로 세분화하여 나뉩니다.


2. (Worm)

 컴퓨터 바이러스처럼 다른 파일을 감염시키는 것이 아닌 자기 자신을 레지스트리에 등록하거나 복사본을 생성하여 전파하는 등의 독자적으로 실행되는 악성코드를 말합니다

 웜은 이메일에 첨부되어 확산되거나, P2P 파일 공유, 프로그램 보안 취약점, 네트워크 공유 기능 등을 이용하여 스스로 증식하여 빠르게 확산한다는 특징을 가지고 있습니다.


3. 트로이 목마 (Trojan horse)

 정상적인 소프트웨어의 형태를 띠지만 악의적인 행위를 포함하고 있는 악성코드를 말합니다

다른 파일을 감염하는 형태가 아닌 스스로 피해를 유발하는 악성코드를 말하며, 주로 웹페이지, 이메일, 파일 공유 사이트 등을 통해서 일반 프로그램으로 가장하여 사용자가 클릭하기를 기다리는 형태의 전파 방법을 사용합니다.


4. 스파이웨어 (Spyware)

 사용자의 PC에서 사전 동의 없이 설치되어 컴퓨터의 정보와 개인 정보를 수집하는 악성코드의 종류입니다. 신용카드와 같은 금융정보 및 주민등록번호와 같은 신상정보, 사이트 아이디나 비밀번호 등의 각종 정보를 수집하여 원격지의 특정 서버에 주기적으로 보내는 형태를 말합니다.


5. 백도어 (Backdoor)

 감염된 사용자 PC에 특정 포트를 열어두어 정상적인 인증 과정 없이 원격 접속을 통해 직접 조작하는 형태로, 사용자 몰래 특정 파일을 삭제하거나 파일이나 정보를 빼가는 등의 행위를 합니다

 백도어의 종류에는 여러 가지가 있는데

1) 로컬 백도어의 경우 기존의 일반 계정의 비밀번호를 알아낸 후 새로운 계정을 만들어 사용하는 것을 말하고, 2) 원격 백도어의 경우에는 따로 시스템 계정이 필요하지 않으며, 원격에서 공격자가 지정한 포트를 통해 접속하여 사용합니다. 원격 GUI 백도어의 경우 원격관리 툴과 같은 형태로 직접 마우스를 제어하며 조작하는 형태입니다.


6. 키로거 (Keylogger)

 컴퓨터가 입력받는 정보를 기록하는 것으로 그 중에서도 주로 키보드를 통한 메시지를 중간에 가로채서 기록하는 형태를 말합니다. 키로거 탐지를 위한 다양한 키보드 보안 솔루션이 나왔지만 이에 대응하는 새로운 방식의 키로거가 계속해서 나오고 있습니다. 초기 키로거가 단순히 윈도우 메시지를 후킹하는 형태였지만, 루트킷을 이용하여 점차 고도화 된 기술의 키로거로 변화하고 있습니다.


7. 드롭퍼 (Dropper)

 일종의 악성 프로그램의 설치 프로그램의 형태로, 실행 시 내부에 포함되어 있던 바이러스나 웜, 또는 트로이 목마 등의 악의적인 프로그램이 설치되는 형태의 악성코드를 말합니다. 백신 프로그램에서 악성코드가 실행된 후 의심스러운 행위를 탐지하여 차단하는 방법 등 대응 방법을 우회하기 위해 프로그램 실행 자체에서는 파일 설치 이외의 아무 행위를 하지 않고, 일정 시간이 지난 후 악성코드를 실행하는 등의 시간차를 이용합니다.


8. 다운로더 (Downloader)

 프로그램에서 지정한 웹 사이트에 접속하여 추가 악성코드를 다운로드하여 실행시키는 악성코드입니다. 다운로더는 드롭퍼와 같이 백신 프로그램을 우회하는 목적으로 사용됩니다.


9. 다형성 바이러스 (Polymorphic virus)

 다형성 바이러스의 경우, 원본 바이너리에서 같은 기능을 수행하지만 형태는 달라지는 악성코드로, 바이너리 고유 패턴을 변경시켜 휴리스틱 기반의 백신 업체들의 감염여부를 파악하기 어렵게 하여 추적을 피하려는 목적의 악성코드입니다.


10. 애드웨어 (Adware)

 상업용 광고 목적으로 만들어진 악성코드로, 웹 브라우저 시작페이지 변경, BHO(Browser Helper Object) 객체를 이용하여 팝업 윈도우 생성 등의 동작을 합니다. 애드웨어와 비슷한 형태로는 하이재커(Hijacker)라는 툴바 설치 등의 브라우저 형태 자체를 변경시키는 악성코드가 있습니다.


11. 랜섬웨어 (Ransomware)

 랜섬웨어란 사용자의 문서와 사진 등을 암호화 시켜 일정 시간 안에 일정 금액을 지불하면 암호를 풀어주는 방식으로 사용자에게 금전적인 요구를 하는 악성코드를 말합니다.


12. 루트킷 (Rootkit)

 악의적인 행동을 하는 프로그램을 숨기기 위한 목적으로 시작된 악성코드로, 현재는 프로세스나 파일 등의 흔적을 사용자가 볼 수 없도록 하는 프로그램에 대한 이름으로 사용됩니다. 루트킷의 설치 위치는 커널, 가상화 계층, 부트로더, 펌웨어, 라이브러리 등 다양한 위치에서 작동될 수 있습니다.


13. 부트킷 (Bootkit)

 PC의 부팅영역인 MBR(Master Boot Record)를 조작하는 프로그램을 칭하는 이름으로, 크게 파괴형 부트킷과 은신형 부트킷으로 나뉩니다

 1) 파괴형 부트킷이란 MBR(Master Boot Record)VBR(Volume Boot Record)를 의미 없는 문자열 등으로 덮어씌워 부팅 시 시스템 정보를 읽어오지 못하게 만드는 형태이고

 2) 은신형 부트킷의 경우 부트섹터의 빈 영역에 악성코드를 설치하여 백신이 탐지하지 않게 만드는 형태를 말합니다.

'Security > 정보보호 잡지식' 카테고리의 다른 글

OWASP TOP 10(2017) 간단 정리  (0) 2021.05.27
데이터 3법에 대하여  (0) 2020.03.29
Scan(스캔)  (0) 2016.07.17
DNS를 이용한 정보 습득  (0) 2016.07.13
Whois 서버를 이용해 정보 획득하기  (0) 2016.07.13

WRITTEN BY
SiriusJ

,

[ 정렬들(Selection, Insertion, Bubble, Quick, Merge, Heap, Radix)에 대하여

수행시간, 비교 횟수, Swap횟수를 통한 비교 ]

 

1) Selection Sort :

Best Case

Worst Case

Stable

In-place

비교 횟수

Swap 횟수

O( n^2 )

O( n^2 )

X

O

O( n^2 )

O( n )

분석 : 데이터의 개수가 n개 일 때, 최소값을 선택하는 데 걸리는 시간 = O( n )

-> 최솟값을 선택하여 리스트의 첫 번째 요소와 자리를 바꾼다.

안정성을 만족하지 않고, 전체 시간복잡도는 O( n^2 )을 따른다.

데이터를 0부터 n까지 매번 비교해야 하므로, n(n-1)/2 번 비교하게 되며 O( n^2 ) 이 되고,

Swap횟수는 찾은 최소값과 리스트의 첫 번째 요소와 바꿔주면 되므로, n-1번 수행되어 O( n ) 이 된다.

 

2) Insertion Sort :

Best Case

Worst Case

Stable

In-place

비교 횟수

Swap 횟수

O( n )

O( n^2 )

O

O

O( n^2 )

O( n^2 )

분석 : 리스트의 값들을 순회하며 리스트에서 정렬이 필요한 데이터는 적당한 곳에 삽입해나간다.

정렬되어 있는 부분에 새로운 레코드를 적절한 위치에 삽입해야하는 과정을 반복해야 한다.

안정성이 보장되고 이미 정렬되어있으면 O(n)이 되어 효율적이지만, 삽입하는 과정이 있으므로 데이터가 많고 정렬이 되어있지 않다면 비효율적이다.

Worst Case는 데이터가 역순으로 정렬되어 있는 경우이며, 비교 횟수는 매번 데이터들의 값을 비교하여 위치를 옮겨주는 과정을 수행해야 하므로, n(n-1)/2 번 비교하며,

Swap은 데이터를 매번 비교하여 이동하는 횟수( n(n-1)/2 ) , 데이터가 n개일 때, 처음 인덱스에 대하여 n-1번 만큼 수행해야 하므로 n(n-1)/2 +(n-1) = O( n^2 ) 이 되고, 결과적으로 비교 횟수와 Swap횟수 둘 다 O( n^2 )이 된다.

 

3) Bubble Sort :

Best Case

Worst Case

Stable

In-place

비교 횟수

Swap 횟수

O( n^2 )

O( n^2 )

O

O

O( n^2 )

O( n^2 )

분석 : 인접한 리스트의 요소를 매번 비교해야 하고, 비교 시마다 인접한 요소가 정렬이 되어 있지 않다면 Swap을 해야하므로, 비교 횟수의 경우에는 항상 n(n-1)/2 번 비교하게 되며,

Swap의 경우에는 Best Case의 경우에는 이미 정렬되어 Swap하지 않아도 되는 경우이므로 0이지만, Worst Case의 경우일 때에는 비교와 같이 n(n-1)/2 번 수행해야 하므로, 비교 횟수와 Swap횟수 둘 다 O( n^2 ) 이 된다.

 

4) Quick Sort :

Best Case

Worst Case

Stable

In-place

비교 횟수

Swap 횟수

O( nlogn )

O( n^2 )

X

O

O( n^2 )

O( n )

분석 : 평균적으로 가장 빠르다고 알려져 있는 정렬방법이다. 리스트를 O( n )만큼 추가적으로 할당하여 정렬하는 방법이 있긴 하지만 Stable하고 In-place하지 않으므로 보통은 추가적인 메모리를 더 필요로 하지 않는 방법을 사용한다.

pivot값을 세우고, pivot값을 기준으로 왼쪽과 오른쪽을 분할하여 각각의 부분을 다시 재귀적으로 정렬하는 방법이다.

(pivot 값을 median, 즉 리스트의 중간 값으로 설정하게 되면 Worst case를 피할 수 있는 확률이 더 높아지기 때문에 효율이 높아지게 된다.)

Best Case는 왼쪽과 오른쪽 분할된 부분의 크기가 거의 균등하게 분할되는 경우이며, 분할된 Level의 크기는 logn 이며, 각 분할된 부분에 대하여 비교를 수행하는 횟수는 n 이므로, 비교 횟수는 O(logn) 이 된다.

그러나 Worst Case의 경우에는 왼쪽과 오른쪽이 불균등하게(한쪽은 1, 한쪽은 n-1) 분할되는 경우이며,

 

분할된 Level의 크기가 n 이 되고, 분할된 부분에 대하여 비교를 수행하는 횟수는 n 이므로 총 비교 횟수는 O( n^2 ) 이 되고, Swap횟수는 분할된 부분에 대하여만 Swap을 수행하면 되므로 비교 횟수보다 항상 적다.

 

5) Merge Sort :

Best Case

Worst Case

Stable

In-place

비교 횟수

Swap 횟수

O( nlogn )

O( nlogn )

O

X

O( nlogn )

O( nlogn )

분석 : 리스트를 두 개로 분할하는 과정을 재귀적으로 수행한 후, 다시 올라오며 각각을 정렬하며 합쳐지는 과정을 수행한다. 분할할 때에는 정렬을 하지 않으며, 전부 분할 한 후에 다시 분할된 부분을 합치며 정렬을 수행하는 방식이다.

Best Case, Worst Case에 관계없이 퀵 정렬의 이상적인 분배를 위하여 항상 균등 분할을 하게 되며, 분할된 Level의 크기는 logn 이 되며, 각 분할된 부분에서 n 번의 비교가 수행된다. 따라서 비교 횟수는 O(nlogn) 이 된다.

Swap의 경우 각 분할된 부분에서 ( n *2)번의 Swap이 수행되므로 비교 횟수는 O(nlogn) 이 된다.

 

6) Heap Sort:

Best Case

Worst Case

Stable

In-place

비교 횟수

Swap 횟수

O( nlogn )

O( nlogn )

X

O

O( nlogn )

O( nlogn )

분석 : Heap에서는 각 노드는 자식 노드의 값보다 작으면 안 된다는 성질을 가지고 있다.

n개의 노드에 대하여 완전이진트리는 log(n+1) 의 Level을 가지므로 평균 시간복잡도는 O( nlogn )이 되며,

최악의 경우에 비교는 매번 Heapify를 통해 힙을 재구성 하는 부분에서 Level의 크기( logn )에 대하여 n번 비교를 하게 되므로, O( nlogn )이 되고, Swap횟수는 heapify에서 매번 Swap이 일어나는 경우에, 루트노드와 마지막 노드를 바꿔주는 수행 횟수도 더해주어야 하므로, O( nlogn ) + O(n) = O( nlogn ) 이 된다.

 

7) Radix Sort:

Best Case

Worst Case

Stable

In-place

비교 횟수

Swap 횟수

O( d*n )

O( d*n )

O

X

O

O

분석 : 리스트의 전체를 비교하는 것이 아니라, 자리 수를 이용하여 정렬하는 방법이다.

현재 일반적인 정렬 방법의 가장 효율적인 시간복잡도는 O( nlogn )이지만, 이것보다 더 좋은 효율을 낼 수 있는 방법이기도 하지만, 정렬할 수 있는 타입이 숫자와 같이 제한적이라는 단점이 있고, 큐 방식을 이용하여 자릿수에 따라 분할하기 때문에 비교나 Swap횟수는 따로 수행하지 않아 0이 된다.


아래의 그림은 테스트 데이터(정수)의 갯수 n을 각각 100개, 1000개, 10000개, 50000개로 했을 때의 Swap횟수와 비교횟수, 수행시간의 데이터이다.

n = 100

n = 1000 

 

 

 n = 10000

n = 50000 

 

 



WRITTEN BY
SiriusJ

,

C Shell Sort로 오름차순 정렬하는 방식입니다.

시간복잡도 : O(n^2) - Worst case

Best case : O(n), Average Case : O(n^1.25)


#include <stdio.h>


void printArray(int value[], int count) {

int i = 0;

for (i = 0; i < count; i++)

printf("%d ", value[i]);

printf("\n");

}


void shellInsertionSort(int value[], int start, int end, int interval) {

int i = 0, item = 0, index = 0;

for (i = start + interval; i <= end; i = i + interval) {

item = value[i];

index = i - interval;

while ((index >= start) && item < value[index]) {

value[index + interval] = value[index];

index = index - interval;

}

value[index + interval] = item;

}

}


void shellSort(int value[], int count) {

int i = 0, interval = 0;

interval = count / 2;

while (interval >= 1) {

for (i = 0; i < interval; i++)

shellInsertionSort(value, i, count - 1, interval);

printf("Interval-%d, ", interval);

printArray(value, count);

interval = interval / 2;

}

}


int main(int argc, char *argv[]) {

int values[] = { 80, 50, 70, 10, 60, 20, 40, 30 };

printf("Before shellSort\n");

printArray(values, 8);


shellSort(values, 8);


printf("\nAfter Sort\n");

printArray(values, 8);


return 0;

}


WRITTEN BY
SiriusJ

,

C Merge Sort로 오름차순 정렬하는 방식입니다.

시간복잡도 : O(nlogn)


#include <stdio.h>

#include <stdlib.h>


void printArray(int value[], int count) {

int i = 0;

for (i = 0; i < count; i++)

printf("%d ", value[i]);

printf("\n");

}


void mergeSortInternal(int value[], int buffer[], int start, int middle, int end) {

int i, j, k, t;

i = start;

j = middle + 1;

k = start;

while (i <= middle && j <= end) {

if (value[i] <= value[j]) {

buffer[k] = value[i];

i++;

}

else {

buffer[k] = value[j];

j++;

}

k++;

}


if (i > middle) {

for (t = j; t <= end; t++, k++)

buffer[k] = value[t];

}

else {

for (t = i; t <= middle; t++, k++)

buffer[k] = value[t];

}


for (t = start; t <= end; t++)

value[t] = buffer[t];


printf("start-%d, middle-%d, end-%d, ", start, middle, end);


for (t = start; t <= end; t++)

printf("%d ", value[t]);

printf("\n");

}


void mergeSort(int value[], int buffer[], int start, int end) {

int middle = 0;

if (start < end) {

middle = (start + end) / 2;

mergeSort(value, buffer, start, middle);

mergeSort(value, buffer, middle + 1, end);

mergeSortInternal(value, buffer, start, middle, end);

}

}


int main(int argc, char *argv[]) {

int values[] = { 80, 50, 70, 10, 60, 20, 40, 30 };

int *pBuffer = NULL;

pBuffer = (int *)malloc(sizeof(values));

printf("Before mergeSort\n");

printArray(values, 8);


if (pBuffer != NULL) {

mergeSort(values, pBuffer, 0, 7);

free(pBuffer);

}


printf("\nAfter Sort\n");

printArray(values, 8);


return 0;

}


WRITTEN BY
SiriusJ

,

C Quick Sort로 오름차순 정렬하는 방식입니다.

시간복잡도 : O(n^2) - Worst Case 일 때 이지만,

일반적으로는 가장 빠른 정렬으로 시간복잡도를 O(nlogn) 으로 봅니다.


#include <stdio.h>


void printArray(int value[], int count) {

int i = 0;

for (i = 0; i < count; i++)

printf("%d ", value[i]);

printf("\n");

}

void quickSort(int value[], int start, int end) {

int pivot = 0;

if (start < end) {

pivot = partitionQuickSort(value, start, end);

quickSort(value, start, pivot - 1);

quickSort(value, pivot + 1, end);

}

}

int partitionQuickSort(int value[], int start, int end) {

int pivot = 0, temp = 0, left = 0, right = 0;

left = start;

right = end;

pivot = end;


while (left < right) {

while ((value[left] < value[pivot]) && (left < right))

left++;

while ((value[right] >= value[pivot]) && (left < right))

right--;

if (left < right) {

temp = value[left];

value[left] = value[right];

value[right] = temp;

printf("start-[%d], end-[%d], pivot-[%d], in-loop, ", start, end, value[pivot]);

printArray(value, end - start + 1);

}

}

temp = value[pivot];

value[pivot] = value[right];

value[right] = temp;

printf("start-[%d], end-[%d], pivot-[%d], out-loop, ", start, end, value[right]);


printArray(value, end - start + 1);

return right;

}


int main(int argc, char *argv[]) {

int values[] = { 80, 50, 70, 10, 60, 20, 40, 30 };

printf("Before QuickSort\n");

printArray(values, 8);


quickSort(values, 0, 7);


printf("\nAfter Sort\n");

printArray(values, 8);


return 0;

}


WRITTEN BY
SiriusJ

,