剑指offer:面试的基础知识(二)
2014-03-31 18:58
162 查看
二、数据结构
数据结构一直是技术面试的重点,大多数面试题围绕着数组、字符串、链表、树、栈及队列这几种数据结构展开。
1. 数组
数组最大的特点是内存连续,可以在O(1)时间读/写任何元素,因此时间效率高。根据这个特点,可以用数组实现简单的哈希表。其实,数组和哈希表也经常是空间换时间、减少搜索时间的常客。关于数组一个很有名的问题,就是数组与指针之间的区别。在C++中,当数组作为函数的参数进行传递时,数组就自动退化成同类型的指针。
面试题3:二维数组中的查找
题目:在一个二维数组中,每一行都按照从左到右递增的顺便排序,每一列都按照从上到下的顺序排序。请完成一个函数,输入这样一个二维数组和一个整数,判断数组中是否存在改整数。
解析:最直接的方法就是把二维数组遍历一般,找到那个整数。但是有没有更高效的算法呢?显然,想要找到高效的算法,就不能走寻常遍历的路!怎么走呢?从左上角、右下角、左下角,还是右上角(怎不可能从中间开始走吧)。举个小例子,可能就会发现规律了。
2. 字符串
面试题4:替换字符串
题目:请实现一个函数,把字符串中每个空格替换成“%20”。例如“We are happy”, 则输出"We%20are%20happy.'
解析:看到题目想到的方式是将原先字符串拷贝到一个新字符串中,并将空格替换成%20。但这样做有个风险,就是我们不知道扩展后的字符串多大,特别是原字符串很长的时候。这里考虑一种在原字符串上作替换的方法。
3. 链表
链表的创建、插入节点、删除等操作都比较时候写代码。需要注意的是,链表的创建和删除可能改变链表头指针的值
面试题5:从尾到头打印链表
解析:打印链表肯定需要遍历链表,通常遍历的话是从头到尾,这里要求从尾到头,有思路了吗?不需要额外的数据结构我们可以实现吗?这里有递归(不属于基本控制的一朵奇葩)!
4. 树
树最常见的就是它的三种遍历方法:先序,中序,后序。另外就是宽度优先搜索。其中三种遍历方法本质上都是深度优先搜索。二叉树为最常见的形式。比较特殊的二叉树有二叉搜索树,二叉平衡搜索树,heap,红黑树。这些是满足某种特性的二叉树,该特性也符合递归特性。
面试题6:重建二叉树
题目:输入某二叉树的前序遍历和中序遍历的结果,请重新建出该二叉树。假设输入的前序遍历和中序遍历结构中都不含有重复数字。例如前序遍历序列{1,2,4,7,3,5,6, 8}和中序遍历序列{4,7,2,1,5,3,8,6}, 则重返二叉树的头结点。
面试题24:二叉树的后续遍历序列
题目:输入一组整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数都不同。
解析:很明显又要在数组上实现递归了。
数据结构一直是技术面试的重点,大多数面试题围绕着数组、字符串、链表、树、栈及队列这几种数据结构展开。
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; }如果需要返回值,那么请给该返回变量设定初始值。与链表不同的是,树要时刻记得检查是否存在左右分支。
相关文章推荐
- 剑指offer读书总结-->面试所需的基础知识
- 剑指Offer-第2章 面试需要的基础知识
- 剑指offer——C++面试需要的基础知识
- 剑指offer:面试的基础知识(三)
- 剑指offer——C++面试需要的基础知识
- 剑指offer-数据结构:数组和指针(基础知识)
- 剑指offer-数据结构:字符串(基础知识)
- 剑指offer 第二章 基础知识
- 剑指Offer Java版 基础知识1
- 剑指Offer——中国银行面试知识储备
- C++面试总结 笔试基础知识常考点
- 剑指Offer之面试位运算总结
- (C++)剑指offer-38:二叉树的深度(知识迁移能力)
- 【剑指Offer面试编程题】题目1510:替换空格--九度OJ
- JAVA基础知识复习面试笔试宝典
- 剑指offer:(19)面试官谈面试思路:二叉树的镜像
- 面试常备题-----jsp基础知识
- 面试基础知识之LINUX篇
- 数据库基础知识面试必备
- JS 面试知识学习历程(第二天) -- JS基础知识(上)