@qiezhian
2014-06-27T11:26:51.000000Z
字数 9121
阅读 1522
算法
大多数面试题都是围绕数组、字符串、链表、树、栈、队列等几种最常见的数据结构展开的。因为高级数据结构实现复杂,很难在段时间内完全写出代码,而这些简单的数据结构是基础却可以解决很多复杂的问题,所以面试官更愿意考察这些基础结构。
二维数组查找:在一个二维数组中,每一行按从左至右的递增顺序排序,每一列都按从上到下顺序递增排列。完成一个函数,输入这样一个二维数组和一个整数,判断数组中是否含有该整数。
容易想到二分查找。但是从矩阵(二维数组)的右下角开始取点的话,需要分3种情况来分析查找的过程。这一题的创新思维在于要从矩阵边上取点,则每次只需分2种情况进行查找,也就实现了二分查找。
#include <iostream>using namespace std;int FindinMatrix( int* m, int rows, int cols, int element );int main(){int m[]={1,2,3,4,2,3,4,5,3,4,5,6,4,5,6,7,};int element = 8;if( FindinMatrix( m ,4,4, element ) )cout<< " search success, element " << element << " is in matrix !"<<endl;elsecout<< " search failed, element " << element<< " is not in matrix !"<<endl;return 0;}int FindinMatrix( int* matrix, int rows, int cols, int element ){int col, row;row = 0;col = cols-1;while( row <= rows-1 && col >= 0 ){if( matrix[row*cols+col] == element )return 1;else if( matrix[row*cols+col] >= element )col--;elserow++;}return 0;}
字符串替换:实现一个函数,把字符串中的每个空格替换成"%20"。
将一个空格字符替换为"%20",会使得字符串变长,会覆盖后面的字符。因此需要从后往前替换,首先需要计算空格个数,以正确的计算替换后字符串长度。
/*Filename: ReplaceString.cFunction: replace the space ' ' with '%20' in stringDetails:*/#include <stdio.h>#include <stdlib.h>#include <string.h>void ReplaceString( char *str, int length );int main(){char str[30] = "I'm so happy!";printf( "original string:%s\n ", str );ReplaceString( str, 30 );printf( "replaced string:%s\n", str );return 0;}/* length 是str拥有的实际内存大小,单位字节 */void ReplaceString( char *str, int length ){int original_length = strlen( str );int new_length;int i=0;int j;int space_num=0;while( str[i] != '\0' )if( str[i++]== ' ' )space_num++;new_length = 2*space_num + original_length;if( new_length >= length ){printf("string's memory not enough!");return;}i = original_length;j = new_length;while( i>=0 && j>=0 )if( str[i]==' ' ){str[j--] = '0';str[j--] = '2';str[j--] = '%';i--;}else{str[j--] = str[i--];}}
从尾到头打印链表:反序输出是典型的“后进先出”,容易想到递归和栈结构。用栈结构需要多stack.h在其他文中有完整实现。
/*Filename: PrintListReversingly.cppFunction: print list reversinglyDetails:*/#include <iostream>#include "stack.hpp"struct ListNode{int mElement;ListNode *pNext;};ListNode* BuildList( ListNode *list );void PrintListNormally( ListNode *list );void PrintListReversingly( ListNode *list );void PrintListReversingly2( ListNode *list );int main( ){int i=0;ListNode *list = NULL;list = BuildList( list );if( list==NULL )return 0;PrintListNormally( list );PrintListReversingly( list );PrintListReversingly2( list );}ListNode* BuildList( ListNode *list ){if( list==NULL )list = new ListNode;int i=0;ListNode *head, *tmp;head = list;for( i=1; i<25; i++){tmp = new ListNode;if( tmp == NULL )return NULL;tmp->mElement = i;head->pNext = tmp;head = head->pNext;}return list;}void PrintListNormally( ListNode *list ){cout<<"print list normally"<<endl;ListNode *p = list;while( p!= NULL ){cout<< p->mElement <<endl;p = p->pNext;}}void PrintListReversingly( ListNode *list ){cout<<"Print List Reversingly Iteratively"<<endl;if( list->pNext!=NULL )PrintListReversingly( list->pNext );cout<< list->mElement <<endl;}void PrintListReversingly2( ListNode *list ){cout<<"Print List Reversingly using struct stack"<<endl;MyStack<ListNode*> stack;ListNode *p = list;while( p!=NULL ){stack.Push( p );p = p->pNext;}while( !stack.IsEmpty() )cout<< stack.Pop()->mElement <<endl;}
树的操作涉及大量的指针,因此与树有关的面试题都不太容易,而树中常考的莫过于二叉树,这是重中之重。
重建二叉树:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中不含重复数字。
在二叉树前序遍历中,第一个数字总是树的根节点的值。但在中序遍历中,根节点的值正好处于序列的中间,其左边为左子树的值集合,其右边为右子树的值集合。因此重建二叉树步骤为:先重建根节点,再重建根节点的左儿子,再重建根节点的右儿子。程序为递归实现。
/*Filename: ReconstructBinTree.cppFunction: 给出某二叉树的前序及中序遍历结果,重建该二叉树Details:*/#include <iostream>using namespace std;struct BinTreeNode{int m_Element;BinTreeNode* m_pLeft;BinTreeNode* m_pRight;};void PrintTreePreOrderly( BinTreeNode* root );void PrintTreeMidOrderly( BinTreeNode* root );BinTreeNode* Reconstruct( int* pre, int* mid, int length );BinTreeNode* ReconstructCore( int* pre, int* mid, int pre_start,int pre_end, int mid_start, int mid_end);int FindIndex( int *a, int start, int length, int element );int main( ){int Pre[]={1,2,4,7,3,5,6,8};int Mid[]={4,7,2,1,5,3,8,6};int size = sizeof( Pre )/sizeof( int );int i;cout<<"Tree preorderly print: " << endl;for( i=0; i<size; i++ )cout<< Pre[i];cout<<endl;cout<<"Tree midorderly print: " << endl;for( i=0; i<size; i++ )cout<< Mid[i];cout<<endl;BinTreeNode* root = Reconstruct( Pre, Mid, size );if( root==NULL )cout<<" Reconstruct failed!"<< endl;cout<<"Reconstructed tree preorderly print: " << endl;PrintTreePreOrderly( root );cout<<" Reconstructed tree midorderly print: " << endl;PrintTreeMidOrderly( root );return 0;}BinTreeNode* Reconstruct( int* pre, int* mid, int length ){return ReconstructCore( pre, mid, 0, length-1, 0, length-1 );}BinTreeNode* ReconstructCore( int* pre, int* mid, int pre_start,int pre_end, int mid_start, int mid_end){if( pre_end - pre_start != mid_end - mid_start|| pre_end < pre_start || mid_end < mid_start )return NULL;BinTreeNode* node = NULL ;node = new BinTreeNode;node->m_Element = pre[pre_start];if( pre_end > pre_start ){int index = FindIndex( mid , mid_start,mid_end-mid_start+1, pre[pre_start] );if( index!= -1 ){node->m_pLeft = ReconstructCore( pre,mid,pre_start+1,pre_start+index,mid_start, mid_start+index-1 );node->m_pRight = ReconstructCore( pre,mid, pre_start+index+1, pre_end,mid_start+index+1, mid_end );}}return node;}int FindIndex( int* a, int start, int length, int element ){if( a==NULL || length<=0 )return -1;int i=start;for( ; i<start+length; i++)if( element == a[i] )return ( i-start );return -1;}void PrintTreePreOrderly( BinTreeNode *root ){if( !root )return;cout<< root->m_Element << endl;PrintTreePreOrderly( root->m_pLeft );PrintTreePreOrderly( root->m_pRight );}void PrintTreeMidOrderly( BinTreeNode *root ){if( !root )return;PrintTreeMidOrderly( root->m_pLeft );cout<< root->m_Element << endl;PrintTreeMidOrderly( root->m_pRight );}
队列的声明如下,请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的功能。
栈的特点是“先进后出”,在计算机领域被广泛应用,而队列是“先进先出”,二者针锋相对,但却互相联系。
/*Filename: CQueue.hppFunction: implement simple version of struct "queue" using two "stack"s.Details: please check IsEmpty() function before call deleteHead() function*/#include <iostream>#include "stack.hpp"using namespace std;template<typename T>class CQueue{public:CQueue();~CQueue();void appendTail( const T& node );T deleteHead( );bool IsEmpty( );private:MyStack<T> stack_in;MyStack<T> stack_out;};template<typename T>CQueue<T>::CQueue( ){}template<typename T>CQueue<T>::~CQueue( ){}template<typename T>bool CQueue<T>::IsEmpty( ){return ( stack_in.IsEmpty() && stack_out.IsEmpty() );}template<typename T>void CQueue<T>::appendTail( const T& node ){stack_in.Push( node );}template<typename T>T CQueue<T>::deleteHead( ){if( stack_out.IsEmpty() )while( !stack_in.IsEmpty() )stack_out.Push( stack_in.Pop() );return stack_out.Pop();}
题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素min函数。在该栈中,调用min、push、pop等函数的时间复杂度均为
分析: 光用一个成员变量来记录最小元素是不够的,当最小元素弹出时,下一个最小元素是多少?要找出其最小元素就不可能在
/*Filename: StackWithMin.hppFunction: implement a version of "stack" with min() function in O(1) timeDetails:*/#include "stack.hpp"template<typename T>class StackWithMin{public:StackWithMin();~StackWithMin();void Push( const T& node );T Pop( );bool IsEmpty();T Min();private:MyStack<T> stack_data;MyStack<T> stack_min;};template<typename T>StackWithMin<T>::StackWithMin(){}template<typename T>StackWithMin<T>::~StackWithMin(){}template<typename T>void StackWithMin<T>::Push( const T& node ){if( stack_min.IsEmpty() )stack_min.Push( node );else{if( node > stack_min.Top() )stack_min.Push( stack_min.Top() );elsestack_min.Push( node );}stack_data.Push( node );}template<typename T>T StackWithMin<T>::Pop( ){stack_min.Pop();return stack_data.Pop();}template<typename T>T StackWithMin<T>::Min(){return stack_min.Top();}template<typename T>bool StackWithMin<T>::IsEmpty(){return ( stack_data.IsEmpty() );}
题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1、2、3、4、5是某栈的压栈序列,序列4、5、3、2、1是一个合法的弹出序列,但是序列4、3、5、1、2就不可能是该栈的弹出序列。
分析: 比较直观的思路是用栈来模拟压入弹出过程。假设压入序列为I,弹出序列为O,i和j分别是I和O的索引。则按一下三步走:
1.如果I[i]==O[j],则说明此元素入栈后直接又出栈,直接略过,i++;j++;
2.否则将O[j]和栈顶元素比较,相等说明此元素此时需弹出,则j++且栈弹出一个元素;不相等则栈压入I[i++];
3.最后判断栈是否为空,不为空则说明该弹出序列不合法。
#include <iostream>#include "stack.hpp"using namespace std;bool IsPopOrder( int* push_order, int* pop_order, int length );int main( ){int push_order[]={ 2,4,6,8,10,9,7,5,3,1 };int pop_order[]={ 6,4,2,9,10,8,3,7,1,5 };int size = sizeof( push_order )/sizeof( int );if( IsPopOrder( push_order, pop_order, size ) )cout<< "pop order is right!"<<endl;elsecout<< "pop order is wrong!"<<endl;return 0;}bool IsPopOrder( int* push_order, int* pop_order, int length ){if( push_order==NULL || pop_order==NULL || length<=0 )return false;MyStack<int> mystack;int i=0, j=0;while( i<length && j<length ){if( push_order[i] == pop_order[j] ){i++; j++;}else{if( !mystack.IsEmpty() && pop_order[j] == mystack.Top() ){j++;mystack.Pop();}else{mystack.Push( push_order[i++] );}}}while( !mystack.IsEmpty() && mystack.Top() == pop_order[j] ){mystack.Pop(); j++;}return ( mystack.IsEmpty() );}
题目:从上往下打印出二叉树的每个结点,同一层的结点按照从左到又的顺序打印。
分析:这是树的一种遍历方法,称为层序遍历。本题看似考查树的知识,实际上用的却是优先队列结构。方法如下:从队列头部取出最早进入队列的结点(根结点),当每一次打印一个结点的时候,如果该结点有子结点,则把子结点放入队列的末尾,并弹出打印过的结点,再取下一个结点进行打印。重复前面的动作,直至队列中所有的结点都被打印出来为止。
template<typename T>void PrintTreeHierarchically( BinTreeNode<T>* root ){if( root==NULL )return;CQueue<BinTreeNode<T>*> queue;BinTreeNode<T>* pTree = root;queue.Push( pTree );while( !queue.IsEmpty() ){pTree = queue.Pop();cout<< pTree->m_nElement <<endl;if( pTree->m_pLeft != NULL )queue.Push( pTree->m_pLeft );if( pTree->m_pRight != NULL )queue.Push( pTree->m_pRight );}}