最近要参加百度公司2020春招的校招面试,特整理了下百度公司的算法题笔试面试题,以此纪念百度对我的认可。资源来源于LeetCode,纯手打,喜欢请支持正版。
再次感谢百度!
目录
两数之和... 1
两数相加... 1
无重复字符的最长子串... 2
两个排序数组的中位数... 3
最长回文子串... 4
有效的括号... 5
搜索旋转排序数组... 6
旋转图像... 6
螺旋矩阵... 8
编辑距离... 10
删除排序数组中的重复项... 11
合并两个有序数组... 12
环形链表... 13
二又树的后序遍历... 13
LRU缓存机制... 14
排序链表... 15
求众数... 16
反转链表... 16
前K个高频元素... 17
寻找数组的中心索引... 17
给定一个整数数组和一个目标值,找出数组中和为目标值的 两个 数。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] == target - nums[i]) {
return new int[] { i, j };
}
}
}
throw new IllegalArgumentException("No two sum solution");
}
给定两个 非空 链表来表示两个非负整数。位数按照 逆序 方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
示例:
输入: (2 -> 4 -> 3) + (5 -> 6 -> 4)
输出: 7 -> 0 -> 8
原因: 342 + 465 = 807
public class ListNode {
public int val;
public ListNode next;
public ListNode(int i) this.val = i;
public int val() return val;
}
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode p = l1, q = l2, curr = dummyHead;
int carry = 0;
while (p != null || q != null) {
int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;
int sum = carry + x + y;
carry = sum / 10;
curr.next = new ListNode(sum % 10);
curr = curr.next;
if (p != null) p = p.next;
if (q != null) q = q.next;
}
if (carry > 0) curr.next = new ListNode(carry);
return dummyHead.next;
}
给定一个字符串,找出不含有重复字符的 最长子串 的长度。
示例:
给定 `"abcabcbb"` ,没有重复字符的最长子串是 `"abc"` ,那么长度就是3。
给定 `"bbbbb"` ,最长的子串就是 `"b"` ,长度是1。
给定 `"pwwkew"` ,最长子串是 `"wke"` ,长度是3。请注意答案必须是一个 子串 , `"pwke"` 是 _子序列_ 而不是子串。
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
//尽量扩大范围[i, j]
if (!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else set.remove(s.charAt(i++));
}
return ans;
}
}
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。
请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。
示例 1:
nums1 = [1, 3]
nums2 = [2]
中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
中位数是 (2 + 3)/2 = 2.5
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) { // 确保 m<=n
int[] temp = A; A = B; B = temp;
int tmp = m; m = n; n = tmp;
}
int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = halfLen - i;
if (i < iMax && B[j-1] > A[i]) iMin = iMin + 1;
else if (i > iMin && A[i-1] > B[j]) iMax = iMax - 1;
else { // i is perfect
int maxLeft = 0;
if (i == 0) { maxLeft = B[j-1]; }
else if (j == 0) { maxLeft = A[i-1]; }
else { maxLeft = Math.max(A[i-1], B[j-1]); }
if ( (m + n) % 2 == 1 ) { return maxLeft; }
int minRight = 0;
if (i == m) { minRight = B[j]; }
else if (j == n) { minRight = A[i]; }
else { minRight = Math.min(B[j], A[i]); }
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
}
给定一个字符串 s ,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba"也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i);
int len2 = expandAroundCenter(s, i, i + 1);
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
private int expandAroundCenter(String s, int left, int right) {
int L = left, R = right;
while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
L--;
R++;
}
return R - L - 1;
}
给定一个只包括 `'('` , `')'` , `'{'` , `'}'` , `'['` , `']'` 的字符串,判断字符串是否有效。
有效字符串需满足:
1. 左括号必须用相同类型的右括号闭合。
2. 左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
public boolean isValid(String s) {
Deque stack = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(' || s.charAt(i) == '{' || s.charAt(i) == '[') {
stack.push(s.charAt(i));
} else {
if (stack.isEmpty()) return false;
else {
if (stack.peek() == '(' && s.charAt(i) != ')') {
return false;
} else if (stack.peek() == '{' && s.charAt(i) != '}') {
return false;
} else if (stack.peek() == '[' && s.charAt(i) != ']') {
return false;
}
stack.pop();
}
}
}
return stack.isEmpty();
}
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 `[0,1,2,4,5,6,7]` 可能变为 `[4,5,6,7,0,1,2]` )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 `-1` 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 _O_ (log _n_ ) 级别。
示例 1:
输入: nums = [`4,5,6,7,0,1,2]`, target = 0
输出: 4
示例 2:
输入: nums = [`4,5,6,7,0,1,2]`, target = 3
输出: -1
public static class Solution2 {
public int search(int[] nums, int target) {
if (nums == null || nums.length == 0) return -1;
int lo = 0;
int hi = nums.length - 1;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (nums[mid] == target) return mid;
if (nums[lo] <= nums[mid]) {
if (target >= nums[lo] && target < nums[mid]) {
hi = mid - 1;
} else {
lo = mid + 1;
}
} else {
if (target > nums[mid] && target <= nums[hi]) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
}
return nums[lo] == target ? lo : -1;
}
}
给定一个 _n_ × _n_ 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
说明:
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。 请不要 使用另一个矩阵来旋转图像。
示例 1:
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
示例 2:
给定 matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
原地旋转输入矩阵,使其变为:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
public class Solution {
public static class Solution1 {
public void rotate(int[][] matrix) {
//第一步:将矩阵转置,[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
int m = matrix.length;
for (int i = 0; i < m; i++) {
for (int j = i; j < m; j++) {
int tmp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = tmp;
}
}
//第一步得到的结果为:[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
//第二步保持矩阵的横坐标不变,对称交换纵坐标
for (int i = 0; i < m; i++) {
int left = 0;
int right = m - 1;
while (left < right) {
int tmp = matrix[i][left];
matrix[i][left] = matrix[i][right];
matrix[i][right] = tmp;
left++;
right--;
}
}
}
}
public static class Solution2 {
public void rotate(int[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
int top = 0;
int bottom = n - 1;
while (top < bottom) {
int[] tmp = matrix[top];
matrix[top] = matrix[bottom];
matrix[bottom] = tmp;
top++;
bottom--;
}
for (int i = 0; i < m; i++) {
for (int j = i + 1; j < n; j++) {
int tmp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = tmp;
}
}
}
}
}
给定一个包含 _m_ x _n_ 个元素的矩阵( _m_ 行, _n_ 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
示例 1:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]
示例 2:
输入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]
public class Solution {
public static class Solution1 {
public List spiralOrder(int[][] matrix) {
List result = new ArrayList();
int row = matrix.length;
if (row == 0) return result;
int rowStart = 0;
int rowEnd = matrix.length - 1;
int colStart = 0;
int colEnd = matrix[0].length - 1;
while (rowStart <= rowEnd && colStart <= colEnd) {
//向右移动
for (int j = colStart; j <= colEnd; j++) {
result.add(matrix[rowStart][j]);
}
rowStart++;
//向下
for (int i = rowStart; i <= rowEnd; i++) {
result.add(matrix[i][colEnd]);
}
colEnd--;
//only when rowStart <= rowEnd
//向左走
if (rowStart <= rowEnd) {
for (int j = colEnd; j >= colStart; j--) {
result.add(matrix[rowEnd][j]);
}
}
rowEnd--;
//only when colStart <= colEnd
//向上
if (colStart <= colEnd) {
for (int i = rowEnd; i >= rowStart; i--) {
result.add(matrix[i][colStart]);
}
}
colStart++;
}
return result;
}
}
}
给定两个单词 _word1_ 和 _word2_ ,计算出将 _word1_ 转换成 _word2_ 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
1. 插入一个字符
2. 删除一个字符
3. 替换一个字符
示例 1:
输入: word1 = "horse", word2 = "ros"
输出: 3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2:
输入: word1 = "intention", word2 = "execution"
输出: 5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
public class Solution {
public static class Solution1 {
public int minDistance(String word1, String word2) {
int m = word1.length();
int n = word2.length();
if (m == 0) return n;
if (n == 0) return m;
char[] str1 = word1.toCharArray();
char[] str2 = word2.toCharArray();
int[][] table = new int[m + 1][n + 1];
for (int i = 0; i < m + 1; i++) table[i][0] = i;
for (int j = 0; j < n + 1; j++) table[0][j] = j;
for (int i = 1; i < m + 1; i++) {
for (int j = 1; j < n + 1; j++) {
int cost = 0;
if (str1[i - 1] != str2[j - 1]) cost = 1;
table[i][j] = Math.min(Math.min(table[i - 1][j] + 1,
table[i][j - 1] + 1),
table[i - 1][j - 1] + cost);
}
}
return table[m][n];
}
}
}
给定一个排序数组,你需要在删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定 _nums_ = [1,1,1,2,2,3],
函数应返回新长度 length = `5`, 并且原数组的前五个元素被修改为 `1, 1, 2, 2,` 3 。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 _nums_ = [0,0,1,1,1,1,2,3,3],
函数应返回新长度 length = `7`, 并且原数组的前五个元素被修改为 `0`, 0, 1, 1, 2, 3, 3 。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以 “引用” 方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
public class Solution {
public static class Solution1 {
public int removeDuplicates(int[] nums) {
int counter = 0;
int len = nums.length;
if (len == 0) return 0;
if (len == 1) return 1;
if (len == 2) return 2;
List a = new ArrayList();
a.add(nums[0]);
a.add(nums[1]);
for (int i = 2; i < len; i++) {
if (nums[i] != nums[i - 1]) {
a.add(nums[i]);
} else if (nums[i] != nums[i - 2]) {
a.add(nums[i]);
}
}
counter = a.size();
for (int i = 0; i < counter; i++) {
nums[i] = a.get(i);
}
return counter;
}
}
}
给定两个有序整数数组 _nums1_ 和 _nums2_ ,将 _nums2_ 合并到 _nums1_ 中 _,_ 使得 _num1_ 成为一个有序数组。
说明:
* 初始化 _nums1_ 和 _nums2_ 的元素数量分别为 _m_ 和 _n_ 。
* 你可以假设 _nums1_ 有足够的空间(空间大小大于或等于 _m + n_ )来保存 _nums2_ 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
public class Solution {
public static class Solution1 {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1;
int j = n - 1;
int k = m + n - 1;
while (i >= 0 && j >= 0) {
if (nums1[i] > nums2[j]) nums1[k--] = nums1[i--];
else nums1[k--] = nums2[j--];
}
while (j >= 0) {
nums1[k--] = nums2[j--];
}
}
}
}
给定一个链表,判断链表中是否有环。
进阶:
你能否不使用额外空间解决此题?
public static class Solution2 {
public boolean hasCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) return true;
}
return false;
}
}
给定一个二叉树,返回它的 _后序_ 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
public static class Solution2 {
public List postorderTraversal(TreeNode root) {
List result = new ArrayList();
return post(root, result);
}
List post(TreeNode root, List result) {
if (root == null) return result;
post(root.left, result);
post(root.right, result);
result.add(root.val);
return result;
}
}
运用你所掌握的数据结构,设计和实现一个 [LRU (最近最少使用) 缓存机制] 。它应该支持以下操作: 获取数据 `get` 和 写入数据 `put` 。
获取数据 `get(key)` \- 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 `put(key, value)` \- 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得密钥 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得密钥 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
解法一:
public class Solution1 {
public class LRUCache {
private Map cache;
private final int max;
public LRUCache(int capacity) {
max = capacity;
cache = new LinkedHashMap(capacity, 1.0f, true) {
public boolean removeEldestEntry(Map.Entry eldest) {
return cache.size() > max;
}
};
}
public int get(int key) {
return cache.getOrDefault(key, -1);
}
public void set(int key, int value) {
cache.put(key, value);
}
}
}
看得一脸懵逼,解法二:
class Solution2 {
public static class LRUCache {
private class Node {
int key;
int value;
LRUCache.Node prev;
LRUCache.Node next;
Node(int k, int v) {
this.key = k;
this.value = v;
}
Node() {
this.key = 0;
this.value = 0;
}
}
private int capacity;
private int count;
private LRUCache.Node head;
private LRUCache.Node tail;
private Map map;
// 值应该是节点类型!这就是拥有一个名为Node的类的全部意义!
public LRUCache(int capacity) {
this.capacity = capacity;
this.count = 0;//我们需要一个计数来跟踪缓存中元素的数量
//我们知道什么时候把LRU从缓存中驱逐出去
this.map = new HashMap();
head = new LRUCache.Node();
tail = new LRUCache.Node();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
LRUCache.Node node = map.get(key);
//// HashMap允许值为null,这比HashTable更好!
if (node == null) return -1;
else {
remove(node);
add(node);
return node.value;
}
}
public void set(int key, int value) {
LRUCache.Node node = map.get(key);
if (node == null) {
node = new LRUCache.Node(key, value);
map.put(key, node);
add(node);
count++;
if (count > capacity) {
LRUCache.Node toDelete = tail.prev;
map.remove(toDelete.key);
remove(toDelete);
count--;
}
} else {
remove(node);
node.value = value;
add(node);
}
}
private void remove(LRUCache.Node node) {
LRUCache.Node next = node.next;
LRUCache.Node prev = node.prev;
prev.next = next;
next.prev = prev;
}
private void add(LRUCache.Node node) {
// 我们总是将节点添加到第一个位置:head.next!!
LRUCache.Node next = head.next;
head.next = node;
node.next = next;
node.prev = head;
next.prev = node;
}
}
}
在 _O_ ( _n_ log _n_ ) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
public class Solution {
public static class Solution1 {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) return head;
//步骤1:把链表分成两半
ListNode prev = null;
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
prev = slow;
fast = fast.next.next;
slow = slow.next;
}
prev.next = null;
//步骤2:对每一半进行排序
ListNode l1 = sortList(head);
ListNode l2 = sortList(slow);
//步骤3: 合并两部分
return merge(l1, l2);
}
private ListNode merge(ListNode l1, ListNode l2) {
ListNode result = new ListNode(0);
ListNode tmp = result;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
tmp.next = l1;
l1 = l1.next;
} else {
tmp.next = l2;
l2 = l2.next;
}
tmp = tmp.next;
}
if (l1 != null) tmp.next = l1;
if (l2 != null) tmp.next = l2;
return result.next;
}
}
}
给定一个大小为 _n_ 的数组,找到其中的众数。众数是指在数组中出现次数 大于 `⌊ n/2 ⌋` 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
public static class Solution3 {
//This is O(nlogn) time.
public int majorityElement(int[] nums) {
Arrays.sort(nums);//就这么简单
return nums[nums.length / 2];
}
}
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
public static class Solution1 {
public ListNode reverseList(ListNode head) {
ListNode newHead = null;
while (head != null) {
ListNode next = head.next;
head.next = newHead;
newHead = head;
head = next;
}
return newHead;
}
}
/* //测试代码
public static void main(String[] args) {
Node n = new Node(1);
n.appendToTail(2);
n.appendToTail(3);
n.appendToTail(4);
n.appendToTail(5);
System.out.println("反转前:" + n);
System.out.println("反转后:" + reverseList(n));
}
public static Node reverseList(Node head) {
Node newHead = null;
while (head != null) {
Node next = head.next;
head.next = newHead;
newHead = head;
head = next;
}
return newHead;
}
static class Node {
int data;
Node next = null;
Node(int d) data = d;
@Override
public String toString() return data + "->" + next;
void appendToTail(int d) {//添加数据到链表尾部
Node end = new Node(d);
Node n = this;
while (n.next != null) n = n.next;
n.next = end;
}
}*/
//方法二:递归
public class LinkedRevers {
public static void main(String[] args) {
Node node = new Node(1, new Node(2, new Node(3, new Node(4, new Node(5, new Node(6, null))))));
System.out.println(new Node().reverse(node));//6->5->4->3->2->1->null
}
}
class Node {
public int value;
public Node next;
@Override
public String toString() {
return value + "->" + next;
}
public Node() {
}
public Node(int value, Node next) {
this.value = value;
this.next = next;
}
public Node reverse(Node head) {
if (head == null || head.next == null) return head;
Node temp = head.next;
Node newHead = reverse(head.next);
temp.next = head;
head.next = null;
return newHead;
}
}
附:链表翻转问题
k个一组翻转链表
给出一个链表,每 _k_ 个节点一组进行翻转,并返回翻转后的链表。
_k_ 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 _k_ 的整数倍,那么将最后剩余节点保持原有顺序。
示例 :
给定这个链表: `1->2->3->4->5`
当 _k_ = 2 时,应当返回: `2->1->4->3->5`
当 _k_ = 3 时,应当返回: `3->2->1->4->5`
说明 :
* 你的算法只能使用常数的额外空间。
* 你不能只是单纯的改变节点内部的值 ,而是需要实际的进行节点交换。
public class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode curr = head;
int count = 0;
while (curr != null && count != k) {
curr = curr.next;
count++;
}
if (count == k) {
curr = reverseKGroup(curr, k);
while (count-- > 0) {
ListNode temp = head.next;
head.next = curr;
curr = head;
head = temp;
}
head = curr;
}
return head;
}
}
旋转链表
给定一个链表,旋转链表,将链表每个节点向右移动 _k_ 个位置,其中 _k_ 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4
输出: `2->0->1->NULL`
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: `0->1->2->NULL`
向右旋转 4 步: `2->0->1->NULL`
public class Solution {
public static class Solution1 {
public ListNode rotateRight(ListNode head, int k) {
if (head == null) return head;
ListNode copyHead = head;
int len = 1;
while (copyHead.next != null) {
copyHead = copyHead.next;
len++;
}
copyHead.next = head;//把尾和头连在一起做成一个圆圈
for (int i = len - k % len; i > 1; i--) head = head.next;
copyHead = head.next;
head.next = null;
return copyHead;
}
}
}
反转链表Ⅱ
反转从位置 _m_ 到 _n_ 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ _m_ ≤ _n_ ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, _m_ = 2, _n_ = 4
输出: 1->4->3->2->5->NULL
public class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pre = dummy;
for (int i = 0; i < m - 1; i++) pre = pre.next;
ListNode start = pre.next;
ListNode then = start.next;
for (int i = 0; i < n - m; i++) {
start.next = then.next;
then.next = pre.next;
pre.next = then;
then = start.next;
}
return dummy.next;
}
}
附:二叉树翻转问题
public static TreeNode reversTree(TreeNode root) {
if (root == null) return null;
if (root.left != null) reversTree(root.left);
if (root.right != null) reversTree(root.right);
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
return root;
}
给定一个非空的整数数组,返回其中出现频率前 _k_ 高的元素。
例如,
给定数组 `[1,1,1,2,2,3]` , 和 k = 2,返回 `[1,2]` 。
注意:
* 你可以假设给定的 _k_ 总是合理的,1 ≤ k ≤ 数组中不相同的元素的个数。
* 你的算法的时间复杂度 必须 优于 O( _n_ log _n_ ) , _n_ 是数组的大小。
public class Solution {
public static void main(String[] args) {
int[] arr = {1, 1, 1, 2, 2, 3};
System.out.println(topKFrequent(arr, 2));
System.out.println(topKFrequent2(arr, 2));
}
/**
* 优先队列
* @param nums 数组
* @param k topK
* @return
*/
public static List topKFrequent(int[] nums, int k) {
//先构造频率映射,然后遍历映射
//把它们放到堆里,O(n)
Map map = new HashMap<>();
for (int num : nums) map.put(num, map.getOrDefault(num, 0) + 1);
//构建堆,O(logn)
Queue> heap = new PriorityQueue<>((o1, o2) -> o2.getValue() - o1.getValue());
for (Map.Entry entry : map.entrySet()) heap.offer(entry);
List res = new ArrayList<>();
while (k-- > 0) res.add(heap.poll().getKey());
return res;
}
/**
* 桶排序
* @param nums 数组
* @param k topK
* @return
*/
public static List topKFrequent2(int[] nums, int k) {
HashMap map = new HashMap<>();
for (int num : nums) {
if (map.containsKey(num)) map.put(num, map.get(num) + 1);
else map.put(num, 1);
}
// 创建 nums.length + 1 个桶
List[] bucket = new List[nums.length + 1];
// 遍历map,根据value值 出现的次数 放到对应的桶中
for (Map.Entry e : map.entrySet()) {
Integer value = e.getValue();
if (bucket[value] == null) bucket[value] = new ArrayList<>();
bucket[value].add(e.getKey());
}
List freList = new ArrayList<>();
// 桶的编号表示出现次数,所以倒数桶
for (int j = bucket.length - 1; j > -1 && freList.size() < k; j--) {
if (bucket[j] != null) freList.addAll(bucket[j]);
}
return freList;
}
}
给定一个整数类型的数组 `nums` ,请编写一个能够返回数组 “中心索引” 的方法。
我们是这样定义数组 中心索引 的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。
如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。
示例 1:
输入:
nums = [1, 7, 3, 6, 5, 6]
输出: 3
解释:
索引3 (nums[3] = 6) 的左侧数之和(1 + 7 + 3 = 11),与右侧数之和(5 + 6 = 11)相等。
同时, 3 也是第一个符合要求的中心索引。
示例 2:
输入:
nums = [1, 2, 3]
输出: -1
解释:
数组中不存在满足此条件的中心索引。
说明:
* `nums` 的长度范围为 `[0, 10000]` 。
* 任何一个 `nums[i]` 将会是一个范围在 `[-1000, 1000]` 的整数。
public static class Solution2 {
public int pivotIndex(int[] nums) {
int total = 0;
for (int num : nums) total += num;
int sum = 0;
for (int i = 0; i < nums.length; sum += nums[i++]) {
if (sum * 2 == total - nums[i]) return i;
}
return -1;
}
}
import java.util.Arrays;
import java.util.Random;
public class BaiduBigDataSort {
public static void main(String[] args) {
int[] a = {49, 38, 65, 97, 76, 13, 27, 50};
int[] b1 = {49, 38, 65, 97, 76, 13, 27, 50};
int[] b2 = {49, 38, 65, 97, 76, 13, 27, 50};
int[] c = {49, 38, 65, 97, 76, 13, 27, 50};
MergeSort.mergeSort(a, 0, a.length - 1);
QuickSort.quickSort(b1, 0, b1.length - 1);
QuickSort.newQuickSort(b2);
HeapSort.heapSort(c);
System.out.println("归排排好序的数组:" + Arrays.toString(a));
System.out.println("快排排好序的数组:" + Arrays.toString(b1));
System.out.println("快排优化后排好序的数组:" + Arrays.toString(b2));
System.out.println("堆排排好序的数组:" + Arrays.toString(c));
}
}
class MergeSort {
//两路归并算法,两个排好序的子序列合并为一个子序列
private static void merge(int[] a, int left, int mid, int right) {
int[] tmp = new int[a.length];//辅助数组
int p1 = left, p2 = mid + 1, k = left;//p1、p2是检测指针,k是存放指针
while (p1 <= mid && p2 <= right) {
if (a[p1] <= a[p2]) tmp[k++] = a[p1++];
else tmp[k++] = a[p2++];
}
while (p1 <= mid) tmp[k++] = a[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
while (p2 <= right) tmp[k++] = a[p2++];//同上
for (int i = left; i <= right; i++) a[i] = tmp[i];//复制回原素组
}
static void mergeSort(int[] a, int start, int end) {
if (start < end) {//当子序列中只有一个元素时结束递归
int mid = (start + end) / 2;//划分子序列
mergeSort(a, start, mid);//对左侧子序列进行递归排序
mergeSort(a, mid + 1, end);//对右侧子序列进行递归排序
merge(a, start, mid, end);//合并
}
}
}
class QuickSort {
//优化前的快排:最好平均最差时间复杂度为O(nlogn)、O(nlogn)、O(n2)
static void quickSort(int[] arr, int start, int end) {
int i, j, temp, t;
if (start > end) return;
i = start;
j = end;
temp = arr[start];//temp就是基准位
while (i < j) {
while (temp <= arr[j] && i < j) j--;//先看右边,依次往左递减
while (temp >= arr[i] && i < j) i++;//再看左边,依次往右递增
t = arr[j];//如果满足条件则交换
arr[j] = arr[i];
arr[i] = t;
}
arr[start] = arr[i];//最后将基准为与i和j相等位置的数字交换
arr[i] = temp;
quickSort(arr, start, j - 1);//递归调用左半数组
quickSort(arr, j + 1, end);//递归调用右半数组
}
//优化后的快排:最好平均最差时间复杂度均为O(nlogn)
static void newQuickSort(int[] arr) {
int random = new Random().nextInt(arr.length + 1);
int temp = arr[random];//将基准设置为随机元素
arr[random] = arr[0];
arr[0] = temp;
quickSort(arr, 0, arr.length - 1);
}
}
class HeapSort {
static void heapSort(int[] arr) {
//创建堆,从第一个非叶子结点从下至上,从右至左调整结构
for (int i = (arr.length - 1) / 2; i >= 0; i--) adjustHeap(arr, i, arr.length);
//调整堆结构+交换堆顶元素与末尾元素
for (int i = arr.length - 1; i > 0; i--) {
int temp = arr[i];//将堆顶元素与末尾元素进行交换
arr[i] = arr[0];
arr[0] = temp;
adjustHeap(arr, 0, i);//重新对堆进行调整
}
}
//调整堆@param arr待排序列 @param parent 父节点 @param length 待排序列尾元素索引
private static void adjustHeap(int[] arr, int parent, int length) {
int temp = arr[parent];//将temp作为父节点
int lChild = 2 * parent + 1;//左孩子
while (lChild < length) {
int rChild = lChild + 1;//右孩子
if (rChild < length && arr[lChild] < arr[rChild]) lChild++;// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
if (temp >= arr[lChild]) break;// 如果父结点的值已经大于孩子结点的值,则直接结束
arr[parent] = arr[lChild];// 把孩子结点的值赋给父结点
parent = lChild;//选取孩子结点的左孩子结点,继续向下筛选
lChild = 2 * lChild + 1;
}
arr[parent] = temp;
}
}