笔试

232 阅读6分钟

给定一个字符串,计算这个字符串中有多少个回文子串

标题含义:

用例:输入一个字符串S 例如“aabcb”
符合条件的字符串有"a",“a”,“aa”,“b”,“c”,“b”,“bcb” 所以答案:7

let count = 0;
function calculate(param){
    let str = param;
    let arr = [];

    for(let i=0;i<str.length;i++){
        arr.push(str.charAt(i));
    }
    console.log('ggg',arr);
    for(let i=0;i<arr.length;i++){
        for(let j=i+1;j<arr.length;j++){
            //找首尾相同的字符串
            if(arr[i]==arr[j]){
                diGui(i,j,arr);
            }
        }
    }
    console.log('count: ',count);
}
function diGui(i, j, arr){
    if(i-j==1||i==j){
        count+=1;
    }else{
        //比较前后字符是否一一对应
        if(arr[i]==arr[j]){
            diGui(i+1,j-1,arr);
        }else{//不对应结束该方法
            return;
        }
    }
}

关于排序

冒泡:

思路:

第一层循环决定要循环几轮,每一轮都会在最后比较出一个最大的数(升序)。那么第二层循环就负责每相邻的两个数的比较,交换位置。第二层循环有一个-i的操作,是因为每一轮都会选出一个最大的数,所以被选出的数据不被用在下一轮的循环中比较,所以要减去。

// 编写方法,实现冒泡
    var arr = [6,2,4,1];
    //外层循环,控制趟数,每一次找到一个最大值
    for (var i = 0; i < arr.length - 1; i++) {
        // 内层循环,控制比较的次数,并且判断两个数的大小
        for (var j = 0; j < arr.length - 1 - i; j++) {
            // 白话解释:如果前面的数大,放到后面(当然是从小到大的冒泡排序)
            if (arr[j] > arr[j + 1]) {
                var temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }

    }

快速排序:

第一种:

阮一峰思路:

从数组中找出一个数,作为标准。凡是大于这个标杆的数都放到一个叫right的数组里面去。凡是小于这个标杆的数都放到一个left的数组里面去。再把左边的数组和这个标杆数以及右边的数组用concat连接起来,最后再将left和right数组递归用此方法排序。

function quickSort(arr) {
   /*
    * 创建len保存数组的长度,每次获取数组的长度都要实时查询不利于性能;
    * index作为保存取到的中间值;
    * pivot保存比较参照物;
    * left、right作为子数组的容器;
    */
    var len = arr.length,
        index,
        pivot,
        left=[],
        right=[];
    // 如果数组只有一位,就直接返回数组,递归的终止条件;
    if (len <= 1) {
        return arr;
    }
    //获取中间值的索引,使用Math.floor向下取整;
    index = Math.floor(len / 2);

    // 使用splice截取中间值,第一个参数为截取的索引,第二个参数为截取的长度;splice返回的是一个数组
    // 如果此处使用pivot=arr[index]; 那么将会出现无限递归的错误;
    // splice影响原数组,原数组长度减一;
    pivot = arr.splice(index, 1)[0];
    len -= 1;

    // 小于arr[pivot]的存到left数组里,大于arr[pivot]的存到right数组;
    for (var i = 0; i < len; i++) {
        if (pivot > arr[i]) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    // 不断把分割的左右子数组传入quickSort,直到分割的只有一位直接返回子数组本身,递归终止;

    // 把每次分割的数组一层一层的用concat连接起来;
    // 每一层left里的元素都小于对应的pivot,right里的元素都大于对应的pivot;
    return quickSort(left).concat(pivot, quickSort(right));
}

第二种:

常规思路:

从数组中取出一个数作为标准(暂时用第一个数作为标准)。从左往右开始比较的下标作为i,从右往左开始比较的下标叫j。从左往右依次把数组里面的数和标准数作比较,如果这个数小于标准数,那就把 i 加一,直到遇到大于标准的数。同理,从右往左依次把数组里面的数和标准数作比较,如果这个数大于标准数,那就把 j 减一,直到遇到小于标准的数。然后把这两个找出来的数交换位置。依次找完这些数交换位置。直到 i > j。这时第一个标准数还要换到中间去(即和i的位置的数交换),之后在递归标准数左边和右边的数组。

/**
题目:快速排序算法
思路:两个哨兵,i,j,j从右边找比基数小的,i从左边找比基数大的,然后交换两个目标元素的位置,
直到i=j,然后交换i和基数的位置,递归处理。
**/
function quick_sort(arr,from,to){
	var i = from; //哨兵i
	var j = to; //哨兵j
	var key = arr[from]; //标准值
	if(from >= to){ //如果数组只有一个元素
	   return;
	}
	while(i < j){
		while(arr[j] > key && i < j){ //从右边向左找第一个比key小的数,找到或者两个哨兵相碰,跳出循环
			j--;
		}
                /**
                从左边向右找第一个比key大的数,找到或者两个哨兵相碰,跳出循环,
                这里的=号保证在本轮循环结束前,key的位置不变,
                否则的话跳出循环,交换i和from的位置的时候,from位置的上元素有可能不是key
                **/
		while(arr[i] <= key && i < j){  
			i++;
		}
		/**
		  代码执行道这里,1、两个哨兵到找到了目标值。2、j哨兵找到了目标值。
                   3、两个哨兵都没找到(key是当前数组最小值)
		**/
		if(i < j){ //交换两个元素的位置
			var temp = arr[i];
			arr[i] = arr[j];
			arr[j] = temp;

		}
	}
	arr[from] = arr[i] //
	arr[i] = key;
        quick_sort(arr,from,i-1);
	quick_sort(arr,i+1,to);
}

var arr = [3,3,-5,6,0,2,-1,-1,3];
console.log(arr);
quick_sort(arr,0,arr.length-1);
console.log(arr);

选择排序:

方法1:

思路:

第一层循环依次取出数组中的元素的下标作为最小值,然后最小值后面的元素和最小值比较,如果后面元素的值小于最小值,就把元素的下标重新赋值给最小值。然后后面的元素又和最小值比较,如果后面元素小于最小值,又重新赋值,如此循环。就可以找出第一层循环的真实最小值,然后把这个得到的真实最小值和最开始设置的最小值交换。就可以把真实最小值放到第一个,如此循环,就可以排序

function selectionSort(arr) {
    var len = arr.length,
        min,
        temp;
    /**
    第一层循环依次取出数组中的每一个值,留作之后和这轮循环找出的最小值交换
    **/
    for (var i = 0; i < len - 1; ++i) {
        // 把每一个值的下标先赋值给最小值
        min = i;
        // 后面的每一个元素都要和最小值比较
        for (var j = i + 1; j < len; ++j) {
        // 如果这个值小于最小值就把它的下标重新赋值给最小值,然后后面每一个都要和所谓的
        // 最小值比较,一个比一个小,那最后一个肯定是此次循环的最小值
            if (arr[j] < arr[min]) {
                min = j;
            }
        }
        // 最后此次循环的最小值和第一层循环的数组的值交换
        temp = arr[min];
        arr[min] = arr[i];
        arr[i] = temp;
    }

    return arr;
}

方法2:

思路:

两层循环,第一层是对数组中的每一个数进行比较。第二层循环是第一层循环每一个数后面的所有数与第一层循环的每个数进行比较。只要第一层循环的数大于第二层循环的数(即之后的每一个数),就让他们两个交换。这样,第一层循环的第一个数就会是最小的,依次类推,就会得到从小到大的排列:

function fnSort(arr){
    var t;
    for (var i = 0;i<arr.length-1;i++) {
        for (var j = i+1;j<arr.length;j++) {
            // 举例,拿第一层循环第一个来说,只要发现后面的数比它小,就交换,交换完后,又拿
            //被交换的数继续和下面的数比较,这样,就能保证循环完后最后他是最小的
            if(arr[i]>arr[j]){
                t = arr[i];
                arr[i] = arr[j];
                arr[j] = t;
            }
        }
    }
    return arr;
}
console.log(fnSort([2,7,1,55,58]))

插入排序:

思路:

就像扑克牌一样,每摸一次牌就要把它和之前的牌排好序,下次摸的牌要在手里已经拍好序的牌里面找到自己的位置。例如之前两次摸的牌分别是3和5和6,那如果第四次摸的牌是4,就要插在3和5的中间: 第一层循环是对数组中的每一个数都要拿出来和前面排好序的元素比较。第二层循环是让数组中被比较并且符合条件的元素罗动位置,最后让待比较的元素放到对应的位置上。

function insertSort(arr) {
  let length = arr.length;
  for(let i = 1; i < length; i++) {
    let temp = arr[i];
    let j = i;
    //让待排序的元素依次和前面的元素比较
    for(; j > 0; j--) {
      if(temp >= arr[j-1]) {
        break; // 当前考察的数大于前一个数,证明有序(因为之前的数都是排序好了的),退出循环
      }
       // 将前一个数复制到后一个数上,就像排队,被插入后,前面被插队的人都要往后走一步      
       arr[j] = arr[j-1]; 
    }
    arr[j] = temp;  // 找到考察的数应处于的位置
  }
  return arr;
}
let arr = [2,5,10,7,10,32,90,9,11,1,0,10]
console.log(insertSort(arr));

二分法排序:

思路:

和插入排序是差不多的思路,还是从默认已经排序好的数组里面去找位置,然后放入待排序的元素,从而达到排序。只不过是从已经排序好的数组里面去通过二分法找:找出已经排序好的数组中的中间那个数,然后用待排序的数和中间的数比较,如果待排序的数大于中间数,那就在中间数的右边部分去找位置。如果待排序的数小于中间数,那就在中间数的左边部分去找位置。找到位置后,从这个位置开始,一直到待排序的位置,每个元素都要往后挪动位置,然后把待排序元素放到该位置,结束。

function binaryInsertSort(arr) {
        var len =arr.length;
        for (var i=1;i<len; i++) {
            var key=arr[i], left=0,
                // 左边的元素最开始从0开始,右边的元素肯定是从待排序的元素的前一个开始算
                right=i-1;
            while(left<=right){       //在已排序的元素中二分查找第一个比它大的值
              var mid= parseInt((left+right)/2); //二分查找的中间值
              if(key<arr[mid]){ //当前值比中间值小  则在左边的子数组中继续寻找   
                right = mid-1;
              }else{
                left=mid+1;//当前值比中间值大   在右边的子数组继续寻找
              }
            } 
            // 从插入点开始,一直到带排序的元素的前面一个,每个元素都往后挪动一个位置             
            for(var j=i-1;j>=left;j--){
              arr[j+1]=arr[j];
            }
            // 再把待排序元素放到指定位置
            arr[left]=key;
          }
        return arr;
     }

关于去重:

用splice改变原数组:

优点:该方法可以顾虑到重复的 String、Boolean、 Number、undefined、null,返回的是去重后的原数组。

 缺点:不能过滤掉 NaN(因为NaN不等于NaN)、Object

let arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN','NaN', 0, 0, 'a', 'a',{},{}];
    function distinct (arr) {
        for(let i = 0; i < arr.length; i++) {
            for(let j = i + 1; j < arr.length ; j++) {
                if(arr[i] === arr[j]) {
                    arr.splice(j, 1)
                    j--;
                }
            }
        }
    } 
    distinct(arr)
    console.log(arr)

includes去重 返回新数组:

优点是可以过滤重复的NaN了,但是返回的是个新数组,对比方法一二,该方法多消耗了一些存储空间。

  • 优点:该方法可以顾虑到重复的 StringBooleanNumberundefinednullNaN,返回的是去重后的新数组。
  • 缺点:不能过滤掉 Object
let arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN','NaN', 0, 0, 'a', 'a',{},{}];
    function distinct (arr) {
        let newArr = []
        for(let i = 0; i < arr.length; i++) {
            if(!newArr.includes(arr[i])) {
                newArr.push(arr[i])
            } 
        }
        return newArr
    }
    console.log(distinct(arr))

indexOf去重 返回新数组:

该方法类比includes,不能过滤掉NaN

  • 优点:该方法可以顾虑到重复的 StringBooleanNumberundefinednull,返回的是去重后的新数组。
  • 缺点:不能过滤掉 NaNObject
let arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN','NaN', 0, 0, 'a', 'a',{},{}];
    function distinct (arr) {
        let newArr = []
        for(let i = 0; i < arr.length; i++) {
            if(newArr.indexOf(arr[i]) < 0) {
                newArr.push(arr[i])
            } 
        }
        return newArr
    }

    console.log(distinct(arr)) 

对象数组去重:

var arr = [{
      key: '01',
      value: '乐乐'
   }, {
      key: '02',
      value: '博博'
   }, {
      key: '03',
      value: '淘淘'
   },{
      key: '01',
      value: '乐乐'
   }];

   // 利用对象访问属性的方法,判断对象中是否存在key
   var result = [];
   var obj = {};
   for(var i =0; i<arr.length; i++){
      if(!obj[arr[i].key]){
         result.push(arr[i]);
         obj[arr[i].key] = true;
      }
   }
   console.log(result);

关于字符串:

统计字符串中出现次数最多的字符串:

function strTest1(){
      var ss="aacdefgabaag";
      for(var i=0,len=0,temp="";i<ss.length;i++){
        var s=ss.substr(i,1);//循环取出每个字符
        var tt=ss.split(s);//分割开的数组长度-1就是这个字符出现次数
        if((tt.length-1)>len){// 只有下一个字符出现次数大于上一个的时候才重新赋值
            len=tt.length-1;
            temp=s+"出现最多次数为"+len;
        }
      }
      console.log(temp);
    }

每个字符出现的次数:

function strTest2(){
      var ss="aabbaac";
      var obj = {};//用对象key和值得方式比较
      var ssArr = ss.split("");

      for(var i=0;i<ssArr.length;i++){
        if(obj[ssArr[i]]){
          obj[ssArr[i]] = obj[ssArr[i]] + 1
        }else{
          obj[ssArr[i]] = 1
        }
      }
      console.log(ssArr,obj);
 }

或者:

    var str = 'ABCABC你好你好ののA';
    //用来存储不重复的字符串
    var newStr = '';
    //字符串去重  将不重复的字符串存储到一个新的字符串内

    //将每一个字符单独提出来
    for (var i = 0; i < str.length; i++) {
        //判断有没有在newStr中出现过 没有出现过 放到newStr内
        if (newStr.lastIndexOf(str[i]) == -1) {
            newStr += str[i];
        }
    }
    //  console.log(newStr);//ABC你好の
    for (var i = 0; i < newStr.length; i++) {
        var count = 0;
        for (var j = 0; j < str.length; j++) {
            //判断元素是否相等 如果相等 则次数加1
            if(newStr[i]==str[j]){
                count++;
            }
        }
        console.log(newStr[i],count);
    }