【100题】第七十六题~第八十题

一,复杂链表的复制(链表、算法)


1)题目:有一个复杂链表,其结点除了有一个m_pNext指针指向下一个结点外,还有一个m_pSibling指向链表中的任一结点或者NULL。其结点的C++定义如下:参考【转载】

 

 struct  ComplexNode
{
      int    m_nValue;
     ComplexNode*   m_pNext;
     ComplexNode*   m_pSibling;    //指向任意一个结点

};


 

       图是一个含有5个结点的该类型复杂链表。图中实线箭头表示m_pNext指针,虚线箭头表示m_pSibling指针。为简单起见,指向NULL的指针没有画出。

    方法一看到这个问题,我的第一反应是分成两步:

              第一步:是复制原始链表上的每个链表,并用m_pNext链接起来;

              第二步,假设原始链表中的某节点Nm_pSibling指向结点S,由于S的位置在链表上有可能在N的前面也可能在N的后面,所以要定位N的位置需要从原始链表的头结点开始找,假设从原始链表的头结点开始经过s步找到结点S,那么在复制链表上结点Nm_pSiblingS’,离复制链表的头结点的距离也是s,用这种办法我们就能为复制链表上的每个结点设置m_pSibling了。对含有n个结点的链表,由于定位每个结点的m_pSibling都需要从链表头结点开始经过O(n)步才能找到,因此这种方法的总时间复杂度是O(n2)

   方法二空间换时间:由于上述方法的时间主要花费在定位结点的m_pSibling上面,我们试着在这方面去做优化,还是分为两步

             第一步仍然是复制原始链表上的每个结点N,并创建N’,然后把这些创建出来的结点链接起来,这里我们对<NN’>的配对信息放到一个哈希表中;

             第二步还是设置复制链表上每个结点的m_pSibling,如果在原始链表中结点Nm_pSibling指向结点S,那么在复制链表中对应的N’应该指向S’,由于有了哈希表,可以用O(1)的时间根据S找到S’,相当于用空间换时间,以O(n)的空间消耗实现了O(n)的时间效率

   方法三换一种思路,在不用辅助空间的情况下实现O(n)的时间效率。在原来基础上创建节点,然后拆除。

              第一步:根据原始链表的每个结点N,创建对应的N’。这一次,我们把N’链接在N的后面。 

                  【关键】注意是在原来每个节点后面,创建一个该节点的拷贝

// Clone all nodes in a complex linked list with head pHead, and connect all nodes with m_pNext link
void CloneNodes(ComplexNode* pHead)
{
    ComplexNode* pNode = pHead;
    while(pNode != NULL)
    {
        ComplexNode* pCloned = new ComplexNode();

        pCloned->m_nValue = pNode->m_nValue;
        pCloned->m_pNext = pNode->m_pNext;
        pCloned->m_pSibling = NULL;

        pNode->m_pNext = pCloned;
        pNode = pCloned->m_pNext;
    }
}

      第二步是设置我们复制出来的链表上的结点的m_pSibling。假设原始链表上的Nm_pSibling指向结点S,那么其对应复制出来的N’N->m_pNext,同样S’也是S->m_pNext。这就是我们在上一步中把每个结点复制出来的结点链接在原始结点后面的原因。有了这样的链接方式,我们就能在O(1)中就能找到每个结点的m_pSibling了。例子中的链表经过这一步,就变成如下结构了:
// Connect sibling nodes in a complex link list
void ConnectSiblingNodes(ComplexNode* pHead)
{
    ComplexNode* pNode = pHead;
    while(pNode != NULL)
    {
        ComplexNode* pCloned = pNode->m_pNext;
        if(pNode->m_pSibling != NULL)
            pCloned->m_pSibling = pNode->m_pSibling->m_pNext;
        pNode = pCloned->m_pNext;
    }
}

         第三步是把这个长链表拆分成两个:把奇数位置的结点链接起来就是原始链表,把偶数位置的结点链接出来就是复制出来的链表。上述例子中的链表拆分后的两个链表如下:

 
// Split a complex list into two: Reconnect nodes to get the original list, and its cloned list
ComplexNode* ReconnectNodes(ComplexNode* pHead)
{
    ComplexNode* pNode = pHead;
    ComplexNode* pClonedHead = NULL;
    ComplexNode* pClonedNode = NULL;

    if(pNode != NULL)
    {
        pClonedHead = pClonedNode = pNode->m_pNext;
        pNode->m_pNext = pClonedNode->m_pNext;
        pNode = pNode->m_pNext;
    }

    while(pNode != NULL)
    {
        pClonedNode->m_pNext = pNode->m_pNext;
        pClonedNode = pClonedNode->m_pNext;
        pNode->m_pNext = pClonedNode->m_pNext;
        pNode = pNode->m_pNext;
    }

    return pClonedHead;
}

把上面三步合起来,就是复制链表的完整过程:
// Clone a complex linked list with head pHead

ComplexNode* Clone(ComplexNode* pHead)
{
    CloneNodes(pHead);
    ConnectSiblingNodes(pHead);
    return ReconnectNodes(pHead);
}



二,链表的面试题目如下(链表):


【1】给定单链表,检测是否有环


         使用两个指针p1,p2从链表头开始遍历,p1每次前进一步,p2每次前进两步。如果p2到达链表尾部,说明无环,否则p1、p2必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。


 


【2】给定两个单链表(head1,head2),检测两个链表是否有交点,如果有返回第一个交点。


          1)如果head1==head2,那么显然相交,直接返回head1。
          2)否则,分别从head1,head2开始遍历两个链表获得其长度len1与len2,假设len1>=len2,那么指针p1由head1开始向后移动len1-len2步,指针p2=head2,
         3)下面p1、p2每次向后前进一步并比较p1p2是否相等,如果相等即返回该结点,否则说明两个链表没有交点。




【3】给定单链表(head),如果有环的话请返回从头结点进入环的第一个节点。【判断有环,分成两个单链表,求交点】
       运用题一,我们可以检查链表中是否有环。
       如果有环,那么p1p2重合点p必然在环中。从p点断开环,
       方法为:p1=p,p2=p->next,p->next=NULL。此时,原单链表可以看作两条单链表,一条从head开始,另一条从p2开始,于是运用题二的方法,我们找到它们的第一个交点即为所求。


【4】只给定单链表中某个结点p(并非最后一个结点,即p->next !=NULL)  指针,删除该结点【狸猫换太子】

           办法很简单,首先是放p中数据,然后将p->next的数据copy入p中,接下来删除p->next即可。


【5】只给定单链表中某个结点p(非空结点),在p前面插入一个结点。【狸猫换太子】
          办法与前者类似,首先分配一个结点q,将q插入在p后,接下来将p中的数据copy入q中,然后再将要插入的数据记录在p中。


三,链表和数组的区别在哪里(链表、数组)

     链表:元素在内存中非连续存放,用next指向下一个元素。

          查找任意元素慢

          添加、删除元素快,不需要移动其它的元素

          动态分配内存的形式实现。需要时可以用new分配内存空间,不需要时用delete将已分配的空间释放,不会造成内存空间的浪费

 

    数组:元素在内存中连续存放,通过下标迅速访问数组中任何元素。

          查找元素快
          增加、删除元素慢,需要移动大量元素,
         不允许动态定义数组的大小,在使用数组之前必须确定数组的大小,容易造成内存空间的浪费。



 
四,(链表、字符串)

      1,编写实现链表排序的一种算法。说明为什么你会选择用这样的方法?

#include <iostream>
using namespace std;

struct node 
{
	int data;
	node *next;
};

void swap(node *x,node *y)
{
	
	int temp=x->data;
	x->data=y->data;
	y->data=temp;
	
}

node* createLink(int a[],int n)
{
	node *head=new node();
	node *temp=head;
	
	for(int i=0;i<n;i++)
	{
		node *p=new node();
		p->data=a[i];
		temp->next=p;
		temp=p;
	}
	
	return head;
	
}

node *sort(node *head)//选择排序 
{
	node *p=head->next;
	node *q=head->next;
	
	node *temp=p; 
	
	while(p)
	{ 
	    temp=p;
		while(q)
		{
			if(temp->data > q->data)
			{
				temp= q; 
			} 
			
		    q=q->next; 
		} 
		swap(temp,p);
		p=p->next; 
		q=p; 
	} 
	return head; 
}

void print(node *head)
{
	node *p=head->next;
	while(p)
	{
		cout<<p->data<<" ";
		p=p->next;
	}
	
}

int main()
{
	int a[]={1,2,6,4,3,5};
	node *head=createLink(a,6);
	print(head);
	
	cout<<endl; 
	node *sortHead= sort(head);  
	print(sortHead); 
	
}


 

      2,编写实现数组排序的一种算法。说明为什么你会选择用这样的方法?

#include  "stdio.h"
void HeapAdjust(int array[],int i,int nLength)//调整堆 
{
	int nChild;
	int nTemp;//赋值为待调整的 节点
	 //从应该调整的节点开始 ,直到该节点 已经没有孩子节点 
	for(nTemp=array[i];2*i+1<nLength;i=nChild)//由于根节点从0开始计数 故左孩子 为2*i+1  
	{
		nChild=2*i+1;//子节点=2(父节点)+1 
			
		/*一共两个子节点的话得到 较大的一个*/
		  //nChild<nLength-1 判断到头没有 
		if(nChild<nLength-1&&array[nChild+1]>array[nChild])
		      ++nChild;
		   
		    /*如果较大子节点大于父节点  将子节点 调整到父节点*/
			if(nTemp<array[nChild])
			  array[i]=array[nChild];
		    else
		      break;//这个地方不加 会出错  第一个会输出第二个 
		      
      array[nChild]=nTemp;//子节点 等于父节点 
	} 
}
void HeapSort(int a[],int length)
{
	/*初建堆 */
	for(int i=length/2-1;i>=0;--i)//从最后一个 非叶子节点调整 (这里的  i是下标) 
	   HeapAdjust(a,i,length);
   
    int temp;
    for(int i=length-1;i>0;--i)
       {
       	/*第一个最大元素跟最后一个交换*/
       	  temp=a[0];
       	  a[0]=a[i];
       	  a[i]=temp;
       	  
       	  HeapAdjust(a,0,i);//调整堆 (注意 length=i  由于堆是逐渐变小的)
       }
	
}
int main()
{
	int a[10]={1,2,5,3,8,4,7,6};
    HeapSort(a,8);
    printf("对数组1,2,5,3,8,4,7,6 进行从小到大的堆排序\n");
    
    
	 for(int i=0;i<8;i++)
	   printf("%d ",a[i]);
	return 0;
} 

 

      3,请编写能直接实现strstr()函数功能的代码。

#include <iostream>
using namespace std;
/*功能:找出str2字符串在str1字符串中第一次出现的位置(不包括str2的串结束符)。   
 返回值:返回该位置的指针,如找不到,返回空指针。*/ 
char *MyStrstr( char *str1, char *str2)
{
	int   i=0,j=0; 
	if(strlen(str1) <strlen(str2))
		   return   NULL; 
	while(*(str1+i)   &&   *(str2+j)) 
    { 
        if(*(str1+i)==*(str2+j))
		{
			i++;
			j++;
		} 
        else
		{
			i=i-j+1;
			j=0;
		} 
    } 
	return   (j> 0)?str1+i-j:NULL;
} 

int main()
{
	char *str1= "My name is tianshuai";
	char *str2= "is";
	cout<< strstr(str1,str2)<<endl;
	cout<< MyStrstr(str1,str2)<<endl;
	
	
}

 

      5,编写反转字符串的程序,要求优化速度、优化空间。

方法一:空间复杂度 O(n) 时间复杂度 O(n)

#include <iostream>
using namespace std;

char* strrev1(const char* str)
{
    int len=strlen(str);
	char *str2=new char[len+1];  //需要申请内存地址
	strcpy(str2,str);//将只读字串复制到刚申请的地址中

	//char str2[len];//这种方法不成
	//memset(str2, '0' , sizeof(char)*len);

	for(int i=0;i<len;++i)
		str2[i]=str[len-i-1];	
	//str2[len]='\0';
	return str2;
	
}
int main()
{
	char *str="hello";
	
	cout<<strrev1(str)<<endl;

	getchar();
}

  交换两个数的值

        int a=2;
	int b=3;
	
	a^=b;
	b^=a;
	a^=b;
	cout<<a<<endl;
	cout<<b<<endl; 

方法二:递归

方法三:如果参数不是const 则时间复杂度 O(n)     空间复杂度 不需要(采用上述交换策略)

      6,在链表里如何发现循环链接?

           使用两个指针p1,p2从链表头开始遍历,p1每次前进一步,p2每次前进两步。如果p2到达链表尾部,说明无环,否则p1、p2必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。

      7.给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里

         实际上是打乱一个数组

         首先54张牌分别用0到53 的数值表示并存储在一个整形数组里,数组下标代表纸牌所在的位置。接下来,遍历整个数组,在遍历过程中随机产生一个随机数,并以该随机数为下标的数组元素与当前遍历到的数组元素进行对换。时间复杂度为O(n) (注:所得到的每一种结果的概率的分母越大越好)

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

void shuffle(int boke[])  //洗牌
{
  int i,r,t;
  srand((unsigned)time(NULL)); //随机数种子
  for(i=0; i<54; i++)
    {
      r=(rand()%107)/2;
      //交换
      t=boke[i];
      boke[i]=boke[r];
      boke[r]=t;
    }
}
int main()
{
  int boke[54],i;
  for(i=0;i<54;i++) //初始化纸牌
    boke[i]=i;
  printf("before shuffle:\n");
  for(i=0; i<54; i++)    //打印
      printf("%d ",boke[i]);

  shuffle(boke);     //洗牌

  printf("\nafter shuffle:\n");
  for(i=0; i<54; i++)   //打印
      printf("%d ",boke[i]);
  return 0;
}


 

      8.写一个函数,检查字符是否是整数,如果是,返回其整数值。(或者:怎样只用4行代码编写出一个从字符串到长整形的函数?)

 

          if(ch>'0' && ch <'9')

                     return  ch-'0';

          else

                    return  false;

 

#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <iostream>

using namespace std;

long strtoint(char *str,int length);


long strtoint(char *str,int length)
{
    if(length > 1) 
 	
      return str[0]=='-' ? strtoint(str, length-1)*10-(str[length-1]-'0') : strtoint(str, length-1)*10+str[length-1]-'0';
     
 	else 
     return str[0]=='-' ? -1/10 : str[0]-'0';
 
 
}


int main(int argc, char* argv[])
{
    
	long n=strtoint("123",3);
    cout<<n<<endl;
    
    cout<<strtoint("-123",4)<<endl;
    return 0;
}


     9,给出一个函数来输出一个字符串的所有排列。

         

    10.请编写实现malloc()内存分配函数功能一样的代码。

           void *  malloc(unsigned int size);    //需要时申请内存

           void    free(void *ptr);                       //不需要时释放内存

          ---->参考博客

          malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时

             1)它沿连接表寻找一个大到足以满足用户请求所需要的内存块。

             2)然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。

             3)接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。

         调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。

      malloc()在操作系统中的实现

  在 C 程序中,多次使用malloc () 和 free()。不过,您可能没有用一些时间去思考它们在您的操作系统中是如何实现的。本节将向您展示 malloc 和 free 的一个最简化实现的代码,来帮助说明管理内存时都涉及到了哪些事情。
  在大部分操作系统中,内存分配由以下两个简单的函数来处理:
  void *malloc (long numbytes):该函数负责分配 numbytes 大小的内存,并返回指向第一个字节的指针。
  void   free(void *firstbyte):如果给定一个由先前的 malloc 返回的指针,那么该函数会将分配的空间归还给进程的“空闲空间”。

  malloc_init 将是初始化内存分配程序的函数。它要完成以下三件事:将分配程序标识为已经初始化,找到系统中最后一个有效内存地址,然后建立起指向我们管理的内存的指针。这三个变量都是全局变量:



//清单 1. 我们的简单分配程序的全局变量

int has_initialized= 0;
 
void *
managed_memory_start;
 
void *last_valid_address;

     如前所述,被映射的内存的边界(最后一个有效地址)常被称为系统中断点或者 当前中断点。在很多 UNIX? 系统中,为了指出当前系统中断点,必须使用 sbrk(0) 函数。 sbrk 根据参数中给出的字节数移动当前系统中断点,然后返回新的系统中断点。使用参数 0 只是返回当前中断点。这里是我们的 malloc 初始化代码,它将找到当前中断点并初始化我们的变量:



清单 2. 分配程序初始化函数
/* Include the sbrk function*/


#include
void malloc_init()
{
/* grab the last valid address from the OS*/

last_valid_address
= sbrk(0);
/*
we don''t have any memory to manage yet, so
*just set the beginning to be last_valid_address
*/

managed_memory_start
= last_valid_address;
/* Okay, we''re initialized and ready to go*/

has_initialized
=1;
}

          现在,为了完全地管理内存,我们需要能够追踪要分配和回收哪些内存。在对内存块进行了 free 调用之后,我们需要做的是诸如将它们标记为未被使用的等事情,并且,在调用 malloc 时,我们要能够定位未被使用的内存块。因此, malloc 返回的每块内存的起始处首先要有这个结构:



//清单 3. 内存控制块结构定义
struct mem_control_block {
int
is_available;
int
size;
};

          现在,您可能会认为当程序调用 malloc 时这会引发问题 —— 它们如何知道这个结构?答案是它们不必知道;在返回指针之前,我们会将其移动到这个结构之后,把它隐藏起来。这使得返回的指针指向没有用于任何其他用途的内存。那样,从调用程序的角度来看,它们所得到的全部是空闲的、开放的内存。然后,当通过 free() 将该指针传递回来时,我们只需要倒退几个内存字节就可以再次找到这个结构。

  在讨论分配内存之前,我们将先讨论释放,因为它更简单。为了释放内存,我们必须要做的惟一一件事情就是,获得我们给出的指针,回退 sizeof(struct mem_control_block) 个字节,并将其标记为可用的。这里是对应的代码:



清单 4. 解除分配函数
void free(void*
firstbyte) {
struct mem_control_block*
mcb;
/*
Backup from the given pointer to find the
* mem_control_block
*/

mcb
= firstbyte- sizeof(struct mem_control_block);
/* Mark the block as being available*/

mcb
->is_available= 1;
/* That''s It! We''re done.*/

return;
}

         如您所见,在这个分配程序中,内存的释放使用了一个非常简单的机制,在固定时间内完成内存释放。分配内存稍微困难一些。我们主要使用连接的指针遍历内存来寻找开放的内存块。这里是代码:



//清单 6. 主分配程序
void*malloc(long numbytes) {
/* Holds where we are looking in memory*/

void*current_location;
/*
This is the same as current_location, but cast to a
* memory_control_block
*/

struct mem_control_block*current_location_mcb;
/*
This is the memory location we will return. It will
* be set to 0 until we find something suitable
*/

void*memory_location;
/* Initialize if we haven''t already done so*/

if(! has_initialized) {
malloc_init();
}
/*
The memory we search for has to include the memory
* control block, but the users of malloc don''t need
* to know this, so we''ll just add it in for them.
*/

numbytes
= numbytes+ sizeof(struct mem_control_block);
/*
Set memory_location to 0 until we find a suitable
* location
*/

memory_location
=0;
/* Begin searching at the start of managed memory*/

current_location
= managed_memory_start;
/* Keep going until we have searched all allocated space*/

while(current_location!= last_valid_address)
{
/*
current_location and current_location_mcb point
* to the same address. However, current_location_mcb
* is of the correct type, so we can use it as a struct.
* current_location is a void pointer so we can use it
* to calculate addresses.
*/

current_location_mcb
=
(
struct mem_control_block*)current_location;
if(current_location_mcb->
is_available)
{
if(current_location_mcb->size>=
numbytes)
{
/*
Woohoo! We''ve found an open,
* appropriately-size location.
*/

/* It is no longer available*/
current_location_mcb
->is_available= 0;
/* We own it*/

memory_location
= current_location;
/* Leave the loop*/

break;
}
}
/*
If we made it here, it''s because the Current memory
* block not suitable; move to the next one
*/

current_location
= current_location+
current_location_mcb
->size;
}
/*
If we still don''t have a valid location, we''ll
* have to ask the operating system for more memory
*/

if(! memory_location)
{
/* Move the program break numbytes further*/

sbrk(numbytes);
/* The new memory will be where the last valid
* address left off
*/

memory_location
= last_valid_address;
/*
We''ll move the last valid address forward
* numbytes
*/

last_valid_address
= last_valid_address+ numbytes;
/* We need to initialize the mem_control_block*/

current_location_mcb
= memory_location;
current_location_mcb
->is_available= 0
;
current_location_mcb
->size=
numbytes;
}
/*
Now, no matter what (well, except for error conditions),
* memory_location has the address of the memory, including
* the mem_control_block
*/

/* Move the pointer past the mem_control_block*/
memory_location
= memory_location+ sizeof(struct mem_control_block);
/* Return the pointer*/

return memory_location;
}

             这就是我们的内存管理器。现在,我们只需要构建它,并在程序中使用它即可.多次调用malloc()后空闲内存被切成很多的小内存片段,这就使得用户在申请内存使用时,由于找不到足够大的内存空间,malloc()需要进行内存整理,使得函数的性能越来越低。聪明的程序员通过总是分配大小为2的幂的内存块,而最大限度地降低潜在的malloc性能丧失。也就是说,所分配的内存块大小为4字节、8字节、16字节、18446744073709551616字节,等等。这样做最大限度地减少了进入空闲链的怪异片段(各种尺寸的小片段都有)的数量。尽管看起来这好像浪费了空间,但也容易看出浪费的空间永远不会超过50%。

 

    11.给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。

         例如:给定 str1="abcd";    str2="cdcsd";

                     则    res="abcdcsd";

        思路:找出str1末尾与str2重叠的部分

        源码:

#include <iostream>
#include <string>
using namespace std;
int main()
{
	char *str1="abcdefgh";
	char *str2="defghijkl";
	
	int  pos=0; 
	for(int i=0;i<strlen(str1);++i)
	{
		if(strstr(str2,str1+i) == str2)//str1+i  为前缀
        {
        	pos=i;
			break; 
		} 
		
	}
	int size=strlen(str1)+strlen(str2)-pos+1;
	
	char *str=(char*)malloc(size*sizeof(char));
	strcpy(str,str1);
	strcat(str,str2+ (strlen(str2)-1)-pos);
	 
	cout<<str<<endl; 
	
	
}

 

    12.怎样编写一个程序,把一个有序整数数组放到二叉树中?

          题意:建立一个中序遍历是有序的二叉树

             解答:采用递归,每次创建的节点都是 数组中间的元素 mid =length>>1 而当数组长度==0 时候,则返回NULL叶子

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
 
struct student {
        int value;
        struct student *lchild;
        struct student *rchild;
};
 
void arraytotree(int *a, int len, struct student **p) {
        if(len) {
                *p = (struct student*)malloc(sizeof(struct student));
                (*p)->value = a[len/2];
                arraytotree(a, len/2, &((*p)->lchild));
                arraytotree(a+len/2+1, len-len/2-1, &((*p)->rchild));
        } else {
                *p = NULL;
        }
}
 
void display_tree(struct student *head) {
        if(head->lchild)display_tree(head->lchild);
        printf("%d\t", head->value);
        if(head->rchild)display_tree(head->rchild);
}
 
int main() {
 
        int a[] = {1,2,3,4,9,10,33,56,78,90};
        struct student *tree;
        arraytotree(a, sizeof(a)/sizeof(a[0]), &tree);
        printf("After convert:\n");
        display_tree(tree);
        printf("\n");
        return 0;
 
}

 

    13.怎样从顶部开始逐层打印二叉树结点数据?请编程。

 

#include <cstdio> 
#include <cstdlib> 
#include <string> 
#include <iostream> 
#include <deque> 
using namespace std; 

struct student {
        int value;
        student *lchild;
        student *rchild;
};
 
void arraytotree(int *a, int len, student **p) {
        if(len) {
                *p = (student*)malloc(sizeof(student));
                (*p)->value = a[len/2];
                arraytotree(a, len/2, &((*p)->lchild));
                arraytotree(a+len/2+1, len-len/2-1, &((*p)->rchild));
        } else {
                *p = NULL;
        }
}
 
void display_tree(student *head) {
        if(head->lchild)display_tree(head->lchild);
        printf("%d\t", head->value);
        if(head->rchild)display_tree(head->rchild);
}

void Level_display(student *head)
{
	deque<student*>  queue;
	queue.push_back(head);
	while(queue.size())
	{
		cout<<queue.front()->value<<"  ";
		if(queue.front()->lchild!=NULL) 
			queue.push_back(queue.front()->lchild);
		if(queue.front()->rchild!=NULL) 
			queue.push_back(queue.front()->rchild);  
		
		queue.pop_front(); 
	} 
	
} 
 
int main() {
 
        int a[] = {1,2,3,4,5,6,7};
        student *tree;
        arraytotree(a, sizeof(a)/sizeof(a[0]), &tree);
        printf("After convert:\n");
        display_tree(tree);
        printf("\n");
        
        printf("Level Display:\n");
        Level_display(tree);
        printf("\n");
        
        
        return 0;
 
}


 

    14.怎样把一个链表掉个顺序(也就是反序,注意链表的边界条件并考虑空链表)? 

Lnode* Reverse(Lnode *head)  
{  
  Lnode * p;
  Lnode * q;
  p=head-> next;  
  head-> next=NULL;  
  while(p!=NULL)  
  {  
  q = p-> next;  
  p-> next = head;  
  head =p;  
  p = q;  
  }  
  return head;
} 




五,阿里巴巴一道笔试题(运算、算法)

问题描述:
       12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?

        这道题其实考察的是,卡特兰数。类似的题目为,10个小朋友去买门票,5个拿着1元,5个拿着2元。而售货员自己没有零钱,问所有人可以买到票的排序方式有多少?

        卡特兰公式为:C(2n,n)/(n+1)   本题 n=6

         参考博文 :http://blog.csdn.net/tianshuai11/article/details/7838142

                               http://blog.csdn.net/tianshuai11/article/details/7557619

 

 

 

 

 

你可能感兴趣的:(【100题】第七十六题~第八十题)