C/C++笔试忍法帖05——数据结构篇

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方法,不动态分配内存,一切从简。未经测试,欢迎指正,多多交流,共同进步。
*/

C/C++ code
?
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);
*/

C/C++ code
?
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+*/=








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值