交换排序-起泡排序、快速排序算法

交换排序是一类借助交换排序的算法,其主要思想是:在待排序序列中选两个记录,将它们的关键码进行比较,如果反序则交换它们的位置。

一、起泡排序

起泡排序是交换排序中最简单的算法,基本思想:两两比较相邻记录的关键码,如果反序则交换,直到没有反序的记录。代码如下

function bubbleSort(arr){
    var length=arr.length;
    var exchange=length-1,bound,temp;
    while(exchange!=0){
        bound=exchange;
        exchange=0;
        for(var j=0;j<length;j++){
            if(arr[j]>arr[j+1]){
                temp=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=temp;
                exchange=j;
            }
        }
    }
    return arr;
}
bubbleSort([4,3,5,6]);

效率:

时间复杂度:最好:O(n),最坏:O(n^2),平均:O(n^2)。

空间复杂度:O(1)。

稳定性:稳定

备注:冒泡排序,思想和起泡排序一样,但是比较的次数>=起泡排序。代码如下:

/*冒泡排序*/
function sort(arr) {
        for (i = 1; i < arr.length; i++) {
            for (j = 0; j < i; j++) {
                if (arr[j] > arr[i]) {
                    maxTemp = arr[j];
                    arr[j] = arr[i];
                    arr[i] = maxTemp;
                }
            }
        }
        return arr;
 }
console.log(sort([5, 100, 6, 3, -12]));

一、快速排序

起泡排序的比较和移动是在相邻的位置进行的,记录每次只能后移一个位置,因此总的比较和移动次数比较多,在快速排序中,记录的比较和移动是从两端向中间进行,关键码较大的记录一次就能从前面移动到后面,关键码较小的记录一次就能从后面移到前面,记录移动的距离较远,从而减少了总的比较次数和移动次数,快速排序又称为分区交换排序,基本思想:首先选一个轴值(即比较的基准),将待排序序列分成两部分,左侧记录的关键码均小于或等于轴值,右侧记录的关键码均大于或等于轴值,然后分别对这两个部分重复上述过程知道整个序列有序,这是一个递归的过程。代码如下:

/*快速排序*/
function Sort(arr) {    
        if (arr.length <= 1) {
            return arr;
        }
        //取中间数值
        var standardNum = arr.splice(Math.floor(arr.length / 2), 1);
        var leftArr = [];
        var rightArr = [];
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] <= standardNum[0]) {
                leftArr.push(arr[i]);
            } else {
                rightArr.push(arr[i]);
            }
        }
        return Sort(leftArr).concat(standardNum, Sort(rightArr));
 }
Sort([5, 100, 6, 3, -12]); //[-12, 3, 5, 6, 100]

改进版:

function quickSort(arr) {
    let swap = (arr, i, j) => {
        let tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    
    let partition = (arr, first, end) => {
        var i = first;
        var j = end;
        while(i < j) {
            while(i < j && arr[i] <= arr[j]) {
                j--;
            }
            if(i < j) {
                swap(arr, i, j);
            }
            while(i < j && arr[i] <= arr[j]) {
                i++;
            }
            if(i < j) {
                swap(arr, i, j)
                j--;
            }
        }
        return i;
    }

    let sort = (arr, first, end) => {
        if(first < end) {
            mid = partition(arr, first, end);
            sort(arr, first, mid - 1);
            sort(arr, mid + 1, end);
        }
    }
    sort(arr, 0, arr.length - 1);
    return arr;
}
var arr = [23, 13, 49, 6, 31, 19, 28]
quickSort(arr);

 效率:

时间复杂度:最好:O(nlog2n),最坏:O(n^2),平均:O(nlog2n)。

空间复杂度:O(log2n)~O(n)。

稳定性:不稳定

快速排序适用于待排序记录个数很大且原始记录随机排列的情况,平均性能是迄今为止所有排序算法中最好的一种。

三、利用快排的思想找一个无序数组的中位数

如果数组长度是奇数,中位数是排序后的第(n+1)/2个元素;若是偶数,中位数是排序后第n/2个元素。

换个思路:从无序数组中查找第k大的元素。

利用快速排序的partition函数,任意挑一个元素,以该元素key,划分数组为两部分,key左边元素小于等于key,右边元素大于等于key。

在第一次partition后,如果左侧元素个数 < k - 1,则在右侧子序列中递归查找;

如果左侧元素个数 = k-1,则第k大元素即在分点处;

如果左侧元素个数 > k - 1,则递归地在左侧序列中继续查找。代码如下:

function findNum(arr) {
    let swap = (arr, i, j) => {
        let tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    
    let partition = (arr, first, end) => {
        var i = first;
        var j = end;
        while(i < j) {
            while(i < j && arr[i] <= arr[j]) {
                j--;
            }
            if(i < j) {
                swap(arr, i, j);
            }
            while(i < j && arr[i] <= arr[j]) {
                i++;
            }
            if(i < j) {
                swap(arr, i, j)
                j--;
            }
        }
        return i;
    }

    // 第k大的数,如果数组长度奇数,则k = (1+n)/2, 否则 k= n/2
    function findMediate(arr, k, low, high) {
        if(k > high - low + 1) {
            return -1;
        }
        let pos = partition(arr, low, high);
        if(pos - low < k - 1) {
            return findMediate(arr, k - pos - 1, pos + 1, high);
        } else if(pos - low == k - 1) {
            return arr[pos];
        } else {
            return findMediate(arr, k, low, pos - 1);
        }
    }
    
    function main(arr) {
        let res;
        if(arr.length%2 == 1) {
            res = findMediate(arr, (arr.length+1)/2, 0, arr.length-1);
        } else {
            res = findMediate(arr, arr.length/2, 0, arr.length-1);
        }
        return res
    }
    return main(arr);
}

var arr = [23, 13, 49, 6, 31, 19, 28, 30, 50]
findNum(arr); // 28
var arr = [23, 13, 49, 6, 31, 19, 28]
findNum(arr); // 23
var arr = [23, 13, 49, 6, 31, 19, 28, 30]
findNum(arr); // 23
原文地址:https://www.cnblogs.com/Iona/p/4734445.html