@qiezhian
2014-06-27T11:26:51.000000Z
字数 9121
阅读 1455
算法
大多数面试题都是围绕数组、字符串、链表、树、栈、队列等几种最常见的数据结构展开的。因为高级数据结构实现复杂,很难在段时间内完全写出代码,而这些简单的数据结构是基础却可以解决很多复杂的问题,所以面试官更愿意考察这些基础结构。
二维数组查找:在一个二维数组中,每一行按从左至右的递增顺序排序,每一列都按从上到下顺序递增排列。完成一个函数,输入这样一个二维数组和一个整数,判断数组中是否含有该整数。
容易想到二分查找。但是从矩阵(二维数组)的右下角开始取点的话,需要分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;
else
cout<< " 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--;
else
row++;
}
return 0;
}
字符串替换:实现一个函数,把字符串中的每个空格替换成"%20"。
将一个空格字符替换为"%20",会使得字符串变长,会覆盖后面的字符。因此需要从后往前替换,首先需要计算空格个数,以正确的计算替换后字符串长度。
/*
Filename: ReplaceString.c
Function: replace the space ' ' with '%20' in string
Details:
*/
#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.cpp
Function: print list reversingly
Details:
*/
#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.cpp
Function: 给出某二叉树的前序及中序遍历结果,重建该二叉树
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.hpp
Function: 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.hpp
Function: implement a version of "stack" with min() function in O(1) time
Details:
*/
#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() );
else
stack_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;
else
cout<< "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 );
}
}