๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿค– data structures & algorithms

Data Structures & Algorithms with Javascript : Sorting Algorithms (Advanced)

2024.07.27 - [๐Ÿค– data structures & algorithms] - Data Structures & Algorithms with Javascript : Sorting Algorithms (basic)

 

Data Structures & Algorithms with Javascript : Sorting Algorithms (basic)

2024.07.26 - [๐Ÿชฒ debug] - Data Structures & Algorithms with Javascript : Graphs and Graph Algorithms Data Structures & Algorithms with Javascript : Graphs and Graph Algorithms2024.07.25 - [๐Ÿค– data structures & algorithms] - Data Structures & Algorithm

pyotato-dev.tistory.com

๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป ์ด์ „ ํฌ์ŠคํŒ…์—์„œ๋Š” ๊ธฐ๋ณธ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ธ  ๋ฒ„๋ธ” ์ •๋ ฌ, ์„ ํƒ ์ •๋ ฌ, ๊ทธ๋ฆฌ๊ณ  ์‚ฝ์ž… ์ •๋ ฌ์— ๋Œ€ํ•ด ์•Œ์•„๋ดค๋‹ค.
๐Ÿ€ ์ด๋ฒˆ์—๋Š” ๋งŽ์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„ ๋•Œ ํšจ์œจ์ ์œผ๋กœ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ๋Š” ์ข€ ๋” ์‹ฌํ™”๋œ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜๋“ค์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

โ˜‘๏ธ ์˜ค๋Š˜ ์‚ดํŽด๋ณผ ์•Œ๊ณ ๋ฆฌ์ฆ˜ : Shellsort, Mergesort, Quicksort

The Shellsort Algorithm

 

Shellsort๋Š” ๊ฐœ๋ฐœํ•œ Donald Shell์—์„œ ๋”ฐ์˜จ ์ด๋ฆ„์ด๋‹ค. ์ด์ „ ์‚ดํŽด๋ณธ ์‚ฝ์ž… ์ •๋ ฌ์„ ๊ฐœ์„ ํ•œ ๋ฒ„์ „์ด๋‹ค. 

Shellsort๋Š” ์ธ์ ‘ํ•œ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋จผ์ € ์‚ดํŽด๋ณด๋Š” ๋Œ€์‹  ๊ฐ€์žฅ ๋จผ ์—˜๋ฆฌ๋จผํŠธ๋“ค์„ ๋น„๊ตํ•œ๋‹ค. 

๊ฐ€์žฅ ๋จผ ์—˜๋ฆฌ๋จผํŠธ๋“ค์˜ gap์„ ์ ์  ์ค„์—ฌ๊ฐ€๋Š”๋ฐ, ์ด gap ์‹œํ€€์Šค๋Š”  Marcin Ciura์˜ ๋…ผ๋ฌธ(“Best Increments for the Average Case of Shell Sort”, 2001)์—์„œ Shellsort์˜ ํ‰๊ท  gap ์‹œํ€€์Šค์ธ 701, 301, 132, 57, 23, 10, 4, 1์œผ๋กœ ์ •์˜ํ•œ ๊ฒƒ์„ ํ™œ์šฉํ•  ๊ฒƒ์ด๋‹ค.

 

๋‹ค์Œ์€ gap ์‹œํ€€์Šค๋ฅผ ๊ฐ๊ฐ 3,2,1๋กœ ์„ค์ •ํ–ˆ์„ ๊ฒฝ์šฐ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

gap ์‹œํ€€์Šค = 3์ธ ๊ฒฝ์šฐ shellSort ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด์ž. 

61 85 19 88 68 8 70 29 (์‹œ์ž‘)
61 85 19 88 68 8 70 29 
61 8 19 88 68 85 70 29
61 8 19 88 68 85 70 29
61 8
19 29 68 85 70 88 (gap = 1 ๋กœ ์ค„์ด๊ธฐ)

8 19 61 29 68 85 70 88
8 19 29 61 68 85 70 88
8 19 29 61 68 70 85  88 (์™„๋ฃŒ)

 

๊ตฌํ˜„

 

์œ„์˜ ์˜ˆ์‹œ์—์„œ ์‚ดํŽด๋ดค๋“ฏ์ด ์ •ํ•ด์ง„ gap(๊ฐ„๊ฒฉ)๋งŒํผ ๋ฐฐ์—ด ์ „์ฒด๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ gap ๋‚ด๋ถ€์˜ ๊ฐ’๋“ค์„ ๋น„๊ตํ•ด์„œ ์ •๋ ฌํ•œ๋‹ค.

์ ์  gap์„ ์ค„์ด๋ฉด์„œ ์ž‘์€ ๊ฐ’์ด ์˜ค๋ฅธ์ชฝ์— ์˜ค๋„๋ก ์ž๋ฆฌ๋ฅผ ๋ฐ”๊ฟ”์ฃผ๋‹ค ๋ณด๋ฉด ์ •๋ ฌ์ด ๋œ๋‹ค.

gap์ด 1์ผ ๊ฒฝ์šฐ ์‚ฝ์ž… ์ •๋ ฌ๊ณผ ๋™์ผํ•œ ๋งค์ปค๋‹ˆ์ฆ˜์œผ๋กœ ์ •๋ ฌ์„ ํ•ด์ฃผ๊ณ  ์žˆ๋‹ค.

 

shellSort ๋งค์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด ๋ณด์ž.

์™ธ๋ถ€ for ๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ gap์„ ์ˆœํšŒํ•œ๋‹ค. ์œ„์˜ ์˜ˆ์‹œ์—์„œ๋Š” gap ๋ฐฐ์—ด์ด [3,1]๋กœ ์ด๋ฃจ์–ด์ง„ ๊ฒฝ์šฐ๋‹ค.

๋‚ด๋ถ€ for ๋ฃจํ”„์—์„œ๋Š” ์ •๋ ฌํ•˜๊ณ ์žํ•˜๋Š” ๋ฐฐ์—ด์˜ ์ฒซ ์š”์†Œ์™€ gap ๋งŒํผ์˜ ๋–จ์–ด์ง„ ์ธ๋ฑ์Šค์— ์œ„์น˜ํ•œ ์š”์†Œ์˜ ๋Œ€์†Œ๋ฅผ ๋น„๊ตํ•˜์—ฌ ๊ฐ’์ด ์ž‘์€ ๊ฑฐ๋ฅผ ๋ฐฐ์—ด ์•ž์— ์˜ค๋„๋ก ์ž๋ฆฌ๋ฅผ ๋ฐ”๊ฟ”์ค€๋‹ค. 

์˜ˆ๋ฅผ ๋“ค์–ด, ํ˜„์žฌ gap์ด 3์ผ ๋•Œ ๋ชจ๋“  ๋ฐฐ์—ด ์›์†Œ 0๊ณผ 3, 1๊ณผ 4, 2์™€ 5... ๋Œ€์†Œ ๋น„๊ต๋ฅผ ํ•ด์„œ ์ž๋ฆฌ๋ฅผ ๋ฐ”๊ฟ”์ค€๋‹ค. ๋ชจ๋“  ์ž๋ฆฌ๋ฐ”๊ฟˆ์ด ๋๋‚œ ๋’ค, gap์ด 1์ผ ๋•Œ, ๋ชจ๋“  ๋ฐฐ์—ด ์›์†Œ 0๊ณผ 1, 1๊ณผ 2, 2์™€ 3... ๋Œ€์†Œ ๋น„๊ต๋ฅผ ํ•ด์„œ ์ž๋ฆฌ๋ฅผ ๋ฐ”๊ฟ”์ฃผ๋ฉด ์ •๋ ฌ์„ ์™„๋ฃŒํ•œ๋‹ค.

 shellSort(){
    for (let g = 0; g < this.gaps.length; ++g) {
        for (let i = this.gaps[g]; i < this.dataStore.length; ++i) { 
            let temp = this.dataStore[i];
            for (let j = i; j >= this.gaps[g] && this.dataStore[j-this.gaps[g]] > temp; j -= this.gaps[g]) {
                this.dataStore[j] = this.dataStore[j - this.gaps[g]]; 
            }
            this.dataStore[g] = temp; 
        }
    }
}

 

๋‹จ์ˆœ ์‚ฝ์ž… ์ •๋ ฌ๋ณด๋‹ค gap์„ ์ค„์—ฌ๊ฐ€๋ฉด์„œ ๋น„๊ต๋ฅผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋” ํšจ์œจ์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค.

๋‹ค์‹œ ๋งํ•ด, ์‚ฝ์ž… ์ •๋ ฌ์€  ์ •๋ ฌ์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋ชจ๋“  ์š”์†Œ๋“ค์„ ๋น„๊ตํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ฐฐ์—ด์˜ ๋๊นŒ์ง€ ์ˆœํšŒ๋ฅผ ๋ฐ˜๋ณตํ•ด์•ผํ•œ๋‹ค.

ํ•˜์ง€๋งŒ gap=5์ธ ๊ฒฝ์šฐ ๋ชจ๋“  ๋ฐฐ์—ด์š”์†Œ๋“ค์„ ์ˆœํšŒํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ฐฐ์—ด ๊ธธ์ด -5 ๋งŒํผ ์ˆœํšŒํ•˜๊ณ , ๊ทธ๋‹ค์Œ์—๋Š” gap =3 ์ธ ๊ฒฝ์šฐ์—๋Š” q๋ฐฐ์—ด ๊ธธ์ด -3, ๊ทธ ๋‹ค์Œ gap=1์ธ ๊ฒฝ์šฐ์—๋Š” ์ด๋ฏธ ์ •๋ ฌ์ด ๋งŽ์ด ๋œ ๋ฐฐ์—ด ๊ธธ์ด๋ฅผ ๋Œ๊ธฐ ๋•Œ๋ฌธ์— ํšจ์œจ์ ์ด๋‹ค.

๐Ÿ‘‡ ์ „์ฒด ์ฝ”๋“œ

๋”๋ณด๊ธฐ
class CustomArray{
	constructor(numElements){
    	this.dataStore = [];
        this.pos = 0;
        this.numElements = numElements;
        
        for (let i = 0; i < numElements; ++i) { 
        	this.dataStore[i] = i;
		}
        
        /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////
        this.gaps = [5,3,1];
        /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////
    }
    
    // ์‚ฝ์ž…
    insert(element){
    	this.dataStore[this.pos++] = element;
    }

	// ์ถœ๋ ฅ ํฌ๋ฉงํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ํ—ฌํผํ•จ์ˆ˜
    toString(){
    	let retstr = "";
		for (let i = 0; i < this.dataStore.length; ++i) {
			retstr += this.dataStore[i] + " "; 
            if (i > 0 && i % 10 == 0) {
         		retstr += "\n";
      		}
		}
		return retstr;
    }
    
    // ์ œ๊ฑฐ
    clear(){
    	for (let i = 0; i < this.dataStore.length; ++i) {
			this.dataStore[i] = 0; 
        }
    }
    
    // ๋žœ๋ค ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    setData(){
    	for (let i = 0; i < this.numElements; ++i) {
			this.dataStore[i] = Math.floor(Math.random() * (this.numElements+1));
		}
    }
    
    // index1 ๊ณผ index2์˜ ์ž๋ฆฌ ๋ณ€๊ฒฝ
    swap(arr, index1, index2){
    	let temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }
    
  
    bubbleSort(){
    	let numElements = this.dataStore.length;
        let temp;
        for(let outer = numElements; outer >=2; --outer){
        	for(let inner = 0; inner <= outer-1; ++inner){
            	if(this.dataStore[inner] > this.dataStore[inner+1]){
                	this.swap(this.dataStore, inner, inner+1);
                }
            }
        }
    
    }
    
    
    selectionSort(){
        let min,temp;
        for(let outer = 0; outer <= this.dataStore.length-2; ++outer){
            min = outer;
            for(let inner = outer+1; inner <= this.dataStore.length-1; ++inner){
                if(this.dataStore[inner] < this.dataStore[min]){
                   min = inner;
                }
                this.swap(this.dataStore, outer, min);
            }
        }
	}
     
    insertionSort(){
        let temp, inner;
        for(let outer = 1; outer <= this.dataStore.length-1; ++outer){
            temp = this.dataStore[outer];
            inner = outer;
            while(inner > 0 && (this.dataStore[inner-1] >= temp)){
                this.dataStore[inner] = this.dataStore[inner-1];
                --inner;
            }
            this.dataStore[inner] = temp;
        }
	}
    /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////
    setGaps(arr){
    	this.gaps = arr;
    }
    
      shellSort() {
        for (let g = 0; g < this.gaps.length; ++g) {
          for (let i = this.gaps[g]; i < this.dataStore.length; ++i) {
            let temp = this.dataStore[i];
            let j;
            for (j = i; j >= this.gaps[g] && this.dataStore[j - this.gaps[g]] > temp; j -= this.gaps[g]) {
              this.dataStore[j] = this.dataStore[j - this.gaps[g]];
            }
            this.dataStore[j] = temp;
          }
        }
      }
    /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////

}

const print = item => console.log(item);

let numElements = 10;
let myNums = new CustomArray(numElements);
myNums.setData();
print(myNums.toString());
myNums.shellSort();
print('์ •๋ ฌ ํ›„:');
print(myNums.toString());

/**
9 6 9 10 7 5 9 7 3 7 
์ •๋ ฌ ํ›„:
3 5 6 7 7 7 9 9 9 10 
*/

 

๋…ผ๋ฌธ์—์„œ ์ •ํ•œ gap ์‹œํ€€์Šค๊ฐ€ ์ •๋ง ์ œ์ผ ํšจ์œจ์ ์ผ๊นŒ? 

์ด๋ฒˆ์—๋Š” gap ์‹œํ€€์Šค๋ฅผ ๋™์ ์œผ๋กœ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด์ž.

๋‹ค์Œ์€ Robert Sedgewick์ด ๊ตฌ์•ˆํ•œ gap ์‹œํ€€์Šค๋ฅผ ๋™์ ์œผ๋กœ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋‹ค.

let N = this.dataStore.length; 
let h = 1;
while (h < N/3) {
	h = 3 * h + 1; 
    }

 

gap ๊ฐ’์ด ๊ฒฐ์ •๋˜๋ฉด ์ด์ „ ๋ฐฉ์‹๋Œ€๋กœ shellsort์ด ๋™์ž‘ํ•œ๋‹ค. ๋‹ค๋งŒ ์ฐจ์ด์ ์€ ์™ธ๋ถ€ for ๋ฃจํ”„๋กœ ๋Œ์•„๊ฐ€๊ธฐ ์ „์— ์ƒˆ๋กœ์šด gap ๊ฐ’์„ ๊ณ„์‚ฐํ•œ๋‹ค. 

 dynamicGapShellSort(){
 	let N = this.dataStore.length; 
    let h = 1;
    while (h < N/3) {
        h = 3 * h + 1; 
    }
    while(h >= 1){
    	for(let i = h; i < N; i++){
        	for(let j =i; j>=h && this.dataStore[j] < this.dataStore[j-h]; j -= h){
            	this.swap(this.dataStore,j,j-h);
            }
        }
        h = (h-1)/3;
    }
  	
}

๐Ÿ‘‡ ์ „์ฒด ์ฝ”๋“œ

๋”๋ณด๊ธฐ
class CustomArray{
	constructor(numElements){
    	this.dataStore = [];
        this.pos = 0;
        this.numElements = numElements;
        
        for (let i = 0; i < numElements; ++i) { 
        	this.dataStore[i] = i;
		}
        
        this.gaps = [5,3,1];
    }
    
    // ์‚ฝ์ž…
    insert(element){
    	this.dataStore[this.pos++] = element;
    }

	// ์ถœ๋ ฅ ํฌ๋ฉงํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ํ—ฌํผํ•จ์ˆ˜
    toString(){
    	let retstr = "";
		for (let i = 0; i < this.dataStore.length; ++i) {
			retstr += this.dataStore[i] + " "; 
            if (i > 0 && i % 10 == 0) {
         		retstr += "\n";
      		}
		}
		return retstr;
    }
    
    // ์ œ๊ฑฐ
    clear(){
    	for (let i = 0; i < this.dataStore.length; ++i) {
			this.dataStore[i] = 0; 
        }
    }
    
    // ๋žœ๋ค ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    setData(){
    	for (let i = 0; i < this.numElements; ++i) {
			this.dataStore[i] = Math.floor(Math.random() * (this.numElements+1));
		}
    }
    
    // index1 ๊ณผ index2์˜ ์ž๋ฆฌ ๋ณ€๊ฒฝ
    swap(arr, index1, index2){
    	let temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }
    
  
    bubbleSort(){
    	let numElements = this.dataStore.length;
        let temp;
        for(let outer = numElements; outer >=2; --outer){
        	for(let inner = 0; inner <= outer-1; ++inner){
            	if(this.dataStore[inner] > this.dataStore[inner+1]){
                	this.swap(this.dataStore, inner, inner+1);
                }
            }
        }
    
    }
    
    
    selectionSort(){
        let min,temp;
        for(let outer = 0; outer <= this.dataStore.length-2; ++outer){
            min = outer;
            for(let inner = outer+1; inner <= this.dataStore.length-1; ++inner){
                if(this.dataStore[inner] < this.dataStore[min]){
                   min = inner;
                }
                this.swap(this.dataStore, outer, min);
            }
        }
	}
     
    insertionSort(){
        let temp, inner;
        for(let outer = 1; outer <= this.dataStore.length-1; ++outer){
            temp = this.dataStore[outer];
            inner = outer;
            while(inner > 0 && (this.dataStore[inner-1] >= temp)){
                this.dataStore[inner] = this.dataStore[inner-1];
                --inner;
            }
            this.dataStore[inner] = temp;
        }
	}
    
    setGaps(arr){
    	this.gaps = arr;
    }
    
      shellSort() {
        for (let g = 0; g < this.gaps.length; ++g) {
          for (let i = this.gaps[g]; i < this.dataStore.length; ++i) {
            let temp = this.dataStore[i];
            let j;
            for (j = i; j >= this.gaps[g] && this.dataStore[j - this.gaps[g]] > temp; j -= this.gaps[g]) {
              this.dataStore[j] = this.dataStore[j - this.gaps[g]];
            }
            this.dataStore[j] = temp;
          }
        }
      }
    /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////
    
    dynamicGapShellSort(){
        let N = this.dataStore.length; 
        let h = 1;
        while (h < N/3) {
            h = 3 * h + 1; 
        }
        while(h >= 1){
            for(let i = h; i < N; i++){
                for(let j =i; j>=h && this.dataStore[j] < this.dataStore[j-h]; j -= h){
                    this.swap(this.dataStore,j,j-h);
                }
            }
			 h = (h-1)/3;
        }
       
    }
    /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////

}

const print = item => console.log(item);

let numElements = 10;
let myNums = new CustomArray(numElements);
myNums.setData();
print(myNums.toString());
myNums.dynamicGapShellSort();
print('์ •๋ ฌ ํ›„:');
print(myNums.toString());

/**
0 3 6 4 4 1 7 3 7 5
์ •๋ ฌ ํ›„:
0 1 3 3 4 4 5 6 7 7 
*/

๋‹ค์Œ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜๋“ค์„ ์‚ดํŽด๋ณด๊ธฐ ์ „์— ๋‘ gap ์‹œํ€€์Šค์˜ ๋Ÿฌ๋‹ ํƒ€์ž„์„ ๋น„๊ตํ•ด ๋ณด์ž! 

๋‘ ๋ฐฉ๋ฒ• ๋ชจ๋‘ ํšจ์œจ์„ฑ์€ ๊ฐ™๋‹ค๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

let nums = new CustomArray(10000);
nums.setData();
let start = new Date().getTime();
nums.shellSort();
let stop = new Date().getTime();
let elapsed = stop - start;
print("Shellsort with hard-coded gap sequence: " + elapsed + " ms."); 
nums.clear();
nums.setData();
start = new Date().getTime();
nums.dynamicGapShellSort();
stop = new Date().getTime();
print("Shellsort with dynamic gap sequence: " + elapsed + " ms.");

/**
Shellsort with hard-coded gap sequence: 16 ms.
Shellsort with dynamic gap sequence: 16 ms.

Shellsort with hard-coded gap sequence: 27 ms.
Shellsort with dynamic gap sequence: 27 ms.

Shellsort with hard-coded gap sequence: 29 ms.
Shellsort with dynamic gap sequence: 29 ms.
*/

 

The  Mergesort Algorithm

Mergesort์€ ์ •๋ ฌ๋œ sublist๋“ค์„ merge(ํ•ฉ๋ณ‘)ํ•ด์„œ ์™„์ „ํ•˜๊ฒŒ ์ •๋ ฌ๋œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ ๋‹ค๋Š” ์ ์—์„œ ์ด๋ฆ„์„ ์ง€์—ˆ๋‹ค.

์ด๋ก ์ ์œผ๋กœ mergesort ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๊ตฌํ˜„ํ•˜๊ธฐ ์–ด๋ ต์ง€ ์•Š๋‹ค.

๋‘ ๊ฐœ์˜ ์ •๋ ฌ๋œ ๋ถ€๋ถ„ ๋ฐฐ์—ด๋“ค๊ณผ ์ด ๋‘ ๋ฐฐ์—ด๋“ค์˜ ๋Œ€์†Œ ๊ด€๊ณ„๋ฅผ ๋น„๊ตํ•˜์—ฌ ํ•ฉ์นœ ์„ธ ๋ฒˆ์งธ ๋ฐฐ์—ด๋งŒ ์žˆ์œผ๋ฉด ๋œ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•  ๋•Œ๋Š” ์—„์ฒญ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋“ค์„ ๋‹ค๋ค„์•ผ ํ•œ๋‹ค๋ฉด ๋‘ ๋ฐฐ์—ด๋“ค์„ ํ•ฉ์น  ๊ณต๊ฐ„์„ ๋งˆ๋ จํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ์ธก๋ฉด์—์„œ๋Š” ํšจ์œจ์„ฑ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

Top-down vs. Bottom-up Mergesort

ํ•„์ˆ˜ ์š”์†Œ๋Š” ์•„๋‹ˆ์ง€๋งŒ mergesort๋ฅผ ์žฌ๊ท€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ™œ์šฉํ•ด  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ CustomArray์˜ ๋ฉ”์„œ๋“œ๋กœ ์ถ”๊ฐ€ํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์žฌ๊ท€ ๊นŠ์ด๊ฐ€ ๋„ˆ๋ฌด ๊นŠ์–ด์ ธ์„œ ํ™œ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

// Function to merge two halves into a sorted array
function merge(left, right) {
    let result = [];
    let leftIndex = 0;
    let rightIndex = 0;

    // Concatenate values into the result array in sorted order
    while (leftIndex < left.length && rightIndex < right.length) {
        if (left[leftIndex] < right[rightIndex]) {
            result.push(left[leftIndex]);
            leftIndex++;
        } else {
            result.push(right[rightIndex]);
            rightIndex++;
        }
    }

    // Concatenate remaining elements of left and right arrays
    return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
}

// Function to perform top-down merge sort
function mergeSort(array) {
    // Base case: single element or empty array
    if (array.length <= 1) {
        return array;
    }

    // Find the middle index to split the array into two halves
    const middleIndex = Math.floor(array.length / 2);

    // Recursively sort the left and right halves
    const left = mergeSort(array.slice(0, middleIndex));
    const right = mergeSort(array.slice(middleIndex));

    // Merge the sorted halves
    return merge(left, right);
}

// Example usage
const array = [38, 27, 43, 3, 9, 82, 10];
console.log('Unsorted array:', array);
const sortedArray = mergeSort(array);
console.log('Sorted array:', sortedArray);
/**
	Unsorted array: (7) [38, 27, 43, 3, 9, 82, 10]
	Sorted array: (7) [3, 9, 10, 27, 38, 43, 82]
*/

 

ํ•˜์ง€๋งŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ๋Š” ์žฌ๊ท€ ๊นŠ์ด๊ฐ€ ๋„ˆ๋ฌด ๊นŠ์–ด์„œ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

 

๋Œ€์‹  ๋น„์žฌ๊ท€์ ์ธ bottom-up Mergesort๋ฅผ  ์‚ดํŽด๋ณด์ž.

 

 

๋จผ์ € ์ •๋ ฌํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ํ•˜๋‚˜์˜ ๋ฐฐ์—ด๋กœ ์ชผ๊ฐœ์ค€๋‹ค.

๊ทธ ๋’ค ๊ฐ ๋ฐฐ์—ด ์š”์†Œ๋“ค๋ณด๋‹ค ๋ฌด์กฐ๊ฑด ํฐ ๊ฐ’์ธ infinity๋ฅผ ์˜ค๋ฅธ์ชฝ์— ๋ฐฐ์น˜ํ•˜์—ฌ ์ง์„ ์ง€์–ด์ฃผ๊ณ  ๊ฐ ์ง์„ ์™ผ์ชฝ๊ณผ ์˜ค๋ฅธ์ชฝ ๋ถ€๋ถ„ ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•œ๋‹ค. ์ด๋•Œ infinity(๋ฌดํ•œ)์€ sentinel(๊ฐ์‹œ์ž) ๊ฐ’์œผ๋กœ ์“ฐ์ธ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ, ์™ผ์ชฝ์ด๋‚˜ ์˜ค๋ฅธ์ชฝ ๋ถ€๋ถ„ ๋ฐฐ์—ด์˜ ๋์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด ์“ฐ์ธ๋‹ค. ์™ผ์ชฝ๊ณผ ์˜ค๋ฅธ์ชฝ ๋ถ€๋ถ„ ๋ฐฐ์—ด๋“ค์„ ๊ฐ€์ ธ์™€ ์ •๋ ฌํ•ด ์ค€ ์ƒˆ๋กœ์šด ๋ฐฐ์—ด๋กœ ํ•ฉ์ณ์ฃผ๊ณ , ๋งˆ์ง€๋ง‰์— ๋ถ€๋ถ„ ๋ฐฐ์—ด๋“ค์ด ๋” ์ด์ƒ ์—†์„ ๋•Œ๊นŒ์ง€ ๋ฐ˜๋ณตํ•ด ์ฃผ๋ฉด ์ •๋ ฌ์ด ์™„์„ฑ๋œ๋‹ค.

๊ตฌํ˜„

mergeSort(){
    if(this.dataStore.length <2){
        return;
    }
    let step = 1;
    let left,right;
    while(step < this.dataStore.length){
        left = 0;
        right = step;
        while(right+step <= this.dataStore.length){
            this.mergeArrays(this.dataStore, left, left+step, right, right+step);
            left = right + step;
            right = left + step;
    	}
        if(right < this.dataStore.length){
        	this.mergeArrays(this.dataStore, left, left+step, right, this.dataStore.length);
    	}
    	step *= 2;
    	}
}

mergeArrays(arr,startLeft, stopLeft, startRight, stopRight){
	let rightArr = new Array(stopRight - startRight + 1);
    let leftArr = new Array(stopLeft - startLeft + 1);
    let k = startRight;
    for(let i = 0; i< (rightArr.length-1); ++i){
    	rightArr[i] = arr[k];
        ++k;
    }
    k = startLeft;
    for(let i=0; i< (leftArr.length-1);++i){
    	leftArr[i] = arr[k];
		++k;
    }
	rightArr[rightArr.length-1] = Infinity;
    leftArr[leftArr.length-1] = Infinity;
	let m = 0;
    let n = 0;
    for (let k = startLeft; k < stopRight; ++k) { 
    	if (leftArr[m] <= rightArr[n]) {
             arr[k] = leftArr[m];
    		m++; 
        } else {
    		arr[k] = rightArr[n]; n++;
    	} 
    }
    this.print("left array - ", leftArr);
    this.print("right array - ", rightArr);

}

๐Ÿ‘‡ ์ „์ฒด ์ฝ”๋“œ

๋”๋ณด๊ธฐ
class CustomArray{
	constructor(numElements){
    	this.dataStore = [];
        this.pos = 0;
        this.numElements = numElements;
        
        for (let i = 0; i < numElements; ++i) { 
        	this.dataStore[i] = i;
		}
        
        this.gaps = [5,3,1];
    }

    /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////
    // ์ถœ๋ ฅ ํ—ฌํผ 
    print(label,arr,...others){
        console.log(`${label} [${arr}] ${others}`);
    }
    /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////
    
    // ์‚ฝ์ž…
    insert(element){
    	this.dataStore[this.pos++] = element;
    }

	// ์ถœ๋ ฅ ํฌ๋ฉงํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ํ—ฌํผํ•จ์ˆ˜
    toString(){
    	let retstr = "";
		for (let i = 0; i < this.dataStore.length; ++i) {
			retstr += this.dataStore[i] + " "; 
            if (i > 0 && i % 10 == 0) {
         		retstr += "\n";
      		}
		}
		return retstr;
    }
    
    // ์ œ๊ฑฐ
    clear(){
    	for (let i = 0; i < this.dataStore.length; ++i) {
			this.dataStore[i] = 0; 
        }
    }
    
    // ๋žœ๋ค ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    setData(){
    	for (let i = 0; i < this.numElements; ++i) {
			this.dataStore[i] = Math.floor(Math.random() * (this.numElements+1));
		}
    }
    
    // index1 ๊ณผ index2์˜ ์ž๋ฆฌ ๋ณ€๊ฒฝ
    swap(arr, index1, index2){
    	let temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }
    
  
    bubbleSort(){
    	let numElements = this.dataStore.length;
        let temp;
        for(let outer = numElements; outer >=2; --outer){
        	for(let inner = 0; inner <= outer-1; ++inner){
            	if(this.dataStore[inner] > this.dataStore[inner+1]){
                	this.swap(this.dataStore, inner, inner+1);
                }
            }
        }
    
    }
    
    
    selectionSort(){
        let min,temp;
        for(let outer = 0; outer <= this.dataStore.length-2; ++outer){
            min = outer;
            for(let inner = outer+1; inner <= this.dataStore.length-1; ++inner){
                if(this.dataStore[inner] < this.dataStore[min]){
                   min = inner;
                }
                this.swap(this.dataStore, outer, min);
            }
        }
	}
     
    insertionSort(){
        let temp, inner;
        for(let outer = 1; outer <= this.dataStore.length-1; ++outer){
            temp = this.dataStore[outer];
            inner = outer;
            while(inner > 0 && (this.dataStore[inner-1] >= temp)){
                this.dataStore[inner] = this.dataStore[inner-1];
                --inner;
            }
            this.dataStore[inner] = temp;
        }
	}
    
    setGaps(arr){
    	this.gaps = arr;
    }
    
      shellSort() {
        for (let g = 0; g < this.gaps.length; ++g) {
          for (let i = this.gaps[g]; i < this.dataStore.length; ++i) {
            let temp = this.dataStore[i];
            let j;
            for (j = i; j >= this.gaps[g] && this.dataStore[j - this.gaps[g]] > temp; j -= this.gaps[g]) {
              this.dataStore[j] = this.dataStore[j - this.gaps[g]];
            }
            this.dataStore[j] = temp;
          }
        }
      }
   
    
    dynamicGapShellSort(){
        let N = this.dataStore.length; 
        let h = 1;
        while (h < N/3) {
            h = 3 * h + 1; 
        }
        while(h >= 1){
            for(let i = h; i < N; i++){
                for(let j =i; j>=h && this.dataStore[j] < this.dataStore[j-h]; j -= h){
                    this.swap(this.dataStore,j,j-h);
                }
            }
			 h = (h-1)/3;
        }
       
    }
    /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////
    mergeSort(){
	if(this.dataStore.length <2){
        return;
    }
        let step = 1;
        let left,right;
    	while(step < this.dataStore.length){
        	left = 0;
            right = step;
            while(right+step <= this.dataStore.length){
        	this.mergeArrays(this.dataStore, left, left+step, right, right+step);
            left = right + step;
            right = left + step;
        }
            if(right < this.dataStore.length){
        	this.mergeArrays(this.dataStore, left, left+step, right, this.dataStore.length);
        }
        step *= 2;
        }
       
        
    }


    mergeArrays(arr,startLeft, stopLeft, startRight, stopRight){
        let rightArr = new Array(stopRight - startRight + 1);
        let leftArr = new Array(stopLeft - startLeft + 1);
        let k = startRight;
        for(let i = 0; i< (rightArr.length-1); ++i){
            rightArr[i] = arr[k];
            ++k;
        }
        k = startLeft;
        for(let i=0; i< (leftArr.length-1);++i){
            leftArr[i] = arr[k];
            ++k;
        }
        rightArr[rightArr.length-1] = Infinity;
        leftArr[leftArr.length-1] = Infinity;
        let m = 0;
        let n = 0;
        for (let k = startLeft; k < stopRight; ++k) { 
            if (leftArr[m] <= rightArr[n]) {
                 arr[k] = leftArr[m];
                m++; 
            } else {
                arr[k] = rightArr[n]; n++;
            } 
        }
        this.print("left array - ", leftArr);
        this.print("right array - ", rightArr);

    }
     /////////////////////////// ๐Ÿ’– ์ถ”๊ฐ€ ///////////////////////////

}

const print = item => console.log(item);

let numElements = 10;
let myNums = new CustomArray(numElements);
myNums.setData();
print(myNums.toString());
myNums.mergeSort();
print('์ •๋ ฌ ํ›„:');
print(myNums.toString());

/**
2 8 9 1 3 7 3 9 9 0 
left array -  [2,Infinity] 
right array -  [8,Infinity] 
left array -  [9,Infinity] 
right array -  [1,Infinity] 
left array -  [3,Infinity] 
right array -  [7,Infinity] 
left array -  [3,Infinity] 
right array -  [9,Infinity] 
left array -  [9,Infinity] 
right array -  [0,Infinity] 
left array -  [2,8,Infinity] 
right array -  [1,9,Infinity] 
left array -  [3,7,Infinity] 
right array -  [3,9,Infinity] 
left array -  [1,2,8,9,Infinity] 
right array -  [3,3,7,9,Infinity] 
left array -  [1,2,3,3,7,8,9,9,Infinity] 
right array -  [0,9,Infinity] 
์ •๋ ฌ ํ›„:
0 1 2 3 3 7 8 9 9 9 
*/

The  Quicksort Algorithm

Quicksort์€ ๋งŽ์€ ๋ฐ์ดํ„ฐ์–‘์„ ์ •๋ ฌํ•˜๊ธฐ ์œ„ํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ค‘ ๊ฐ€์žฅ ๋น ๋ฅธ ์ถ•์— ์†ํ•œ๋‹ค.

๋ถ„ํ•  ์ •๋ณต(divide and conquer)์— ์˜ํ•ด ์žฌ๊ท€์ ์œผ๋กœ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋” ์ž‘์€ ๋ถ€๋ถ„ ๋ฆฌ์ŠคํŠธ๋กœ ์ชผ๊ฐœ์ค€๋‹ค.

๋ฆฌ์ŠคํŠธ๋ฅผ pivot์„ ๊ธฐ์ค€์œผ๋กœ ๋ถ€๋ถ„ ๋ฆฌ์ŠคํŠธ๋กœ ์ชผ๊ฐœ์ฃผ๋Š”๋ฐ, pivot๋ณด๋‹ค ์ž‘์€ ๊ฐ’๋“ค์€ ๋ฆฌ์ŠคํŠธ์˜ ์™ผ์ชฝ(์•„๋ž˜)์—, ํฐ ๊ฐ’๋“ค์€ ์˜ค๋ฅธ์ชฝ(์œ„)์— ๋ฐฐ์น˜ํ•œ๋‹ค.

 

๋‹ค์Œ ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด์ž. pivot์€ ๊ฐ ๋ฆฌ์ŠคํŠธ์˜ ๋งˆ์ง€๋ง‰ ์š”์†Œ๋กœ ์ง€์ •ํ•˜์ž.

์ •๋ ฌ ์ „: [10, 7, 8, 9, 1, 5]
Partitioning: pivot=5, array= [1, 5, 8, 9, 10, 7]
Partitioning: pivot=7, array= [1, 5, 7, 9, 10, 8]
Partitioning: pivot=8, array= [1, 5, 7, 8, 10, 9]
Partitioning: pivot=9, array= [1, 5, 7, 8, 9, 10]
์ •๋ ฌ ํ›„: [1, 5, 7, 8, 9, 10]

 

์•Œ๊ณ ๋ฆฌ์ฆ˜ ํ๋ฆ„์„ ์‚ดํŽด๋ณด๋ฉด,

  1. ๋ฐฐ์—ด์„ ๋‘ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆŒ pivot ์š”์†Œ๋ฅผ ๊ณ ๋ฅธ๋‹ค.
  2. pivot๋ณด๋‹ค ์ž‘์€ ๊ฐ’๋“ค์€ ์ „์—, ํฐ ๊ฐ’๋“ค์€ ๋’ค์— ์˜ค๋„๋ก ์ •๋ ฌํ•œ๋‹ค.
  3. ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌ๋  ๋•Œ๊นŒ์ง€ 1๊ณผ 2๋ฅผ ๋ฐ˜๋ณตํ•œ๋‹ค.

๊ตฌํ˜„

์žฌ๊ท€์ ์œผ๋กœ quickSort๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด์ „ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹Œ ์ง์ ‘ ๋žœ๋ค ์ˆซ์ž๋กœ ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด ์“ฐ์ž. 

function quickSort(arr){
	if(arr.length===0){
    	return [];
    }
    let left = [];
    let right = [];
    let pivot = arr[0]; // ๊ตฌํ˜„์€ ๋ฐฐ์—ด์˜ ์ฒซ ์š”์†Œ๋ฅผ pivot์œผ๋กœ ์ง€์ •
    for(let i = 1; i< arr.length; i++){
    	if(arr[i] < pivot){
        	left.push(arr[i]);
        } else {
        	right.push(arr[i]);
        }
    }
    return quickSort(left).concat(pivot, quickSort(right));
}
const print = item => console.log(item);
let arr = Array.from({length:10},()=>Math.floor((Math.random()*100)+1));

print(arr); // [72, 50, 36, 55, 25, 75, 90, 45, 39, 46]
print();
print(quickSort(arr)); // [25, 36, 39, 45, 46, 50, 55, 72, 75, 90]

 

quickSort๋Š” ๋ฐ์ดํ„ฐ ์ˆ˜๊ฐ€ ๋งŽ์„ ๋•Œ ์“ฐ๋ฉด ์ข‹๋‹ค. ๋ฐ์ดํ„ฐ ์ˆ˜๊ฐ€ ์ ์„์ˆ˜๋ก ์„ฑ๋Šฅ์ด ๋–จ์–ด์ง„๋‹ค.

์ด๋ฒˆ์—๋Š” pivot ์„ ํƒ์— ๋”ฐ๋ผ ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๊ฐ€ pivot ์ค‘์‹ฌ์œผ๋กœ ์ •๋ ฌ๋˜๋Š”์ง€ ์‚ดํŽด๋ณด์ž.

const print = item => console.log(item);

function quickSort(arr){
	if(arr.length===0){
    	return [];
    }
    let left = [];
    let right = [];
    let pivot = arr[0]; 
    for(let i = 1; i< arr.length; i++){
		print(`pivot: ${pivot}, current element: ${arr[i]}`); // ๐Ÿ’– ๋กœ๊ทธ ์ถ”๊ฐ€
    	if(arr[i] < pivot){
			print(`moving ${arr[i]} to the left`); // ๐Ÿ’– ๋กœ๊ทธ ์ถ”๊ฐ€
        	left.push(arr[i]);
        } else {
			print(`moving ${arr[i]} to the right`); // ๐Ÿ’– ๋กœ๊ทธ ์ถ”๊ฐ€
        	right.push(arr[i]);    
        }
    }
    return quickSort(left).concat(pivot, quickSort(right));
}

let arr = Array.from({length:10},()=>Math.floor((Math.random()*100)+1));

print(arr);
print();
print(quickSort(arr)); 

/**
[65, 70, 12, 6, 35, 40, 15, 21, 83, 44]
pivot: 65, current element: 70
moving 70 to the right
pivot: 65, current element: 12
moving 12 to the left
pivot: 65, current element: 6
moving 6 to the left
pivot: 65, current element: 35
moving 35 to the left
pivot: 65, current element: 40
moving 40 to the left
pivot: 65, current element: 15
moving 15 to the left
pivot: 65, current element: 21
moving 21 to the left
pivot: 65, current element: 83
moving 83 to the right
pivot: 65, current element: 44
moving 44 to the left
pivot: 12, current element: 6
moving 6 to the left
pivot: 12, current element: 35
moving 35 to the right
pivot: 12, current element: 40
moving 40 to the right
pivot: 12, current element: 15
moving 15 to the right
pivot: 12, current element: 21
moving 21 to the right
pivot: 12, current element: 44
moving 44 to the right
pivot: 35, current element: 40
moving 40 to the right
pivot: 35, current element: 15
moving 15 to the left
pivot: 35, current element: 21
moving 21 to the left
pivot: 35, current element: 44
moving 44 to the right
pivot: 15, current element: 21
moving 21 to the right
pivot: 40, current element: 44
moving 44 to the right
pivot: 70, current element: 83
moving 83 to the right
[6, 12, 15, 21, 35, 40, 44, 65, 70, 83]
*/

๐Ÿ€ ๋งˆ์น˜๋ฉด์„œ

?? 170์ชฝ์—์„œ Heapsort์— ๋Œ€ํ•ด์„œ๋„ ์•Œ์•„๋ณธ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ ํ•ด๋‹น ๋‚ด์šฉ์ด ๋ˆ„๋ฝ๋˜์–ด ์žˆ์–ด์„œ ๋‹นํ™ฉ..
๐Ÿค” ๊ฐœ์ธ์ ์œผ๋กœ heapsort ๊ณต๋ถ€ ํ›„ ๋‚ด์šฉ ์ •๋ฆฌํ•˜์—ฌ ์ถ”๊ฐ€ํ•ด ๋ด์•ผ๊ฒ ๋‹ค.

๐Ÿ“š Reference

- Data Structures and Algorithms Using Javaโ€ Script by Michael McMillian (O’Reilly). Copyright 2014 Michael McMillan, 978-1-449-36493-9