链表面试汇总

参考:http://blog.sina.com.cn/s/blog_725dd1010100tqwp.html

http://www.360doc.com/content/15/1122/15/1317564_515001533.shtml

以下都是针对单链表:

1.    给定一个单链表,如何判断是否存在环?

结论:第一个指针以步长v1为1,第二个指针以步长v2为2同时出发,则它们会在某一节点相遇;充分必要;单链表中存在环。

证明:讨论当v1和v2存在什么关系,在单链表中有环时一定会相遇。


这里k表示当两个指针同时在环中时(t是从此刻计时的),快的指针距离环中的连接点的距离,慢的指针此时刚好处于连接点,这里是指相对距离。

Node* collisionnode=NULL;//碰撞点
bool JudgeCircleExist(Node* head){
	if(!head || head->next==0)
		return false;
	Node *p1=head,*p2=head;
	bool result=false;
	while(p2 && p2->next){
		p1=p1->next;
		p2=p2->next->next;
		if(p1==p2){
			collisionnode=p1;
		    result=true;
			break;
		}
	
	}
	return result;

}

2.    若存在环,判断是6型环,还是o型环。

  答:判断头节点是否在环中

3.   若存在环,则如何知道环的长度?

int Circlelength(Node* head){
	JudgeCircleExist(head);
	if(!collisionnode)
		return 0;
	int count=0;
	Node *p1=collisionnode, *p2=collisionnode;
	while(p2 && p2->next){
	    ++count;
		p1=p1->next;
		p2=p2->next->next;
		if(p1==p2)
			break;
	}
	return count;
}


4.   若存在环,如何找出环的连接点在哪里?

结论:两个指针分别从头节点 和碰撞点以步长为1出发,则它们会在环的连接点相遇。

证明:设头节点到连接点的距离是a,连接点到碰撞点的距离是b,环的长度是l。则s=a+b;2s=a+b+nl(根据问题1,s是慢指针从头节点出发到相遇在碰撞点时走过的距离),可推出 a=nl-b。所以结论正确。

Node* FindLoopPort(Node* head){
	JudgeCircleExist(head);
	Node *p1=head, *p2=collisionnode,*result=0;
	while(p1 && p2){
		p1=p1->next;
		p2=p2->next;
		if(p1==p2){
			result=p1;
			break;
		}
	}
	return result;

}


5.   带环的单链表的长度是多少?

答:环的长度 + 头节点到连接点的长度 

6.   给定两个单链表(head1, head2),检测两个链表是否有交点,如果有返回第一个交点。(第一个公共结点问题)

答:判断是否相交,可通过判断尾节点是否一样

7.   只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。

8.   只给定单链表中某个结点p(非空结点),在p前面插入一个结点。***

void InsertNode_before(Node* p,int x){
	Node* node=new Node(p->val);
	node->next=p->next;
	p->next=node;
	p->val=x;
}


9.   给定单链表头结点,删除链表中倒数第k个结点。

10. 链表排序(归并排序)

  答:链表排序可用:归并、插入、冒泡及选择(单向链表);归并、快排、插入、冒泡及选择(双向链表);优先考虑归并,时间复杂度低(nlogn),实现简单。

//链表排序1 冒泡  由小到大
Node* Bubble(Node* head){
	if(head==NULL)
		return 0;
	Node *p,*p_pre,*node=head;
	int temp;
	while(node){	
		p_pre=head;
		p=head->next;
		while(p){			
			if(p_pre->val>p->val){
				temp=p_pre->val;
				p_pre->val=p->val;
				p->val=temp;
			}			
			p_pre=p;
		    p=p->next;			
		}
		node=node->next;
	
	}
	return head;

	}
//将链表从中间分开
Node* Middle(Node* head){
 
	Node* p1=head;
    Node* p2=head;
	Node* mid;
	while(p2 &&p2->next){	//注意检查p2->next是否为空
		mid=p1;
		p1=p1->next;
		p2=p2->next->next;
	}
	mid->next=NULL;
	return p1;

}
//将两个有序链表链接成一个新的有序链表
Node* Merge(Node* head1, Node* head2){
    Node* p=0;
	if(!head1)
		return head2;
	if(!head2)		
		return head1;
    if(head1->val<head2->val){
		p=head1;
		p->next=Merge(head1->next,head2);
	}
	else{
	
		p=head2;
		p->next=Merge(head1,head2->next);	
	}
	return p;

}
// 归并排序
Node* mergeSortList(Node* head){
	if(!head || !(head->next))
		return head;
	Node* middle=Middle(head);
	Node* first=mergeSortList(head);
	Node* second=mergeSortList(middle);
	return Merge(first,second);
}


11. 反转单链表(额外的指针记录节点的前驱和后继)

Node* ReverseList(Node* head){
	if(!head || !(head->next))
		return head;
	Node* pre=0,*cur=head,*next=0,*newhead=0;
	while(cur){
		next=cur->next;
		if(next==0)
			newhead=cur;
		cur->next=pre;
		pre=cur;
		cur=next;
	}
	return newhead;
}


12. 两个有序链表的合并

13. 找出链表的中间元素

14. 从尾到头打印单链表

void Print_reverse(Node* head){
	if(!head)
		return;
	Print_reverse(head->next);    
	cout<<head->val<<' ';
}

15. 0,1,3...,n - 1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里第m个数字。求出这个圆圈里剩下的最后一个数字。(环形链表)

16. 请实现一个函数实现该链表的复制,其中m_pSibling指向的是链表中任意一个结点或者NULL。(复杂链表的复制)

//复杂链表的复制
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {}
};

//链接链表
void f1(RandomListNode* pHead){

	if(!pHead)
		return;
	RandomListNode* node=pHead;
	while(node){
		RandomListNode* p=new RandomListNode(node->label);
		RandomListNode* pnext=node->next;
		node->next=p;
		p->next=pnext;
		node=pnext;	
	}
}
//设置兄弟指针
void f2(RandomListNode* pHead){

	if(!pHead)
		return;
	RandomListNode* node=pHead;
	while(node){		
		if(!(node->random))
			node->next->random=NULL;
		else
			node->next->random=node->random->next;

		node=node->next->next;
	}
}
//拆分链表
RandomListNode* f3(RandomListNode* pHead){

	if(!pHead)
		return 0;
	RandomListNode* head=pHead->next;
	RandomListNode* node=pHead;
	while(node){
		RandomListNode* p=node->next;
		node->next=p->next;
		node=node->next;
		if(node) //检查节点是否为空 
		p->next=node->next;
		
	}
	return head;
}

RandomListNode* Clone(RandomListNode* pHead){
	f1(pHead);
	f2(pHead);
	return f3(pHead);
}



17. 删除链表重复元素

Node* DeleteRepeat(Node* head){
	if(!head || !(head->next))
		return head;
	set<int> temp;//遍历链表的时候将不同的元素插入set容器内,以作参考
	temp.insert(head->val);
	Node* p=head->next;
	Node* pre=head;
	while(p){
	
		if(temp.find(p->val)!=temp.end()){
			pre->next=p->next;
		    delete p;
			p=pre->next;
			
		}
		else{		
			temp.insert(p->val);
			pre=p;
	        p=p->next;

		}

	}
	return head;


}

18. 链表和数组的区别在哪里?

答:数组静态分配内存,链表动态分配内存;数组在内存中连续存放,链表不连续;数组查找元素时间复杂度为O(1),链表查找元素时间复杂度为O(n);数组删除或插入元素时间复杂度为O(n),链表删除或插入元素时间复杂度为O(1)

补充:数组和指针的区别:

1.身份不同,数组贵族身份,是一种数据结构;指针贫民身份,4个字节的变量。但是当数组作函数形参时,则会退化成指针。

2.内存容量不同。char a[10]="abnffgg",char * p="abnffgg",sizeof(a)和sizeof(p)不同。

3.修改方式不同。char a[10]="abnffgg",char * p="abnffgg",可用a[0]='d'修改,但p[0]='d'不行。因为a表示字符数组,但是p只是一个指针变量,不能通过其修改字符串常量(位于全局区)


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值