1.认识大O表示法
- 在算法的藐视中,我们可以通过类似的快捷方式来描述计算机算法的效率。
- 在计算机中,这种粗略的度量被称作“大O”表示法。
- 在数据项个数发生变化是,算法的效率会跟着发生变化。
- 我们通常使用一种算法的速度会如何跟随数据量的变化的。
符号 | 名称 |
---|---|
O(1) | 常数的 |
O(log(n)) | 对数的 |
O(n) | 线性的 |
O(n^2) | 平方的 |
O(2^n) | 指数的 |
推导大O表示法
①用常量1取代运行时间中所有的加法常量。
②在修改后的运行次数函数中,只保留最高阶项。
③如果最高阶存在且部位1,则去除与这个项相乘的常数。
2.认识排序算法
本博客主要讲解以下排序
简单排序:冒泡排序,选择排序,选择排序
高级排序:快速排序
3.冒泡排序
//冒泡排序
var arrList = [8,6,2,4,5,1,3,10,7,9];
var len = arrList.length;
function swap(m, n){
var temp = arrList[m];
arrList[m] = arrList[n];
arrList[n] = temp;
}
for(var i = len-1; i >=0 ; i--){
for(var j = 0 ; j < i; j++){
if(arrList[j] > arrList[j+1]){
swap(j,j+1)
}
}
}
console.log(arrList);
//(10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
代码解析:
1: 获取数组的长度.
2: 我们现在要写的外层循环, 外层循环应该让i依次减少, 因此我们这里使用了反向的遍历.
3: 内层循环, 内层循环我们使用 j < i. 因为上面的i在不断减小, 这样就可以控制内层循环的次数.
4: 比较两个数据项的大小, 如果前面的大, 那么就进行交换.
图解(引用codewhy老师的图)
冒泡排序的效率:
N个数据我们每次比较的次数依次为N-1.N-2,N-3…3,2,3
所以对于N个数据项为(N-1)+ (N-2) + …1 = N*(N-1)/2
运用大O表示法我们可以知道冒泡排序的大O表示法为O(n^2)
交换次数也为O(n^2)
4.选择排序
//选择排序
var arrList = [8,6,2,4,5,1,3,10,7,9];
var len = arrList.length;
function swap(m, n){
var temp = arrList[m];
arrList[m] = arrList[n];
arrList[n] = temp;
}
for(var i = 0; i < len - 1; i ++){
var min = i;
for(var j = min + 1; j < len; j ++){
if(arrList[min] > arrList[j]){
min = j;
}
}
swap(min,i);
}
console.log(arrList);
//(10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
代码解析:
1: 依然获取数组的长度.
2: 外层循环, 我们已经讲过, 需要从外层循环的第0个位置开始, 依次遍历到length - 2的位置.
3: 先定义一个min, 用于记录最小的位置, 内层循环, 内层循环是从i+1位置开始的数据项, 和i位置的数据项依次比较, 直到length-1的数据项.
4: 如果比较的位置i的数据项, 大于后面某一个数据项, 那么记录最小位置的数据.
5: 将min位置的数据, 那么i位置的数据交换, 那么i位置就是正确的数据了.
图解(引用codewhy老师的图)
选择排序的效率:
选择排序和冒泡排序的比较次数都是N*(N-1)/2, 也就是O(N²).
选择排序的交换次数只有N-1次, 用大O表示法就是O(N).
4.插入排序
//插入排序
var arrList = [8,6,2,4,5,1,3,10,7,9];
var len = arrList.length;
function swap(m, n){
var temp = arrList[m];
arrList[m] = arrList[n];
arrList[n] = temp;
}
//外层循环从第一个位置获取数据,向前面局部有序进行插入
for(var i = 1; i < len; i++){
//内层循环:获取i位置的元素,和前面的数据依次进行比较
var temp = arrList[i];
//取j记录i的位置
var j = i;
while(arrList[j - 1] > temp && j > 0){
arrList[j] = arrList[j - 1];
j--;
}
//将当前j位置的数据放进temp即可
arrList[j] = temp;
}
console.log(arrList);
//(10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
代码解析
1: 获取数组的长度.
2: 外层循环, 从1位置开始, 因为0位置可以默认看成是有序的了.
3: 记录选出的i位置的元素, 保存在变量temp中. i默认等于j
4.内层循环的判断j - 1位置的元素和temp比较, 并且j > 0.那么就将j-1位置的元素放在j位置.j位置向前移.
5: 将目前选出的j位置放置temp元素.
插入排序的比较效率:最大为N*(N-1)/2 ,平均为N*(N-1)/4
5.快速排序(分而治之的思想)
快速排序思想
1.确定基准数,我们把数组的第一个元素作为基准数。
基准数的作用就是我们一次计算结束后,把小于基准数额元素都放到基准数的左边,大于等于基准数的元素都放到基准数的右边。例如,我们一次计算之后的T是这样的:T = [3,1,2,5,4 ,6 ,9,7,10,8]
2.确定哨兵用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边,让哨兵j指向序列的最右边。
首先哨兵j开始出动。哨兵j一步一步地向左挪动(即j–),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。接下来开始哨兵j继续向左挪动。进行交换
他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换。
第二次交换结束,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。哨兵i继续向右移动,糟啦!此时哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。说明此时“探测”结束。我们将基准数6和3进行交换。交换之后的序列如下:
3.交换基准位到此第一轮“探测”真正结束。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。然后对6两边的数组做递归处理即可。
图示快速排序算法:
代码示例:
/**
题目:快速排序算法
思路:两个哨兵,i,j,j从右边找比基数小的,i从左边找比基数大的,然后交换两个目标元素的位置,直到i=j,然后交换i和基数的位置,递归处理。
**/
function quick_sort(arr, left, right) {
//哨兵i
var i = left;
//哨兵j
var j = right;
//基准值
var key = arr[left];
//如果数组只有一个元素
if (left >= right) {
return;
}
while (i < j) {
//从右边向左找第一个比key小的数,找到或者两个哨兵相碰,跳出循环
while (arr[j] > key && i < j) {
j--;
}
//从左边向右找第一个比key大的数,找到或者两个哨兵相碰,跳出循环,这里的=号保证在本轮循环结束前,key的位置不变,否则的话跳出循环,交换i和left的位置的时候,left位置的上元素有可能不是key
while (arr[i] <= key && i <j) {
i++;
}
/**
代码执行道这里,1、两个哨兵到找到了目标值。2、j哨兵找到了目标值。3、两个哨兵都没找到(key是当前数组最小值)
**/
if (i < j) { //交换两个元素的位置
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
//交换基准位
arr[left] = arr[i] //
arr[i] = key;
//重新递归排序key两边的两个数组
quick_sort(arr, left, i - 1);
quick_sort(arr, i + 1, right);
}
var arr = [6,1,2,5,4,3,9,7,10,8];
console.log(arr);
//[6, 1, 2, 5, 4, 3, 9, 7, 10, 8]
quick_sort(arr, 0, arr.length - 1);
console.log(arr);
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]