数据结构和算法经典100题-第7题

题目要求


微软亚院之编程判断俩个链表是否相交
给出俩个单向链表的头指针,比如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;
}

如何判断两条不带环链表是否相交?
通常判断两条不带环的链表相交有两种方法:

  1. 利用无环单链表相交的冲要条件:两条无环链表的尾结点一定相交。
  2. 假设两无环链表分别是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个步骤:

  1. 判断两条链表是否都由环?
  2. 若都有环,则调用都有环的判断相交问题的方法;
  3. 若都无环,则调用都无环的判断相交问题的方法;
  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题的完整解题过程就到这里了,希望大家多指教。


路漫漫其修远兮,吾将上下而…


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值