常见排序算法

快速排序

具体步骤可分为:

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] = aT[n/b] + f(n)  ;对于递归算法的时间复杂度这里就不展开来说了;
   最优情况下时间复杂度
        快速排序最优的情况就是每一次取到的元素都刚好平分整个数组(很显然我上面的不是);
        此时的时间复杂度公式则为:T[n] = 2T[n/2] + f(n);T[n/2]为平分后的子数组的时间复杂度,f[n] 为平分这个数组时所花的时间;
        快速排序最优的情况下时间复杂度为:O( nlogn )

  最差情况下时间复杂度

        最差的情况就是每一次取到的元素就是数组中最小/最大的,这种情况其实就是冒泡排序了(每一次都排好一个元素的顺序)

     这种情况时间复杂度就好计算了,就是冒泡排序的时间复杂度:T[n] = n * (n-1) = n^2 + n;

     综上所述:快速排序最差的情况下时间复杂度为:O( n^2 )

   平均时间复杂度

       快速排序的平均时间复杂度也是:O(nlogn)
   
  空间复杂度
        其实这个空间复杂度不太好计算,因为有的人使用的是非就地排序,那样就不好计算了(因为有的人用到了辅助数组,所以这就要计算到你的元素个数了);我就分析下就地快速排序的空间复杂度吧;
        首先就地快速排序使用的空间是O(1)的,也就是个常数级;而真正消耗空间的就是递归调用了,因为每次递归就要保持一些数据;
     最优的情况下空间复杂度为:O(logn)  ;每一次都平分数组的情况
     最差的情况下空间复杂度为:O( n )      ;退化为冒泡排序的情况

该算法的复杂度和归并排序是相同的,但是额外空间复杂度比归并排序少。

应用:找出第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)
}

 

动态规划

动态规划的本质其实就是两点

  1. 自底向上分解子问题
  2. 通过变量存储已经计算过的解

最长递增子序列

最长递增子序列意思是在一组数字中,找出最长一串递增的数字,比如

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
}

再如:斐波那契数列

 

posted @ 2020-03-11 15:33  Lorasysu  阅读(117)  评论(0)    收藏  举报