《数据结构面试题》

C++链表实现

  1. 网络工程师CCNA+CCNP
  2. 系统管理员网络管理员MCSE2003
  3. 软件开发工程师认证MCPD

一般线性链表类的C++实现

出处:www.examlink.com 作者:小罗 日期:2007年01月24日 14时48分

以下的C++类LinkList实现了线性链表的一般操作。可以直接在其他的程序中直接建立它的对象,其中线性表中的数据在此为整型,具体应用的时候可以适当的修改,并可以在此基础上继续封装特定的功能。

头文件:LinkList.h

typedef struct LNode {
int data;
struct LNode *next;
}LNode, *pLinkList;

class LinkList {
private:
pLinkList m_pList;
int m_listLength;
public:
LinkList();
~LinkList();
bool InitList ();
bool DestroyList ();
bool ClearList();
bool IsEmpty ();
int GetLength ();
bool GetNode(int position, LNode** node);
int LocateElem(int elem);
bool SetNodeData(int position, int newData);
bool GetNodeData(int position, int &data);
bool InsertNode(int beforeWhich, int data);
bool DeleteNode(int position);
};

Cpp文件:LinkList.cpp

#include <iostream.h>
#include 'LinkList.h'

LinkList::LinkList() {
m_pList = NULL;
m_listLength = 0;

InitList();
}

LinkList::~LinkList() {
if (!DestroyList()) {
  DestroyList();
}
}

//初始化,分配一个头节点。
bool LinkList::InitList() {
if (!(m_pList = new LNode)) {
  return false;
}
m_pList->next = NULL;

return true;
}

//销毁链表。
bool LinkList::DestroyList() {
if (!ClearList()) {
  return false;
}

delete m_pList;

return true;
}

//判断链表是否为空。若为空,返回true,否则返回false。
bool LinkList::IsEmpty() {
if (m_pList->next == NULL) {
  return true;
}
return false;
}

//返回链表的中当前节点数。
int LinkList::GetLength() {
return m_listLength;
}

//将链表清空,释放当前所有节点。
bool LinkList::ClearList() {
if (m_pList == NULL) {
  return false;
}

LNode *pTemp = NULL;
while (m_pList->next != NULL) {
  pTemp = m_pList->next;
  m_pList->next = pTemp->next;
  delete pTemp;
}
m_listLength = 0;

return true;
}

//将position指定的节点内的数据设置为newData。
//第一个有效节点的position为1。
bool LinkList::SetNodeData(int position, int newData) {
LNode *pTemp = NULL;

if (!(GetNode(position, &pTemp))) {
  return false;
}

pTemp->data = newData;

return true;
}

//得到指定位置节点的数据。
//节点索引从1到listLength。
bool LinkList::GetNodeData(int position, int &data) {
LNode *pTemp = NULL;

if (!(GetNode(position, &pTemp))) {
  return false;
}

data = pTemp->data;

return true;
}

//在链表中插入一个节点。
//插入的位置由beforeWhich指定,新节点插入在beforeWhich之前。
//beforeWhich的取值在1到ListLength+1之间。
bool LinkList::InsertNode(int beforeWhich, int data) {
LNode *pTemp = NULL;

if (beforeWhich < 1 || beforeWhich > (m_listLength + 1)) {
  return false;
}

if (!(GetNode(beforeWhich - 1, &pTemp))) {
  return false;
}

LNode *newNode = new LNode;
newNode->data = data;
newNode->next = pTemp->next;
pTemp->next = newNode;

m_listLength++;

return true;
}

//删除一个指定的节点。
//节点位置由position指定。
//positon的值从1到listLength。
//若链表为空或指定的节点不存在则返回false。
bool LinkList::DeleteNode(int position) {
if (position < 1 || position > m_listLength) {
  return false;
}

LNode *pTemp = NULL;
if (!(GetNode(position - 1, &pTemp))) {
  return false;
}

LNode *pDel = NULL;
pDel = pTemp->next;
pTemp->next = pDel->next;
delete pDel;

m_listLength--;

return true;
}

//得到指定位置节点的指针。
bool LinkList::GetNode(int position, LNode **node) {
LNode *pTemp = NULL;
int curPos = -1;

pTemp = m_pList;
while (pTemp != NULL) {
  curPos++;
  if (curPos == position) 
   break;
  pTemp = pTemp->next;
}

if (curPos != position) {
  return false;
}

*node = pTemp;

return true;
}

//定位与指定数据相等的数据节点。
//如果在当前链表中已经存在该数据则返回该数据节点的索引号。
//若不存在这样的节点则返回0。
//节点索引从0开始到listLength。
int LinkList::LocateElem(int elem) {
LNode *pTemp = NULL;
int curIndex = 1;

pTemp = m_pList->next;
while ((pTemp != NULL) && (pTemp->data != elem)) {
  pTemp = pTemp->next;
  curIndex++;
}

if (pTemp == NULL) {
  return 0;
}

return curIndex;
}


/*
int main(){
LinkList l;

l.InsertNode(1, 10);
l.InsertNode(2, 20);
l.InsertNode(3, 30);
l.InsertNode(4, 40);
cout << l.GetLength() << endl;

int dataTemp = 0;
for (int i = 1; i <= l.GetLength(); i++) {
  l.GetNodeData(i, dataTemp);
  cout << dataTemp << endl;
}

if (l.SetNodeData(3, 50)) {
  cout <<'DONE\n';
} else {
  cout << 'Failed\n';
}

for (i = 1; i <= l.GetLength(); i++) {
  l.GetNodeData(i, dataTemp);
  cout << dataTemp << endl;
}

if (l.DeleteNode(4)) {
  cout <<'DONE\n';
} else {
  cout << 'Failed\n';
}

for (i = 1; i <= l.GetLength(); i++) {
  l.GetNodeData(i, dataTemp);
  cout << dataTemp << endl;
}

cout << l.LocateElem(50) << endl;
return 0;
}
*/

不使用任何中间变量实现strlen

http://www.51testing.com/?uid-225738-action-viewspace-itemid-221151

《C语言实现strlen函数的几种方法》

 

http://blog.csdn.net/sailor_8318/article/details/3071048

递归的思想:

“不使用中间变量”只是说程序员不能显式的申请内存而已,即不能有局部变量或者动态内存申请。

如果函数自动申请栈内存或者使用寄存器存储变量,或者使用立即数寻址即常量,那么就相当于“不使用中间变量”。

从函数原型看,返回值为int,那么在函数内部必定需要一个地方存储这个值,要么是常数要么是寄存器

长度不为1时不能一次就求出来,说明必须有递归调用,这样递归时函数会自动申请栈内存,这样就相当于程序员“不使用中间变量”了。

中间返回的值通过寄存器自动保存,最后一次返回时拷贝到int中去。

 

这种问题都是利用常量,或者将变量的申请交给编译器在递归过程中自动在栈中申请。

 

 

链表笔试面试题

《链表笔试面试题》

http://blog.csdn.net/fatshaw/article/details/6452460

 

 

 

链表倒序输出(不能改表链表结构,不能用任何自己开的辅助空间)

//递归方法 倒序输出

void reversePrint(node* head)

{

       if(head->next != NULL)

       {

             reversePrint(head->next);

             cout<<head->next->data<<" ";

       }

}

 

 

 

查找单链表中倒数第K个元素

 

设计一个算法,找出一个无环的单链表里面倒数第k个元素。

首先,检查链表是否为空,以及检查输入的k的有效性。

其次,考虑单链表少于K个元素的情况。

struct node{   

    int key;   

    node* next;   

};   

typedef node* List;   

int findLastKthElement(List list, intk)   

{   

    //遍历整个链表,声明一个临时指针指向头节点   

    //当遍历过元素个数小于K的时候,继续遍历   

    //当遍历过的元素个数等于k的时候,临时指针指向下一个元素,然后继续遍历   

    //当遍历到链表尾部的时候,则临时指针指向的节点就为倒数第k个元素。   

    if (list == NULL || k <=0)   

    {   

        return-1;   //查找失败。   

    }   

    List p = list;   

    List tempList =list;   

    int num = 0;   

    while(p)   

    {   

        if (num <k)   

       {   

           num++;   

       }   

        else if (num== k)   

       {   

           tempList = tempList->next;   

       }   

        p =p->next;   

    }   

       

    if (num < k)   

    {   

        return -1; //查找倒数第k个元素失败   

    }   

    return tempList->key;   

}

逆置单链表(手写代码)

 

 

 

有一个单链表,要求写出此单链表的逆置链表。

typedef struct node

{

    int data;

    struct node *next;

}node;

node *reverse(node *head)

{

    //如何实现,请给出说明,谢谢!

}

 

node *reverse(node *head) 

node *p, *q, *t;

 if (NULL == head || NULL == head->next)//检查边界情况

    { 

       cout<<"链表为空"<<endl;

        returnhead; 

    }

    p = head->next;

    t = q = p->next; 

    p->next = NULL;   

    while(t != NULL) 

    { 

        t =q->next; 

        q->next =p; 

        p = q; 

        q = t; 

    } 

head->next = p;

return head;

单链表的创建,逆置(C++代码实现)

#include<iostream>

using namespace std;

 

//创建链表节点结构体

struct node

{

       int data;

       node* next;

};

 

//创建链表 

node *create() 

{   

    node *head; 

    head = (node *)malloc(sizeof(node));//为head指针分配空间 

    head->next=NULL; 

    head->data =0; 

    node *p = head;  //创建尾指针 

    cout<<"Please input all thedata and end with '-1': "<<endl; 

    int x;

    node *s;

 

    while (cin>>x) 

    { 

        if (-1 !=x) 

        { 

           s = (node *)malloc(sizeof(node)); //分配空间

           s->data = x;

           p->next =s; 

           p = s; 

           head->data++; 

        } 

        else 

        { 

           break; 

        } 

    }

    p->next =NULL;

    return head;

}

 

//打印链表

void print(node* head)

{

       if(NULL == head ||NULL == head->next)

       { 

       cout<<"链表为空"<<endl;

    }

       node* temp =head->next;

       while(temp != NULL)

       {

             cout<<temp->data<<"";

             temp = temp->next;

       }

       cout<<endl;

}

 

//逆置函数 

node *reverse(node *head) 

    node *p1, *p2, *p3;//需要三个辅助节点

    if (NULL == head || NULL ==head->next)//检查边界情况

    { 

       cout<<"链表为空"<<endl;

        returnhead; 

    } 

    

       p1 =head->next; 

    p2 = p1->next;

       p1->next = NULL;

    while (NULL != p2)

    { 

        p3 =p2->next; 

        p2->next =p1; 

        p1 = p2; 

        p2 = p3; 

    }

       head->next = p1;

    return head; 

 

int main()

{

       node* list =create();

       print(list);

       reverse(list);

       print(list);

      

       system("pause");

       return 0;

}

 

 

函数

 

 

 

atoi函数 - 字符串转换成整型数


atoi函数,ASCII to Integer.把字符串转换成整型数。

 

int atoi(const char *nptr)

函数说明:参数nptr字符串,如果第一个非空格字符存在,是数字或者正负号则开始做类型转换,之后检测到非数字包括结束符字符时停止转换,返回整型数。否则返回零。

 

示例使用代码:

char*str ="12345.67";

  n =atoi(str);

 

chara[] ="-100";

  charb[] ="123";

  intc ;

  c =atoi( a ) +atoi( b ) ;

 

 

ctype.h里的函数概况

2主要函数简介

2.1 isalpha

2.2 iscntrl

2.3 isdigit

2.4 isgraph

2.5 islower

2.6 isupper

2.7 tolower

2.8 toupper

2.9 isalnum

2.10 isprint

 

写出c库的atoi函数,要求,尽量完美,考虑到溢出异常等情况。

 

#include <limits.h>    

#include <ctype.h>    

#include<stdlib.h>     

#include <stdio.h> 

int  myatoi (const char *nptr)  {   

    int sign = 1;   

    int retval = 0;   

    int int_bound = INT_MAX /10;     

  

    /* ignore preceding white spaces */    忽略最前面连续的空格

    while (isspace (*nptr))  

        nptr++;    

  

    /* handle preceding sign if there isany */    处理正负号

    if (*nptr == '-' || *nptr == '+')   

        sign= (*nptr++ == '+') ? 1 :-1;     

  

    /* add digits to number(retval),  * but not more than one less than the number of digits ofINT_MAX */   

    while (isdigit (*nptr) &&int_bound)  {   

        retval= retval * 10 + (*nptr - '0');   

        int_bound/= 10;   

        nptr++;   

    }     

    if (!isdigit (*nptr))   

        return sign * retval;   

    else  {   

        if (retval > INT_MAX /10)   

            goto overflow;   

        else if (retval < INT_MAX /10)   

            goto normal;   

        else if (*nptr - '0' > abs (((sign == 1) ? INT_MAX : INT_MIN) %10))   

            goto overflow;   

        else   

            goto normal;   

    }     

normal:   

    if (isdigit (*(nptr +1)))   

        goto overflow;   

    else   

        return sign * (retval * 10 + (*nptr - '0'));     

overflow:   

    return ((sign == 1) ? INT_MAX :INT_MIN);   

}     

  

int main() { 

    printf("%d/n",myatoi("-123456c7")); 

    return 0; 

 

memcpy函数

 

 

笔试编程题:已知memcpy的函数为: void* memcpy(void *dest , const void* src , size_t count)其中dest是目的指针,src是源指针。不调用c++/c的memcpy库函数,请编写memcpy。

//copyright@July 2013/9/24 

void* memcpy(void *dst, const void *src, size_t count)     

{     

    //安全检查 

    assert( (dst != NULL)&& (src != NULL) );     

  

    unsigned char *pdst = (unsigned char *)dst;     

    const unsigned char *psrc= (const unsigned char *)src;     

  

10     //防止内存重复 

11     assert(!(psrc<=pdst&& pdst<psrc+count));     

12     assert(!(pdst<=psrc&& psrc<pdst+count));     

13   

14     while(count--)     

15     {     

16         *pdst= *psrc;     

17         pdst++;     

18         psrc++;     

19     }     

20     return dst;     

21 }   

 

说明

1.source和destin所指的内存区域可以重叠,但是如果source和destin所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针

2.strcpy和memcpy主要有以下3方面的区别。

2.1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体等。

2.2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。

2.3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

3.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。

注意:source和destin都不一定是数组,任意的可读写的空间均可。

编辑本段函数实现

微软中:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

void* __cdeclmemcpy(

        void* dst,

        constvoid* src,

        size_tcount

        )

{

        void* ret = dst;

#if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)

        {

        externvoidRtlMoveMemory(void*,constvoid*,size_tcount );

        RtlMoveMemory( dst, src, count );

        }

#else  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */

        /*

         * copy from lower addresses to higher addresses

         */

        while(count--) {

               *(char*)dst = *(char *)src;

               dst = (char*)dst + 1;

               src = (char*)src + 1;

        }

#endif  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */

        return(ret);

}

 

coreutils中:

void *memcpy (void *destaddr,void const*srcaddr, size_t len){    char *dest =destaddr;    char const *src = srcaddr;    while(len-- > 0)        *dest++ =*src++;    return destaddr;}

Linux中:

void *memcpy(void *dest, const void*src, size_t count){    assert(dest != NULL && src !=NULL);    char *tmp = dest;    const char *s =src;    while(count--)        *tmp++ = *s++;    return dest;}

编辑本段程序例

example1

作用:将s中的字符串复制到字符数组d中。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// memcpy.c

#include <stdio.h>

#include <string.h>

 

intmain()

{  

   char*s="Golden Global View";

   chard[20];

   clrscr();

   memcpy(d,s,(strlen(s)+1));

   printf("%s",d);

   getchar();

   return0;

}

 

输出结果:Golden Global View

example2

作用:将s中第14个字符开始的4个连续字符复制到d中。(从0开始)

1

2

3

4

5

6

7

8

9

10

11

12

13

#include <string.h>

 

intmain()

{

   char*s="Golden Global View";

   chard[20];

   memcpy(d,s+14,4);//从第14个字符(V)开始复制,连续复制4个字符(View)

   //memcpy(d,s+14*sizeof(char),4*sizeof(char));也可

   d[4]='\0';

   printf("%s",d);

   getchar();

   return0;

}

 

输出结果: View

example3

作用:复制后覆盖原有部分数据

1

2

3

4

5

6

7

8

9

10

11

12

#include <stdio.h>

#include <string.h>

 

intmain(void)

{

   charsrc[] ="******************************";

   chardest[] ="abcdefghijlkmnopqrstuvwxyz0123as6";

   printf("destination before memcpy: %s\n", dest);

   memcpy(dest, src,strlen(src));

   printf("destination after memcpy: %s\n", dest);

   return0;

}

 

输出结果:

destination beforememcpy:abcdefghijlkmnopqrstuvwxyz0123as6

destination after memcpy:******************************as6

memcpy的函数内部实现

 

memcpymemmove函数的实现,需要注意memmove的覆盖问题,还有指针类型需要考虑。下面的例子中,先给出了错误的例子,而后给出了正确的例子,引以为戒!

区别:两个函数都是进行n字节内存内容的拷贝,入口参数和返回参数也都一样,可是这两个函数在内部实现上是有一定区别的,这主要是因为dest内存区域和src内存区域可能有一下四种不同的情况,注意count的影响:

src的内存区域和dest的内存区域相对位置和重叠关系有四种情况,memcpy没有考虑重叠的情况,而memmove考虑到了全部情况,因此memcpy函数的时候可能出现意向不到的结果。

这两个函数的实现:

***********下面两个是错误的实现**************

void* memcpy(void*dest, void* source, size_t count)

     {

          void* ret = dest;

         //copy from lower address to higher address

         while (count--)

                 *dest++ = *source++;   //不知道两个指针的类型,不可以这样自加。

          return ret;

     }

 

 

 

 

void* memmove(void*dest, void* source, size_t count)

   {

      void* ret = dest;

     if (dest <= source || dest >= (source + count))

      {

         //Non-Overlapping Buffers

        //copy from lower addresses to higher addresses

        while (count --)

              *dest++ =*source++;                  

    } else{

       //Overlapping Buffers

      //copy from higher addresses to lower addresses

      dest += count - 1;

      source += count - 1;

      while (count--)

              *dest-- =*source--;               // 情况同上

    }

     return ret;

   }

 

***********************正确的如下**************************

void*mymemcpy(void* dest, void* source, size_t count)

{

      char *ret = (char *)dest;

      char *dest_t = ret;

      char *source_t = (char *)source;

      

      while (count--){

          *dest_t++ = *source_t++;

       } 

return ret;

}     

 

void*my_memmove(void *dst,const void *src,int count)

{

   char*ret;

   char*dst_t;

   char*src_t;

 

   ret =(char *)dst;

 

   if((unsigned char*)dst <= (unsigned char*)src

|| (unsigned char*)dst >= ((unsigned char *)src + count))

   {

        dst_t = (char *)dst;

     src_t = (char *)src;

    

     while (count--)

     {

        *dst_t++ = *src_t++;

     }

   }else{

    dst_t = (char *)dst + count - 1;

    src_t = (char *)src + count - 1;

    while (count--)

     {

       *dst_t-- = *src_t--;

     }

   }

 

  return(ret);

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 把一个链表反向,递归,非递归都写一遍。 1.试编写3个函数实现   (1)建立一个双向链表   (2)插入一个节点   (3)删除一个节点 2.自己定义数据结构,写出程序:二叉树的前序遍历。 3.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。 4.下面哪种排序法对12354最快 a quick sort b.buble sort c.merge sort 5.哪种结构,平均来讲,获取一个值最快 a. binary tree b. hash table c. stack 6.一个二叉树的三种遍历方法的输出结果 7.链表按升序打印每打印完一个节点就将该节点从链表中删除 8.选择一种算法来整理出一个链接表。你为什么要选择这种方法?现在用o(n)时间来做。 9. 用一种算法在一个循环的链接表里插入一个节点,但不得穿越链接表。    10.给两个变量,如何找出一个带环单链表中是什么地方出现环的? 11.哈希表和数组的定义,区别,优缺点。 12.链接表和数组之间的区别是什么? 任选一门语言,当场定义二叉排序树数据结构,写出两个函数:初始化,删除一个节点,20分钟 13. 递归的折半查找算法[不限语言] 14. 解释一下什么是B+树,如何实现B+树的查找和插入.(用图示) 15.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。 13.排序方法比较 (intel) 排序方法 平均时间 最坏时间 辅助存储 直接插入排序 O(N2) O(N2) O(1) 起泡排序 O(N2) O(N2) O(1) 快速排序 O(Nlog2N) O(N2) O(Nlog2N) 简单选择排序 O(N2) O(N2) O(1) 堆排序 O(Nlog2N) O(Nlog2N) O(1) 归并排序 O(Nlog2N) O(Nlog2N) O(n) 基数排序 O(d(n+radix)) O(d(n+radix)) O(radix) 17.一个链表的操作,注意代码的健壮和安全性。要求: (1)增加一个元素; (2)获得头元素; (3)弹出头元素(获得值并删除)。 18.内排序算法 19.折半查找的复杂度,证明 20.sizeof()和strlen()的使用. 21.顺序存储结构的优点,散列法的思想是什么? 22.汉罗塔算法,不能递归... 23.一个链表的结点结构 struct Node { int data ; Node *next ; }; typedef struct Node Node ; (1)已知链表的头结点head,写一个函数把这个链表逆序 ( Intel) (2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表 依然有序。 (3)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表 依然有序,这次要求用递归方法进行。 ( Autodesk) 24.编最优化Bubble(int *pIntArray,int L),要求:交换元素不能用临时变量,如果有序需要最优。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值