80道面试题及其解法(三)
2016-06-21 07:52
393 查看
现有LA与LB两个链表,已升序排列。将LA与LB合并后也按升序排列.
设指针pa与pb分别均指向LA与LB当前的结点,那么只需要每次比较当前的这两个节点,谁小就把谁加入到LC(新链表)中,然后小的这个链表的指针继续后移。这样最坏的复杂度也只是O(LENA+LENB),算法2的实现如下:
[cpp] view
plain copy
print?
//结点结构体定义
struct LNode{
int data;
LNode *next;
};
typedef LNode * LinkList;
void main()
{//函数声明
void creat_list(LinkList &L,int n);
void merge_list(LinkList &La,LinkList &Lb,LinkList &Lc);
void print_list(LinkList L);
//主函数开始
int n1,n2;
LinkList La,Lb,Lc;
printf("建立La链表开始:请先输入要建立的结点个数\n");
scanf("%d",&n1);
creat_list(La,n1);
printf("La链表信息如下:\n");
print_list(La);
printf("建立Lb链表开始:请先输入要建立的结点个数\n");
scanf("%d",&n2);
creat_list(Lb,n2);
printf("Lb链表信息如下:\n");
print_list(Lb);
printf("合并完两链表后如下:\n");
merge_list(La,Lb,Lc);
print_list(Lc);
}
void creat_list(LinkList &L,int n)
{ int i;
LinkList p,q;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;//建立头结点
q=L;
for (i=1;i<=n;i++)
{p=(LinkList)malloc(sizeof(LNode));
printf("请输入第%d个节点的数据:",i);
scanf("%d",&p->data);
q->next=p;
q=q->next;//q后移
}
p->next=NULL;//最后一个节点是尾结点
}
void merge_list(LinkList &La,LinkList &Lb,LinkList &Lc)
{LinkList pa,pb,pc;
pa=La->next;pb=Lb->next;//a,b链表均指向自己的第一个结点
pc=Lc=La;//Lc链表自始至终就没有建立过,实际上LC就是借助指针去连接la和lb中的结点
while(pa&&pb)//不指向末尾
{
if (pa->data<=pb->data)//插入La
{pc->next=pa;
pc=pa;//pc后移
pa=pa->next;//La后移
}
else
{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}//结束该循环后应该会剩下一个链表没合并完(假设俩个链表长度不一样)
pc->next=pa?pa:pb;//剩余
}
void print_list(LinkList L)
{
LinkList p=L->next;//指向第一个结点
while(p)
{
printf("%3d",p->data);
p=p->next;
}
printf("\n");
}
题目描述:
一个循环有序数组(如:3,4,5,6,7,8,9,0,1,2),不知道其最小值的位置,要查找任一数值的位置。要求算法时间复杂度为log2(n)。
问题分析:
我们可以把循环有序数组分为左右两部分(以mid
= (low+high)/ 2为界),由循环有序数组的特点知,左右两部分必有一部分是有序的,我们可以找出有序的这部分,然后看所查找元素是否在有序部分,若在,则直接对有序部分二分查找,若不在,对无序部分递归调用查找函数。
代码如下:
#include <iostream>
using namespace std;
int binarySearch(int a[],int low,int high,int value) //二分查找
{
if(low>high)
return -1;
int mid=(low+high)/2;
if(value==a[mid])
return mid;
else if(value>a[mid])
return binarySearch(a,mid+1,high,value);
else
return binarySearch(a,low,mid-1,value);
}
int Search(int a[],int low,int high,int value) //循环有序查找函数
{
int mid=(low+high)/2;
if(a[mid]>a[low]) //左有序
{
if(a[low]<=value && value<=a[mid] ) //说明value在左边,直接二分查找
{
return binarySearch(a,low,mid,value);
}
else //value在右边
{
return Search(a,mid+1,high,value);
}
}
else //右有序
{
if(a[mid]<=value && value<=a[high])
{
return binarySearch(a,mid,high,value);
}
else
{
return Search(a,low,mid-1,value);
}
}
}
int main()
{
int a[]={3,4,5,6,7,8,9,0,1,2};
cout<<Search(a,0,9,0)<<endl;
return 0;
}
二叉树递归遍历可谓是学过数据结构的同仁都能想一下就能写出来,但在应聘过程我们常常遇到的是写出一个二叉树非递归遍历函数,接着上篇文章写二叉树的非递归遍历,先难后易,一步一步的来.
先上代码:
[cpp] view
plain copy
#include "binarytree.h"
#include <stack>
#include <queue>
#ifndef RECU
#warning("RECU is not defined")
/**
*前序遍历(根左右)
*
*1、当前节点为非空,访问当前节点,压栈其右子节点,考虑其左子节点
*2、当前节点为NULL,出栈
*
*@param t to visit
*@param visit point to a func
*/
void pre_order(link t, void (*visit)(link))
{
std::stack<link> myStack;
while( t || !myStack.empty() ) {
if ( t ) {
visit(t);
myStack.push(t->rchild);
t = t->lchild;
} else {
t = myStack.top();
myStack.pop();
}
}
}
/**
*中序序遍历(左根右)
*
*1、当前节点为非空,在访问当前节点前要先访问其左子节点,
* 压栈当前节点,判断其左子结点,一直压栈左子节点
*2、当前节点为NULL,出栈访问,其左子结点比当前节点出栈访问早,
* 此时当前节点是其右节点的父节点的角色,考虑其右节点
*
*在遍历过程中角色转换很重要
*
*@param t to visit
*@param visit point to a func
*/
void in_order(link t, void (*visit)(link))
{
std::stack<link> myStack;
while( t || !myStack.empty() ) {
if ( t ) {
myStack.push(t);
t = t->lchild;
} else {
t = myStack.top();
myStack.pop();
visit(t);
t = t->rchild;
}
}
}
/**
*后序遍历(左右根)
*
*1、由于在访问当前树的根结点时,应先访问其左、右子树,因而先将根结点入栈,
* 接着将右子树也入栈,然后考虑左子树,重复这一过程直到某一左子树为空
*2、如果当前考虑的子树为空,
* 1.若栈顶不为空,说明第二栈顶对应的树的右子树未处理,
* 则弹出栈顶,下次循环处理,并将一空指针入栈以表示其另一子树已做处理;
* 2.若栈顶也为空树,说明第二栈顶对应的树的左右子树或者为空,或者均已做处理,
* 直接访问第二栈顶的结点,访问完结点后,若栈仍为非空,说明整棵树尚未遍历完,
* 则弹出栈顶,并入栈一空指针表示第二栈顶的子树之一已被处理。
*
*@param t to visit
*@param visit point to a func
*/
void post_order(link t, void (*visit)(link))
{
std::stack<link> myStack;
while( 1 ) {
if ( t ) {
myStack.push(t);
myStack.push(t->rchild);
t = t->lchild;
} else {
t = myStack.top();
myStack.pop();
if (!t) {
t = myStack.top();
myStack.pop();
visit(t);
if (myStack.empty())
break;
t = myStack.top();
myStack.pop();
}
myStack.push(NULL);
}
}
}
#endif
/**
*层遍历
*
*@param t to visit
*@param visit point to a func
*/
void level_order(link t, void (*visit)(link))
{
std::queue<link> myQueue;
if (t) {
myQueue.push(t);
while( !myQueue.empty() ) {
link tmp = myQueue.front();
myQueue.pop();
visit(tmp);
if (tmp->lchild != NULL)
myQueue.push(tmp->lchild);
if (tmp->rchild != NULL)
myQueue.push(tmp->rchild);
}
}
}
需要在矩阵中查找元素。这个矩阵的排列如下:
每一行都是有序的。每一列都是有序的。
比如一个小矩阵。
10 30
20 80
现在,我们需要在一个这样N * M的矩阵中找到一个元素,并返回其位置。
思路
首先,这个题不太适合二分查找。因为并不能保证第二行的元素都一定比第一行的任意元素大。
所以应该是很难找到O(lgN)的算法。
每次都是取右上角的元素v与x(我们要查找的元素)进行比较较:
如果发现v > x,由于v所在列肯定比x大。所以v所在列可以舍弃。
如果发现v < x,由于v所在行肯定比x小。所以v所在行可能舍弃。
最后余下的,可能是一行,也可能是一列。总之可以利用二分查找来实现了。效率O(min(N, M)) + log(abs(M-N)).
解题
这里直接粘出代码。
[cpp] view
plain copy
int find(int **a, const int row, const int col, const int x, int *rpos, int *cpos) {
//右上角为起始点.
int from_row = 0, from_col = col - 1, v;
int b, e, mid;
*rpos = *cpos = -1;
while (from_row < row && from_col >= 0) {
v = a[from_row][from_col];
if (v == x) {
*rpos = from_row;
*cpos = from_col;
return 1;
}
from_row += x > v;
from_col -= v > x;
}
//最后剩下一行
if (from_row == (row - 1) && from_col != 0) {
b = 0, e = from_col + 1;
while (b < e) {
mid = b + ((e-b)>>1);
v = a[from_row][mid];
if (x == v) {
*rpos = from_row;
*cpos = mid;
return 1;
} else if (v > x) e = mid;
else b = mid + 1;
}
return 0;
}
//最后剩下一列
if (from_col == 0 && from_row != (row - 1)) {
b = from_row, e = row;
while (b < e) {
mid = b + ((e-b)>>1);
v = a[mid][0];
if (v == x) {
*rpos = mid;
*cpos = from_col;
return 1;
} else if (v > x) e = mid;
else b = mid + 1;
}
return 0;
}
//最后只剩下一个点
if (a[from_row][from_col] == x) {
*rpos = from_row;
*cpos = from_col;
return 1;
}
return 0;
}
这里写一个测试程序,如果有错,会输出Error。
大数整数乘法方法
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char * bigmuilty(char*muiltied,int len1,char *muilty,int len2)//muiltied 为被乘数,len1为其长。muilty为乘数,len2为其长
{
int len;
int i=0;
int j=0;
int k=0;
int tmp_result=0;
int carry=0;
char* result;
len=len1+len2; //两个数相乘他们的最大长度为乘数与被乘数的长度和
result=(char*)calloc(len,1);//动态分配内存
for(i=0;i<len2;i++)//用乘数的每一位去乘以被乘数
{
for(j=0;j<len1;j++)
{
tmp_result=muilty[len2-i-1]*muiltied[len1-j-1];//得到零时结果
result[j+i]+=tmp_result;//如果以0为起始标号,被乘数和乘数下标分别从右往左依次增加,则结果存放的位置为j+i
}
for(k=0;k<=j+i-1;k++)//整理结果,使每位的值在0-9之间
{
if(result[k]>9)
{
carry=result[k]/10;
result[k]=result[k]%10;
result[k+1]+=carry;
}
}
}
return result;
}
int main()
{
int len1=0;
int len2=0;
int len;
int i=0;
int num='0';
char *result;
char muiltied[BUFSIZ];//被乘数存放位置
char muilty[BUFSIZ];
printf("entry muiltied : ");
scanf("%s",muiltied);
printf("\nentry muilty : ");
scanf("%s",muilty);
len1=strlen(muiltied);
len2=strlen(muilty);
len=len1+len2;
for(i=0;i<len1;i++)
{
muiltied[i]=muiltied[i]-num;
}
for(i=0;i<len2;i++)
{
muilty[i]=muilty[i]-num;
}
result=bigmuilty(muiltied,len1,muilty,len2);
i=len-2;
printf("\nresult : ");
if(result[len-1]>0)
{
printf("%c",result[len-1]+num);
}
while(i>=0)
{
printf("%c",result[i]+num);
i--;
}
printf("\n");
free(result);
return 0;
}
1.Copyright 1990 Software Development Systems, Inc.
2.Copyright 1986 - 1999 IAR Systems. All rights reserved
设指针pa与pb分别均指向LA与LB当前的结点,那么只需要每次比较当前的这两个节点,谁小就把谁加入到LC(新链表)中,然后小的这个链表的指针继续后移。这样最坏的复杂度也只是O(LENA+LENB),算法2的实现如下:
[cpp] view
plain copy
print?
//结点结构体定义
struct LNode{
int data;
LNode *next;
};
typedef LNode * LinkList;
void main()
{//函数声明
void creat_list(LinkList &L,int n);
void merge_list(LinkList &La,LinkList &Lb,LinkList &Lc);
void print_list(LinkList L);
//主函数开始
int n1,n2;
LinkList La,Lb,Lc;
printf("建立La链表开始:请先输入要建立的结点个数\n");
scanf("%d",&n1);
creat_list(La,n1);
printf("La链表信息如下:\n");
print_list(La);
printf("建立Lb链表开始:请先输入要建立的结点个数\n");
scanf("%d",&n2);
creat_list(Lb,n2);
printf("Lb链表信息如下:\n");
print_list(Lb);
printf("合并完两链表后如下:\n");
merge_list(La,Lb,Lc);
print_list(Lc);
}
void creat_list(LinkList &L,int n)
{ int i;
LinkList p,q;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;//建立头结点
q=L;
for (i=1;i<=n;i++)
{p=(LinkList)malloc(sizeof(LNode));
printf("请输入第%d个节点的数据:",i);
scanf("%d",&p->data);
q->next=p;
q=q->next;//q后移
}
p->next=NULL;//最后一个节点是尾结点
}
void merge_list(LinkList &La,LinkList &Lb,LinkList &Lc)
{LinkList pa,pb,pc;
pa=La->next;pb=Lb->next;//a,b链表均指向自己的第一个结点
pc=Lc=La;//Lc链表自始至终就没有建立过,实际上LC就是借助指针去连接la和lb中的结点
while(pa&&pb)//不指向末尾
{
if (pa->data<=pb->data)//插入La
{pc->next=pa;
pc=pa;//pc后移
pa=pa->next;//La后移
}
else
{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}//结束该循环后应该会剩下一个链表没合并完(假设俩个链表长度不一样)
pc->next=pa?pa:pb;//剩余
}
void print_list(LinkList L)
{
LinkList p=L->next;//指向第一个结点
while(p)
{
printf("%3d",p->data);
p=p->next;
}
printf("\n");
}
题目描述:
一个循环有序数组(如:3,4,5,6,7,8,9,0,1,2),不知道其最小值的位置,要查找任一数值的位置。要求算法时间复杂度为log2(n)。
问题分析:
我们可以把循环有序数组分为左右两部分(以mid
= (low+high)/ 2为界),由循环有序数组的特点知,左右两部分必有一部分是有序的,我们可以找出有序的这部分,然后看所查找元素是否在有序部分,若在,则直接对有序部分二分查找,若不在,对无序部分递归调用查找函数。
代码如下:
#include <iostream>
using namespace std;
int binarySearch(int a[],int low,int high,int value) //二分查找
{
if(low>high)
return -1;
int mid=(low+high)/2;
if(value==a[mid])
return mid;
else if(value>a[mid])
return binarySearch(a,mid+1,high,value);
else
return binarySearch(a,low,mid-1,value);
}
int Search(int a[],int low,int high,int value) //循环有序查找函数
{
int mid=(low+high)/2;
if(a[mid]>a[low]) //左有序
{
if(a[low]<=value && value<=a[mid] ) //说明value在左边,直接二分查找
{
return binarySearch(a,low,mid,value);
}
else //value在右边
{
return Search(a,mid+1,high,value);
}
}
else //右有序
{
if(a[mid]<=value && value<=a[high])
{
return binarySearch(a,mid,high,value);
}
else
{
return Search(a,low,mid-1,value);
}
}
}
int main()
{
int a[]={3,4,5,6,7,8,9,0,1,2};
cout<<Search(a,0,9,0)<<endl;
return 0;
}
二叉树学习之非递归遍历
二叉树递归遍历可谓是学过数据结构的同仁都能想一下就能写出来,但在应聘过程我们常常遇到的是写出一个二叉树非递归遍历函数,接着上篇文章写二叉树的非递归遍历,先难后易,一步一步的来.先上代码:
[cpp] view
plain copy
#include "binarytree.h"
#include <stack>
#include <queue>
#ifndef RECU
#warning("RECU is not defined")
/**
*前序遍历(根左右)
*
*1、当前节点为非空,访问当前节点,压栈其右子节点,考虑其左子节点
*2、当前节点为NULL,出栈
*
*@param t to visit
*@param visit point to a func
*/
void pre_order(link t, void (*visit)(link))
{
std::stack<link> myStack;
while( t || !myStack.empty() ) {
if ( t ) {
visit(t);
myStack.push(t->rchild);
t = t->lchild;
} else {
t = myStack.top();
myStack.pop();
}
}
}
/**
*中序序遍历(左根右)
*
*1、当前节点为非空,在访问当前节点前要先访问其左子节点,
* 压栈当前节点,判断其左子结点,一直压栈左子节点
*2、当前节点为NULL,出栈访问,其左子结点比当前节点出栈访问早,
* 此时当前节点是其右节点的父节点的角色,考虑其右节点
*
*在遍历过程中角色转换很重要
*
*@param t to visit
*@param visit point to a func
*/
void in_order(link t, void (*visit)(link))
{
std::stack<link> myStack;
while( t || !myStack.empty() ) {
if ( t ) {
myStack.push(t);
t = t->lchild;
} else {
t = myStack.top();
myStack.pop();
visit(t);
t = t->rchild;
}
}
}
/**
*后序遍历(左右根)
*
*1、由于在访问当前树的根结点时,应先访问其左、右子树,因而先将根结点入栈,
* 接着将右子树也入栈,然后考虑左子树,重复这一过程直到某一左子树为空
*2、如果当前考虑的子树为空,
* 1.若栈顶不为空,说明第二栈顶对应的树的右子树未处理,
* 则弹出栈顶,下次循环处理,并将一空指针入栈以表示其另一子树已做处理;
* 2.若栈顶也为空树,说明第二栈顶对应的树的左右子树或者为空,或者均已做处理,
* 直接访问第二栈顶的结点,访问完结点后,若栈仍为非空,说明整棵树尚未遍历完,
* 则弹出栈顶,并入栈一空指针表示第二栈顶的子树之一已被处理。
*
*@param t to visit
*@param visit point to a func
*/
void post_order(link t, void (*visit)(link))
{
std::stack<link> myStack;
while( 1 ) {
if ( t ) {
myStack.push(t);
myStack.push(t->rchild);
t = t->lchild;
} else {
t = myStack.top();
myStack.pop();
if (!t) {
t = myStack.top();
myStack.pop();
visit(t);
if (myStack.empty())
break;
t = myStack.top();
myStack.pop();
}
myStack.push(NULL);
}
}
}
#endif
/**
*层遍历
*
*@param t to visit
*@param visit point to a func
*/
void level_order(link t, void (*visit)(link))
{
std::queue<link> myQueue;
if (t) {
myQueue.push(t);
while( !myQueue.empty() ) {
link tmp = myQueue.front();
myQueue.pop();
visit(tmp);
if (tmp->lchild != NULL)
myQueue.push(tmp->lchild);
if (tmp->rchild != NULL)
myQueue.push(tmp->rchild);
}
}
}
面试题整理-矩阵查找元素
需要在矩阵中查找元素。这个矩阵的排列如下:每一行都是有序的。每一列都是有序的。
比如一个小矩阵。
10 30
20 80
现在,我们需要在一个这样N * M的矩阵中找到一个元素,并返回其位置。
思路
首先,这个题不太适合二分查找。因为并不能保证第二行的元素都一定比第一行的任意元素大。
所以应该是很难找到O(lgN)的算法。
每次都是取右上角的元素v与x(我们要查找的元素)进行比较较:
如果发现v > x,由于v所在列肯定比x大。所以v所在列可以舍弃。
如果发现v < x,由于v所在行肯定比x小。所以v所在行可能舍弃。
最后余下的,可能是一行,也可能是一列。总之可以利用二分查找来实现了。效率O(min(N, M)) + log(abs(M-N)).
解题
这里直接粘出代码。
[cpp] view
plain copy
int find(int **a, const int row, const int col, const int x, int *rpos, int *cpos) {
//右上角为起始点.
int from_row = 0, from_col = col - 1, v;
int b, e, mid;
*rpos = *cpos = -1;
while (from_row < row && from_col >= 0) {
v = a[from_row][from_col];
if (v == x) {
*rpos = from_row;
*cpos = from_col;
return 1;
}
from_row += x > v;
from_col -= v > x;
}
//最后剩下一行
if (from_row == (row - 1) && from_col != 0) {
b = 0, e = from_col + 1;
while (b < e) {
mid = b + ((e-b)>>1);
v = a[from_row][mid];
if (x == v) {
*rpos = from_row;
*cpos = mid;
return 1;
} else if (v > x) e = mid;
else b = mid + 1;
}
return 0;
}
//最后剩下一列
if (from_col == 0 && from_row != (row - 1)) {
b = from_row, e = row;
while (b < e) {
mid = b + ((e-b)>>1);
v = a[mid][0];
if (v == x) {
*rpos = mid;
*cpos = from_col;
return 1;
} else if (v > x) e = mid;
else b = mid + 1;
}
return 0;
}
//最后只剩下一个点
if (a[from_row][from_col] == x) {
*rpos = from_row;
*cpos = from_col;
return 1;
}
return 0;
}
这里写一个测试程序,如果有错,会输出Error。
大数整数乘法方法
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char * bigmuilty(char*muiltied,int len1,char *muilty,int len2)//muiltied 为被乘数,len1为其长。muilty为乘数,len2为其长
{
int len;
int i=0;
int j=0;
int k=0;
int tmp_result=0;
int carry=0;
char* result;
len=len1+len2; //两个数相乘他们的最大长度为乘数与被乘数的长度和
result=(char*)calloc(len,1);//动态分配内存
for(i=0;i<len2;i++)//用乘数的每一位去乘以被乘数
{
for(j=0;j<len1;j++)
{
tmp_result=muilty[len2-i-1]*muiltied[len1-j-1];//得到零时结果
result[j+i]+=tmp_result;//如果以0为起始标号,被乘数和乘数下标分别从右往左依次增加,则结果存放的位置为j+i
}
for(k=0;k<=j+i-1;k++)//整理结果,使每位的值在0-9之间
{
if(result[k]>9)
{
carry=result[k]/10;
result[k]=result[k]%10;
result[k+1]+=carry;
}
}
}
return result;
}
int main()
{
int len1=0;
int len2=0;
int len;
int i=0;
int num='0';
char *result;
char muiltied[BUFSIZ];//被乘数存放位置
char muilty[BUFSIZ];
printf("entry muiltied : ");
scanf("%s",muiltied);
printf("\nentry muilty : ");
scanf("%s",muilty);
len1=strlen(muiltied);
len2=strlen(muilty);
len=len1+len2;
for(i=0;i<len1;i++)
{
muiltied[i]=muiltied[i]-num;
}
for(i=0;i<len2;i++)
{
muilty[i]=muilty[i]-num;
}
result=bigmuilty(muiltied,len1,muilty,len2);
i=len-2;
printf("\nresult : ");
if(result[len-1]>0)
{
printf("%c",result[len-1]+num);
}
while(i>=0)
{
printf("%c",result[i]+num);
i--;
}
printf("\n");
free(result);
return 0;
}
1.Copyright 1990 Software Development Systems, Inc.
相关文章推荐
- 一个关于if else容易迷惑的问题
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 一道sql面试题附答案
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C# 超高面试题收集整理
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- 人人网javascript面试题 可以提前实现下
- 用C语言举例讲解数据结构中的算法复杂度结与顺序表
- C#数据结构之堆栈(Stack)实例详解
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- PHP中设置一个严格30分钟过期Session面试题的4种答案
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- 据说是雅虎的一份PHP面试题附答案
- php牛逼的面试题分享
- 一套比较完整的javascript面试题(部分答案)