算法(Algorithm): 是对特定问题求解步骤的一种描述,它是指令的有序序列
,其中每一条指令
表示一个或者多个操做
。算法
执行有穷步骤后结束,有穷时间内完成数组
每一条指令有确切含义,任何条件下只有惟一的执行路径,相同的输入相同的输出markdown
一个算法是能行的, 描述的步骤基于已实现的基本运算
执行有限次
实现ide
有0个或者多个输入,刻画运算的对象的初始状况函数
有一个个或者多个输出, 这些输出和输入存在特定关系测试
效率指的是算法执行时间
,存储量指的是算法执行过程当中须要的最大存储空间
.经过设计好的测试程序和数据
,利用计算机计时器对不一样算法编制的程序
的运行时间进行比较,从而肯定算法效率的高低.优化
缺陷:ui
因而可知,抛开这些与计算机硬件,软件有关的因素,一个程序的运行时间依赖于算法的好坏和问题的输入规模.(所谓的问题输入规模是指输入量的多少(一般用整数量n 表示))spa
一个算法是有控制结构(顺序,分支,循坏)和原操做(固定数据类型的操做),算法时间取决于二者的综合效果。设计
对所研究问题(算法类型)基本操做的原操做以及对基本操做重复执行的次数度量。算法的基本操做的重复次数是问题规模
的某个函数f(n)
. 算法时间量度记作
T(n) = O(f(n));
随着问题规模的n 的增大,算法执行时间的增加率和f(n)的增加率相同。
T(n) = O(f(n));
某些控制语句的重复的执行的次数,(for, while, do while)。
问题的基本的原操做重复执行次数和算法的执行时间成正比。
经常使用的时间复杂度所耗费的时间从小到大依次是:
O(1) < O(logn) < (n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!);
对全部可能的输入数据集合的指望值
分析最坏状况估算执行时间的上线
...
for(i =2; i <=n; i++){
for(j =2; j <=i-1; j++){
++x;
a[i][j] = x;
}
}
语句频度:(n - 1)(n-2)/2
时间平均复杂度T arg(n) = O(n^2);
最坏时间复杂度T(n) = O(n^2);
复制代码
S(n) = O(f(n));
n 问题的规模 f(n)为语句关于n所占存储空间的函数。 一个上机程序除了需哟啊存储空间寄存自己所用的指令、常数、变量和输入数据外,对数据进行操做的工做单元和存储一些为实现计算所须要的信息和辅助空间
.
经过特定的算法因式
将一组或多组数据
按照既定模式
进行从新排序
。
当两个相同的元素
同时出现于某个序列之中,则通过必定的排序算法以后,二者在排序先后的相对位置不发生变化
.
注:稳定性是一个特别重要的评估标准。稳定的算法在排序的过程当中不会改变元素彼此的位置的相对次序,反之不稳定的排序算法常常会改变这个次序,这是咱们不肯意看到的
非线性时间比较类排序:比较,由时间复杂度不能突破(nlogn)
交换排序(冒泡、快速)、插入排序(直接插入、希尔排序)、选择排序(简单选择排序,堆排序), 归并排序(二路归并,多路归并)
线性时间非比较类排序:不比较,突破基于比较排序的时间下界,以线性时间运行 基数排序、桶排序、计数。
内部排序:指的是待排序记录存放在计算机随机存储器进行排序
外部排序:待排序记录数量的数量很大,一次不能够存放所有记录,需对外存访问
经过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另外一部分的关键字小,则可分别对这两部分记录继续进行递归排序,以达到整个序列有序。 这个关键字是从数列中挑出一个元素,也称为 “基准”(pivot).
实现步骤:
target
(通常选择第一个数)target
小的元素移动到数组左边,比target
大的元素移动到数组右边target
左侧和右侧的元素进行快速排序时间复杂度:平均O(nlogn)
,最坏O(n2)
,实际上大多数状况下小于O(nlogn)
空间复杂度:O(logn)
(递归调用消耗)
不稳定
记录一个索引l
从数组最左侧开始,记录一个索引r
从数组右侧开始
在l<r
的条件下,找到右侧小于target
的值array[r]
,并将其赋值到array[l]
在l<r
的条件下,找到左侧大于target
的值array[l]
,并将其赋值到array[r]
这样让l=r
时,左侧的值所有小于target
,右侧的值所有小于target
const quickSort = function(array, start , end) {
if(end - start < 1) return;
let l = start;
let r = end;
const target = array[l];
while(l < r) {
while(l < r && array[r] >= target) {
r--
};
array[l] = array[r];
while(l < r && array[l] < target) {
l++
};
array[r] = array[l];
}
array[l] = target
quickSort(array, start, l - 1);
quickSort(array, l+1, end);
}
const arr = [4, 1, 5, 7, 3, 9];
quickSort(arr, 0, 5);
复制代码
单独开辟两个存储空间left
和right
来存储每次递归比target
小和大的序列
每次递归直接返回left、target、right
拼接后的数组
浪费大量存储空间,写法简单
function quickSort(array) {
if(array.length < 2) {
return array;
}
const target = array[0];
const left = [];
const right = [];
for(let i = 1; i < array.length; i++) {
if(array[i] < target) {
left.push(array[i])
} else {
right.push(array[i]);
}
}
return quickSort(left).concat([target], quickSort(right));
}
const arr = [4, 1, 5, 7, 3, 9];
quickSort(arr, 0, 5);
复制代码
将左侧序列当作一个有序序列,每次将一个数字插入该有序序列。
插入时,从有序序列最右侧开始比较,若比较的数较大,后移一位。
时间复杂度:O(n2)
空间复杂度:O(1)
稳定
function insertSort(array) {
for(let i = 1, len = array.length; i < len; i++) {
let target = i;
for(let j = i - 1; j >=0; j--) {
if(array[target] < array[j]) {
[array[target], array[j]] = [array[j], array[target]];
target = j;
}else break;
}
}
}
const arr = [4, 1, 5, 7, 3, 9];
insertSort(arr, 0, 5);
复制代码
建立一个大顶堆,大顶堆的堆顶必定是最大的元素。
交换第一个元素和最后一个元素,让剩余的元素继续调整为大顶堆。
从后往前以此和第一个元素交换并从新构建,排序完成。
时间复杂度:O(nlogn)
空间复杂度:O(1)
不稳定
function heapSort(array) {
creatHeap(array);
for(let i = array.length - 1; i > 0; i--) {
[array[i], array[0]] = [array[0], array[i]];
adjust(array, 0, i);
}
return array;
}
function creatHeap(array) {
const len = array.length;
const start = parseInt(len/2) - 1;
for(let i = start; i >=0; i--) {
adjust(array, i, len);
}
}
// 将第target个元素进行下沉,孩子节点有比他大的就下沉
function adjust(array, target, len) {
for(let i = 2 * target + 1; i< len; i = 2 * i + 1) {
// 找到孩子节点中最大的
if(i + 1 < len && array[i + 1] > array[i]) {
i = i + 1;
}
// 下沉
if(array[i] > array[target]){
[array[i], array[target]] = [array[target], array[i]]
}else {
break;}
}
}
const arr = [4, 1, 5, 7, 3, 9];
heapSort(arr);
复制代码
利用归并
的思想实现的排序方法。
该算法是采用分治法(Divide and Conquer
)的一个很是典型的应用。(分治法将问题分红一些小的问题而后递归求解,而治的阶段则将分的阶段获得的各答案"修补"在一块儿,即分而治之)。
2
须要合并,那么左右两数组已经有序了。
建立一个临时存储数组temp
,比较两数组第一个元素,将较小的元素加入临时数组
若左右数组有一个为空,那么此时另外一个数组必定大于temp
中的全部元素,直接将其全部元素加入temp
时间复杂度:O(nlogn)
空间复杂度:O(n)
稳定
解法一
分割数组时直接将数组分割为两个数组,合并时直接合并数组。 缺点:空间复杂度略高,须要复制多个数组
function mergeSort(array) {
if(array.length < 2) {
return array;
}
const mid = array.length >> 1;
const front = array.slice(0, mid);
const end = array.slice(mid);
return merge(mergeSort(front), mergeSort(end));
}
function merge(front, end) {
const temp = [];
while(front.length && end.length) {
if(front[0] < end[0]) {
temp.push(front.shift())
} else {
temp.push(end.shift())
}
}
while(front.length) {
temp.push(front.shift());
}
while(end.length) {
temp.push(end.shift())
}
return temp;
}
const arr = [4, 1, 5, 7, 3, 9];
mergeSort(arr);
复制代码
解法2 记录数组的索引
,使用left、right
两个索引来限定当前分割的数组。
优势:空间复杂度低,只需一个temp
存储空间,不须要拷贝数组
function mergeSort(array, left, right, temp) {
if (left < right) {
const mid = Math.floor((left + right) / 2);
mergeSort(array, left, mid, temp)
mergeSort(array, mid + 1, right, temp)
merge(array, left, right, temp);
}
return array;
}
function merge(array, left, right, temp) {
const mid = Math.floor((left + right) / 2);
let leftIndex = left;
let rightIndex = mid + 1;
let tempIndex = 0;
while (leftIndex <= mid && rightIndex <= right) {
if (array[leftIndex] < array[rightIndex]) {
temp[tempIndex++] = array[leftIndex++]
} else {
temp[tempIndex++] = array[rightIndex++]
}
}
while (leftIndex <= mid) {
temp[tempIndex++] = array[leftIndex++]
}
while (rightIndex <= right) {
temp[tempIndex++] = array[rightIndex++]
}
tempIndex = 0;
for (let i = left; i <= right; i++) {
array[i] = temp[tempIndex++];
}
}
const arr = [4, 1, 5, 7, 3, 9];
mergeSort(arr, 0, 5, []);
复制代码
循环数组,比较当前元素和下一个元素,若是当前元素比下一个元素大,向上冒泡。
这样一次循环以后最后一个数就是本数组最大的数。
下一次循环继续上面的操做,不循环已经排序好的数。
优化:当一次循环没有发生冒泡,说明已经排序完成,中止循环。
时间复杂度:O(n2)
空间复杂度:O(1)
稳定
function bubbleSort(array) {
for(let j = 0, len = array.length; j < len; j++) {
let complete = true;
for(let i = 0; i < len - 1 - j; i++) {
if(array[i] > array[i+1]) {
[array[i], array[i+1]] = [array[i+1], array[i]];
complete = false;
}
}
if(complete) {
break;
}
}
return array;
}
const arr = [4, 1, 5, 7, 3, 9];
bubbleSort(arr);
复制代码
每次循环选取一个最小的数字放到前面的有序序列中。
时间复杂度:O(n2)
空间复杂度:O(1)
不稳定
function selectionSort(array) {
for(let i = 0, len = array.length; i < len -1; i++) {
let minIndex = i;
for(let j = i+1; j < len; j++) {
if(array[j] < array[minIndex]) {
minIndex = j
}
}
[array[minIndex], array[i]] = [array[i], array[minIndex]];
}
}
const arr = [4, 1, 5, 7, 3, 9];
selectionSort(arr);
复制代码