本文最后更新于:2023年3月19日 晚上
牛客网多题
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。示例 1:
1 2 3 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
示例 2:
思路 暴力 暴力遍历所有可能,无法通过,超时
中心扩展法 回文串一定是对称的,每次选择一个中心,进行中心向两边扩展比较左右字符是否相等 中心点的选取有两种 aba,中心点是 b aa,中心点是两个 a 之间 所以共有两种组合可能 left:i,right:i left:i,right:i+1
解答 暴力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var longestPalindrome = function (s ) { if (s.length < 2 ) return s; const isReverse = (s ) => { return s.split("" ).reverse().join("" ) === s; }; let max = 0 ; let res = "" ; for (let i = 0 ; i < s.length; i++) { for (let j = i + 1 ; j < s.length + 1 ; j++) { let str = s.slice(i, j); let length = str.length; if (isReverse(str) && Math .max(max, length) === length) { max = length; res = str; } } } return res; };
中心拓展法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 var longestPalindrome = function (s ) { if (s.length < 2 ) return s; let max = 0 ; let start = 0 , end = 0 ; const center = (left, right ) => { while (left >= 0 && right < s.length && s[left] === s[right]) { left--; right++; } return right - left - 1 ; }; let index = 0 ; while (index < s.length) { let maxLen = Math .max(center(index, index), center(index, index + 1 )); let len1 = center(index, index); let len2 = center(index, index + 1 ); if (maxLen > max) { if (maxLen % 2 === 0 ) { max = maxLen; start = index - maxLen / 2 + 1 ; end = index + maxLen / 2 + 1 ; } else { max = maxLen; start = index - (maxLen - 1 ) / 2 ; end = index + (maxLen + 1 ) / 2 ; } } index++; } return s.slice(start, end); };
寻找第 K 大 有一个整数数组,请你根据快速排序的思路,找出数组中第 K 大的数。 给定一个整数数组 a,同时给定它的大小 n 和要找的 K(K 在 1 到 n 之间),请返回第 K 大的数,保证答案存在。 测试样例:
思路 提示使用快速排序的思路,快排一次以后,一个下标的元素在排序后的数组中的位置已经确认。所以不需要全部排完再得到答案,判断当前下标的值大于或小于 K,大于 K 则需要在左边查找,小于 K 则在右边查找,直到查找到 K 为止,
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function findKth (a, n, K ) { function quickSort (start, end ) { if (start >= end) return ; let i = start; let j = end; let tmp = a[start]; while (i < j) { while (i < j && a[j] >= tmp) j--; while (i < j && a[i] <= tmp) i++; [a[i], a[j]] = [a[j], a[i]]; } [a[start], a[i]] = [a[i], a[start]]; return i; } let index = quickSort(0 , n - 1 ); while (index !== n - K) { if (index > n - K) { index = quickSort(0 , index - 1 ); } else { index = quickSort(index + 1 , n - 1 ); } } return a[index]; }module .exports = { findKth : findKth, };
链表中的节点每 k 个一组翻转 将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表 如果链表中的节点数不是k 的倍数,将最后剩下的节点保持原样你不能更改节点中的值,只能更改节点本身。 要求空间复杂度** O (1)** 例如: 给定的链表是 1→2→3→4→5 对于k =2, 你应该返回 2→1→4→3→5 对于k =3, 你应该返回 3→2→1→4→5示例 1 输入
输出
思路 不限制空间复杂度及修改方式 刚开始没看到限制空间复杂度为 O(1),和不能直接修改值。若不限制有简单的实现方法。
解答 不限制空间复杂度及修改方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 function reverseKGroup (head, k ) { let tmp = head; while (tmp) { let reverseStack = []; let tmp2 = tmp; while (reverseStack.length < k && tmp2) { reverseStack.push(tmp2.val); tmp2 = tmp2.next; } if (reverseStack.length === k) { while (reverseStack.length) { tmp.val = reverseStack.pop(); tmp = tmp.next; } } else { break ; } } return head; }module .exports = { reverseKGroup : reverseKGroup, };
括号匹配 给出一个仅包含字符’(‘,’)’,’{‘,’}’,’[‘和’]’,的字符串,判断给出的字符串是否是合法的括号序列 括号必须以正确的顺序关闭,”()”和”()[]{}”都是合法的括号序列,但”(]”和”([)]”不合法。
思路 比较简单,栈实现即可,使用一个对象来判断括号合法性
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function isValid (s ) { const valid = { "()" : true , "{}" : true , "[]" : true , }; if (s.length % 2 !== 0 ) return false ; const stack = []; for (const arr of s) { if (stack.length) { let top = stack[stack.length - 1 ]; let str = top + arr; if (valid[str]) stack.pop(); else return false ; } else { stack.push(arr); } } return stack.length === 0 ; }module .exports = { isValid : isValid, };
斐波那契数列(跳楼梯) 大家都知道斐波那契数列,现在要求输入一个整数 n,请你输出斐波那契数列的第 n 项(从 0 开始,第 0 项为 0,第 1 项是 1)。 n<=39
思路 dp 求解即可
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function Fibonacci (n ) { if (n === 0 ) return 0 ; let a = 0 ; let b = 1 ; let sum = a + b; for (let i = 2 ; i < n; i++) { a = b; b = sum; sum = a + b; } return sum; }module .exports = { Fibonacci : Fibonacci, };
实现二叉树先序,中序和后序遍历 分别按照二叉树先序,中序和后序打印所有的节点。示例 1 输入
输出
1 [[1 ,2 ,3 ],[2 ,1 ,3 ],[2 ,3 ,1 ]]
思路 一次遍历即可建立好结果数组
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function threeOrders (root ) { let res = [[], [], []]; const visit = (root ) => { if (!root) return null ; res[0 ].push(root.val); visit(root.left); res[1 ].push(root.val); visit(root.right); res[2 ].push(root.val); }; visit(root); return res; }module .exports = { threeOrders : threeOrders, };
链表中是否有环 判断给定的链表中是否有环
思路 hash 表 通过 map 存节点,判断 map 中是否已有节点,已有则存在
增加 isVisit 字段判断 通过 js 特性来判断
快慢指针 快指针走两次,慢指针走一次,如果有环,快指针会赶上慢指针。
解答 hash 表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function hasCycle (head ) { const map = new Map (); while (head) { if (map.has(head)) return true ; else map.set(head, true ); head = head.next; } return false ; }module .exports = { hasCycle : hasCycle, };
增加 isVisit 字段判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function hasCycle (head ) { while (head) { if (head.isVisit) return true ; head.isVisit = true ; head = head.next; } return false ; }module .exports = { hasCycle : hasCycle, };
快慢指针 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function hasCycle (head ) { let fast = head; let slow = head; while (fast && fast.next) { fast = fast.next.next; slow = slow.next; if (fast === slow) return true ; } return false ; }module .exports = { hasCycle : hasCycle, };
反转字符串 写出一个程序,接受一个字符串,然后输出该字符串反转后的字符串。(字符串长度不超过 1000)
思路 双指针 一个指向字符串头,一个指向字符串尾。当两个指针相遇强,不断交换指针的值,得到答案。
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function solve (str ) { let start = 0 ; let end = str.length - 1 ; str = str.split("" ); while (start < end) { [str[start], str[end]] = [str[end], str[start]]; start++; end--; } return str.join("" ); }module .exports = { solve : solve, };
合并两个有序的数组 给出两个有序的整数数组 和 ,请将数组 合并到数组 中,变成一个有序的数组 注意: 可以假设 数组有足够的空间存放 数组的元素, 和 中初始的元素数目分别为 和
思路 即归并排序中的合并原理,设置一个指针指向排序结果结尾下标 m+n-1,从这里开始排序。将两个指针指向 A、B 结尾,判断指针指向的值哪个大,大的放入结果指针指向的地方,令结果指针和大的那个值的数组指针减一,当 m、n 其中一个小于 0,结束循环,将另外一个数组剩下的元素赋值进 A 即可。
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function merge (A, m, B, n ) { let k = m + n - 1 ; m = m - 1 ; n = n - 1 ; while (m >= 0 && n >= 0 ) { if (A[m] > B[n]) { A[k--] = A[m--]; } else { A[k--] = B[n--]; } } while (m >= 0 ) { A[k--] = A[m--]; } while (n >= 0 ) { A[k--] = B[n--]; } }module .exports = { merge : merge, };
删除链表倒数第 k 个元素 给定一个链表,删除链表的倒数第 n 个节点并返回链表的头指针 例如, 给出的链表为:1->2->3->4->5, n= 2. 删除了链表的倒数第 n 个节点之后,链表变为 1->2->3->5. 备注: 题目保证 n 一定是有效的 请给出请给出时间复杂度为**O (n )**的算法
思路 使用队列存链表的值,删除倒数第 n 个元素,重新生成链表即可
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function removeNthFromEnd (head, n ) { if (n < 1 ) return head; const queue = []; let tmp = head; while (tmp) { queue.push(tmp.val); tmp = tmp.next; } tmp = new ListNode(null ); queue.splice(queue.length - n, 1 ); let res = tmp; while (queue.length) { tmp.next = new ListNode(queue.shift()); tmp = tmp.next; } return res.next; }module .exports = { removeNthFromEnd : removeNthFromEnd, };
数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为 9 的数组{1,2,3,2,2,2,5,4,2}。由于数字 2 在数组中出现了 5 次,超过数组长度的一半,因此输出 2。如果不存在则输出 0。
思路 哈希表存储,如果出现的次数大于数组一半,返回值。不存在则返回 0
解答 1 2 3 4 5 6 7 8 9 10 11 12 function MoreThanHalfNum_Solution (numbers ) { const map = new Map (); for (const number of numbers) { map.set(number, map.has(number) ? map.get(number) + 1 : 1 ); if (map.get(number) > Math .floor(numbers.length / 2 )) return number; } return 0 ; }module .exports = { MoreThanHalfNum_Solution : MoreThanHalfNum_Solution, };
二分查找 请实现有重复数字的有序数组的二分查找。 输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。示例 1 输入
输出
思路 判断数组第一个值是否大于查找值,是的话直接返回 1。后面是常规的二分查找,知道找到中点大于等于查找值,中点的前一个点小于查找值为止。
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function upper_bound_ (n, v, a ) { if (n > 0 ) { if (a[0 ] >= v) return 1 ; } let start = 0 ; let end = n - 1 ; let middle = null ; while (start <= end) { middle = Math .floor((start + end) / 2 ); if (a[middle] >= v && a[middle - 1 ] < v) { return middle + 1 ; } else if (a[middle] >= v) { end = middle - 1 ; } else { start = middle + 1 ; } } return n + 1 ; }module .exports = { upper_bound_ : upper_bound_, };