JS常见的算法

1 判断一个单词是否是回文?

回文是指把相同的词汇或句子,在下文中调换位置或颠倒过来,产生首尾回环的情趣,叫做回文,也叫回环。比如 mamam redivider .
很多人拿到这样的题目非常容易想到用for 将字符串颠倒字母顺序然后匹配就行了。其实重要的考察的就是对于reverse的实现。其实我们可以利用现成的函数,将字符串转换成数组,这个思路很重要,我们可以拥有更多的自由度去进行字符串的一些操作。

function checkPalindrom(str) {  
    return str == str.split('').reverse().join('');
}

2 去掉一组整型数组重复的值

比如 输入: [1,13,24,11,11,14,1,2], 输出: [1,13,24,11,14,2] ,需要去掉重复的11 和 1 这两个元素。

主要考察个人对Object的使用,利用key来进行筛选。

let unique = function(arr) {  
  let hashTable = {};
  let data = [];
  for(let i=0,l=arr.length;i<l;i++) {
    if(!hashTable[arr[i]]) {
      hashTable[arr[i]] = true;
      data.push(arr[i]);
    }
  }
  return data
}
module.exports = unique;
//ES6方法:
function unique(arr) {
  return Array.from(new Set(arr))
}
module.exports = unique;

3 统计一个字符串出现最多的字母

给出一段英文连续的英文字符窜,找出重复出现次数最多的字母
比如: 输入:afjghdfraaaasdenas 输出 : a
前面出现过去重的算法,这里需要是统计重复次数。

function findMaxDuplicateChar(str) {  
  if(str.length == 1) {
    return str;
  }
  let charObj = {};
  for(let i=0;i<str.length;i++) {
    if(!charObj[str.charAt(i)]) {
      charObj[str.charAt(i)] = 1;
    }else{
      charObj[str.charAt(i)] += 1;
    }
  }
  let maxChar = '',
      maxValue = 1;
  for(var k in charObj) {
    if(charObj[k] >= maxValue) {
      maxChar = k;
      maxValue = charObj[k];
    }
  }
  return maxChar;
}
module.exports = findMaxDuplicateChar;

    var str = "zhufengpeixunyangfanqihang";
    //把每一个字母出现的次数进行统计
    var obj = {};
    str.replace(/[a-z]/gi, function () {
        var a = arguments[0];
        if (obj[a] >= 1) {
            obj[a] = obj[a] + 1;
        } else {
            obj[a] = 1;
        }
    });
    //用假设法,获取最多出现的次数
    var max = 0;
    for (var key in obj) {
        var cur = obj[key];
        max = cur > max ? cur : max;
    }
    //把最多出现次数的字母放到数组中
    var ary = [];
    for (var key in obj) {
        if (obj[key] === max) {
            ary.push(key);
        }
    }
    console.log(ary);
    console.log(max);

4 排序算法

如果说到算法题目的话,应该大多都是比较开放的题目,不限定算法的实现,但是一定要求掌握其中的几种,所以冒泡排序,这种较为基础并且便于理解记忆的算法一定需要熟记于心。冒泡排序算法就是依次比较大小,小的的大的进行位置上的交换。

function bubbleSort(arr) {  
    for(let i = 0,l=arr.length;i<l-1;i++) {
        for(let j = i+1;j<l;j++) { 
          if(arr[i]>arr[j]) {
                let tem = arr[i];
                arr[i] = arr[j];
                arr[j] = tem;
            }
        }
    }
    return arr;
}
module.exports = bubbleSort;

除了冒泡排序外,其实还有很多诸如 (插入排序、快速排序、希尔排序)等。每一种排序算法都有各自的特点。全部掌握也不需要,但是心底一定要熟悉几种算法。 比如快速排序,其效率很高,而其基本原理如图(来自wiki):

算法参考某个元素值,将小于它的值,放到左数组中,大于它的值的元素就放到右数组中,然后递归进行上一次左右数组的操作,返回合并的数组就是已经排好顺序的数组了。

//快速排序
function quickSort(arr) {
    if(arr.length<=1) {
        return arr;
    }
    let leftArr = [];
    let rightArr = [];
    let q = arr[0];
    for(let i = 1,l=arr.length; i<l; i++) {
        if(arr[i]>q) {
            rightArr.push(arr[i]);
        }else{
            leftArr.push(arr[i]);
        }
    }
    return [].concat(quickSort(leftArr),[q],quickSort(rightArr));
}
module.exports = quickSort;
//插入排序
 function insertSort(arr) {
        var len =arr.length;
        for (var i=1;i<len; i++) {
            var temp=arr[i];
            var j=i-1;//默认已排序的元素
            while (j>=0 && arr[j]>temp) {  //在已排序好的队列中从后向前扫描
                    arr[j+1]=arr[j]; //已排序的元素大于新元素,将该元素移到一下个位置
                    j--;
                }
            arr[j+1]=temp;
        }
        return arr
}
module.exports = insertSort;
//希尔排序
function shellSort(arr) {
  for(let gap = Math.floor(arr.length/2); gap > 0; gap = Math.floor(gap/2)) {
    // 内层循环与插入排序的写法基本一致,只是每次移动的步长变为 gap
    for(let i = gap; i < arr.length; i++) {
      let j = i;
      let temp = arr[j];
      for(; j> 0; j -= gap) {
        if(temp >= arr[j-gap]) {
          break;
        }
        arr[j] = arr[j-gap];
      }
      arr[j] = temp;
    }
  }
  return arr;
}
module.exports = shellSort;

几种排序算法以及性能比较

5 不借助临时变量,进行两个整数的交换

举例:输入 a = 2, b = 4 输出 a = 4, b =2
这种问题非常巧妙,需要大家跳出惯有的思维,利用 a , b进行置换。
主要是利用 + - 去进行运算,类似 a = a + ( b - a) 实际上等同于最后 的 a = b;

function swap(a , b) {  
  b = b - a;
  a = a + b;
  b = a - b;
  return [a,b];
}
module.exports = swap;

7 找出下列正数组的最大差值

比如: 输入 [10,5,11,7,8,9] 输出 6
这是通过一道题目去测试对于基本的数组的最大值的查找,很明显我们知道,最大差值肯定是一个数组中最大值与最小值的差。

function getMaxProfit(arr) {
    var minPrice = arr[0];
    var maxProfit = 0;
    for (var i = 0; i < arr.length; i++) {
        var currentPrice = arr[i];
        minPrice = Math.min(minPrice, currentPrice);
        var potentialProfit = currentPrice - minPrice;
        maxProfit = Math.max(maxProfit, potentialProfit);
    }
    return maxProfit;
}
function getMaxProfit(arr){
  var min = arr[0],
      max = arr[0];
  for(var i = 0; i < arr.length; i++){
    if(arr[i] < min) min = arr[i];
    if(arr[i] > max) max = arr[i];
  }
  return max - min;
}

8 随机生成指定长度的字符串

实现一个算法,随机生成指制定长度的字符窜。
比如:给定 长度 8 输出 4ldkfg9j

function randomString(n) {  
  let str = 'abcdefghijklmnopqrstuvwxyz9876543210';
  let tmp = '',
      i = 0,
      l = str.length;
  for (i = 0; i < n; i++) {
    tmp += str.charAt(Math.floor(Math.random() * l));
  }
  return tmp;
}
module.exports = randomString;

9 如何将浮点数左边的数每三位添加逗号

//var num='1200000.11';
function numberFormat(num){
    return num && num.toString().replace(/(\d)(?=(\d{3})+\.)/g,function($1,$2){
        return $2 + ',';
    })
}
module.exports = numberFormat;

10 编写一个函数fn(Number n),将数字转为大写输出,如输入123,输出一百二十三

function fn(n) {
    if (!/^([1-9]\d*)/.test(n)) {
        return '非法数据';
    }
    var unit = '千百十亿千百十万千百十个';
    if (n.length > unit.length) {
        return '数据过长';
    }
    var newStr = '';
    var nlength = n.length;
    unit = unit.substr(unit.length - nlength);
    for (var i = 0; i < nlength; i++) {
        newStr += '零一二三四五六七八九'.charAt(n[i]) + unit.charAt(i);
    }
    newStr = newStr.substr(0, newStr.length - 1);
    newStr = newStr.replace(/零(千|百|十)/g, '零').replace(/(零)+/g, '零').replace(/零(亿|万)/g, '$1');
    return newStr;
}
console.log(fn('205402002103'));

11 不确定数量的数组遍历组合算法

好吧,解释下这题。这题在现实中确实会用到。尤其是做商城网站时,sku的算法真的经常会遇到。
这题的意思就是说。相当于说[1,2,3],[4,5]。。。。的不确定个数的数组进行遍历组合,组成[[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]]这样。然后数组越多,组出来就肯定越多。
那怎么做的,我上网查了一些相关算法都没找到好的,然后我就自己写。可能还是会有点毛病,大家将就看。
有写的更好的欢迎评论教我一下。

function group(arr, re) {
    if (arr.length <= 0) {
        return re;
    }
    if (!re) {
        var arr = arr.slice();
        var re = arr.shift();
        return group(arr, re);
    } else {
        var now = arr.shift();
        var newre = [];
        for (var j = 0; j < now.length; j++) {
            for (var k = 0; k < re.length; k++) {
                var temp = [];
                if (re[k] instanceof Array) {
                    temp = re[k];
                } else {
                    temp.push(re[k]);
                }
                newre.push(temp.concat(now[j]));
            }
        }
        return group(arr, newre);
    }
}
var arr = [['a', 'b', 'c'], ['e', 'd', 'f'], ['h', 'i'], ['j', 'k', 'l', 'm']];
// var arr = [['a','b','c'],['e','d','f'],['h','i']];
// console.log(arr);
var result = group(arr);
console.log(result);

12 翻转字符串

思路一:反向遍历字符串

function reverseString(str){
  var tmp = '';
  for(var i=str.length-1; i>=0; i--)
    tmp += str[i];
  return tmp
}

思路二:转化成array操作

function reverseString(str){
  var arr = str.split("");
  var i = 0,j = arr.length-1;
  while(i<j){
    tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
    i++;
    j--;
  }
  return arr.join("");
}

13 猴子第一天摘下若干个桃子,当即吃掉一半,不过瘾,又多吃了一个,第二天早上又将剩下的桃子吃掉一半,又多吃了一个,以后每天早上都吃掉剩下桃子的一半零一个,到第十天早上的时候,发现只剩下一个桃子了,问猴子第一天摘下多少个桃子?

function peach(day){
     var sum=1;
    for(var i=0;i<day-1;i++){
     sum=(sum+1)*2;
    }
    return sum;
} 
console.log(peach(10));

14 输入一个字符串,打印该字符串中字符的所有排列(这里的思路是递归)

//这里重复的也列出来了有兴趣的可以再写下去重
 var perm = function(s) {
    var result = [];
    if (s.length <= 1) {
      return [s];
    } else {
      for (var i = 0; i < s.length; i++) {
        var c = s[i];
        var newStr = s.slice(0, i) + s.slice(i + 1, s.length);
        var l = perm(newStr);
           
        for (var j = 0; j < l.length; j++) {
          var tmp = c + l[j];
          result.push(tmp);
        }
      }
    }
    return result;
  };
console.log(perm('121'));

15 二分法查找 js 算法

二分法查找算法:
采用二分法查找时,数据需是排好序的。
主要思想是:(设查找的数组区间为array[s, e])
(1)确定该区间的中间位置m
(2)将查找的值T与array[m]比较,若相等,查找成功返回此位置;否则确定新的查找区域,继续二分查找。
区域确定如下:
这里设array从小到大排列,
array[m]>T由数组的有序性可知array[m,……,e]>T;
故新的区间为array[s,……,m-1],
类似上面查找区间array[s,……,m-1]。
每一次查找与中间值比较,判断是否查找成功,不成功当前查找区间缩小一半,循环查找,即可。
时间复杂度:O(log2n)。

let arr = [0, 1, 2, 4, 5, 6, 7, 8];
let arr2 = [88, 77, 66, 55, 44, 33, 22, 11];
BinarySearch(arr2, 77);
BinarySearch(arr, 2);
function BinarySearch(arr, target) {
    let s = 0;
    let e = arr.length - 1;
    let m = Math.floor((s + e) / 2);
    let sortTag = arr[s] <= arr[e];//确定排序顺序

    while (s < e && arr[m] !== target) {
        if (arr[m] > target) {
            sortTag && (e = m - 1);
            !sortTag && (s = m + 1);
        } else {
            !sortTag && (e = m - 1);
            sortTag && (s = m + 1);
        }
        m = Math.floor((s + e) / 2);
    }

    if (arr[m] == target) {
        console.log('找到了,位置%s', m);
        return m;
    } else {
        console.log('没找到');
        return -1;
    }
}

16、斐波那契数列

斐波那契数列的排列是:1,1,2,3,5,8,13,21,34,55,89,144……

function getFib(n){
   let fibarr =[];
   let  i=0;
   while(i<n) {
     if (i<=1){
       fibarr.push(1);
      } else{
     fibarr.push(fibarr[i-1]+fibarr[i-2])
    } 
    i++;
  }
  return fibarr;
}
console.log(getFib(8));

17、找到提供的句子中最长的单词,并计算它的长度。

注意:函数的返回值应该是一个数字。

function findLongestWord(str) {
//转化成数组
  var astr=str.split( " " ); 
//对数组中每个元素的字符串长度进行比较,按照字符串长度由大至小排列数组顺序。
  var bstr=astr.sort(function(a,b){
    return b.length-a.length;
  });
//取出数组中第一个元素(也就是最大长度的字符串)
  var lenMax= bstr[0].length;
//返回长度值
  return lenMax;
}

findLongestWord("The quick brown fox jumped over the lazy dog");
//结果:6

18、确保字符串的每个单词首字母都大写,其余部分小写。

function titleCase(str) {
  var astr=str.toLowerCase().split(" ");
  for(var i=0 ; i<astr.length; i++){
    astr[i]=astr[i][0].toUpperCase()+astr[i].substring(1,astr[i].length);
  }
  var string=astr.join(" ");
  return string;
}
titleCase("I'm a little tea pot");

//结果:I'm A Little Tea Pot

19、如果给定的字符串是回文,返回true,反之,返回false。

// 如果一个字符串忽略标点符号、大小写和空格,正着读和反着读一模一样,那么这个字符串就是palindrome(回文)。
// 注意需要去掉字符串多余的标点符号和空格,然后把字符串转化成小写来验证此字符串是否为回文。

//方式一:
function palindrome(str) {
  astr=str.replace(/[^0-9A-Za-z]/g,'').toLowerCase();
  bstr=astr.split("").reverse().join("");
  if(astr===bstr){
    return true;
  }else{
    return false;
  }
}
var is_true=palindrome("eye");
console.log(is_true);

//方式二:
//正则表达式还可以是:
var str="eye";
astr=str.replace(/[\ |\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\||\\|\[|\]|\{|\}|\;|\:|\"|\'|\,|\<|\.|\>|\/|\?]/g,"").toLowerCase();
console.log(astr);

20、右边大数组中包含了4个小数组,分别找到每个小数组中的最大值,然后把它们串联起来,形成一个新数组。

function largestOfFour(arr) {
  var newArr=[];
  for(i=0;i<arr.length;i++){
    arr[i].sort(function(a,b){
      return b-a;
    });
    
    newArr.push(arr[i][0]);
  }
    return newArr;

}
largestOfFour([[4, 5, 1, 3], [13, 27, 18, 26], [32, 35, 37, 39], [1000, 1001, 857, 1]]);

21、检查一个字符串(str)是否以指定的字符串(target)结尾。

如果是,返回true;如果不是,返回false。

function confirmEnding(str, target) {
  var len=target.length;
  var astr=str.substr(-len);
  if(astr===target){
    return true;
  }else{
    return false;
  }
}
confirmEnding("Bastian", "n");

//语法:substr()方法返回字符串中从指定位置开始到指定长度的子字符串
  str.slice(beginSlice[,endSlice]);

22、截断一个字符串!

如果字符串的长度比指定的参数num长,则把多余的部分用...来表示。

切记,插入到字符串尾部的三个点号也会计入字符串的长度。

但是,如果指定的参数num小于或等于3,则添加的三个点号不会计入字符串的长度。
function truncate(str, num) {
  var len=str.length;
  var astr=str.slice(0,num-3);
  var bstr=str.slice(0,num);
  if(len>num){
    if(num<=3){
      return bstr+'...';
    }else{
      return astr+'...';
    }
  }else{
    return str;
  }
}

truncate("A-tisket a-tasket A green and yellow basket", 11);
//结果:A-tisket...
//运用的语法:
slice()
提取字符串的一部分,并返回这个新的字符串;
  str.slice(beginSlice[,endSlice]);


23、把一个数组arr按照指定的数组大小size分割成若干个数组块。

例如:chunk([1,2,3,4],2)=[[1,2],[3,4]];
chunk([1,2,3,4,5],2)=[[1,2],[3,4],[5]];

function chunk(arr, size) {
  var newarr=[];
  for(var i=0;i<arr.length;i+=size) {
    var brr=arr.slice(i,i+size);
    newarr.push(brr);
  }
  return newarr;
}

chunk(["a", "b", "c", "d"], 2);

24、如果数组第一个字符串元素包含了第二个字符串元素的所有字符,函数返回true。

举例,["hello", "Hello"]应该返回true,因为在忽略大小写的情况下,第二个字符串的所有字符都可以在第一个字符串找到。
function mutation(arr) {
  var astr=arr[0].toLowerCase();
  var bstr=arr[1].toLowerCase();
  for(var i=0;i<bstr.length;i++){
    if(astr.indexOf(bstr[i]) == -1){
      return false;
    }else{
      return true;
    }
  }
}

mutation(["hello", "hey"]);
//结果:true

//
indexOf()
 方法返回指定值在字符串对象中首次出现的位置。从 
fromIndex
 位置开始查找,如果不存在,则返回 -1。
stringObject.indexOf(searchvalue,fromindex)

参数
searchValue:一个字符串表示被查找的值。
fromIndex:可选 表示调用该方法的字符串中开始查找的位置。可以是任意整数。默认值为 0。如果 fromIndex < 0 则查找整个字符串(如同传进了 0)。如果 fromIndex >= str.length,则该方法返回 -1,除非被查找的字符串是一个空字符串,此时返回 str.length。
区分大小写 indexOf 方法区分大小写。例如,下面的表达式返回 -1:
"Blue Whale".indexOf("blue") // returns -1
检测是否存在某字符串 当检测某个字符串是否存在于另一个字符串中时,可使用下面的方法:
"Blue Whale".indexOf("Blue") !== -1; // true
"Blue Whale".indexOf("Bloe") !== -1; // false

25、删除数组中的所有假值。

function bouncer(arr) {
  function isBigEnough(element) {
    if(element!==false || element!==null || element!==0 || element!=="" || element!==undefined || element!==NaN){
      return element;
    }
  }
  var filtered =arr.filter(isBigEnough);
  return filtered;
}

bouncer([7, "ate", "", false, 9]);

filter()
方法使用指定的函数测试所有元素,并创建一个包含所有通过测试的元素的新数组
var new_arrary = arr.filter(callback[, thisArg])

26、找到你的另一半

都说优秀的程序员擅长面向对象编程,但却经常找不到另一半,这是为什么呢?因为你总是把自己局限成为一个程序员,没有打开自己的思维。

这是一个社群的时代啊,在这里你应该找到与你有相同价值观但又互补的另一半。

譬如:你编程能力强,估值11分,如果以20分为最佳情侣来计算,你应该找一个设计能力强,估值为9分的女生。

那么当你遇到一个设计能力为9分的女生,千万别犹豫,大胆去表白。千万别以为后面的瓜比前面的甜哦。

举个例子:有一个能力数组[7,9,11,13,15],按照最佳组合值为20来计算,只有7+13和9+11两种组合。而7在数组的索引为0,13在数组的索引为3,9在数组的索引为1,11在数组的索引为2。

所以我们说函数:pairwise([7,9,11,13,15],20) 的返回值应该是0+3+1+2的和,即6。

pairwise([1, 4, 2, 3, 0, 5], 7) 应该返回 11.
pairwise([1, 3, 2, 4], 4) 应该返回 1.
pairwise([1, 1, 1], 2) 应该返回 1.
pairwise([0, 0, 0, 0, 1, 1], 1) 应该返回 10.
pairwise([], 100) 应该返回 0.

function pairwise(arr, arg) {
  var sum=0;
  for(var i=0;i<arr.length-1;i++)
    {        
      for(var j=i+1;j<arr.length;j++){
        if(arr[i]+arr[j]===arg){
           sum+=i+j;
           arr[j]=-50;
           break;
        }
      }

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