[基础算法]面试简单算法实现
https://github.com/iyjhabc/simple_algorithm
1、快速排序
选择数组的其中一个元素(一般为第一个)作为分界pivot,用两个游标分别从后往前和从前往后扫描数组。先从后游标开始,当后游标所指的值比pivot小,则与pivot交换,后游标交换后才扫描前游标;当前游标所指值比pivot大,则与pivot交换。一次分组的结果是pivot前面的元素全部比pivot小,后面的全部比pivot大。既然对前后两部分继续调用分组函数即可完成排序。
下面的程序对上述过程做了优化,交换的时候直接把游标所指的值覆盖到pivot的位置上,覆盖后,原来游标所指的位置作为下一次的pivot位置,准备被下一次调换时被覆盖。当前后两个游标相遇时,此位置就是pivot值在有序数组中的位置了。此优化其实就是利用了pivot的位置进行元素交换,避免了使用多余的空间。
int partition(int *p,int begin,int end){ int ipivot=begin; int pivot=p[begin]; begin++; while(begin<=end){ while(begin<=end && p[end]>=pivot){ end--; } if(begin<=end){ p[ipivot]=p[end]; ipivot=end; end--;//这里不用忘记了 } while(begin<=end && p[begin]<=pivot){ begin++; } if(begin<=end){ p[ipivot]=p[begin]; ipivot=begin; begin++;//这里不用忘记了 } } p[ipivot]=pivot; return ipivot; } void myqs(int *p,int begin,int end){ if(begin>=end){ return; } int ipivot=partition(p,begin,end); myqs(p,begin,ipivot-1); myqs(p,ipivot+1,end); }
2、完全二叉树的判断、二叉树的按层遍历
完全二叉树:给二叉树按层编号,如果二叉树的编号与满二叉树的编号一一对应(但节点数比满二叉树少),就称为完全二叉树。通俗来说就是叶子节点都集中在树的左侧。
性质:除最底层外,上层是一棵满二叉树。不可能单独出现右叶节点。利用队列可按层遍历二叉树,遍历过程中如发现叶节点或只有左儿子的节点,则后面的节点都只能为叶子节点,否则不是完全二叉树。如发现节点只有右儿子,则不是完全二叉树。
按层遍历二叉树:从根节点开始,压根节点进队。当队列非空,把队头节点出队(浏览节点数据),把节点左右儿子入队。不断重复此操作至队列为空。
void Tree::show_tree_bylevel(){ queue<BinaryTreeNode*> que; que.push(m_pRoot); while(!que.empty()){ cout<<que.front()->m_nValue<<endl; if(que.front()->m_pLeft!=NULL)que.push(que.front()->m_pLeft); if(que.front()->m_pRight!=NULL)que.push(que.front()->m_pRight); que.pop(); } } int Tree::is_complete_binarytree(){ queue<BinaryTreeNode*> que; int flag=0; que.push(m_pRoot); while(!que.empty()){ BinaryTreeNode *p=que.front(); if(flag && (p->m_pLeft!=NULL || p->m_pRight!=NULL))return 0;//标记后只能出现叶子节点 if(p->m_pLeft==NULL && p->m_pRight!=NULL)return 0;//有单右儿子,不是完全树 if(p->m_pRight==NULL)flag=1;//出现单左或者叶子节点,则标记 if(p->m_pLeft!=NULL)que.push(p->m_pLeft); if(p->m_pRight!=NULL)que.push(p->m_pRight); que.pop(); } return 1; }
3、普通插入排序与希尔排序
插入排序(insert sort):从数组头往后扫描,发现非升序(降序)的元素时,先记录于buf,把前面的元素往后移动,直到遇到比buf小的元素,则把buf放到此元素后面。复杂度O(n^2)
希尔排序(shell sort):是插入排序的改进。与插入排序不同的是,它把原数组按一定的gap分组,在分组内进行插入排序。逐渐把分组的间隔缩小,最后gap=1时就相当于进行普通的插入排序。因为组内的元素间隔为gap,所以元素需要移动时可以比普通插入排序(即gap=1)时更快地移动,从而提高效率.
如 4,3,6,2,65,1,7,8 以gap=4: 4 65;3 1;6 7;2 8 因此1后移到3的位置,其余不需要移动,结果为:4,1,6,2,65,3,7,8 gap=2:4 6 65 7;1 2 3 8 因此7后移到65之后,第二个分组不需移动,结果为:4,1,6,2,7,3,65,8 gap=1:普通的插入排序,把后面的元素往前面的有序部分插入
void insert_sort(int *p,int n){ for(int i=1;i<n;++i){ if(p[i-1]>p[i]){ int buf=p[i];//暂存需要插入前方的数据 int j; for(j=i-1;p[j]>buf && j>=0;--j)//数据往后移,直到buf的合适位置 p[j+1]=p[j]; p[j+1]=buf;//最后一次操作j多减了1 } } } void shell_sort(int *p,int n){ for(int gap=n/2;gap>0;gap/=2){//最后一次gap=1,以一个普通的插入排序结束 for(int i=gap;i<n;i++){ if(p[i-gap]>p[i]){ int buf=p[i];//暂存需要插入前方的数据 int j; for(j=i-gap;p[j]>buf && j>=0;j-=gap)//数据以gap速度移动,较插入排序快 p[j+gap]=p[j]; p[j+gap]=buf;//最后一次操作j多减了1 } } } }
4、实现strstr函数
char* strstr(char *str1,const char *str2){//查找str2,如有则从受托人str2位置开始返回 /*string target=str1; string::size_type k=target.find(str2); return str1+k;*/ size_t len=strlen(str2); while(*str1!='