面试题中的算法题

查找单链表的中间节点:

法一》:遍历一遍整个链表,计算出链表的长度,进而遍历第二遍找出中间位置的数据

法二》:建立两个指针,一个指针一次遍历两个节点,另一个指针一次遍历一个节点,当快指针遍历到空节点时,慢指针指向的位置为链表的中间位置

算法实现:

 

  1. SListNode * FindMidNode(SListNode * phead)  
  2. {  
  3.     SListNode *fast = phead;  
  4.     SListNode *slow = phead;  
  5.     while (fast)  
  6.     {  
  7.         if (fast->next != NULL)  
  8.         {  
  9.             fast = fast->next->next;  
  10.          }  
  11.         else  
  12.         {  
  13.             break;  
  14.         }  
  15.         slow = slow->next;  
  16.     }  
  17.     return slow;  
  18. }  
  19.   
  20.   
  21.   
  22. 也可以这样写,更为简洁  
  23.     while (fast&&fast->next )  
  24.     {  
  25.         fast = fast->next->next;  
  26.         slow = slow->next;  
  27.     }  

 

 

 

找出两个字符串中的公共子串:

js中去除数组中重复元素的四种方法:

1、

function unique(arr) {
			var newarr = [];
			for (var i =0;i<arr.length;i++) {
				if (newarr.indexOf(arr[i])== -1) {
					newarr.push(arr[i]);
				}
			}
			return newarr;
		}
		var newarr = unique([2,2,2,3,3,3,4,4,4,5,5,5,0]);
		console.log(newarr);

2、

Array.prototype.unique = function () {
			var temp = {};
			var arr = [];
			for (var i = 0;i<this.length;i++) {
				if (!temp[this[i]]) {
					temp[this[i]]='abc';
					arr.push(this[i]);
				}
			}
			return arr;
		}

3、

  1.  Array.prototype.method3 = function(){  
  2.             //直接定义结果数组  
  3.             var arr[this[0]];  
  4.             for(var i = 1; i < this.length; i++){    //从数组第二项开始循环遍历此数组  
  5.                 //对元素进行判断:  
  6.                 //如果数组当前元素在此数组中第一次出现的位置不是i  
  7.                 //那么我们可以判断第i项元素是重复的,否则直接存入结果数组  
  8.                 if(this.indexOf(this[i]) == i){  
  9.                     arr.push(this[i]);  
  10.                 }  
  11.             }  
  12.             return arr;  
  13.           
  14.         }  

4、

function unique(arr) {
			var newarr = [];
			arr.sort();
			for (var i = 0;i<arr.length;i++) {
				if (arr[i] !== arr[i+1]) {
					newarr.push(arr[i]);
				}
			}
			return newarr;
		}
		var newarr = unique([2,2,2,3,3,3,4,4,4,5,5,5,0]);
		console.log(newarr);

 求两数只和:

解法一:暴力循环

function twoSum(arr, target) {
    for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[i] + arr[j] === target) {
                console.log(arr[i], arr[j])
            }
        }
    }
}

解法二:利用一个hasmap空间

 function twoSum(arr, target) {
    let temp = {}
    for(let i = 0; i < arr.length; i++) {
        temp[arr[i]] = i
        if (temp[target - arr[i]] && temp[target - arr[i]] !== i) {
            console.log(i, temp[target - arr[i]])
        }
    }
}

判断字符串是否是回文

法一:

function huiwen(str) {
			var str1 = '';
			for (var i = str.length - 1; i >= 0; i--) {
				str1 += str[i];
			}
			console.log(str1==str);
			// body...
		}
		var a = 'abccba';
		console.log(huiwen(a));

法二:

function huiwen(str) {
			var str1 = str.split('').reverse();//先将字符串变成数组,
//之后翻转数组,这两步一定要分开,split()方法和reverse()方法会改变原数组
			var str2 = str1.join('');//再将数组变成字符串,join()方法不会改变原数组
			if(str == str2){
				console.log('是回文');
			}else{
				console.log('不是回文');
			}
		}

判断数字是否是回文数字:

function huiwenNam(num) {
    if (num < 0) return false
    let arr = []
    while(num > 0) {
        arr.push(num % 10)
        num = (num - num % 10) / 10
    }
    for (let i = 0; i < arr.length / 2; i++) {
        if (arr[i] !== arr[arr.length - 1 - i]) {
            return false
        }
    }
    return true
}

求最大子数组之和

法一:

function sumMax(arr) {//求最大子数组之和o(n^2)
			var sumMax = 0;
			for (var i = 0;i < arr.length;i++) {
				var temp = 0;
				for (var j = i;j < arr.length;j++) {
					temp += arr[j];
					if (temp > sumMax) {
						sumMax = temp;
					}
				}
			}
			console.log(sumMax);
		}

法二:

function sumMax(arr) {//动态规划的方法,o(n)
			var sumMax = 0;
			var cur = 0;
			for (var i = 0;i < arr.length;i++) {
				cur += arr[i];
				if (cur < 0 ) {
					cur = 0;
				}
				if (cur > sumMax) {
					sumMax = cur;
				}
			}
			console.log(sumMax);
		}

将字符串短杠连接变成驼峰式命名 border-left-radius------------->borderLeftRadius

法一:

function change(str) {
			var newstr = str.split('-');
			for (var i = 1;i<newstr.length;i++) {
				var char = newstr[i];
				newstr[i] = char[0].toUpperCase()+char.slice(1);
			}
			return newstr.join('');
		}
		console.log(change('first-of-name'));

法二:

var str = 'first-of-name';
		str = str.replace(/-(\w)/g,function (match,$1) {
			return $1.toUpperCase();
		})
		console.log(str);

将字符串驼峰命名变成短杠 borderLeftRadius------------->border-left-radius

法一:

function change(str) {
			var arr = str.split('');
			for (var i = 0;i<arr.length;i++) {
				if (arr[i]>'A'&&arr[i]<'Z') {
					arr[i] = '-'+arr[i];
					arr[i] = arr[i].toLowerCase();
				}
			}
			return arr.join('');
		}
		console.log(change('borderLeftRadius'));

法二:

var str = 'borderLeftRadius';
		str = str.replace(/([A-Z])+/g,function (match,$1) {
			return $1 = '-' + $1.toLowerCase();
		})
		console.log(str);

寻找连续三项相同的字符串,判断里面有没有连续三位或者三位以上相同,如果有,返回下标数组比如:
  ‘aannnccddddss’ ------>[2,3,4,7,8,9,10]

var str = 'aaabbcccddeewffff';
		function same(str) {
			var i = 0;
			var j = 1;
			var result = [];
			while(i<str.length){
				if (str[i]!=str[j]) {
					if (j-i>=3) {
						for (var m = i;m<j;m++) {
							result.push(m);
						}
					}
					i = j;
				}
				j++;
			}
			return result;
		}
		console.log(same(str));

给数字加千分位符

var num = 12345678901;
		var str = num.toString();
		console.log(str);
		str = str.replace(/\B(?=(\d{3})+$)/g,'.');
		console.log(str);

法二:

function sangedian(num) {
			var str = String(num);
			var iNum = str.length%3;
			var arr = [];
			var temp = '';
			var preve = '';
			var iNow = 0;
			if (iNum!==0) {
				preve = str.slice(0,iNum);
				arr.push(preve);
			}
			str = str.slice(iNum);
			for (var i = 0;i<str.length;i++) {
				iNow++;
				temp += str[i];
				if (iNow == 3) {
					arr.push(temp);
					temp = '';
					iNow = 0;
				}
			}
			console.log(arr.join(','));
		}
		console.log(sangedian(3456786789901));

数组的差值,返回在arr1里但是不在arr2里,根据刚才的经验,我们可以先遍历arr2,把arr2作为属性名插入对象

var arr1 = [1,2,3,4,5,6];
		var arr2 = [3,4,5,6,7,8];
		function cha(arr1,arr2) {
			var temp = {};
			var result = [];
			for (var i = 0;i<arr2.length;i++) {
				if (!temp[arr2[i]]) {
					temp[arr2[i]] = 1;
				}
			}
			for (var j = 0;j<arr1.length;j++) {
				if (!temp[arr1[j]]) {
					result.push(arr1[j]);
				}
			}
			return result;

		}
		console.log(cha(arr1,arr2));

找到字符串中出现次数最多的字符,并返回它出现的次数

法一:

function fn(str) {
			var temp = {};
			var arr = [];
			var num = 0;
			var value = '';
			for (var i = 0;i<str.length;i++) {
				if (!temp[str[i]]) {
					temp[str[i]] = [];
				}
				temp[str[i]].push(str[i]);
			}
			for(var attr in temp){
				if (temp[attr].length>num) {
					num = temp[attr].length;
					value = temp[attr][0];
				}
			}
			return value + num;
		}
console.log(fn('sssndifnssssdnifinsksssndifnodsss'));

法二:

function fn(str) {
			var num = 0;
			var value = '';
			var arr = str.split('');
			arr.sort();
			str = arr.join('');
			str.replace(/(\w)\1+/g,function (match,$1) {
				if (match.length>num) {
					num = match.length;
					value = $1;
				}
			})
			return num + value;
		}
		console.log(fn('sssndifnssssdnifinsksssndifnodsss'));

爬楼梯,一次只能爬一阶或二阶,有多少种爬法

法一:递归

function fn(n) {
			if (n<1) {
				return 0;
			}
			if (n ==1) {
				return 1;
			}
			if (n == 2) {
				return 2;
			}
			return fn(n-1)+fn(n-2);
			
		}
		console.log(fn(9));

法二:用迭代的方式实现递归

function fn(n) {
      let arr = []
      if (n < 1) return 0
      arr[0] = 1
      arr[1] = 2
      for (let i = 2; i < n; i++) {
        arr[i] = arr[i-1] + arr[i-2]
      } 
      return arr[n-1]
    }

 

找出数组中重复的数字。长度为n的数组,所有的数字都在0~n-1的范围内,数组中某些数字是重复的,请找出任意一个重复的数字。

解法一:增加一个容器,把数组里的数字存入,存之前判断是否已存在了,如果存在就是重复的

function findRepeatNumber(arr) {
    let newArr = []
    for (let i = 0; i < arr.length; i++) {
        if (newArr.includes(arr[i])) {
            console.log(arr[i])
            return
        }
        newArr.push(arr[i])
    }
}

解法二:先排序,后查找,重复的元素是相邻的

function findRepeatNumber(arr) {
    // 先排序
    arr.sort((a, b) => {return a - b})
    console.log(arr)
    //判断相邻元素是否相等
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] === arr[i + 1]) {
            console.log(arr[i])
            return
        }
    }
}

解法三:使用临时数组,长度为n的数组nums里的所有数字都在0~n-1里,所以临时数组中的索引,对应所有可能出现的数字遍历时,将出现时置为1

function findRepeatNumber(arr) {
     let temp = []
     for (let i = 0; i < arr.length; i++) {
       temp[i] = 0
     }
     for (let i = 0; i < arr.length; i++) {
        if (temp[arr[i]] !== 0) {
          console.log(arr[i])
          return
        }
        temp[arr[i]] = 1
      }
}

解法四:用hashmap的方式,利用临时对象找

function findRepeatNumber(arr) {
      let temp = {}
      for (let i = 0; i < arr.length; i++) {
        if (temp[arr[i]]) {
          console.log(arr[i])
          return
        }
        temp[arr[i]] = 1
      }
    }

给定一个n个元素的数组,元素范围在1~n的,数组中的元素一些出现了两次,找出数组中没有的元素,在不使用额外空间,且时间复杂度为O(n)的情况下完成么?

解法一:使用容器存储已出现的数字,再找1~n的数在不在容器里,不在就是消失的元素

function findDisappearNumber(arr) {
      let temp = []
      for (let i = 0; i < arr.length; i++) {
        if (!temp.includes(arr[i])) {
          temp.push(arr[i])
        } 
      }
      for (let i = 1; i < arr.length; i++) {
        if (!temp.includes(i)) {
          console.log(i)
        }
      }
    }

找到出现次数最多的数字,给定一个大小为n的数组,在数组中出现的次数大于n/2 的元素,输出它

解法一:用hashmap来记录每个元素出现的次数

function majorityNumber(arr) {
    let temp = {}
    for (let i = 0; i < arr.length; i++) {
        let count = 1
        if (temp[arr[i]]) {
            temp[arr[i]] = temp[arr[i]]++
        } else {
            temp[arr[i]] = 1
        }
    }
    let halflen = arr.length / 2
    for (let i in temp) {
      if (temp[i] > halflen) {
        console.log(i)
      }
    }
}

解法二:先将数组进行排序,排好后,在中间的元素就是多数元素

function majorityNumber(arr) {
    let halflen = parseInt(arr.length / 2)
    arr.sort((a, b) => { return a - b })
    console.log(arr[halflen])
}

经典算法思想之双指针:

删除排序数组中的重复项,给定一个排好序的数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回最终数组的新长度。

解法一:需要额外的指针记录不同元素新出现的位置

function deleteDuplicate(arr) {
    let index = 0 //记录要比较的元素位置
    for (let i = 1; i < arr.length; i++) {
        //如果不同,进行覆盖
        if (arr[i] !== arr[index]) {
            index++
            arr[index] = arr[i]
        } 
    }
    return index + 1
}

移除元素,给定一个数组和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度

解法一:使用额外指针,记录要覆盖的索引位置(双指针)

function removeEle(arr, val) {
    let index = 0
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] !== val) {
            arr[index] = arr[i]
            index++
        }
    }
    return index
}

 解法二:对撞指针,遇到val就和最后一位元素交换并且数组长度减1

function removeEle(arr, val) {
    let index = 0
    let len = arr.length
    while(true) {
      if (index >= len) {
        console.log(len)
        return
      }
      if (arr[index] === val) {
        arr[index] = arr[len-1]
        arr[len - 1] = val
        len--
        index--
      }
      index++
    }
  }

链表类的算法题:

找到单链表的倒数第k个节点

解法一:先遍历出链表的总长度n,倒数第k个节点,就是正数第n-k+1个节点

function kthNode(head, k) {
    let n = 0
    listNode temp = head
    //先求出链表总长度
    while(temp.next !== null) {
        n++
        temp = temp.next
    }
    temp = head
    //再次遍历找到倒数第k个节点
    for (let i = 0; i < n-k+1; i++) {
        temp = temp.next
    }
    return temp
}

解法二:先定义额外指针找到第k个节点,然后两个指针同时移动,当快指针到达尾部时,慢指针刚好到达倒数第k个节点

function getkthNode(head, k) {
    let slow = head
    let fast = head
    for (let i = 0; i < k; i++) {
        fast = fast.next
    }
    while(fast.next !== null) {
        slow = slow.next
        fast = fast.next
    }
    return slow
}

反转链表

解法一:让一个节点的next指向前置节点,让原next节点指向自身 (双指针)

function reverse(head) {
    let pre = null
    let cur = head
    let temp
    while(cur !== null) { //cur 不为空就一直遍历
        temp = cur.next
        cur.next = pre
        pre = cur
        cur = temp
    }
    return pre
}

数组中的最大值

解法一:

function maxNum(arr) {
    let max = arr[0]
    for (let i = 1; i < arr.length; i++) {
        if (arr[i] > max) {
            max = arr[i]
        }
    }
    console.log(max)
}

找不同

给定两个字符串s和t,t中是由s随机重排后增加了一个字符,找出这个被添加的字母来

解法一:使用map分别记录s和t中字母和出现的次数,那么就是出现次数多1的,要么就是在s中没有出现的字母

function strMap(s, t) {
      let stemp = {}
      for(let i = 0; i < s.length; i++) {
          if (!stemp[s[i]]) {
            stemp[s[i]] = 1
          } else {
            stemp[s[i]] = ++stemp[s[i]]
          }
      }
      for (let i = 0; i < t.length; i++) {
          if (stemp[t[i]]) {
              stemp[t[i]] = --stemp[t[i]]
              if (stemp[t[i]] < 0) {
                  console.log(t[i])
              }
          } else {
              console.log(t[i])
          }
      }
  }

解法二:遍历s中的每个字符,将其在t中替换为空,t最后只剩一个字母了

function findDiff(s, t) {
    for (let i = 0; i < s.length; i++) {
        t = t.replace(s[i], '')
    }
    console.log(t)
}

解法三:根据阿斯克码来解

function findDiff(s, t) {
    let count = 0
    let tcount = 0
    for (let i = 0; i < s.length; i++) {
        count += s.charCodeAt(s[i])
    }
    for (let i = 0; i < t.length; i++) {
      tcount += t.charCodeAt(t[i])
    }
    console.log(count)
}

递归

将复杂问题,递推分解为最简问题,然后将结果回归的过程

使用方式:

1、推导出递推公式

2、找到递推出口

返回第n个月有多少只兔子

function fibonacci(n) {
    if (n === 1) return 1
    if (n === 2) return 1
    return fibonacci(n - 1) + fibonacci(n - 2)
}

大部分递归可以转化为迭代处理,数组存储 逆向存储n-1和n-2的值

function fib(n) {
    let arr = []
    if (n <= 1) return 1
    arr[0] = 1
    arr[1] = 1
    for (let i = 2; i < n; i++) {
        arr[i] = arr[i -1] + arr[i - 2]
    }
    console.log(arr[n - 1])
}

迭代比递归性能好一些

汉诺塔原理

先把前n-1个圆盘从A移动到B(经由C)

再把最大的圆盘从A移动到C

最后把前n-1个圆盘,从B移动到C(经由A)

function hanni(n) {
    if (n === 1) return 1
    return hanni(n-1) + 1 + hanni(n-1)
}

汉诺塔用迭代的方式实现

function hanni(n) {
      if (n < 1) return 0
      let arr = []
      arr[0] = 1
      for(let i = 1; i < n; i++) {
        arr[i] = arr[i-1] + 1 + arr[i-1]
      }
      return arr[n-1]
    }

将‘AA’转为27, ‘BB’转为54

function from(str) {
    let temp = 'A'.charCodeAt() // 65
    let num = 0
    for (let i = 0; i < str.length; i++) {
        num += (str[i].charCodeAt() - temp + 1) * Math.pow(26, str.length - i - 1)
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值