freeCodeCamp 中级javascript算法体验

1、区间求值算法(Sum All Numbers in a Range)

我们会传递给你一个包含两个数字的数组。返回这两个数字和它们之间所有数字的和。
最小的数字并非总在最前面。
对你有帮助的资源:
Math.max() 函数返回一组数中的最大值。
Math.min() 函数返回一组数中的最小值。

//思路:先找出最大值和最小值,循环算和
function sumAll(arr) {
  var sum = 0;
  var min = Math.min(arr[0],arr[1]);
  var max = Math.max(arr[0],arr[1]);
  for(var i = min;i<=max;i++){
    sum+=i;
  }
  return sum;
}
sumAll([1, 4]);
2、找出数组间差异算法(Diff Two Arrays)

比较两个数组,然后返回一个新数组,该数组的元素为两个给定数组中所有独有的数组元素。换言之,返回两个数组的差异。
对你有帮助的资源:
Array.indexOf() 方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
Array.concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

//思路:分别对比两个数组中的元素,不同的加入新数组中,然后将不同的元素连接成一个新数组
function diff(arr1, arr2) {
  var newArr = [];
  var arr3 = [];  
  for (var i=0;i<arr1.length;i++) {  
    if(arr2.indexOf(arr1[i]) === -1)     //对比
      arr3.push(arr1[i]);  
  }  
   var arr4 = [];  
  for (var j=0;j<arr2.length;j++) {  
    if(arr1.indexOf(arr2[j]) === -1)    //对比
      arr4.push(arr2[j]);  
  }  
   newArr = arr3.concat(arr4);     //连接
  // Same, same; but different.
  return newArr;
}
diff([1, 2, 3, 5], [1, 2, 3, 4, 5]);
3、数字转罗马数字(Roman Numeral Converter)

所有返回的 罗马数字 都应该是大写形式。
对你有帮助的资源:
Array.prototype.forEach() 方法对数组的每个元素执行一次提供的函数。

//思路:将数字对应的罗马数字建立对应的数组,将数字数组循环跟要转化的数字进行对比,找出对应区间对应的罗马数字
function convert(num) {
 var nums = [1000,900,500,400,100,90,50,40,10,9,5,4,1];  
  var romans =["m","cm","d","cd","c","xc","l","xl","x","ix","v","iv","i"];  
  var str = '';  
  nums.forEach(function(item,index,array){    //循环对比要转化的数字
    while(num >= item){  
      str += romans[index];    //转化成对应的罗马数字
      num -= item;  
    }  
  });  
 return str.toUpperCase();   //全部转化成大写
}
convert(36);
4、对象搜索算法(Where art thou)

写一个 function,它遍历一个对象数组(第一个参数)并返回一个包含相匹配的属性-值对(第二个参数)的所有对象的数组。如果返回的数组中包含 source 对象的属性-值对,那么此对象的每一个属性-值对都必须存在于 collection 的对象中。
例如,如果第一个参数是[{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }],第二个参数是{ last: "Capulet" },那么你必须从数组(第一个参数)返回其中的第三个对象,因为它包含了作为第二个参数传递的属性-值对。
对你有帮助的资源:
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)。
语法

function where(collection, source) {
  var arr = [];
  var arrSource=Object.keys(source);//把source的属性转化为数组  
  var i='',j=0;
  for(i in collection){//循环collection的元素
    var count=0;
    for(j=0;j<arrSource.length;j++){//针对source的属性进行循环,查找这个collection元素中是否有指定的source的属性
      if(collection[i][arrSource[j]]&&source[arrSource[j]]==collection[i][arrSource[j]]){
        count++;
      } 
    }
    //判断:如果完全包含,这个collection的元素就被允许添加到里边。
    if(count==arrSource.length){
       arr.push(collection[i]);
    }
  }
  return arr;
}
where([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" });
5、查询替换算法(Search and Replace)

使用给定的参数对句子执行一次查找和替换,然后返回新句子。
第一个参数是将要对其执行查找和替换的句子。
第二个参数是将被替换掉的单词(替换前的单词)。
第三个参数用于替换第二个参数(替换后的单词)。
注意:替换时保持原单词的大小写。例如,如果你想用单词 "dog" 替换单词 "Book" ,你应该替换成 "Dog"。

//思路:确定好了要替换的单词和原来的单词之后用replace方法直接替换
function myReplace(str, before, after) {
 if(before[0] === before[0].toUpperCase()){  //如果原单词大写,替换的单词也要大写
    after = after[0].toUpperCase() + after.slice(1);
  }
  str = str.replace(before,after);  
  return str;
}
myReplace("A quick brown fox jumped over the lazy dog", "jumped", "leaped");
6、字符串移动插入算法(Pig Latin)

把指定的字符串翻译成 pig latin。
Pig Latin 把一个英文单词的第一个辅音或辅音丛(consonant cluster)移到词尾,然后加上后缀 "ay"。
如果单词以元音开始,你只需要在词尾添加 "way" 就可以了。
对你有帮助的资源:
String.substr() 方法返回一个字符串中从指定位置开始到指定字符数的字符。

//思路:用indexOf方法判断元素的第一个字母是不是元音,如果是就直接加way,如果不是就用substr方法截取将第一个字母移到最后再加ay
function translate(str) {
var vowel = ["a", "e", "i", "o", "u"];
if (vowel.indexOf(str[0]) != -1) {
return str + "way";
}
while (vowel.indexOf(str[0]) == -1) {
str = str.substr(1) + str.substr(0, 1);
}
return str + "ay";
}
translate("consonant");
7、字符配对算法(DNA Pairing)

DNA 链缺少配对的碱基。依据每一个碱基,为其找到配对的碱基,然后将结果作为第二个数组返回。
Base pairs(碱基对) 是一对 AT 和 CG,为给定的字母匹配缺失的碱基。
在每一个数组中将给定的字母作为第一个碱基返回。
例如,对于输入的 GCG,相应地返回 [["G", "C"], ["C","G"],["G", "C"]]
字母和与之配对的字母在一个数组内,然后所有数组再被组织起来封装进一个数组。
对你有帮助的资源:
Array.prototype.map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

//思路:将字符串分割成数组,然后将每一个元素进行配对
function pair(str) {
  var arr = str.split("");
    var pair = '';
    var result = arr.map(function(item,index,array){
        switch(item){
            case 'A':
                pair = 'T';
                break;
            case 'T':
                pair = 'A';
                break;
            case 'C': 
                pair = 'G';
                break;
            case 'G':
                pair = 'C';
                break;
        }
        return [item,pair];
    });
    return result;
}
pair("GCG");
8、字符串查询补充算法()

从传递进来的字母序列中找到缺失的字母并返回它。
如果所有字母都在序列中,返回 undefined。

/*思路:
(1)如果传进来的不是一个按顺序来的字母队列,需要给他转成键码,排序,再转化为字符串。这段是题目之外的要求。
(2)接下来查找字符串排序后在a-z中的索引,然后根据索引用substring切割出来,用以比较(strCompare)
(3)遍历这个数组,找到没有的,*/
function fearNotLetter(str) {
var arrCover=[];
  for(var i=0;i<str.length;i++){
    arrCover.push(str[i].charCodeAt());
  }//转化为键码
  arrCover.sort(function(a,b){
    return a-b;
  });//重新排序
  str='';//清空
  for(i=0;i<arrCover.length;i++){
    str+=(String.fromCharCode(arrCover[i]));
  }//把转化出来的数组重新给字符串
  //获取索引
  var strAll='abcdefghijklmnopqrstuvwxyz';
  var indexOver=strAll.indexOf(str.charAt(str.length-1));
  var indexStart=strAll.indexOf(str.charAt(0));
  //切割出用以比较的字符串
  var strCompare=strAll.substring(indexStart,indexOver+1);
  var newStr='';
  if(strCompare==str){
    return undefined;
  }
  for(i=0;i<strCompare.length;i++){
    if(str.indexOf(strCompare[i])==-1){
      newStr+=strAll[i];
    }
  }
  console.log(newStr);
  return newStr;
}
fearNotLetter("abce");
9、输入检查算法(Boo who)

检查一个值是否是基本布尔类型,并返回 true 或 false。

function boo(bool){
    return (typeof bool == 'boolean');
}
boo(null);
10、数组去重算法(Sorted Union)

写一个 function,传入两个或两个以上的数组,返回一个以给定的原始数组排序的不包含重复值的新数组。
换句话说,所有数组中的所有值都应该以原始顺序被包含在内,但是在最终的数组中不包含重复值。
非重复的数字应该以它们原始的顺序排序,但最终的数组不应该以数字顺序排序。
这是一些对你有帮助的资源:
Arguments object

//思路:循环逐个进行对比,符合条件的放入数组中
function unite(arr1, arr2, arr3) {
var newArr=[];
  for(var i=1;i<arguments.length;i++){
    for(var j=0;j<arguments[i].length;j++){
      if(arguments[0].indexOf(arguments[i][j])==-1){
        newArr.push(arguments[i][j]);
      }
    }
  }
  newArr=arguments[0].concat(newArr);
  console.log(newArr);
  return newArr;
}
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);
11、html符号转实体算法(Convert HTML Entities)

将字符串中的字符&、<、>、"(双引号), 以及'(单引号)转换为它们对应的 HTML 实体。
对你有帮助的资源:
HTML Entities

//思路:将数组分割成数组时候,每个元素进行检查,如果是符号就转化成相应的实体
function convert(str) {
  var i=0;
  var arr=str.split("");
  for(i=0;i<arr.length;i++){
    switch(arr[i]){
      case '&':
        arr.splice(i,1,'&amp;');
        break;
      case '>':
        arr.splice(i,1,'&gt;');
        break;
      case '<':
        arr.splice(i,1,'&lt;');
        break;
      case "'":
        arr.splice(i,1,'&apos;');
        break;
      case '"':
        arr.splice(i,1,'&quot;');
        break;
    }
  }
  str=arr.join('');
  return str;
}
convert("Dolce & Gabbana");
12、字符串连接算法(Spinal Tap Case)

将字符串转换为 spinal case。Spinal case 是 all-lowercase-words-joined-by-dashes 这种形式的,也就是以连字符连接所有小写单词。

function spinalCase(str) {
str=str.replace(/([A-Z]+)/g,' $1');//在大写字母前面加个空格。用$来选择小括号  
  str=str.replace(/\s+|_+/g,'-');//空格全部替换为- 
  if(str[0]=='-'){
    str=str.substring(1);
  }//去掉可能大写开头前面的-
  str=str.replace(/--/g,'-');//把中段的--(两个)替换为一个-。(因为下划线+大写导致生成了两个-)
  str=str.toLowerCase();//转换小写
rn str;
}
spinalCase('This Is Spinal Tap')
spinalCase("This Is Spinal Tap") 应该返回 "this-is-spinal-tap"。
spinalCase("thisIsSpinalTap") 应该返回 "this-is-spinal-tap"。
spinalCase("The_Andy_Griffith_Show") 应该返回 "the-andy-griffith-show"。
spinalCase("Teletubbies say Eh-oh") 应该返回 "teletubbies-say-eh-oh"。
13、斐波纳契奇数求和算法(Sum All Odd Fibonacci Numbers)

给一个正整数num,返回小于或等于num的斐波纳契奇数之和。
斐波纳契数列中的前几个数字是 1、1、2、3、5 和 8,随后的每一个数字都是前两个数字之和。
例如,sumFibs(4)应该返回 5,因为斐波纳契数列中所有小于4的奇数是 1、1、3。
提示:此题不能用递归来实现斐波纳契数列。因为当num
较大时,内存会溢出,推荐用数组来实现。
参考文档:博客园Issue

function sumFibs(num) {
var fibo = [1, 1];  //定义数组,存放奇数1
  var oddSum = 2;  //定义固定和为2
  while(true){
    var item = fibo[0] + fibo[1];   //
    if(num < item){  //当数字小于2的时候,直接返回2
      return oddSum;
    }
    if(item % 2){   //算奇数,算和
      oddSum += item;    
    }
    fibo[0] = fibo[1];
    fibo[1] = item;
  }
}
sumFibs(4);
15、质素求和算法(Sum All Primes)

求小于等于给定数值的质数之和。
只有 1 和它本身两个约数的数叫质数。例如,2 是质数,因为它只能被 1 和 2 整除。1 不是质数,因为它只能被自身整除。
给定的数不一定是质数。

function sumPrimes(num) {
var arr=[2];
  var sum=0;
  for(var i=2;i<=num;i++){
    var bCheck=true;//假定为真
    for(var j=0;j<arr.length;j++){//循环数组
      if(i%arr[j]==0){//如果可以被质数整除
        bCheck=false;//判定为false
        break;//退出循环,用不着继续判断了。
      }
    }
    if(bCheck){
      arr.push(i);
    }
  }
  console.log(arr);
  for(var k=0;k<arr.length;k++){
    sum+=arr[k];
  }//求和
  return sum;
}
sumPrimes(10);
16、最小公倍数算法(Smallest Common Multiple)

找出能被两个给定参数和它们之间的连续数字整除的最小公倍数。
范围是两个数字构成的数组,两个数字不一定按数字顺序排序。
例如对 1 和 3 —— 找出能被 1 和 3 和它们之间所有数字整除的最小公倍数。
如果你被卡住了,记得开大招 Read-Search-Ask 。尝试与他人结伴编程、编写你自己的代码。
对你有帮助的资源:
Smallest Common Multiple

/*思路:
(1)在此之前要了解欧拉算法求最大公约数。
简单的说,求两个数的最大公约数,用大数对小数求模,如果能被整除,则小数是这两个数的最大公约数。
如果不能整除,就用小数对余数再次求模,循环此过程直到返回能除尽的那个除数。就是最大公约数。
比如20和15,用20除以15余数为5,然后用15除以余数5,能够整除,所以返回出来的除数5为这两个数的最大公约数。
(2)有了最大公约数,就可以求最小公倍数。
最小公倍数的求法是:彼此之间的乘积除以最大公约数。
因为是求几个连续自然数之间的公倍数,所以,求出前两个最小公倍数之后,用这个最小公倍数和下一个值比较。
然后就得出了新的最小公倍数。主要用的是递归的思想。*/
function smallestCommons(arr) {
  arr=arr.sort(function(a,b){
    return a-b;
  });
  function fun(m,n){
    if(m%n===0) return n;
    return fun(n,m%n);
  }
  var num=arr[0];
  for(var i=arr[0]+1;i<=arr[1];i++){
    num*=i/fun(num,i);
  }
  return num;
}
smallestCommons([1,5]);
17、数组验证算法(Finders Keepers)

写一个 function,它遍历数组 arr,并返回数组中第一个满足 func 返回值的元素。
举个例子,如果 arr 为 [1, 2, 3],func 为 function(num) {return num === 2; },那么 find 的返回值应为 2。
find([1, 3, 5, 8, 9, 10], function(num) { return num % 2 === 0; }) 应该返回 8。
find([1, 3, 5, 9], function(num) { return num % 2 === 0; }) 应该返回 undefined。

//思路:这题相比前面的简单很多了。注意:用break强制跳出循环。
function find(arr, func) {
  var num = 0;
  for(var i=0;i<arr.length;i++){
    if(func(arr[i])===true){
      num=arr[i];
      break;
    }
  }
  if(num===0){
    return undefined;  
  }
  return num;
}
find([1, 2, 3, 4], function(num){ return num % 2 === 0; });
18、数组取值算法(Drop it)

队友该卖就卖,千万别舍不得。
当你的队伍被敌人包围时,你选择拯救谁、抛弃谁非常重要,如果选择错误就会造成团灭。
如果是AD或AP,优先拯救。
因为AD和AP是队伍输出的核心。
其次应该拯救打野。
因为打野死了对面就可以无所顾虑地打龙。
最后才是辅助或上单。
因为辅助和上单都是肉,死了也不会对团队造成毁灭性影响,该卖就卖。
但真实中的团战远比这要复杂,你的队伍很可能会被敌人分割成2个或3个部分。
当你救了一个重要的人时,很可能其他队友也会因此获救。
举个例子:
辅助和AD经常是在一起的,打野和中单在一起,上单经常一个人。
你救了AD,辅助也经常因此获救。
让我们来丢弃数组(arr)的元素,从左边开始,直到回调函数return true就停止。
第二个参数,func,是一个函数。用来测试数组的第一个元素,如果返回fasle,就从数组中抛出该元素(注意:此时数组已被改变),继续测试数组的第一个元素,如果返回fasle,继续抛出,直到返回true。
最后返回数组的剩余部分,如果没有剩余,就返回一个空数组。

//思路:遍历这个数组,然后用func判断返回的是布尔值,根据布尔值插入一个占位的字符串‘false’。待循环结束之后用filter过滤掉就行了。
function drop(arr, func) {
  // Drop them elements.
  for(var i=0;i<arr.length;i++){
    if(func(arr[i])===false){
      arr.splice(i,1,'false');
    }else{
      break;
    }
  }
  arr=arr.filter(function(a){
    return a!='false';
  });
  console.log(arr);
  return arr;
}
drop([1, 2, 3], function(n) {return n < 3; });
19、数组简化算法(Steamroller)

对嵌套的数组进行扁平化处理。你必须考虑到不同层级的嵌套。
steamroller([[["a"]], [["b"]]]) 应该返回 ["a", "b"]。
steamroller([1, [2], [3, [[4]]]]) 应该返回 [1, 2, 3, 4]。
steamroller([1, [], [3, [[4]]]]) 应该返回 [1, 3, 4]。
steamroller([1, {}, [3, [[4]]]]) 应该返回 [1, {}, 3, 4]。

//思路:还是用递归的办法。判断数组用isArray。算是递归方法的简单实例。
function steamroller(arr) {
  // I'm a steamroller, baby
  var newArr=[];
  function fun(a){
    for(var j=0;j<a.length;j++){
      if(Array.isArray(a[j])===true){
        fun(a[j]);    
      }else{
        newArr.push(a[j]);      
      }
    }
    return newArr;
  }
  fun(arr); 
  console.log(newArr);
  return newArr;
}
steamroller([1, [2], [3, [[4]]]]);
20、二进制转字符算法(Binary Agents)

传入二进制字符串,翻译成英语句子并返回。
二进制字符串是以空格分隔的。

function binaryAgent(str) {
 var arr=str.split(' ');
  var arr10=[];
  var newStr='';
  for(var i=0;i<arr.length;i++){
    arr10.push(parseInt(arr[i],2));
  }
  for(var j=0;j<arr10.length;j++){
    newStr+=String.fromCharCode(arr10[j]);
  }
  return newStr;
}
binaryAgent("01000001 01110010 01100101 01101110 00100111 01110100 00100000 01100010 01101111 01101110 01100110 01101001 01110010 01100101 01110011 00100000 01100110 01110101 01101110 00100001 00111111");
21、数组元素判断算法(Everything Be True)

所有的东西都是真的!
完善编辑器中的every函数,如果集合(collection)中的所有对象都存在对应的属性(pre),并且属性(pre)对应的值为真。函数返回ture。反之,返回false。
记住:你只能通过中括号来访问对象的变量属性(pre)。
提示:你可以有多种实现方式,最简洁的方式莫过于Array.prototype.every()

//思路:这个看起来很复杂,其实只要做个json的for-in循环,就可以实现。
function every(collection, pre) {
  var i='';
  var bCheck=true;//假设为真
  for(i in collection){
    if(!collection[i][pre]){
      bCheck=false;
    }
  }
  return bCheck;
}
every([{"user": "Tinky-Winky", "sex": "male"}, {"user": "Dipsy", "sex": "male"}, {"user": "Laa-Laa", "sex": "female"}, {"user": "Po", "sex": "female"}], "sex");

22、函数迭代可选参数算法(Arguments Optional)

创建一个计算两个参数之和的 function。如果只有一个参数,则返回一个 function,该 function 请求一个参数然后返回求和的结果。
例如,add(2, 3) 应该返回 5,而 add(2) 应该返回一个 function。
调用这个有一个参数的返回的 function,返回求和的结果:
var sumTwoAnd = add(2);
sumTwoAnd(3) 返回 5。
如果两个参数都不是有效的数字,则返回 undefined。
对你有帮助的资源:
Closures 闭包

/*思路:这个其实就是call()方法。jquery的链式操作就是这样实现的。
函数链式操作原理:返回函数本身。当然也可以返回一个别的函数。
function show(str){
    alert(str);
    return show;//关键
}
show('abc')('bcd');
上述代码将弹出两个框,abc,和bcd。只要你想,可以无限地写下去。
你可以返回一个函数,那么下次操作时,得到的就是这个函数。*/
function add(x) {
 if (arguments.length === 1 && typeof x === "number") {
   return function (y) { 
     if (typeof y === "number"){
       return x + y;
     }  
   }; 
 }else {
    if (typeof x !== "number"|| typeof arguments[1] !== "number") {
      return undefined;
    }
    return arguments[0] + arguments[1];
  } 
} 
add(2,3);

写在最后:有更好的方法欢迎交流(__)

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

推荐阅读更多精彩内容