查找单链表的中间节点:
法一》:遍历一遍整个链表,计算出链表的长度,进而遍历第二遍找出中间位置的数据
法二》:建立两个指针,一个指针一次遍历两个节点,另一个指针一次遍历一个节点,当快指针遍历到空节点时,慢指针指向的位置为链表的中间位置
算法实现:
- SListNode * FindMidNode(SListNode * phead)
- {
- SListNode *fast = phead;
- SListNode *slow = phead;
- while (fast)
- {
- if (fast->next != NULL)
- {
- fast = fast->next->next;
- }
- else
- {
- break;
- }
- slow = slow->next;
- }
- return slow;
- }
- 也可以这样写,更为简洁
- while (fast&&fast->next )
- {
- fast = fast->next->next;
- slow = slow->next;
- }
找出两个字符串中的公共子串:
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、
- Array.prototype.method3 = function(){
- //直接定义结果数组
- var arr[this[0]];
- for(var i = 1; i < this.length; i++){ //从数组第二项开始循环遍历此数组
- //对元素进行判断:
- //如果数组当前元素在此数组中第一次出现的位置不是i
- //那么我们可以判断第i项元素是重复的,否则直接存入结果数组
- if(this.indexOf(this[i]) == i){
- arr.push(this[i]);
- }
- }
- return arr;
- }
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)
}
}