每日算法之5

每日算法之5

1.之重温查找算法

//二分查找:针对于有序序列
int B_search(int a[],int length,int e){
    if(a==NULL || length<1)
        return -1;   
    
    int low,high,mid;
    	
    low=0;
    high=length-1;
    
    while(low<=high){
        mid=(low+high)/2;
        if(a[mid]==e)
            return mid;
        else if(a[mid]>e)
            high=mid-1;
        else
            low=mid+1;
    }
    return -1;
} 

//递归改编:
int B_search(int a[],int low,int high,int e){
    if(a==NULL)
        return -1;  
    int mid=(low+high)/2;
    if(low<=high){
        if(a[mid]==e)
            return mid;
        else if(a[mid]>e)
            return B_search(a,low,mid-1,e);
        else
            return B_search(a,mid+1,high,e);
    }else
        return -1;
}

//测试用例
int main(){
    int a[]={2,4,6,6,9,10};
    int e=6;
    cout<<e<<"的下标位置为:"<<B_search(a,6,6);
}

//时间复杂度为:基本操作为mid取值,循环结束的关键在于mid=1,假设循环k次结束,此时mid=n/2^k
//n/2^k+a=1,a为补偿,化简可得时间复杂度为O(logn);;


2.之重温排序算法

//问题描述:将一个公司的几万员工按照年龄进行排序,要求时间复杂度为O(n),空间复杂度不超过O(n)

//算法分析:由于问题规模就是n,要求时间复杂度为O(n),也就是说,对每个数据的操作,只能一次;只能够扫描一遍,就能排好序列;;考虑使用桶排序,分配0~99个桶,只需要扫描一一遍员工的年龄数组,对应的桶加一;
//最后将所有桶的次数直接输出即可

//算法实现:
void bucketSortAge(int ages[],int length){
    if(ages==NULL || length<1)
        throw "empty error!";
    
    const int ageMAX=100;
    int countAge[ageMAX];//定义0~99的存储年龄次数的数组
    
    //对数组进行初始化
    for(int i=0;i<ageMAX;++i)
        countAge[i]=0;
    
    //遍历一次,将相应的年龄次数加一
    for(int i=0;i<length;++i){
        int age=ages[i];
        if(age<0 || age>ageMAX-1)
            throw "age error!";
        ++countAge[age];
    }
    
    //将桶中的年龄次数数据重新还原存入ages数组中
    int j=0;//表示ages的下标
    for(int countAgeIndex=0;countAgeIndex<ageMAX;++countAgeIndex){
        for(int i=0;i<countAge[countAgeIndex];++i){
            ages[j++]=countAgeIndex;
        }
    }
}//注:不要养成思维误区,看到双重循环,并不代表时间复杂度就是平方阶,要学会抓住时间复杂度的本质
	//是执行基本操作的次数,循环只是累加这个次数,而关键在于循环到什么时候开始结束!!

//测试样例
void generateRandom(int a[],int length,int Max){
	srand((unsigned)time(NULL));
	for(int i=0;i<length;++i){
		a[i]=rand()%Max;
	}
}

int main(){
	int ages[30000];
	generateRandom(ages,30000,100);
	bucketSortAge(ages,30000);
	for(int i=0;i<30000;++i)
		cout<<ages[i]<<" ";
	cout<<endl;
} 

//算法分析:该算法只使用了100个int数组的辅助空间,却完成了O(n)的时间复杂度

3.使用二分查找寻找旋转数组的最小的数字

//输入一个递增数组的前若干个数的一个旋转,要求找出其中最小的数字

//如:数组{3,4,5,1,2}为{1,2,3,4,5}一个旋转,其最小值为1

//算法分析:如果直接使用顺序查找,时间复杂度为O(n),显然没有利用旋转数字的特性
		//注意到,在旋转前k个数字后,数组分为前一半有序,后一半也有序,中间数字5为最大的数,
		//其后一位数即为最小的数,使用二分查找,如果中间值mid大于前一个数,直接往上半区查找,直到
		//找到一个mid小于前一个的数,即为最小值
//时间复杂度为:O(logn)

//算法实现:
int findMin(int rotate[],int length){
    if(rotate==NULL || length<1)
        throw "empty error!";
    
    int low,mid,high;
    
    low=0;
    high=length-1;
    
    while(low<=high){
        mid=(low+high)/2;
        if(rotate[mid]>rotate[mid-1])
            low=mid+1;
        else
            return rotate[mid];
    }
    return rotate[0];//如果没有找到,说明旋转数组旋转了0个数,最小值即为第一个数
}

//测试用例:
int main(){
	int a[]={1,2,3,4,5,-2,0};
	cout<<"最小值为:"<<findMin(a,7);
    
    int b[]={1,2,3,4,5}
    cout<<"最小值为:"<<findMin(b,5);
} 

/*输出结果:
	最小值为:-2
	最小值为:1
*/


//问题变种:如果输入的是一个非递减的数组,即有可能含有相同的数字,又当如何?
//如:{1,1,0,0,0,0}
//问题分析:二分查找的关键在于剔除掉不会含有所求值的区间,那么怎样确定这个区间是否含有所求值,需要什
	//么条件?
//分析问题的基本思路:先针对一般的情况分析,如果一般情况复杂,则进行分解为多个简单的情况;;一般情况分析完毕,再分析特殊的情况,对代码进行完善!!可以在一个函数里调用另一个函数,轻松完善代码的不足之处!

//算法实现:
int findMin_2(int re[],int length){
    if(re==NULL || length<1)
        throw "empty error!";
    
    int low,mid,high;
    low=0;
    high=length-1;
    
    while(re[low]>=re[high]){
        if(high-low==1){
            mid=high;
            break;
        }
        mid=(low+high)/2;
        
        //直接使用顺序查找 
        if(re[low]==re[high]&&re[low]==re[mid])
            return findMin_order(re,low,high);
        //否则使用二分查找
        if(re[mid]>=re[low])
            low=mid;
        else if(re[mid]<=re[high])
            high=mid;
    }
    return re[mid];
}
int findMin_order(int re[],int low,int high){
    int res=re[low];
    for(int index=low+1;index<=high;++index){
        if(res>re[index])
            res=re[index];
    }
    return res;
}

//思考总结:二分查找的核心逻辑三大要点
	/*
		1.什么样的区间才会含有所求值,而什么样的区间不会含有所求值
		2.缩小区间到什么时候就能找到所求值
		3.满足什么条件退出查找
		4.其他的特殊情况!!必须做特殊的处理
	*/

4.之计算2的N次方[涉及大数计算及进位原理]

//问题分析:如果N=10000,显然即使unsigned long long有心而长度不足,这个使用必须使用字符数组+
	//进位的原理进行解决,即每次按照位数,每次乘以2时逢十进一

void calulator(int n){
    if(n<0)
        throw "n error!";
    
    int MAX=10000;
    char result[MAX];//定义存入结果的字符数组
    
    int multiNum;//表示乘以2的次数
    int flag;//表示进位数
    int resNum;//表示字符数组当前的存入字符个数
    int temp;//表示当前位的临时数字
    int bit;//表示位数
    
    //进行初始化
    for(int i=0;i<MAX;++i)
        result[i]='0';
    resNum=1;
    result[0]='1';
    
 	
    for(multiNum=0;multiNum<n;++multiNum){
        flag=0;
        for(bit=0;bit<resNum;++bit){
            //循环将计算结果从第一位字符数字取出进行计算,逢十进一,存储之后,再取出下一高位!!
            temp=result[bit]-'0';//转为数字计算!!
            temp=temp*2+flag;
            
            flag=0;//必须进行清0,保证是最后一位flag>0时,后面的if才会执行 
            if(temp>=10){
                flag=temp/10;
                temp=temp%10;
            }
            result[bit]=temp+'0';//转换为字符
        }
        if(flag>0){
            result[bit]=flag+'0';
            ++resNum;//表示最后一位flag不为0时进位,存储,并字符数组个数加一
        }
    }
    if(resNum>MAX)
    	throw "Max error!";
    	
    for(int i=resNum-1;i>=0;--i){//必须反向输出!!
        cout<<result[i];
    }
    cout<<endl;
    cout<<"字符个数为:"<<resNum<<endl;	
}

//测试用例:
int main(){
    while(1){//使用循环测试
        cout<<"请输入n=:";
        int n;

        cin>>n; 
        try{
            calulator(n);
        }catch(const char *e){
            cout<<e;
        }
    }
}
//输入33333
//输出:MAX error!
//表示最大能够计算33333次方!!

//算法时间复杂度分析:假设每乘以4次进一位,则乘以n次,基本操作数为n^2/16+3n+8,
//即时间复杂度为O(n^2)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值