您的位置:首页 > 职场人生

剑指offer:面试的基础知识(二)

2014-03-31 18:58 162 查看
二、数据结构

数据结构一直是技术面试的重点,大多数面试题围绕着数组、字符串、链表、树、栈及队列这几种数据结构展开。

1. 数组

数组最大的特点是内存连续,可以在O(1)时间读/写任何元素,因此时间效率高。根据这个特点,可以用数组实现简单的哈希表。其实,数组和哈希表也经常是空间换时间、减少搜索时间的常客。关于数组一个很有名的问题,就是数组与指针之间的区别。在C++中,当数组作为函数的参数进行传递时,数组就自动退化成同类型的指针。

面试题3:二维数组中的查找

题目:在一个二维数组中,每一行都按照从左到右递增的顺便排序,每一列都按照从上到下的顺序排序。请完成一个函数,输入这样一个二维数组和一个整数,判断数组中是否存在改整数。

解析:最直接的方法就是把二维数组遍历一般,找到那个整数。但是有没有更高效的算法呢?显然,想要找到高效的算法,就不能走寻常遍历的路!怎么走呢?从左上角、右下角、左下角,还是右上角(怎不可能从中间开始走吧)。举个小例子,可能就会发现规律了。

bool searchValue(int *a, int row, int col, int val){  //二维数组传参有点奇怪
int found=false;
if(a!=NULL && row>0 && col>0){      //别忘记错误输入处理!
int i=0;
int j=col-1;
while(i<row&&j>=0){
if(a[i*col+j]==val) return true;
if(a[i*col+j]>val) j--;
else i++;
}
}
return found;
}
想清楚之后,代码居然这么简单,不可思议吧。

2. 字符串

面试题4:替换字符串

题目:请实现一个函数,把字符串中每个空格替换成“%20”。例如“We are happy”, 则输出"We%20are%20happy.'

解析:看到题目想到的方式是将原先字符串拷贝到一个新字符串中,并将空格替换成%20。但这样做有个风险,就是我们不知道扩展后的字符串多大,特别是原字符串很长的时候。这里考虑一种在原字符串上作替换的方法。

void replaceBlank(char* str, int len){
if(str==NULL || len<=0) return;

int numOfBlank=0;
for(int i=0;str[i]!='\0';i++){
if(str[i]==' ')
numOfBlank++;
}
int newlen=len+2*numOfBlank;
int i=len;
int j=newlen;
while(i<j&&i>=0){  //小心边界条件
if(str[i]==' '){
str[j--]='0';
str[j--]='2';
str[j]='%';
}
else str[j]=str[i];
j--;i--;
}
}
从后面往前,逐个拷贝,可减少移动次数。

3. 链表

链表的创建、插入节点、删除等操作都比较时候写代码。需要注意的是,链表的创建和删除可能改变链表头指针的值

void addToTail(ListNode** phead, int value);
void removeNode(ListNode** phead, int value);
因为改变的head本身的值,因此需要传入head的地址。

面试题5:从尾到头打印链表

解析:打印链表肯定需要遍历链表,通常遍历的话是从头到尾,这里要求从尾到头,有思路了吗?不需要额外的数据结构我们可以实现吗?这里有递归(不属于基本控制的一朵奇葩)!

void printListReverse(ListNode* phead){
if(phead==NULL) return;
if(phead->next!=NULL){
printListReverse(phead->next);
}
cout<<phead->data<<"\t";
}
想到递归,就应该联想到stack,他俩可是天生一对呢。

4. 树

树最常见的就是它的三种遍历方法:先序,中序,后序。另外就是宽度优先搜索。其中三种遍历方法本质上都是深度优先搜索。二叉树为最常见的形式。比较特殊的二叉树有二叉搜索树,二叉平衡搜索树,heap,红黑树。这些是满足某种特性的二叉树,该特性也符合递归特性。

面试题6:重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重新建出该二叉树。假设输入的前序遍历和中序遍历结构中都不含有重复数字。例如前序遍历序列{1,2,4,7,3,5,6, 8}和中序遍历序列{4,7,2,1,5,3,8,6}, 则重返二叉树的头结点。

BinaryTreeNode* construct(int pre[],int in[],int len){
if(pre==NULL || in==NULL || len==0) return NULL;
return doConstruct(pre,len,in);
}

BinaryTreeNode* doConstruct(int* pre, int len, int* in){  //这里比纯用下标控制子数字简洁
BinaryTreeNode *root;
root=(BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
root->data=pre[0];
root->left=root->right=NULL;

if(len==1){
return root;
}
int rootInorder=0;
for(int i=0;i<len;i++){
rootInorder++;
if(in[i]==pre[0]) break;
}
//这里可加入出差处理!!
if(rootInorder>1)
root->left=doConstruct(pre+1,rootInorder-1,in);
if(len>rootInorder)
root->right=doConstruct(pre+rootInorder,len-rootInorder,in+rootInorder);
return root;
}

面试题24:二叉树的后续遍历序列

题目:输入一组整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数都不同。

解析:很明显又要在数组上实现递归了。

bool postReverse(int* a, int len){
if(a==NULL || len<=0) return false;

int pivot;
for(pivot=0;pivot<len;pivot++)
if(a[pivot]>a[len-1]) break;

for(int j=pivot;j<len;j++)
if(a[j]<a[len-1]) return false;

int left,right;
left=right=false;    //设定默认返回值

if(pivot>0)          //判断是否有左子树
left=postReverse(a, pivot);
if(pivot<len)        //判断是否有右子树
right=postReverse(a+pivot,len-pivot);

return left&right;
}
如果需要返回值,那么请给该返回变量设定初始值。与链表不同的是,树要时刻记得检查是否存在左右分支。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: