刷题记录及题目理解
完成题目编号:27、80、125、209、3
一.题目编号:27
- 题目描述
给你一个数组 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。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。
- 题目理解
对数组进行原地操作,不可以赋值新数组。 3. 逻辑思路 原地操作数组,可以采用双指针的思想,双指针又分左左指针,和左右指针两种方法 感谢小虎男神做出的总结
数组双指针操作总结: 按照双指针分布样式(左右|左左) 1.两指针(l/cur)均在左边:cur用于遍历数组,l是被操作索引,用于指向(盯住)被操作数。当cur没遇到被操作数时,l/cur需要进行操作(不论是交换还是赋值),然后分别向前走一位;当cur遇到了被操作数时,cur往前走。(此时对l不予操作,是要让l盯住被操作数)。 2. 两指针(cur/r)一左一右: cur用于遍历数组,r是被操作索引,等待cur与其进行操作。当cur没遇到被操作数时,cur接着走;当cur遇到了被操作数时,cur/r需要进行操作(交换或赋值,注意会改变原来的数组顺序),并且操作完后r向左走一步,但cur不能动(因为不能保证操作过后的元素满足条件,毕竟右边的数是没有经过保证的),等待下一次判定。
- 优解代码
class Solution {
public int removeElement(int[] nums, int val) {
int right =nums.length-1;
int current =0;
//current和right相等时,比较最后一个元素值
while(current <= right){
if(nums[current] == val ){
swap(nums,current,right);
right--;
//current 不进行操作
}else{
current++;
}
}
return right+1;
}
public void swap(int[] nums,int current,int right){
int temp = nums[current];
nums[current] = nums[right];
nums[right] = temp;
}
}
- 优解理解
当左右指针所指数字进行操作后,需要注意交换后左指针所指的数字未进行判断。 6. 总结
二、题目编号:80
- 题目描述
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 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 。
你不需要考虑数组中超出新长度后面的元素
- 题目理解 相同的元素最多出现两次,多出来的元素需要原地删除。
- 逻辑思路 应用索引对当前元素和下一索引元素进行比较,使用计数器记录元素出现次数。
- 优解代码
class Solution {
public int removeDuplicates(int[] nums) {
//
// 初始化计数器,覆盖位置的索引
//
int j = 1, count = 1;
//
// 从数组的第二个元素开始,一个一个进行判断
//
for (int i = 1; i < nums.length; i++) {
//
// 如果当前元素重复,计数器加一
//
if (nums[i] == nums[i - 1]) {
count++;
} else {
//
// 如果当前元素和前一个元素不重复,计数器置一
// than the previous one.
//
count = 1;
}
//
// 如果判断出的元素计数小于等于2,则将当前元素赋值给j,j加1
//
if (count <= 2) {
nums[j++] = nums[i];
}
}
return j;
}
}
-
优解理解 直接将数组元素进行重新赋值。
-
提交代码
class Solution {
public void swapToLast(int[] nums,int i){
for(int k =i;k<nums.length-1;k++){
int temp = nums[k];
nums[k] = nums[k+1];
nums[k+1]=temp;
}
}
public int removeDuplicates(int[] nums) {
int count =0;
int push =0;
//int temp1=0;
int time =0;
if(nums.length <=2){
return nums.length;
}
for(int i =0;i<nums.length-1;i++){
if(i == nums.length-time-1){
break;
}
if(nums[i]-nums[i+1]==0){
count++;
if(count>=2){
push ++;
}
}else if(count >=2){
int k =i;
for(int j=0;j<count-1;j++){
swapToLast(nums,k-j);
i--;
time++;
}
count =0;
}
else{
count =0;
}
}
return nums.length-push;
}
}
- 总结 提交的答案实现的比较臃肿,思路稍混乱。
三、题目编号:125
- 题目描述
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明: 本题中,我们将空字符串定义为有效的回文串。
示例1:
输入: "A man, a plan, a canal: Panama" 输出: true
示例2:
输入: "race a car" 输出: false
-
题目理解 空字符串定义为有效回文串。
-
逻辑思路 可以使用双指针,判断头尾指针索引指向的元素。
-
优解代码
class Solution {
public boolean isPalindrome(String s) {
StringBuilder sb = new StringBuilder();
for(int i =0;i<s.length();i++){
if(Character.isLetterOrDigit(s.charAt(i))){
sb.append(s.charAt(i));
}
}
int left =0;
int right =sb.length()-1;
while(left<right){
if(Character.toLowerCase(sb.charAt(left)) != Character.toLowerCase(sb.charAt(right))){
return false;
}
left++;
right--;
}
return true;
}
}
-
优解理解 利用StringBuilder取出其中的letter 或者digit ,最后用toLowerCase函数全部转为小写。
-
问题代码思路 isLetterOrDigit函数和toLowerCase函数的使用。
-
总结 无话可说
四、题目编号:209
- 题目描述 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
示例:
输入: s = 7, nums = [2,3,1,2,4,3] 输出: 2 解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
进阶:
如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。
- 题目理解 数组未排序,需要找出连续子数组。
- 逻辑思路 滑动窗口法:。
- 优解代码
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int left =0;
int right =0;
int sum =0;
int minLength = nums.length+1;
if(minLength == 0){
return 0;
}
while(right < nums.length){
sum =sum + nums[right];
right++;
while(sum >= s){
minLength = (right - left)<minLength? (right -left):minLength;
sum = sum - nums[left];
left++;
}
}
return minLength == nums.length +1? 0:minLength;
}
}
- 优解理解
- 问题解代码
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int left =0;
int right =0;
int sum =0;
int minLength = nums.length;
if(minLength == 0){
return 0;
}
while(right < nums.length){
if(sum < s){
sum =sum + nums[right];
right++;
}else{
minLength = (right - left)<minLength? (right -left):minLength;
sum = sum - nums[left];
left++;
}
}
return minLength;
}
}
- 总结 问题解代码存在的问题在于最后一个索引指向的位置始终没有被加到,因此不需要判断加和大于目标数的情况,只需要考虑加和小于等于目标数的情况,当出现数组全部元素加和仍然小于目标数的情况,要考虑返回结果的选择:首先定义一个不可能的结果,之后进行判断,如果结果被更新,则返回结果,否则,未出现加和大于等于目标数情况,返回0。
五、题目编号:3
- 题目描述 给定一个字符串,请你找出其中不含有重复字符的最长子串的长度
示例1:
输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例2:
输入: "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例3:
输入: "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是子串的长度,"pwke" 是一个子序列,不是子串。
- 题目理解 子串必须是连续的字符 子序列可以是不连续的字符
- 逻辑思路 滑动窗口法:窗口是索引为i到j的左右端点构成的区间,两端都可以滑动。构建set集合,可以在集合中添加不重复的元素,同时右端点滑动,更新最大子串长度,遇到子串中重复的字符,移除当前左端点的元素,直到左端点和右断点都移动到数组尾端
- 优解代码
class Solution {
public int lengthOfLongestSubstring(String s) {
int sLength = s.length();
int i = 0;
int j = 0;
int result =0;
Set<Character> hashSet = new HashSet<>();
while(i<sLength&&j<sLength){
if(!hashSet.contains(s.charAt(j))){
hashSet.add(s.charAt(j++));
result = Math.max(result,j-i);
}else{
hashSet.remove(s.charAt(i++));
}
}
return result;
}
}
-
优解理解 hashSet可以加入不重复的元素。else下的代码是关键
-
总结