实习整理(十四)
2015-11-26 17:45
169 查看
前面一篇博文简要地讲了下我在笔试准备过程中碰到过的有关字符串的题目,现在再提下有关链表的题目
做有关链表的题目最好画图,这样有助于理解
1、单向链表逆序
2、有序链表合并
3、双向链表删除结点
4、寻找链表中间结点
5、判断一个链表是否有环
1、单向链表逆序
(1) 具有链表头的单链表
student *reverse(student *stu)
{
student
*p1,*p2,*p3;
if(stu == NULL ||stu->next
== NULL)
return
stu;
p1=stu->next; //p1指向链表头节点的下一个节点
p2=p1->next;
p1->next=NULL;
while(p2)
{
p3=p2->next;
p2->next
= p1;
p1=p2;
p2=p3;
}
printf("p1 = %d,next = %dn
",p1->number,p1->next->number);
stu->next=p1; //将链表头节点指向p1
return stu;
}
核心思想:
p=head->next->next;
s=head->next;
head->next->next=NULL;
while(p)
{
q=p->next;
p->next=s;
s=p;
p=q;
}
(2) 无链表头单链表
typedef struct student
{
int number;
char name[20];
int score;
struct student *next;
}student;
student *reverse2(student *stu)
{
student *p1,*p2,*p3;
if(stu == NULL ||stu->next == NULL)
return stu;
p1=stu; //p1指向链表的第一个节点
p2=p1->next;
p1->next = NULL;
while(p2)
{
p3=p2->next;
p2->next = p1;
p1=p2;
p2=p3; }
printf("p1 = %d,next = %d\n ",p1->number,p1->next->number);
stu=p1; //将链表第一个节点指向p1
return stu;
}
(3) 只用一个变量,递归
List* recurReverse(List* p, List* head)
{
if(p == NULL || p->next == NULL)
{
head = p;
return p;
}
else
{
List* q = reverse(p->next, head);
q->next = p;
return p;
}
}
node* reverse(node * head)
{
if(head==NULL || head->next==NULL)
return head;
node* tail= head->next;
node* newHead= reverse(head->next);
tail->next=head;
head->next=NULL;
return newHead;
}
2、有序链表合并
(1)递归方法。
比如有下面两个链表:
链表1:1->3->5
链表2:2->4->6
递归方法的步骤如下:
(1)比较链表1和链表2的第一个节点数据,由于1<2,因此把结果链表头节点指向链表1中的第一个节点,即数据1所在的节点。
(2)对剩余的链表1(3->5)和链表2再调用本过程,比较得到结果链表的第二个节点,即2与3比较得到2。此时合并后的链表节点为1->2。
接下来的过程类似(2),如此递归直到两个链表的节点都被加到结果链表中
node * MergeRecursive(node *head1, node *head2)
{
node *head = NULL;
if (head1 == NULL)
{
return head2;
}
if (head2 == NULL)
{
return head1;
}
if ( head1->data < head2->data )
{
head = head1 ;
head->next = MergeRecursive(head1->next,head2);
}
else
{
head = head2 ;
head->next = MergeRecursive(head1,head2->next);
}
return head ;
}
(2)非递归方法。有链表头
Node* Merge2(Node* head1, Node* head2)
{
Node *res, *ret, *p, *q;
if(head1 == NULL) return head2;
if(head2 == NULL) return head1;
ret = res = head1;
p = head1->next;
q = head2->next;
while(p && q)
{
if(p->value < q->value)
{
res->next = p;
res = p;
p = p->next;
}
else
{
res->next = q;
res = q;
q = q->next;
}
}
res->next = p ? p : q; //p或q有剩,把剩下的链表插入到结果链表后
return ret;
}
还可以借助第三个链表,只是这样的话需要的空间会大点
Node *head3,*temp;
head3->next=NULL;
temp=head3;//保存表头
p=head1->next;
q=head2->next;
while(p!=NULL&&q!=NULL)
{
if(p->value<q->value)
{
head3->next=p;
head3=p;
p=p->next;
}
else
{
head3->next=q;
head3=q;
q=q->next;
}
if(p==NULL)
head3->next=q;
else
head3->next=p;
head3=temp;
}
3、双向链表删除结点
dnode *deletenode(dnode *head,dnode *node)
{
if(node->pre == NULL)
{
// 头结点
head = head->next;
head->pre = NULL;
}
else if(node->next == NULL)
{
// 尾结点
node->pre->next = NULL;
}
else
{
// 中间结点
node->pre->next = node->next;
node->next->pre = node->pre;
}
free(node);
return head;
}
4、寻找链表中间结点
【方法1】普通的方法很简单,首先遍历一遍单链表以确定单链表的长度L。然后再次从头节点出发循环L/2次找到单链表的中间节点。算法复杂度为O(L+L/2)=O(3L/2)。
【方法2】使用快慢指针。设定两个指向第一个结点的指针,一个(p)一次走一个,一个(q)一次走两个。当走的快的到达链表尾部(q->next==NULL ||q->next->next==NULL)时,慢的就指向中间结点(快指针到链表尾部时,当链表长度为奇数时,慢指针指向的即是链表中间指针,当链表长度为偶数时,慢指针指向的结点和慢指针指向结点的下一个结点都是链表的中间结点)
node *findMid(node *head) // 有链表头
{
if(NULL == head)
return NULL;
node *fast, *slow;
fast = slow = head;
while((fast != NULL) && (fast->next != NULL))
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
node *findMid(node *head) // 无链表头
{
if(NULL == head)
return NULL;
if(head->next == NULL || head->next->next == NULL)
return head;
node *fast = head;
node *slow = head;
while(fast->next != NULL && fast->next->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
5、判断一个链表是否有环
(1)判断是否有环。
设置两个指针(fast, slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定 相遇。(当然,fast先行头到尾部为NULL,则为无环链表)
(2)找到环的入口点
当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。假设slow走了s步,则 fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:
2s = s + nr
s= nr
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)
(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每 次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
node *findCircle(node *head)
{
if(NULL == head)
return NULL;
node *fast, *slow;
fast = slow = head;
while((fast != NULL) && (fast->next != NULL))
{
fast = fast->next->next; //走两步
slow = slow->next; //走一步
if(fast == slow){ //有环
fast = head;
while(fast != slow){
fast = fast->next;
slow = slow->next;
}
return fast;
}
}
return NULL;
}
PS:感觉链表经常在面试中遇到,一般面试官经常会要你进行纸上编程,考查你的动手能力等等。。。
未完待续。。。
做有关链表的题目最好画图,这样有助于理解
1、单向链表逆序
2、有序链表合并
3、双向链表删除结点
4、寻找链表中间结点
5、判断一个链表是否有环
1、单向链表逆序
(1) 具有链表头的单链表
student *reverse(student *stu)
{
student
*p1,*p2,*p3;
if(stu == NULL ||stu->next
== NULL)
return
stu;
p1=stu->next; //p1指向链表头节点的下一个节点
p2=p1->next;
p1->next=NULL;
while(p2)
{
p3=p2->next;
p2->next
= p1;
p1=p2;
p2=p3;
}
printf("p1 = %d,next = %dn
",p1->number,p1->next->number);
stu->next=p1; //将链表头节点指向p1
return stu;
}
核心思想:
p=head->next->next;
s=head->next;
head->next->next=NULL;
while(p)
{
q=p->next;
p->next=s;
s=p;
p=q;
}
(2) 无链表头单链表
typedef struct student
{
int number;
char name[20];
int score;
struct student *next;
}student;
student *reverse2(student *stu)
{
student *p1,*p2,*p3;
if(stu == NULL ||stu->next == NULL)
return stu;
p1=stu; //p1指向链表的第一个节点
p2=p1->next;
p1->next = NULL;
while(p2)
{
p3=p2->next;
p2->next = p1;
p1=p2;
p2=p3; }
printf("p1 = %d,next = %d\n ",p1->number,p1->next->number);
stu=p1; //将链表第一个节点指向p1
return stu;
}
(3) 只用一个变量,递归
List* recurReverse(List* p, List* head)
{
if(p == NULL || p->next == NULL)
{
head = p;
return p;
}
else
{
List* q = reverse(p->next, head);
q->next = p;
return p;
}
}
node* reverse(node * head)
{
if(head==NULL || head->next==NULL)
return head;
node* tail= head->next;
node* newHead= reverse(head->next);
tail->next=head;
head->next=NULL;
return newHead;
}
2、有序链表合并
(1)递归方法。
比如有下面两个链表:
链表1:1->3->5
链表2:2->4->6
递归方法的步骤如下:
(1)比较链表1和链表2的第一个节点数据,由于1<2,因此把结果链表头节点指向链表1中的第一个节点,即数据1所在的节点。
(2)对剩余的链表1(3->5)和链表2再调用本过程,比较得到结果链表的第二个节点,即2与3比较得到2。此时合并后的链表节点为1->2。
接下来的过程类似(2),如此递归直到两个链表的节点都被加到结果链表中
node * MergeRecursive(node *head1, node *head2)
{
node *head = NULL;
if (head1 == NULL)
{
return head2;
}
if (head2 == NULL)
{
return head1;
}
if ( head1->data < head2->data )
{
head = head1 ;
head->next = MergeRecursive(head1->next,head2);
}
else
{
head = head2 ;
head->next = MergeRecursive(head1,head2->next);
}
return head ;
}
(2)非递归方法。有链表头
Node* Merge2(Node* head1, Node* head2)
{
Node *res, *ret, *p, *q;
if(head1 == NULL) return head2;
if(head2 == NULL) return head1;
ret = res = head1;
p = head1->next;
q = head2->next;
while(p && q)
{
if(p->value < q->value)
{
res->next = p;
res = p;
p = p->next;
}
else
{
res->next = q;
res = q;
q = q->next;
}
}
res->next = p ? p : q; //p或q有剩,把剩下的链表插入到结果链表后
return ret;
}
还可以借助第三个链表,只是这样的话需要的空间会大点
Node *head3,*temp;
head3->next=NULL;
temp=head3;//保存表头
p=head1->next;
q=head2->next;
while(p!=NULL&&q!=NULL)
{
if(p->value<q->value)
{
head3->next=p;
head3=p;
p=p->next;
}
else
{
head3->next=q;
head3=q;
q=q->next;
}
if(p==NULL)
head3->next=q;
else
head3->next=p;
head3=temp;
}
3、双向链表删除结点
dnode *deletenode(dnode *head,dnode *node)
{
if(node->pre == NULL)
{
// 头结点
head = head->next;
head->pre = NULL;
}
else if(node->next == NULL)
{
// 尾结点
node->pre->next = NULL;
}
else
{
// 中间结点
node->pre->next = node->next;
node->next->pre = node->pre;
}
free(node);
return head;
}
4、寻找链表中间结点
【方法1】普通的方法很简单,首先遍历一遍单链表以确定单链表的长度L。然后再次从头节点出发循环L/2次找到单链表的中间节点。算法复杂度为O(L+L/2)=O(3L/2)。
【方法2】使用快慢指针。设定两个指向第一个结点的指针,一个(p)一次走一个,一个(q)一次走两个。当走的快的到达链表尾部(q->next==NULL ||q->next->next==NULL)时,慢的就指向中间结点(快指针到链表尾部时,当链表长度为奇数时,慢指针指向的即是链表中间指针,当链表长度为偶数时,慢指针指向的结点和慢指针指向结点的下一个结点都是链表的中间结点)
node *findMid(node *head) // 有链表头
{
if(NULL == head)
return NULL;
node *fast, *slow;
fast = slow = head;
while((fast != NULL) && (fast->next != NULL))
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
node *findMid(node *head) // 无链表头
{
if(NULL == head)
return NULL;
if(head->next == NULL || head->next->next == NULL)
return head;
node *fast = head;
node *slow = head;
while(fast->next != NULL && fast->next->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
5、判断一个链表是否有环
(1)判断是否有环。
设置两个指针(fast, slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定 相遇。(当然,fast先行头到尾部为NULL,则为无环链表)
(2)找到环的入口点
当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。假设slow走了s步,则 fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:
2s = s + nr
s= nr
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)
(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每 次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
node *findCircle(node *head)
{
if(NULL == head)
return NULL;
node *fast, *slow;
fast = slow = head;
while((fast != NULL) && (fast->next != NULL))
{
fast = fast->next->next; //走两步
slow = slow->next; //走一步
if(fast == slow){ //有环
fast = head;
while(fast != slow){
fast = fast->next;
slow = slow->next;
}
return fast;
}
}
return NULL;
}
PS:感觉链表经常在面试中遇到,一般面试官经常会要你进行纸上编程,考查你的动手能力等等。。。
未完待续。。。
相关文章推荐
- 感恩节寄语
- Java 获取当前所有的线程
- (笔记)angular 路由
- 响应式网页设计:rem、em设置网页字体大小自适应
- [Java代码] Java Bean 工具包 Dozer
- PHP实现从1累加到100(1+2+….+100=)的几种思路
- Matlab中统计矩阵中相同元素的个数的方法
- 冒泡和捕获小结
- linux下bluetooth编程(四)L2CAP层编程
- Java文件操作大全
- 轮播图插件
- JVM 并发性: 使用 Akka 构建 actor 应用程序
- linux下bluetooth编程(三)HCI层编程
- 告诉你 IOS9.0 之后的Bitcode到底是什么!!
- [python]python中的if, while, for
- hdu4521 dp+树状数组
- WebApi中跨域解决办法
- [Mysql数据库] MySql 获取数据表中随机一条数据
- 安卓项目版本更新(保存xml数据)
- 服务器会自动进行urldecode()一次!!!