第3章 数据结构与算法
1. memmove边界问题
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函数: 进行了比较好的判断, 主要是便于比较快的结束判断
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 *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位图来记录出现的字符
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 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)正则表达式匹配问题
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 <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<< ' ';
}
} //这是一种比较好的方式来实现后序遍历的
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( int* in, 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: 求出从根节点到给定节点的路径.
代码如下:
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. 递归在树中的应用
代码如下:
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*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;
}
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;
}
}
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;
}
}
if(root= 0)
return 0;
if(root->left== 0&&root->right== 0)
return 1;
return leaf_cnt(root->left)+leaf_cnt(root->right);
}