题目要求
微软亚院之编程判断俩个链表是否相交
给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。
为了简化问题,我们假设俩个链表均不带环。
问题扩展:
1.如果链表可能有环列?
2.如果需要求出俩个链表相交的第一个节点列?
问题分析:
其实本题目是考察链表的相交和环的问题。
一个链表的相交判断中,带环和不带环的判断方法是不一样的。稍后介绍两个都带环链表和都不带环链表的相交的判断方法。
于是此问题就分解为了3个:
- 如何判断一条链表是否带环?
- 如何判断两条不带环链表是否相交?
- 如何判断两条带环的链表是否相交?
如何判断一条链表是否带环?
那么我们先解决如何判断一条链表是否带环的问题。
一般解决判断链表是否有环问题的方法都是设置两个指针p1和p2,p1和p2分别在链表每次移动1步和2步,若最终相遇,则说明链表有环;若p2指向空,则说明无环。
稍微引申一下,如何返还链表的环开始所在的结点。这里涉及到一个公式推导:
假设单链表的总长度为L,头结点到环入口的距离为a,环入口到p1,p2指针相遇的结点距离为x,环的长度为r,p1总共走了s步,则p2走了2s步。另外,p2要追上p1的话,p2至少要在环里面转了一圈多(假设转了n圈加x的距离),得到以下关系:
s = a + x;
2s = a + nr + x;
=>a + x = nr;
=>a = nr - x;
由上式可知:若在头结点和相遇结点分别设一指针,同步(单步)前进,则最后一定相遇在环入口结点.
那么我们看一下代码实现:
node* DoubleListOfCrossing::isCircle(node * head) {
if (NULL == head) {
return NULL;
}
node* p1 = head;
node* p2 = head;
do {
if (NULL != p1->next && NULL != p2->next && NULL != p2->next->next) {
p1 = p1->next;
p2 = p2->next->next;
} else {
return NULL;
}
}while (p1 != p2);
p2 = head;
while (p1 != p2) {
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
如何判断两条不带环链表是否相交?
通常判断两条不带环的链表相交有两种方法:
- 利用无环单链表相交的冲要条件:两条无环链表的尾结点一定相交。
假设两无环链表分别是L1和L2,把L2头结点接到L1的尾结点,判断L1是否有环。
这里我给出第二种方法的代码实现:
/* 不带环的情况下判断两个链表是否相交 */
bool DoubleListOfCrossing::isCrossingOfNoCircle() {
if (NULL == m_h1 || NULL == m_h2) {
return NULL;
}
/* 把链表2接到链表1上. */
node * tail1 = m_h1;
while (NULL != tail1->next) {
tail1 = tail1->next;
}
tail1->next = m_h2;
return (NULL == isCircle(m_h1)) ? false : true;
}
如何判断两条都带环的链表是否相交?
这里利用一个充分必要条件:若两条都带环的链表相交,则其中任意一条链表的环所处的结点,必然在与其相交的链表上。
这样就将判断两条都带环的链表的相交问题转化为:
- 求出一条链表的环结点;
求该环结点是否在另一条链表上。
下面我们看一下代码实现:
node* DoubleListOfCrossing::findCircleNode(node * head) {
return isCircle(head);
}
/* 判断一个结点是否在链表上. */
bool DoubleListOfCrossing::isExist(node* head,node * element) {
if (NULL == head) {
return false;
}
node * tmp = head;
while (NULL != tmp->next) {
if (tmp == element) {
return true;
} else {
tmp = tmp->next;
}
}
return false;
}
/* 两个链表都带环的情况下,判断第一个链表的环结点是否在第二个链表上 */
bool DoubleListOfCrossing::isCrossingOfCircle() {
node * cross = findCircleNode(m_h1);
return isExist(m_h2, cross);
}
ok,解决完以上三个问题,我们继续看我们的题目,那么很容易的就将此问题分解成4个步骤:
- 判断两条链表是否都由环?
- 若都有环,则调用都有环的判断相交问题的方法;
- 若都无环,则调用都无环的判断相交问题的方法;
- 若一条有环,一条无环,则说明此两条链表不可能相交。
那么我们看一下相关的代码实现:
bool DoubleListOfCrossing::isCrossing() {
#define IS_CIRCLE(a) NULL != isCircle((a))
#define NOT_CIRCLE(a) NULL == isCircle((a))
if (NULL == m_h1 && NULL == m_h2) {
cout<<"NULL == m_h1 && NULL == m_h2"<<endl;
return false;
}
bool bothNotCircle = (NOT_CIRCLE(m_h1) && NOT_CIRCLE(m_h2)) ? true : false;
bool bothCircle = (IS_CIRCLE(m_h1) && IS_CIRCLE(m_h2)) ? true : false;
/* 判断两个链表是否都有环,若都没有环 */
if (bothNotCircle) {
return isCrossingOfNoCircle();
}
/* 若都有环 */
if (bothCircle) {
return isCrossingOfCircle();
}
/* 若一个有环,一个没有环则两个链表不可能相交 */
return false;
}
为了验证这个问题,我写了一个测试程序,代码如下:
node* initOneNode(valueType value) {
node* tmp = (node*)malloc(sizeof(node));
memset(tmp, 0, sizeof(node));
tmp->value = value;
return tmp;
}
/* 初始化一个有环的链表,返回链表头结点指针 */
node* initOneCircleList(int size,int crossPosition) {
node *array[size];
node * prev = NULL;
for (int i = 0; i < size; i++) {
array[i] = initOneNode(i);
//cout<<"initOneCircleList value:"<<array[i]->value<<endl;
//cout<<"initOneCircleList address:"<<array[i]<<endl;
if (NULL != prev) {
prev->next = array[i];
}
prev = array[i];
//cout<<"prev address:"<<prev<<endl;
}
array[size-1]->next = array[crossPosition-1];
#if 0
node * tmp = array[0];
for (int i = 0; i < size*2; i++) {
cout<<"array["<<i<<"] address:"<<tmp<<endl;
tmp = tmp->next;
}
#endif
return array[0];
}
/* 初始化一个有环的链表,返回链表头结点指针 */
node* initOneNoCircleList(int size)
{
node* tmp = NULL;
node* prev = initOneNode(0);
node* head = prev;
for (int i = 1; i < size; i++) {
tmp = initOneNode(i);
prev->next = tmp;
prev = tmp;
}
#if 0
tmp = head;
for (int i = 0; i < size; i++) {
cout<<"tmp address:"<<tmp<<endl;
tmp = tmp->next;
}
#endif
return head;
}
/* 测试两条有环不相交链表 */
int test_case_2(node* h1, node* h2) {
h1 = initOneCircleList(5, 3);
h2 = initOneCircleList(5, 3);
return 0;
}
/* 测试两条无环相交链表 */
int test_case_3(node* h1, node* h2) {
h1 = initOneNoCircleList(5);
h2 = initOneNoCircleList(4);
node* tmp = h2;
while (NULL != tmp->next) {
tmp = tmp->next;
}
tmp->next = h1;
return 0;
}
/* 测试两条无环不相交链表 */
int test_case_4(node* h1, node* h2) {
h1 = initOneNoCircleList(5);
h2 = initOneNoCircleList(4);
return 0;
}
/* 测试两条有环相交链表 */
int test_case_1(node** h1,node** h2) {
*h1 = initOneCircleList(5, 3);
*h2 = initOneNoCircleList(3);
node* tmp_1 = *h1;
node* tmp_2 = *h2;
while (NULL != tmp_2->next) {
// cout<<"tmp_1 address:"<<tmp_1<<endl;
// cout<<"tmp_2 address:"<<tmp_2<<endl;
tmp_1 = tmp_1->next;
tmp_2 = tmp_2->next;
}
tmp_2->next = tmp_1;
#if 0
tmp_1 = h1;
tmp_2 = h2;
for(int i = 0; i < 5*2; i++) {
cout<<"tmp_1 address:"<<tmp_1<<endl;
cout<<"tmp_2 address:"<<tmp_2<<endl;
tmp_1 = tmp_1->next;
tmp_2 = tmp_2->next;
}
#endif
cout<<"h1:"<<*h1<<" h2:"<<*h2<<endl;
return 0;
}
int test_7() {
node *h1 = NULL;
node *h2 = NULL;
test_case_1(&h1, &h2);
//test_case_2(h1, h2);
//test_case_3(h1, h2);
//test_case_4(h1, h2);
cout<<"h1 address:"<<h1<<" h2 address:"<<h2<<endl;
DoubleListOfCrossing obj(h1,h2);
if ( obj.isCrossing())
cout<<"list h1 & h2 is crossing."<<endl;
else
cout<<"list h1 & h2 is not crossing"<<endl;
return 0;
}
int main(int argc, const char * argv[]) {
//test_1();
//test_2();
//test_3();
//test_4();
//maxHeap_test();
//test_5_1();
test_7();
return 0;
}
经过测试程序测试,以上代码能够跑通。下面是解决这个题目的完整版的代码。
先看头文件:
//
// 7th.h
// 100-alg-tests
//
// Created by bobkentt on 15-4-11.
// Copyright (c) 2015年 kedong. All rights reserved.
//
#ifndef ___00_alg_tests___th__
#define ___00_alg_tests___th__
#include <stdio.h>
#define valueType int
struct node {
valueType value;
node * next;
};
class DoubleListOfCrossing {
/* 判断一个链表是否有环 */
node* isCircle(node * head);
/// 判断两个链表是否相交
bool isCrossingOfNoCircle();
bool isCrossingOfCircle();
/* 找到一个带环链表的环结点. */
node* findCircleNode(node * head);
bool isExist(node* head,node * element);
node * m_h1;
node * m_h2;
public:
DoubleListOfCrossing(node * h1, node * h2) { m_h1 = h1;m_h2 = h2;};
~DoubleListOfCrossing() {};
bool isCrossing();
/* @type == 1 print m_h1;@type==2 print m_h2 */
void printList(bool type);
};
// 测试程序
int test_7();
#endif /* defined(___00_alg_tests___th__) */
再看一下源码实现和测试程序
//
// 7th.cpp
// 100-alg-tests
//
// Created by bobkentt on 15-4-11.
// Copyright (c) 2015年 kedong. All rights reserved.
//
#include <iostream>
#include <stdlib.h>
#include "7th.h"
using namespace std;
/*题目要求:
微软亚院之编程判断俩个链表是否相交
给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。
为了简化问题,我们假设俩个链表均不带环。
问题扩展:
1.如果链表可能有环列?
2.如果需要求出俩个链表相交的第一个节点列?
*/
/// 判断一个链表是否有环
/*
假设单链表的总长度为L,头结点到环入口的距离为a,
环入口到快慢指针相遇的结点距离为x,环的长度为r,
慢指针总共走了s步,则快指针走了2s步。另外,
快指针要追上慢指针的话快指针至少要在环里面转了
一圈多(假设转了n圈加x的距离),得到以下关系:
s = a + x;
2s = a + nr + x;
=>a + x = nr;
=>a = nr - x;
由上式可知:若在头结点和相遇结点分别设一指针,
同步(单步)前进,则最后一定相遇在环入口结点.
*/
/*判断链表是否有环,如果有环则返回环的首结点指针,否则返回NULL值*/
node* DoubleListOfCrossing::isCircle(node * head) {
if (NULL == head) {
return NULL;
}
node* p1 = head;
node* p2 = head;
do {
if (NULL != p1->next && NULL != p2->next && NULL != p2->next->next) {
p1 = p1->next;
p2 = p2->next->next;
} else {
return NULL;
}
}while (p1 != p2);
p2 = head;
while (p1 != p2) {
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
node* DoubleListOfCrossing::findCircleNode(node * head) {
return isCircle(head);
}
/* 不带环的情况下判断两个链表是否相交 */
bool DoubleListOfCrossing::isCrossingOfNoCircle() {
if (NULL == m_h1 || NULL == m_h2) {
return NULL;
}
/* 把链表2接到链表1上. */
node * tail1 = m_h1;
while (NULL != tail1->next) {
tail1 = tail1->next;
}
tail1->next = m_h2;
return (NULL == isCircle(m_h1)) ? false : true;
}
/* 判断一个结点是否在链表上. */
bool DoubleListOfCrossing::isExist(node* head,node * element) {
if (NULL == head) {
return false;
}
node * tmp = head;
while (NULL != tmp->next) {
if (tmp == element) {
return true;
} else {
tmp = tmp->next;
}
}
return false;
}
/* 两个链表都带环的情况下,判断第一个链表的环结点是否在第二个链表上 */
bool DoubleListOfCrossing::isCrossingOfCircle() {
node * cross = findCircleNode(m_h1);
return isExist(m_h2, cross);
}
/*
* @param <type> <true> 打印链表1;
* <false>打印链表2.
*/
void DoubleListOfCrossing::printList(bool type) {
node * tail = (true == type) ? m_h1 : m_h2;
if (NULL == tail) {
return;
}
while (NULL != tail->next) {
cout<<tail->value<<" ";
tail = tail->next;
}
cout<<endl;
return;
}
bool DoubleListOfCrossing::isCrossing() {
#define IS_CIRCLE(a) NULL != isCircle((a))
#define NOT_CIRCLE(a) NULL == isCircle((a))
if (NULL == m_h1 && NULL == m_h2) {
cout<<"NULL == m_h1 && NULL == m_h2"<<endl;
return false;
}
bool bothNotCircle = (NOT_CIRCLE(m_h1) && NOT_CIRCLE(m_h2)) ? true : false;
bool bothCircle = (IS_CIRCLE(m_h1) && IS_CIRCLE(m_h2)) ? true : false;
/* 判断两个链表是否都有环,若都没有环 */
if (bothNotCircle) {
return isCrossingOfNoCircle();
}
/* 若都有环 */
if (bothCircle) {
return isCrossingOfCircle();
}
/* 若一个有环,一个没有环则两个链表不可能相交 */
return false;
}
node* initOneNode(valueType value) {
node* tmp = (node*)malloc(sizeof(node));
memset(tmp, 0, sizeof(node));
tmp->value = value;
return tmp;
}
/* 初始化一个有环的链表,返回链表头结点指针 */
node* initOneCircleList(int size,int crossPosition) {
node *array[size];
node * prev = NULL;
for (int i = 0; i < size; i++) {
array[i] = initOneNode(i);
//cout<<"initOneCircleList value:"<<array[i]->value<<endl;
//cout<<"initOneCircleList address:"<<array[i]<<endl;
if (NULL != prev) {
prev->next = array[i];
}
prev = array[i];
//cout<<"prev address:"<<prev<<endl;
}
array[size-1]->next = array[crossPosition-1];
#if 0
node * tmp = array[0];
for (int i = 0; i < size*2; i++) {
cout<<"array["<<i<<"] address:"<<tmp<<endl;
tmp = tmp->next;
}
#endif
return array[0];
}
/* 初始化一个有环的链表,返回链表头结点指针 */
node* initOneNoCircleList(int size)
{
node* tmp = NULL;
node* prev = initOneNode(0);
node* head = prev;
for (int i = 1; i < size; i++) {
tmp = initOneNode(i);
prev->next = tmp;
prev = tmp;
}
#if 0
tmp = head;
for (int i = 0; i < size; i++) {
cout<<"tmp address:"<<tmp<<endl;
tmp = tmp->next;
}
#endif
return head;
}
/* 测试两条有环不相交链表 */
int test_case_2(node* h1, node* h2) {
h1 = initOneCircleList(5, 3);
h2 = initOneCircleList(5, 3);
return 0;
}
/* 测试两条无环相交链表 */
int test_case_3(node* h1, node* h2) {
h1 = initOneNoCircleList(5);
h2 = initOneNoCircleList(4);
node* tmp = h2;
while (NULL != tmp->next) {
tmp = tmp->next;
}
tmp->next = h1;
return 0;
}
/* 测试两条无环不相交链表 */
int test_case_4(node* h1, node* h2) {
h1 = initOneNoCircleList(5);
h2 = initOneNoCircleList(4);
return 0;
}
/* 测试两条有环相交链表 */
int test_case_1(node** h1,node** h2) {
*h1 = initOneCircleList(5, 3);
*h2 = initOneNoCircleList(3);
node* tmp_1 = *h1;
node* tmp_2 = *h2;
while (NULL != tmp_2->next) {
// cout<<"tmp_1 address:"<<tmp_1<<endl;
// cout<<"tmp_2 address:"<<tmp_2<<endl;
tmp_1 = tmp_1->next;
tmp_2 = tmp_2->next;
}
tmp_2->next = tmp_1;
#if 0
tmp_1 = h1;
tmp_2 = h2;
for(int i = 0; i < 5*2; i++) {
cout<<"tmp_1 address:"<<tmp_1<<endl;
cout<<"tmp_2 address:"<<tmp_2<<endl;
tmp_1 = tmp_1->next;
tmp_2 = tmp_2->next;
}
#endif
cout<<"h1:"<<*h1<<" h2:"<<*h2<<endl;
return 0;
}
int test_7() {
node *h1 = NULL;
node *h2 = NULL;
test_case_1(&h1, &h2);
//test_case_2(h1, h2);
//test_case_3(h1, h2);
//test_case_4(h1, h2);
cout<<"h1 address:"<<h1<<" h2 address:"<<h2<<endl;
DoubleListOfCrossing obj(h1,h2);
if ( obj.isCrossing())
cout<<"list h1 & h2 is crossing."<<endl;
else
cout<<"list h1 & h2 is not crossing"<<endl;
return 0;
}
最后看一下主函数
//
// main.cpp
// 100-alg-tests
//
// Created by bobkentt on 15-3-24.
// Copyright (c) 2015年 kedong. All rights reserved.
//
#include <iostream>
//#include "2th.h"
//#include "3th.h"
//#include "4th.h"
//#include "maxHeap.h"
//#include "5th.h"
#include "7th.h"
int main(int argc, const char * argv[]) {
//test_1();
//test_2();
//test_3();
//test_4();
//maxHeap_test();
//test_5_1();
test_7();
return 0;
}
Okay,第7题的完整解题过程就到这里了,希望大家多指教。
路漫漫其修远兮,吾将上下而…