【算法】二分查找,冒泡排序

目录
二分法查找
需求...在有序数组中插入新成员后,仍然是一个有序的数组
冒泡排序
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'));
   }
 });
}
image.png

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




最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容