目录
二分法查找
需求...在有序数组中插入新成员后,仍然是一个有序的数组
冒泡排序
url编码
二分法查找
二分法查找
- 条件:适用于有序的数组 ( 有序 )
- 原理
1. 从 ( 有序数组 ) 的中间元素开始搜索,如果正好是目标元素就返回该元素位置,否则下一步
2. 如果目标元素大于或者小于中间元素,则在数组大于 小于中间元素的那一半数组搜索,然后重复第一步
3. 如果某一步数组为空,表示找不到目标元素,返回 -1
- 单词
- binary 一半
- closure 闭包
- recursive 递归
- heap 堆
- stack 栈
- 代码
(1) 非递归算法
const arr = [1, 2, 3, 4, 5, 6]
function binary_search (arr, value) {
let low = 0
let heigh = arr.length
while (low <= heigh) {
const mid = Math.floor((low + heigh) / 2)
// - 1. 注意:这里用 Math.floor 或者 Math.ceil 或者 parseInt 都没有影响
// 因为条件不成立时,都会跳到对应的一半的部分继续寻找
// - 2. parseInt() 当有小数点的时候,会取整数,舍弃掉小数部分
if (low === heigh && arr[mid] !== value) {
return -1 // 当low和height相等,arr[mid]还不等于value,说明value在数组中不存在,返回-1
}
switch (true) {
case arr[mid] === value:
return mid // 结束掉整个函数,即binary_search, 并且返回mid的值
case arr[mid] > value:
heigh = mid - 1 // 数组中间值大于value,则在前一半的数组中查找,其实这里也可以不减去1,直接 height = mid
break
case arr[mid] < value:
low = mid + 1 // 后一半查找
break
default:
return -1 // 没找到
}
}
}
const res = binary_search(arr, 5)
console.log(res)
------
(2) 递归算法1
// 闭包加递归
// 通过闭包改变low和heigh
const arr = [1, 2, 3, 4, 5, 6]
function binary_search (arr, value) {
let low = 0
let heigh = arr.length - 1
const binary_search_closureAndRecursive = function (value) { // 闭包和递归函数,改变low和heigh
let mid = parseInt((low + heigh) / 2) // 每次调用重新计算mid
if ( low > heigh) {
return -1 // 递归调用结束条件
}
switch (true) {
case arr[mid] === value:
return mid
case arr[mid] < value:
low = mid + 1
return binary_search_closureAndRecursive(value) //闭包递归函数
case arr[mid] > value:
heigh = mid - 1
return binary_search_closureAndRecursive(value)
default:
return -1
}
}
return binary_search_closureAndRecursive(value)
}
const res = binary_search (arr, 5)
console.log(res)
------
(2) 递归算法2
// 通过参数改变low和heigh
const arr = [1, 2, 3, 4, 5, 6]
function binary_search_recursive (arr, value, low, heigh) {
if (low > heigh) {
return -1 // 递归结束条件
}
const mid = Math.floor((low + heigh) / 2)
if (arr[mid] === value) {
return mid
}
if (arr[mid] < value) {
low = mid + 1
return binary_search_recursive(arr, value, low, heigh)
}
if (arr[mid] > value) {
heigh = mid - 1
return binary_search_recursive(arr, value, low, heigh)
}
}
const res = binary_search_recursive(arr, 5, 0, arr.length - 1)
console.log(res)
https://juejin.im/post/5c62dbdfe51d45015a21f32a
如何在有序数组中插入新值,仍然是一个数组?
- 要返回新插入的成员的下标
sortedIndex([10, 20, 30], 25); // 2
版本一:
function sortedIndex (arr, value) {
let low = 0
let heigh = arr.length - 1
while (low < heigh) {
// 当low < heigh时,移动low或者height到对应区间的一半
const mid = parseInt((low + heigh) / 2)
if (arr[mid] < value) low = mid + 1
else if (arr[mid] > value) heigh = mid - 1
}
if (low === heigh) {
// 当low和height相当时,需要判断插入和值,和hight或者low位置对应的值
if (arr[low] > value) {
// 如果插入值小,说明要插入low或者height位置的前面
return heigh
} else {
// 如果插入大,说明要插入low或者height位置的后面
return heigh + 1
}
}
return heigh // 当low=heigh时,都还没有确定到位置,说明
}
const res = sortedIndex([10, 20, 30], 15);
console.log(res)
冒泡排序 (Bubble-Sort)
冒泡排序 (bubble-sort)
1. 趟数 和 每一趟比较的次数
- 趟数 = 数组长度 - 1
- 每一趟比较的次数 = 数组长度 - 当前趟数
// 注意,之所以减去趟数,是因为第一趟确定了一个最大值,第二次确定了第二大的值,不需要再做无畏的比较
2.
第一趟比较完:即得出最大值,且冒泡到数组末尾 ------------------------------------ 执行次数:数组长度 - 当前趟数
第二趟比较完:即得出第二大的值,且冒泡到数组倒数第二个位置,依次类推 -------------- 执行次数:数组长度 - 当前趟数
3.代码
第一版
// 第一趟
// 第一次 1432 --------------------- 1432
// 第二次 1432 --------------------- 1342
// 第三次 1342 --------------------- 1324 ------------ 趟数1,执行次数 arr.length - 趟数1 = 3
// 第二趟
// 第一次 1324 --------------------- 1324
// 第二次 1324 --------------------- 1234 ------------ 趟数2,执行次数 arr.length - 趟数2 = 2
// 第三次不用再比较了,因为第一次时,已经找到了最大值。
// 第三趟
// 第一次 1234 --------------------- 1234 ------------ 趟数3,执行次数 arr.length - 趟数3 = 1
// 剩下的不用再比较了,因为前面一次已经找到对应的第二大的值了
const arr = [1, 4, 3, 2]
function bubble_sort (arr) {
// const Arr = JSON.parse(JSON.stringify(arr)) 复制一份不改变原数组
const len = arr.length - 1
for (let i = 0; i < len; i++) { ---------------- 趟数 = 数组长度 - 1
for (let j = 0; j < len - i; j++) { ---------- 每趟比较的次数 = 数组长度 - 当前趟数
if (arr[j] > arr[j+1]) { ------------------- 如果两两比较的前成员,前 > 后,就交换位置,大的冒泡的后面
const temp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = temp
}
}
}
return arr // 排好序后,返回
}
const res = bubble_sort(arr)
console.log(res)
console.log(arr, '原数组') ------------------------- 注意,这样的写法会改变原数组
-------
第二版
1. 可以优化的点
- 每趟的次数
- 最原始:每趟比较 length -1 次
- 第一版:每趟比较 length -1 - i 次
//(第一趟确定一个最大值,第二趟则无需再比较length - 1的最后一次)
//(第二趟确定一个第二大的值,则第三趟无需再比较length -2的倒数两次....)
- 趟数
- 如果一共有7趟,再第6趟的时候就已经全部排好了,那最后一趟就无需再比较
2. 在第一版中已经优化了每趟比较的次数,现在可以可以多余的趟数了
3. 如何找到某趟已经排好了?
- 用一个标志位,进入 (趟数的循环) 默认为true,假设这趟已经排好了
- 在(每趟次数的循环)中,比较相邻大小时,进入了判断,说明没有排好,标志位设置为 false
- 在趟数循环的末尾,判断是不是标志位为true,为true表示该趟拍好了,break掉外层的趟数循环
const arr = [1,4,2,3,8,6,7,5]
let outTimes = 0 // 记录外层循环的次数
let inTimes = 0 // 记录内层循环的次数
function bubble_sort (arr) {
const Arr = JSON.parse (JSON.stringify (arr))
const len = arr.length
for (let i = 0; i < len - 1; i++) {
let isSortOk = true //------------------------------ 标志位,假设该趟已经排好
outTimes++
for (let j = 0; j < len - i - 1; j++) {
inTimes++
if (Arr[j] > Arr[j+1]) {
isSortOk = false // ---------------------------- 进入该判断,表示该趟还未排序好
const temp = Arr[j]
Arr[j] = Arr[j+1]
Arr[j+1] = temp
}
}
if (isSortOk) {
break
// 如果该趟标志位为true,表示该趟其实已经排好了,剩下的趟数都不用在比较了,直接breck整个趟数循环
}
}
return Arr
}
interview 对于冒泡排序,如何通过添加一个参数(函数),来控制升序和降序
const arrs = [1, 3, 2, 5, 4]
function bubbling_sort (arr, compareFn) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length - i; j++) {
if (compareFn(arr[j], arr[j+1]) > 0) {
const temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr
}
const res = bubbling_sort(arrs, (a, b) => b - a)
// 即通过传入一个函数,比较这个函数中两个参数的大小来判断是升序还是降序
// 如果是 (a, b) => a - b 说明 前 > 后 ------------------------------- 升序
// 如果是 (a, b) => b - a 说明 后 > 前 ------------------------------- 降序
console.log(res)
(1)encodeURIComponent() 和 decodeURIComponent
(2)encodeURI() 和 decodeURI
(3)escape() 和 unescape()
1. 区别 encodeURIComponent() 和 encodeURI()
- encodeURI 用于整个url
- encodeURIComponent用于对 url的某一段进行编码 (component嘛)
- encodeURIComponent编码的返回更大
2.
编码:encode
解码:decode
3. escape encodeURI encodeURIComponent
- 共同点:都不会对字母和数字编码
- 区别
- escape:用于字符串 -------------------------------------- 过时产物,尽量不要使用
- encodeURIComponent 和 encodeURI 用于 url
4. encodeURIComponent(URIstring)
- encodeURIComponent()函数可以把字符串作为 URI组件 进行编码
- 返回值的某些字符将被十六进制的转义序列进行替换
- 注意:
- encodeURIComponent() 不会对 ASCII字母和数字进行编码
- 不会编码: -_.*~( )'! ---------------- (刚刚电信拨了小括号,还有 ' 不要忘了)
- 会编码:: / ? = & # @ + $ , ; --------- ( 用于分隔URI组件的标点符号 ),替换为一个或者多个十六进制转义序列
5.实例
A页面
componentWillMount() {
const _token = window.localStorage.getItem('access_token');
const href = window.encodeURIComponent(window.location.href);
if (_token) {
this.props.getUserInfo(_token);
return ;
}
window.location.href = `http://localhost:40004/login?redirect_url=${href}`;
}
说明:
window.location.href = `http://localhost:40004/login?redirect_url=${href}`;
地址栏跳转到 (B页面) 并且携带当前完整的href地址 (A页面)
该href就需要编码,因为在B页面要拿到search就只能有一个?
----------------------------------------------------------------------------------------
B页面
function * watchLogin() {
yield takeEvery(constants.LOGIN, function * login(action) {
try {
const query = qs.parse(window.location.search.slice(1)); // 去掉search的问号,并转成对象
const redirect = query['redirect_url'] ; // 拿到对象redirect_url属性对应的值
const redirectHref = window.decodeURIComponent(redirect); // 再反解码
yield call(() => window.location.href = redirect); // 再跳回A页面
} catch (err) {
yield call(() => swal('错误', '用户名或密码错误', 'error'));
}
});
}
https://aotu.io/notes/2017/06/15/The-mystery-of-URL-encoding/?o2src=juejin&o2layout=compat
https://juejin.im/post/582e3bc4da2f600063e697ab
把cookie聊清楚 https://juejin.im/post/59d1f59bf265da06700b0934
编码 https://juejin.im/post/59cd8420518825276f49f921