每日算法之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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章