underscore学习总结,献给晦涩的函数式编程之美

underscore.js 越看越美,如果在项目中不断尝试underscore的方法,将会事半功倍

underscore 体现出 functionial javascript的思想,采用函数式编程的思路来解决日常生活中的一些 util的小问题

javascript 属于弱语言,对象类型用得最多的就是 array和object,underscore是基于js 封装一些对象和数组方法的库,使用起来非常便捷

这里推荐一本函数式编程的书,Functionial Javascript ,这本书对于函数式编程艺术表现形式发挥得淋漓精致,实在是越看越美

collection function 部分强烈建议看完,后半部分的array function有点水,想复习的可以看看,不想看的就别看了,略水(其中数组的flatten方法建议还是看下)

Functional Javascript

源码一开始就给出了非常实用的 创建 通用回调函数的方法

_.iteratee = function(value, context, argCount) {
    if (value == null) return _.identity;
    if (_.isFunction(value)) return createCallback(value, context, argCount);
    if (_.isObject(value)) return _.matches(value);
    return _.property(value);
  };

iteratee :产生通用回调函数callback的内部入口

1、如果 value是 null 就直接返回 identity函数

_.identity = function(value) {
    return value;
 };

看似没用的identity方法,在函数式编程中属于一个通用的接口(该想法纯属个人理解,如有更好的理解方式,欢迎告知)

2、如果value是 函数

即返回一个 由 creatCallback 创建的 callback

var createCallback = function(func, context, argCount) {
    if (context === void 0) return func;
    switch (argCount == null ? 3 : argCount) {
      case 1: return function(value) {
        return func.call(context, value);
      };
      case 2: return function(value, other) {
        return func.call(context, value, other);
      };
      case 3: return function(value, index, collection) {
        return func.call(context, value, index, collection);
      };
      case 4: return function(accumulator, value, index, collection) {
        return func.call(context, accumulator, value, index, collection);
      };
    }
    return function() {
      return func.apply(context, arguments);
    };
 };

1、如果上下文是 void 0(查资料void 0 实际就是等于undefined),那么直接返回 func

2、argCount 核心方法,underscore 中很多的方法的callback都是基于 这个switch实现的

譬如 map,each,find,filter ,argCount 对应的是3,对应的callback的参数为 value, index, list

tips:这里要讲讲这3个参数,参数名字不是随便起的,是有意义的

value代表一组list中的一个值,index代表索引值,list(collection)代表那个list集合数组或对象,理解这些变量的含义对于理解作者的程序有很大的帮助_each:

_.each = _.forEach = function(obj, iteratee, context) {
    if (obj == null) return obj;
    iteratee = createCallback(iteratee, context);
    var i, length = obj.length;
    if (length === +length) {
      for (i = 0; i < length; i++) {
        iteratee(obj[i], i, obj);
      }
    } else {
      var keys = _.keys(obj);
      for (i = 0, length = keys.length; i < length; i++) {
        iteratee(obj[keys[i]], keys[i], obj);
      }
    }
    return obj;
  };

each 方法,argCount为undefined,那么undefined==null,那么他就是 argCount为3的回调函数

这里有2个分支,如果是数组,如果是对象,采用内部方法keys来实现,underscore很多的方法都是基于这个判断

_.keys = function(obj) {
    if (!_.isObject(obj)) return [];
    if (nativeKeys) return nativeKeys(obj);
    var keys = [];
    for (var key in obj) if (_.has(obj, key)) keys.push(key);
    return keys;
 };

如果对象不存在,那么返回空数组,如果存在内部keys方法,则使用内部keys方法

那么什么是内部keys方法呢   nativeKeys = Object.keys,

由于现代浏览器支持 ecmascript5的规范,浏览器本身支持keys方法的话,则直接使用浏览器内部方法,加速执行效率

_.has方法,判断是否是对象的自有属性(hasOwnProperty),默认不使用继承的prototype的属性

最后返回数组keys,这里需要注意的是,为了接口的一致性,数组和对象经过处理之后全部可以使用相同的方法接口的 数组

_.map :

map方法并没有特殊的地方,只是在内部创建了一个新的result数组,然后逐个编辑数组对象func求值后插入result数组,最终返回这个数组

_.find:

_.find = _.detect = function(obj, predicate, context) {
    var result;
    predicate = _.iteratee(predicate, context);
    _.some(obj, function(value, index, list) {
      if (predicate(value, index, list)) {
        result = value;
        return true;
      }
    });
    return result;
  };
_.some = _.any = function(obj, predicate, context) {
    if (obj == null) return false;
    predicate = _.iteratee(predicate, context);
    var keys = obj.length !== +obj.length && _.keys(obj),
        length = (keys || obj).length,
        index, currentKey;
    for (index = 0; index < length; index++) {
      currentKey = keys ? keys[index] : index;
      if (predicate(obj[currentKey], currentKey, obj)) return true;
    }
    return false;
 };

这里需要提一下的是这个predicate,什么是predicate ,predicate是一个函数,返回 true or false,这对于查找和判断非常有用

some是如果有一个 值满足 predicate 那么就是返回true,some结合 find方法 可以找到 第一个满足 predicate的 值

相当于 sql中的 limit one

_.filter:

内部使用 each遍历+predicate判断的方法 ,创建 result 数组,然后符合predicate的true值就插入push返回,没啥多说的

_.reject:

filter的 reverse方法,返回的result 是 false的情况

_.every:

some函数的升级版本,如果所有的集合元素和符合predicate,那么返回true,如果有一个不符合条件就返回false

argCount=4的情况

_reduce:

_.reduce = _.foldl = _.inject = function(obj, iteratee, memo, context) {
    if (obj == null) obj = [];
    iteratee = createCallback(iteratee, context, 4);
    var keys = obj.length !== +obj.length && _.keys(obj),
        length = (keys || obj).length,
        index = 0, currentKey;
    if (arguments.length < 3) {
      if (!length) throw new TypeError(reduceError);
      memo = obj[keys ? keys[index++] : index++];
    }
    for (; index < length; index++) {
      currentKey = keys ? keys[index] : index;
      memo = iteratee(memo, obj[currentKey], currentKey, obj);
    }
    return memo;
 };

这里多了一个参数 memo,对应于 createCallback中就是 accumulator

accumulator顾名思义就是一个累加器,这里有一个分支,如果累加器不存在,默认取数组的第一个值作为memo的初始值

然后循环执行iteratee后,返回最终的memo

_reduceRight

reduceRight的内部实现原理和reduce一致,只是循环的顺序从数组最后一个开始, while (index--)  的方式实现

相关扩展函数

_.pluck = function(obj, key) {
    return _.map(obj, _.property(key));
 };
_.where = function(obj, attrs) {
    return _.filter(obj, _.matches(attrs));
  };
_.findWhere = function(obj, attrs) {
    return _.find(obj, _.matches(attrs));
  };

这里开始体现函数式编程的强大优美之处,扩展现有的函数,实现新的函数,这就是函数式编程,

pluck基于 map,map使用property

where基于filter,filter使用matches,matches使用pairs,pairs使用keys,一切皆是函数

_.property,_matches属于基础函数

_.property = function(key) {
    return function(obj) {
      return obj[key];
    };
  };

propery是锁定了key值,可以被map用来获取每个元素指定的key值,这里是用到js中强大的闭包功能

 _.matches = function(attrs) {
    var pairs = _.pairs(attrs), length = pairs.length;
    return function(obj) {
      if (obj == null) return !length;
      obj = new Object(obj);
      for (var i = 0; i < length; i++) {
        var pair = pairs[i], key = pair[0];
        if (pair[1] !== obj[key] || !(key in obj)) return false;
      }
      return true;
    };
 };
_.pairs = function(obj) {
    var keys = _.keys(obj);
    var length = keys.length;
    var pairs = Array(length);
    for (var i = 0; i < length; i++) {
      pairs[i] = [keys[i], obj[keys[i]]];
    }
    return pairs;
 };

pairs是一个 数组,每一个元素又是一个数组,每个数组有2个值,index 0为key值,index 1为 value值

这里要注意 matches 返回的也是一个function,这个function是一个predicate,这里再次强调predicate的重要性,他的重要性就是这个思想,返回true or false

用来 筛选 结果,一个会经常使用的功能。

下面说一说其他的 collection function ,由于缺少关联性,所以就逐一列举说明

_.contains = _.include = function(obj, target) {
    if (obj == null) return false;
    if (obj.length !== +obj.length) obj = _.values(obj);
    return _.indexOf(obj, target) >= 0;
 };

如果说keys是返回对象的key值数组的话,那么values函数就是返回对象的 value值数组,用法基本是一致的

_indexOf:

_.indexOf = function(array, item, isSorted) {
    if (array == null) return -1;
    var i = 0, length = array.length;
    if (isSorted) {
      if (typeof isSorted == ‘number‘) {
        i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
      } else {
        i = _.sortedIndex(array, item);
        return array[i] === item ? i : -1;
      }
    }
    for (; i < length; i++) if (array[i] === item) return i;
    return -1;
 };

这里的 isSorted有3个分支

1. 为undefined, 这个情况说明 查找的数组不需要跳过几个元素进行查找,从下标为0开始查找

2.为 数值,这个跟undefined严格来说是属于一个分支,从下表为 isSorted 开始查找,如果是负值,先求max再循环查找

3.为 true ,那么使用 binary search (二进制查找,本人算法需要勤加修炼,如翻译不对,欢迎拍砖)

_.sortedIndex = function(array, obj, iteratee, context) {
    iteratee = _.iteratee(iteratee, context, 1);
    var value = iteratee(obj);
    var low = 0, high = array.length;
    while (low < high) {
      var mid = low + high >>> 1;
      if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
    }
    return low;
 };

这里一起过一遍这个算法 low代表 0, high 代表最后一个元素  >>>运算符相当于 除以2

mid是 高位的一半+低位

如果 中间mid 小于这个 value,那么 低位就要变成 mid+1位 ,然后继续循环,让high位除以2

如果中间mid大于这个value,那么高位high变成mid位,继续循环

直到low位大于high位

此时就找到了 low 这个 数组 索引 index的值,如果这个index对应的值跟item的值相同,则返回相关的索引

_invoke

_.invoke = function(obj, method) {
    var args = slice.call(arguments, 2);
    var isFunc = _.isFunction(method);
    return _.map(obj, function(value) {
      return (isFunc ? method : value[method]).apply(value, args);
    });
 };

使用指定的上下文 来触发函数的执行

method可以使 函数,或者是字符串

_max 和 _min 函数

2种情况,有判断函数和没有判断函数

有判断函数就求值判断,没有就遍历数组或对象,然后使用内置方法比较返回

_shuffle

_.random = function(min, max) {
    if (max == null) {
      max = min;
      min = 0;
    }
    return min + Math.floor(Math.random() * (max - min + 1));
 };
_.shuffle = function(obj) {
    var set = obj && obj.length === +obj.length ? obj : _.values(obj);
    var length = set.length;
    var shuffled = Array(length);
    for (var index = 0, rand; index < length; index++) {
      rand = _.random(0, index);
      if (rand !== index) shuffled[index] = shuffled[rand];
      shuffled[rand] = set[index];
    }
    return shuffled;
 };

这个函数其实没有什么特别的作用,就是重新排列集合中的元素

这里的算法还是比较精妙的,一开始没看懂,看几遍又懂了,过几天再看,又晦涩难懂了,有一种被智商压制的感觉

这里的精妙之处就在于 _.random(0,index)

第一次进入循环,index为0,那么random值肯定是0   shuffled[0]=set[0];

第二次进入循环  index为 1  那么 random值为0或1

什么情况会出现 rand !==index , 那么 index为 1, rand=0 ,这个时候 shuffled[1]=shuffled[0],  shuffled[0]=set[1],互换了位置

如果 rand!==index不成立,那么 shuffed[1]=set[1];

下面看第三次循环  index为2 ,random值为 0,1,2 ,他要shuffle几次,还是一次,原理就是index互换

这个shuffle每次只换了一个元素,效率奇高,而且也做到了shuffle 可能存在的所有结果,非常高效,不管你有几个元素,每次shuffle只做了一个切换,

自己写不出这种感觉有木有,实在是牛x。

_.sample

_.sample = function(obj, n, guard) {
    if (n == null || guard) {
      if (obj.length !== +obj.length) obj = _.values(obj);
      return obj[_.random(obj.length - 1)];
    }
    return _.shuffle(obj).slice(0, Math.max(0, n));
 };

guard官方说是一个保护值,如果有guard,那么无论n给多少值,都是取一个值

多个值的情况使用了shuffle函数

_.sortBy

_.sortBy = function(obj, iteratee, context) {
    iteratee = _.iteratee(iteratee, context);
    return _.pluck(_.map(obj, function(value, index, list) {
      return {
        value: value,
        index: index,
        criteria: iteratee(value, index, list)
      };
    }).sort(function(left, right) {
      var a = left.criteria;
      var b = right.criteria;
      if (a !== b) {
        if (a > b || a === void 0) return 1;
        if (a < b || b === void 0) return -1;
      }
      return left.index - right.index;
    }), ‘value‘);
  };

乍一看比较吓人,现在肢解一下

iteratee 回调函数

pluck 接受2个参数,一个集合,一个 key值,这里的key值就是"value"

map 返回的是一个 数组 集合对象,每个对象包含3个 键值对,然后使用 含有 sort  function 方法的进行最后返回一个 经过排序的 values 数组,分解之后思路就比较清晰了。

_.toArray:

_.toArray = function(obj) {
    if (!obj) return [];
    if (_.isArray(obj)) return slice.call(obj);
    if (obj.length === +obj.length) return _.map(obj, _.identity);
    return _.values(obj);
 };

官方的解释是可以将任何 可迭代的集合对象转换为数组

obj.length === +obj.length  可以表示一个带有length属性的伪数组对象

关于伪数组对象的概念,欢迎告知更为精确的理解方式

_.size:

_.size = function(obj) {
    if (obj == null) return 0;
    return obj.length === +obj.length ? obj.length : _.keys(obj).length;
 };

返回集合对象或者数组的长度

_.partition

_.partition = function(obj, predicate, context) {
    predicate = _.iteratee(predicate, context);
    var pass = [], fail = [];
    _.each(obj, function(value, key, obj) {
      (predicate(value, key, obj) ? pass : fail).push(value);
    });
    return [pass, fail];
 };

从英文来看是分区的概念,并没有什么特别之处,就是将filter和reject 2个函数的结果放入到一个数组中去,true的数组叫pass,fail的数组为fail

最后说一下集合函数中又一个重要的group函数

内部groupby 函数

var group = function(behavior) {
    return function(obj, iteratee, context) {
      var result = {};
      iteratee = _.iteratee(iteratee, context);
      _.each(obj, function(value, index) {
        var key = iteratee(value, index, obj);
        behavior(result, value, key);
      });
      return result;
    };
 };

behavior本身是一个函数,可以理解为group 传递一个函数,然后返回一个函数,返回的函数将使用到这个behavior函数

这个包含3个group使用到的函数

_.groupBy = group(function(result, value, key) {
    if (_.has(result, key)) result[key].push(value); else result[key] = [value];
  });
_.indexBy = group(function(result, value, key) {
    result[key] = value;
  });
_.countBy = group(function(result, value, key) {
    if (_.has(result, key)) result[key]++; else result[key] = 1;
  });

groupBy:

groupBy的 key值其实就是 iteratee 函数的 return 结果值,每个分组由一个数组保存,最后的result根据group的定义可知,是一个map对象

然后根据 结果值 分组

indexBy:

默认传入的集合中的元素是唯一的

countBy:

countBy的结果是 进行统计,跟 result push不同,但是原理是相同的

自此所有的集合函数都已经复习完毕

******************华丽的分割线*************************

Array Functions  数组函数

下面都是一些工具函数,没啥新意,就当一起复习下吧

_first:

_.first = _.head = _.take = function(array, n, guard) {
    if (array == null) return void 0;
    if (n == null || guard) return array[0];
    if (n < 0) return [];
    return slice.call(array, 0, n);
 };

返回数组的第一个元素,或者slice(0,n)个元素,如果n为负值,那么会返回空数组,如果存在guard值为true,无论n为多少都只会返回第一个元素

_.initial:

_.initial = function(array, n, guard) {
    return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
 };

返回数组中 length-n个元素,如果guard值存在,就剔除最后一个元素返回

_.last:

_.last = function(array, n, guard) {
    if (array == null) return void 0;
    if (n == null || guard) return array[array.length - 1];
    return slice.call(array, Math.max(array.length - n, 0));
 };

返回数组的最后一个元素,或者slice(n)到数组结尾个元素,如果n为负值,那么会返回全数组,如果存在guard值为true,无论n为多少都只会返回最后一个元素

_rest:

_.rest = _.tail = _.drop = function(array, n, guard) {
    return slice.call(array, n == null || guard ? 1 : n);
  };

剔除数组中的第一个元素,如果n存在,返回n到数组结尾的数组,如果guard存在,剔除第一个数组中的元素返回

_.compact

_.compact = function(array) {
    return _.filter(array, _.identity);
 };

剔除 所以会判断为false的 数组值,内部使用filter实现,这里就能看出identity函数的作用了

下面是比较重要的一个flatten 函数,先来看内部实现

var flatten = function(input, shallow, strict, output) {
    if (shallow && _.every(input, _.isArray)) {
      return concat.apply(output, input);
    }
    for (var i = 0, length = input.length; i < length; i++) {
      var value = input[i];
      if (!_.isArray(value) && !_.isArguments(value)) {
        if (!strict) output.push(value);
      } else if (shallow) {
        push.apply(output, value);
      } else {
        flatten(value, shallow, strict, output);
      }
    }
    return output;
 };

shallow 控制是否是深度 迭代

譬如 数组 a=[1,2,[2,3,[1,2]]]

shallow=true的情况

flatten之后的结果为 [1,2,2,3,[1,2]]

shallow 为假的情况

flatten[1,2,2,3,1,2]

strict 控制是否 字符串也可以 作为 数组来 扁平化(flatten)

具体的实现函数如下:

_.flatten

 _.flatten = function(array, shallow) {
    return flatten(array, shallow, false, []);
 };

flatten 函数默认 strict为false

_.difference

_.difference = function(array) {
    var rest = flatten(slice.call(arguments, 1), true, true, []);
    return _.filter(array, function(value){
      return !_.contains(rest, value);
    });
 };

可以看到这里使用了filter函数,内部又使用了contains函数,这里还要注意的是 strict为true,那么如果你传入字符串的话,是没有用,不会去重

_.difference([2,3],"23",[2]);

result: [3]

_.union

_.union = function() {
    return _.uniq(flatten(arguments, true, true, []));
 };
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
    if (array == null) return [];
    if (!_.isBoolean(isSorted)) {
      context = iteratee;
      iteratee = isSorted;
      isSorted = false;
    }
    if (iteratee != null) iteratee = _.iteratee(iteratee, context);
    var result = [];
    var seen = [];
    for (var i = 0, length = array.length; i < length; i++) {
      var value = array[i];
      if (isSorted) {
        if (!i || seen !== value) result.push(value);
        seen = value;
      } else if (iteratee) {
        var computed = iteratee(value, i, array);
        if (_.indexOf(seen, computed) < 0) {
          seen.push(computed);
          result.push(value);
        }
      } else if (_.indexOf(result, value) < 0) {
        result.push(value);
      }
    }
    return result;
  };
if (!_.isBoolean(isSorted)) {
      context = iteratee;
      iteratee = isSorted;
      isSorted = false;
}

这一段代码是典型的修正变量的代码,在jquery的实现里面大量的出现

函数传入的参数非常灵活

如果isSorted函数不传递的话

那么 isSorted这个位置的参数值,实际是 iteratee函数

那么每个参数都需要进行一次修正

context=iteratee 3->4

iteratee=isSorted 2->3

isSorted=false false->2

isSorted 由用户自行判断是否是已经排序过的数组,如果是已经排序过的数组,内部使用简洁快速的方法进行排序

如果iteratee 自定义函数存在,那么 执行运算后判断是否是新值

如果不存在,直接取 array中元素的值进行 indexOf的判断

_.without

_.without = function(array) {
    return _.difference(array, slice.call(arguments, 1));
  };

difference的扩展方法,用法基本于difference一致

_.intersection

_.intersection = function(array) {
    if (array == null) return [];
    var result = [];
    var argsLength = arguments.length;
    for (var i = 0, length = array.length; i < length; i++) {
      var item = array[i];
      if (_.contains(result, item)) continue;
      for (var j = 1; j < argsLength; j++) {
        if (!_.contains(arguments[j], item)) break;
      }
      if (j === argsLength) result.push(item);
    }
    return result;
 };

本方法不支持深层递归

a=[[1,2],3]

b=[[1,2],3]

_.intersect(a,b)  result=> [3]

根据arguments的长度来判断,只要一个元素不在任何一个数组中,那么就剔除这个元素,该元素的引用地址必须相同

如果对象的引用地址相同,则返回那个共有的对象

a={}  b=a

arr1=[a,1]

arr2=[b,2]

_.intersect(a,b)  result=> {}

_.zip

_.zip = function(array) {
    if (array == null) return [];
    var length = _.max(arguments, ‘length‘).length;
    var results = Array(length);
    for (var i = 0; i < length; i++) {
      results[i] = _.pluck(arguments, i);
    }
    return results;
 };

根据 数组长度的最大值判断 新的数组中每个一个元素数组的长度, max内部是使用_.property函数获取了length这个属性

Example

_.zip([1,2,3],[2,3],[3]);

result [[1,2,3],[2,3,undefined],[3,undefined,undefined]]

一般使用的方法为 每个数组长度都是相同的

_.object

_.object = function(list, values) {
    if (list == null) return {};
    var result = {};
    for (var i = 0, length = list.length; i < length; i++) {
      if (values) {
        result[list[i]] = values[i];
      } else {
        result[list[i][0]] = list[i][1];
      }
    }
    return result;
 };

如果values存在 将 2个数组 结合为一个对象

如果values不存在 则使用list中每一个item[0]作为 key值,item[1] 作为 value值

最后返回这个合成的对象

_.lastIndexOf

_.lastIndexOf = function(array, item, from) {
    if (array == null) return -1;
    var idx = array.length;
    if (typeof from == ‘number‘) {
      idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1);
    }
    while (--idx >= 0) if (array[idx] === item) return idx;
    return -1;
 };

并没有什么特别的地方,判断了小于0和大于边界的情况,一如既往的向前遍历 使用 while循环

_.range:

_.range = function(start, stop, step) {
    if (arguments.length <= 1) {
      stop = start || 0;
      start = 0;
    }
    step = step || 1;

    var length = Math.max(Math.ceil((stop - start) / step), 0);
    var range = Array(length);

    for (var idx = 0; idx < length; idx++, start += step) {
      range[idx] = start;
    }

    return range;
 };

如果只有一个参数, start的值给到stop, start 默认从0开始

step如果不存在,就使用1

length值代表每一个step的元素个数

实际的效果就是 给到一个 基于step 断点的效果

_.range(1,30,4)

result=>[1,5,9,13,17,21,25,29]

自此 数组方法也已经复习完毕,写的有点累,相关的 oop、util Function、object function、函数式函数,将在以后进行复习

时间: 2025-01-05 03:57:46

underscore学习总结,献给晦涩的函数式编程之美的相关文章

Java 编程之美:并发极速赛车平台出租编程高级篇

借用 Java 并发极速赛车平台出租haozbbs.comQ1446595067 编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了. 相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作的顺序是不可预期的. 并发编程相比 Java 中其他知识点学习起来门槛相对较高,学习起来比较费劲,从而导致很多人望而却步: 而无论是职场面试和高并发高流量的系统的实现却都还离不开并发编程,从而导致能够真正掌握并发编程的人才成为市场比较迫

编程之美2.14 求数组的子数组之和的最大值

问题描述: 一个有N个整数元素的一维数组(A[0], A[1], A[2],...,A[n-1]),这个数组当然有很多子数组,那么子数组之和的最大值是什么呢? 解法: 1. 暴力解法-------O(N^3) 2. 改进版暴力解法-------O(N^2) *3. 分治算法-------O(NlogN)(暂时未去实现) 4. 数组间关系法-------O(N) 具体思路和代码: 1.暴力解法 思路:Sum[i,...,j]为数组第i个元素到第j个元素的和,遍历所有可能的Sum[i,...,j].

编程之美2.17 数组循环移位

问题描述: 设计一个算法,把一个含有N元素的数组循环左移或者右移K位. 解决方法: 1. 暴力解法------O(KN) 2. 颠倒位置------O(N) 具体思路和代码: 1. 暴力解法------O(KN) 思路:循环K次,每次移动一位 代码: 1 //右移 2 void s1(int A[], int n, int k) 3 { 4 k = k % n; 5 for(int i = 0; i < k; i++) 6 { 7 int t = A[n-1]; 8 for(int j = n-

编程之美leetcode之编辑距离

Edit Distance Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.) You have the following 3 operations permitted on a word: a) Insert a character b) Delete a char

编程之美2.17之数组循环移位

题目描述:设计一个算法,把一个含有N个元素的数组循环右移K位,要求算法的时间复杂度位O(Log2N),且只允许使用两个附加变量. 什么意思呢,就是说如果输入序列为:abcd1234,右移2位即变为34abcd12.唯一的要求就是使用两个附加变量. 其实这道题编程珠玑上面也出现过,书中给出的一种符合题意的解法是巧妙地进行翻转.以把abcd1234右移4位为例: 第一步:翻转1234,abcd1234---->abcd4321 第二步:翻转abcd,abcd4321---->dcba4321 第三

编程之美2.3: 寻找发帖水王

题目:传说,Tango有一大"水王",他不但喜欢发帖,还会回复其他ID发的帖子,发帖数目超过帖子总数的一半,如果你有一个当前论坛上所有帖子的列表,其中帖子作者的ID也在表中,你能快速找到这个传说中的Tango水王吗? 解题思路:由于水王的发帖数目超过一半,当每次删除两个不同ID的帖子时,水王占得帖子数目仍然大于剩下帖子的一半,重复整个过程,将ID列表中的ID总数降低,转化为更小的问题,从而得到最后水王的ID. #include <iostream> #include <

编程之美2.13 子数组最大乘积

问题描述: 给定一个长度为N的整数数组,只允许用乘法,不能用除法,计算任意(N-1)个数的组合乘积中最大的一组,并写出算法的时间复杂度. 解法: 1.暴力解法------O(n^2) 2.前后缀法------O(n) 3.统计法--------O(n) 具体思路和代码: 1.暴力解法: 思路:利用两层循环,依次删掉一个,其余的做乘法,计算出最大的. 代码: 1 int s1(int A[], int n) 2 { 3 int s = 1; 4 int max; 5 for(int i = 1;

编程之美2.1 求二进制中1的个数

最近一段的时间,一直在看编程之美之类的算法书籍,刚开始看编程之美,感觉到难度太大,有时候也不愿意去翻动这本书,不过,经过一段时间的修炼,我也彻底的喜欢上这本书了, 书中的算法涉及到很多方面,树,链表,位运算,数组,hash表应用等等. 由于最近事情也忙得差不多了,我重新写了一遍编程之美中的算法,在这里记录下来,以便以后阅读方便. 第一道题从2.1写起,这道题目难度不是很大,首先,给出这个题目的函数声明: /*2.1 求二进制中1的个数*/ int DutCountOf1InBin_1(unsig

函数式编程之-F#类型系统

在深入到函数式编程思想之前,了解函数式独有的类型是非常有必要的.函数式类型跟OO语言中的数据结构截然不同,这也导致使用函数式编程语言来解决问题的思路跟OO的思路有明显的区别. 什么是类型?类型在编程语言中有什么作用呢?一般来说,类型有两个作用: 首先当你对某个数据声明类型后,就拥有了编译时的检查,换句话说,你可以认为类型充当了"编译时的单元测试"; 类型系统可以让你建立一种模型,用来表达真实世界中的模型; Tuple type 元组是函数式编程语言中的常用类型,同时在.NET 4.0中