参考: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只是一个指针变量,不能通过其修改字符串常量(位于全局区)。