题目:
给你一个整数数组 nums
。数组中唯一元素是那些只出现 恰好一次 的元素。
请你返回 nums
中唯一元素的 和
示例 1:
输入:nums = [1,2,3,2] 输出:4 解释:唯一元素为 [1,3] ,和为 4 。
示例 2:
输入:nums = [1,1,1,1,1] 输出:0 解释:没有唯一元素,和为 0 。
示例 3 :
输入:nums = [1,2,3,4,5] 输出:15 解释:唯一元素为 [1,2,3,4,5] ,和为 15
提示:
1 <= nums.length <= 100
1 <= nums[i] <= 100
方法一:桶排序的思想:
首先简单的说一下桶排序是什么:桶排序就是在我们对一些数字进行排序的时候,假设这些数字大小在0-100之间,那我们就要重新创建长度为101的数组arr,当输入一个范围内的数n时就让arr[n]++,这其实就是把n对应数组下标的数组值作为一个桶,进来一个n就让这个桶++,因为我们在创建数组的时候,数组的默认值都是0,所以我们可以遍历该数组,通过查看哪些下标的值不为零,知道那些数放到了桶中,通过数组值知道哪些数放入桶中放了几次,当所有数都放入数组后,遍历数组,判断不同下标的数组值是多少,比如是2就输出两次,直到把遍历结束。示例代码如下:
public class test {
public static void main(String[] args) {
int[] arr={2,32,2,5,3,9,6,55,4};//用一个数组储存要输入的数,就不一个一个输入了
//创建新的数组
//因为要将对应的数放到对应下标,所以为了让55这个数有的放就要数组大小就要创建到55+1
//这也是桶排序的一个弊端,就是数有多大,那桶就要有多大,这样子很浪费空间比如上述数据
//32到55之间的空间都没有被利用到,但是还是要开辟这个空间,还有就是不能对负数进行排序,因为没有负数的下标来储存它
int[] newarr=new int[56];
for (int i=0;i<arr.length;i++){//遍历arr数组
newarr[arr[i]]++;//把arr中的值放到对应下标上
}
for(int i=0;i< newarr.length;i++){//遍历新数组
while (newarr[i]>0){ //只输出有数的桶,并且有多少个数就输出几次
System.out.print(i+" ");//注意这里输出的不是newarr[i]而是下标i,我们要明确i才是数而对应的数组值只是桶而已
newarr[i]--;//输出一次就减少一个
}
}
}
}
输出如下:
通过上面的描述我们可以简单的知道桶排序的基本思想(希望可以qaq),然后我们就可以用这个方法来解这道题:首先我们就要创建一个新的数组(桶)去储存题目给我们的数组中的数,那这个数组要有多大呢,我们可以看到提示nums[i]的值的范围在1-100之间,那我们只需要开到101就可以了(天助我也!),然后再遍历题目给的数组,将数对应新数组中的下标,让数组值++,然后再遍历新数组,因为题目要求只要对出现一次的值就行求和就可以了,那我们就加个判断即可,代码如下:
class Solution {
public int sumOfUnique(int[] nums) {
int[] newArr=new int[101];
for (int i = 0;i < nums.length;i++){
newArr[nums[i]]++;
}
int sum = 0;
for (int i= 1;i < newArr.length;i++){
if (newArr[i] == 1){
sum+= i;//要记住这里加的不是数组值,而是下标,下标就是原来那个数
}
}
return sum;
}
}
提交结果:
可以看出通过这种方法可以很快速的求解出答案而且代码十分简洁,但是在空间的使用上就会与之相反,桶排序是基数排序的一个简单体现,这种排序的特点就是用空间换取时间,有兴趣可以接着去了解一下~
方法二 :双指针的思想
第二种方法就是一般人的第一思路,定义两个索引,一个快索引,一个慢索引,挨个去判断是否相等,然后再进行相应的操作,我的代码如下:
class Solution {
public int sumOfUnique(int[] nums) {
int sum=0;
for (int i = 0; i < nums.length-1; i++) {
int j=i+1;
boolean flag=false;
int haveFinded=0;
while (j<nums.length){
if (nums[i]==nums[j]){
nums[j]=0;
flag=true;
haveFinded=i;
}
j++;
}
if (flag){
nums[haveFinded]=0;
}
}
for (int i=0;i<nums.length;i++){
if (nums[i]!=0){
sum+=nums[i];
}
}
return sum;
}
}
解读:定义一个sum,在后面进行求和时使用,然后我们从数组的第一个数开始遍历这个数组,这时候这个i就是慢索引,我们在定义一个j,j永远指向i的前一个数,然后进行比较,因为j=i+1,我们就可以通过while(j<nums.length)从j开始向后找,如果找到当前这个nums[i]==当前的这个nums[j],我们就让nums[j]等于零,这个时候不能让两个数同时为零,因为即使找到了当前这个与nums[i]相等的值,后面可能还有与它相等的值,所以这时候我们就定义一个haveFinded,和一个flag,一个来记录这个i,表明nums[i]是有重复的,只不过我现在暂时不将它置为零,而是保存着i这个下标,等j将后面的数找完了,再通过flag去判断,如果flag为true就说明是找到有重复了的,然后将num[haveFinded]置零,这里一定要记得将flag定义再for循环里,每一次循环将flag置回false,不然flag一直为true,结果就会错误,然后i++,依次去重复上述操作,直到i等于数组长度减一了,就将所有有重复的数置零了,然后再通过一个循环,将非零的值加一起就可以得到答案了!
提交结果如下:
感觉在时间上还可以再优化一下,比如当nums[i]已经是零的时候就不用再进行循环,让j去找了,直接continue就可以了,没想到提交不会卡时间(xixi :D)就没有修改了~
That's all~