前端算法入门之路(七)(快速排序)

126 阅读1分钟

快速排序

  1. 先从数列中取出一个数作为基准数
  2. 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
  3. 再对左右区间重复第二步,直到各区间只有一个数
// 初始版本快排
function quick_sort_v1(arr, l, r) {
    if (l >= r) return
    // 取基准值在当前区间第一位
    let x = l, y = r, base = arr[l]
    while (x < y) {
        // 先找右边小于基准值的位置,把值放到x的位置上,然后x++
        while (x < y && arr[y] >= base) y--
        if (x < y) arr[x++] = arr[y]
        // 再找左边大于基准值的位置,把值放到y的位置上,然后y--
        while (x < y && arr[x] <= base) x++
        if (x < y) arr[y--] = arr[x]
    }
    // 最后x、y的位置就是基准值的位置
    arr[x] = base
    quick_sort_v1(arr, l, x - 1)
    quick_sort_v1(arr, x + 1, r)
}
// 单边递归法快排
// (说实话没咋搞懂)
function quick_sort_v2(arr, l, r) {
    while (l < r) {
        if (l >= r) return
        let x = l, y = r, base = arr[l]
        while (x < y) {
            while (x < y && arr[y] >= base) y--
            if (x < y) arr[x++] = arr[y]
            while (x < y && arr[x] <= base) x++
            if (x < y) arr[y--] = arr[x]
        }
        arr[y] = base
        quick_sort_v2(arr, x + 1, r)
        r = x - 1
    }
}
// 插入排序优化版快排
// 设置一个区间大小16,大于16使用快速排序,小于16的区间使用插入排序
const threshold = 16
// 从三个数中取第二大的数
function median(a, b, c) {
    if (a > b) [a, b] = [b, a]
    if (a > c) [a, c] = [c, a]
    if (b > c) [b, c] = [c, b]
    return b
}
function __quick_sort_v3(arr, l, r) {
    // 只有小于预设的区间(16)才执行
    while (r - l > threshold) {
        // 基准值取当前区间第一个值、最后一个值、中间位置的值三个数第二大的数
        let x = l, y = r, base = median(arr[l], arr[(l + r) / 2], arr[r])
        do {
            // 定位左半部分大于基准值和右半部分小于基准值的下标,交换值
            while (arr[x] < base) { x++ }
            while (arr[y] > base) { y-- }
            if (x <= y) {
                [arr[x], arr[y]] = [arr[y], arr[x]]
                x++
                y--
            }
        } while (x <= y)
        __quick_sort_v3(arr, x, r)
        r = y
    }
}
// 插入排序,进行收尾,适合小区间的排序
function final_insert_sort(arr, l, r) {
    let ind = l
    for (let i = l + 1; i <= r; i++) {
        if (arr[i] < arr[ind]) {
            ind = i
        }
    }
    while (ind > l) {
        [arr[ind], arr[ind - 1]] = [arr[ind - 1], arr[ind]]
        --ind
    }
    for (let i = l + 2; i <= r; i++) {
        let j = i
        while (arr[j] < arr[j - 1]) {
            [arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]
            j--
        }
    }
}
function quick_sort_v3(arr, l, r) {
    __quick_sort_v3(arr, l, r)
    final_insert_sort(arr, l, r)
}

LeetCode肝题

    1. 排序数组
function median(a, b, c) {
    if (a > b) [a, b] = [b, a]
    if (a > c) [a, c] = [c, a]
    if (b > c) [b, c] = [c, b]
    return b
}
function quick_sort_v2(arr, l, r) {
    while (l < r) {
        if (l >= r) return
        let x = l, y = r, base = arr[l]
        while (x < y) {
            while (x < y && arr[y] >= base) y--
            if (x < y) arr[x++] = arr[y]
            while (x < y && arr[x] <= base) x++
            if (x < y) arr[y--] = arr[x]
        }
        arr[y] = base
        quick_sort_v2(arr, x + 1, r)
        r = x - 1
    }
}
var sortArray = function(nums) {
    quick_sort_v2(nums, 0, nums.length-1)
    return nums
};
    1. 排序链表
// 找基准值,定义两个空链表,将小于基准值的节点放ret1上,大于基准值的放ret2上,最后将俩链表连接
var sortList = function(head) {
    if(!head) return head
    let p = head, q, min = p.val, max = p.val, base = 0, ret1 = null, ret2 = null
    while(p) {
        min = Math.min(min, p.val)
        max = Math.max(max, p.val)
        p = p.next
    }
    if (min == max) return head
    base = (max + min) / 2
    p = head
    while(p) {
        q = p.next
        if (p.val <= base) {
            p.next = ret1
            ret1 = p
        } else {
            p.next = ret2
            ret2 = p
        }
        p = q
    }
    ret1 = sortList(ret1)
    ret2 = sortList(ret2)
    p = ret1
    while(p.next) p = p.next
    p.next = ret2
    return ret1
};
  1. 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
// 定义两个指针,i指针指向左侧的奇数,j指针指向右侧的偶数,然后交换
var exchange = function(nums) {
    let i = 0, j = nums.length - 1
    while(i < j) {
        while(nums[i] % 2 == 1 && i < j) i++
        while(nums[j] % 2 == 0 && i < j) j--
        if (i == j) break
        [nums[i], nums[j]] = [nums[j], nums[i]]
        i++
        j--
    }
    return nums
};
    1. 颜色分类
// 设置三个指针,l指向红色0,i指向白色1,r指向蓝色2,基准值为1
var sortColors = function(nums) {
    if (nums.length <= 1) return nums
    let l = -1, r = nums.length,  i = 0, base = 1
    while(i < r) {
        if (nums[i] == base) {
            i++
        } else if (nums[i] < base) {
            l++
            [nums[i], nums[l]] = [nums[l], nums[i]]
            i++
        } else {
            r--
            [nums[i], nums[r]] = [nums[r], nums[i]]
        }
    }
};
  1. 面试题 17.14. 最小K个数
// 快速排序完放k个数字进结果集中(可以优化至左区间剩k个的时候结束)
function median(a, b, c) {
    if (a > b) [a, b] = [b, a]
    if (a > c) [a, c] = [c, a]
    if (b > c) [b, c] = [c, b]
    return b
}
function quick_sort_v2(arr, l, r) {
    while (l < r) {
        if (l >= r) return
        let x = l, y = r, base = arr[l]
        while (x < y) {
            while (x < y && arr[y] >= base) y--
            if (x < y) arr[x++] = arr[y]
            while (x < y && arr[x] <= base) x++
            if (x < y) arr[y--] = arr[x]
        }
        arr[y] = base
        quick_sort_v2(arr, x + 1, r)
        r = x - 1
    }
}
var smallestK = function(arr, k) {
    let ans = []
    if (k == 0) return ans
    quick_sort_v2(arr, 0, arr.length - 1)
    while(k) ans.push(arr[--k])
    return ans
};
    1. 不同的二叉搜索树 II
// 排列组合所有的结果
var dfs = function(l, r) {
    let ans = []
    if (l > r) {
        ans.push(null)
        return ans
    }
    for (let i = l; i <= r; i++) {
        let left_tree = dfs(l, i - 1)
        let right_tree = dfs(i + 1, r)
        for (let leftItem of left_tree){
            for(let rightItem of right_tree) {
                let treeNode = new TreeNode(i, leftItem, rightItem)
                ans.push(treeNode)
            }
        }
    }
    return ans
}
var generateTrees = function(n) {
    if(n == 0) return []
    return dfs(1, n)
};
    1. 字符串解码
// 遍历字符串按照类型拼接
var decodeString = function(s) {
    let numArr = [], strArr = [], num = 0, ans = ''
    for(let item of s) {
        if (item >= '0' && item <= '9') {
            num = num * 10 + parseInt(item)
        } else if (item == '[') {
            strArr.push(ans)
            ans = ''
            numArr.push(num)
            num = 0
        } else if (item == ']') {
            ans = strArr.pop() + ans.repeat(numArr.pop())
        } else {
            ans += item
        }
    }
    return ans
};