一、数组删除
1)【27】移除元素
题目链接:https://leetcode-cn.com/problems/remove-element/
我的解:
class Solution {
public int removeElement(int[] nums, int val) {
if(nums.length==0){
return 0;
}
int max=nums[0],min=nums[0];
for(int i : nums){
if(i>max){
max=i;
}
if(i<min){
min=i;
}
}
int[] list=new int[max-min+1];
int num=0;
for(int i : nums){
if(i!=val){
list[i-min]++;
}
}
for(int i=0;i<list.length;i++){
if(list[i]!=0){
nums[num]=i+min;
num++;
list[i]--;
i--;
}
}
return num;
}
}
我的思路是类似于计数排序,通过另外一个数组来对原数组计数,再将新数组中非0的元素下标放入原数组。
这个是计数排序的原理,在这个的基础之上排除等于val的值。
复杂度分析:
时间复杂度:因为是用了计数排序的思想,所以当输入的元素是n个0到k之间的整数时,它的运行时间是O(n+k)。
官方通解:
class Solution {
public int removeElement(int[] nums, int val) {
int i=0;
for(int j=0;j<nums.length;j++){
if(nums[j]!=val){
nums[i]=nums[j];
i++;
}
}
return i;
}
}
官方采用的是双指针,当nums[j] 与给定的值相等时,递增 j 以跳过该元素。只要nums[j]≠val,我们就复制nums[j] 到nums[i] 并同时递增两个索引。重复这一过程,直到 j 到达数组的末尾,该数组的新长度为i。
复杂度分析:
- 时间复杂度为O(n)
- 空间复杂度为O(1)
2)【26】删除排序数组中的重复项
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/
我的解:
class Solution {
public int removeDuplicates(int[] nums) {
int i=1;
for(int j=1;j<nums.length;j++){
if(nums[j]!=nums[j-1]){
nums[i]=nums[j];
i++;
}
}
return i;
}
}
通过第一题的思路,双指针感觉同样适用。
数组完成排序后,我们可以放置两个指针 i 和 j,其中 i 是慢指针,而 j 是快指针。只要 nums[j] = nums[j-1],我们就增加 j 以跳过重复项。当我们遇到 nums[j]≠nums[j-1] 时,跳过重复项的运行已经结束,因此我们必须把它(nums[j])的值复制到 nums[i]。然后递增 i,接着我们将再次重复相同的过程,直到 j 到达数组的末尾为止。
复杂度分析:
- 时间复杂度为O(n)
- 空间复杂度为O(1)
官方通解:
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
放置两个指针 i 和 j,其中 i 是慢指针,而 j 是快指针。只要 nums[j] = nums[i],我们就增加 j 以跳过重复项。当我们遇到 nums[j]≠nums[i] 时,跳过重复项的运行已经结束,因此我们必须递增 i,然后 把它(nums[j])的值复制到 nums[i]。接着我们将再次重复相同的过程,直到 j 到达数组的末尾为止。
复杂度分析:
- 时间复杂度为O(n)
- 空间复杂度为O(1)
3)【80】删除排序数组中的重复项Ⅱ
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/
我的解:
class Solution {
public int removeDuplicates(int[] nums) {
int i=1,j=1;
int count=1;
for(;j<nums.length;j++){
if(nums[j]==nums[j-1]){
if(count>1){
continue;
}
count++;
}else{
count=1;
}
nums[i]=nums[j];
i++;
}
return i;
}
}
我的思路还是延续上面的双指针的思路,所以即使要求发生了改变,但是总的来说还是没变,只是现在除了双指针以外,因为要使每个元素最多出现两次,所以加了一个计数器count,通过计算重复元素的个数。
我们放置两个指针 i 和 j,其中 i 是慢指针,而 j 是快指针。只要 nums[j] = nums[j-1],我们就开始判断计数器,如果计数器计数大于1说明重复了两个以上,那么就不自增,并增加 j 以跳过重复项;如果计数器计数小于等于1,那就必须把它(nums[j])的值复制到 nums[i]。然后递增 i。当我们遇到 nums[j]≠nums[j-1] 时,跳过重复项的运行已经结束,这时候我们也必须把它(nums[j])的值复制到 nums[i]。然后递增 i,接着我们将再次重复相同的过程,直到 j 到达数组的末尾为止。
改良解:
class Solution {
public int removeDuplicates(int[] nums) {
int i=1,j=2;
for(;j<nums.length;j++){
if(nums[j]!=nums[i-1]){
i++;
nums[i]=nums[j];
}
}
return i+1;
}
}
放置两个指针 i 和 j,其中 i 是慢指针,而 j 是快指针。只要 nums[j] = nums[i-1],我们就增加 j 以跳过重复项。当我们遇到 nums[j]≠nums[i-1] 时,跳过重复项的运行已经结束,因此我们必须递增 i,然后 把它(nums[j])的值复制到 nums[i]。接着我们将再次重复相同的过程,直到 j 到达数组的末尾为止,注意返回的是 i+1。
总结:
如果说有序数组要原地删除重复k个后的元素,那么只要将 i 初值设置为k-1,j 初值设为 k,然后循环判断nums[j]=nums[i-k+1],然后返回 i+k-1;
二、数组移动
1)【189】旋转数组
链接:https://leetcode-cn.com/problems/rotate-array/
解法一:
class Solution {
public void rotate(int[] nums, int k) {
int len=nums.length;
for(int i=0;i<k;i++){
int last=nums[len-1];
for(int j=0;j<len;j++){
int temp=nums[j];
nums[j]=last;
last=temp;
}
}
}
}
暴力法,双重循环,移动k次,每次向右移动一位。
复杂度分析
- 时间复杂度:O(n+k)
- 空间复杂度:O(1)
解法二:
class Solution {
public void rotate(int[] nums, int k) {
k%=nums.length;
reverse(nums,0,nums.length-1);
reverse(nums,0,k-1);
reverse(nums,k,nums.length-1);
}
public void reverse(int[] nums,int start,int end){
while(start<end){
int temp=nums[start];
nums[start]=nums[end];
nums[end]=temp;
start++;
end--;
}
}
}
翻转数组,分为三步:
- 将整个数组翻转
- 前k个数进行翻转
- 剩余部分进行翻转
复杂度分析:
- 时间复杂度:O(n),数组被翻转了三次
- 空间复杂度:O(1)
三、数组查询
1)【41】缺失的第一个正数
链接:https://leetcode-cn.com/problems/first-missing-positive/
思路
class Solution {
public int firstMissingPositive(int[] nums) {
int i=1;
for(int index=0;index<nums.length;index++){
if(i==nums[index]){
i++;
index=-1;
}
}
return i;
}
}
这是我觉得个人能够理解的解法(能力有限),就是通过遍历数组,看是否能找到和 i 相等的元素,存在就将 i 自增,然后将index清零,再去循环,直到数组遍历完都找不到对应的 i 说明这个数就是缺失的最小正整数。
缺点
题目本身难度设为难,是因为对复杂度本身有要求时间复杂度O(n),而这题解虽然也能找出缺失最小正整数,但是它的时间复杂度应该是O(n2)。
这里提供官方题解链接:
链接:https://leetcode-cn.com/problems/first-missing-positive/solution/que-shi-de-di-yi-ge-zheng-shu-by-leetcode/
四、其他
1)【299】猜数字游戏
链接:https://leetcode-cn.com/problems/bulls-and-cows/
解法一
class Solution {
public String getHint(String secret, String guess) {
StringBuilder sb = new StringBuilder();
char[] ss=secret.toCharArray();
char[] gs=guess.toCharArray();
int bulls=0;
int cows=0;
for(int i=0;i<ss.length;i++){
if(ss[i]==gs[i]){
bulls++;
ss[i]='.';
gs[i]=',';
}
}
for(int i=0;i<ss.length;i++){
for(int j=0;j<gs.length;j++){
if(ss[i]==gs[j]){
cows++;
gs[j]=',';
break;
}
}
}
return sb.append(bulls).append("A").append(cows).append("B").toString();
}
}
这种解法是最暴力的感觉,就是先计算出公牛的个数,因为公牛是数字相同并且位置相同,ss[i]==gs[i]的时候,计数器bulls自增,然后把对应的元素换了,防止后面被重复计算。然后再去算母牛,双重循环,每计算出一次就替换了,防止被重复计算。最后返回,需要注意的是:我一开始用的是bulls+“A”+cows+“B”,然后发现这种“+“拼接的方式,很消耗性能,后面改用了StringBuilder的append()方法。
解法二
class Solution {
public String getHint(String secret, String guess) {
StringBuilder sb = new StringBuilder();
int[] a=new int[10];
int[] b=new int[10];
int bulls=0;
int cows=0;
int all=0;
for(int i=0;i<secret.length();i++){
a[secret.charAt(i)-'0']++;
b[guess.charAt(i)-'0']++;
if(secret.charAt(i)-'0'==guess.charAt(i)-'0'){
bulls++;
}
}
for(int i=0;i<10;i++){
all+=Math.min(a[i],b[i]);
}
cows=all-bulls;
return sb.append(bulls).append("A").append(cows).append("B").toString();
}
}
这种解法是用两个数组a[]和b[]来计算单个数字出现的个数,在循环计数的同时,判断出公牛的个数,然后再用循环算出相同数字出现次数的最小值累加算出总的多少次相同数字。这种方法,首先我们得理解公牛是相同数字相同位置,母牛是相同数字不同位置,加起来就是相同数字有几对。所以最后母牛的个数就是用all-bulls。
这种方法耗时短很多,推荐。
2)【134】加油站
链接:https://leetcode-cn.com/problems/gas-station/
思路
这个题目本身并不是很难,如果理解透了题意中给出的隐性条件:
- 车是从左往右开的
- 所有站立的油总量要>=车子的总耗油量
- 如果从0~k站中补的油总量<车子的耗油量,那说明不通
- 假设从编号为0站开始,一直到k站都正常,在开往k+1站的时候车没有油了,那么从0~k站中任何一站都到不了k+1站,所以起点得从k+1开始
这里我主要对第四点进一步进行补充说明:
假设0 ~ k站剩余的油为rest0,i ~ k 站剩余的油为rest1,0 ~ i 站的剩余油为rest2,那么rest0-rest1=rest2,因为 0 ~ k 都是正常的,说明 0 ~ i 也没问题,rest2>0,所以rest0>rest1,所以rest0都不够k+1站消耗的,那么rest1自然是不够的。所以第四点就能够解释了。
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int run=0,rest=0,start=0;
for(int i=0;i<gas.length;i++){
run+=gas[i]-cost[i];//从起点到当前站的剩余油量
rest+=gas[i]-cost[i];//总的剩余油量
if(run<0){//如果到i站的剩余油量为负数,说明到不了
start=i+1;//起点就改成i+1
run=0;//重置从起点到当前站的剩余油量
}
}
return rest<0?-1:start;//判断总的剩余油量,小于0说明到不了
}
}
3)【274】H指数
链接:https://leetcode-cn.com/problems/h-index/
题解
class Solution {
public int hIndex(int[] citations) {
Arrays.sort(citations);
int num=0;
int len=citations.length;
for(int i=len-1;i>=0;i--){
if(citations[i]>(len-1-i)){
num++;
}
}
return num;
}
}
这一题,降序排序,然后找出满足 citations[i]>i 的个数就可以。但是没有直接降序排序的方法,所以采取升序,然后 citations[i]>(n-1-i) 同样可以达到效果。