审题
一个整数数组,只有一个数据是出现一次,其他都出现两次。找到出现一次的数据。
示例
[1,2,2,3,3] //1出现一次
[22133] //1出现一次
思想
其实还是比较数据,只要是比较数据,那么就是冒泡循环,即外一层里一层。
唯一的区别就是,这个题目需要计算次数,而不是排序题目需要排序。
核心步骤
1.比较数据 //基于冒泡排序的外循环和内循环
if(相等) //相等,说明是两次
break; //结束内循环
2.用什么数据结构存储次数//map还是数组?map,数组不行,因为数组不能判断包含关系即某个key是否存在。
map<2,2> //2这个数据的次数是2。
在内循环之前要判断外循环的数据是否在map里,如果在就不用比较了,直接结束内循环;如果不在,再内循环比较。
步骤
1.每一趟内循环比较
2.计算次数,只要出现一次相等,那么结束当前内循环
3.如果内循环之前,map包含,那么也结束当前内循环
伪代码
for(){
if(map是否包含){
//是,就结束内循环
}
for(){
if(相等) //相等,说明是两次
break; //结束内循环(内循环包含多次) //continue只结束当前这一次循环。
}
}
完整源码
基于冒泡算法实现
思想
1.冒泡
外循环
内循环 //总共两层循环
2.比较数据
数据相等,说明出现两次;
不等,说明出现一次。
import java.util.HashMap;
public class CountNum {
public static void main(String[] args) {
//输入数据
int[] a= {2,2,1,3,3};
//查找只出现一次的数据
int i = countNum(a);
//输出数据
if(i==-1) { //没找到数据
System.out.print("没找到");
}else { //找打数据
System.out.print(a[i]);
}
}
//找到只出现一次的数据
private static int countNum(int[] a) {
// HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < a.length; i++) {
// if(map.containsKey(i)) { //查询数据 //什么时候写数据到map?如果只有一个数据出现一次,那么不需要存储数据和次数,也就不需要使用map;如果有多个数据出现一次,那么就需要存储数据,可以使用map。
// return i;
// }
for (int j = 0; j < a.length; j++) {
//i不和自己比较
if(i==j) { //如果相等,就跳过本次循环
continue;
}
//i和自己索引不一样的所有数据比较
if(a[i] == a[j]) { //如果有相等的数据,那么说明该数据出现两次,然后跳过内循环
break;
}
if(j==(a.length-1)) { //最后一个数据仍然不相等,说明当前数据只出现一次
return i;
}
}
}
return -1; //没找到
}
}
//总结 速度 其实就是冒泡排序,所以是N*N。怎么才能提高速度?
基于异或运算实现
思想
1.异或运算
相同的数据异或运算的结果是0;
不同的数据是1。
2.异或数组里的所有数据
比如有5个数据,异或这5个数据,最后结果就是那个只出现一次的数据,因为其他数据都出现两次,所以彼此异或运算的时候互相抵消了。
package CountNum;
public class CountNum2 {
public static void main(String[] args) {
//输入数据
int[] a= {2,2,1,3,3};
//查找只出现一次的数据
int result = countNum(a);
//输出数据
System.out.println(result);
}
//找到只出现一次的数据
private static int countNum(int[] a) {
int result = a[0];
for (int i = 1; i < a.length; i++) {
result ^= a[i]; //异或运算
}
return result; //最后结果就是那个只出现一次的数据,因为其他数据都出现两次,所以彼此异或运算的时候互相抵消了
}
}
//总结 速度 N
举一反三-有两个数据出现一次
待补充
参考
剑指offer