宇龙酷派笔试题 2014 武汉

1、9*9乘法口诀

#include <stdio.h>


void main()
{
int i,j;
for (i=1;i<=9;i++)
{
for (j=1;j<=i;j++)
{
printf("%d*%d=%-3d",i,j,i*j);
}
printf("\n");
}
}

2、static作用

(1)先来介绍它的第一条也是最重要的一条:隐藏。

当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c。

下面是a.c的内容

char  a  =   ' A ' //  global variable
void  msg() 
{
    printf(
" Hello\n " ); 
}
 

下面是main.c的内容

int  main( void )
{    
    
extern   char  a;     //  extern variable must be declared before use
    printf( " %c  " , a);
    (
void )msg();
    
return   0 ;
}
 

程序的运行结果是:

A Hello

你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。

如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。

2 static 的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和 static 变量,只不过和全局变量比起来, static 可以控制变量的可见范围,说到底 static 还是用来隐藏的。虽然这种用法不常见,但我还是举一个例子。
#include  < stdio.h >

int  fun( void ){
    
static   int  count  =   10 ;     //  事实上此赋值语句从来没有执行过
     return  count -- ;
}

int  count  =   1 ;

int  main( void )
{    
    printf(
" global\t\tlocal static\n " );
    
for (; count  <=   10 ++ count)
        printf(
" %d\t\t%d\n " , count, fun());    
    
    
return   0 ;
}
 

程序的运行结果是:

global          local static

1               10

2               9

3               8

4               7

5               6

6               5

7               4

8               3

9               2

10              1

 

(3)static的第三个作用是默认初始化为0。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加’\0’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是’\0’。不妨做个小实验验证一下。

#include  < stdio.h >

int  a;

int  main( void )
{
    
int  i;
    
static   char  str[ 10 ];

    printf(
" integer: %d;  string: (begin)%s(end) " , a, str);

    
return   0 ;
}
程序的运行结果如下

integer: 0; string: (begin)(end)

最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0。


3、字符串反转

这是网络流传的Microsoft的面试题目之一:“编写反转字符串的程序,要求优化速度、优化空间”。因为最近一直很多关注算法方面的实践和研究,因此对这个问题进行了一些思考,给出了5种实现方法(有两种解法相关性比较大)。

解法一:第一次看到这题目,想到最简单、最直觉的解法就是:遍历字符串,将第一个字符和最后一个交换,第二个和倒数第二个交换,依次循环,即可,于是有了第一个解法:

char* strrev1(const char* str)

{

       int len = strlen(str);

       char* tmp = new char[len + 1];

       strcpy(tmp,str);

 

       for (int i = 0; i < len/2; ++i)

       {

              char c = tmp[i];

        tmp[i] = tmp[len – i - 1];

        tmp[len – i - 1] = c;

       }

 

       return tmp;

}

这里是通过数组的下标方式访问字符串的字符,实际上用指针直接操作即可。解法二正是基于此,实现代码为:

char* strrev2(const char* str)

{

       char* tmp = new char[strlen(str) + 1];

       strcpy(tmp,str);

       char* ret = tmp;

 

       char* p = tmp + strlen(str) - 1;

 

       while (p > tmp)

       {

              char t = *tmp;

              *tmp = *p;

              *p = t;

 

              --p;

              ++tmp;

       }

 

       return ret;

}

显然上面的两个解法中没有考虑时间和空间的优化,一个典型的优化策略就是两个字符交换的算法优化,我们可以完全不使用任何外部变量即完成两个字符(或者整数)的交换,这也是一个很经典的面试题目。特别是一些嵌入式硬件相关编程中经常要考虑寄存器的使用,因此经常有不使用任何第三个寄存器即完成两个寄存器数据的交换的题目。一般有两个解法,对应这里的解法三和解法四。

解法三的实现代码为:

char* strrev3(const char* str)

{

       char* tmp = new char[strlen(str) + 1];

       strcpy(tmp,str);

       char* ret = tmp;

 

       char* p = tmp + strlen(str) - 1;

 

       while (p > tmp)

       {

              *p ^= *tmp;

              *tmp ^= *p;             

              *p ^= *tmp;

 

              --p;

              ++tmp;

       }

 

       return ret;

}

解法四的实现代码为:

char* strrev4(const char* str)

{

       char* tmp = new char[strlen(str) + 1];

       strcpy(tmp,str);

       char* ret = tmp;

 

       char* p = tmp + strlen(str) - 1;

 

       while (p > tmp)

       {

              *p = *p + *tmp;

              *tmp = *p - *tmp;

              *p = *p - *tmp;

 

              --p;

              ++tmp;

       }

 

       return ret;

}

实际上我们还可以通过递归的思想来解决这个问题,思想很简单:每次交换首尾两个字符,中间部分则又变为和原来字符串同样的问题,因此可以通过递归的思想来解决这个问题,对应解法五的实现代码为:

char* strrev5(/*const */char* str,int len)

{

       if (len <= 1)

              return str;

 

       char t = *str;

       *str = *(str + len -1);

       *(str + len -1) = t;

 

       return (strrev5(str + 1,len - 2) - 1);

}

以下给出一个测试程序

int main(int argc,char* argv[])

{

       char* str = "hello";

       P(str);

 

       char* str2 = strrev1(str);

       P(str2);

 

       char* str3 = strrev2(str2);

       P(str3);

 

       char* str4 = strrev3(str3);

       P(str4);

 

       char* str5 = strrev4(str4);

       P(str5);

 

       char* str6 = strrev5(str5,strlen(str5));

       P(str6);

      

       return 0;

}

  你就可以看到字符串"hello"和"olleh"交替输出了。

  说明:1)这里解法中没有认真考虑输入字符串的合法性和特殊长度(如NULL、一个字符等)字符串的处理;2)前4个算法不改变输入字符串的值,解法五修改了输入字符串。


4、两个线程通信,不使用queue和list等

5、分支限界求旅行商问题

#include <STDIO.H>
#include <STDLIB.H>
#include <MATH.H>
#include <GRAPHICS.H>

#define  MAX_CITIES  15                                /* 城市的数目       */ 
#define  INFINITY    9999                              /* 表示无穷大       */ 
#define  I           INFINITY                          /* 表示无穷大       */

typedef struct _POINT {                                /* 定义点的结构     */ 
 int x; 
 int y; 
} POINT;

typedef struct _EDGE {                                 /* 定义边的结构     */ 
 int head; 
 int tail; 
} EDGE;

typedef struct _PATH {                                 /* 定义路径结构     */ 
 EDGE edge[MAX_CITIES]; 
 int  edgesNumber; 
} PATH;

typedef struct _MATRIX {                               /* 定义花费矩阵结构 */ 
 int distance[MAX_CITIES][MAX_CITIES]; 
 int citiesNumber; 
} MATRIX;

typedef struct _NODE {                                 /* 定义树结点       */ 
 int bound;                                     /* 结点的花费下界   */ 
 MATRIX matrix;                                 /* 当前花费矩阵     */ 
 PATH path;                                     /* 已经选定的边     */ 
} NODE;

int  minDist = INFINITY; 
int  GraphDriver; 
int  GraphMode; 
int  ErrorCode; 
POINT city[MAX_CITIES] = { 
 {459, 333}, {345, 234}, {362, 245}, {332, 183}, 
 {323, 343}, {630, 345}, {154, 263}, {213, 112}, 
 {432, 254}, {534, 223}, {334, 333}, {432, 234}, 
 { 23, 442}, {600, 400}, {500, 300} 
};

int     Simplify(MATRIX *);                    /* 归约矩阵并返回归约常数   */ 
int     MatrixSize(MATRIX, PATH);              /* 计算矩阵阶数             */ 
EDGE    SelectBestEdge(MATRIX);                /* 返回最合适的分枝边       */ 
MATRIX  InitMatrix(void);                      /* 初始化费用矩阵数据       */ 
MATRIX  LeftNode(MATRIX, EDGE);                /* 计算左枝结点费用矩阵     */ 
MATRIX  RightNode(MATRIX, EDGE, PATH);         /* 计算右枝结点费用矩阵     */ 
PATH    AddEdge(EDGE, PATH);                   /* 将边添加到路径数组中     */ 
PATH    BABA(NODE *);                          /* 分枝回溯函数 B-and-B Ar. */ 
PATH    MendPath(PATH, MATRIX);                /* 修补没有完成的路径       */ 
void    ShowMatrix(MATRIX);                    /* 文本显示费用矩阵 调试用  */ 
void    ShowPath(PATH);                        /* 文本显示路径             */ 
void    DrawPath(PATH);                        /* 图形显示路径             */

void main() 

 PATH path; 
 NODE root; 
 GraphDriver = DETECT; 
 initgraph( &GraphDriver, &GraphMode, "" ); 
 ErrorCode = graphresult(); 
 if( ErrorCode != grOk ) { 
  printf(" Graphics System Error: %s\n", 
   grapherrormsg(ErrorCode)); 
  exit(1); 
 } 
 
 /* 初始化数据,归约,建立根结点 */ 
 root.matrix = InitMatrix(); 
 root.bound = Simplify(&(root.matrix)); 
 (root.path).edgesNumber = 0; 
 
 /* 进入搜索循环,最终返回最佳路线 */ 
 path = BABA(&root); 
 
 /* 显示结果 */ 
 DrawPath(path); 
 ShowPath(path); 
 printf("\nminDist:%d\n", minDist); 
 
 getch(); 
 closegraph(); 
}

/* 初始化数据 */ 
MATRIX InitMatrix() 

 int row, col, n; 
 double dx, dy; 
 MATRIX c; 
 
 n = MAX_CITIES; /* 有待完善数据读取方式 */ 
 c.citiesNumber = n; 
 for (row = 0; row < n; row++) { 
  putpixel(city[row].x, city[row].y, 5); 
  for (col = 0; col < n; col++) { 
   dx = (double)(city[row].x - city[col].x); 
   dy = (double)(city[row].y - city[col].y); 
   /* 求两点间距离 */ 
   c.distance[row][col] = (int)sqrt(dx * dx + dy * dy); 
   if (row == col) 
    c.distance[row][col] = INFINITY; 
  } 
 } 
 return c; 

/* 
算法主搜索函数,Branch-And-Bound Algorithm Search 
root 是当前的根结点,已归约,数据完善 
*/ 
PATH BABA(NODE* root) 

 static PATH minPath; 
 EDGE selectedEdge; 
 NODE *left, *right; 
 
 /* 如果当前矩阵大小为2,说明还有两条边没有选,而这两条边必定只能有一 
 种组合,才能构成整体回路,所以事实上所有路线已经确定。 
 */ 
 if (MatrixSize(root->matrix, root->path) == 2) { 
  if (root->bound < minDist) { 
   minDist = root->bound; 
   minPath = MendPath(root->path, root->matrix); 
   free(root); 
   return (minPath); 
  } 
 } 
 /* 根据左下界尽量大的原则选分枝边 */ 
 setcolor(7); 
 selectedEdge = SelectBestEdge(root->matrix); 
 line(city[selectedEdge.head].x, city[selectedEdge.head].y, 
  city[selectedEdge.tail].x, city[selectedEdge.tail].y); 
 putpixel(city[selectedEdge.head].x, city[selectedEdge.head].y, MAGENTA); 
 putpixel(city[selectedEdge.tail].x, city[selectedEdge.tail].y, MAGENTA); 
 
 /* 
 建立左右分枝结点 
 */ 
 right = (NODE *)malloc(sizeof(NODE)); 
 if (right == NULL) { 
  fprintf(stderr,"Error malloc branch.\n"); 
  exit(-1); 
 } 
 /* 使左枝结点站局原根结点位置,节省空间 */ 
 left = root; 
 /* 初始化左右分枝结点 */ 
 right->matrix = RightNode(root->matrix, selectedEdge, root->path); 
 right->bound =         root->bound + Simplify(&(right->matrix)); 
 right->path = AddEdge(selectedEdge, root->path); 
 
 left->matrix = LeftNode(left->matrix, selectedEdge); 
 left->bound = left->bound + Simplify(&(left->matrix)); 
 
 /* 如果右结点下界小于当前最佳答案,继续分枝搜索 */ 
 if (right->bound < minDist) { 
  BABA(right); 
 } 
 /* 否则删除这条不可能产生更佳路线的死枝 */ 
 else { 
  free(right); 
 } 
 
 setcolor(BLACK);; 
 line(city[selectedEdge.head].x, city[selectedEdge.head].y, 
  city[selectedEdge.tail].x, city[selectedEdge.tail].y); 
 putpixel(city[selectedEdge.head].x, city[selectedEdge.head].y, MAGENTA); 
 putpixel(city[selectedEdge.tail].x, city[selectedEdge.tail].y, MAGENTA); 
 
 /* 如果右结点下界小于当前最佳答案,继续分枝搜索 */ 
 if (left->bound < minDist) { 
  BABA(left); 
 } 
 /* 
 如果不是最初根结点才删除,避免'Null pointer assignment'问题 
 ‘Null pointer assingnment'问题指如果手动删除主函数里面的数据 
 当main()执行完毕后释放空间时找不到数据的指针。 
 */ 
 else if ((left->path).edgesNumber != 0){ 
  free(left); 
 } 
 
 gotoxy(1, 1); 
 printf("Current minDist: %d  ", minDist); 
 return (minPath); 
}

/* 修补路径 */ 
PATH MendPath(PATH path, MATRIX c) 

 int row, col; 
 EDGE edge; 
 int n = c.citiesNumber; 
 
 for (row = 0; row < n; row++) { 
  edge.head = row; 
  for (col = 0; col < n; col++) { 
   edge.tail = col; 
   if (c.distance[row][col] == 0) { 
    path = AddEdge(edge, path); 
   } 
  } 
 } 
 return path; 
 
}

/* 归约费用矩阵,返回费用矩阵的归约常数 */ 
int Simplify(MATRIX* c) 

 int row, col, min_dist, h; 
 int n = c->citiesNumber; 
 
 h = 0; 
 /* 行归约 */ 
 for (row = 0; row < n; row++) { 
  /* 找出本行最小的元素 */ 
  min_dist = INFINITY; 
  for (col = 0; col < n; col++) { 
   if (c->distance[row][col] < min_dist) { 
    min_dist = c->distance[row][col]; 
   } 
  } 
  /* 如果本行元素都是无穷,说明本行已经被删除 */ 
  if (min_dist == INFINITY) continue; 
  /* 本行每元素减去最小元素 */ 
  for (col = 0; col < n; col++) { 
   if (c->distance[row][col] != INFINITY) { 
    c->distance[row][col] -= min_dist; 
   } 
  } 
  /* 计算归约常数 */ 
  h += min_dist; 
 } 
 
 /* 列归约 */ 
 for (col = 0; col < n; col++) { 
  /* 找出本列最小的元素 */ 
  min_dist = INFINITY; 
  for (row = 0; row < n; row++) { 
   if (c->distance[row][col] < min_dist) { 
    min_dist = c->distance[row][col]; 
   } 
  } 
  /* 如果本列元素都是无穷,说明本列已经被删除 */ 
  if (min_dist == INFINITY) continue; 
  /* 本列元素减去最小元素 */ 
  for (row = 0; row < n; row++) { 
   if (c->distance[row][col] != INFINITY) { 
    c->distance[row][col] -= min_dist; 
   } 
  } 
  /* 计算归约常数 */ 
  h += min_dist; 
 } 
 return (h); 
}

/* 搜索所有花费为零的边中最合适的,使左枝下界更大 */ 
EDGE SelectBestEdge(MATRIX c) 

 int row, col; 
 int n = c.citiesNumber; 
 int maxD; 
 EDGE best, edge; 
 
 /* 所用函数声明 */ 
 int D(MATRIX, EDGE); 
 
 maxD = 0; 
 for (row = 0; row < n; row++) { 
  for (col = 0; col < n; col++) { 
   edge.head = row; 
   edge.tail = col; 
   if (!c.distance[row][col] && maxD < D(c, edge)) { 
    maxD = D(c, edge); 
    best = edge; 
   } 
  } 
 } 
 return (best); 
}

/* 计算如果选 edge 作为分枝边,左枝( 不含 edge )下界的增量 */ 
int D(MATRIX c, EDGE edge) 

 int row, col, dRow, dCol; 
 int n = c.citiesNumber; 
 
 dRow = INFINITY; 
 for (col = 0; col < n; col++) { 
  if (dRow < c.distance[edge.head][col] && col != edge.tail) { 
   dRow = c.distance[edge.head][col]; 
  } 
 } 
 dCol = INFINITY; 
 for (row = 0; row < n; row++) { 
  if (dCol < c.distance[row][edge.tail] && row != edge.head) { 
   dCol = c.distance[row][edge.tail]; 
  } 
 } 
 return (dRow + dCol); 
}

/* 删掉所选分枝边( left ) */ 
MATRIX LeftNode(MATRIX c, EDGE edge) 

 c.distance[edge.head][edge.tail] = INFINITY; 
 return c; 
}

/* 删除行列和回路边( right ) */ 
MATRIX        RightNode(MATRIX c, EDGE edge, PATH path) 

 int row, col; 
 int n = c.citiesNumber; 
 EDGE loopEdge; 
 
 /* 声明所需要的求回路边函数 */ 
 EDGE LoopEdge(PATH, EDGE); 
 
 for (col = 0; col < n; col++) 
  c.distance[edge.head][col] = INFINITY; 
 for (row = 0; row < n; row++) 
  c.distance[row][edge.tail] = INFINITY; 
 
 loopEdge = LoopEdge(path, edge); 
 c.distance[loopEdge.head][loopEdge.tail] = INFINITY; 
 
 return (c); 
}

/* 计算回路边的函数 
除了加入的新边, 当前结点路线集合中还可能包含一些已经选定的边, 这些边构成 
一条或几条路径, 为了不构成回路, 必须使其中包含新边的路径头尾不能相连,本 
函数返回这个头尾相连的边,以便把这个回路边的长度设成无穷。 
*/

EDGE LoopEdge(PATH path, EDGE edge) 

 int i, j; 
 EDGE loopEdge; 
 
 /* 最小的回路边 */ 
 loopEdge.head = edge.tail; 
 loopEdge.tail = edge.head; 
 
 /* 寻找回路边的头端点,即包含新边的路径的尾端点 */ 
 for (i = 0; i < path.edgesNumber; i++) { 
  for (j = 0; j < path.edgesNumber; j++) { 
   if (loopEdge.head == path.edge[j].head) { 
    /* 扩大回路边 */ 
    loopEdge.head = path.edge[j].tail; 
    break; 
   } 
  } 
 } 
 /* 寻找回路边的尾端点,即包含新边的路径的头端点 */ 
 for (i = 0; i < path.edgesNumber; i++) { 
  for (j = 0; j < path.edgesNumber; j++) { 
   if (loopEdge.tail == path.edge[j].tail) { 
    /* 扩大回路边 */ 
    loopEdge.tail = path.edge[j].head; 
    break; 
   } 
  } 
 } 
 
 return (loopEdge); 
}

/* 将新边加入到路径中 */ 
PATH AddEdge(EDGE edge, PATH path) 

 path.edge[path.edgesNumber++] = edge; 
 return path; 
}


/* 计算花费矩阵当前阶数 */ 
int MatrixSize(MATRIX c, PATH path) 

 return (c.citiesNumber - path.edgesNumber); 
}

/* 文本方式显示路径 */ 
void ShowPath(PATH path) 

 int i; 
 EDGE edge; 
 int n = path.edgesNumber; 
 
 printf("\nThe path is:\n"); 
 for (i = 0; i < n; i++) { 
  edge = path.edge[i]; 
  printf("(%d,%d)", edge.head + 1, edge.tail + 1); 
 } 
}

/* 图形方式显示路径 */ 
void DrawPath(PATH path) 

 int i; 
 POINT a, b; 
 int n = path.edgesNumber; 
 
 for (i = 0; i < n; i++) { 
  a.x = city[(path.edge[i]).head].x; 
  a.y = city[(path.edge[i]).head].y; 
  b.x = city[(path.edge[i]).tail].x; 
  b.y = city[(path.edge[i]).tail].y; 
  line(a.x, a.y, b.x, b.y); 
  setcolor(MAGENTA); 
  circle(a.x, a.y, 5); 
  circle(b.x, b.y, 5); 
  setcolor(BLUE); 
 } 
}


/* 文本方式显示花费矩阵,调试用 */ 
void ShowMatrix(MATRIX c) 

 int row, col; 
 int n =  c.citiesNumber; 
 
 for (row = 0; row < n; row++) { 
  for (col = 0; col < n; col++) { 
   if (c.distance[row][col] != INFINITY) { 
    printf("%4d", c.distance[row][col]); 
   } 
   else { 
    printf("   -"); 
   } 
  } 
  printf("\n"); 
 } 
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值