算法力扣刷题记录二【移除元素】

算法小白算法刷题记录【二】


前言

第二篇尝试,继续。修改过程是本文最重要的价值体现,怎么想怎么改才是关键。
记录二:移除元素。
力扣题目【27】


一、题目阅读和理解

1.题目阅读

给一个数组 nums 和一个值 val,你需要 原地 移除所有等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

假设 nums 中不等于 val 的元素数量为 k,完成:更改 nums 数组,使 nums 的前 k 个元素是包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
返回 k。

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,_,_]
解释:返回 k = 2, 并且 nums 中的前两个元素均为 2,和val相等的元素都被删掉。数组即使空了两个,也不重要
      除了返回的 k 个元素,其余的无所谓(因此它们并不计入评测)。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3,_,_,_]
解释:返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。
注意:返回的元素可以任意顺序。

提示:

	0 <= nums.length <= 100
	0 <= nums[i] <= 50
	0 <= val <= 100

2.理解题目

  1. “原地”移除元素:
    每个元素先比较之后,如果=val,删去,留下空位;如果不等于val;看前面有没有删掉的空位移到前面去。
  2. “返回的元素顺序任意”:所以我可以按上一句话来操作,不在乎排序。
  3. “返回k”:如果是固定数组,长度不变,不能用sizeof(这是全部个数,不是删掉的个数);如果用vector可以自动管理内存,那么删除之后,size()可以的。
  4. 理解评测通过条件:
  • 调用实现那一行,说明参数和返回值。
  • k用expectedNums.length断言,所以数组的长度得刚好是剩余元素的个数(第3点后一种)。
  • 元素是否正确的断言:元素删除是对原数组的直接操作。

二、第一次尝试下笔

1.代码实现

先上代码:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int k = 0; // 返回值先摆上
        int equalNum = 0;
        vector<int> equalIndex; // 记录元素=val的下标
        int i = 0;//遍历vector

        //整个while把所有元素不等于val都放到了前面,每个元素都是“原地”删除
        while(i < nums.size()){
            if(nums[i] == 1000){
                i++;
                continue;
            }else if(nums[i] == val){
                for(int j = (nums.size()-1);j >= i;j--){
                    if((nums[j] != val) && (nums[j] != 1000)){     
                        nums[i] = nums[j];
                        nums[j] =1000;          //  占着位,代表移动过,不让后面的元素前移,还不能删。
                        k++;                    //有个不相等的元素放过来了。
                        break;
                    }else if(j == i){
                        nums[i] = 1000;         //保证最后一个相等的元素被删掉
                    }
                }
            }else{
                k++;
            }
            i++;
        }

        //所有1000都弹出
        for(int i = nums.size()-1;i >=0; i--){      
            if(nums[i] == 1000)
                nums.pop_back();
        }

        return k;
    }
};

2.修改过程()

第一版本

最开始想直接nums.erase()删除元素,但是vector直接删除元素,会移动位置,自动管理内存。不符合“原地”。
所以想到遍历找出所有元素=val的下标。

	int k;//返回值
	vector<int> equalIndex;//放所有=val的元素下标
	for(int i=0;i <nums.size();i++{
			if(nums[i] == val){
					equalIndex.push_back(i);  //记下元素等于val的下标
			}
	}

行不通:我找出下标,重点是我删除得原地不能让vector元素移动,找下标没用啊。怎么删才是关键

第二版本(怎么删呢?)

  1. 直接用erase()是不行的。想到:我把后面不等于val的元素拿到这个位置不就行了

    第一步:while()此时条件还没确定,放着。while里面第一个if(nums[i] == val),在这个if里面:遍历i后面,找一个不相等的元素。我先是正向找的:
    for(int j = 0;j < nums.size();j++){
    if(nums[j] != val) //可是我正向找,循环条件行不通,逻辑不正常,循环不下去。
    };

    所以确定倒着找第一个不等于val的元素。

     	for(int j = (nums.size()-1);j >= i;j--){				//行得通,挪到前面不影响
     			if(nums[j] == val){     
                         continue;
                     }else{
                         nums[i] = nums[j];
                         break;
                     }
     	}		//第一次写成了这样,正向思维,if改成不等于更简单。
     	
     	改进:当前的while内容
     	if(nums[i] == val){
             for(int j = (nums.size()-1);j > i;j--){
                 if(nums[j] != val){     
                     nums[i] = nums[j];		//原来的nums[j]没有处理,并且没有记录k
                     break;
                 }
             }
         }else{	
             k++:
         }
         i++;
    

2.等于val的元素用nums[j]替换,可是num[j]我没处理,这不合理,num[j]已经被挪到前面了,所以我用一个不在范围的值1000,改掉nums[j]。

  • while多了if(nums[i] == 1000);

  • if((nums[j] != val) && (nums[j] != 1000))

  • 记录挪到前面那个元素,k+1

    所以当前while内容修正

      while(i < nums.size()){							//遍历整个nums,所以确定while条件。
              if(nums[i] == 1000){
                  i++;								    //换下一轮
                  continue;
              }else if(nums[i] == val){
                  for(int j = (nums.size()-1);j > i;j--){
                      if((nums[j] != val) && (nums[j] != 1000)){     
                          nums[i] = nums[j];
                          nums[j] =1000;          //  占着位,代表移动过,不让后面的元素前移,还不能删。
                          k++;                    //有个不相等的元素放过来了。
                          break;
                      }
                  }
              }else{							//正向遍历时直接不等于val的元素。
                  k++:
              }
              i++;
          }
    
  1. 好像到这里没有问题了,单走一遍用例,发现nums最后一个等于val的元素没有处理。原因:走到break;后面没有不等于val的元素了,j = i+1;进不了for循环。所以

    • for循环条件j > i,改成 j >=i;让最后一个等于val的元素换成1000.

所以最终得到了 1.代码实现 的while循环,实现“原地”删除元素。可以回过头再看一遍。(一点一点改出来的为什么这样写)

大胆的pop元素

while结束后,nums后面都是1000替换的无效元素,所以用for循环倒着pop值为1000的元素。

3.代码随想录学习

学习内容:学到了“双指针”法。

1.数组内存连续,删除中间的元素需要后面的元素向前覆盖。vector.earse()的确有覆盖前移的操作,所以时间复杂度是O(n)。
我的误区:earse前移之后,末尾空下的空间被自动回收,但视频说没有作处理。
2.vector.size()求长度,我以为是:回收了内存,才确定长度。其实是:没回收内存,确定元素个数。
3.解法一:外层for循环找到要删除的元素:找到之后内层for循环把后面的元素覆盖前移。
4.解法二:双指针法(没想到过)
	(1)定义快指针int fast;和慢指针int slow;  操作同一个数组,指针只是指向的意思,不是指针变量,int变量即可。
	(2)实现:相当于earse()
			slow = 0;
			for(int fast = 0; fast<nums.size() ; fast++){
				if(nums[fast] != val){
					nums[slow] = nums[fast];
					slow++;
				}
			}
			return slow;

不认同点:

他认为该题目只是数组删除元素,需要覆盖前移。直接erase()就是实现这个过程。
我认为这只是删除操作,不是题目中说的“原地”删除,我理解的原地删除是——找到元素位置之前,它不应该被移动,覆盖前移也算移动。
	为什么我觉得我理解正确?
	因为题目中“原地”加粗,并且后面“元素的顺序可能发生改变。”这句话说明我理解正确。如果只是覆盖前移,顺序可以认为没有变化。

欢迎讨论“原地”删除的认同。


总结

  1. 数组删除需要覆盖前移操作,使用“双指针”实现更简单方便。
  2. 关于原地删除操作,个人理解实现while过程,是真正的原地。但也是覆盖的方法。

(欢迎讨论指正)
(转载须标明出处)

  • 13
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值