1.写出下列算法的时间复杂度。
(1)冒泡排序; O(n^2)
(2)插入排序;直接插入排序是 O(n^2) 最好的情况是0(n)
(3)选择排序; 直接选择排序是O(n^2)
(4)快速排序; O(nlog2 n)
(5)堆排序; O(nlog2 n)
(6)归并排序; O(nlog2 n)
2.编程,请实现一个c语言中类似atoi的函数功能(输入可能包含非数字和空格)
#include <stdio.h> int isspace(int x) { if ((x == 0)||(x == '\t') || (x == '\n') || (x == '\f') || (x == '\b') || (x == '\r')) return 1; else return 0; } int isdigit(int x) { if (x <= '9' && x >= '0') return 1; else return 0; } int atoi(const char *nptr) { int c; /* current char */ int total; /* current total */ int sign; /* if '-', then negative, otherwise positive */ /* skip whitespace */ while (isspace((int) (unsigned char) *nptr)) ++nptr; c = (int) (unsigned char) *nptr++; sign = c; /* save sign indication */ if (c == '-' || c == '+') c = (int) (unsigned char) *nptr++; /* skip sign */ total = 0; while (isdigit(c)) { total = 10 * total + (c - '0'); /* accumulate digit */ c = (int) (unsigned char) *nptr++; /* get next char */ } if (sign == '-') return -total; else return total; /* return result, negated if necessary */ } int main() { char aaa[10] = "20"; printf("======%d=====\n" , atoi(aaa)); return 0; }
3.在排序方法中,关键码比较次数与记录地初始排列无关的是 D
A. Shell排序 B. 归并排序 C. 直接插入排序 D. 选择排序
5. 编写strcat函数(6分)
已知strcat函数的原型是char *strcat (char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc 是源字符串。
(1)不调用C++/C 的字符串库函数,请编写函数 strcat
答:
VC源码:
char * __cdecl strcat (char * dst, const char * src)
{
char * cp = dst;
while( *cp )
cp++; /* find end of dst */
while( *cp++ = *src++ ) ; /* Copy src to end of dst */
return( dst ); /* return dst */
}
(2)strcat能把strSrc 的内容连接到strDest,为什么还要char * 类型的返回值?
答:方便赋值给其他变量
6.用两个栈实现一个队列的功能?要求给出算法和思路!
答 :设2个栈为A,B, 一开始均为空.
入队:
将新元素push入栈A;
出队:
(1)判断栈B是否为空;
(2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;
(3)将栈B的栈顶元素pop出;
7.链表反转
单向链表的反转是一个经常被问到的一个面试题,也是一个非常基础的问题。比如一个链表是这样的:
1->2->3->4->5
通过反转后成为5->4->3->2->1。
最容易想到的方法遍历一遍链表,利用一个辅助指针,存储遍历过程中当前指针指向的下一个元素,然
后将当前节点元素的指针反转后,利用已经存储的指针往后面继续遍历。源代码如下:
struct linka {
int data;
linka* next;
};
void reverse(linka*& head) {
if(head ==NULL)
return;
linka *pre, *cur, *ne;
pre=head;
cur=head->next;
while(cur)
{
ne = cur->next;
cur->next = pre;
pre = cur;
cur = ne;
}
head->next = NULL;
head = pre;
}
还有一种利用递归的方法。这种方法的基本思想是在反转当前节点之前先调用递归函数反转后续节点。
源代码如下。不过这个方法有一个缺点,就是在反转后的最后一个结点会形成一个环,所以必须将函数的
返回的节点的next域置为NULL。因为要改变head指针,所以我用了引用。算法的源代码如下:
linka* reverse(linka* p,linka*& head)
{
if(p == NULL || p->next == NULL)
{
head=p;
return p;
}
else
{
linka* tmp = reverse(p->next,head);
tmp->next = p;
return p;
}
}
8.编写strcpy函数(10分)
已知strcpy函数的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest是目的字符串,strSrc是源字符串。
(1)不调用C++/C的字符串库函数,请编写函数 strcpy
char *strcpy(char *strDest, const char *strSrc);
{
assert((strDest!=NULL) && (strSrc !=NULL)); // 2分
char *address = strDest; // 2分
while( (*strDest++ = * strSrc++) != ‘ 0’ ) // 2分
NULL ;
return address ; // 2分
}
(2)strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值?
答:为了实现链式表达式。 // 2分
例如: int length = strlen( strcpy( strDest, “hello world”) );
9.设计一个栈结构,满足一下条件:min,push,pop操作的时间复杂度为O(1)。
思路:为了使min操作的复杂度将为O(1),就不能遍历整个栈,因此需要时刻保存min值的索引,用以min方法的请求,再者,若在连续使用pop()方法后,仍能使min方法返回正确的最小值,则仅保存一个min值索引是不够的,使用辅助栈是我目前能想到的唯一可行办法,代码只演示min方法,不动态分配内存,一切从简。未经测试,欢迎指正,多多交流,共同进步。
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
class
Overflow {};
template
<
typename
T,
int
size = 100>
class
MinStack
{
public
:
MinStack():top_arr(0),top_min(0){}
bool
empty()
const
{ top_arr == 0; }
bool
full()
const
{ top_arr == size; }
const
T& top()
const
;
const
T& min()
const
;
void
push(
const
T& val);
T pop();
private
:
T arr[size];
int
min_idx[size];
int
top_arr, top_min;
};
template
<
typename
T,
int
size>
const
T& MinStack<T,size>::top()
const
{
if
(empty())
{
throw
Overflow();
}
return
arr[top_arr-1];
}
template
<
typename
T,
int
size>
const
T& MinStack<T,size>::min()
const
{
if
(empty())
{
throw
Overflow();
}
return
arr[min_idx[top_min-1]];
}
template
<
typename
T,
int
size>
void
MinStack<T,size>::push(
const
T& val)
{
if
(full())
{
throw
Overflow();
}
if
(empty() || val < min())
{
mix_idx[top_min++] = top_arr;
}
arr[top_arr++] = val;
}
template
<
typename
T,
int
size>
T MinStack<T,size>::pop()
{
if
(empty())
{
throw
Overflow();
}
T retval = arr[--top_arr];
if
(top_arr == min_idx[top_min-1])
{
top_min--;
}
return
retval;
}
|
10./*
2010年 10月18日下午 July
--------------------------------
1.把二元查找树转变成排序的双向链表
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/ \
6 14
/ \ / \
4 8 12 16
转换成双向链表
4=6=8=10=12=14=16。
首先我们定义的二元查找树 节点的数据结构如下:
struct BSTreeNode
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
*/
//引用 245 楼 tree_star 的回复
#include <stdio.h>
#include <iostream.h>
struct
BSTreeNode
{
int
m_nValue;
// value of node
BSTreeNode *m_pLeft;
// left child of node
BSTreeNode *m_pRight;
// right child of node
};
typedef
BSTreeNode DoubleList;
DoubleList * pHead;
DoubleList * pListIndex;
void
convertToDoubleList(BSTreeNode * pCurrent);
// 创建二元查找树
void
addBSTreeNode(BSTreeNode * & pCurrent,
int
value)
{
if
(NULL == pCurrent)
{
BSTreeNode * pBSTree =
new
BSTreeNode();
pBSTree->m_pLeft = NULL;
pBSTree->m_pRight = NULL;
pBSTree->m_nValue = value;
pCurrent = pBSTree;
}
else
{
if
((pCurrent->m_nValue) > value)
{
addBSTreeNode(pCurrent->m_pLeft, value);
}
else
if
((pCurrent->m_nValue) < value)
{
addBSTreeNode(pCurrent->m_pRight, value);
}
else
{
//cout<<"重复加入节点"<<endl;
}
}
}
// 遍历二元查找树 中序
void
ergodicBSTree(BSTreeNode * pCurrent)
{
if
(NULL == pCurrent)
{
return
;
}
if
(NULL != pCurrent->m_pLeft)
{
ergodicBSTree(pCurrent->m_pLeft);
}
// 节点接到链表尾部
convertToDoubleList(pCurrent);
// 右子树为空
if
(NULL != pCurrent->m_pRight)
{
ergodicBSTree(pCurrent->m_pRight);
}
}
// 二叉树转换成list
void
convertToDoubleList(BSTreeNode * pCurrent)
{
pCurrent->m_pLeft = pListIndex;
if
(NULL != pListIndex)
{
pListIndex->m_pRight = pCurrent;
}
else
{
pHead = pCurrent;
}
pListIndex = pCurrent;
cout<<pCurrent->m_nValue<<endl;
}
int
main()
{
BSTreeNode * pRoot = NULL;
pListIndex = NULL;
pHead = NULL;
addBSTreeNode(pRoot, 10);
addBSTreeNode(pRoot, 4);
addBSTreeNode(pRoot, 6);
addBSTreeNode(pRoot, 8);
addBSTreeNode(pRoot, 12);
addBSTreeNode(pRoot, 14);
addBSTreeNode(pRoot, 15);
addBSTreeNode(pRoot, 16);
ergodicBSTree(pRoot);
return
0;
}
///
4
6
8
10
12
14
15
16
Press any key to
continue
//
11.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
/*
第24题:
链表操作,
(1)单链表就地逆置,
*/
//定义链表结构体
struct
ListNode
{
int
m_nKey;
ListNode* m_pNext;
};
/*------------------------------------------
1 -> 2 -> 3 -> 4 -> 5
m -> n
1 <- 2 <- 3 <- 4 <- 5
m <- n
|
->m_PNext
pNode \
\ //翻转
/
pPrev<---------/
if(pNext==NULL)
ppReversedHead = pNode;
反转链表: pNode->m_pNext=pPrev;
pPrev<-pNode<-pNext
2. pNode=pNext;
1. pPrev=pNode;
------------------------------------------*/
ListNode* ReverseIteratively(ListNode* pHead)
{
ListNode* pReversedHead = NULL;
ListNode* pNode = pHead;
ListNode* pPrev = NULL;
while
(pNode != NULL)
//pNode<=>m
{
ListNode* pNext = pNode->m_pNext;
//n保存在pNext下
//如果pNext指为空,则当前结点pNode设为头。
if
(pNext == NULL)
pReversedHead = pNode;
// reverse the linkage between nodes
pNode->m_pNext = pPrev;
// move forward on the the list
pPrev = pNode;
pNode = pNext;
}
return
pReversedHead;
}
|
12.跳台阶问题
题目:一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。
求总共有多少总跳法,并分析算法的时间复杂度。
这道题最近经常出现,包括MicroStrategy等比较重视算法的公司都
曾先后选用过个这道题作为面试题或者笔试题。
这个问题,想了好久都没头绪,后来问了个朋友,他只告诉我用递归,递归意味着什么?数列中的递推公式,数学归纳法,等等,这些概念几乎如出一辙。我顿时恍然大悟,其实这个题目编程本身并不难,难的只是我们要想到借用数学的知识来建立这个递推关系。
对于N级台阶,如果第一跳只跳一级,那么还剩下N-1级,如果第一跳只跳两级,那么还剩下N-2级,由此不难得出对于N级的台阶,总的跳法树相当于N-1级与N-2级的跳法数量之和。
sum(N)=1 (N=1);
sum(N)=2 (N=2);
sum(N)=sum(N-1)+sum(N-2) (N>2);
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
int
jump_sum(
int
n)
//1.递归版本
{
assert
(n>0);
if
(n == 1 || n == 2)
return
n;
return
jump_sum(n-1)+jump_sum(n-2);
}
int
jump_sum(
int
n)
//2.迭代版本
{
assert
(n>0);
if
(n == 1 || n == 2)
return
n;
int
an, an_1=2, an_2=1;
for
(; n>=3; n--)
{
an = an_2 + an_1;
an_2 = an_1;
an_1 = an;
}
return
an;
}
|
13.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/*第24题:
链表操作,
(1).单链表就地逆置,
(2)有序合并链表
*/
Node * MergeRecursive(Node *head1 , Node *head2)
{
if
( head1 == NULL )
return
head2 ;
if
( head2 == NULL)
return
head1 ;
Node *head = NULL ;
if
( head1->data < head2->data )
{
head = head1 ;
head->next = MergeRecursive(head1->next,head2);
}
else
{
head = head2 ;
head->next = MergeRecursive(head1,head2->next);
}
return
head ;
}
|
14、已知一个线性表(38,25,74,63,52,48),假定采用散列函数h(key) = key%7计算散列地址,并散列存储在散列表A【0....6】中,若采用线性探测方法解决冲突,则在该散列表上进行等概率成功查找的平均查找长度为(C)
A、1.5 B、1.7 C、2.0 D、2.3
依次进行取模运算求出哈希地址:
A | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
记录 | 63 | 48 |
| 38 | 25 | 74 | 52 |
查找次数 | 1 | 3 |
| 1 | 1 | 2 | 4 |
15.应该放在下标为4的位置,由于25已经放在这个地方,所以74往后移动,放在了下标为5的位置上了。
由于是等概率查找,所以结果为:1/6*(1+3+1+1+2+4)= 2.0
16、表达式“X=A+B*(C--D)/E”的后缀表示形式可以为(C)
A、XAB+CDE/-*= B、XA+BC-DE/*= C、XABCD-*E/+= D、XABCDE+*/=