大家好,我是怒码少年小码。
数组中出现次数超过一半的数字
这是剑指offer中的一道题目,数组中有一个数字出现的次数超过
数组长度的一半,请找到它。
示例:
- 输入:[1,2,3,2,2,2,5,4,1]
- 输出:2
统计次数最多?那我们如果使用某种排序算法排好之后,出现次数最多的那个不就在中位数上吗?ok,第一种方法get√。这里我在给大家讲讲第二种方法——辅助空间法😎。
int moreThanHalfNum(int arr[], int size) {
if (arr == nullptr) {
return 0;
}
//假设数组中元素的值的范围在0到100000之间
int res[100001] = { 0 };
for (int i = 0; i < size; i++) {
res[arr[i]]++;
if (res[arr[i]] > size / 2) {
cout << arr[i] << endl;
return arr[i];
}
}
return 0;
}
创建一个数组res
,用于统计每个元素在输入数组中出现的次数。这个方法妙就妙在利用重复出现的元素的值相同,⭐把arr[i]
的值当作res
的下标,用res[arr[i]]
来统计arr[i]
出现的次数。
for循环中,如果arr[i]
值相同,则res[arr[i]]
是res
数组中的同一元素,每遇到一次就自增加一res[arr[i]]++;
。一直到res[arr[i]] > size / 2
返回。真的是秒啊~😉
数组种只出现一次的数字。
LeetCode 136:给定一个非空整数数组,除了某个元素只出现一次之外,其余每个元素均出现两次,找出那个只出现一次的元素。
示例:
- 输入:[4,1,2,1,2]
- 输出:4
不知道大家是否和我的第一反应一样:用上面第一题方法,改改输出条件就好了🤣。
方法一:辅助空间法
int moreThanHalfNum02(int arr[], int size) {
if (arr == nullptr) {
return 0;
}
//假设数组中元素的值的范围在0到100000之间
int res[100001] = { 0 };
//统计
for (int i = 0; i < size; i++) {
res[arr[i]]++;
}
//查找
for (int i = 0; i < size; i++) {
if (res[arr[i]] == 1) {
cout << arr[i] << endl;
return arr[i];
}
}
return 0;
}
普通人的第一反应可能是用两个for循环一个个比较吧:盯住数组种一个,然后和其他的元素一次次对比,有重复的就记录一下,只输出那个没有找到的。
方法二:双循环
int findOneNum02(int arr[], int size) {
int num;
bool found = false;
for (int i = 0; i < size; i++) {
num = arr[i];
found = false;
for (int j = 0; j < size ; j++) {
//和盯住的那个一样,跳过
if (i == j) {
continue;
}
//在其他的元素种找到一个一样的,记录为找到过
if (num == arr[j]) {
found = true;
}
}
//只输出没有找到过一样的那个元素
if (!found) {
cout << num << endl;
return num;
}
}
return -1;
}
方法三:位运算
位运算运算规则:
- 0^0 = 0;
- 0^a = a;
- a^a = 0;
- aba = b;
0与其他数字异或的结果是这个数字;相等的数字异或得0。这题中数组中只有一个元素出现了一次,其他的都出现了两次,所以可以定义一个变量flag
赋值为0,用它与数组中的每一个做异或运算。
int findOneNum03(int arr[], int size) {
int flag = 0;
for (int i = 0; i < size; i++) {
flag ^= arr[i];
}
cout << flag << endl;
return flag;
}
出现两次的元素一定会一次做异或运算变为0,只出现一次的元素最后会保存下来。
颜色分类问题(荷兰国旗问题)
LeetCode 75:给定一个包含红色、白色和蓝色,共n个元素的数组nums,原地对它们进行排序,使得相同颜色的元素相邻,并按红色、白色、蓝色顺序排列。0、1、2分别表示红白蓝。不能使用库的sort函数。
示例:
- 输入:[2,0,2,1,1,0]
- 输出:[0,0,1,1,2,2]
透过现象看本质,你不难发现这就是一个排序的问题。
方法一:快慢指针
这个方法很像冒泡排序。
void sortColor(int arr[], int size) {
//把0放到前面
int left = 0;
for (int right = 0; right < size; right++) {
if (arr[right] == 0) {
int temp = arr[right];
arr[right] = arr[left];
arr[left] = temp;
left++;
}
}
//把所有1放到2前面
for (int right = left; right < size; right++) {
if (arr[right] == 1) {
int temp = arr[right];
arr[right] = arr[left];
arr[left] = temp;
left++;
}
}
//输出检测
for (int i = 0; i < size; i++) {
cout << arr[i] << ",";
}
cout << endl;
}
方法二:
面试官如果要求一次遍历就搞定,那么就要三个指针才可以了。
left
表示头部,right
表示尾部,index
表示当前位置,从头开始遍历数组,根据arr[index]
是0还是2决定与left
交换还是与right
交换。arr[index]==1
就什么都不需要做,直接index++
。一直到index==right
。
void sortColor02(int arr[], int size) {
int left = 0;
int right = size - 1;
int index = 0;
while (index <= right) {
if (arr[index] == 0) {
int temp = arr[left];
arr[left] = arr[index];
arr[index] = temp;
left++;
index++;
}
else if(arr[index] == 2) {
int temp = arr[right];
arr[right] = arr[index];
arr[index] = temp;
right--;
}
else {
index++;
}
}
//输出检测
for (int i = 0; i < size; i++) {
cout << arr[i] << ",";
}
cout << endl;
}
注意:这种方法中很容易就会想到用for循环,但其实不对,应该用while循环,因为index的增加是有条件的,例如:当index
和right
交换值时,我们无法确定新换的index
上的值是否符合要求,所以不能直接index++
,必须在检测一次。
END
坚持就是胜利!!路过的uu可以点个赞赞哈,评论我们一起讨论。