• 程序员求职成功路(2) 第3章 数据结构与算法


    第3章 数据结构与算法

    1. memmove边界问题

    void memmove_(char *pDst,const char* pSrc,size_t size){
        assert(pSrc!=NULL&&pDst!=NULL);
        const char* p;
        char*q;
        if(pSrc<pDst&&pSrc+size>pDst){
            p=pSrc+size-1;
            q=pDst+size-1;
            while(size--)
                *q--=*p--;
        }
        else{
            p=pSrc;
            q=pDst;
            while(size--)
                *q++=*p++;
        }                                                        
    }

    2. 出错处理方式

    3. 字符串算法的实现

    (1) strstr函数: 进行了比较好的判断, 主要是便于比较快的结束判断

    char* strstr_(const char *str1,const char* str2){
        assert(str1!=NULL&&str2!=NULL);
        int len1=strlen(str1);
        int len2=strlen(str2);
        if(len2>len1)//如果str2的长度大于str1的长度, 直接可以返回NULL
            return NULL;
        while(*str1!='\0'){
            const char *p=str1;
            const char *q=str2;
            while(*q&&*p==*q)
                p++,q++;
            if(*q=='\0')
                return str1;
            str1++;
            len1--;
            if(len1<len2)//如果此时str1剩余的长度小于str2的话,这样就没有再继续查找的必要了
                return NULL;
        }
    }

    (2) strtok函数的实现

    char *strtok_(char *str,const char *delim){
        char *p;
        static char *nexttoken;
        char map[32]={0};//map是一个位图,256位,用来记录delim中的字符(分隔符)
        do
            map[*delim>>3]|=(1<<(*delim&7));//将分隔符对应的位置设置为1
        }while(*delim++);
        if(str)
            p=str;
        else 
            p=nexttoken;
    //如果开始字符为分隔符, 略过
        while((map[*p>>3]&(1<<(*p&7)))&&*p)
            p++;
        str=p;
        while(*p){
            if(map[*p>>3]&(1<<(*p&7))){//如果*p为分隔符, 那么将这个位置设为’\0’, 这样就完成了一个单词的分割
                *p++='\0';
                break;
            }
            p++;
        }
        nexttoken=p;//用static变量记录下一个分隔开始的起点.
        if(str==p)
            return NULL;
        else 
            return str;

    (3) 删除特定的字符数组

    这儿同样是使用map位图来记录出现的字符

    char *deleteChars(char *str, const char *chr){
        assert(str!=NULL);
        char map[32]={0};
        int i=0,j=0;
        while(*chr){
            map[*chr>>3]|=(1<<(*chr&7));
            chr++;
        }
        
        do
            if(!(map[str[j]>>3]&(1<<(str[j]&7))))
                str[i++]=str[j];
        }   
        while(str[j++]!='\0');
        return str;
    }  

    (4) IP地址与无符号整型数的转换

    unsigned int str2int(char *str,int n){
        unsigned int val=0;
        int i;
        for(i=0;i<n;i++){
            val=val*10+(str[i]-'0');
        }
        return val;
    }
    unsigned int ip2int(char* ip,int* flag){
        char *p1=ip;
        int len=0;
        char pos[3];
        int count=0;
        unsigned int s1,s2,s3,s4;
        while(*p1!='\0'){
            if(!(*p1>='0'&&*p1<='9'||*p1=='.')) {
                printf("Invalid char:%c\n",*p1);
    //            exit(-1);
                *flag=-1;
                return -1;
            }
            if(*p1=='.')
                pos[count++]=len;
            p1++;
            len++;
        }
        if(count!=3) {
            printf("Not enough \'.\'\n");
            *flag=-2;
            return -2;
    //        exit(-2);
        }
        printf("pos:%d %d %d\n",pos[0],pos[1],pos[2]);
        if(pos[0]==0||pos[2]==len-1||pos[1]-pos[0]==1||pos[2]-pos[1]==1) {
            printf("Invalid Format:缺少一个IP段\n");
            *flag=-3;
            return -3;
    //        exit(-3);
        }
        if(pos[0]>3||pos[1]-pos[0]>4||pos[2]-pos[1]>4||len-pos[2]>4){
            printf("Invalid Format:IP段太长\n");
            *flag=-4;
            return -4;
    //        exit(-4);
        }
        s1=str2int(ip,pos[0]);
        s2=str2int(ip+pos[0]+1,pos[1]-pos[0]-1);
        s3=str2int(ip+pos[1]+1,pos[2]-pos[1]-1);
        s4=str2int(ip+pos[2]+1,len-pos[2]-1);
        printf("%x.%x.%x.%x -> ",s1,s2,s3,s4);
        if(!(s1>=0&&s1<=255&&
            s2>=0&&s2<=255&&
            s3>=0&&s3<=255&&
            s4>=0&&s4<=255)) {
            printf("Invalid Format:IP段超出范围\n");
            *flag=-5;
            return -5;
    //        exit(-5);
        }
        *flag=1;
        return s1<<24|s2<<16|s3<<8|s4;
    }
    void ip_test(){
        char *ip[]={"0.0.0.0","211.211.22.33",
            ".","..","...","......","211...","211.22.0.0",
            "211.22..0","211.22.ab.33",".22.33.55"
        };
        int len=sizeof(ip)/sizeof(ip[0]);
        int i;
        for(i=0;i<len;i++){
            int flag;
            unsigned int val;
            printf("%s\n",ip[i]);
            val=ip2int(ip[i],&flag);
            printf("%#x\n",val);
            printf("flag=%d\n");
            if(flag<0){
                printf("Invalid Format\n");
            }
            printf("==================\n");
        }

    }

    (5)正则表达式匹配问题

    int PatternMatch(const char* pat,const char*str){
         const char *s=NULL;
         const char *p=NULL;
         int star=0,bBreak=0;
         do{
             bBreak=0;
             for(s=str,p=pat;*s;++s,++p){
                 switch(*p){
                     case '?':
                         break;
                     case '*':
                         star=1;
                         str=s;
                         pat=p;
                         if(!*++pat)
                             return 1;
                         bBreak=1;
                         break;
                     default:
                         if(*s!=*p){
                             if(!star)
                                 return 0;
                             str++;
                             bBreak=1;
                         }
                         break;
                 }           
                 if(bBreak)      
                     break;  
             }               
             if(!bBreak){
                 if(*p=='*')
                     ++p;    
                 return !*p;     
             }       
             
         }while(1);
     }           
    void PatternMatch_test(){
        char *pat="hell*world*!";
        char *str="hellfasdfdworldfadsf!";
        int b=PatternMatch(pat,str);
        printf("%d\n",b);
    }

    4. 判断单向链表中是否存在循环?

     

    5. 树的遍历算法

    使用两种方式:递归和非递归的方式

    #include <cstdio>
    #include <cstdlib>
    #include <iostream>
    #include <stack>
    #include <queue>
    #define print_arr(a,n) for(int i=0;i<n;i++)\
                                     printf("%d ",a[i]);\
    printf("\n")
    using namespace std;
    struct node{
        node():left(0),right(0){}
        node(int d):data(d),left(0),right(0){}
        int data;
        node* left;
        node* right;
    };
    //node *tree_create(int *first,int* end){
    //    if(first!=end){
    //    node *root=new node;
    //    root->data=*first++;
    //    node* p=root;
    //    while(first!=end){
    //        node *q1=new node;
    //        q1->data=*first++;
    //        p->left=q1;
    //        if(first!=end){
    //            node *q2=new node;
    //            q2->data=*first++;
    //            p->right=q2;
    //        }
    //    }
    //    }
    //}
    node *make_tree(node*root,int l,int r){
        node* p=new node(l);
        root->left=p;
        p=new node(r);
        root->right=p;
        return root;
    }
    node *make_tree(node*root,int l,bool flag=true){
        node* p=new node(l);
        if(flag)
            root->left=p;
        else
            root->right=p;
        return root;
    }
    node *free_tree(node *root){
        if(root!=0){
            free_tree(root->left);
            free_tree(root->right);
        }
    }
    //递归的前序遍历
    void preorder(node *root){
        if(root){
            cout<<root->data<<' ';
            preorder(root->left);
            preorder(root->right);
        }
    }
    //使用栈来实现非递归的前序遍历
    void preorder_(node *root){
        if(!root)
            return;
        stack<node*> st;
        st.push(root);
        node* p;
        while(!st.empty()){
            p=st.top();
            st.pop();
            cout<<p->data<<' ';
            if(p->right!=0)//将左子树压入栈
                st.push(p->right);
            if(p->left!=0)//将右子树压入栈
                st.push(p->left);
        }
    }
    //递归方式的中序遍历
    void inorder(node *root){
        if(root){
            inorder(root->left);
            cout<<root->data<<' ';
            inorder(root->right);
        }
    }
    //同样使用栈来实现非递归的中序遍历
    void inorder_(node *root){
        if(!root)
            return;
        stack<node*> st;
        node *p=root;
        do{
            //先将左子树不断的入栈
            while(p!=0){
                st.push(p);
                p=p->left;
            }
            //访问节点的值,然后再转向处理右子树
            if(!st.empty()){
                p=st.top();
                st.pop();
                cout<<p->data<<' ';
                p=p->right;
            }
        }while(p!=0||!st.empty());
    }
    //递归方式的后序遍历
    void postorder(node *root){
        if(root){
            postorder(root->left);
            postorder(root->right);
            cout<<root->data<<' ';
        }
    }//这是一种比较好的方式来实现后序遍历的
    void postorder(Node* root){
        stack<Node*> st;
        Node* p=root,*visited=0;
        while(p!=0||!st.empty()){
            while(p!=0){
                st.push(p);
                p=p->left;
            }
            p=st.top();
            if(p->right==0||visited==p->right){
                cout<<(int)p->data<<' ';
                st.pop();
                visited=p;
                p=0;
            }
            else{
                p=p->right;
            }   
        }       
        cout<<endl;
    }      
    //使用栈来实现非递归的后序遍历
    void postorder_(node *root){
        if(!root)
            return;
        stack<node*> st;
        stack<bool> tag;//用来区分左右子树,false表示左子树,true表示右子树
        node *p=root;
        do{
            while(p!=0){
                st.push(p);
                tag.push(false);
                p=p->left;
            }
            if(!st.empty()){
                p=st.top();
                if(tag.top()){//如果是右子树就输出节点值
                    st.pop();
                    tag.pop();
                    cout<<p->data<<' ';
                    p=0;
                }
                else{//当前是节点的左子树,需要将其右子树进行处理(入栈)
                    p=p->right;
                    tag.top()=true;
                }
            }
        }while(p!=0||!st.empty());
    }
    //分层遍历,很自然的使用队列
    void levelorder(node *root){
        if(!root)
            return;
        queue<node*> q;
        node *p=root;
        q.push(p);
        while(!q.empty()){
            p=q.front();
            q.pop();
            cout<<p->data<<' ';
            if(p->left!=0)
                q.push(p->left);
            if(p->right!=0)
                q.push(p->right);
        }
    }
    void in_post_pre(intin,int *post,int *pre,int n){
        

    }

    int main(){
        node *root=new node(20);
        make_tree(root,10,30);
        node *p=root->left;
        make_tree(p,15,14);
        p=p->right;
        make_tree(p,28,false);
        p=root->right;
        make_tree(p,29,41);

    //    preorder(root);    cout<<endl;
    //    preorder_(root);    cout<<endl;
    //    inorder(root);cout<<endl;
    //    inorder_(root);cout<<endl;
    //    postorder(root);cout<<endl;
    //    postorder_(root);cout<<endl;
        levelorder(root);cout<<endl;
        
        free_tree(root);
    }

    6. 平衡二叉排序树

    7. 常用的查找方法

    主要包括折半查找,二叉排序树查找和Hash表查找.

    (1). 折半查找复杂度是O(logn), 需要满足两个条件: 元素必须是连续存储并且是有序的.

    (2). 二叉排序树查找.

    (3) hash表查找

    问题1: 查找兄弟单词

    问题2: 查找字符串中第一个不重复的字符

    问题3: 统计最热门的10个查询串

     

    8. 树中某些节点的公共祖先问题

    问题1: 求出从根节点到给定节点的路径.

    代码如下:

    void find_path(node*root,node* p){
        node* stk[100],*s;
        int tag[100],i,top=-1;
        s=root;
        do{
            while(s!=0){//不断的将左子树进行压栈
                top++;
                stk[top]=s;
                tag[top]=0;
                s=s->left;
            }   
            if(top>-1){
                s=stk[top];
                if(tag[top]==1){//右子树遍历完毕,开始遍历根节点
                    if(s==p) {
                        for(i=0;i<=top;i++){
                            printf("%d ",stk[i]->data);
                        }
                        break;
                    }   
                    else    
                        top--;
                }       
                else{//还没有遍历右子树,开始遍历右子树
                    s=s->right;
                    tag[top]=1;
                }                                                   
            }   
        }while(s!=0||top>-1);

    问题2: 查找两个节点的公共祖先

    利用上面的查找到根节点路径的方法就可以了.

    问题3: 找出二叉搜索树上两个节点的公共祖先.

    公共祖先节点的值肯定位于这两个值之间.

     

    10. 字典树(trie树)

    字典树可以解决下面一些问题:

    问题1: 纠错问题

    问题2: 查找公共的url

     

    11. 递归在树中的应用

    代码如下:

    int like(node* root1,node* root2){
        if(root1==0&&root2==0)
            return 1;
        else if(root1==0||root2==0)
            return 0;
        else
            return like(root1->left,root2->left)&&
                like(root1->right,root2->right);
    }                                         

    node* copy_tree(node*root){
         node*root2;
         if(root!=0){
             root2=(node*)malloc(sizeof(node));
             root2->data=root->data;
             root2->left=copy_tree(root->left);
             root2->right=copy_tree(root->right);
             return root2;
         }
         else
             return 0;                               
     }

    void maxminleaf(node*root,int *m,int *n){
        int m1,m2,n1,n2;
        if(root=0){
            *m=0;
            *n=0;
        }  
        else{
            maxminleaf(root->left,&m1,&n1);
            maxminleaf(root->right,&m2,&n2);
            m=max(*m1,*m2)+1;
            n=min(*n1,*n2)+1;
        }
    }

    node* swap_tree(node*root){
         if(root=0)
             return 0;
         else{
             node* root2=(node*)malloc(sizeof(node));
             root2->data=root->data;
             root2->left=swap_tree(root->right);
             root2->right=swap_tree(root->left);
             return root2;
         }
     }                                        

    int leaf_cnt(node* root){
        if(root=0)
            return 0;
        if(root->left==0&&root->right==0)
            return 1;
        return leaf_cnt(root->left)+leaf_cnt(root->right);      
    }

     

  • 相关阅读:
    插件化架构深入剖析<一>-----插庄式实现Activity跳转机制剖析
    网易云音乐动态式换肤框架分析与手写实现<三>
    网易云音乐动态式换肤框架分析与手写实现<二>
    网易云音乐动态式换肤框架分析与手写实现<一>
    跨进程架构HermesEventBus原理分析到手写实现<三>
    在eclipse里用jdbc连接MySQL
    jdk环境变量配置
    oracle设置主键自增
    关于Navicat连接oralcle出现Cannot load OCI DLL 87,126,193 ,ORA-28547等错误
    Oracle 11g 安装过程及测试方法
  • 原文地址:https://www.cnblogs.com/xkfz007/p/2720607.html
Copyright © 2020-2023  润新知