数组篇(二)
不要纠结,干就完事了,熟练度很重要!!!多练习,多总结!!!
LeetCode905:按奇偶排序数组
给定一个非负整数数组 A,返回一个数组,在该数组中, A 的所有偶数元素之后跟着所有奇数元素。你可以返回满足此条件的任何数组作为答案。
解题思路
我们可以引入一个指针j用来存放数组中所有偶数的元素,j默认初始化为0,同时遍历数组寻找偶数元素,找到后,我们将当前位置元素与指针j指向元素进行交换,然后指针后移,同时继续遍历元素!相当于指针j负责存放我们遍历过程中寻找到的偶数元素,从索引0开始依次向后存储,这样就把偶数元素全都放在了数组前面!
代码实现
class Solution {
public int[] sortArrayByParity(int[] A) {
if (A==null||A.length==0){
return A;
}
int index=0;
for (int i=0;i<A.length;i++){
if (A[i]%2==0){
swap(A,index,i);
index++;
}
}
return A;
}
public void swap(int[] num,int i,int j){
int tmp=num[j];
num[j]=num[i];
num[i]=tmp;
}
}
LeetCode11:盛水最多的容器
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
解题思路
本题是双指针的思路!双指针初始化为数组的首尾。双指针代表的高度就是容器的两边,盛水的多少取决于最短的那个高度,所以,想要盛水多,我们每次只能让高度低的那个指针向高度高的那个指针移动,每次移动我们都计算下当前的盛水多少,将每次记录的盛水容量取最大值即可得到答案!!!
代码实现
class Solution {
public int maxArea(int[] height) {
int i=0,j=height.length-1;
int res=0;
while (i<j){
res=height[i]<height[j]?Math.max(res,(j-i)*height[i++]):Math.max(res,(j-i)*height[j--]);
}
return res;
}
}
LeetCode66:加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
解题思路
这题,可能会出现一种特殊情况,就是每一位均为9,那么加一进位后,位数也会多一位的!那么怎么来做呢?我们从数组最高位(相当于数字最低位)遍历元素,每次将该位元素加1后,然后对10进行取模运算,看是否为0,如果不为0,那么相当于没有进位,直接返回即可!如果为零,那么继续遍历重复上述步骤(下一位加1,再对10进行取模运算) ,如果数组遍历完都没有返回,那么就是出现了每位都是9的情况,这是只需重新new一个比原来大一位的数组,并将数组的首元素设为1即可,代表加1后进位的结果!
代码实现
class Solution {
class Solution {
public int[] plusOne(int[] digits) {
for (int i=digits.length-1;i>=0;i--){
digits[i]++;
digits[i]=digits[i]%10;
if (digits[i]!=0){
return digits;
}
}
int[] tmp=new int[digits.length+1];
tmp[0]=1;
return tmp;
}
}
LeetCode122:买卖股票的最佳时机Ⅱ
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。注意你不能在买入股票前卖出股票。
解题思路
这是股票系列最简单的一道题了,后面我会单独开一篇文章讲解股票系列的8道题。
本题题意可以总结出两点:1.我们每次同时只能进行一笔交易,也就是你必须卖了才能再买。2.前后一共可以进行多次交易,只要卖了就能再买,收益是每笔交易的累加。
那么解决本题的思想就是低买高卖!我们只需判断相邻两次的股价是否是上涨的,上涨就买,然后进行收益累加!否则不进行任何操作。
代码实现
class Solution {
public int maxProfit(int[] prices) {
int sum=0;
for (int i=1;i<prices.length;i++){
if (prices[i]-prices[i-1]>0){
sum+=prices[i]-prices[i-1];
}
}
return sum;
}
}
LeetCode189:旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
解题思路
这题题意是数组旋转,但是我们需要想的是,这个旋转操作可以通过什么变换的到?其他这个旋转操作等价于三次反转变换!对,三次!我们只需要将所有元素反转,然后反转前 k 个元素,再反转后面l-k个元素,就能得到想要的结果。
实际我们只需实现一个反转函数,反转函数就是依据两边中心进行元素的对应交换!
代码实现
class Solution {
public void rotate(int[] nums, int k) {
reverse(nums,0,nums.length-1);
reverse(nums,0,k%nums.length-1);
reverse(nums,k%nums.length,nums.length-1);
}
public void reverse(int[] nums,int left,int right){
while (left<right){
int tmp=nums[left];
nums[left++]=nums[right];
nums[right--]=tmp;
}
}
}
LeetCode350:两个数组的交集Ⅱ
给定两个数组,编写一个函数来计算它们的交集。
解题思路
我们当然可以通过hashmap来解!但是如果数组是有序的,该怎么来优化算法呢?我们借助双指针的思想,两个指针分别指向数组的初始元素,每次我们比较两指针元素的大小,如果指向arr1的元素大于arr2的元素,那么arr2的指针后移,相反,arr1的指针后移,如果相同我们记录元素并同时移动两个指针!
代码实现
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
int i=0,j=0,k=0;
Arrays.sort(nums1);
Arrays.sort(nums2);
while (i<nums1.length&&j<nums2.length){
if (nums1[i]<nums2[j]){
i++;
}else if (nums1[i]>nums2[j]){
j++;
}else {
nums1[k++]=nums1[i++];
j++;
}
}
return Arrays.copyOfRange(nums1,0,k);
}
}
LeetCode27:移除元素
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
解题思路
本题我们要做到原地删除。我们维护一个指针初始位置为0,用来记录移除过后还存活的元素,遍历数组当遍历元素不等于目标元素时,将元素记录在维护指针的位置,并将指针后移,当遇到等于目标元素时,指针不动,也就是不进行任何操作去寻找下一个不等于val的值!遍历完成后,返回指针下标即可!
代码实现
class Solution {
public int removeElement(int[] nums, int val) {
int j=0;
for (int i=0;i<nums.length;i++){
if (nums[i]!=val){
nums[j++]=nums[i];
}
}
return j;
}
}
LeetCode26:删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
解题思路
本题思路和上体相似,同样需要维护一个指针这个指针初始化0,我们遍历从第二个开始,因为我们要找出不同的元素,相当于每次我们都要找到和指针指向的元素不同时才可以!指针指向的是前一个不重复的元素,当我们找到下一个不重复的元素时,先是指针后移,然后将当前元素复制到指针指向的元素处!依次进行,即可剔除重复元素!
代码实现
class Solution {
public int removeDuplicates(int[] nums) {
int j=0;
for (int i=1;i<nums.length;i++){
if (nums[i]!=nums[j]){
j++;
nums[j]=nums[i];
}
}
return j+1;
}
}
剑指offer12:矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
解题思路
DFS的搜索思路!看代码体会下
代码实现
class Solution {
public boolean exist(char[][] board, String word) {
if (board.length==0||word.length()==0){
return false;
}
int rows=board.length,cols=board[0].length;
char[] words=word.toCharArray();
int[][] flag=new int[rows][cols];
for (int i=0;i<rows;i++){
for (int j=0;j<cols;j++){
if (dfs(board,i,j,flag,words,0)){
return true;
}
}
}
return false;
}
public boolean dfs(char[][] board,int i,int j,int[][] flag,char[] word,int k){
if (i<0||j<0||i>=board.length||j>=board[0].length||flag[i][j]==1||board[i][j]!=word[k]){
return false;
}
if (k==word.length-1){
return true;
}
flag[i][j]=1;
if (dfs(board,i+1,j,flag,word,k+1)||dfs(board,i-1,j,flag,word,k+1)||
dfs(board,i,j+1,flag,word,k+1)||dfs(board,i,j-1,flag,word,k+1)){
return true;
}
flag[i][j]=0;
return false;
}
}
剑指offer13:机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
解题思路
递归,一把梭!
代码实现
class Solution {
int count=0;
public int movingCount(int m, int n, int k) {
int[][] flag=new int[m][n];
search(flag,0,0,m-1,n-1,k);
return count;
}
public void search(int[][] flag,int i,int j,int m,int n,int k){
if (i<=m&&j<=n&&flag[i][j]!=1&&(indexSum(i)+indexSum(j))<=k){
count++;
flag[i][j]=1;
search(flag,i+1,j,m,n,k);
search(flag,i,j+1,m,n,k);
}
}
public int indexSum(int n){
int sum=n%10;
int tmp=n/10;
while (tmp>0){
sum+=tmp%10;
tmp/=10;
}
return sum;
}
}
总结
本题来源于Leetcode中 归属于数组类型题目。
同许多在算法道路上不断前行的人一样,不断练习,修炼自己!
如有博客中存在的疑问或者建议,可以在下方留言一起交流,感谢各位!
觉得本博客有用的客官,可以给个赞鼓励下! 嘿嘿
喜欢本系列博客的可以关注下,以后除了会继续更新面试手撕代码文章外,还会出其他系列的文章!