Loading

常用算法

计算函数运行时间

#include <stdio.h>
#include <time.h>
#include <math.h>
clock_t start,stop;               //clock_t   是函数clock()返回数的数据类型 
double duration;
int main()
{
	start=clock();                 
	for(int i=0;i<1000000000;i++)          //重复调用函数,时间增多 
		pow(123,3);
	stop=clock();
	duration=(double)(stop-start)/CLOCKS_PER_SEC;		//转化为时间Sec 
	printf("%f\n",duration);
	printf("%d\n",CLK_TCK);              //每台机器的CLOCKS_PER_SEC的值是不一样的,我的这台是1000 
	return 0;
}

Top K算法

应用场景

搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。 假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G

生成1000万个重复随机数并计算其生成时间

#include <iostream>
#include <ctime>
using namespace std;
#define SELF_RAND_MAX 0x7FFFFFFF    //rand 生成的最大数

int main()
{
    clock_t StartTime=clock();   //程序运行到这一步的时间
    srand(unsigned (time(NULL)));   //time(0)==time(NULL)  随机种子  每次运行程序得到的随机数都不同
    const int Min=10;
    const int Max=100000;
    freopen("/Users/xingkong2/Documents/C/hello/hello/data.txt","w", stdout);
    for (int i=0; i<10000; i++)
    {
        unsigned long long data=rand()%(Max-Min+1)+Min;         //生成【10-100000】之间的随机数
        cout<<data<<' ';
    }
    fclose(stdout);
    freopen("/Users/xingkong2/Documents/C/hello/hello/time.txt", "w", stdout);
    cout<<"end"<<'\n';
    cout<<"time"<<double(clock()-StartTime)/CLOCKS_PER_SEC<<"s";   //时间差就等于随机数生成时间
    return 0;
}

Reference

参考1

参考2

实现1:堆

最直观:小顶堆->找最大,大顶堆 -> 找最小

小顶堆解决Top K问题的思路:小顶堆维护当前扫描到的最大100个数,其后每一次的扫描到的元素,若大于堆顶,则入堆,然后删除堆顶;依此往复,直至扫描完所有元素

实现2:Quick Select

Quick Select的目标是找出第k大元素,所以

  • 若切分后的左子数组的长度 > k,则第k大元素必出现在左子数组中;
  • 若切分后的左子数组的长度 = k-1,则第k大元素为pivot;
  • 若上述两个条件均不满足,则第k大元素必出现在右子数组中。
#include <iostream>
using namespace std;

template <class T>
int GetLength(T& arr)
{
    return (sizeof(arr));
}

int partition(int num[],int start,int end)
{
    if(start==end)
        return start;
    
    int pivot=num[start];           //选主元
    
    while (start<end)
    {
        while (start<end && num[end]>=pivot)        //从区间的最后一个元素扫描,大于主元就向前。否则就停在这个元素这里,并把这个元素复制到start下标中(就是把后面小于主元的元素往前移)
            end--;
        num[start]=num[end];

        while (start<end && num[start]<=pivot)      //从区间的第一个元素扫描,小于主元就向前。否则就停在这个元素这里,并把这个元素复制到end下标中(就是把后面大于主元的元素往后移)
            start++;
        num[end]=num[start];
    }
    num[start]=pivot;                           //start==end 主元的位置,
    return start;
}

int QuickSelect(int num[],int n,int k)      //查找第k小的元素
{
    int i=0;
    int j=n-1;  //数组最后一个下标
    
    while (i<=j)
    {
        int partitionIdx=partition(num,i,j);    //得到主元实际应该在数组中的位置
        if((k-1)==partitionIdx)                 //第k小1的元素在数组中的位置应该是在k-1下标的位置,若与主元位置相等,说明找到
            return num[partitionIdx];
        else if((k-1)<partitionIdx)             //k-1在[i,partitionIdx-1]
            j=partitionIdx-1;
        else
            i=partitionIdx+1;                   //k-1在[partitionIdx+1,j]
    }
    return 0;
}

int main()
{
    int A[6]={10,78,45,43,23,66};   //数组元素不能有重复
    cout<<QuickSelect(A,6,2)<<'\n';
    return 0;
}

上面给出的代码都是求解第k大元素;若想要得到Top K元素,仅需要将代码做稍微的修改。当然,这里是按照树的大小进行一个取topk,也可以用其他规则取topk

快速幂

/*
    快速幂算法:
            1,当b为偶数时,a^b可以转为a^2的b/2次方。

            2,当b为奇数时,a^b可以转为a^2的b/2次方,再乘以a。

            而a^2的b/2次方,以可以使用上述方式转为a^4的b/4次方再乘以某个数
    
    快速幂对某个数取余:
            a*b%m=(a%m)*(b%m)%m
            a^b%c=(a%c)^b;
            
*/
# include <stdio.h>
# include <time.h>
 
long long Pow(long long a,long long b)    //迭代版
{
    long long Sum=1;
    while(b>0)
    {
        if(b%2)
        { 
            a=a%1000;
            Sum=Sum%1000;
            Sum=Sum*a%1000;
        }
        a=a%1000;
        a=a*a;
        b=b>>1;
    }
    return Sum%1000;
}


long long pow_mod(long long a,long long i,long long n)  //递归版
{
    if(i==0) return 1%n;
    int temp=pow_mod(a,i>>1,n);    //每次返回来的都是对指数折半并对其取余的结果
        temp=temp*temp%n;         
    if(i&1) temp=(long long)temp*a%n;
    return temp;
}


int main()  
{
    double  start, finish;
    long long a=100,x=100;
    start = clock();


    printf("%lld\n", Pow(5576,1832353248));
    finish = clock();
    printf( "%f seconds\n",(finish - start) / CLOCKS_PER_SEC);

    return 0;
}

数独

思想:暴力,递归,DFS,回溯。填一个数字后检测是否满足条件,不满足就回溯

#include <iostream>
#include <time.h>
using namespace std;

int S[9][9];
int Check(int Sum)
{
    int row=Sum/9,col=Sum%9;
    int row2=row/3*3;
    int col2=col/3*3;
    int i=0,j=0,m=0,n=0;
    
    for(i=0;i<9;i++)//行检查
        if(S[row][i]==S[row][col] && col!=i)
            return 1;
    for(j=0;j<9;j++)//检查列
        if(S[j][col]==S[row][col] && row!=j)
            return 1;
    for(m=row2;m<row2+3;m++)//检查所在的九宫格
    {
        for(n=col2;n<col2+3;n++)
            if(S[row][col]==S[m][n]&&row!=m&&col!=m) return 1;
    }
    return 0;
}

int SD(int Sum)
{
    int row=Sum/9,col=Sum%9;
    int flag=0;
    if(Sum>80)
    {
        printf("\\nanswer:\\n");
        for(int i=0;i<9;i++)
        {
            
            for(int j=0;j<9;j++)
                printf("%d ",S[i][j]);
            printf("\\n");
        }
        return 1;
    }
   
    if(S[row][col]==0)  //没有数字
    {
        for (int i=1; i<10; i++)    //暴力
        {
            S[row][col]=i;
            if(!Check(Sum))
            {
                flag=SD(Sum+1);     //满足三个条件就递归下一格
                if (flag) return 1;     //递归返回

            }
            S[row][col]=0;      //注意:回溯后面的数据是要变为零的
        }
    }
    else        //有数字
    {
        flag=SD(Sum+1);         //
        if (flag) return 1;
    }
    return 0;
}

int main()
{
    clock_t Star,End;
    Star=clock();
    printf("请输入题目,空白用0代替:\\n");
    for(int i=0;i<9;i++)
        for(int j=0;j<9;j++)
            scanf("%d",&S[i][j]);
    SD(0);
    End=clock();
    double Time=(double)(End-Star)/CLOCKS_PER_SEC;
    printf("用时:%f秒\\n",Time);
    
    return 0;
}

N皇后问题

实现1

#include<iostream>
#include<list>
using namespace std;

int nSolu=0;

struct Queen{
    int x,y;
    Queen(int xx,int yy):x(xx),y(yy){ };
    bool operator==(const Queen & q)
    {
        return (x==q.x)||(y==q.y)||(x+y==q.x+q.y)||(x-y==q.x-q.y);
    };
};

template<class T>
struct Stack{           //由链表s实现的堆
    list<T> li;
    
    void push(T queen)
    {
        li.push_back(queen);
    }
    T pop()
    {
        T queen=li.back();
        li.pop_back();
        return queen;
    }
    
    int find(T queen)
    {
        list<Queen>::iterator i;
        int sum=-1;
        for(i=li.begin();i!=li.end();i++)
        {
            sum++;
            if(*i==queen)
                break;
        }
        if(i==li.end())
            sum=-1;
        return sum;
    }
    int size()
    {
        list<Queen>::iterator i;
        int sum=0;
        for(i=li.begin();i!=li.end();i++)
            sum++;
        return sum;
    }
};

void placeQueens(int N)   //N皇后
{
    Stack<Queen> solu;
    Queen q(0,0);               //初始化
    while (q.x>0 || q.y<N)
    {
        if(solu.size()>=N || q.y>=N)        //solu.size()>=N:如果堆里面皇后个数达到了N个说明已经找到了一种j解决方案,然后继续找背的方案                                           //q.y>=N:出界了
        {
            q=solu.pop();                   //回溯,然后把相应的Y加1
            q.y++;
        }
        else
        {
            while (q.y<N && solu.find(q)>=0) q.y++;     //退出循环只有两种情况:在界限内找到了可以放置的点   出界(没有可以放置的点)
            if(q.y<N)                       //若q.y小于N说明找到了f放置点保存位置,否则就是出界
            {
                solu.push(q);
                if(solu.size()>=N)          //当堆栈==N说明满了,找到一种解决方案
                {
                    nSolu++;
                }
                q.x++;                      //去寻找下一个皇后的位置
                q.y=0;
            }
        }
        //cout<<solu.size()<<endl;
    }
}

int main()
{
    while(1)
    {
        int n=0;
        scanf("%d",&n);
        if(n==0)
            break;
        placeQueens(n);
        cout<<nSolu<<endl;
        nSolu=0;
    }
    return 0;
}

实现2

#include<iostream>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;

int nSolu=0;

struct Queen{
    int x,y;
    Queen():x(0),y(0){ };
    Queen(int xx,int yy):x(xx),y(yy){ };
    bool operator==(const Queen & q)
    {
        return (x==q.x)||(y==q.y)||(x+y==q.x+q.y)||(x-y==q.x-q.y);
    };
};

vector<Queen> qs;

bool findfind(Queen q)
{
    bool flag=true;
    vector<Queen>::iterator i;
    for (i=qs.begin(); i!=qs.end(); i++)
        if(*i==q)
            break;
    if(i==qs.end())
        flag=false;
    return flag;
}


void placeQueens(int N)   //N皇后
{
    qs.clear();
    stack<Queen> solu;
    Queen q(0,0);               //初始化
    while (q.x>0 || q.y<N)
    {
        if(solu.size()>=N || q.y>=N)        //solu.size()>=N:如果堆里面皇后个数达到了N个说明已经找到了一种j解决方案,然后继续找背的方案                                         //q.y>=N:出界了
        {
            q=solu.top();                   //回溯,然后把相应的Y加1
            solu.pop();
            qs.pop_back();
            q.y++;
        }
        else
        {
            while (q.y<N && findfind(q)) q.y++;     //退出循环只有两种情况:在界限内找到了可以放置的点   出界(没有可以放置的点)
            if(q.y<N)                       //若q.y小于N说明找到了f放置点保存位置,否则就是出界
            {
                solu.push(q);
                qs.push_back(q);
                if(solu.size()>=N)          //当堆栈==N说明满了,找到一种解决方案
                    nSolu++;
                q.x++;                      //去寻找下一个皇后的位置
                q.y=0;
            }
        }
        
    }
    
}

int main()
{

    while(1)
    {
        int n=0;
        scanf("%d",&n);
        if(n==0)
            break;
        placeQueens(n);
        cout<<nSolu<<endl;
        nSolu=0;
    }
    return 0;
}
posted @ 2021-05-30 20:15  兔子翻书  阅读(73)  评论(0)    收藏  举报