目录
一、解法一:基于Partition函数(快速排序的基础)的复杂度O(n)的解法
2.3 函数参数为指针传递、引用传递两种情况下,函数定义和调用的总结
一、解法一:基于Partition函数(快速排序的基础)的复杂度O(n)的解法
这道题我首先用的书上的解法一,是一种写起来很繁琐的解法,也出了很多bug,记录如下:
1、 throw new std::exception("Invalid Inputs"); 异常提醒的写法不对,暂时注释掉了,return 0;
2、RandomInRange不是C++内置的函数,需要自己写,后来参考别人的写法结果没注意那是Java,Math.random()是Java中的方法,最后搜了一下C++的随机数生成的写法:https://www.cnblogs.com/afarmer/archive/2011/05/01/2033715.html
3、swap函数:一开始把 逗号, 打成中文字符都没注意,后来报错&a = &b,不能赋值(C++的基本知识还要再巩固一下)
改成第二种写法,依然报错 3 errors,调用形式不正确,直接把调用时的&data改为data即可。
(这里是因为&data是取地址,形参是data的引用,调用直接用data就可以,下面2.3有详细说明)
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.empty()) return 0;
int middle = numbers.size()>>1;
int start = 0;
int end = numbers.size()-1;
int index = Partition(numbers, start, end); //初始索引
while(index != middle) //返回的索引不是中位数,就一直循环下去
{
if(index>middle){
index = Partition(numbers, start, index-1); //注意-1,类似的二分法也是有这点注意
}
else{
index = Partition(numbers, index+1, end); //注意+1
}
}
int times = 0; //记录次数
for(int i=0; i<numbers.size(); i++) //i为int,numbers.size()返回的为size_t,?会报warning
{
if(numbers[index]==numbers[i]) times++;
}
if (times <= numbers.size()/2) return 0; // times是int,numbers.size()/2是unsigned long
return numbers[index];
}
int Partition(vector<int> data, int start, int end) //此处vector应该加&,但是我不加&依然通过了,???
{
if(data.empty()==true || start<0 || end>data.size()-1)
//throw new std::exception("Invalid Inputs");
return 0;
//int index = RandomInRange(start, end); //随机选中一个值作为分界点
int index = start + (rand() %(end-start+1)); // 取得[start,end]区间随机整数
swap(data, index, end); //把分界点元素放到数组最后
int small = start-1; //比分界点值小的数的边界索引值
for(index=start; index<end; index++){
if(data[index]<data[end]){
small++;
if(small != index){
swap(data, small, index);
}
}
}
small++;
swap(data, small, end); //把分界点放回到small+1的位置上
return small; //返回分界点索引
}
void swap(vector<int> &numbers, int a, int b)
{
int temp = numbers[a];
numbers[a] = numbers[b];
numbers[b] = temp;
}
//void swap(int& a, int& b)//之前逗号打成了中文字符,自己没发觉到后面的int都没有变蓝色
//{
//int temp = &a;
//&a = &b;
//&b = temp;
//}
};
二、针对解法一程序中Bug的再讨论
2.1 指针形式的参数传递
将swap函数写成第二种写法:
void swap(int& a, int& b)
{
int temp = &a;
&a = &b;
&b = temp;
}
将上面的函数定义、调用中的&全部改成*,依然会报错:
indirection requires pointer operand ('value_type' (aka 'int') invalid)
原因是:函数调用时出错,data[small]本身就是int,*int自然会报错(为什么会犯这种智障的错误……)
swap(*data[small], *data[end]);
改成:
swap(&data[small], &data[end]);
像上面这样,取地址传入,就不会报错了。
2.2 引用形式的参数传递
再来看一看,把swap函数的形参改回到引用形式,调用也加上了取地址符 & :
报错 5 erors,将swap函数内部变量a,b前的&去掉(我一直把在a,b前加 & 理解成了 引用 ,其实是 取地址 符号)
void swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
只改swap定义,不改调用(调用还是这样:swap(&data[small], &data[end]); )
报错 3errors ,调用3次,报错3次:non-const lvalue reference to type 'int' cannot bind to a temporary of type 'value_type *' (aka 'int *')
调用时,取地址符不加,就顺利通过。
之前出错的主要原因:&用作 引用 和 取地址 搞混淆了,函数调用时&a是取地址,我在写程序时理解成引用了。
C++中 引用&与取地址&的区别:https://blog.csdn.net/passtome/article/details/7937141
https://blog.csdn.net/qq_33266987/article/details/52047473
2.3 函数参数为指针传递、引用传递两种情况下,函数定义和调用的总结
1、指针传递
void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
swap(*data[i], *data[j] );
2、引用传递
void swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
swap(data[i], data[j] );
3、值传递
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
swap(data[i], data[j] );
这种情况下swap函数实际上起不到任何作用,因为是形参向实参拷贝了一份数据进行操作,函数返回就释放掉了,原来的实参并不会有任何变化。
所以最简单的办法,就是把函数的形参定义为引用的形式,这样传递进来的a,b,就是实参的别名,函数中改变了a,b的值,实际的实参也会被改变。
一个讲解很好的博客:C与C++关于*与&(引用方式传参,传值方式传参)
https://blog.csdn.net/qq_34243930/article/details/81638852
三、解法二:思路同解法一,直接利用sort函数排序
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.size() == 0)
return 0;
sort(numbers.begin(), numbers.end());
int x = numbers[numbers.size() / 2];
int count = 0;
for(int i = 0; i < numbers.size(); i++)
{
if(numbers[i] == x)
count ++;
}
if(count > numbers.size() / 2)
return x;
else
return 0;
}
};
四、解法三
书上的第二种解法:
在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。
遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
int n = numbers.size(); //取数组的长度,返回的是size_t(unsigned long),放入int n中
if(n==0) return 0;
int result, times;
//初始化
result = numbers[0];
times = 1;
for(int i=1; i<n; i++)
{
if(numbers[i]==result){
times++;
}
else{
times--;
if(times==0){
result=numbers[i+1];
}
}
}
if(CheckMoreThanHalf(numbers, result)) return result;
else return 0;
}
bool CheckMoreThanHalf(vector<int> &numbers, int result)
{
int n = numbers.size();
int times = 0;
bool flag = false;
for(int i=0; i<n; i++){
if(numbers[i]==result) times++;
}
if(times>n/2) flag = true;
return flag;
}
};
五、解法四
注意到目标数字 超过数组长度的一半,对数组同时去掉两个不同的数字,到最后剩下的一个数就是该数字。如果剩下两个,那么这两个也是一样的,就是结果,在其基础上把最后剩下的一个数字或者两个回到原来数组中,将数组遍历一遍统计一下数字出现次数进行最终判断。
Java代码:
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int length=array.length;
if(array==null||length<=0){
return 0;
}
if(length==1){
return array[1];
}
int[] tempArray=new int[length];
for(int i=0;i<length;i++){
tempArray[i]=array[i];
}
for(int i=0;i<length;i++){
//后面需要用零来代表抹除数字,所以对0时做特殊处理
if(tempArray[i]==0){
continue;
}
for(int j=i+1;j<length;j++){
if(tempArray[i]!=tempArray[j]&&tempArray[j]!=0){
tempArray[i]=0;//此处用0代表抹去该数字
tempArray[j]=0;
break;
}
}
}
for(int i=0;i<length;i++){
System.out.println(tempArray[i]);
}
//找出未被抹去的数字
int result=0;
for(int i=0;i<length;i++){
if(tempArray[i]!=0){
result=tempArray[i];
break;
}
}
int times=0;
for(int i=0;i<length;i++){
if(result==array[i]){
times++;
}
}
if(times*2<length){
result=0;
}
return result;
}
}