第1次
题目链接:移动零
题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
解题思路:对数组进行遍历,将遍历到的非零元素依次往数组里装,从第一个位置开始装,之后再按照数组的大小在该数组后面补0。定义两个指针,第一个为index,为装非零数的指针,第二个为for循环里的遍历指针。
时间复杂度:O(N)
空间复杂度:O(1)。
代码:
public void moveZeroes(int[] nums) {
int index = 0;
for(int i=0; i < nums.length; i++){
if(nums[i] != 0)
nums[index++] = nums[i];
}
while(index < nums.length){
nums[index++] = 0;
}
}
类似引申:
27. 移除元素
26. 删除排序数组中的重复项
第1次
题目链接:最大连续1的个数
题目描述:
给定一个二进制数组, 计算其中最大连续1的个数。
解题思路:利用增强for循环进行遍历,遇到1将cnt加上1,同时将结果暂存,遇到0将cnt置0,然后取暂存结果的最大值。
代码:
class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int result = 0;
int cnt = 0;
int pase = 0;
for(int num : nums){
if(num == 1){
cnt++;
pase = cnt;
}
else {
cnt = 0;
}
result = Math.max(result,pase);
}
return result;
}
}
/*参考精简写法
public int findMaxConsecutiveOnes(int[] nums) {
int max = 0, cur = 0;
for (int x : nums) {
cur = x == 0 ? 0 : cur + 1;
max = Math.max(max, cur);
}
return max;
}
*/
第1次
题目链接:嵌套数组
题目描述:
索引从0开始长度为N的数组A,包含0到N - 1的所有整数。找到并返回最大的集合S,S[i] = {A[i], A[A[i]], A[A[A[i]]], … }且遵守以下的规则。
假设选择索引为i的元素A[i]为S的第一个元素,S的下一个元素应该是A[A[i]],之后是A[A[A[i]]]… 以此类推,不断添加直到S出现重复的元素。
解题思路:每一个最终结果都是一个圆圈,不管从圆圈的哪个元素开始,回到原来位置时遍历到的长度都是一样的,不同的集合之间互不相交,所以可以将已经遍历的元素标记为-1,知道数组所有元素都变成-1时,则结束遍历,取最大长度的圈(即集合)。
代码:
class Solution {
public int arrayNesting(int[] nums) {
int length = 0;
for(int i = 0; i < nums.length; i++){
int cnt = 0;
for(int j = i; nums[j] != -1;){
cnt++;
int t = nums[j];
nums[j] = -1;
j = t;
}
length = Math.max(length,cnt);
}
return length;
}
}
第0次
题目链接:分隔数组
题目描述:
按照下面的规则,最多能将数组分成多少块?
解题思路:
代码:
第1次
题目链接:删除排序数组中的重复项 II
题目描述:
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
解题思路:将遍历到的符合条件的数组中元素从数组头开始,从前往后填。
代码:
我的垃圾代码:
public int removeDuplicates(int[] nums) {
if(nums.length == 0)
return 0;
int index = 1;
int cnt = 1;
int val = nums[0];
for(int i=1; i < nums.length; i++){
if(nums[i] == val && cnt == 1){
nums[index++] = nums[i];
cnt++;
}
else if(nums[i] != val){
nums[index++] = nums[i];
val = nums[i];
cnt = 1;
}
}
return index;
}
改进后代码:大赞
public int removeDuplicates(int[] nums) { //太优秀优秀优秀了了了
int i = 0;
for (int n : nums)
if (i < 2 || n > nums[i-2])
nums[i++] = n;
return i;
}
第1次
题目链接:颜色分类
题目描述:
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
解题思路:三路快排
重要注意:对于数组元素交换,一定是:public void swap(int[] nums, int i, int j),而不是:public void swap(int i, int j)
错误的写法为:
public void sortColors(int[] nums) { //错误
swap(nums[i],nums[j);
}
public void swap(int i,int j){
int temp = j;
j = i;
i = temp;
}
代码:
public void sortColors(int[] nums) {
int zero = -1;
int two = nums.length;
for(int i=0;i < two;){
if(nums[i] == 1)
i++;
else if(nums[i] == 2){
two--;
//swap(nums[two],nums[i]);
swap(nums,two,i);
}
else {
zero++;
//swap(nums[zero],nums[i++]);
swap(nums,zero,i++);
}
}
}
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
第1次
题目链接:重塑矩阵
题目描述:
给出一个由二维数组表示的矩阵,以及两个正整数r和c,分别表示想要的重构的矩阵的行数和列数。
重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。
如果具有给定参数的reshape操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。
解题思路:首先判断重塑前后两个矩阵的元素个数是否一致,一致则可以重塑,不一致则不可以重塑,返回原矩阵;然后,利用双重for循环,将原矩阵的元素装入所创建的重塑矩阵。
代码:
class Solution {
public int[][] matrixReshape(int[][] nums, int r, int c) {
if(nums.length * nums[0].length != r*c){
return nums;
}
int m = 0;
int n = 0;
int [][] matrix = new int[r][c];
for(int i = 0; i < r; i++){
for(int j = 0; j < c; j++){
matrix[i][j] = nums[m][n];
if(n == nums[0].length - 1){
m++;
n = 0;
}
else
n++;
}
}
return matrix;
}
}
第0次
题目链接:搜索二维矩阵 II
题目描述:
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
解题思路:
从最右上角的元素开始一行或者一列地搜索,也就是从例子中的15开始搜索,要是目标值target比15大,则减去15所在的行,要是目标值target比15小,则你去15所在的列,在剩余部分搜索。另外,判断一个二维矩阵是否为空的方法:
1、二维数组首地址是否为空,即array == null;
2、二维数组是否为 {},即 array.length == 0的情况;
3、二维数组是否为{{}},即array.length == 1 && array[0].length == 0的情况;
综上所述,Java中判断二维数组为空的条件为:
if((array==null||array.length==0)||(array.length==1&&array[0].length==0));
转自参考:判断二维矩阵是否为空
代码:
public boolean searchMatrix(int[][] matrix, int target) {
if((matrix==null||matrix.length==0)||(matrix.length==1 && matrix[0].length==0)){
return false;
}
int r = matrix.length - 1;
int x = 0;
int y = matrix[0].length - 1;
while(x <= r && y >= 0){
if(matrix[x][y] == target){
return true;
}
else if(matrix[x][y] > target){
y--;
}
else if(matrix[x][y] < target){
x++;
}
}
return false;
}
第0次
题目链接:有序矩阵中第K小的元素
题目描述:
解题思路:
代码:
第1次
题目链接:托普利茨矩阵
题目描述:
如果一个矩阵的每一方向由左上到右下的对角线上具有相同元素,那么这个矩阵是托普利茨矩阵。
给定一个 M x N 的矩阵,当且仅当它是托普利茨矩阵时返回 True。
解题思路:用两个for循环,从矩阵的第一行和第一列的每一个元素进行判断,即判断以该元素开始的右斜对角线的元素值是否相等。
代码:
class Solution {
public boolean isToeplitzMatrix(int[][] matrix) {
boolean result = true;
for(int i = 0; i < matrix[0].length; i++){
result = result && check(matrix,0,i);
}
for(int i = 1; i < matrix.length; i++){
result = result && check(matrix,i,0);
}
return result;
}
public boolean check(int[][] matrix, int row, int col){
int target = matrix[row][col];
while(row <= matrix.length-1 && col <= matrix[0].length-1){
if(matrix[row][col] == target){
row++;
col++;
}
else {
return false;
}
}
return true;
}
}
第0次
题目链接:两数之和
题目描述:
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
解题思路:
暴力解法O(N^2):使用两个for循环进行遍历;
时间复杂度O(N):定义两个指针,一个指向数组头部,一个指向数组尾部,在一定条件下分别向中间遍历;
时间复杂度O(Nlog(N)):对数组进行扫描一遍,在扫描到每一个值的时候,使用二分查找;
代码:
时间复杂度O(N),空间O(1):
public int[] twoSum(int[] numbers, int target) {
int i = 0, j = numbers.length - 1;
while (i < j) {
int sum = numbers[i] + numbers[j];
if (sum == target) {
return new int[]{i + 1, j + 1};
} else if (sum < target) {
i++;
} else {
j--;
}
}
return null;
}
时间复杂度O(Nlog(N)):
public int[] twoSum(int[] numbers, int target) {
int[] result = new int[2];
for(int i=0; i < numbers.length-1; i++){
int l = i + 1;
int h = numbers.length - 1;
while(l < h){
int m = (l + h) >>> 1;
if(numbers[m] < target - numbers[i]){
l = m + 1;
}
else
h = m;
}
if(numbers[i] + numbers[l] == target){
result[0] = i+1;
result[1] = l+1;
}
}
return result;
}
第1次
题目链接:平方数之和
题目描述:
解题思路:
可以先给c开平方,然后在0~(int)sqrt©之间找两个数进行相加,相加过程可以采用双指针进行同时搜索;
代码:
class Solution {
public boolean judgeSquareSum(int c) {
int i = 0;
int j = (int)Math.sqrt(c);
while(i <= j){
if(i*i + j*j > c){
j--;
}
else if(i*i + j*j < c){
i++;
}
else {
return true;
}
}
return false;
}
}
第1次
题目链接:反转字符串中的元音字母
题目描述:
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
解题思路:
解法一:先将字符串s转成字符数组,然后用双指针的方法分别从两头进行遍历,先遍历左边,遍历到元音字母后,遍历右边,两边都遍历到元音字母后交换,然后两边指针都加一,由此循环,直到i < j为止(如果条件是i <= j,则下面的交换条件也是i <= j),最后再将字符数组转成字符串;
解法二:创建一个新数组,使用双指针,从两边进行遍历,然后将遍历到的字符按序填入数组中,再将字符数组转换成字符串;
代码:
解法一代码:
class Solution {
private final static HashSet<Character> vowels = new HashSet<>(
Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
public String reverseVowels(String s) {
char[] chs = s.toCharArray();
int i = 0;
int j = chs.length - 1;
while(i < j){
while(!vowels.contains(chs[i]) && i < chs.length - 1){
i++;
}
while(!vowels.contains(chs[j]) && j > 0){
j--;
}
if(i < j){
swap(chs,i,j);
i++;
j--;
}
}
String s2 = new String(chs);
return s2;
}
public void swap(char[] chs, int i, int j){
char t = chs[i];
chs[i] = chs[j];
chs[j] = t;
}
}
解法二代码:
class Solution {
private final static HashSet<Character> vowels = new HashSet<>(
Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
public String reverseVowels(String s) {
int i = 0, j = s.length() - 1;
char[] result = new char[s.length()];
while (i <= j) {
char ci = s.charAt(i);
char cj = s.charAt(j);
if (!vowels.contains(ci)) {
result[i++] = ci;
} else if (!vowels.contains(cj)) {
result[j--] = cj;
} else {
result[i++] = cj;
result[j--] = ci;
}
}
return new String(result);
}
}
第0次
题目链接:验证回文字符串
题目描述:
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
解题思路:
代码:
第0次
题目链接:合并两个有序数组
题目描述:
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
解题思路:
代码:
第1次
题目链接:环形链表
题目描述:
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
解题思路:定义两个指针p1和p2,p1指向head,p2指向head.next,往后遍历,p1一次走一步,p2一次走两步,如果有环,则p1和p2一定会相遇(因为p2每次比p1多走一步,肯定会再次追上p1),使用while循环,while循环的条件只需要判断p2和p2.next是否为空就行,不需要判断p1,因为p2走在p1前面。
代码:
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null){
return false;
}
ListNode p1 = head;
ListNode p2 = head.next;
while(p2 != null && p2.next != null){
p1 = p1.next;
p2 = p2.next.next;
if(p1 == p2){
return true;
}
}
return false;
}
}
第0次
题目链接:通过删除字母匹配到字典里最长单词
题目描述:
给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。
解题思路:注意字典的顺序
代码:
第1次
题目链接:验证回文串
题目描述:
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
解题思路:首先将字符串中的字母全部转成大写字母,然后双指针。
代码:
public boolean isPalindrome(String s) {
int l = 0;
int h = s.length() - 1;
s = s.toUpperCase();
while(l < h){
while(l < h && (s.charAt(l) < '0' || (s.charAt(l) > '9' && s.charAt(l) < 'A') ||
s.charAt(l) > 'Z')){
l++;
}
while(l < h && (s.charAt(h) < '0' || (s.charAt(h) > '9' && s.charAt(h) < 'A') ||
s.charAt(h) > 'Z')){
h--;
}
if(s.charAt(l) != s.charAt(h))
return false;
else{
l++;
h--;
}
}
return true;
}
第1次
题目链接:Container With Most Water
题目描述:
解题思路:使用双指针,左右两边,找到墙高的那一边先保持不变,然后又矮的那一边向高的一边移动,直到两边墙重合。因为只有找到更高的墙,才能形成更大的容量,并且一个巴掌拍不响,找到一边高的墙后,必须从另一边开始寻找,才能有机会找到更大的容量池子。
代码:
public int maxArea(int[] height) {
int l = 0;
int h = height.length - 1;
int area = 0;
while(l < h){
area = Math.max(area,(h-l)*Math.min(height[l],height[h]));
if(height[l] >= height[h]){
h--;
}
else {
l++;
}
}
return area;
}
第1次
题目链接:长度最小的子数组
题目描述:
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
解题思路:使用同向双指针,而且双指针包括的窗口大小在不断调整,h < nums.length - 1 && sum < s 则++h,否则h >= nums.length - 1 || sum >= s,则l++,在满足sun >= s时进行长度大小判断。
代码:
参考优秀代码:建议将这个双指针代码作为模板代码
public int minSubArrayLen(int s, int[] nums) {
int l = 0;
int h = -1;
int sum = 0;
int result = nums.length + 1;
while(l < nums.length){
if(h < nums.length - 1 && sum < s){
sum = sum + nums[++h];
}
else {
/* 这段代码也可以放里面
if(sum >= s){
result = Math.min(result,h-l+1);
} */
sum = sum - nums[l++];
}
if(sum >= s){
result = Math.min(result,h-l+1);
}
}
if(result == nums.length + 1)
return 0;
else
return result;
}
O(n2)级别算法:
class Solution {
private int min = 0;
public int minSubArrayLen(int s, int[] nums) {
int sum = 0;
for(int i=0; i < nums.length; i++){
sum += nums[i];
}
min = nums.length;
minSubArrayLen(nums,0,nums.length-1,sum,s);
return sum < s ? 0 : min;
}
public void minSubArrayLen(int[] nums,int l,int r,int sum,int s){
if(l > r)
return ;
if(sum - nums[l] >= s){
min = Math.min(min,r-l);
minSubArrayLen(nums,l+1,r,sum-nums[l],s);
}
if(sum - nums[r] >= s){
min = Math.min(min,r-l);
minSubArrayLen(nums,l,r-1,sum-nums[r],s);
}
}
}
第1次
题目链接:添加链接描述
题目描述:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
解题思路:同向双指针,在参考代码中,使用大小为256的记忆频率数组(巧妙),然而我的代码中使用传统的for循环判断是否包含某个元素(暴力),不过我能写出来,也值得表扬。
代码:
参考优秀代码:建议将这个双指针代码作为模板代码
public int lengthOfLongestSubstring(String s) {
int l = 0;
int h = -1;
int result = 0;
int[] freq = new int[256];
while(l < s.length()){
if(h < s.length() - 1 && freq[s.charAt(h+1)] == 0){
freq[s.charAt(++h)] = 1;
}
else {
freq[s.charAt(l++)] = 0; //通过不断l++找到重复的,并将其置为0,然后h就可以继续向后移动啦!!!
}
result = Math.max(result,h-l+1);
}
return result;
}
我的代码::
public int lengthOfLongestSubstring(String s) {
if(s == null || s.length() == 0)
return 0;
int l = 0;
int h = 0;
int result = 1;
while(h < s.length()){
int index = notContain(s,l,h);
if(index == -1){
result = Math.max(result,h-l+1);
}
else {
l = index + 1;
}
h++;
}
return result;
}
public int notContain(String s,int l,int h){
for(int i=l; i < h; i++){
if(s.charAt(i) == s.charAt(h))
return i;
}
return -1;
}
第0次
题目链接:找到字符串中所有字母异位词
题目描述:
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
解题思路:这题归为Easy,搞笑吧。利用双指针,固定窗口,定义两个长度为26的标记数组freq_s和freq_p,分别来标记s和p,通过固定数组的移动,不断地对freq_s进行更新,并不断与freq_p进行比较,若相同,则将l添加到链表array中,并l++;
代码:
非常优秀的参考代码:
public List<Integer> findAnagrams(String s, String p) {
List<Integer> array = new ArrayList<Integer>();
if(p.length() > s.length())
return array;
int [] freq_s = new int[26];
int [] freq_p = new int[26];
int l = 0;
int h = -1;
for(int i=0; i < p.length(); i++){
freq_p[p.charAt(i) - 'a']++;
freq_s[s.charAt(++h) - 'a']++;
}
if(Arrays.equals(freq_p, freq_s))
array.add(l);
while(h < s.length()-1){
freq_s[s.charAt(++h) - 'a']++;
freq_s[s.charAt(l++) - 'a']--;
if(Arrays.equals(freq_p, freq_s))
array.add(l);
}
return array;
}