常见排序算法
快速排序
具体步骤可分为:
1)从数组中取出一个数作为基准key,例如取i=0的元素,此时0位置相当于挖了一个坑
2)j从数组末尾开始扫描,遇到小于key的元素将其放到i=0处,自己的坑待填补
3)调换顺序,i从数组头部开始扫描,遇到大于key的元素将其放到步骤2的坑中
4)重复以上反复查找的过程,直至i=j,将key值填入这个位置,第一趟排序完成
5)接着在左右两个子数组中重复上面的排序过程,直到数组长度为1
function kuaisu(arr) {
if (checkArray(arr)) {
quicksort(arr, 0, arr.length-1)
return arr
}
}
function quicksort(arr,left, right) {
var i = left
var j = right
var key = arr[left]
if (left >= right) return
while (i < j) {
while (arr[j] > key && i <j) {
j--
}
if(i < j) {
arr[i++] = arr[j]
}
while (arr[j] < key && i < j) {
i++
}
if (i < j) {
arr[j--] = arr[i]
}
}
arr[i] = key
quicksort(arr, left, i-1)
quicksort(arr, i+1, right)
return arr
}
时间复杂度
最差情况下时间复杂度
最差的情况就是每一次取到的元素就是数组中最小/最大的,这种情况其实就是冒泡排序了(每一次都排好一个元素的顺序)
这种情况时间复杂度就好计算了,就是冒泡排序的时间复杂度:T[n] = n * (n-1) = n^2 + n;
综上所述:快速排序最差的情况下时间复杂度为:O( n^2 )
平均时间复杂度
该算法的复杂度和归并排序是相同的,但是额外空间复杂度比归并排序少。
应用:找出第K大元素
归并排序
递归将数组两两分开直到最多包含两个元素,然后将数组合并排序。
//guibing,O(N * logN)
function guibing(arr) {
if (checkArray(arr)) {
mergesort(arr, 0, arr.length-1)
return arr
}
}
function mergesort(arr, left, right) {
if (left == right) return
var mid = parseInt((left + right) / 2)
mergesort(arr, left, mid)
mergesort(arr, mid+1, right)
help = []
p1 = left
p2 = mid + 1
i=0
while (p1<=mid && p2<=right) {
help[i++] = arr[p1++] > arr[p2++] ? arr[p2++] : arr[p1++]
}
while (p1<=mid) {
help[i++] = arr[p1++]
}
while (p2<=right) {
help[i++] = arr[p2++]
}
for (var j=0; j<help.length; j++){
arr[left + j] = help[j]
}
}
冒泡排序
从第一个元素开始,将当前元素与下一个元素进行比较,若当前元素比较大,那么交换位置,重复操作直至比较到最后一位元素,第一趟排序完成,最后一位元素为最大值。下一轮重复操作比较到倒数第二个元素。
//maopao,o(n^2)
function maopao(arr) {
if (checkArray(arr)) {
for (var i=arr.length()-1; i>0; i--) {
for (var j=0; j<i; j++) {
if (arr[j] > arr[j+1]) swap(arr, j, j+1)
}
}
return arr
}
}
选择排序
设置最小值的索引为0,遍历数组,如果取出的值比当前最小值要小,就替换最小值索引,一趟遍历后将最小值与第一个元素进行交换,此时第一个元素为最小值。下一次遍历从1开始,重复操作。
//xuanze
function xuanze(arr) {
if (checkArray(arr)) {
for (var i=0; i<arr.length; i++) {
var min = i
for (var j=i+1; j<arr.length; j++) {
min = arr[min] > arr[j] ? j : min
}
swap(arr, i, min)
}
return arr
}
}
插入排序
第一个元素默认是已排序的,取出下一个元素与当前元素比较,若当前元素大则交换位置,此时第一个元素为当前最小值。下次取出操作从以三个元素开始,向前比较,重复之前的操作。
//charu
function charu(arr) {
if (checkArray(arr)) {
for (var i=1; i<arr.length; i++) {
for (var j=i-1; j>=0; j--) {
if (arr[j] > arr[j+1]) swap(arr, j, j+1)
}
}
return arr
}
}
//sort
function checkArray(arr) {
return Array.isArray(arr)
}
function swap(arr, left, right) {
var tmp = arr[left]
arr[right] = tmp
arr[left] = arr[right]
}
稳定算法:冒泡排序、插入排序、归并排序、基数排序
不稳定算法 :选择排序、快速排序、希尔排序、堆排序
链表
反向链表思路很简单,使用三个变量分别表示当前节点和当前节点的前后节点。
function reverse(head) {
if (!head || !head.next) return head
var cur = head
var pre = null
var nex = null
while (cur) {
nex = cur.next
cur.next = pre
pre = cur
cur = nex
}
return pre
}
位运算
function sum(a, b) {
if (a==0) return b
if (b==0) return a
var a = a ^ b
var b = (a & b) << 1
return sum(a, b)
}
动态规划
动态规划的本质其实就是两点
- 自底向上分解子问题
- 通过变量存储已经计算过的解
最长递增子序列
最长递增子序列意思是在一组数字中,找出最长一串递增的数字,比如
0, 3, 4, 17, 2, 8, 6, 10
对于以上这串数字来说,最长递增子序列就是 0, 3, 4, 8, 10。找出刚好比当前数字小的数,并且在小的数组成的长度基础上加一。
function lis(n) {
if (n.length === 0) return 0
// 创建一个和参数相同大小的数组,并填充值为 1
let array = new Array(n.length).fill(1)
// 从索引 1 开始遍历,因为数组已经所有都填充为 1 了
for (let i = 1; i < n.length; i++) {
// 从索引 0 遍历到 i
// 判断索引 i 上的值是否大于之前的值
for (let j = 0; j < i; j++) {
if (n[i] > n[j]) {
array[i] = Math.max(array[i], 1 + array[j])
}
}
}
let res = 1
for (let i = 0; i < array.length; i++) {
res = Math.max(res, array[i])
}
return res
}
再如:斐波那契数列
浙公网安备 33010602011771号