1.移动零
1.1题目分析
我们需要上面的数组更改成下面的样子,我们需要将0放在最后面并保持非零元素的顺序。
1.2做题方法思路
使用双指针的方法可以来解决这个问题。
1.2.1数据划分
我们将这个数组的区间划分为三个部分并用两个指针来进行划分,一个定义为cur,一个定义为dest。dest是我们处理的目标数据,cur是我们处理到哪里了。在这个划分的区间里面,[0,dest]这个区间里面都是处理过的数据,在(dest,cur)这个区间里面是存放的0,[cur,nums.size())这个区间里面都是没有处理过数据。
1.2.2数据处理
一开始让cur在数组开头,dest在开头的前一个位置。
1.如果数据是0,cur就++;
2.如果数据不是0,cur和++pest的位置上的数据就进行互换
到最后cur走到末尾时就循环结束。就完成了。
2.复写零
这个题目中虽然标注的是简单题,但是由于特殊情况比较多,需要要一一攻克比较复杂。
2.1题目分析
这个题我们需要原地更改它的数组,我们需要把零进行复写然后再把后面的数依次写入,最后超出空间的舍去。
这个我们是不可能从左往右来对这个数组进行覆盖的,这样做会使后面的数据被覆盖掉。所以我们要从后往前覆盖,我们找到从后往前的开始和结束就可以对数组进行覆盖了。
2.2做题方法思路
我的第一次思考方式
我们可以把它大致分为两步:1.找到从后开始往前覆盖的起点。2.从后往前开始覆盖。
我们定义两个指针,一个dest,一个cur。
1.找到从后面开始往前覆盖的起点。
(1)我们让dest指向数组第一个元素的前一个元素,cur指向第一个元素。
(2)判断arr[cur]是否为零:是0就让dest走两步,不是0就走一步。
(3)重复这几步到dest走到最后就停止,这样就确定好了两个指针的位置。
2.从后往前开始覆盖
(1)当arr[cur] 等于零时,就把当前dest的数据置为零,还有前一个数据置为零,dest -= 2,cur--。
(2)当arr[cur]不等于零的时候,就把arr[cur]的值赋值给arr[dest],dest--,cur--。
(3)当cur指向数组第一个元素的就结束了。
但是这个题还有两个特殊情况:一个是数组全是0,第二种是前面都是非零数据后面全是零。这两种通常都会使dest越界访问。
正确的方法:
这个可以模拟一个栈来实现:我们依次将数据push到(栈的大小为数组大小+1)栈中。满了的时候就开始top。把顶上面的元素依次此前往后去覆盖数组中的元素。这个通法可以解决所有的特殊情况。
实际上我们可以不需要开辟栈空间来模拟放置元素,我们只需要用两个指针来进行标记栈顶位置和现在需要放置的元素位置即可。我们用 top 来标记栈顶位置,用 i 来标记现在需要放置的元素位置,那么我们找到原数组中对应放置在最后位置的元素位置,然后在数组最后从该位置元素往前来进行模拟放置即可。
3.快乐数
3.1题目分析
我们这个要知道这个题怎么做需要清楚如何才可以知道怎样才能判断它是无限循环的。
我们把这两个实例走一下过程:
我们在对这个数一步一步的走的时候我们可以发现到最后都会进入一个循环第一个也可以理解为一个循环只不过这个循环的圈里面都是1。我们要做的就是找到这个圈的入口。
3.2做题方法思路
我们要找到这个圈我们需要运用一个方法叫做快慢指针法我们就把这个数据当做一个链表,我们来判断一个链表是否成环的时候就是用这个方法来判断,如果快慢指针可以相遇就有环,不相遇就不存在环。
这个题就是看看快指针和慢指针是否指向同一块区域。
我们这里可以直接使用它的数据来判断是否相等来判断成环。
但是我们用数据来直接判断的话需要看看数据是否可以存下。
巢穴原理(抽屉原理)
如果每个抽屉代表一个集合,每个苹果代表一个元素,加入有n+1个元素放到n个集合中必定有一个集合中至少有两个元素。
我们依照上方的原理可以看到下方n的数据范围,我们就可以知道2的31次方的大小为2.1x10九次方之多,我们假如n取最大的数据9999999999,10个9,我们经过计算的结果为810,那么我们的数据就在[1,810]之间,不管n是何值我们的数据一定会在810里面重复出现的,最后一定是带环的。
代码思路:
1.先定义一个fast和一个slow,让fast走两步slow走一步,当fast==slow的时候我们就跳出循环。
2.判断这两个的值是否等于1:
(1)如果等1就是快乐数。
(2)不等于1就不是快乐数。
4.盛最多水的容器
4.1题目分析
这个题的目的是想找到最大存水量的值,最大存水量的大小取决于最短的边缘和下面的宽度(v = h * w )。要保证这两个的乘积是里面最大的,可能第一次大家会想到枚举法,把每种情况的容纳的水量给算出来存到一个数组里面这肯定是不行的,时间复杂度是O(n^2),复杂度太大,运行时间通不过。
4.2做题方法思路
解法一:暴力枚举法 时间复杂度:O(n^2)
上面说过了,时间复杂度上通不过。
解法二:利用单调性,使用双指针来解决问题 时间复杂度:O(n)
我们可以按照上面的公式来思考一下:
我们使用两个指针一个指向左边第一个元素,一个指向右边第一个元素。我们用公式来判断怎样移动。我们的水容水量取决于最短的那个板子的长度,两个指针的移动是往中间移动的所以板子的高度要尽可能的用大的,在公式里面w是一直单调递减的,所以我们要控制h尽可能的大,我们移动就需要把指向较短的板子往中间移动。这样我们只需要遍历一遍就可以取到一组数据,里面一定有最大的容水量。
代码思路:
(1)定义两个指针,一个为right,一个为left,分别指向数组的右边和左边。
(2)计算此时的容水量,将容水量存在一个数组里面,再比较板子的长短,把指向板子较短的指针进行移动。重复这个步骤,直到两个指针相遇。
(3)容水量这组数据里面找到最大的容水量并返回就好了。
5.有效三角形的个数
5.1题目分析
需要在一个数组中选取三个数字来组成一个三角形,统计这个数组可以组成的三角形的个数,不能重复选择。我们判断三角形的规则是任意两边之和大于第三边,所以一个三角形需要判断三次,其实用最小的两边相加大于第三边就形了。这个题我们暴力枚举的话就是先定两个边,再来找第三条边,然后用这三条边里面较小的那条边来和最大的那条边比较,判断是否符合。这样写出来的时间复杂度是O(n^3)。显然是不行的,我们可以使用类似上一道题的方法来对数组进行控制。
5.2做题方法思路
方法一:暴力枚举(时间复杂度为O(n^3))
时间复杂度过大,不合适。
方法二:把数组变为有序数组,使用双指针,利用单调性来确定三角形三边大小关系(时间复杂度为O(n^2))
需要先把数组变成有升序的数组。
我们定义三个指针,一个left,一个right,含有一个max。我们需要将max指向数组最后一个元素,让right指向max的前一个元素,left指向第一个元素。
我们的max现在是不动的我们控制left和rigth来判断是否组成三角形。
(1)当我们的 arry[left]+arr[right] <=arr[max]的时候是不能组成三角形的,我们利用单调性可以判断把left往前移动是会让arr[left]增大的,可以让等式成立,控制left向前移动,从图可得当left移动到第三个元素时候等式arry[left]+arr[right] > arr[max]可以构成三角形,那么left再往前就都是满足的了。我们只需要用right-left这样就可以得到此时有几个符合要求了。
(2)这一次走完我们的right--,我们right指向七开始判断是否有符合的重复上面的步骤。
当max指向10时,left和right的区间判断完了,max--,right = max-1,left = 0。再重复上面两步,最后当max<2的时候循环结束。
(3)在此过程中中间添加一个计数器就可以完成计数。
6.两数之和-输入有序数组
6.1题目分析
如果大家把上面的题都理解透了的话那么这个题就十分简单了。暴力是一定不行的,时间复杂度过大。我们依旧是使用利用单调性的方法来解决这个问题。
6.2做题方法思路
这道题还是使用利用单调性使用双指针的方法来解决,一个指针left,另一个指针right,分别指向数组的最左边和最右边。我们的target的值等于arr[left]+arr[right],所以根据单调性两个指针的移动就有两种情况:
(1)arr[left]+arr[right] > target,把right向右移动。
(2)arr[left]+arr[right] < target,把left向左移动。
直到找到目标下标,存在一个数组里面返回就行。
7.三数之和
7.1题目分析
这个题一看到可能会让人觉得和上面那一题的两数之和差不多,还是有一定的区别,依旧是暴力枚举不行,需要排序后使用双指针,我们这个题的思路需要把上面的六题和五题合并在一起看。
7.2做题方法思路
(1)对数组进行一个升序的排序。
(2)我们要先固定一个数,下标为i,i一开始为0(指向数组第一个元素)。然后再在后面的区间里面去定义一个left,right。一个指向后面区间的左边第一个元素,一个指向后面区间的最后一个元素。我们要让这三个数和为0就需要nums[left] + nums[right] == -(nums[i])。这样就可以证明三个数的和为0,就存到数组里面去。如果nums[left] + nums[right] > -(nums[i]),就让right--。如果nums[left] + nums[right] < -(nums[i]),就让left++。
注意:
光靠这样做是不全面的。
(1)去重:如果nums[left] == nums[left-1],nums[right] == nums[right + 1],的时候我们就不需要再把数据存到数组里面,我们就进行对应的操作来跳过重复的数据。
(2)越界:在我们进行去重的时候肯定会出现越界的情况我们需要判断left<right,让left指针一直在right指针的后面。
按照这样的思路来完成代码,这个题就完成了。这个题的去重和防止越界是最难理清楚的。
8.四数之和
8.1题目分析
这个题的解法和三数之和差不多,就是多了一个需要固定的数,如果会做上面的三数之和那么这个四数之和是非常轻松的。
8.2做题方法思路
(1)排序
(2)先固定一个数,让三数之和等于这个数减target。
(3)利用三数之和的方法来求解
注意:
这里也需要对越界和去重进行处理。