1 移动零
题目链接:https://leetcode.cn/problems/move-zeroes/description/
这道题要在原数组中交换位置,并且还要求算法有稳定性。我们的常规思路是划分数组,用双指针解决。
public static void doubleIndex3(int[] arr){for(int cur = 0,dest = -1 ; cur < arr.length; cur++) {if(arr[cur] != 0) {dest++;if(cur != dest) {swap(arr , cur ,dest);}}}}
我们定义两个指针,desc 和 cur。数组左到desc是非零元素,desc到cur是0元素,0到数组末尾是待处理元素。这里就体现了数组划分。我们用cur遍历整个数组,在遍历过程中未处理元素会被依次添加到0区和非0区。因此,desc指向的是非零元素的最后一个元素。
代码中,要先让desc == -1,防止数组第一个元素是0,如果arr[cur]不为0,那就让desc++,并且,我们提到,数组左边到desc是非0区,所以如果遍历到的不是0,那么就要放到非0区,而放到非0区的做法就是交换desc后一位元素和当前元素的位置。直至遍历完整个数组。
时间复杂度O(n);
空间复杂度O(1);
相同拓展:
移除相同元素:
题目链接:27. 移除元素 - 力扣(LeetCode)
代码展示:
class Solution {public int removeElement(int[] nums, int val) {int desc = -1;int cur = 0;while(cur < nums.length) {if(nums[cur] != val) {desc++;if(cur != desc){int tmp = nums[desc];nums[desc] = nums[cur];nums[cur] = tmp;}cur++;}else{cur++;}}return desc + 1;}
}
解题思路:
题目要求我们移除数组中值为value的元素,并返回其余元素的元素个数,那我们就可以将值为value的元素放到一边,不为value的元素放到另一边。最终返回的个数就是下标desc + 1即可。
2 复写零
题目链接:https://leetcode.cn/problems/duplicate-zeros/
这道题表示对数组原地操作,那我们就不申请数组,用原数组进行互换即可。
代码展示
class Solution {public void duplicateZeros(int[] arr) {int slow = 0;int fast = -1;while(fast < arr.length - 1){if(arr[slow] == 0) {fast = fast + 2;}else{fast++;}if(fast < arr.length - 1){slow++;}}if(fast == arr.length) {fast = arr.length - 2;slow--;arr[arr.length - 1] = 0;}while(slow < fast) {if(arr[slow] == 0){arr[fast--] = 0;arr[fast--] = 0;}else{arr[fast--] = arr[slow];}slow--;}}}
代码分析:
我们用的是双指针;
首先我们要找到最后一个要复写的元素;定义两个快慢指针,当慢指针等于零的时候,那么快指针走两步,慢指针走一步;此时慢指针每走一步都要判断快指针是否到达数组最后。如果到最后,那么慢指针就无需移动。注意点,要将快指针的起始值设置为1.
这个时候分析案例可知,有个特殊情况,当快指针在倒数第二个位置时,慢指针为0的话,此时快指针会走两步,那么快指针就会越界,这种情况下,当我们在复写的时候,末位置的0不需要复写,因为我们处理完这种特殊情况即可
第三步就是开始复写,每次复写完就将指针减减,直到两个指针相逢,那么此时剩下的元素均不要复写。
3 快乐数
题目链接:. - 力扣(LeetCode)
class Solution {public boolean isHappy(int count) {int slow = count;int fast = value(count);while(fast != slow) {slow = value(slow);fast = value(fast);fast = value(fast);}if(slow == 1) {return true;}return false;} public static int value(int count){int sum = 0;while (count > 0){int val = count % 10;sum = sum + val * val;count = count / 10;}return sum;}}
代码解析:
这道题类似于判断链表是否循环;
有题可知,一个数经过不断平方求和后都会循环,如果循环数均为1那么返回True,否则返回false。因此我们定义两个变量,一个一次变换两次,一个变换一次,因为形成的是圈,所以每次走两步的变量总会和每次走一步的变量相遇。所以只需要判断二者相遇的时候是否为1即可。
//以下的双指针题的原理通过单调性来实现
4 盛水最多的容器
题目链接:. - 力扣(LeetCode)
思路解析:
这道题要求我们找到数组间距乘以两元素之间乘积的最大值,我们一开始想到的暴力解法一定是将每个可能情况都枚举出来,然后比较。但是当我们稍微思考一下,就能发现,一个指针desc在0位置处,另一个指针cur在1位置,当我们后移cur时由于宽是增大的,所以当cur向后移的元素值大于当前元素值,那结果肯定是增大;我们要求的是最大值,因此当我们将两个指针放置数组两侧的时候,这样宽是最大的,因此在向中心移动过程中一旦遇到比当前值小的时候就能直接跳过。
此时我们要记住一个套路,通过单调性运用双指针的时候,两个指针应该交替都要移动,而不是一个指针固定,只让另一个指针移动;就是说,我们定义的两个指针cur和desc,哪个指针指向的元素小,哪个指针移动。
代码展示:
class Solution {public int maxArea(int[] arr) {int left = 0;int right = arr.length - 1 ;int max = 0;while(left < right){if(max < volume(arr,left,right)){max = volume(arr , left ,right);}if(arr[left] < arr[right]) {int tmp = arr[left];left++;while(left < right && arr[left] < tmp ) {left++;}}else {int tmp = arr[right];right--;while (left < right && arr[right] < tmp) {right--;}}}return max;}public static int volume(int[] arr , int left , int right){if(arr[left] < arr[right]){return arr[left] * (right - left );}else {return arr[right] * (right - left );}}
}