数组
数组题:
- 数组移动,在原数组上操作。不需要额外的数组空间。性能 O(n).
1) 移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
解题思路:
定义一个移位索引,找到非零的元素按移位索引移位,并且把自己本来的位置设置为0。移位索引向后移位。
class Solution {
public void moveZeroes(int[] nums) {
int index = 0;
for(int i = 0; i < nums.length; i++){
if (nums[i] != 0) {
if(index < i) {
nums[index] = nums[i];
nums[i] = 0;
}
index++;
}
}
}
}
2) 移除元素
给你一个数组 nums 和一个值 val,你需要
原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。
解题思路:
定义移位索引,遍历数组。把非val的元素按移位索引设置。移位索引往后移。最后返回移位索引。
class Solution {
public int removeElement(int[] nums, int val) {
int index = 0;
for(int i = 0; i < nums.length; i++){
if(nums[i] != val){
nums[index++] = nums[i];
}
}
return index;
}
}
3)
删除排序数组中的重复项
给定一个排序数组,你需要在
原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
解题思路: 此题中元素是排好序的。定义一个移位索引用来存储移动的元素索引。
按元素比较来判断是否是重复元素。重复就往后找,不是重复就移位。把比较元素赋值为移位的元素。
class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length <= 1){
return nums.length;
}
int index = 0;
int originalVal = nums[0];
for(int i = 1; i < nums.length; i++){
if(nums[i] != originalVal){
index++;
nums[index] = nums[i];
originalVal = nums[i];
}
}
return index+1;
}
}
4)
删除排序数组中的重复项||
给定一个增序排列数组 nums ,你需要在
原地 删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3]
解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。 你不需要考虑数组中超出新长度后面的元素。
示例 2:
输入:nums = [0,0,1,1,1,1,2,3,3]
输出:7, nums = [0,0,1,1,2,3,3]
解释:函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。 你不需要考虑数组中超出新长度后面的元素。
解题思路:
跟上题增加一个重复次数。如果相等,单重复次数小于2的话正常移位。
class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length <= 2){
return nums.length;
}
int index = 0;
int duplicate = 1;
int original = nums[0];
for(int i = 1; i < nums.length; i++){
if(original == nums[i] && duplicate < 2){
duplicate++;
index++;
nums[index] = nums[i];
}
if(original != nums[i]){
original = nums[i];
duplicate = 1;
index++;
nums[index] = nums[i];
}
}
return index + 1;
}
}
- 数组查询排序
1) 颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
进阶:
你可以不使用代码库中的排序函数来解决这道题吗?
你能想出一个仅使用常数空间的一趟扫描算法吗?
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
示例 3:
输入:nums = [0]
输出:[0]
示例 4:
输入:nums = [1]
输出:[1]
解题思路:数组只有3种颜色, 把0往数组前端移,2往数组后端移。中间剩余的就是1。
class Solution {
public void sortColors(int[] nums) {
int length = nums.length;
int redIndex = 0;
int blueIndex = length - 1;
for(int i = 0; i <= blueIndex; i++){
if(nums[i] == 0){
nums[redIndex++] = 0;
} else if(nums[i] == 2){
while(nums[blueIndex] == 2 && blueIndex > i){
blueIndex--;
}
if(nums[blueIndex] == 0){
nums[redIndex++] = 0;
}
nums[blueIndex] = 2;
blueIndex--;
}
}
for(int k = redIndex; k <= blueIndex; k++){
nums[k] = 1;
}
}
}
2). 数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
解题思路: 数组先进行排序,找出第k个元素。
class Solution {
public int findKthLargest(int[] nums, int k) {
Arrays.sort(nums);
return nums[nums.length - k];
}
}
3) 合并两个有序数组
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
初始化 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]
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int totalLength = m + n;
while(m > 0 && n > 0) {
if(nums2[n - 1] > nums1[m - 1]){
nums1[totalLength - 1] = nums2[n-1];
n--;
} else{
nums1[totalLength - 1] = nums1[m-1];
m--;
}
totalLength--;
}
if(n > 0){
for(int i = 0; i < n; i++){
nums1[i] = nums2[i];
}
}
}
}
- 双指针收缩
1)两数之和
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
解题思路:本题的数组是升序排列的, 可以通过双指针收缩来找到结果。
class Solution {
public int[] twoSum(int[] numbers, int target) {
int low = 0;
int high = numbers.length - 1;
int sum = 0;
int [] result = new int[2];
while(low < high){
sum = numbers[low] + numbers[high];
if(sum == target){
result[0] = low + 1;
result[1] = high + 1;
return result;
} else if(sum < target) {
low++;
} else {
high--;
}
}
return result;
}
}
2) 验证回文串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama"
输出: true
示例 2:
输入: "race a car"
输出: false
解题思路:回文串就是左和右相同位置的字符相同。利用双指针夹击对撞
class Solution {
public boolean isPalindrome(String s) {
int first = 0;
int last = s.length() - 1;
char[] strArray = s.toCharArray();
while(first < last){
if(!Character.isLetterOrDigit(strArray[first])){
first++;
continue;
}
if(!Character.isLetterOrDigit(strArray[last])){
last--;
continue;
}
if(Character.toLowerCase(strArray[first]) != Character.toLowerCase(strArray[last])){
return false;
}
first++;
last--;
}
return true;
}
}
3). 反转字符串中的元音字母
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
示例 1:
输入:"hello"
输出:"holle"
示例 2:
输入:"leetcode"
输出:"leotcede"
解题思路:用双指针对碰,前后找元音字母。前后都找到后互换位置。
class Solution {
private List<Character> vowels = Arrays.asList('a','e','i','o','u','A','E','I','O','U');
public String reverseVowels(String s) {
int first = 0;
int last = s.length() - 1;
char[] chars = s.toCharArray();
while(first < last){
if(!isVowel(chars[first])){
first++;
continue;
}
if(!isVowel(chars[last])){
last--;
continue;
}
char temp = chars[first];
chars[first] = chars[last];
chars[last] = temp;
first++;
last--;
}
return String.valueOf(chars);
}
private boolean isVowel(char c){
if(vowels.contains(c)){
return true;
} else{
return false;
}
}
}
3). 盛最多水的容器
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
示例 3:
输入:height = [4,3,2,1,4]
输出:16
示例 4:
输入:height = [1,2,1]
输出:2
解题思路:使用双指针,前后夹击。我们会使用变量 maxarea 来持续存储到目前为止所获得的最大面积。 在每一步中,我们会找出指针所指向的两条线段形成的区域,更新 maxarea,并将指向较短线段的指针向较长线段那端移动一步。
class Solution {
public int maxArea(int[] height) {
int begin = 0;
int end = height.length - 1;
int oriBegin = height[0];
int oriEnd = height[end];
int maxVolume = 0;
while (begin < end) {
if(oriBegin > height[begin]){
begin++;
continue;
}else {
oriBegin = height[begin];
}
if(oriEnd > height[end]){
end--;
continue;
} else{
oriEnd = height[end];
}
maxVolume = Math.max(maxVolume, (end - begin) * (Math.min(height[begin], height[end])));
if(height[begin] > height[end]){
end--;
}
else{
begin++;
}
}
return maxVolume;
}
}
4) 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
解题思路: 通过快慢双指针,快指针向前移动,判断组合是否大于目标值。小于目标值则继续往前移动。如果大于目标值了,就记下组合的长度。开始移动慢指针,如果大于目标值记下数组长度。如果小于目标值,则移动快指针。直到查找完毕。
class Solution {
public int minSubArrayLen(int s, int[] nums) {
if(nums.length == 0){
return 0;
}
if(nums[0] >= s){
return 1;
}
int begin = 0;
int end = 1;
int minLength = nums.length + 1;
int sum = nums[begin];
while(end < nums.length) {
if(nums[end] >= s){
return 1;
}
sum += nums[end];
if(sum < s){
end++;
}
else{
minLength = Math.min(minLength, end - begin + 1);
while((sum = sum - nums[begin++]) >= s){
minLength = Math.min(minLength, end - begin + 1);
}
end++;
}
}
if(minLength == nums.length + 1){
return 0;
}
return minLength;
}
}
- 数组操作
1) 寻找数组的中心索引
给定一个整数类型的数组 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
解释:
数组中不存在满足此条件的中心索引。
解题思路:从题意可以得出中间分隔左右两边相等, 即leftSum + rightSum + num[i] = totalsum, leftsum = rightsum.
最后得出结果是 2 * leftsum = totalsum – nums[i]; i即为中间索引。
class Solution {
public int pivotIndex(int[] nums) {
if(nums.length <= 2){
return -1;
}
int sum = 0;
for(int num : nums) {
sum += num;
}
int leftSum = 0;
for(int i = 0; i < nums.length; i++) {
if((leftSum << 1) == sum - nums[i]) {
return i;
}
leftSum += nums[i];
}
return -1;
}
}
2) 搜索插入位置
本题使用二分查找法。
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left < right) {
int middle = left + ((right - left) >> 1);
if(nums[middle] == target) {
return middle;
}
else if(nums[middle] > target){
right = middle - 1;
} else {
left = middle + 1;
}
}
if(target > nums[left]){
return left + 1;
} else{
return left;
}
}
}
3) 合并区间
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: intervals = [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: intervals = [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
解题思路:把二维数组进行升序排序,进行依次比较前一个的后坐标和后一个的前坐标,判断是否有交叉。没交叉直接放入合并列表,有交叉则直接修改列表中的后坐标。
class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, Comparator.comparingInt((int[] num) -> num[0]));
List<int[]> mergedList = new ArrayList<>();
for(int i = 0; i < intervals.length; i++) {
int low = intervals[i][0];
int high = intervals[i][1];
if(mergedList.size() == 0 || mergedList.get(mergedList.size() - 1)[1] < low){
mergedList.add(intervals[i]);
} else{
mergedList.get(mergedList.size() - 1)[1] = Math.max(mergedList.get(mergedList.size() - 1)[1], high);
}
}
return mergedList.toArray(new int[mergedList.size()][]);
}
}
其他:
1. 数组拆分
给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大。
返回该 最大总和 。
示例 1:
输入:nums = [1,4,3,2]
输出:4
解释:所有可能的分法(忽略元素顺序)为:
1. (1, 4), (2, 3) -> min(1, 4) + min(2, 3) = 1 + 2 = 3
2. (1, 3), (2, 4) -> min(1, 3) + min(2, 4) = 1 + 2 = 3
3. (1, 2), (3, 4) -> min(1, 2) + min(3, 4) = 1 + 3 = 4
所以最大总和为 4
示例 2:
输入:nums = [6,2,6,5,1,2]
输出:9
解释:最优的分法为 (2, 1), (2, 5), (6, 6). min(2, 1) + min(2, 5) + min(6, 6) = 1 + 2 + 6 = 9
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/c24he/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解题思路:先排序,使配对的组合损失尽量减少。
2. 寻找旋转排序数组中的最小值
假设按照升序排序的数组在预先未知的某个点上进行了旋转。例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] 。
请找出其中最小的元素。
示例 1:
输入:nums = [3,4,5,1,2]
输出:1
示例 2:
输入:nums = [4,5,6,7,0,1,2]
输出:0
示例 3:
输入:nums = [1]
输出:1
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/c3ki5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解题思路:二分查找。
3.两个数组的交集 II
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。
我们可以不考虑输出结果的顺序。
进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/hash-table/xx5hsd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法一:使用HashMap来存储数组1中每个元素出现的次数。然后比较数组2。此方法适合,数组2很大,存储在磁盘上,不能一次加载到内存。
方法2:数组排序好后,对数组元素依次一一比较。
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58o46i/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解题思路:双指针依次往后遍历,暴力解法。其中可以发现有优化点,就是前一个比较过的组合,在下次比较时可以借用。如果弹出的数不是上次组合中的最大值,那么只要比较上个组合的最大值和新加入的数,取大者,可以大量减少重复比较。
给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。
示例:
输入: [1,2,4,7,10,11,7,12,6,7,16,18,19] 输出: [3,9]
提示:
0 <= len(array) <= 1000000
class Solution { public int[] subSort(int[] array) { if(array.length == 0) { return new int[] {-1, -1}; } int maxVal = array[0]; int minVal = array[array.length - 1]; int m = -1, n = -1; for(int i = 0; i < array.length; i++) { if(array[i] >= maxVal) { maxVal = array[i]; } if(array[i] < maxVal) { n = i; } } for(int j = array.length - 1; j >= 0; j--) { if(array[j] <= minVal) { minVal = array[j]; } if(array[j] > minVal) { m = j; } } return new int[] {m, n}; } }
31. 下一个排列
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]
示例 4:
输入:nums = [1]
输出:[1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/next-permutation
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution { public void nextPermutation(int[] nums) { int len = nums.length; int i = len - 2; //从后往前找到第一个前面的数比后面的数小的数的位置 while(i >= 0 && nums[i] >= nums[i+1]) { i--; } //如果找到,就从后往前找到第一个比上面的数大的数。 if(i >= 0) { int j = len - 1; while(j >= 0 && nums[i] >= nums[j]) { j--; } //找到后进行交换 swap(nums, i, j); } //对后面的数进行从小到大重排列, reverse(nums, i + 1, len-1); } private void swap(int[] nums, int start, int end) { int temp = nums[start]; nums[start] = nums[end]; nums[end] = temp; } private void reverse(int[] nums, int begin, int end) { while(begin < end) { swap(nums, begin, end); begin++; end--; } } }

浙公网安备 33010602011771号