在程序算法上我们经常会遇到 对于一个数组来讲删除奇数位,直到剩下最后一个元素。
这样的题目可以有不同的问的形式,最后呢是引出来数组按一定规则,删掉元素,问最后怎么样。
这样的问题,如果我们会利用数组本身的一些属性,那么就不用真的去删除元素,或者
再创建一个新的数组来装变化后的元素,这些操作的目的都是只有一个就是真的或者模拟删除的过程。
从数组中真的删除元素是很麻烦的,删掉一个元素之后我们要做的是将元素后面的所有元素迁移,如果是链式数组还可,但是也很麻烦,再者我们也很少说对于这样的题去用链式数组。
所以呢,要删掉数组中的元素,我们只需模拟删除就可以。
怎么做呢,下面用三个例子详细说明。
第一个借用LeetCode的一个题
删除重复元素
public int select(int[] nums)
{
int index = 0;
for(int i = 1;i<nums.length;i++)
{
if(nums[index] != nums[i])
nums[++index] = nums[i];
}
return index +1;
}
这里的删除理解成移动,我们要返回的是完成重复元素删除后的数组的长度,而不需要考虑在这个长度后的元素,所以我们返回的长度就是新数组的长度。
然后开始从原数组中筛选符合要求的数组
开始新数组的长为1,index是新数组中最后一个元素的下标。
index初始化为0,开始从i = 1位上对元素组进行扫描,当 i 位置上的元素和index上的元素不同时则将i 上的元素加入到新数组中,因为题目要求的就是删除重复,所以不重复我们就添加进去,先把index后移一位然后填入 i 位置上的元素,i 上的元素便作为新数组的最后一个元素。这样一直扫描到元素组的最后,扫描结束。
返回index+1 表示新数组的长度
整个过程就是移动,找到我们想要的元素然后增加新数组一个长度,将元素移动到新数组的最后一位上去。并没有真正删除,没有新创建空间。只是将数组上的空间重新利用。
第二个借用蓝桥杯的一道题
这个题目的描述是
从a到s 19个连续的字母重复排列106 得到2014长度的一串
每次删除奇数位置上的元素
问最后剩下的那个元素是
移除改移动
char[] str = new char[2014];
public void fill_word()//先把字符数组构建出来
{
for(int i = 0;i<106;i++)
{
for(int j=0;j<19;j++)
str[j+index] = (char)('a'+j);
index += 19;
}
}
public void check(char[] str)//按奇数移动直到有效数组长度是1
{
int len = 19;
int index= 0;
while(len != 1)
{
for(int i=1;i<len;i+=2)
{
str[index] = str[i];
index++;
}
len = index;
index= 0;
}
}
这也是把删除看成移动
按规则每次从第2个元素开始间隔的将元素移动到
新的字符数组上去,每次循环完成后有效长度就是index
index以内的元素就是筛选后的元素
当长度不为1时就继续 直到有效长度为1 那么原数组上的第一个元素就是最后剩下的元素
第三题是一个环数组,报数问题
说一个班上有41个同学每个同学按次序编号从1到41,然后同学们让围成一个圈1,2,3报数,数到2的退出来,问最后剩下的一个同学的序号是多少?
public int circle_out()
{
int[] number = new int[41];
for(int i = 0;i<number.length;i++)//给每个同学编上号
{
number[i] = i+1;
}
int len = 41;
int count = 0;//报数 计数器
int index = 0;
while(len!=1)
{
for(int i=0;i<len;i++)
{
count++;
if(count!=2)
number[index ++] = number[i];
count = count%3;
}
len = index ;
index = 0;
}
return number[0];
}
环的移除也是看做移动来解,
说新的数组里是报数一轮后剩下的同学,当计数器到二这个同学不进入新数组
index记录的是进入新数组的个数
环内就是数到头了再从头数,计数器满3变0。
最后返回第一个元素。
小结:
对于数组的删除,我们其实没有删除,就是将原来的数组经过一定规则的筛选得到的新的数组放在了原数组的前面,并记录下新数组的长度,以免下次筛选访问到旧数组的元素上去。
这样我们不用去新声明空间,直接用已有的,是节约空间的。